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;
42mod signature_help;
43#[cfg(any(test, feature = "test-support"))]
44pub mod test;
45
46use ::git::diff::{DiffHunk, DiffHunkStatus};
47use ::git::{parse_git_remote_url, BuildPermalinkParams, GitHostingProviderRegistry};
48pub(crate) use actions::*;
49use aho_corasick::AhoCorasick;
50use anyhow::{anyhow, Context as _, Result};
51use blink_manager::BlinkManager;
52use client::{Collaborator, ParticipantIndex};
53use clock::ReplicaId;
54use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
55use convert_case::{Case, Casing};
56use debounced_delay::DebouncedDelay;
57use display_map::*;
58pub use display_map::{DisplayPoint, FoldPlaceholder};
59pub use editor_settings::{CurrentLineHighlight, EditorSettings};
60use element::LineWithInvisibles;
61pub use element::{
62 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
63};
64use futures::FutureExt;
65use fuzzy::{StringMatch, StringMatchCandidate};
66use git::blame::GitBlame;
67use git::diff_hunk_to_display;
68use gpui::{
69 div, impl_actions, point, prelude::*, px, relative, size, uniform_list, Action, AnyElement,
70 AppContext, AsyncWindowContext, AvailableSpace, BackgroundExecutor, Bounds, ClipboardItem,
71 Context, DispatchPhase, ElementId, EntityId, EventEmitter, FocusHandle, FocusOutEvent,
72 FocusableView, FontId, FontStyle, FontWeight, HighlightStyle, Hsla, InteractiveText,
73 KeyContext, ListSizingBehavior, Model, MouseButton, PaintQuad, ParentElement, Pixels, Render,
74 SharedString, Size, StrikethroughStyle, Styled, StyledText, Subscription, Task, TextStyle,
75 UnderlineStyle, UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext,
76 WeakFocusHandle, WeakView, WhiteSpace, WindowContext,
77};
78use highlight_matching_bracket::refresh_matching_bracket_highlights;
79use hover_popover::{hide_hover, HoverState};
80use hunk_diff::ExpandedHunks;
81pub(crate) use hunk_diff::HoveredHunk;
82use indent_guides::ActiveIndentGuidesState;
83use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
84pub use inline_completion_provider::*;
85pub use items::MAX_TAB_TITLE_LEN;
86use itertools::Itertools;
87use language::{
88 char_kind,
89 language_settings::{self, all_language_settings, InlayHintSettings},
90 markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
91 CursorShape, Diagnostic, Documentation, IndentKind, IndentSize, Language, OffsetRangeExt,
92 Point, Selection, SelectionGoal, TransactionId,
93};
94use language::{point_to_lsp, BufferRow, Runnable, RunnableRange};
95use linked_editing_ranges::refresh_linked_ranges;
96use task::{ResolvedTask, TaskTemplate, TaskVariables};
97
98use hover_links::{HoverLink, HoveredLinkState, InlayHighlight};
99pub use lsp::CompletionContext;
100use lsp::{
101 CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity, InsertTextFormat,
102 LanguageServerId,
103};
104use mouse_context_menu::MouseContextMenu;
105use movement::TextLayoutDetails;
106pub use multi_buffer::{
107 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
108 ToPoint,
109};
110use multi_buffer::{ExpandExcerptDirection, MultiBufferPoint, MultiBufferRow, ToOffsetUtf16};
111use ordered_float::OrderedFloat;
112use parking_lot::{Mutex, RwLock};
113use project::project_settings::{GitGutterSetting, ProjectSettings};
114use project::{
115 CodeAction, Completion, FormatTrigger, Item, Location, Project, ProjectPath,
116 ProjectTransaction, TaskSourceKind, WorktreeId,
117};
118use rand::prelude::*;
119use rpc::{proto::*, ErrorExt};
120use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
121use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
122use serde::{Deserialize, Serialize};
123use settings::{update_settings_file, Settings, SettingsStore};
124use smallvec::SmallVec;
125use snippet::Snippet;
126use std::{
127 any::TypeId,
128 borrow::Cow,
129 cell::RefCell,
130 cmp::{self, Ordering, Reverse},
131 mem,
132 num::NonZeroU32,
133 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
134 path::{Path, PathBuf},
135 rc::Rc,
136 sync::Arc,
137 time::{Duration, Instant},
138};
139pub use sum_tree::Bias;
140use sum_tree::TreeMap;
141use text::{BufferId, OffsetUtf16, Rope};
142use theme::{
143 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
144 ThemeColors, ThemeSettings,
145};
146use ui::{
147 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize,
148 ListItem, Popover, Tooltip,
149};
150use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
151use workspace::item::{ItemHandle, PreviewTabsSettings};
152use workspace::notifications::{DetachAndPromptErr, NotificationId};
153use workspace::{
154 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
155};
156use workspace::{OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
157
158use crate::hover_links::find_url;
159use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
160
161pub const FILE_HEADER_HEIGHT: u8 = 1;
162pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u8 = 1;
163pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u8 = 1;
164pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
165const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
166const MAX_LINE_LEN: usize = 1024;
167const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
168const MAX_SELECTION_HISTORY_LEN: usize = 1024;
169pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
170#[doc(hidden)]
171pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
172#[doc(hidden)]
173pub const DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
174
175pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
176
177pub fn render_parsed_markdown(
178 element_id: impl Into<ElementId>,
179 parsed: &language::ParsedMarkdown,
180 editor_style: &EditorStyle,
181 workspace: Option<WeakView<Workspace>>,
182 cx: &mut WindowContext,
183) -> InteractiveText {
184 let code_span_background_color = cx
185 .theme()
186 .colors()
187 .editor_document_highlight_read_background;
188
189 let highlights = gpui::combine_highlights(
190 parsed.highlights.iter().filter_map(|(range, highlight)| {
191 let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
192 Some((range.clone(), highlight))
193 }),
194 parsed
195 .regions
196 .iter()
197 .zip(&parsed.region_ranges)
198 .filter_map(|(region, range)| {
199 if region.code {
200 Some((
201 range.clone(),
202 HighlightStyle {
203 background_color: Some(code_span_background_color),
204 ..Default::default()
205 },
206 ))
207 } else {
208 None
209 }
210 }),
211 );
212
213 let mut links = Vec::new();
214 let mut link_ranges = Vec::new();
215 for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
216 if let Some(link) = region.link.clone() {
217 links.push(link);
218 link_ranges.push(range.clone());
219 }
220 }
221
222 InteractiveText::new(
223 element_id,
224 StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
225 )
226 .on_click(link_ranges, move |clicked_range_ix, cx| {
227 match &links[clicked_range_ix] {
228 markdown::Link::Web { url } => cx.open_url(url),
229 markdown::Link::Path { path } => {
230 if let Some(workspace) = &workspace {
231 _ = workspace.update(cx, |workspace, cx| {
232 workspace.open_abs_path(path.clone(), false, cx).detach();
233 });
234 }
235 }
236 }
237 })
238}
239
240#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
241pub(crate) enum InlayId {
242 Suggestion(usize),
243 Hint(usize),
244}
245
246impl InlayId {
247 fn id(&self) -> usize {
248 match self {
249 Self::Suggestion(id) => *id,
250 Self::Hint(id) => *id,
251 }
252 }
253}
254
255enum DiffRowHighlight {}
256enum DocumentHighlightRead {}
257enum DocumentHighlightWrite {}
258enum InputComposition {}
259
260#[derive(Copy, Clone, PartialEq, Eq)]
261pub enum Direction {
262 Prev,
263 Next,
264}
265
266pub fn init_settings(cx: &mut AppContext) {
267 EditorSettings::register(cx);
268}
269
270pub fn init(cx: &mut AppContext) {
271 init_settings(cx);
272
273 workspace::register_project_item::<Editor>(cx);
274 workspace::FollowableViewRegistry::register::<Editor>(cx);
275 workspace::register_serializable_item::<Editor>(cx);
276
277 cx.observe_new_views(
278 |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
279 workspace.register_action(Editor::new_file);
280 workspace.register_action(Editor::new_file_in_direction);
281 },
282 )
283 .detach();
284
285 cx.on_action(move |_: &workspace::NewFile, 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 cx.on_action(move |_: &workspace::NewWindow, cx| {
295 let app_state = workspace::AppState::global(cx);
296 if let Some(app_state) = app_state.upgrade() {
297 workspace::open_new(app_state, cx, |workspace, cx| {
298 Editor::new_file(workspace, &Default::default(), cx)
299 })
300 .detach();
301 }
302 });
303}
304
305pub struct SearchWithinRange;
306
307trait InvalidationRegion {
308 fn ranges(&self) -> &[Range<Anchor>];
309}
310
311#[derive(Clone, Debug, PartialEq)]
312pub enum SelectPhase {
313 Begin {
314 position: DisplayPoint,
315 add: bool,
316 click_count: usize,
317 },
318 BeginColumnar {
319 position: DisplayPoint,
320 reset: bool,
321 goal_column: u32,
322 },
323 Extend {
324 position: DisplayPoint,
325 click_count: usize,
326 },
327 Update {
328 position: DisplayPoint,
329 goal_column: u32,
330 scroll_delta: gpui::Point<f32>,
331 },
332 End,
333}
334
335#[derive(Clone, Debug)]
336pub enum SelectMode {
337 Character,
338 Word(Range<Anchor>),
339 Line(Range<Anchor>),
340 All,
341}
342
343#[derive(Copy, Clone, PartialEq, Eq, Debug)]
344pub enum EditorMode {
345 SingleLine { auto_width: bool },
346 AutoHeight { max_lines: usize },
347 Full,
348}
349
350#[derive(Clone, Debug)]
351pub enum SoftWrap {
352 None,
353 PreferLine,
354 EditorWidth,
355 Column(u32),
356}
357
358#[derive(Clone)]
359pub struct EditorStyle {
360 pub background: Hsla,
361 pub local_player: PlayerColor,
362 pub text: TextStyle,
363 pub scrollbar_width: Pixels,
364 pub syntax: Arc<SyntaxTheme>,
365 pub status: StatusColors,
366 pub inlay_hints_style: HighlightStyle,
367 pub suggestions_style: HighlightStyle,
368}
369
370impl Default for EditorStyle {
371 fn default() -> Self {
372 Self {
373 background: Hsla::default(),
374 local_player: PlayerColor::default(),
375 text: TextStyle::default(),
376 scrollbar_width: Pixels::default(),
377 syntax: Default::default(),
378 // HACK: Status colors don't have a real default.
379 // We should look into removing the status colors from the editor
380 // style and retrieve them directly from the theme.
381 status: StatusColors::dark(),
382 inlay_hints_style: HighlightStyle::default(),
383 suggestions_style: HighlightStyle::default(),
384 }
385 }
386}
387
388type CompletionId = usize;
389
390#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
391struct EditorActionId(usize);
392
393impl EditorActionId {
394 pub fn post_inc(&mut self) -> Self {
395 let answer = self.0;
396
397 *self = Self(answer + 1);
398
399 Self(answer)
400 }
401}
402
403// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
404// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
405
406type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
407type GutterHighlight = (fn(&AppContext) -> Hsla, Arc<[Range<Anchor>]>);
408
409struct ScrollbarMarkerState {
410 scrollbar_size: Size<Pixels>,
411 dirty: bool,
412 markers: Arc<[PaintQuad]>,
413 pending_refresh: Option<Task<Result<()>>>,
414}
415
416impl ScrollbarMarkerState {
417 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
418 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
419 }
420}
421
422impl Default for ScrollbarMarkerState {
423 fn default() -> Self {
424 Self {
425 scrollbar_size: Size::default(),
426 dirty: false,
427 markers: Arc::from([]),
428 pending_refresh: None,
429 }
430 }
431}
432
433#[derive(Clone, Debug)]
434struct RunnableTasks {
435 templates: Vec<(TaskSourceKind, TaskTemplate)>,
436 offset: MultiBufferOffset,
437 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
438 column: u32,
439 // Values of all named captures, including those starting with '_'
440 extra_variables: HashMap<String, String>,
441 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
442 context_range: Range<BufferOffset>,
443}
444
445#[derive(Clone)]
446struct ResolvedTasks {
447 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
448 position: Anchor,
449}
450#[derive(Copy, Clone, Debug)]
451struct MultiBufferOffset(usize);
452#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
453struct BufferOffset(usize);
454/// Zed's primary text input `View`, allowing users to edit a [`MultiBuffer`]
455///
456/// See the [module level documentation](self) for more information.
457pub struct Editor {
458 focus_handle: FocusHandle,
459 last_focused_descendant: Option<WeakFocusHandle>,
460 /// The text buffer being edited
461 buffer: Model<MultiBuffer>,
462 /// Map of how text in the buffer should be displayed.
463 /// Handles soft wraps, folds, fake inlay text insertions, etc.
464 pub display_map: Model<DisplayMap>,
465 pub selections: SelectionsCollection,
466 pub scroll_manager: ScrollManager,
467 /// When inline assist editors are linked, they all render cursors because
468 /// typing enters text into each of them, even the ones that aren't focused.
469 pub(crate) show_cursor_when_unfocused: bool,
470 columnar_selection_tail: Option<Anchor>,
471 add_selections_state: Option<AddSelectionsState>,
472 select_next_state: Option<SelectNextState>,
473 select_prev_state: Option<SelectNextState>,
474 selection_history: SelectionHistory,
475 autoclose_regions: Vec<AutocloseRegion>,
476 snippet_stack: InvalidationStack<SnippetState>,
477 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
478 ime_transaction: Option<TransactionId>,
479 active_diagnostics: Option<ActiveDiagnosticGroup>,
480 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
481 project: Option<Model<Project>>,
482 completion_provider: Option<Box<dyn CompletionProvider>>,
483 collaboration_hub: Option<Box<dyn CollaborationHub>>,
484 blink_manager: Model<BlinkManager>,
485 show_cursor_names: bool,
486 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
487 pub show_local_selections: bool,
488 mode: EditorMode,
489 show_breadcrumbs: bool,
490 show_gutter: bool,
491 redact_all: bool,
492 show_line_numbers: Option<bool>,
493 show_git_diff_gutter: Option<bool>,
494 show_code_actions: Option<bool>,
495 show_runnables: Option<bool>,
496 show_wrap_guides: Option<bool>,
497 show_indent_guides: Option<bool>,
498 placeholder_text: Option<Arc<str>>,
499 highlight_order: usize,
500 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
501 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
502 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
503 scrollbar_marker_state: ScrollbarMarkerState,
504 active_indent_guides_state: ActiveIndentGuidesState,
505 nav_history: Option<ItemNavHistory>,
506 context_menu: RwLock<Option<ContextMenu>>,
507 mouse_context_menu: Option<MouseContextMenu>,
508 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
509 signature_help_state: SignatureHelpState,
510 auto_signature_help: Option<bool>,
511 find_all_references_task_sources: Vec<Anchor>,
512 next_completion_id: CompletionId,
513 completion_documentation_pre_resolve_debounce: DebouncedDelay,
514 available_code_actions: Option<(Location, Arc<[CodeAction]>)>,
515 code_actions_task: Option<Task<()>>,
516 document_highlights_task: Option<Task<()>>,
517 linked_editing_range_task: Option<Task<Option<()>>>,
518 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
519 pending_rename: Option<RenameState>,
520 searchable: bool,
521 cursor_shape: CursorShape,
522 current_line_highlight: Option<CurrentLineHighlight>,
523 collapse_matches: bool,
524 autoindent_mode: Option<AutoindentMode>,
525 workspace: Option<(WeakView<Workspace>, Option<WorkspaceId>)>,
526 keymap_context_layers: BTreeMap<TypeId, KeyContext>,
527 input_enabled: bool,
528 use_modal_editing: bool,
529 read_only: bool,
530 leader_peer_id: Option<PeerId>,
531 remote_id: Option<ViewId>,
532 hover_state: HoverState,
533 gutter_hovered: bool,
534 hovered_link_state: Option<HoveredLinkState>,
535 inline_completion_provider: Option<RegisteredInlineCompletionProvider>,
536 active_inline_completion: Option<Inlay>,
537 show_inline_completions: bool,
538 inlay_hint_cache: InlayHintCache,
539 expanded_hunks: ExpandedHunks,
540 next_inlay_id: usize,
541 _subscriptions: Vec<Subscription>,
542 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
543 gutter_dimensions: GutterDimensions,
544 pub vim_replace_map: HashMap<Range<usize>, String>,
545 style: Option<EditorStyle>,
546 next_editor_action_id: EditorActionId,
547 editor_actions: Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut ViewContext<Self>)>>>>,
548 use_autoclose: bool,
549 use_auto_surround: bool,
550 auto_replace_emoji_shortcode: bool,
551 show_git_blame_gutter: bool,
552 show_git_blame_inline: bool,
553 show_git_blame_inline_delay_task: Option<Task<()>>,
554 git_blame_inline_enabled: bool,
555 serialize_dirty_buffers: bool,
556 show_selection_menu: Option<bool>,
557 blame: Option<Model<GitBlame>>,
558 blame_subscription: Option<Subscription>,
559 custom_context_menu: Option<
560 Box<
561 dyn 'static
562 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
563 >,
564 >,
565 last_bounds: Option<Bounds<Pixels>>,
566 expect_bounds_change: Option<Bounds<Pixels>>,
567 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
568 tasks_update_task: Option<Task<()>>,
569 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
570 file_header_size: u8,
571 breadcrumb_header: Option<String>,
572 focused_block: Option<FocusedBlock>,
573}
574
575#[derive(Clone)]
576pub struct EditorSnapshot {
577 pub mode: EditorMode,
578 show_gutter: bool,
579 show_line_numbers: Option<bool>,
580 show_git_diff_gutter: Option<bool>,
581 show_code_actions: Option<bool>,
582 show_runnables: Option<bool>,
583 render_git_blame_gutter: bool,
584 pub display_snapshot: DisplaySnapshot,
585 pub placeholder_text: Option<Arc<str>>,
586 is_focused: bool,
587 scroll_anchor: ScrollAnchor,
588 ongoing_scroll: OngoingScroll,
589 current_line_highlight: CurrentLineHighlight,
590 gutter_hovered: bool,
591}
592
593const GIT_BLAME_GUTTER_WIDTH_CHARS: f32 = 53.;
594
595#[derive(Debug, Clone, Copy)]
596pub struct GutterDimensions {
597 pub left_padding: Pixels,
598 pub right_padding: Pixels,
599 pub width: Pixels,
600 pub margin: Pixels,
601 pub git_blame_entries_width: Option<Pixels>,
602}
603
604impl GutterDimensions {
605 /// The full width of the space taken up by the gutter.
606 pub fn full_width(&self) -> Pixels {
607 self.margin + self.width
608 }
609
610 /// The width of the space reserved for the fold indicators,
611 /// use alongside 'justify_end' and `gutter_width` to
612 /// right align content with the line numbers
613 pub fn fold_area_width(&self) -> Pixels {
614 self.margin + self.right_padding
615 }
616}
617
618impl Default for GutterDimensions {
619 fn default() -> Self {
620 Self {
621 left_padding: Pixels::ZERO,
622 right_padding: Pixels::ZERO,
623 width: Pixels::ZERO,
624 margin: Pixels::ZERO,
625 git_blame_entries_width: None,
626 }
627 }
628}
629
630#[derive(Debug)]
631pub struct RemoteSelection {
632 pub replica_id: ReplicaId,
633 pub selection: Selection<Anchor>,
634 pub cursor_shape: CursorShape,
635 pub peer_id: PeerId,
636 pub line_mode: bool,
637 pub participant_index: Option<ParticipantIndex>,
638 pub user_name: Option<SharedString>,
639}
640
641#[derive(Clone, Debug)]
642struct SelectionHistoryEntry {
643 selections: Arc<[Selection<Anchor>]>,
644 select_next_state: Option<SelectNextState>,
645 select_prev_state: Option<SelectNextState>,
646 add_selections_state: Option<AddSelectionsState>,
647}
648
649enum SelectionHistoryMode {
650 Normal,
651 Undoing,
652 Redoing,
653}
654
655#[derive(Clone, PartialEq, Eq, Hash)]
656struct HoveredCursor {
657 replica_id: u16,
658 selection_id: usize,
659}
660
661impl Default for SelectionHistoryMode {
662 fn default() -> Self {
663 Self::Normal
664 }
665}
666
667#[derive(Default)]
668struct SelectionHistory {
669 #[allow(clippy::type_complexity)]
670 selections_by_transaction:
671 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
672 mode: SelectionHistoryMode,
673 undo_stack: VecDeque<SelectionHistoryEntry>,
674 redo_stack: VecDeque<SelectionHistoryEntry>,
675}
676
677impl SelectionHistory {
678 fn insert_transaction(
679 &mut self,
680 transaction_id: TransactionId,
681 selections: Arc<[Selection<Anchor>]>,
682 ) {
683 self.selections_by_transaction
684 .insert(transaction_id, (selections, None));
685 }
686
687 #[allow(clippy::type_complexity)]
688 fn transaction(
689 &self,
690 transaction_id: TransactionId,
691 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
692 self.selections_by_transaction.get(&transaction_id)
693 }
694
695 #[allow(clippy::type_complexity)]
696 fn transaction_mut(
697 &mut self,
698 transaction_id: TransactionId,
699 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
700 self.selections_by_transaction.get_mut(&transaction_id)
701 }
702
703 fn push(&mut self, entry: SelectionHistoryEntry) {
704 if !entry.selections.is_empty() {
705 match self.mode {
706 SelectionHistoryMode::Normal => {
707 self.push_undo(entry);
708 self.redo_stack.clear();
709 }
710 SelectionHistoryMode::Undoing => self.push_redo(entry),
711 SelectionHistoryMode::Redoing => self.push_undo(entry),
712 }
713 }
714 }
715
716 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
717 if self
718 .undo_stack
719 .back()
720 .map_or(true, |e| e.selections != entry.selections)
721 {
722 self.undo_stack.push_back(entry);
723 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
724 self.undo_stack.pop_front();
725 }
726 }
727 }
728
729 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
730 if self
731 .redo_stack
732 .back()
733 .map_or(true, |e| e.selections != entry.selections)
734 {
735 self.redo_stack.push_back(entry);
736 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
737 self.redo_stack.pop_front();
738 }
739 }
740 }
741}
742
743struct RowHighlight {
744 index: usize,
745 range: RangeInclusive<Anchor>,
746 color: Option<Hsla>,
747 should_autoscroll: bool,
748}
749
750#[derive(Clone, Debug)]
751struct AddSelectionsState {
752 above: bool,
753 stack: Vec<usize>,
754}
755
756#[derive(Clone)]
757struct SelectNextState {
758 query: AhoCorasick,
759 wordwise: bool,
760 done: bool,
761}
762
763impl std::fmt::Debug for SelectNextState {
764 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
765 f.debug_struct(std::any::type_name::<Self>())
766 .field("wordwise", &self.wordwise)
767 .field("done", &self.done)
768 .finish()
769 }
770}
771
772#[derive(Debug)]
773struct AutocloseRegion {
774 selection_id: usize,
775 range: Range<Anchor>,
776 pair: BracketPair,
777}
778
779#[derive(Debug)]
780struct SnippetState {
781 ranges: Vec<Vec<Range<Anchor>>>,
782 active_index: usize,
783}
784
785#[doc(hidden)]
786pub struct RenameState {
787 pub range: Range<Anchor>,
788 pub old_name: Arc<str>,
789 pub editor: View<Editor>,
790 block_id: CustomBlockId,
791}
792
793struct InvalidationStack<T>(Vec<T>);
794
795struct RegisteredInlineCompletionProvider {
796 provider: Arc<dyn InlineCompletionProviderHandle>,
797 _subscription: Subscription,
798}
799
800enum ContextMenu {
801 Completions(CompletionsMenu),
802 CodeActions(CodeActionsMenu),
803}
804
805impl ContextMenu {
806 fn select_first(
807 &mut self,
808 project: Option<&Model<Project>>,
809 cx: &mut ViewContext<Editor>,
810 ) -> bool {
811 if self.visible() {
812 match self {
813 ContextMenu::Completions(menu) => menu.select_first(project, cx),
814 ContextMenu::CodeActions(menu) => menu.select_first(cx),
815 }
816 true
817 } else {
818 false
819 }
820 }
821
822 fn select_prev(
823 &mut self,
824 project: Option<&Model<Project>>,
825 cx: &mut ViewContext<Editor>,
826 ) -> bool {
827 if self.visible() {
828 match self {
829 ContextMenu::Completions(menu) => menu.select_prev(project, cx),
830 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
831 }
832 true
833 } else {
834 false
835 }
836 }
837
838 fn select_next(
839 &mut self,
840 project: Option<&Model<Project>>,
841 cx: &mut ViewContext<Editor>,
842 ) -> bool {
843 if self.visible() {
844 match self {
845 ContextMenu::Completions(menu) => menu.select_next(project, cx),
846 ContextMenu::CodeActions(menu) => menu.select_next(cx),
847 }
848 true
849 } else {
850 false
851 }
852 }
853
854 fn select_last(
855 &mut self,
856 project: Option<&Model<Project>>,
857 cx: &mut ViewContext<Editor>,
858 ) -> bool {
859 if self.visible() {
860 match self {
861 ContextMenu::Completions(menu) => menu.select_last(project, cx),
862 ContextMenu::CodeActions(menu) => menu.select_last(cx),
863 }
864 true
865 } else {
866 false
867 }
868 }
869
870 fn visible(&self) -> bool {
871 match self {
872 ContextMenu::Completions(menu) => menu.visible(),
873 ContextMenu::CodeActions(menu) => menu.visible(),
874 }
875 }
876
877 fn render(
878 &self,
879 cursor_position: DisplayPoint,
880 style: &EditorStyle,
881 max_height: Pixels,
882 workspace: Option<WeakView<Workspace>>,
883 cx: &mut ViewContext<Editor>,
884 ) -> (ContextMenuOrigin, AnyElement) {
885 match self {
886 ContextMenu::Completions(menu) => (
887 ContextMenuOrigin::EditorPoint(cursor_position),
888 menu.render(style, max_height, workspace, cx),
889 ),
890 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, max_height, cx),
891 }
892 }
893}
894
895enum ContextMenuOrigin {
896 EditorPoint(DisplayPoint),
897 GutterIndicator(DisplayRow),
898}
899
900#[derive(Clone)]
901struct CompletionsMenu {
902 id: CompletionId,
903 initial_position: Anchor,
904 buffer: Model<Buffer>,
905 completions: Arc<RwLock<Box<[Completion]>>>,
906 match_candidates: Arc<[StringMatchCandidate]>,
907 matches: Arc<[StringMatch]>,
908 selected_item: usize,
909 scroll_handle: UniformListScrollHandle,
910 selected_completion_documentation_resolve_debounce: Arc<Mutex<DebouncedDelay>>,
911}
912
913impl CompletionsMenu {
914 fn select_first(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
915 self.selected_item = 0;
916 self.scroll_handle.scroll_to_item(self.selected_item);
917 self.attempt_resolve_selected_completion_documentation(project, cx);
918 cx.notify();
919 }
920
921 fn select_prev(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
922 if self.selected_item > 0 {
923 self.selected_item -= 1;
924 } else {
925 self.selected_item = self.matches.len() - 1;
926 }
927 self.scroll_handle.scroll_to_item(self.selected_item);
928 self.attempt_resolve_selected_completion_documentation(project, cx);
929 cx.notify();
930 }
931
932 fn select_next(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
933 if self.selected_item + 1 < self.matches.len() {
934 self.selected_item += 1;
935 } else {
936 self.selected_item = 0;
937 }
938 self.scroll_handle.scroll_to_item(self.selected_item);
939 self.attempt_resolve_selected_completion_documentation(project, cx);
940 cx.notify();
941 }
942
943 fn select_last(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
944 self.selected_item = self.matches.len() - 1;
945 self.scroll_handle.scroll_to_item(self.selected_item);
946 self.attempt_resolve_selected_completion_documentation(project, cx);
947 cx.notify();
948 }
949
950 fn pre_resolve_completion_documentation(
951 buffer: Model<Buffer>,
952 completions: Arc<RwLock<Box<[Completion]>>>,
953 matches: Arc<[StringMatch]>,
954 editor: &Editor,
955 cx: &mut ViewContext<Editor>,
956 ) -> Task<()> {
957 let settings = EditorSettings::get_global(cx);
958 if !settings.show_completion_documentation {
959 return Task::ready(());
960 }
961
962 let Some(provider) = editor.completion_provider.as_ref() else {
963 return Task::ready(());
964 };
965
966 let resolve_task = provider.resolve_completions(
967 buffer,
968 matches.iter().map(|m| m.candidate_id).collect(),
969 completions.clone(),
970 cx,
971 );
972
973 return cx.spawn(move |this, mut cx| async move {
974 if let Some(true) = resolve_task.await.log_err() {
975 this.update(&mut cx, |_, cx| cx.notify()).ok();
976 }
977 });
978 }
979
980 fn attempt_resolve_selected_completion_documentation(
981 &mut self,
982 project: Option<&Model<Project>>,
983 cx: &mut ViewContext<Editor>,
984 ) {
985 let settings = EditorSettings::get_global(cx);
986 if !settings.show_completion_documentation {
987 return;
988 }
989
990 let completion_index = self.matches[self.selected_item].candidate_id;
991 let Some(project) = project else {
992 return;
993 };
994
995 let resolve_task = project.update(cx, |project, cx| {
996 project.resolve_completions(
997 self.buffer.clone(),
998 vec![completion_index],
999 self.completions.clone(),
1000 cx,
1001 )
1002 });
1003
1004 let delay_ms =
1005 EditorSettings::get_global(cx).completion_documentation_secondary_query_debounce;
1006 let delay = Duration::from_millis(delay_ms);
1007
1008 self.selected_completion_documentation_resolve_debounce
1009 .lock()
1010 .fire_new(delay, cx, |_, cx| {
1011 cx.spawn(move |this, mut cx| async move {
1012 if let Some(true) = resolve_task.await.log_err() {
1013 this.update(&mut cx, |_, cx| cx.notify()).ok();
1014 }
1015 })
1016 });
1017 }
1018
1019 fn visible(&self) -> bool {
1020 !self.matches.is_empty()
1021 }
1022
1023 fn render(
1024 &self,
1025 style: &EditorStyle,
1026 max_height: Pixels,
1027 workspace: Option<WeakView<Workspace>>,
1028 cx: &mut ViewContext<Editor>,
1029 ) -> AnyElement {
1030 let settings = EditorSettings::get_global(cx);
1031 let show_completion_documentation = settings.show_completion_documentation;
1032
1033 let widest_completion_ix = self
1034 .matches
1035 .iter()
1036 .enumerate()
1037 .max_by_key(|(_, mat)| {
1038 let completions = self.completions.read();
1039 let completion = &completions[mat.candidate_id];
1040 let documentation = &completion.documentation;
1041
1042 let mut len = completion.label.text.chars().count();
1043 if let Some(Documentation::SingleLine(text)) = documentation {
1044 if show_completion_documentation {
1045 len += text.chars().count();
1046 }
1047 }
1048
1049 len
1050 })
1051 .map(|(ix, _)| ix);
1052
1053 let completions = self.completions.clone();
1054 let matches = self.matches.clone();
1055 let selected_item = self.selected_item;
1056 let style = style.clone();
1057
1058 let multiline_docs = if show_completion_documentation {
1059 let mat = &self.matches[selected_item];
1060 let multiline_docs = match &self.completions.read()[mat.candidate_id].documentation {
1061 Some(Documentation::MultiLinePlainText(text)) => {
1062 Some(div().child(SharedString::from(text.clone())))
1063 }
1064 Some(Documentation::MultiLineMarkdown(parsed)) if !parsed.text.is_empty() => {
1065 Some(div().child(render_parsed_markdown(
1066 "completions_markdown",
1067 parsed,
1068 &style,
1069 workspace,
1070 cx,
1071 )))
1072 }
1073 _ => None,
1074 };
1075 multiline_docs.map(|div| {
1076 div.id("multiline_docs")
1077 .max_h(max_height)
1078 .flex_1()
1079 .px_1p5()
1080 .py_1()
1081 .min_w(px(260.))
1082 .max_w(px(640.))
1083 .w(px(500.))
1084 .overflow_y_scroll()
1085 .occlude()
1086 })
1087 } else {
1088 None
1089 };
1090
1091 let list = uniform_list(
1092 cx.view().clone(),
1093 "completions",
1094 matches.len(),
1095 move |_editor, range, cx| {
1096 let start_ix = range.start;
1097 let completions_guard = completions.read();
1098
1099 matches[range]
1100 .iter()
1101 .enumerate()
1102 .map(|(ix, mat)| {
1103 let item_ix = start_ix + ix;
1104 let candidate_id = mat.candidate_id;
1105 let completion = &completions_guard[candidate_id];
1106
1107 let documentation = if show_completion_documentation {
1108 &completion.documentation
1109 } else {
1110 &None
1111 };
1112
1113 let highlights = gpui::combine_highlights(
1114 mat.ranges().map(|range| (range, FontWeight::BOLD.into())),
1115 styled_runs_for_code_label(&completion.label, &style.syntax).map(
1116 |(range, mut highlight)| {
1117 // Ignore font weight for syntax highlighting, as we'll use it
1118 // for fuzzy matches.
1119 highlight.font_weight = None;
1120
1121 if completion.lsp_completion.deprecated.unwrap_or(false) {
1122 highlight.strikethrough = Some(StrikethroughStyle {
1123 thickness: 1.0.into(),
1124 ..Default::default()
1125 });
1126 highlight.color = Some(cx.theme().colors().text_muted);
1127 }
1128
1129 (range, highlight)
1130 },
1131 ),
1132 );
1133 let completion_label = StyledText::new(completion.label.text.clone())
1134 .with_highlights(&style.text, highlights);
1135 let documentation_label =
1136 if let Some(Documentation::SingleLine(text)) = documentation {
1137 if text.trim().is_empty() {
1138 None
1139 } else {
1140 Some(
1141 Label::new(text.clone())
1142 .ml_4()
1143 .size(LabelSize::Small)
1144 .color(Color::Muted),
1145 )
1146 }
1147 } else {
1148 None
1149 };
1150
1151 div().min_w(px(220.)).max_w(px(540.)).child(
1152 ListItem::new(mat.candidate_id)
1153 .inset(true)
1154 .selected(item_ix == selected_item)
1155 .on_click(cx.listener(move |editor, _event, cx| {
1156 cx.stop_propagation();
1157 if let Some(task) = editor.confirm_completion(
1158 &ConfirmCompletion {
1159 item_ix: Some(item_ix),
1160 },
1161 cx,
1162 ) {
1163 task.detach_and_log_err(cx)
1164 }
1165 }))
1166 .child(h_flex().overflow_hidden().child(completion_label))
1167 .end_slot::<Label>(documentation_label),
1168 )
1169 })
1170 .collect()
1171 },
1172 )
1173 .occlude()
1174 .max_h(max_height)
1175 .track_scroll(self.scroll_handle.clone())
1176 .with_width_from_item(widest_completion_ix)
1177 .with_sizing_behavior(ListSizingBehavior::Infer);
1178
1179 Popover::new()
1180 .child(list)
1181 .when_some(multiline_docs, |popover, multiline_docs| {
1182 popover.aside(multiline_docs)
1183 })
1184 .into_any_element()
1185 }
1186
1187 pub async fn filter(&mut self, query: Option<&str>, executor: BackgroundExecutor) {
1188 let mut matches = if let Some(query) = query {
1189 fuzzy::match_strings(
1190 &self.match_candidates,
1191 query,
1192 query.chars().any(|c| c.is_uppercase()),
1193 100,
1194 &Default::default(),
1195 executor,
1196 )
1197 .await
1198 } else {
1199 self.match_candidates
1200 .iter()
1201 .enumerate()
1202 .map(|(candidate_id, candidate)| StringMatch {
1203 candidate_id,
1204 score: Default::default(),
1205 positions: Default::default(),
1206 string: candidate.string.clone(),
1207 })
1208 .collect()
1209 };
1210
1211 // Remove all candidates where the query's start does not match the start of any word in the candidate
1212 if let Some(query) = query {
1213 if let Some(query_start) = query.chars().next() {
1214 matches.retain(|string_match| {
1215 split_words(&string_match.string).any(|word| {
1216 // Check that the first codepoint of the word as lowercase matches the first
1217 // codepoint of the query as lowercase
1218 word.chars()
1219 .flat_map(|codepoint| codepoint.to_lowercase())
1220 .zip(query_start.to_lowercase())
1221 .all(|(word_cp, query_cp)| word_cp == query_cp)
1222 })
1223 });
1224 }
1225 }
1226
1227 let completions = self.completions.read();
1228 matches.sort_unstable_by_key(|mat| {
1229 // We do want to strike a balance here between what the language server tells us
1230 // to sort by (the sort_text) and what are "obvious" good matches (i.e. when you type
1231 // `Creat` and there is a local variable called `CreateComponent`).
1232 // So what we do is: we bucket all matches into two buckets
1233 // - Strong matches
1234 // - Weak matches
1235 // Strong matches are the ones with a high fuzzy-matcher score (the "obvious" matches)
1236 // and the Weak matches are the rest.
1237 //
1238 // For the strong matches, we sort by the language-servers score first and for the weak
1239 // matches, we prefer our fuzzy finder first.
1240 //
1241 // The thinking behind that: it's useless to take the sort_text the language-server gives
1242 // us into account when it's obviously a bad match.
1243
1244 #[derive(PartialEq, Eq, PartialOrd, Ord)]
1245 enum MatchScore<'a> {
1246 Strong {
1247 sort_text: Option<&'a str>,
1248 score: Reverse<OrderedFloat<f64>>,
1249 sort_key: (usize, &'a str),
1250 },
1251 Weak {
1252 score: Reverse<OrderedFloat<f64>>,
1253 sort_text: Option<&'a str>,
1254 sort_key: (usize, &'a str),
1255 },
1256 }
1257
1258 let completion = &completions[mat.candidate_id];
1259 let sort_key = completion.sort_key();
1260 let sort_text = completion.lsp_completion.sort_text.as_deref();
1261 let score = Reverse(OrderedFloat(mat.score));
1262
1263 if mat.score >= 0.2 {
1264 MatchScore::Strong {
1265 sort_text,
1266 score,
1267 sort_key,
1268 }
1269 } else {
1270 MatchScore::Weak {
1271 score,
1272 sort_text,
1273 sort_key,
1274 }
1275 }
1276 });
1277
1278 for mat in &mut matches {
1279 let completion = &completions[mat.candidate_id];
1280 mat.string.clone_from(&completion.label.text);
1281 for position in &mut mat.positions {
1282 *position += completion.label.filter_range.start;
1283 }
1284 }
1285 drop(completions);
1286
1287 self.matches = matches.into();
1288 self.selected_item = 0;
1289 }
1290}
1291
1292#[derive(Clone)]
1293struct CodeActionContents {
1294 tasks: Option<Arc<ResolvedTasks>>,
1295 actions: Option<Arc<[CodeAction]>>,
1296}
1297
1298impl CodeActionContents {
1299 fn len(&self) -> usize {
1300 match (&self.tasks, &self.actions) {
1301 (Some(tasks), Some(actions)) => actions.len() + tasks.templates.len(),
1302 (Some(tasks), None) => tasks.templates.len(),
1303 (None, Some(actions)) => actions.len(),
1304 (None, None) => 0,
1305 }
1306 }
1307
1308 fn is_empty(&self) -> bool {
1309 match (&self.tasks, &self.actions) {
1310 (Some(tasks), Some(actions)) => actions.is_empty() && tasks.templates.is_empty(),
1311 (Some(tasks), None) => tasks.templates.is_empty(),
1312 (None, Some(actions)) => actions.is_empty(),
1313 (None, None) => true,
1314 }
1315 }
1316
1317 fn iter(&self) -> impl Iterator<Item = CodeActionsItem> + '_ {
1318 self.tasks
1319 .iter()
1320 .flat_map(|tasks| {
1321 tasks
1322 .templates
1323 .iter()
1324 .map(|(kind, task)| CodeActionsItem::Task(kind.clone(), task.clone()))
1325 })
1326 .chain(self.actions.iter().flat_map(|actions| {
1327 actions
1328 .iter()
1329 .map(|action| CodeActionsItem::CodeAction(action.clone()))
1330 }))
1331 }
1332 fn get(&self, index: usize) -> Option<CodeActionsItem> {
1333 match (&self.tasks, &self.actions) {
1334 (Some(tasks), Some(actions)) => {
1335 if index < tasks.templates.len() {
1336 tasks
1337 .templates
1338 .get(index)
1339 .cloned()
1340 .map(|(kind, task)| CodeActionsItem::Task(kind, task))
1341 } else {
1342 actions
1343 .get(index - tasks.templates.len())
1344 .cloned()
1345 .map(CodeActionsItem::CodeAction)
1346 }
1347 }
1348 (Some(tasks), None) => tasks
1349 .templates
1350 .get(index)
1351 .cloned()
1352 .map(|(kind, task)| CodeActionsItem::Task(kind, task)),
1353 (None, Some(actions)) => actions.get(index).cloned().map(CodeActionsItem::CodeAction),
1354 (None, None) => None,
1355 }
1356 }
1357}
1358
1359#[allow(clippy::large_enum_variant)]
1360#[derive(Clone)]
1361enum CodeActionsItem {
1362 Task(TaskSourceKind, ResolvedTask),
1363 CodeAction(CodeAction),
1364}
1365
1366impl CodeActionsItem {
1367 fn as_task(&self) -> Option<&ResolvedTask> {
1368 let Self::Task(_, task) = self else {
1369 return None;
1370 };
1371 Some(task)
1372 }
1373 fn as_code_action(&self) -> Option<&CodeAction> {
1374 let Self::CodeAction(action) = self else {
1375 return None;
1376 };
1377 Some(action)
1378 }
1379 fn label(&self) -> String {
1380 match self {
1381 Self::CodeAction(action) => action.lsp_action.title.clone(),
1382 Self::Task(_, task) => task.resolved_label.clone(),
1383 }
1384 }
1385}
1386
1387struct CodeActionsMenu {
1388 actions: CodeActionContents,
1389 buffer: Model<Buffer>,
1390 selected_item: usize,
1391 scroll_handle: UniformListScrollHandle,
1392 deployed_from_indicator: Option<DisplayRow>,
1393}
1394
1395impl CodeActionsMenu {
1396 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
1397 self.selected_item = 0;
1398 self.scroll_handle.scroll_to_item(self.selected_item);
1399 cx.notify()
1400 }
1401
1402 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
1403 if self.selected_item > 0 {
1404 self.selected_item -= 1;
1405 } else {
1406 self.selected_item = self.actions.len() - 1;
1407 }
1408 self.scroll_handle.scroll_to_item(self.selected_item);
1409 cx.notify();
1410 }
1411
1412 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
1413 if self.selected_item + 1 < self.actions.len() {
1414 self.selected_item += 1;
1415 } else {
1416 self.selected_item = 0;
1417 }
1418 self.scroll_handle.scroll_to_item(self.selected_item);
1419 cx.notify();
1420 }
1421
1422 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
1423 self.selected_item = self.actions.len() - 1;
1424 self.scroll_handle.scroll_to_item(self.selected_item);
1425 cx.notify()
1426 }
1427
1428 fn visible(&self) -> bool {
1429 !self.actions.is_empty()
1430 }
1431
1432 fn render(
1433 &self,
1434 cursor_position: DisplayPoint,
1435 _style: &EditorStyle,
1436 max_height: Pixels,
1437 cx: &mut ViewContext<Editor>,
1438 ) -> (ContextMenuOrigin, AnyElement) {
1439 let actions = self.actions.clone();
1440 let selected_item = self.selected_item;
1441 let element = uniform_list(
1442 cx.view().clone(),
1443 "code_actions_menu",
1444 self.actions.len(),
1445 move |_this, range, cx| {
1446 actions
1447 .iter()
1448 .skip(range.start)
1449 .take(range.end - range.start)
1450 .enumerate()
1451 .map(|(ix, action)| {
1452 let item_ix = range.start + ix;
1453 let selected = selected_item == item_ix;
1454 let colors = cx.theme().colors();
1455 div()
1456 .px_2()
1457 .text_color(colors.text)
1458 .when(selected, |style| {
1459 style
1460 .bg(colors.element_active)
1461 .text_color(colors.text_accent)
1462 })
1463 .hover(|style| {
1464 style
1465 .bg(colors.element_hover)
1466 .text_color(colors.text_accent)
1467 })
1468 .whitespace_nowrap()
1469 .when_some(action.as_code_action(), |this, action| {
1470 this.on_mouse_down(
1471 MouseButton::Left,
1472 cx.listener(move |editor, _, cx| {
1473 cx.stop_propagation();
1474 if let Some(task) = editor.confirm_code_action(
1475 &ConfirmCodeAction {
1476 item_ix: Some(item_ix),
1477 },
1478 cx,
1479 ) {
1480 task.detach_and_log_err(cx)
1481 }
1482 }),
1483 )
1484 // TASK: It would be good to make lsp_action.title a SharedString to avoid allocating here.
1485 .child(SharedString::from(action.lsp_action.title.clone()))
1486 })
1487 .when_some(action.as_task(), |this, task| {
1488 this.on_mouse_down(
1489 MouseButton::Left,
1490 cx.listener(move |editor, _, cx| {
1491 cx.stop_propagation();
1492 if let Some(task) = editor.confirm_code_action(
1493 &ConfirmCodeAction {
1494 item_ix: Some(item_ix),
1495 },
1496 cx,
1497 ) {
1498 task.detach_and_log_err(cx)
1499 }
1500 }),
1501 )
1502 .child(SharedString::from(task.resolved_label.clone()))
1503 })
1504 })
1505 .collect()
1506 },
1507 )
1508 .elevation_1(cx)
1509 .px_2()
1510 .py_1()
1511 .max_h(max_height)
1512 .occlude()
1513 .track_scroll(self.scroll_handle.clone())
1514 .with_width_from_item(
1515 self.actions
1516 .iter()
1517 .enumerate()
1518 .max_by_key(|(_, action)| match action {
1519 CodeActionsItem::Task(_, task) => task.resolved_label.chars().count(),
1520 CodeActionsItem::CodeAction(action) => action.lsp_action.title.chars().count(),
1521 })
1522 .map(|(ix, _)| ix),
1523 )
1524 .with_sizing_behavior(ListSizingBehavior::Infer)
1525 .into_any_element();
1526
1527 let cursor_position = if let Some(row) = self.deployed_from_indicator {
1528 ContextMenuOrigin::GutterIndicator(row)
1529 } else {
1530 ContextMenuOrigin::EditorPoint(cursor_position)
1531 };
1532
1533 (cursor_position, element)
1534 }
1535}
1536
1537#[derive(Debug)]
1538struct ActiveDiagnosticGroup {
1539 primary_range: Range<Anchor>,
1540 primary_message: String,
1541 group_id: usize,
1542 blocks: HashMap<CustomBlockId, Diagnostic>,
1543 is_valid: bool,
1544}
1545
1546#[derive(Serialize, Deserialize, Clone, Debug)]
1547pub struct ClipboardSelection {
1548 pub len: usize,
1549 pub is_entire_line: bool,
1550 pub first_line_indent: u32,
1551}
1552
1553#[derive(Debug)]
1554pub(crate) struct NavigationData {
1555 cursor_anchor: Anchor,
1556 cursor_position: Point,
1557 scroll_anchor: ScrollAnchor,
1558 scroll_top_row: u32,
1559}
1560
1561enum GotoDefinitionKind {
1562 Symbol,
1563 Type,
1564 Implementation,
1565}
1566
1567#[derive(Debug, Clone)]
1568enum InlayHintRefreshReason {
1569 Toggle(bool),
1570 SettingsChange(InlayHintSettings),
1571 NewLinesShown,
1572 BufferEdited(HashSet<Arc<Language>>),
1573 RefreshRequested,
1574 ExcerptsRemoved(Vec<ExcerptId>),
1575}
1576
1577impl InlayHintRefreshReason {
1578 fn description(&self) -> &'static str {
1579 match self {
1580 Self::Toggle(_) => "toggle",
1581 Self::SettingsChange(_) => "settings change",
1582 Self::NewLinesShown => "new lines shown",
1583 Self::BufferEdited(_) => "buffer edited",
1584 Self::RefreshRequested => "refresh requested",
1585 Self::ExcerptsRemoved(_) => "excerpts removed",
1586 }
1587 }
1588}
1589
1590pub(crate) struct FocusedBlock {
1591 id: BlockId,
1592 focus_handle: WeakFocusHandle,
1593}
1594
1595impl Editor {
1596 pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
1597 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1598 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1599 Self::new(
1600 EditorMode::SingleLine { auto_width: false },
1601 buffer,
1602 None,
1603 false,
1604 cx,
1605 )
1606 }
1607
1608 pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
1609 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1610 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1611 Self::new(EditorMode::Full, buffer, None, false, cx)
1612 }
1613
1614 pub fn auto_width(cx: &mut ViewContext<Self>) -> Self {
1615 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1616 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1617 Self::new(
1618 EditorMode::SingleLine { auto_width: true },
1619 buffer,
1620 None,
1621 false,
1622 cx,
1623 )
1624 }
1625
1626 pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
1627 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1628 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1629 Self::new(
1630 EditorMode::AutoHeight { max_lines },
1631 buffer,
1632 None,
1633 false,
1634 cx,
1635 )
1636 }
1637
1638 pub fn for_buffer(
1639 buffer: Model<Buffer>,
1640 project: Option<Model<Project>>,
1641 cx: &mut ViewContext<Self>,
1642 ) -> Self {
1643 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1644 Self::new(EditorMode::Full, buffer, project, false, cx)
1645 }
1646
1647 pub fn for_multibuffer(
1648 buffer: Model<MultiBuffer>,
1649 project: Option<Model<Project>>,
1650 show_excerpt_controls: bool,
1651 cx: &mut ViewContext<Self>,
1652 ) -> Self {
1653 Self::new(EditorMode::Full, buffer, project, show_excerpt_controls, cx)
1654 }
1655
1656 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1657 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1658 let mut clone = Self::new(
1659 self.mode,
1660 self.buffer.clone(),
1661 self.project.clone(),
1662 show_excerpt_controls,
1663 cx,
1664 );
1665 self.display_map.update(cx, |display_map, cx| {
1666 let snapshot = display_map.snapshot(cx);
1667 clone.display_map.update(cx, |display_map, cx| {
1668 display_map.set_state(&snapshot, cx);
1669 });
1670 });
1671 clone.selections.clone_state(&self.selections);
1672 clone.scroll_manager.clone_state(&self.scroll_manager);
1673 clone.searchable = self.searchable;
1674 clone
1675 }
1676
1677 pub fn new(
1678 mode: EditorMode,
1679 buffer: Model<MultiBuffer>,
1680 project: Option<Model<Project>>,
1681 show_excerpt_controls: bool,
1682 cx: &mut ViewContext<Self>,
1683 ) -> Self {
1684 let style = cx.text_style();
1685 let font_size = style.font_size.to_pixels(cx.rem_size());
1686 let editor = cx.view().downgrade();
1687 let fold_placeholder = FoldPlaceholder {
1688 constrain_width: true,
1689 render: Arc::new(move |fold_id, fold_range, cx| {
1690 let editor = editor.clone();
1691 div()
1692 .id(fold_id)
1693 .bg(cx.theme().colors().ghost_element_background)
1694 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1695 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1696 .rounded_sm()
1697 .size_full()
1698 .cursor_pointer()
1699 .child("⋯")
1700 .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
1701 .on_click(move |_, cx| {
1702 editor
1703 .update(cx, |editor, cx| {
1704 editor.unfold_ranges(
1705 [fold_range.start..fold_range.end],
1706 true,
1707 false,
1708 cx,
1709 );
1710 cx.stop_propagation();
1711 })
1712 .ok();
1713 })
1714 .into_any()
1715 }),
1716 merge_adjacent: true,
1717 };
1718 let file_header_size = if show_excerpt_controls { 3 } else { 2 };
1719 let display_map = cx.new_model(|cx| {
1720 DisplayMap::new(
1721 buffer.clone(),
1722 style.font(),
1723 font_size,
1724 None,
1725 show_excerpt_controls,
1726 file_header_size,
1727 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1728 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1729 fold_placeholder,
1730 cx,
1731 )
1732 });
1733
1734 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1735
1736 let blink_manager = cx.new_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1737
1738 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1739 .then(|| language_settings::SoftWrap::PreferLine);
1740
1741 let mut project_subscriptions = Vec::new();
1742 if mode == EditorMode::Full {
1743 if let Some(project) = project.as_ref() {
1744 if buffer.read(cx).is_singleton() {
1745 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1746 cx.emit(EditorEvent::TitleChanged);
1747 }));
1748 }
1749 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1750 if let project::Event::RefreshInlayHints = event {
1751 editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1752 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1753 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1754 let focus_handle = editor.focus_handle(cx);
1755 if focus_handle.is_focused(cx) {
1756 let snapshot = buffer.read(cx).snapshot();
1757 for (range, snippet) in snippet_edits {
1758 let editor_range =
1759 language::range_from_lsp(*range).to_offset(&snapshot);
1760 editor
1761 .insert_snippet(&[editor_range], snippet.clone(), cx)
1762 .ok();
1763 }
1764 }
1765 }
1766 }
1767 }));
1768 let task_inventory = project.read(cx).task_inventory().clone();
1769 project_subscriptions.push(cx.observe(&task_inventory, |editor, _, cx| {
1770 editor.tasks_update_task = Some(editor.refresh_runnables(cx));
1771 }));
1772 }
1773 }
1774
1775 let inlay_hint_settings = inlay_hint_settings(
1776 selections.newest_anchor().head(),
1777 &buffer.read(cx).snapshot(cx),
1778 cx,
1779 );
1780 let focus_handle = cx.focus_handle();
1781 cx.on_focus(&focus_handle, Self::handle_focus).detach();
1782 cx.on_focus_in(&focus_handle, Self::handle_focus_in)
1783 .detach();
1784 cx.on_focus_out(&focus_handle, Self::handle_focus_out)
1785 .detach();
1786 cx.on_blur(&focus_handle, Self::handle_blur).detach();
1787
1788 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1789 Some(false)
1790 } else {
1791 None
1792 };
1793
1794 let mut this = Self {
1795 focus_handle,
1796 show_cursor_when_unfocused: false,
1797 last_focused_descendant: None,
1798 buffer: buffer.clone(),
1799 display_map: display_map.clone(),
1800 selections,
1801 scroll_manager: ScrollManager::new(cx),
1802 columnar_selection_tail: None,
1803 add_selections_state: None,
1804 select_next_state: None,
1805 select_prev_state: None,
1806 selection_history: Default::default(),
1807 autoclose_regions: Default::default(),
1808 snippet_stack: Default::default(),
1809 select_larger_syntax_node_stack: Vec::new(),
1810 ime_transaction: Default::default(),
1811 active_diagnostics: None,
1812 soft_wrap_mode_override,
1813 completion_provider: project.clone().map(|project| Box::new(project) as _),
1814 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1815 project,
1816 blink_manager: blink_manager.clone(),
1817 show_local_selections: true,
1818 mode,
1819 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1820 show_gutter: mode == EditorMode::Full,
1821 show_line_numbers: None,
1822 show_git_diff_gutter: None,
1823 show_code_actions: None,
1824 show_runnables: None,
1825 show_wrap_guides: None,
1826 redact_all: false,
1827 show_indent_guides,
1828 placeholder_text: None,
1829 highlight_order: 0,
1830 highlighted_rows: HashMap::default(),
1831 background_highlights: Default::default(),
1832 gutter_highlights: TreeMap::default(),
1833 scrollbar_marker_state: ScrollbarMarkerState::default(),
1834 active_indent_guides_state: ActiveIndentGuidesState::default(),
1835 nav_history: None,
1836 context_menu: RwLock::new(None),
1837 mouse_context_menu: None,
1838 completion_tasks: Default::default(),
1839 signature_help_state: SignatureHelpState::default(),
1840 auto_signature_help: None,
1841 find_all_references_task_sources: Vec::new(),
1842 next_completion_id: 0,
1843 completion_documentation_pre_resolve_debounce: DebouncedDelay::new(),
1844 next_inlay_id: 0,
1845 available_code_actions: Default::default(),
1846 code_actions_task: Default::default(),
1847 document_highlights_task: Default::default(),
1848 linked_editing_range_task: Default::default(),
1849 pending_rename: Default::default(),
1850 searchable: true,
1851 cursor_shape: Default::default(),
1852 current_line_highlight: None,
1853 autoindent_mode: Some(AutoindentMode::EachLine),
1854 collapse_matches: false,
1855 workspace: None,
1856 keymap_context_layers: Default::default(),
1857 input_enabled: true,
1858 use_modal_editing: mode == EditorMode::Full,
1859 read_only: false,
1860 use_autoclose: true,
1861 use_auto_surround: true,
1862 auto_replace_emoji_shortcode: false,
1863 leader_peer_id: None,
1864 remote_id: None,
1865 hover_state: Default::default(),
1866 hovered_link_state: Default::default(),
1867 inline_completion_provider: None,
1868 active_inline_completion: None,
1869 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1870 expanded_hunks: ExpandedHunks::default(),
1871 gutter_hovered: false,
1872 pixel_position_of_newest_cursor: None,
1873 last_bounds: None,
1874 expect_bounds_change: None,
1875 gutter_dimensions: GutterDimensions::default(),
1876 style: None,
1877 show_cursor_names: false,
1878 hovered_cursors: Default::default(),
1879 next_editor_action_id: EditorActionId::default(),
1880 editor_actions: Rc::default(),
1881 vim_replace_map: Default::default(),
1882 show_inline_completions: mode == EditorMode::Full,
1883 custom_context_menu: None,
1884 show_git_blame_gutter: false,
1885 show_git_blame_inline: false,
1886 show_selection_menu: None,
1887 show_git_blame_inline_delay_task: None,
1888 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1889 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1890 .session
1891 .restore_unsaved_buffers,
1892 blame: None,
1893 blame_subscription: None,
1894 file_header_size,
1895 tasks: Default::default(),
1896 _subscriptions: vec![
1897 cx.observe(&buffer, Self::on_buffer_changed),
1898 cx.subscribe(&buffer, Self::on_buffer_event),
1899 cx.observe(&display_map, Self::on_display_map_changed),
1900 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1901 cx.observe_global::<SettingsStore>(Self::settings_changed),
1902 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1903 cx.observe_window_activation(|editor, cx| {
1904 let active = cx.is_window_active();
1905 editor.blink_manager.update(cx, |blink_manager, cx| {
1906 if active {
1907 blink_manager.enable(cx);
1908 } else {
1909 blink_manager.show_cursor(cx);
1910 blink_manager.disable(cx);
1911 }
1912 });
1913 }),
1914 ],
1915 tasks_update_task: None,
1916 linked_edit_ranges: Default::default(),
1917 previous_search_ranges: None,
1918 breadcrumb_header: None,
1919 focused_block: None,
1920 };
1921 this.tasks_update_task = Some(this.refresh_runnables(cx));
1922 this._subscriptions.extend(project_subscriptions);
1923
1924 this.end_selection(cx);
1925 this.scroll_manager.show_scrollbar(cx);
1926
1927 if mode == EditorMode::Full {
1928 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1929 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1930
1931 if this.git_blame_inline_enabled {
1932 this.git_blame_inline_enabled = true;
1933 this.start_git_blame_inline(false, cx);
1934 }
1935 }
1936
1937 this.report_editor_event("open", None, cx);
1938 this
1939 }
1940
1941 pub fn mouse_menu_is_focused(&self, cx: &mut WindowContext) -> bool {
1942 self.mouse_context_menu
1943 .as_ref()
1944 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(cx))
1945 }
1946
1947 fn key_context(&self, cx: &AppContext) -> KeyContext {
1948 let mut key_context = KeyContext::new_with_defaults();
1949 key_context.add("Editor");
1950 let mode = match self.mode {
1951 EditorMode::SingleLine { .. } => "single_line",
1952 EditorMode::AutoHeight { .. } => "auto_height",
1953 EditorMode::Full => "full",
1954 };
1955
1956 if EditorSettings::get_global(cx).jupyter.enabled {
1957 key_context.add("jupyter");
1958 }
1959
1960 key_context.set("mode", mode);
1961 if self.pending_rename.is_some() {
1962 key_context.add("renaming");
1963 }
1964 if self.context_menu_visible() {
1965 match self.context_menu.read().as_ref() {
1966 Some(ContextMenu::Completions(_)) => {
1967 key_context.add("menu");
1968 key_context.add("showing_completions")
1969 }
1970 Some(ContextMenu::CodeActions(_)) => {
1971 key_context.add("menu");
1972 key_context.add("showing_code_actions")
1973 }
1974 None => {}
1975 }
1976 }
1977
1978 for layer in self.keymap_context_layers.values() {
1979 key_context.extend(layer);
1980 }
1981
1982 if let Some(extension) = self
1983 .buffer
1984 .read(cx)
1985 .as_singleton()
1986 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
1987 {
1988 key_context.set("extension", extension.to_string());
1989 }
1990
1991 if self.has_active_inline_completion(cx) {
1992 key_context.add("copilot_suggestion");
1993 key_context.add("inline_completion");
1994 }
1995
1996 key_context
1997 }
1998
1999 pub fn new_file(
2000 workspace: &mut Workspace,
2001 _: &workspace::NewFile,
2002 cx: &mut ViewContext<Workspace>,
2003 ) {
2004 Self::new_in_workspace(workspace, cx).detach_and_prompt_err(
2005 "Failed to create buffer",
2006 cx,
2007 |e, _| match e.error_code() {
2008 ErrorCode::RemoteUpgradeRequired => Some(format!(
2009 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2010 e.error_tag("required").unwrap_or("the latest version")
2011 )),
2012 _ => None,
2013 },
2014 );
2015 }
2016
2017 pub fn new_in_workspace(
2018 workspace: &mut Workspace,
2019 cx: &mut ViewContext<Workspace>,
2020 ) -> Task<Result<View<Editor>>> {
2021 let project = workspace.project().clone();
2022 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2023
2024 cx.spawn(|workspace, mut cx| async move {
2025 let buffer = create.await?;
2026 workspace.update(&mut cx, |workspace, cx| {
2027 let editor =
2028 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx));
2029 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
2030 editor
2031 })
2032 })
2033 }
2034
2035 pub fn new_file_in_direction(
2036 workspace: &mut Workspace,
2037 action: &workspace::NewFileInDirection,
2038 cx: &mut ViewContext<Workspace>,
2039 ) {
2040 let project = workspace.project().clone();
2041 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2042 let direction = action.0;
2043
2044 cx.spawn(|workspace, mut cx| async move {
2045 let buffer = create.await?;
2046 workspace.update(&mut cx, move |workspace, cx| {
2047 workspace.split_item(
2048 direction,
2049 Box::new(
2050 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)),
2051 ),
2052 cx,
2053 )
2054 })?;
2055 anyhow::Ok(())
2056 })
2057 .detach_and_prompt_err("Failed to create buffer", cx, |e, _| match e.error_code() {
2058 ErrorCode::RemoteUpgradeRequired => Some(format!(
2059 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2060 e.error_tag("required").unwrap_or("the latest version")
2061 )),
2062 _ => None,
2063 });
2064 }
2065
2066 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
2067 self.buffer.read(cx).replica_id()
2068 }
2069
2070 pub fn leader_peer_id(&self) -> Option<PeerId> {
2071 self.leader_peer_id
2072 }
2073
2074 pub fn buffer(&self) -> &Model<MultiBuffer> {
2075 &self.buffer
2076 }
2077
2078 pub fn workspace(&self) -> Option<View<Workspace>> {
2079 self.workspace.as_ref()?.0.upgrade()
2080 }
2081
2082 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
2083 self.buffer().read(cx).title(cx)
2084 }
2085
2086 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
2087 EditorSnapshot {
2088 mode: self.mode,
2089 show_gutter: self.show_gutter,
2090 show_line_numbers: self.show_line_numbers,
2091 show_git_diff_gutter: self.show_git_diff_gutter,
2092 show_code_actions: self.show_code_actions,
2093 show_runnables: self.show_runnables,
2094 render_git_blame_gutter: self.render_git_blame_gutter(cx),
2095 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
2096 scroll_anchor: self.scroll_manager.anchor(),
2097 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
2098 placeholder_text: self.placeholder_text.clone(),
2099 is_focused: self.focus_handle.is_focused(cx),
2100 current_line_highlight: self
2101 .current_line_highlight
2102 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
2103 gutter_hovered: self.gutter_hovered,
2104 }
2105 }
2106
2107 pub fn language_at<T: ToOffset>(&self, point: T, cx: &AppContext) -> Option<Arc<Language>> {
2108 self.buffer.read(cx).language_at(point, cx)
2109 }
2110
2111 pub fn file_at<T: ToOffset>(
2112 &self,
2113 point: T,
2114 cx: &AppContext,
2115 ) -> Option<Arc<dyn language::File>> {
2116 self.buffer.read(cx).read(cx).file_at(point).cloned()
2117 }
2118
2119 pub fn active_excerpt(
2120 &self,
2121 cx: &AppContext,
2122 ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
2123 self.buffer
2124 .read(cx)
2125 .excerpt_containing(self.selections.newest_anchor().head(), cx)
2126 }
2127
2128 pub fn mode(&self) -> EditorMode {
2129 self.mode
2130 }
2131
2132 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
2133 self.collaboration_hub.as_deref()
2134 }
2135
2136 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
2137 self.collaboration_hub = Some(hub);
2138 }
2139
2140 pub fn set_custom_context_menu(
2141 &mut self,
2142 f: impl 'static
2143 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
2144 ) {
2145 self.custom_context_menu = Some(Box::new(f))
2146 }
2147
2148 pub fn set_completion_provider(&mut self, provider: Box<dyn CompletionProvider>) {
2149 self.completion_provider = Some(provider);
2150 }
2151
2152 pub fn set_inline_completion_provider<T>(
2153 &mut self,
2154 provider: Option<Model<T>>,
2155 cx: &mut ViewContext<Self>,
2156 ) where
2157 T: InlineCompletionProvider,
2158 {
2159 self.inline_completion_provider =
2160 provider.map(|provider| RegisteredInlineCompletionProvider {
2161 _subscription: cx.observe(&provider, |this, _, cx| {
2162 if this.focus_handle.is_focused(cx) {
2163 this.update_visible_inline_completion(cx);
2164 }
2165 }),
2166 provider: Arc::new(provider),
2167 });
2168 self.refresh_inline_completion(false, cx);
2169 }
2170
2171 pub fn placeholder_text(&self, _cx: &WindowContext) -> Option<&str> {
2172 self.placeholder_text.as_deref()
2173 }
2174
2175 pub fn set_placeholder_text(
2176 &mut self,
2177 placeholder_text: impl Into<Arc<str>>,
2178 cx: &mut ViewContext<Self>,
2179 ) {
2180 let placeholder_text = Some(placeholder_text.into());
2181 if self.placeholder_text != placeholder_text {
2182 self.placeholder_text = placeholder_text;
2183 cx.notify();
2184 }
2185 }
2186
2187 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
2188 self.cursor_shape = cursor_shape;
2189
2190 // Disrupt blink for immediate user feedback that the cursor shape has changed
2191 self.blink_manager.update(cx, BlinkManager::show_cursor);
2192
2193 cx.notify();
2194 }
2195
2196 pub fn set_current_line_highlight(
2197 &mut self,
2198 current_line_highlight: Option<CurrentLineHighlight>,
2199 ) {
2200 self.current_line_highlight = current_line_highlight;
2201 }
2202
2203 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
2204 self.collapse_matches = collapse_matches;
2205 }
2206
2207 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
2208 if self.collapse_matches {
2209 return range.start..range.start;
2210 }
2211 range.clone()
2212 }
2213
2214 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
2215 if self.display_map.read(cx).clip_at_line_ends != clip {
2216 self.display_map
2217 .update(cx, |map, _| map.clip_at_line_ends = clip);
2218 }
2219 }
2220
2221 pub fn set_keymap_context_layer<Tag: 'static>(
2222 &mut self,
2223 context: KeyContext,
2224 cx: &mut ViewContext<Self>,
2225 ) {
2226 self.keymap_context_layers
2227 .insert(TypeId::of::<Tag>(), context);
2228 cx.notify();
2229 }
2230
2231 pub fn remove_keymap_context_layer<Tag: 'static>(&mut self, cx: &mut ViewContext<Self>) {
2232 self.keymap_context_layers.remove(&TypeId::of::<Tag>());
2233 cx.notify();
2234 }
2235
2236 pub fn set_input_enabled(&mut self, input_enabled: bool) {
2237 self.input_enabled = input_enabled;
2238 }
2239
2240 pub fn set_autoindent(&mut self, autoindent: bool) {
2241 if autoindent {
2242 self.autoindent_mode = Some(AutoindentMode::EachLine);
2243 } else {
2244 self.autoindent_mode = None;
2245 }
2246 }
2247
2248 pub fn read_only(&self, cx: &AppContext) -> bool {
2249 self.read_only || self.buffer.read(cx).read_only()
2250 }
2251
2252 pub fn set_read_only(&mut self, read_only: bool) {
2253 self.read_only = read_only;
2254 }
2255
2256 pub fn set_use_autoclose(&mut self, autoclose: bool) {
2257 self.use_autoclose = autoclose;
2258 }
2259
2260 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
2261 self.use_auto_surround = auto_surround;
2262 }
2263
2264 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
2265 self.auto_replace_emoji_shortcode = auto_replace;
2266 }
2267
2268 pub fn set_show_inline_completions(&mut self, show_inline_completions: bool) {
2269 self.show_inline_completions = show_inline_completions;
2270 }
2271
2272 pub fn set_use_modal_editing(&mut self, to: bool) {
2273 self.use_modal_editing = to;
2274 }
2275
2276 pub fn use_modal_editing(&self) -> bool {
2277 self.use_modal_editing
2278 }
2279
2280 fn selections_did_change(
2281 &mut self,
2282 local: bool,
2283 old_cursor_position: &Anchor,
2284 show_completions: bool,
2285 cx: &mut ViewContext<Self>,
2286 ) {
2287 // Copy selections to primary selection buffer
2288 #[cfg(target_os = "linux")]
2289 if local {
2290 let selections = self.selections.all::<usize>(cx);
2291 let buffer_handle = self.buffer.read(cx).read(cx);
2292
2293 let mut text = String::new();
2294 for (index, selection) in selections.iter().enumerate() {
2295 let text_for_selection = buffer_handle
2296 .text_for_range(selection.start..selection.end)
2297 .collect::<String>();
2298
2299 text.push_str(&text_for_selection);
2300 if index != selections.len() - 1 {
2301 text.push('\n');
2302 }
2303 }
2304
2305 if !text.is_empty() {
2306 cx.write_to_primary(ClipboardItem::new(text));
2307 }
2308 }
2309
2310 if self.focus_handle.is_focused(cx) && self.leader_peer_id.is_none() {
2311 self.buffer.update(cx, |buffer, cx| {
2312 buffer.set_active_selections(
2313 &self.selections.disjoint_anchors(),
2314 self.selections.line_mode,
2315 self.cursor_shape,
2316 cx,
2317 )
2318 });
2319 }
2320 let display_map = self
2321 .display_map
2322 .update(cx, |display_map, cx| display_map.snapshot(cx));
2323 let buffer = &display_map.buffer_snapshot;
2324 self.add_selections_state = None;
2325 self.select_next_state = None;
2326 self.select_prev_state = None;
2327 self.select_larger_syntax_node_stack.clear();
2328 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2329 self.snippet_stack
2330 .invalidate(&self.selections.disjoint_anchors(), buffer);
2331 self.take_rename(false, cx);
2332
2333 let new_cursor_position = self.selections.newest_anchor().head();
2334
2335 self.push_to_nav_history(
2336 *old_cursor_position,
2337 Some(new_cursor_position.to_point(buffer)),
2338 cx,
2339 );
2340
2341 if local {
2342 let new_cursor_position = self.selections.newest_anchor().head();
2343 let mut context_menu = self.context_menu.write();
2344 let completion_menu = match context_menu.as_ref() {
2345 Some(ContextMenu::Completions(menu)) => Some(menu),
2346
2347 _ => {
2348 *context_menu = None;
2349 None
2350 }
2351 };
2352
2353 if let Some(completion_menu) = completion_menu {
2354 let cursor_position = new_cursor_position.to_offset(buffer);
2355 let (word_range, kind) = buffer.surrounding_word(completion_menu.initial_position);
2356 if kind == Some(CharKind::Word)
2357 && word_range.to_inclusive().contains(&cursor_position)
2358 {
2359 let mut completion_menu = completion_menu.clone();
2360 drop(context_menu);
2361
2362 let query = Self::completion_query(buffer, cursor_position);
2363 cx.spawn(move |this, mut cx| async move {
2364 completion_menu
2365 .filter(query.as_deref(), cx.background_executor().clone())
2366 .await;
2367
2368 this.update(&mut cx, |this, cx| {
2369 let mut context_menu = this.context_menu.write();
2370 let Some(ContextMenu::Completions(menu)) = context_menu.as_ref() else {
2371 return;
2372 };
2373
2374 if menu.id > completion_menu.id {
2375 return;
2376 }
2377
2378 *context_menu = Some(ContextMenu::Completions(completion_menu));
2379 drop(context_menu);
2380 cx.notify();
2381 })
2382 })
2383 .detach();
2384
2385 if show_completions {
2386 self.show_completions(&ShowCompletions { trigger: None }, cx);
2387 }
2388 } else {
2389 drop(context_menu);
2390 self.hide_context_menu(cx);
2391 }
2392 } else {
2393 drop(context_menu);
2394 }
2395
2396 hide_hover(self, cx);
2397
2398 if old_cursor_position.to_display_point(&display_map).row()
2399 != new_cursor_position.to_display_point(&display_map).row()
2400 {
2401 self.available_code_actions.take();
2402 }
2403 self.refresh_code_actions(cx);
2404 self.refresh_document_highlights(cx);
2405 refresh_matching_bracket_highlights(self, cx);
2406 self.discard_inline_completion(false, cx);
2407 linked_editing_ranges::refresh_linked_ranges(self, cx);
2408 if self.git_blame_inline_enabled {
2409 self.start_inline_blame_timer(cx);
2410 }
2411 }
2412
2413 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2414 cx.emit(EditorEvent::SelectionsChanged { local });
2415
2416 if self.selections.disjoint_anchors().len() == 1 {
2417 cx.emit(SearchEvent::ActiveMatchChanged)
2418 }
2419 cx.notify();
2420 }
2421
2422 pub fn change_selections<R>(
2423 &mut self,
2424 autoscroll: Option<Autoscroll>,
2425 cx: &mut ViewContext<Self>,
2426 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2427 ) -> R {
2428 self.change_selections_inner(autoscroll, true, cx, change)
2429 }
2430
2431 pub fn change_selections_inner<R>(
2432 &mut self,
2433 autoscroll: Option<Autoscroll>,
2434 request_completions: bool,
2435 cx: &mut ViewContext<Self>,
2436 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2437 ) -> R {
2438 let old_cursor_position = self.selections.newest_anchor().head();
2439 self.push_to_selection_history();
2440
2441 let (changed, result) = self.selections.change_with(cx, change);
2442
2443 if changed {
2444 if let Some(autoscroll) = autoscroll {
2445 self.request_autoscroll(autoscroll, cx);
2446 }
2447 self.selections_did_change(true, &old_cursor_position, request_completions, cx);
2448
2449 if self.should_open_signature_help_automatically(
2450 &old_cursor_position,
2451 self.signature_help_state.backspace_pressed(),
2452 cx,
2453 ) {
2454 self.show_signature_help(&ShowSignatureHelp, cx);
2455 }
2456 self.signature_help_state.set_backspace_pressed(false);
2457 }
2458
2459 result
2460 }
2461
2462 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2463 where
2464 I: IntoIterator<Item = (Range<S>, T)>,
2465 S: ToOffset,
2466 T: Into<Arc<str>>,
2467 {
2468 if self.read_only(cx) {
2469 return;
2470 }
2471
2472 self.buffer
2473 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2474 }
2475
2476 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2477 where
2478 I: IntoIterator<Item = (Range<S>, T)>,
2479 S: ToOffset,
2480 T: Into<Arc<str>>,
2481 {
2482 if self.read_only(cx) {
2483 return;
2484 }
2485
2486 self.buffer.update(cx, |buffer, cx| {
2487 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2488 });
2489 }
2490
2491 pub fn edit_with_block_indent<I, S, T>(
2492 &mut self,
2493 edits: I,
2494 original_indent_columns: Vec<u32>,
2495 cx: &mut ViewContext<Self>,
2496 ) where
2497 I: IntoIterator<Item = (Range<S>, T)>,
2498 S: ToOffset,
2499 T: Into<Arc<str>>,
2500 {
2501 if self.read_only(cx) {
2502 return;
2503 }
2504
2505 self.buffer.update(cx, |buffer, cx| {
2506 buffer.edit(
2507 edits,
2508 Some(AutoindentMode::Block {
2509 original_indent_columns,
2510 }),
2511 cx,
2512 )
2513 });
2514 }
2515
2516 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
2517 self.hide_context_menu(cx);
2518
2519 match phase {
2520 SelectPhase::Begin {
2521 position,
2522 add,
2523 click_count,
2524 } => self.begin_selection(position, add, click_count, cx),
2525 SelectPhase::BeginColumnar {
2526 position,
2527 goal_column,
2528 reset,
2529 } => self.begin_columnar_selection(position, goal_column, reset, cx),
2530 SelectPhase::Extend {
2531 position,
2532 click_count,
2533 } => self.extend_selection(position, click_count, cx),
2534 SelectPhase::Update {
2535 position,
2536 goal_column,
2537 scroll_delta,
2538 } => self.update_selection(position, goal_column, scroll_delta, cx),
2539 SelectPhase::End => self.end_selection(cx),
2540 }
2541 }
2542
2543 fn extend_selection(
2544 &mut self,
2545 position: DisplayPoint,
2546 click_count: usize,
2547 cx: &mut ViewContext<Self>,
2548 ) {
2549 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2550 let tail = self.selections.newest::<usize>(cx).tail();
2551 self.begin_selection(position, false, click_count, cx);
2552
2553 let position = position.to_offset(&display_map, Bias::Left);
2554 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2555
2556 let mut pending_selection = self
2557 .selections
2558 .pending_anchor()
2559 .expect("extend_selection not called with pending selection");
2560 if position >= tail {
2561 pending_selection.start = tail_anchor;
2562 } else {
2563 pending_selection.end = tail_anchor;
2564 pending_selection.reversed = true;
2565 }
2566
2567 let mut pending_mode = self.selections.pending_mode().unwrap();
2568 match &mut pending_mode {
2569 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2570 _ => {}
2571 }
2572
2573 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
2574 s.set_pending(pending_selection, pending_mode)
2575 });
2576 }
2577
2578 fn begin_selection(
2579 &mut self,
2580 position: DisplayPoint,
2581 add: bool,
2582 click_count: usize,
2583 cx: &mut ViewContext<Self>,
2584 ) {
2585 if !self.focus_handle.is_focused(cx) {
2586 self.last_focused_descendant = None;
2587 cx.focus(&self.focus_handle);
2588 }
2589
2590 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2591 let buffer = &display_map.buffer_snapshot;
2592 let newest_selection = self.selections.newest_anchor().clone();
2593 let position = display_map.clip_point(position, Bias::Left);
2594
2595 let start;
2596 let end;
2597 let mode;
2598 let auto_scroll;
2599 match click_count {
2600 1 => {
2601 start = buffer.anchor_before(position.to_point(&display_map));
2602 end = start;
2603 mode = SelectMode::Character;
2604 auto_scroll = true;
2605 }
2606 2 => {
2607 let range = movement::surrounding_word(&display_map, position);
2608 start = buffer.anchor_before(range.start.to_point(&display_map));
2609 end = buffer.anchor_before(range.end.to_point(&display_map));
2610 mode = SelectMode::Word(start..end);
2611 auto_scroll = true;
2612 }
2613 3 => {
2614 let position = display_map
2615 .clip_point(position, Bias::Left)
2616 .to_point(&display_map);
2617 let line_start = display_map.prev_line_boundary(position).0;
2618 let next_line_start = buffer.clip_point(
2619 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2620 Bias::Left,
2621 );
2622 start = buffer.anchor_before(line_start);
2623 end = buffer.anchor_before(next_line_start);
2624 mode = SelectMode::Line(start..end);
2625 auto_scroll = true;
2626 }
2627 _ => {
2628 start = buffer.anchor_before(0);
2629 end = buffer.anchor_before(buffer.len());
2630 mode = SelectMode::All;
2631 auto_scroll = false;
2632 }
2633 }
2634
2635 let point_to_delete: Option<usize> = {
2636 let selected_points: Vec<Selection<Point>> =
2637 self.selections.disjoint_in_range(start..end, cx);
2638
2639 if !add || click_count > 1 {
2640 None
2641 } else if selected_points.len() > 0 {
2642 Some(selected_points[0].id)
2643 } else {
2644 let clicked_point_already_selected =
2645 self.selections.disjoint.iter().find(|selection| {
2646 selection.start.to_point(buffer) == start.to_point(buffer)
2647 || selection.end.to_point(buffer) == end.to_point(buffer)
2648 });
2649
2650 if let Some(selection) = clicked_point_already_selected {
2651 Some(selection.id)
2652 } else {
2653 None
2654 }
2655 }
2656 };
2657
2658 let selections_count = self.selections.count();
2659
2660 self.change_selections(auto_scroll.then(|| Autoscroll::newest()), cx, |s| {
2661 if let Some(point_to_delete) = point_to_delete {
2662 s.delete(point_to_delete);
2663
2664 if selections_count == 1 {
2665 s.set_pending_anchor_range(start..end, mode);
2666 }
2667 } else {
2668 if !add {
2669 s.clear_disjoint();
2670 } else if click_count > 1 {
2671 s.delete(newest_selection.id)
2672 }
2673
2674 s.set_pending_anchor_range(start..end, mode);
2675 }
2676 });
2677 }
2678
2679 fn begin_columnar_selection(
2680 &mut self,
2681 position: DisplayPoint,
2682 goal_column: u32,
2683 reset: bool,
2684 cx: &mut ViewContext<Self>,
2685 ) {
2686 if !self.focus_handle.is_focused(cx) {
2687 self.last_focused_descendant = None;
2688 cx.focus(&self.focus_handle);
2689 }
2690
2691 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2692
2693 if reset {
2694 let pointer_position = display_map
2695 .buffer_snapshot
2696 .anchor_before(position.to_point(&display_map));
2697
2698 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
2699 s.clear_disjoint();
2700 s.set_pending_anchor_range(
2701 pointer_position..pointer_position,
2702 SelectMode::Character,
2703 );
2704 });
2705 }
2706
2707 let tail = self.selections.newest::<Point>(cx).tail();
2708 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2709
2710 if !reset {
2711 self.select_columns(
2712 tail.to_display_point(&display_map),
2713 position,
2714 goal_column,
2715 &display_map,
2716 cx,
2717 );
2718 }
2719 }
2720
2721 fn update_selection(
2722 &mut self,
2723 position: DisplayPoint,
2724 goal_column: u32,
2725 scroll_delta: gpui::Point<f32>,
2726 cx: &mut ViewContext<Self>,
2727 ) {
2728 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2729
2730 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2731 let tail = tail.to_display_point(&display_map);
2732 self.select_columns(tail, position, goal_column, &display_map, cx);
2733 } else if let Some(mut pending) = self.selections.pending_anchor() {
2734 let buffer = self.buffer.read(cx).snapshot(cx);
2735 let head;
2736 let tail;
2737 let mode = self.selections.pending_mode().unwrap();
2738 match &mode {
2739 SelectMode::Character => {
2740 head = position.to_point(&display_map);
2741 tail = pending.tail().to_point(&buffer);
2742 }
2743 SelectMode::Word(original_range) => {
2744 let original_display_range = original_range.start.to_display_point(&display_map)
2745 ..original_range.end.to_display_point(&display_map);
2746 let original_buffer_range = original_display_range.start.to_point(&display_map)
2747 ..original_display_range.end.to_point(&display_map);
2748 if movement::is_inside_word(&display_map, position)
2749 || original_display_range.contains(&position)
2750 {
2751 let word_range = movement::surrounding_word(&display_map, position);
2752 if word_range.start < original_display_range.start {
2753 head = word_range.start.to_point(&display_map);
2754 } else {
2755 head = word_range.end.to_point(&display_map);
2756 }
2757 } else {
2758 head = position.to_point(&display_map);
2759 }
2760
2761 if head <= original_buffer_range.start {
2762 tail = original_buffer_range.end;
2763 } else {
2764 tail = original_buffer_range.start;
2765 }
2766 }
2767 SelectMode::Line(original_range) => {
2768 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2769
2770 let position = display_map
2771 .clip_point(position, Bias::Left)
2772 .to_point(&display_map);
2773 let line_start = display_map.prev_line_boundary(position).0;
2774 let next_line_start = buffer.clip_point(
2775 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2776 Bias::Left,
2777 );
2778
2779 if line_start < original_range.start {
2780 head = line_start
2781 } else {
2782 head = next_line_start
2783 }
2784
2785 if head <= original_range.start {
2786 tail = original_range.end;
2787 } else {
2788 tail = original_range.start;
2789 }
2790 }
2791 SelectMode::All => {
2792 return;
2793 }
2794 };
2795
2796 if head < tail {
2797 pending.start = buffer.anchor_before(head);
2798 pending.end = buffer.anchor_before(tail);
2799 pending.reversed = true;
2800 } else {
2801 pending.start = buffer.anchor_before(tail);
2802 pending.end = buffer.anchor_before(head);
2803 pending.reversed = false;
2804 }
2805
2806 self.change_selections(None, cx, |s| {
2807 s.set_pending(pending, mode);
2808 });
2809 } else {
2810 log::error!("update_selection dispatched with no pending selection");
2811 return;
2812 }
2813
2814 self.apply_scroll_delta(scroll_delta, cx);
2815 cx.notify();
2816 }
2817
2818 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
2819 self.columnar_selection_tail.take();
2820 if self.selections.pending_anchor().is_some() {
2821 let selections = self.selections.all::<usize>(cx);
2822 self.change_selections(None, cx, |s| {
2823 s.select(selections);
2824 s.clear_pending();
2825 });
2826 }
2827 }
2828
2829 fn select_columns(
2830 &mut self,
2831 tail: DisplayPoint,
2832 head: DisplayPoint,
2833 goal_column: u32,
2834 display_map: &DisplaySnapshot,
2835 cx: &mut ViewContext<Self>,
2836 ) {
2837 let start_row = cmp::min(tail.row(), head.row());
2838 let end_row = cmp::max(tail.row(), head.row());
2839 let start_column = cmp::min(tail.column(), goal_column);
2840 let end_column = cmp::max(tail.column(), goal_column);
2841 let reversed = start_column < tail.column();
2842
2843 let selection_ranges = (start_row.0..=end_row.0)
2844 .map(DisplayRow)
2845 .filter_map(|row| {
2846 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2847 let start = display_map
2848 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2849 .to_point(display_map);
2850 let end = display_map
2851 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2852 .to_point(display_map);
2853 if reversed {
2854 Some(end..start)
2855 } else {
2856 Some(start..end)
2857 }
2858 } else {
2859 None
2860 }
2861 })
2862 .collect::<Vec<_>>();
2863
2864 self.change_selections(None, cx, |s| {
2865 s.select_ranges(selection_ranges);
2866 });
2867 cx.notify();
2868 }
2869
2870 pub fn has_pending_nonempty_selection(&self) -> bool {
2871 let pending_nonempty_selection = match self.selections.pending_anchor() {
2872 Some(Selection { start, end, .. }) => start != end,
2873 None => false,
2874 };
2875
2876 pending_nonempty_selection
2877 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2878 }
2879
2880 pub fn has_pending_selection(&self) -> bool {
2881 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2882 }
2883
2884 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
2885 if self.clear_clicked_diff_hunks(cx) {
2886 cx.notify();
2887 return;
2888 }
2889 if self.dismiss_menus_and_popups(true, cx) {
2890 return;
2891 }
2892
2893 if self.mode == EditorMode::Full {
2894 if self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel()) {
2895 return;
2896 }
2897 }
2898
2899 cx.propagate();
2900 }
2901
2902 pub fn dismiss_menus_and_popups(
2903 &mut self,
2904 should_report_inline_completion_event: bool,
2905 cx: &mut ViewContext<Self>,
2906 ) -> bool {
2907 if self.take_rename(false, cx).is_some() {
2908 return true;
2909 }
2910
2911 if hide_hover(self, cx) {
2912 return true;
2913 }
2914
2915 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2916 return true;
2917 }
2918
2919 if self.hide_context_menu(cx).is_some() {
2920 return true;
2921 }
2922
2923 if self.mouse_context_menu.take().is_some() {
2924 return true;
2925 }
2926
2927 if self.discard_inline_completion(should_report_inline_completion_event, cx) {
2928 return true;
2929 }
2930
2931 if self.snippet_stack.pop().is_some() {
2932 return true;
2933 }
2934
2935 if self.mode == EditorMode::Full {
2936 if self.active_diagnostics.is_some() {
2937 self.dismiss_diagnostics(cx);
2938 return true;
2939 }
2940 }
2941
2942 false
2943 }
2944
2945 fn linked_editing_ranges_for(
2946 &self,
2947 selection: Range<text::Anchor>,
2948 cx: &AppContext,
2949 ) -> Option<HashMap<Model<Buffer>, Vec<Range<text::Anchor>>>> {
2950 if self.linked_edit_ranges.is_empty() {
2951 return None;
2952 }
2953 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2954 selection.end.buffer_id.and_then(|end_buffer_id| {
2955 if selection.start.buffer_id != Some(end_buffer_id) {
2956 return None;
2957 }
2958 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2959 let snapshot = buffer.read(cx).snapshot();
2960 self.linked_edit_ranges
2961 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2962 .map(|ranges| (ranges, snapshot, buffer))
2963 })?;
2964 use text::ToOffset as TO;
2965 // find offset from the start of current range to current cursor position
2966 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2967
2968 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2969 let start_difference = start_offset - start_byte_offset;
2970 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2971 let end_difference = end_offset - start_byte_offset;
2972 // Current range has associated linked ranges.
2973 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2974 for range in linked_ranges.iter() {
2975 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2976 let end_offset = start_offset + end_difference;
2977 let start_offset = start_offset + start_difference;
2978 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2979 continue;
2980 }
2981 let start = buffer_snapshot.anchor_after(start_offset);
2982 let end = buffer_snapshot.anchor_after(end_offset);
2983 linked_edits
2984 .entry(buffer.clone())
2985 .or_default()
2986 .push(start..end);
2987 }
2988 Some(linked_edits)
2989 }
2990
2991 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2992 let text: Arc<str> = text.into();
2993
2994 if self.read_only(cx) {
2995 return;
2996 }
2997
2998 let selections = self.selections.all_adjusted(cx);
2999 let mut bracket_inserted = false;
3000 let mut edits = Vec::new();
3001 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3002 let mut new_selections = Vec::with_capacity(selections.len());
3003 let mut new_autoclose_regions = Vec::new();
3004 let snapshot = self.buffer.read(cx).read(cx);
3005
3006 for (selection, autoclose_region) in
3007 self.selections_with_autoclose_regions(selections, &snapshot)
3008 {
3009 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
3010 // Determine if the inserted text matches the opening or closing
3011 // bracket of any of this language's bracket pairs.
3012 let mut bracket_pair = None;
3013 let mut is_bracket_pair_start = false;
3014 let mut is_bracket_pair_end = false;
3015 if !text.is_empty() {
3016 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
3017 // and they are removing the character that triggered IME popup.
3018 for (pair, enabled) in scope.brackets() {
3019 if !pair.close && !pair.surround {
3020 continue;
3021 }
3022
3023 if enabled && pair.start.ends_with(text.as_ref()) {
3024 bracket_pair = Some(pair.clone());
3025 is_bracket_pair_start = true;
3026 break;
3027 }
3028 if pair.end.as_str() == text.as_ref() {
3029 bracket_pair = Some(pair.clone());
3030 is_bracket_pair_end = true;
3031 break;
3032 }
3033 }
3034 }
3035
3036 if let Some(bracket_pair) = bracket_pair {
3037 let snapshot_settings = snapshot.settings_at(selection.start, cx);
3038 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
3039 let auto_surround =
3040 self.use_auto_surround && snapshot_settings.use_auto_surround;
3041 if selection.is_empty() {
3042 if is_bracket_pair_start {
3043 let prefix_len = bracket_pair.start.len() - text.len();
3044
3045 // If the inserted text is a suffix of an opening bracket and the
3046 // selection is preceded by the rest of the opening bracket, then
3047 // insert the closing bracket.
3048 let following_text_allows_autoclose = snapshot
3049 .chars_at(selection.start)
3050 .next()
3051 .map_or(true, |c| scope.should_autoclose_before(c));
3052 let preceding_text_matches_prefix = prefix_len == 0
3053 || (selection.start.column >= (prefix_len as u32)
3054 && snapshot.contains_str_at(
3055 Point::new(
3056 selection.start.row,
3057 selection.start.column - (prefix_len as u32),
3058 ),
3059 &bracket_pair.start[..prefix_len],
3060 ));
3061
3062 if autoclose
3063 && bracket_pair.close
3064 && following_text_allows_autoclose
3065 && preceding_text_matches_prefix
3066 {
3067 let anchor = snapshot.anchor_before(selection.end);
3068 new_selections.push((selection.map(|_| anchor), text.len()));
3069 new_autoclose_regions.push((
3070 anchor,
3071 text.len(),
3072 selection.id,
3073 bracket_pair.clone(),
3074 ));
3075 edits.push((
3076 selection.range(),
3077 format!("{}{}", text, bracket_pair.end).into(),
3078 ));
3079 bracket_inserted = true;
3080 continue;
3081 }
3082 }
3083
3084 if let Some(region) = autoclose_region {
3085 // If the selection is followed by an auto-inserted closing bracket,
3086 // then don't insert that closing bracket again; just move the selection
3087 // past the closing bracket.
3088 let should_skip = selection.end == region.range.end.to_point(&snapshot)
3089 && text.as_ref() == region.pair.end.as_str();
3090 if should_skip {
3091 let anchor = snapshot.anchor_after(selection.end);
3092 new_selections
3093 .push((selection.map(|_| anchor), region.pair.end.len()));
3094 continue;
3095 }
3096 }
3097
3098 let always_treat_brackets_as_autoclosed = snapshot
3099 .settings_at(selection.start, cx)
3100 .always_treat_brackets_as_autoclosed;
3101 if always_treat_brackets_as_autoclosed
3102 && is_bracket_pair_end
3103 && snapshot.contains_str_at(selection.end, text.as_ref())
3104 {
3105 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
3106 // and the inserted text is a closing bracket and the selection is followed
3107 // by the closing bracket then move the selection past the closing bracket.
3108 let anchor = snapshot.anchor_after(selection.end);
3109 new_selections.push((selection.map(|_| anchor), text.len()));
3110 continue;
3111 }
3112 }
3113 // If an opening bracket is 1 character long and is typed while
3114 // text is selected, then surround that text with the bracket pair.
3115 else if auto_surround
3116 && bracket_pair.surround
3117 && is_bracket_pair_start
3118 && bracket_pair.start.chars().count() == 1
3119 {
3120 edits.push((selection.start..selection.start, text.clone()));
3121 edits.push((
3122 selection.end..selection.end,
3123 bracket_pair.end.as_str().into(),
3124 ));
3125 bracket_inserted = true;
3126 new_selections.push((
3127 Selection {
3128 id: selection.id,
3129 start: snapshot.anchor_after(selection.start),
3130 end: snapshot.anchor_before(selection.end),
3131 reversed: selection.reversed,
3132 goal: selection.goal,
3133 },
3134 0,
3135 ));
3136 continue;
3137 }
3138 }
3139 }
3140
3141 if self.auto_replace_emoji_shortcode
3142 && selection.is_empty()
3143 && text.as_ref().ends_with(':')
3144 {
3145 if let Some(possible_emoji_short_code) =
3146 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
3147 {
3148 if !possible_emoji_short_code.is_empty() {
3149 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
3150 let emoji_shortcode_start = Point::new(
3151 selection.start.row,
3152 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
3153 );
3154
3155 // Remove shortcode from buffer
3156 edits.push((
3157 emoji_shortcode_start..selection.start,
3158 "".to_string().into(),
3159 ));
3160 new_selections.push((
3161 Selection {
3162 id: selection.id,
3163 start: snapshot.anchor_after(emoji_shortcode_start),
3164 end: snapshot.anchor_before(selection.start),
3165 reversed: selection.reversed,
3166 goal: selection.goal,
3167 },
3168 0,
3169 ));
3170
3171 // Insert emoji
3172 let selection_start_anchor = snapshot.anchor_after(selection.start);
3173 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3174 edits.push((selection.start..selection.end, emoji.to_string().into()));
3175
3176 continue;
3177 }
3178 }
3179 }
3180 }
3181
3182 // If not handling any auto-close operation, then just replace the selected
3183 // text with the given input and move the selection to the end of the
3184 // newly inserted text.
3185 let anchor = snapshot.anchor_after(selection.end);
3186 if !self.linked_edit_ranges.is_empty() {
3187 let start_anchor = snapshot.anchor_before(selection.start);
3188
3189 let is_word_char = text.chars().next().map_or(true, |char| {
3190 let scope = snapshot.language_scope_at(start_anchor.to_offset(&snapshot));
3191 let kind = char_kind(&scope, char);
3192
3193 kind == CharKind::Word
3194 });
3195
3196 if is_word_char {
3197 if let Some(ranges) = self
3198 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3199 {
3200 for (buffer, edits) in ranges {
3201 linked_edits
3202 .entry(buffer.clone())
3203 .or_default()
3204 .extend(edits.into_iter().map(|range| (range, text.clone())));
3205 }
3206 }
3207 }
3208 }
3209
3210 new_selections.push((selection.map(|_| anchor), 0));
3211 edits.push((selection.start..selection.end, text.clone()));
3212 }
3213
3214 drop(snapshot);
3215
3216 self.transact(cx, |this, cx| {
3217 this.buffer.update(cx, |buffer, cx| {
3218 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3219 });
3220 for (buffer, edits) in linked_edits {
3221 buffer.update(cx, |buffer, cx| {
3222 let snapshot = buffer.snapshot();
3223 let edits = edits
3224 .into_iter()
3225 .map(|(range, text)| {
3226 use text::ToPoint as TP;
3227 let end_point = TP::to_point(&range.end, &snapshot);
3228 let start_point = TP::to_point(&range.start, &snapshot);
3229 (start_point..end_point, text)
3230 })
3231 .sorted_by_key(|(range, _)| range.start)
3232 .collect::<Vec<_>>();
3233 buffer.edit(edits, None, cx);
3234 })
3235 }
3236 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3237 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3238 let snapshot = this.buffer.read(cx).read(cx);
3239 let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
3240 .zip(new_selection_deltas)
3241 .map(|(selection, delta)| Selection {
3242 id: selection.id,
3243 start: selection.start + delta,
3244 end: selection.end + delta,
3245 reversed: selection.reversed,
3246 goal: SelectionGoal::None,
3247 })
3248 .collect::<Vec<_>>();
3249
3250 let mut i = 0;
3251 for (position, delta, selection_id, pair) in new_autoclose_regions {
3252 let position = position.to_offset(&snapshot) + delta;
3253 let start = snapshot.anchor_before(position);
3254 let end = snapshot.anchor_after(position);
3255 while let Some(existing_state) = this.autoclose_regions.get(i) {
3256 match existing_state.range.start.cmp(&start, &snapshot) {
3257 Ordering::Less => i += 1,
3258 Ordering::Greater => break,
3259 Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
3260 Ordering::Less => i += 1,
3261 Ordering::Equal => break,
3262 Ordering::Greater => break,
3263 },
3264 }
3265 }
3266 this.autoclose_regions.insert(
3267 i,
3268 AutocloseRegion {
3269 selection_id,
3270 range: start..end,
3271 pair,
3272 },
3273 );
3274 }
3275
3276 drop(snapshot);
3277 let had_active_inline_completion = this.has_active_inline_completion(cx);
3278 this.change_selections_inner(Some(Autoscroll::fit()), false, cx, |s| {
3279 s.select(new_selections)
3280 });
3281
3282 if !bracket_inserted && EditorSettings::get_global(cx).use_on_type_format {
3283 if let Some(on_type_format_task) =
3284 this.trigger_on_type_formatting(text.to_string(), cx)
3285 {
3286 on_type_format_task.detach_and_log_err(cx);
3287 }
3288 }
3289
3290 let editor_settings = EditorSettings::get_global(cx);
3291 if bracket_inserted
3292 && (editor_settings.auto_signature_help
3293 || editor_settings.show_signature_help_after_edits)
3294 {
3295 this.show_signature_help(&ShowSignatureHelp, cx);
3296 }
3297
3298 let trigger_in_words = !had_active_inline_completion;
3299 this.trigger_completion_on_input(&text, trigger_in_words, cx);
3300 linked_editing_ranges::refresh_linked_ranges(this, cx);
3301 this.refresh_inline_completion(true, cx);
3302 });
3303 }
3304
3305 fn find_possible_emoji_shortcode_at_position(
3306 snapshot: &MultiBufferSnapshot,
3307 position: Point,
3308 ) -> Option<String> {
3309 let mut chars = Vec::new();
3310 let mut found_colon = false;
3311 for char in snapshot.reversed_chars_at(position).take(100) {
3312 // Found a possible emoji shortcode in the middle of the buffer
3313 if found_colon {
3314 if char.is_whitespace() {
3315 chars.reverse();
3316 return Some(chars.iter().collect());
3317 }
3318 // If the previous character is not a whitespace, we are in the middle of a word
3319 // and we only want to complete the shortcode if the word is made up of other emojis
3320 let mut containing_word = String::new();
3321 for ch in snapshot
3322 .reversed_chars_at(position)
3323 .skip(chars.len() + 1)
3324 .take(100)
3325 {
3326 if ch.is_whitespace() {
3327 break;
3328 }
3329 containing_word.push(ch);
3330 }
3331 let containing_word = containing_word.chars().rev().collect::<String>();
3332 if util::word_consists_of_emojis(containing_word.as_str()) {
3333 chars.reverse();
3334 return Some(chars.iter().collect());
3335 }
3336 }
3337
3338 if char.is_whitespace() || !char.is_ascii() {
3339 return None;
3340 }
3341 if char == ':' {
3342 found_colon = true;
3343 } else {
3344 chars.push(char);
3345 }
3346 }
3347 // Found a possible emoji shortcode at the beginning of the buffer
3348 chars.reverse();
3349 Some(chars.iter().collect())
3350 }
3351
3352 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
3353 self.transact(cx, |this, cx| {
3354 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3355 let selections = this.selections.all::<usize>(cx);
3356 let multi_buffer = this.buffer.read(cx);
3357 let buffer = multi_buffer.snapshot(cx);
3358 selections
3359 .iter()
3360 .map(|selection| {
3361 let start_point = selection.start.to_point(&buffer);
3362 let mut indent =
3363 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3364 indent.len = cmp::min(indent.len, start_point.column);
3365 let start = selection.start;
3366 let end = selection.end;
3367 let selection_is_empty = start == end;
3368 let language_scope = buffer.language_scope_at(start);
3369 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3370 &language_scope
3371 {
3372 let leading_whitespace_len = buffer
3373 .reversed_chars_at(start)
3374 .take_while(|c| c.is_whitespace() && *c != '\n')
3375 .map(|c| c.len_utf8())
3376 .sum::<usize>();
3377
3378 let trailing_whitespace_len = buffer
3379 .chars_at(end)
3380 .take_while(|c| c.is_whitespace() && *c != '\n')
3381 .map(|c| c.len_utf8())
3382 .sum::<usize>();
3383
3384 let insert_extra_newline =
3385 language.brackets().any(|(pair, enabled)| {
3386 let pair_start = pair.start.trim_end();
3387 let pair_end = pair.end.trim_start();
3388
3389 enabled
3390 && pair.newline
3391 && buffer.contains_str_at(
3392 end + trailing_whitespace_len,
3393 pair_end,
3394 )
3395 && buffer.contains_str_at(
3396 (start - leading_whitespace_len)
3397 .saturating_sub(pair_start.len()),
3398 pair_start,
3399 )
3400 });
3401
3402 // Comment extension on newline is allowed only for cursor selections
3403 let comment_delimiter = maybe!({
3404 if !selection_is_empty {
3405 return None;
3406 }
3407
3408 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
3409 return None;
3410 }
3411
3412 let delimiters = language.line_comment_prefixes();
3413 let max_len_of_delimiter =
3414 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3415 let (snapshot, range) =
3416 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3417
3418 let mut index_of_first_non_whitespace = 0;
3419 let comment_candidate = snapshot
3420 .chars_for_range(range)
3421 .skip_while(|c| {
3422 let should_skip = c.is_whitespace();
3423 if should_skip {
3424 index_of_first_non_whitespace += 1;
3425 }
3426 should_skip
3427 })
3428 .take(max_len_of_delimiter)
3429 .collect::<String>();
3430 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3431 comment_candidate.starts_with(comment_prefix.as_ref())
3432 })?;
3433 let cursor_is_placed_after_comment_marker =
3434 index_of_first_non_whitespace + comment_prefix.len()
3435 <= start_point.column as usize;
3436 if cursor_is_placed_after_comment_marker {
3437 Some(comment_prefix.clone())
3438 } else {
3439 None
3440 }
3441 });
3442 (comment_delimiter, insert_extra_newline)
3443 } else {
3444 (None, false)
3445 };
3446
3447 let capacity_for_delimiter = comment_delimiter
3448 .as_deref()
3449 .map(str::len)
3450 .unwrap_or_default();
3451 let mut new_text =
3452 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3453 new_text.push_str("\n");
3454 new_text.extend(indent.chars());
3455 if let Some(delimiter) = &comment_delimiter {
3456 new_text.push_str(&delimiter);
3457 }
3458 if insert_extra_newline {
3459 new_text = new_text.repeat(2);
3460 }
3461
3462 let anchor = buffer.anchor_after(end);
3463 let new_selection = selection.map(|_| anchor);
3464 (
3465 (start..end, new_text),
3466 (insert_extra_newline, new_selection),
3467 )
3468 })
3469 .unzip()
3470 };
3471
3472 this.edit_with_autoindent(edits, cx);
3473 let buffer = this.buffer.read(cx).snapshot(cx);
3474 let new_selections = selection_fixup_info
3475 .into_iter()
3476 .map(|(extra_newline_inserted, new_selection)| {
3477 let mut cursor = new_selection.end.to_point(&buffer);
3478 if extra_newline_inserted {
3479 cursor.row -= 1;
3480 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3481 }
3482 new_selection.map(|_| cursor)
3483 })
3484 .collect();
3485
3486 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
3487 this.refresh_inline_completion(true, cx);
3488 });
3489 }
3490
3491 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
3492 let buffer = self.buffer.read(cx);
3493 let snapshot = buffer.snapshot(cx);
3494
3495 let mut edits = Vec::new();
3496 let mut rows = Vec::new();
3497
3498 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3499 let cursor = selection.head();
3500 let row = cursor.row;
3501
3502 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3503
3504 let newline = "\n".to_string();
3505 edits.push((start_of_line..start_of_line, newline));
3506
3507 rows.push(row + rows_inserted as u32);
3508 }
3509
3510 self.transact(cx, |editor, cx| {
3511 editor.edit(edits, cx);
3512
3513 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3514 let mut index = 0;
3515 s.move_cursors_with(|map, _, _| {
3516 let row = rows[index];
3517 index += 1;
3518
3519 let point = Point::new(row, 0);
3520 let boundary = map.next_line_boundary(point).1;
3521 let clipped = map.clip_point(boundary, Bias::Left);
3522
3523 (clipped, SelectionGoal::None)
3524 });
3525 });
3526
3527 let mut indent_edits = Vec::new();
3528 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3529 for row in rows {
3530 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3531 for (row, indent) in indents {
3532 if indent.len == 0 {
3533 continue;
3534 }
3535
3536 let text = match indent.kind {
3537 IndentKind::Space => " ".repeat(indent.len as usize),
3538 IndentKind::Tab => "\t".repeat(indent.len as usize),
3539 };
3540 let point = Point::new(row.0, 0);
3541 indent_edits.push((point..point, text));
3542 }
3543 }
3544 editor.edit(indent_edits, cx);
3545 });
3546 }
3547
3548 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
3549 let buffer = self.buffer.read(cx);
3550 let snapshot = buffer.snapshot(cx);
3551
3552 let mut edits = Vec::new();
3553 let mut rows = Vec::new();
3554 let mut rows_inserted = 0;
3555
3556 for selection in self.selections.all_adjusted(cx) {
3557 let cursor = selection.head();
3558 let row = cursor.row;
3559
3560 let point = Point::new(row + 1, 0);
3561 let start_of_line = snapshot.clip_point(point, Bias::Left);
3562
3563 let newline = "\n".to_string();
3564 edits.push((start_of_line..start_of_line, newline));
3565
3566 rows_inserted += 1;
3567 rows.push(row + rows_inserted);
3568 }
3569
3570 self.transact(cx, |editor, cx| {
3571 editor.edit(edits, cx);
3572
3573 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3574 let mut index = 0;
3575 s.move_cursors_with(|map, _, _| {
3576 let row = rows[index];
3577 index += 1;
3578
3579 let point = Point::new(row, 0);
3580 let boundary = map.next_line_boundary(point).1;
3581 let clipped = map.clip_point(boundary, Bias::Left);
3582
3583 (clipped, SelectionGoal::None)
3584 });
3585 });
3586
3587 let mut indent_edits = Vec::new();
3588 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3589 for row in rows {
3590 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3591 for (row, indent) in indents {
3592 if indent.len == 0 {
3593 continue;
3594 }
3595
3596 let text = match indent.kind {
3597 IndentKind::Space => " ".repeat(indent.len as usize),
3598 IndentKind::Tab => "\t".repeat(indent.len as usize),
3599 };
3600 let point = Point::new(row.0, 0);
3601 indent_edits.push((point..point, text));
3602 }
3603 }
3604 editor.edit(indent_edits, cx);
3605 });
3606 }
3607
3608 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3609 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3610 original_indent_columns: Vec::new(),
3611 });
3612 self.insert_with_autoindent_mode(text, autoindent, cx);
3613 }
3614
3615 fn insert_with_autoindent_mode(
3616 &mut self,
3617 text: &str,
3618 autoindent_mode: Option<AutoindentMode>,
3619 cx: &mut ViewContext<Self>,
3620 ) {
3621 if self.read_only(cx) {
3622 return;
3623 }
3624
3625 let text: Arc<str> = text.into();
3626 self.transact(cx, |this, cx| {
3627 let old_selections = this.selections.all_adjusted(cx);
3628 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3629 let anchors = {
3630 let snapshot = buffer.read(cx);
3631 old_selections
3632 .iter()
3633 .map(|s| {
3634 let anchor = snapshot.anchor_after(s.head());
3635 s.map(|_| anchor)
3636 })
3637 .collect::<Vec<_>>()
3638 };
3639 buffer.edit(
3640 old_selections
3641 .iter()
3642 .map(|s| (s.start..s.end, text.clone())),
3643 autoindent_mode,
3644 cx,
3645 );
3646 anchors
3647 });
3648
3649 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3650 s.select_anchors(selection_anchors);
3651 })
3652 });
3653 }
3654
3655 fn trigger_completion_on_input(
3656 &mut self,
3657 text: &str,
3658 trigger_in_words: bool,
3659 cx: &mut ViewContext<Self>,
3660 ) {
3661 if self.is_completion_trigger(text, trigger_in_words, cx) {
3662 self.show_completions(
3663 &ShowCompletions {
3664 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3665 },
3666 cx,
3667 );
3668 } else {
3669 self.hide_context_menu(cx);
3670 }
3671 }
3672
3673 fn is_completion_trigger(
3674 &self,
3675 text: &str,
3676 trigger_in_words: bool,
3677 cx: &mut ViewContext<Self>,
3678 ) -> bool {
3679 let position = self.selections.newest_anchor().head();
3680 let multibuffer = self.buffer.read(cx);
3681 let Some(buffer) = position
3682 .buffer_id
3683 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3684 else {
3685 return false;
3686 };
3687
3688 if let Some(completion_provider) = &self.completion_provider {
3689 completion_provider.is_completion_trigger(
3690 &buffer,
3691 position.text_anchor,
3692 text,
3693 trigger_in_words,
3694 cx,
3695 )
3696 } else {
3697 false
3698 }
3699 }
3700
3701 /// If any empty selections is touching the start of its innermost containing autoclose
3702 /// region, expand it to select the brackets.
3703 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
3704 let selections = self.selections.all::<usize>(cx);
3705 let buffer = self.buffer.read(cx).read(cx);
3706 let new_selections = self
3707 .selections_with_autoclose_regions(selections, &buffer)
3708 .map(|(mut selection, region)| {
3709 if !selection.is_empty() {
3710 return selection;
3711 }
3712
3713 if let Some(region) = region {
3714 let mut range = region.range.to_offset(&buffer);
3715 if selection.start == range.start && range.start >= region.pair.start.len() {
3716 range.start -= region.pair.start.len();
3717 if buffer.contains_str_at(range.start, ®ion.pair.start)
3718 && buffer.contains_str_at(range.end, ®ion.pair.end)
3719 {
3720 range.end += region.pair.end.len();
3721 selection.start = range.start;
3722 selection.end = range.end;
3723
3724 return selection;
3725 }
3726 }
3727 }
3728
3729 let always_treat_brackets_as_autoclosed = buffer
3730 .settings_at(selection.start, cx)
3731 .always_treat_brackets_as_autoclosed;
3732
3733 if !always_treat_brackets_as_autoclosed {
3734 return selection;
3735 }
3736
3737 if let Some(scope) = buffer.language_scope_at(selection.start) {
3738 for (pair, enabled) in scope.brackets() {
3739 if !enabled || !pair.close {
3740 continue;
3741 }
3742
3743 if buffer.contains_str_at(selection.start, &pair.end) {
3744 let pair_start_len = pair.start.len();
3745 if buffer.contains_str_at(selection.start - pair_start_len, &pair.start)
3746 {
3747 selection.start -= pair_start_len;
3748 selection.end += pair.end.len();
3749
3750 return selection;
3751 }
3752 }
3753 }
3754 }
3755
3756 selection
3757 })
3758 .collect();
3759
3760 drop(buffer);
3761 self.change_selections(None, cx, |selections| selections.select(new_selections));
3762 }
3763
3764 /// Iterate the given selections, and for each one, find the smallest surrounding
3765 /// autoclose region. This uses the ordering of the selections and the autoclose
3766 /// regions to avoid repeated comparisons.
3767 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3768 &'a self,
3769 selections: impl IntoIterator<Item = Selection<D>>,
3770 buffer: &'a MultiBufferSnapshot,
3771 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3772 let mut i = 0;
3773 let mut regions = self.autoclose_regions.as_slice();
3774 selections.into_iter().map(move |selection| {
3775 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3776
3777 let mut enclosing = None;
3778 while let Some(pair_state) = regions.get(i) {
3779 if pair_state.range.end.to_offset(buffer) < range.start {
3780 regions = ®ions[i + 1..];
3781 i = 0;
3782 } else if pair_state.range.start.to_offset(buffer) > range.end {
3783 break;
3784 } else {
3785 if pair_state.selection_id == selection.id {
3786 enclosing = Some(pair_state);
3787 }
3788 i += 1;
3789 }
3790 }
3791
3792 (selection.clone(), enclosing)
3793 })
3794 }
3795
3796 /// Remove any autoclose regions that no longer contain their selection.
3797 fn invalidate_autoclose_regions(
3798 &mut self,
3799 mut selections: &[Selection<Anchor>],
3800 buffer: &MultiBufferSnapshot,
3801 ) {
3802 self.autoclose_regions.retain(|state| {
3803 let mut i = 0;
3804 while let Some(selection) = selections.get(i) {
3805 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3806 selections = &selections[1..];
3807 continue;
3808 }
3809 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3810 break;
3811 }
3812 if selection.id == state.selection_id {
3813 return true;
3814 } else {
3815 i += 1;
3816 }
3817 }
3818 false
3819 });
3820 }
3821
3822 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3823 let offset = position.to_offset(buffer);
3824 let (word_range, kind) = buffer.surrounding_word(offset);
3825 if offset > word_range.start && kind == Some(CharKind::Word) {
3826 Some(
3827 buffer
3828 .text_for_range(word_range.start..offset)
3829 .collect::<String>(),
3830 )
3831 } else {
3832 None
3833 }
3834 }
3835
3836 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
3837 self.refresh_inlay_hints(
3838 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
3839 cx,
3840 );
3841 }
3842
3843 pub fn inlay_hints_enabled(&self) -> bool {
3844 self.inlay_hint_cache.enabled
3845 }
3846
3847 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
3848 if self.project.is_none() || self.mode != EditorMode::Full {
3849 return;
3850 }
3851
3852 let reason_description = reason.description();
3853 let ignore_debounce = matches!(
3854 reason,
3855 InlayHintRefreshReason::SettingsChange(_)
3856 | InlayHintRefreshReason::Toggle(_)
3857 | InlayHintRefreshReason::ExcerptsRemoved(_)
3858 );
3859 let (invalidate_cache, required_languages) = match reason {
3860 InlayHintRefreshReason::Toggle(enabled) => {
3861 self.inlay_hint_cache.enabled = enabled;
3862 if enabled {
3863 (InvalidationStrategy::RefreshRequested, None)
3864 } else {
3865 self.inlay_hint_cache.clear();
3866 self.splice_inlays(
3867 self.visible_inlay_hints(cx)
3868 .iter()
3869 .map(|inlay| inlay.id)
3870 .collect(),
3871 Vec::new(),
3872 cx,
3873 );
3874 return;
3875 }
3876 }
3877 InlayHintRefreshReason::SettingsChange(new_settings) => {
3878 match self.inlay_hint_cache.update_settings(
3879 &self.buffer,
3880 new_settings,
3881 self.visible_inlay_hints(cx),
3882 cx,
3883 ) {
3884 ControlFlow::Break(Some(InlaySplice {
3885 to_remove,
3886 to_insert,
3887 })) => {
3888 self.splice_inlays(to_remove, to_insert, cx);
3889 return;
3890 }
3891 ControlFlow::Break(None) => return,
3892 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3893 }
3894 }
3895 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3896 if let Some(InlaySplice {
3897 to_remove,
3898 to_insert,
3899 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3900 {
3901 self.splice_inlays(to_remove, to_insert, cx);
3902 }
3903 return;
3904 }
3905 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3906 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3907 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3908 }
3909 InlayHintRefreshReason::RefreshRequested => {
3910 (InvalidationStrategy::RefreshRequested, None)
3911 }
3912 };
3913
3914 if let Some(InlaySplice {
3915 to_remove,
3916 to_insert,
3917 }) = self.inlay_hint_cache.spawn_hint_refresh(
3918 reason_description,
3919 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3920 invalidate_cache,
3921 ignore_debounce,
3922 cx,
3923 ) {
3924 self.splice_inlays(to_remove, to_insert, cx);
3925 }
3926 }
3927
3928 fn visible_inlay_hints(&self, cx: &ViewContext<'_, Editor>) -> Vec<Inlay> {
3929 self.display_map
3930 .read(cx)
3931 .current_inlays()
3932 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3933 .cloned()
3934 .collect()
3935 }
3936
3937 pub fn excerpts_for_inlay_hints_query(
3938 &self,
3939 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3940 cx: &mut ViewContext<Editor>,
3941 ) -> HashMap<ExcerptId, (Model<Buffer>, clock::Global, Range<usize>)> {
3942 let Some(project) = self.project.as_ref() else {
3943 return HashMap::default();
3944 };
3945 let project = project.read(cx);
3946 let multi_buffer = self.buffer().read(cx);
3947 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3948 let multi_buffer_visible_start = self
3949 .scroll_manager
3950 .anchor()
3951 .anchor
3952 .to_point(&multi_buffer_snapshot);
3953 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3954 multi_buffer_visible_start
3955 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3956 Bias::Left,
3957 );
3958 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3959 multi_buffer
3960 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
3961 .into_iter()
3962 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3963 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
3964 let buffer = buffer_handle.read(cx);
3965 let buffer_file = project::File::from_dyn(buffer.file())?;
3966 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3967 let worktree_entry = buffer_worktree
3968 .read(cx)
3969 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3970 if worktree_entry.is_ignored {
3971 return None;
3972 }
3973
3974 let language = buffer.language()?;
3975 if let Some(restrict_to_languages) = restrict_to_languages {
3976 if !restrict_to_languages.contains(language) {
3977 return None;
3978 }
3979 }
3980 Some((
3981 excerpt_id,
3982 (
3983 buffer_handle,
3984 buffer.version().clone(),
3985 excerpt_visible_range,
3986 ),
3987 ))
3988 })
3989 .collect()
3990 }
3991
3992 pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
3993 TextLayoutDetails {
3994 text_system: cx.text_system().clone(),
3995 editor_style: self.style.clone().unwrap(),
3996 rem_size: cx.rem_size(),
3997 scroll_anchor: self.scroll_manager.anchor(),
3998 visible_rows: self.visible_line_count(),
3999 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
4000 }
4001 }
4002
4003 fn splice_inlays(
4004 &self,
4005 to_remove: Vec<InlayId>,
4006 to_insert: Vec<Inlay>,
4007 cx: &mut ViewContext<Self>,
4008 ) {
4009 self.display_map.update(cx, |display_map, cx| {
4010 display_map.splice_inlays(to_remove, to_insert, cx);
4011 });
4012 cx.notify();
4013 }
4014
4015 fn trigger_on_type_formatting(
4016 &self,
4017 input: String,
4018 cx: &mut ViewContext<Self>,
4019 ) -> Option<Task<Result<()>>> {
4020 if input.len() != 1 {
4021 return None;
4022 }
4023
4024 let project = self.project.as_ref()?;
4025 let position = self.selections.newest_anchor().head();
4026 let (buffer, buffer_position) = self
4027 .buffer
4028 .read(cx)
4029 .text_anchor_for_position(position, cx)?;
4030
4031 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
4032 // hence we do LSP request & edit on host side only — add formats to host's history.
4033 let push_to_lsp_host_history = true;
4034 // If this is not the host, append its history with new edits.
4035 let push_to_client_history = project.read(cx).is_remote();
4036
4037 let on_type_formatting = project.update(cx, |project, cx| {
4038 project.on_type_format(
4039 buffer.clone(),
4040 buffer_position,
4041 input,
4042 push_to_lsp_host_history,
4043 cx,
4044 )
4045 });
4046 Some(cx.spawn(|editor, mut cx| async move {
4047 if let Some(transaction) = on_type_formatting.await? {
4048 if push_to_client_history {
4049 buffer
4050 .update(&mut cx, |buffer, _| {
4051 buffer.push_transaction(transaction, Instant::now());
4052 })
4053 .ok();
4054 }
4055 editor.update(&mut cx, |editor, cx| {
4056 editor.refresh_document_highlights(cx);
4057 })?;
4058 }
4059 Ok(())
4060 }))
4061 }
4062
4063 pub fn show_completions(&mut self, options: &ShowCompletions, cx: &mut ViewContext<Self>) {
4064 if self.pending_rename.is_some() {
4065 return;
4066 }
4067
4068 let Some(provider) = self.completion_provider.as_ref() else {
4069 return;
4070 };
4071
4072 let position = self.selections.newest_anchor().head();
4073 let (buffer, buffer_position) =
4074 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
4075 output
4076 } else {
4077 return;
4078 };
4079
4080 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
4081 let is_followup_invoke = {
4082 let context_menu_state = self.context_menu.read();
4083 matches!(
4084 context_menu_state.deref(),
4085 Some(ContextMenu::Completions(_))
4086 )
4087 };
4088 let trigger_kind = match (&options.trigger, is_followup_invoke) {
4089 (_, true) => CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS,
4090 (Some(trigger), _) if buffer.read(cx).completion_triggers().contains(&trigger) => {
4091 CompletionTriggerKind::TRIGGER_CHARACTER
4092 }
4093
4094 _ => CompletionTriggerKind::INVOKED,
4095 };
4096 let completion_context = CompletionContext {
4097 trigger_character: options.trigger.as_ref().and_then(|trigger| {
4098 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
4099 Some(String::from(trigger))
4100 } else {
4101 None
4102 }
4103 }),
4104 trigger_kind,
4105 };
4106 let completions = provider.completions(&buffer, buffer_position, completion_context, cx);
4107
4108 let id = post_inc(&mut self.next_completion_id);
4109 let task = cx.spawn(|this, mut cx| {
4110 async move {
4111 this.update(&mut cx, |this, _| {
4112 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
4113 })?;
4114 let completions = completions.await.log_err();
4115 let menu = if let Some(completions) = completions {
4116 let mut menu = CompletionsMenu {
4117 id,
4118 initial_position: position,
4119 match_candidates: completions
4120 .iter()
4121 .enumerate()
4122 .map(|(id, completion)| {
4123 StringMatchCandidate::new(
4124 id,
4125 completion.label.text[completion.label.filter_range.clone()]
4126 .into(),
4127 )
4128 })
4129 .collect(),
4130 buffer: buffer.clone(),
4131 completions: Arc::new(RwLock::new(completions.into())),
4132 matches: Vec::new().into(),
4133 selected_item: 0,
4134 scroll_handle: UniformListScrollHandle::new(),
4135 selected_completion_documentation_resolve_debounce: Arc::new(Mutex::new(
4136 DebouncedDelay::new(),
4137 )),
4138 };
4139 menu.filter(query.as_deref(), cx.background_executor().clone())
4140 .await;
4141
4142 if menu.matches.is_empty() {
4143 None
4144 } else {
4145 this.update(&mut cx, |editor, cx| {
4146 let completions = menu.completions.clone();
4147 let matches = menu.matches.clone();
4148
4149 let delay_ms = EditorSettings::get_global(cx)
4150 .completion_documentation_secondary_query_debounce;
4151 let delay = Duration::from_millis(delay_ms);
4152 editor
4153 .completion_documentation_pre_resolve_debounce
4154 .fire_new(delay, cx, |editor, cx| {
4155 CompletionsMenu::pre_resolve_completion_documentation(
4156 buffer,
4157 completions,
4158 matches,
4159 editor,
4160 cx,
4161 )
4162 });
4163 })
4164 .ok();
4165 Some(menu)
4166 }
4167 } else {
4168 None
4169 };
4170
4171 this.update(&mut cx, |this, cx| {
4172 let mut context_menu = this.context_menu.write();
4173 match context_menu.as_ref() {
4174 None => {}
4175
4176 Some(ContextMenu::Completions(prev_menu)) => {
4177 if prev_menu.id > id {
4178 return;
4179 }
4180 }
4181
4182 _ => return,
4183 }
4184
4185 if this.focus_handle.is_focused(cx) && menu.is_some() {
4186 let menu = menu.unwrap();
4187 *context_menu = Some(ContextMenu::Completions(menu));
4188 drop(context_menu);
4189 this.discard_inline_completion(false, cx);
4190 cx.notify();
4191 } else if this.completion_tasks.len() <= 1 {
4192 // If there are no more completion tasks and the last menu was
4193 // empty, we should hide it. If it was already hidden, we should
4194 // also show the copilot completion when available.
4195 drop(context_menu);
4196 if this.hide_context_menu(cx).is_none() {
4197 this.update_visible_inline_completion(cx);
4198 }
4199 }
4200 })?;
4201
4202 Ok::<_, anyhow::Error>(())
4203 }
4204 .log_err()
4205 });
4206
4207 self.completion_tasks.push((id, task));
4208 }
4209
4210 pub fn confirm_completion(
4211 &mut self,
4212 action: &ConfirmCompletion,
4213 cx: &mut ViewContext<Self>,
4214 ) -> Option<Task<Result<()>>> {
4215 use language::ToOffset as _;
4216
4217 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
4218 menu
4219 } else {
4220 return None;
4221 };
4222
4223 let mat = completions_menu
4224 .matches
4225 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
4226 let buffer_handle = completions_menu.buffer;
4227 let completions = completions_menu.completions.read();
4228 let completion = completions.get(mat.candidate_id)?;
4229 cx.stop_propagation();
4230
4231 let snippet;
4232 let text;
4233
4234 if completion.is_snippet() {
4235 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4236 text = snippet.as_ref().unwrap().text.clone();
4237 } else {
4238 snippet = None;
4239 text = completion.new_text.clone();
4240 };
4241 let selections = self.selections.all::<usize>(cx);
4242 let buffer = buffer_handle.read(cx);
4243 let old_range = completion.old_range.to_offset(buffer);
4244 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4245
4246 let newest_selection = self.selections.newest_anchor();
4247 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4248 return None;
4249 }
4250
4251 let lookbehind = newest_selection
4252 .start
4253 .text_anchor
4254 .to_offset(buffer)
4255 .saturating_sub(old_range.start);
4256 let lookahead = old_range
4257 .end
4258 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4259 let mut common_prefix_len = old_text
4260 .bytes()
4261 .zip(text.bytes())
4262 .take_while(|(a, b)| a == b)
4263 .count();
4264
4265 let snapshot = self.buffer.read(cx).snapshot(cx);
4266 let mut range_to_replace: Option<Range<isize>> = None;
4267 let mut ranges = Vec::new();
4268 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4269 for selection in &selections {
4270 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4271 let start = selection.start.saturating_sub(lookbehind);
4272 let end = selection.end + lookahead;
4273 if selection.id == newest_selection.id {
4274 range_to_replace = Some(
4275 ((start + common_prefix_len) as isize - selection.start as isize)
4276 ..(end as isize - selection.start as isize),
4277 );
4278 }
4279 ranges.push(start + common_prefix_len..end);
4280 } else {
4281 common_prefix_len = 0;
4282 ranges.clear();
4283 ranges.extend(selections.iter().map(|s| {
4284 if s.id == newest_selection.id {
4285 range_to_replace = Some(
4286 old_range.start.to_offset_utf16(&snapshot).0 as isize
4287 - selection.start as isize
4288 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4289 - selection.start as isize,
4290 );
4291 old_range.clone()
4292 } else {
4293 s.start..s.end
4294 }
4295 }));
4296 break;
4297 }
4298 if !self.linked_edit_ranges.is_empty() {
4299 let start_anchor = snapshot.anchor_before(selection.head());
4300 let end_anchor = snapshot.anchor_after(selection.tail());
4301 if let Some(ranges) = self
4302 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4303 {
4304 for (buffer, edits) in ranges {
4305 linked_edits.entry(buffer.clone()).or_default().extend(
4306 edits
4307 .into_iter()
4308 .map(|range| (range, text[common_prefix_len..].to_owned())),
4309 );
4310 }
4311 }
4312 }
4313 }
4314 let text = &text[common_prefix_len..];
4315
4316 cx.emit(EditorEvent::InputHandled {
4317 utf16_range_to_replace: range_to_replace,
4318 text: text.into(),
4319 });
4320
4321 self.transact(cx, |this, cx| {
4322 if let Some(mut snippet) = snippet {
4323 snippet.text = text.to_string();
4324 for tabstop in snippet.tabstops.iter_mut().flatten() {
4325 tabstop.start -= common_prefix_len as isize;
4326 tabstop.end -= common_prefix_len as isize;
4327 }
4328
4329 this.insert_snippet(&ranges, snippet, cx).log_err();
4330 } else {
4331 this.buffer.update(cx, |buffer, cx| {
4332 buffer.edit(
4333 ranges.iter().map(|range| (range.clone(), text)),
4334 this.autoindent_mode.clone(),
4335 cx,
4336 );
4337 });
4338 }
4339 for (buffer, edits) in linked_edits {
4340 buffer.update(cx, |buffer, cx| {
4341 let snapshot = buffer.snapshot();
4342 let edits = edits
4343 .into_iter()
4344 .map(|(range, text)| {
4345 use text::ToPoint as TP;
4346 let end_point = TP::to_point(&range.end, &snapshot);
4347 let start_point = TP::to_point(&range.start, &snapshot);
4348 (start_point..end_point, text)
4349 })
4350 .sorted_by_key(|(range, _)| range.start)
4351 .collect::<Vec<_>>();
4352 buffer.edit(edits, None, cx);
4353 })
4354 }
4355
4356 this.refresh_inline_completion(true, cx);
4357 });
4358
4359 if let Some(confirm) = completion.confirm.as_ref() {
4360 (confirm)(cx);
4361 }
4362
4363 if completion.show_new_completions_on_confirm {
4364 self.show_completions(&ShowCompletions { trigger: None }, cx);
4365 }
4366
4367 let provider = self.completion_provider.as_ref()?;
4368 let apply_edits = provider.apply_additional_edits_for_completion(
4369 buffer_handle,
4370 completion.clone(),
4371 true,
4372 cx,
4373 );
4374
4375 let editor_settings = EditorSettings::get_global(cx);
4376 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4377 // After the code completion is finished, users often want to know what signatures are needed.
4378 // so we should automatically call signature_help
4379 self.show_signature_help(&ShowSignatureHelp, cx);
4380 }
4381
4382 Some(cx.foreground_executor().spawn(async move {
4383 apply_edits.await?;
4384 Ok(())
4385 }))
4386 }
4387
4388 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
4389 let mut context_menu = self.context_menu.write();
4390 if let Some(ContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4391 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4392 // Toggle if we're selecting the same one
4393 *context_menu = None;
4394 cx.notify();
4395 return;
4396 } else {
4397 // Otherwise, clear it and start a new one
4398 *context_menu = None;
4399 cx.notify();
4400 }
4401 }
4402 drop(context_menu);
4403 let snapshot = self.snapshot(cx);
4404 let deployed_from_indicator = action.deployed_from_indicator;
4405 let mut task = self.code_actions_task.take();
4406 let action = action.clone();
4407 cx.spawn(|editor, mut cx| async move {
4408 while let Some(prev_task) = task {
4409 prev_task.await;
4410 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4411 }
4412
4413 let spawned_test_task = editor.update(&mut cx, |editor, cx| {
4414 if editor.focus_handle.is_focused(cx) {
4415 let multibuffer_point = action
4416 .deployed_from_indicator
4417 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4418 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4419 let (buffer, buffer_row) = snapshot
4420 .buffer_snapshot
4421 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4422 .and_then(|(buffer_snapshot, range)| {
4423 editor
4424 .buffer
4425 .read(cx)
4426 .buffer(buffer_snapshot.remote_id())
4427 .map(|buffer| (buffer, range.start.row))
4428 })?;
4429 let (_, code_actions) = editor
4430 .available_code_actions
4431 .clone()
4432 .and_then(|(location, code_actions)| {
4433 let snapshot = location.buffer.read(cx).snapshot();
4434 let point_range = location.range.to_point(&snapshot);
4435 let point_range = point_range.start.row..=point_range.end.row;
4436 if point_range.contains(&buffer_row) {
4437 Some((location, code_actions))
4438 } else {
4439 None
4440 }
4441 })
4442 .unzip();
4443 let buffer_id = buffer.read(cx).remote_id();
4444 let tasks = editor
4445 .tasks
4446 .get(&(buffer_id, buffer_row))
4447 .map(|t| Arc::new(t.to_owned()));
4448 if tasks.is_none() && code_actions.is_none() {
4449 return None;
4450 }
4451
4452 editor.completion_tasks.clear();
4453 editor.discard_inline_completion(false, cx);
4454 let task_context =
4455 tasks
4456 .as_ref()
4457 .zip(editor.project.clone())
4458 .map(|(tasks, project)| {
4459 let position = Point::new(buffer_row, tasks.column);
4460 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
4461 let location = Location {
4462 buffer: buffer.clone(),
4463 range: range_start..range_start,
4464 };
4465 // Fill in the environmental variables from the tree-sitter captures
4466 let mut captured_task_variables = TaskVariables::default();
4467 for (capture_name, value) in tasks.extra_variables.clone() {
4468 captured_task_variables.insert(
4469 task::VariableName::Custom(capture_name.into()),
4470 value.clone(),
4471 );
4472 }
4473 project.update(cx, |project, cx| {
4474 project.task_context_for_location(
4475 captured_task_variables,
4476 location,
4477 cx,
4478 )
4479 })
4480 });
4481
4482 Some(cx.spawn(|editor, mut cx| async move {
4483 let task_context = match task_context {
4484 Some(task_context) => task_context.await,
4485 None => None,
4486 };
4487 let resolved_tasks =
4488 tasks.zip(task_context).map(|(tasks, task_context)| {
4489 Arc::new(ResolvedTasks {
4490 templates: tasks
4491 .templates
4492 .iter()
4493 .filter_map(|(kind, template)| {
4494 template
4495 .resolve_task(&kind.to_id_base(), &task_context)
4496 .map(|task| (kind.clone(), task))
4497 })
4498 .collect(),
4499 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4500 multibuffer_point.row,
4501 tasks.column,
4502 )),
4503 })
4504 });
4505 let spawn_straight_away = resolved_tasks
4506 .as_ref()
4507 .map_or(false, |tasks| tasks.templates.len() == 1)
4508 && code_actions
4509 .as_ref()
4510 .map_or(true, |actions| actions.is_empty());
4511 if let Some(task) = editor
4512 .update(&mut cx, |editor, cx| {
4513 *editor.context_menu.write() =
4514 Some(ContextMenu::CodeActions(CodeActionsMenu {
4515 buffer,
4516 actions: CodeActionContents {
4517 tasks: resolved_tasks,
4518 actions: code_actions,
4519 },
4520 selected_item: Default::default(),
4521 scroll_handle: UniformListScrollHandle::default(),
4522 deployed_from_indicator,
4523 }));
4524 if spawn_straight_away {
4525 if let Some(task) = editor.confirm_code_action(
4526 &ConfirmCodeAction { item_ix: Some(0) },
4527 cx,
4528 ) {
4529 cx.notify();
4530 return task;
4531 }
4532 }
4533 cx.notify();
4534 Task::ready(Ok(()))
4535 })
4536 .ok()
4537 {
4538 task.await
4539 } else {
4540 Ok(())
4541 }
4542 }))
4543 } else {
4544 Some(Task::ready(Ok(())))
4545 }
4546 })?;
4547 if let Some(task) = spawned_test_task {
4548 task.await?;
4549 }
4550
4551 Ok::<_, anyhow::Error>(())
4552 })
4553 .detach_and_log_err(cx);
4554 }
4555
4556 pub fn confirm_code_action(
4557 &mut self,
4558 action: &ConfirmCodeAction,
4559 cx: &mut ViewContext<Self>,
4560 ) -> Option<Task<Result<()>>> {
4561 let actions_menu = if let ContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
4562 menu
4563 } else {
4564 return None;
4565 };
4566 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4567 let action = actions_menu.actions.get(action_ix)?;
4568 let title = action.label();
4569 let buffer = actions_menu.buffer;
4570 let workspace = self.workspace()?;
4571
4572 match action {
4573 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4574 workspace.update(cx, |workspace, cx| {
4575 workspace::tasks::schedule_resolved_task(
4576 workspace,
4577 task_source_kind,
4578 resolved_task,
4579 false,
4580 cx,
4581 );
4582
4583 Some(Task::ready(Ok(())))
4584 })
4585 }
4586 CodeActionsItem::CodeAction(action) => {
4587 let apply_code_actions = workspace
4588 .read(cx)
4589 .project()
4590 .clone()
4591 .update(cx, |project, cx| {
4592 project.apply_code_action(buffer, action, true, cx)
4593 });
4594 let workspace = workspace.downgrade();
4595 Some(cx.spawn(|editor, cx| async move {
4596 let project_transaction = apply_code_actions.await?;
4597 Self::open_project_transaction(
4598 &editor,
4599 workspace,
4600 project_transaction,
4601 title,
4602 cx,
4603 )
4604 .await
4605 }))
4606 }
4607 }
4608 }
4609
4610 pub async fn open_project_transaction(
4611 this: &WeakView<Editor>,
4612 workspace: WeakView<Workspace>,
4613 transaction: ProjectTransaction,
4614 title: String,
4615 mut cx: AsyncWindowContext,
4616 ) -> Result<()> {
4617 let replica_id = this.update(&mut cx, |this, cx| this.replica_id(cx))?;
4618
4619 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4620 cx.update(|cx| {
4621 entries.sort_unstable_by_key(|(buffer, _)| {
4622 buffer.read(cx).file().map(|f| f.path().clone())
4623 });
4624 })?;
4625
4626 // If the project transaction's edits are all contained within this editor, then
4627 // avoid opening a new editor to display them.
4628
4629 if let Some((buffer, transaction)) = entries.first() {
4630 if entries.len() == 1 {
4631 let excerpt = this.update(&mut cx, |editor, cx| {
4632 editor
4633 .buffer()
4634 .read(cx)
4635 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4636 })?;
4637 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4638 if excerpted_buffer == *buffer {
4639 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4640 let excerpt_range = excerpt_range.to_offset(buffer);
4641 buffer
4642 .edited_ranges_for_transaction::<usize>(transaction)
4643 .all(|range| {
4644 excerpt_range.start <= range.start
4645 && excerpt_range.end >= range.end
4646 })
4647 })?;
4648
4649 if all_edits_within_excerpt {
4650 return Ok(());
4651 }
4652 }
4653 }
4654 }
4655 } else {
4656 return Ok(());
4657 }
4658
4659 let mut ranges_to_highlight = Vec::new();
4660 let excerpt_buffer = cx.new_model(|cx| {
4661 let mut multibuffer =
4662 MultiBuffer::new(replica_id, Capability::ReadWrite).with_title(title);
4663 for (buffer_handle, transaction) in &entries {
4664 let buffer = buffer_handle.read(cx);
4665 ranges_to_highlight.extend(
4666 multibuffer.push_excerpts_with_context_lines(
4667 buffer_handle.clone(),
4668 buffer
4669 .edited_ranges_for_transaction::<usize>(transaction)
4670 .collect(),
4671 DEFAULT_MULTIBUFFER_CONTEXT,
4672 cx,
4673 ),
4674 );
4675 }
4676 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4677 multibuffer
4678 })?;
4679
4680 workspace.update(&mut cx, |workspace, cx| {
4681 let project = workspace.project().clone();
4682 let editor =
4683 cx.new_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, cx));
4684 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
4685 editor.update(cx, |editor, cx| {
4686 editor.highlight_background::<Self>(
4687 &ranges_to_highlight,
4688 |theme| theme.editor_highlighted_line_background,
4689 cx,
4690 );
4691 });
4692 })?;
4693
4694 Ok(())
4695 }
4696
4697 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4698 let project = self.project.clone()?;
4699 let buffer = self.buffer.read(cx);
4700 let newest_selection = self.selections.newest_anchor().clone();
4701 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4702 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4703 if start_buffer != end_buffer {
4704 return None;
4705 }
4706
4707 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
4708 cx.background_executor()
4709 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4710 .await;
4711
4712 let actions = if let Ok(code_actions) = project.update(&mut cx, |project, cx| {
4713 project.code_actions(&start_buffer, start..end, cx)
4714 }) {
4715 code_actions.await
4716 } else {
4717 Vec::new()
4718 };
4719
4720 this.update(&mut cx, |this, cx| {
4721 this.available_code_actions = if actions.is_empty() {
4722 None
4723 } else {
4724 Some((
4725 Location {
4726 buffer: start_buffer,
4727 range: start..end,
4728 },
4729 actions.into(),
4730 ))
4731 };
4732 cx.notify();
4733 })
4734 .log_err();
4735 }));
4736 None
4737 }
4738
4739 fn start_inline_blame_timer(&mut self, cx: &mut ViewContext<Self>) {
4740 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4741 self.show_git_blame_inline = false;
4742
4743 self.show_git_blame_inline_delay_task = Some(cx.spawn(|this, mut cx| async move {
4744 cx.background_executor().timer(delay).await;
4745
4746 this.update(&mut cx, |this, cx| {
4747 this.show_git_blame_inline = true;
4748 cx.notify();
4749 })
4750 .log_err();
4751 }));
4752 }
4753 }
4754
4755 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4756 if self.pending_rename.is_some() {
4757 return None;
4758 }
4759
4760 let project = self.project.clone()?;
4761 let buffer = self.buffer.read(cx);
4762 let newest_selection = self.selections.newest_anchor().clone();
4763 let cursor_position = newest_selection.head();
4764 let (cursor_buffer, cursor_buffer_position) =
4765 buffer.text_anchor_for_position(cursor_position, cx)?;
4766 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4767 if cursor_buffer != tail_buffer {
4768 return None;
4769 }
4770
4771 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4772 cx.background_executor()
4773 .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT)
4774 .await;
4775
4776 let highlights = if let Some(highlights) = project
4777 .update(&mut cx, |project, cx| {
4778 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4779 })
4780 .log_err()
4781 {
4782 highlights.await.log_err()
4783 } else {
4784 None
4785 };
4786
4787 if let Some(highlights) = highlights {
4788 this.update(&mut cx, |this, cx| {
4789 if this.pending_rename.is_some() {
4790 return;
4791 }
4792
4793 let buffer_id = cursor_position.buffer_id;
4794 let buffer = this.buffer.read(cx);
4795 if !buffer
4796 .text_anchor_for_position(cursor_position, cx)
4797 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4798 {
4799 return;
4800 }
4801
4802 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4803 let mut write_ranges = Vec::new();
4804 let mut read_ranges = Vec::new();
4805 for highlight in highlights {
4806 for (excerpt_id, excerpt_range) in
4807 buffer.excerpts_for_buffer(&cursor_buffer, cx)
4808 {
4809 let start = highlight
4810 .range
4811 .start
4812 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4813 let end = highlight
4814 .range
4815 .end
4816 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4817 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4818 continue;
4819 }
4820
4821 let range = Anchor {
4822 buffer_id,
4823 excerpt_id: excerpt_id,
4824 text_anchor: start,
4825 }..Anchor {
4826 buffer_id,
4827 excerpt_id,
4828 text_anchor: end,
4829 };
4830 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4831 write_ranges.push(range);
4832 } else {
4833 read_ranges.push(range);
4834 }
4835 }
4836 }
4837
4838 this.highlight_background::<DocumentHighlightRead>(
4839 &read_ranges,
4840 |theme| theme.editor_document_highlight_read_background,
4841 cx,
4842 );
4843 this.highlight_background::<DocumentHighlightWrite>(
4844 &write_ranges,
4845 |theme| theme.editor_document_highlight_write_background,
4846 cx,
4847 );
4848 cx.notify();
4849 })
4850 .log_err();
4851 }
4852 }));
4853 None
4854 }
4855
4856 fn refresh_inline_completion(
4857 &mut self,
4858 debounce: bool,
4859 cx: &mut ViewContext<Self>,
4860 ) -> Option<()> {
4861 let provider = self.inline_completion_provider()?;
4862 let cursor = self.selections.newest_anchor().head();
4863 let (buffer, cursor_buffer_position) =
4864 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4865 if !self.show_inline_completions
4866 || !provider.is_enabled(&buffer, cursor_buffer_position, cx)
4867 {
4868 self.discard_inline_completion(false, cx);
4869 return None;
4870 }
4871
4872 self.update_visible_inline_completion(cx);
4873 provider.refresh(buffer, cursor_buffer_position, debounce, cx);
4874 Some(())
4875 }
4876
4877 fn cycle_inline_completion(
4878 &mut self,
4879 direction: Direction,
4880 cx: &mut ViewContext<Self>,
4881 ) -> Option<()> {
4882 let provider = self.inline_completion_provider()?;
4883 let cursor = self.selections.newest_anchor().head();
4884 let (buffer, cursor_buffer_position) =
4885 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4886 if !self.show_inline_completions
4887 || !provider.is_enabled(&buffer, cursor_buffer_position, cx)
4888 {
4889 return None;
4890 }
4891
4892 provider.cycle(buffer, cursor_buffer_position, direction, cx);
4893 self.update_visible_inline_completion(cx);
4894
4895 Some(())
4896 }
4897
4898 pub fn show_inline_completion(&mut self, _: &ShowInlineCompletion, cx: &mut ViewContext<Self>) {
4899 if !self.has_active_inline_completion(cx) {
4900 self.refresh_inline_completion(false, cx);
4901 return;
4902 }
4903
4904 self.update_visible_inline_completion(cx);
4905 }
4906
4907 pub fn display_cursor_names(&mut self, _: &DisplayCursorNames, cx: &mut ViewContext<Self>) {
4908 self.show_cursor_names(cx);
4909 }
4910
4911 fn show_cursor_names(&mut self, cx: &mut ViewContext<Self>) {
4912 self.show_cursor_names = true;
4913 cx.notify();
4914 cx.spawn(|this, mut cx| async move {
4915 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
4916 this.update(&mut cx, |this, cx| {
4917 this.show_cursor_names = false;
4918 cx.notify()
4919 })
4920 .ok()
4921 })
4922 .detach();
4923 }
4924
4925 pub fn next_inline_completion(&mut self, _: &NextInlineCompletion, cx: &mut ViewContext<Self>) {
4926 if self.has_active_inline_completion(cx) {
4927 self.cycle_inline_completion(Direction::Next, cx);
4928 } else {
4929 let is_copilot_disabled = self.refresh_inline_completion(false, cx).is_none();
4930 if is_copilot_disabled {
4931 cx.propagate();
4932 }
4933 }
4934 }
4935
4936 pub fn previous_inline_completion(
4937 &mut self,
4938 _: &PreviousInlineCompletion,
4939 cx: &mut ViewContext<Self>,
4940 ) {
4941 if self.has_active_inline_completion(cx) {
4942 self.cycle_inline_completion(Direction::Prev, cx);
4943 } else {
4944 let is_copilot_disabled = self.refresh_inline_completion(false, cx).is_none();
4945 if is_copilot_disabled {
4946 cx.propagate();
4947 }
4948 }
4949 }
4950
4951 pub fn accept_inline_completion(
4952 &mut self,
4953 _: &AcceptInlineCompletion,
4954 cx: &mut ViewContext<Self>,
4955 ) {
4956 let Some(completion) = self.take_active_inline_completion(cx) else {
4957 return;
4958 };
4959 if let Some(provider) = self.inline_completion_provider() {
4960 provider.accept(cx);
4961 }
4962
4963 cx.emit(EditorEvent::InputHandled {
4964 utf16_range_to_replace: None,
4965 text: completion.text.to_string().into(),
4966 });
4967 self.insert_with_autoindent_mode(&completion.text.to_string(), None, cx);
4968 self.refresh_inline_completion(true, cx);
4969 cx.notify();
4970 }
4971
4972 pub fn accept_partial_inline_completion(
4973 &mut self,
4974 _: &AcceptPartialInlineCompletion,
4975 cx: &mut ViewContext<Self>,
4976 ) {
4977 if self.selections.count() == 1 && self.has_active_inline_completion(cx) {
4978 if let Some(completion) = self.take_active_inline_completion(cx) {
4979 let mut partial_completion = completion
4980 .text
4981 .chars()
4982 .by_ref()
4983 .take_while(|c| c.is_alphabetic())
4984 .collect::<String>();
4985 if partial_completion.is_empty() {
4986 partial_completion = completion
4987 .text
4988 .chars()
4989 .by_ref()
4990 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
4991 .collect::<String>();
4992 }
4993
4994 cx.emit(EditorEvent::InputHandled {
4995 utf16_range_to_replace: None,
4996 text: partial_completion.clone().into(),
4997 });
4998 self.insert_with_autoindent_mode(&partial_completion, None, cx);
4999 self.refresh_inline_completion(true, cx);
5000 cx.notify();
5001 }
5002 }
5003 }
5004
5005 fn discard_inline_completion(
5006 &mut self,
5007 should_report_inline_completion_event: bool,
5008 cx: &mut ViewContext<Self>,
5009 ) -> bool {
5010 if let Some(provider) = self.inline_completion_provider() {
5011 provider.discard(should_report_inline_completion_event, cx);
5012 }
5013
5014 self.take_active_inline_completion(cx).is_some()
5015 }
5016
5017 pub fn has_active_inline_completion(&self, cx: &AppContext) -> bool {
5018 if let Some(completion) = self.active_inline_completion.as_ref() {
5019 let buffer = self.buffer.read(cx).read(cx);
5020 completion.position.is_valid(&buffer)
5021 } else {
5022 false
5023 }
5024 }
5025
5026 fn take_active_inline_completion(&mut self, cx: &mut ViewContext<Self>) -> Option<Inlay> {
5027 let completion = self.active_inline_completion.take()?;
5028 self.display_map.update(cx, |map, cx| {
5029 map.splice_inlays(vec![completion.id], Default::default(), cx);
5030 });
5031 let buffer = self.buffer.read(cx).read(cx);
5032
5033 if completion.position.is_valid(&buffer) {
5034 Some(completion)
5035 } else {
5036 None
5037 }
5038 }
5039
5040 fn update_visible_inline_completion(&mut self, cx: &mut ViewContext<Self>) {
5041 let selection = self.selections.newest_anchor();
5042 let cursor = selection.head();
5043
5044 if self.context_menu.read().is_none()
5045 && self.completion_tasks.is_empty()
5046 && selection.start == selection.end
5047 {
5048 if let Some(provider) = self.inline_completion_provider() {
5049 if let Some((buffer, cursor_buffer_position)) =
5050 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5051 {
5052 if let Some(text) =
5053 provider.active_completion_text(&buffer, cursor_buffer_position, cx)
5054 {
5055 let text = Rope::from(text);
5056 let mut to_remove = Vec::new();
5057 if let Some(completion) = self.active_inline_completion.take() {
5058 to_remove.push(completion.id);
5059 }
5060
5061 let completion_inlay =
5062 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
5063 self.active_inline_completion = Some(completion_inlay.clone());
5064 self.display_map.update(cx, move |map, cx| {
5065 map.splice_inlays(to_remove, vec![completion_inlay], cx)
5066 });
5067 cx.notify();
5068 return;
5069 }
5070 }
5071 }
5072 }
5073
5074 self.discard_inline_completion(false, cx);
5075 }
5076
5077 fn inline_completion_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5078 Some(self.inline_completion_provider.as_ref()?.provider.clone())
5079 }
5080
5081 fn render_code_actions_indicator(
5082 &self,
5083 _style: &EditorStyle,
5084 row: DisplayRow,
5085 is_active: bool,
5086 cx: &mut ViewContext<Self>,
5087 ) -> Option<IconButton> {
5088 if self.available_code_actions.is_some() {
5089 Some(
5090 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5091 .shape(ui::IconButtonShape::Square)
5092 .icon_size(IconSize::XSmall)
5093 .icon_color(Color::Muted)
5094 .selected(is_active)
5095 .on_click(cx.listener(move |editor, _e, cx| {
5096 editor.focus(cx);
5097 editor.toggle_code_actions(
5098 &ToggleCodeActions {
5099 deployed_from_indicator: Some(row),
5100 },
5101 cx,
5102 );
5103 })),
5104 )
5105 } else {
5106 None
5107 }
5108 }
5109
5110 fn clear_tasks(&mut self) {
5111 self.tasks.clear()
5112 }
5113
5114 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5115 if let Some(_) = self.tasks.insert(key, value) {
5116 // This case should hopefully be rare, but just in case...
5117 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5118 }
5119 }
5120
5121 fn render_run_indicator(
5122 &self,
5123 _style: &EditorStyle,
5124 is_active: bool,
5125 row: DisplayRow,
5126 cx: &mut ViewContext<Self>,
5127 ) -> IconButton {
5128 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5129 .shape(ui::IconButtonShape::Square)
5130 .icon_size(IconSize::XSmall)
5131 .icon_color(Color::Muted)
5132 .selected(is_active)
5133 .on_click(cx.listener(move |editor, _e, cx| {
5134 editor.focus(cx);
5135 editor.toggle_code_actions(
5136 &ToggleCodeActions {
5137 deployed_from_indicator: Some(row),
5138 },
5139 cx,
5140 );
5141 }))
5142 }
5143
5144 fn render_close_hunk_diff_button(
5145 &self,
5146 hunk: HoveredHunk,
5147 row: DisplayRow,
5148 cx: &mut ViewContext<Self>,
5149 ) -> IconButton {
5150 IconButton::new(
5151 ("close_hunk_diff_indicator", row.0 as usize),
5152 ui::IconName::Close,
5153 )
5154 .shape(ui::IconButtonShape::Square)
5155 .icon_size(IconSize::XSmall)
5156 .icon_color(Color::Muted)
5157 .tooltip(|cx| Tooltip::for_action("Close hunk diff", &ToggleHunkDiff, cx))
5158 .on_click(cx.listener(move |editor, _e, cx| editor.toggle_hovered_hunk(&hunk, cx)))
5159 }
5160
5161 pub fn context_menu_visible(&self) -> bool {
5162 self.context_menu
5163 .read()
5164 .as_ref()
5165 .map_or(false, |menu| menu.visible())
5166 }
5167
5168 fn render_context_menu(
5169 &self,
5170 cursor_position: DisplayPoint,
5171 style: &EditorStyle,
5172 max_height: Pixels,
5173 cx: &mut ViewContext<Editor>,
5174 ) -> Option<(ContextMenuOrigin, AnyElement)> {
5175 self.context_menu.read().as_ref().map(|menu| {
5176 menu.render(
5177 cursor_position,
5178 style,
5179 max_height,
5180 self.workspace.as_ref().map(|(w, _)| w.clone()),
5181 cx,
5182 )
5183 })
5184 }
5185
5186 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
5187 cx.notify();
5188 self.completion_tasks.clear();
5189 let context_menu = self.context_menu.write().take();
5190 if context_menu.is_some() {
5191 self.update_visible_inline_completion(cx);
5192 }
5193 context_menu
5194 }
5195
5196 pub fn insert_snippet(
5197 &mut self,
5198 insertion_ranges: &[Range<usize>],
5199 snippet: Snippet,
5200 cx: &mut ViewContext<Self>,
5201 ) -> Result<()> {
5202 struct Tabstop<T> {
5203 is_end_tabstop: bool,
5204 ranges: Vec<Range<T>>,
5205 }
5206
5207 let tabstops = self.buffer.update(cx, |buffer, cx| {
5208 let snippet_text: Arc<str> = snippet.text.clone().into();
5209 buffer.edit(
5210 insertion_ranges
5211 .iter()
5212 .cloned()
5213 .map(|range| (range, snippet_text.clone())),
5214 Some(AutoindentMode::EachLine),
5215 cx,
5216 );
5217
5218 let snapshot = &*buffer.read(cx);
5219 let snippet = &snippet;
5220 snippet
5221 .tabstops
5222 .iter()
5223 .map(|tabstop| {
5224 let is_end_tabstop = tabstop.first().map_or(false, |tabstop| {
5225 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
5226 });
5227 let mut tabstop_ranges = tabstop
5228 .iter()
5229 .flat_map(|tabstop_range| {
5230 let mut delta = 0_isize;
5231 insertion_ranges.iter().map(move |insertion_range| {
5232 let insertion_start = insertion_range.start as isize + delta;
5233 delta +=
5234 snippet.text.len() as isize - insertion_range.len() as isize;
5235
5236 let start = ((insertion_start + tabstop_range.start) as usize)
5237 .min(snapshot.len());
5238 let end = ((insertion_start + tabstop_range.end) as usize)
5239 .min(snapshot.len());
5240 snapshot.anchor_before(start)..snapshot.anchor_after(end)
5241 })
5242 })
5243 .collect::<Vec<_>>();
5244 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
5245
5246 Tabstop {
5247 is_end_tabstop,
5248 ranges: tabstop_ranges,
5249 }
5250 })
5251 .collect::<Vec<_>>()
5252 });
5253 if let Some(tabstop) = tabstops.first() {
5254 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5255 s.select_ranges(tabstop.ranges.iter().cloned());
5256 });
5257
5258 // If we're already at the last tabstop and it's at the end of the snippet,
5259 // we're done, we don't need to keep the state around.
5260 if !tabstop.is_end_tabstop {
5261 let ranges = tabstops
5262 .into_iter()
5263 .map(|tabstop| tabstop.ranges)
5264 .collect::<Vec<_>>();
5265 self.snippet_stack.push(SnippetState {
5266 active_index: 0,
5267 ranges,
5268 });
5269 }
5270
5271 // Check whether the just-entered snippet ends with an auto-closable bracket.
5272 if self.autoclose_regions.is_empty() {
5273 let snapshot = self.buffer.read(cx).snapshot(cx);
5274 for selection in &mut self.selections.all::<Point>(cx) {
5275 let selection_head = selection.head();
5276 let Some(scope) = snapshot.language_scope_at(selection_head) else {
5277 continue;
5278 };
5279
5280 let mut bracket_pair = None;
5281 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
5282 let prev_chars = snapshot
5283 .reversed_chars_at(selection_head)
5284 .collect::<String>();
5285 for (pair, enabled) in scope.brackets() {
5286 if enabled
5287 && pair.close
5288 && prev_chars.starts_with(pair.start.as_str())
5289 && next_chars.starts_with(pair.end.as_str())
5290 {
5291 bracket_pair = Some(pair.clone());
5292 break;
5293 }
5294 }
5295 if let Some(pair) = bracket_pair {
5296 let start = snapshot.anchor_after(selection_head);
5297 let end = snapshot.anchor_after(selection_head);
5298 self.autoclose_regions.push(AutocloseRegion {
5299 selection_id: selection.id,
5300 range: start..end,
5301 pair,
5302 });
5303 }
5304 }
5305 }
5306 }
5307 Ok(())
5308 }
5309
5310 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5311 self.move_to_snippet_tabstop(Bias::Right, cx)
5312 }
5313
5314 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5315 self.move_to_snippet_tabstop(Bias::Left, cx)
5316 }
5317
5318 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
5319 if let Some(mut snippet) = self.snippet_stack.pop() {
5320 match bias {
5321 Bias::Left => {
5322 if snippet.active_index > 0 {
5323 snippet.active_index -= 1;
5324 } else {
5325 self.snippet_stack.push(snippet);
5326 return false;
5327 }
5328 }
5329 Bias::Right => {
5330 if snippet.active_index + 1 < snippet.ranges.len() {
5331 snippet.active_index += 1;
5332 } else {
5333 self.snippet_stack.push(snippet);
5334 return false;
5335 }
5336 }
5337 }
5338 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
5339 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5340 s.select_anchor_ranges(current_ranges.iter().cloned())
5341 });
5342 // If snippet state is not at the last tabstop, push it back on the stack
5343 if snippet.active_index + 1 < snippet.ranges.len() {
5344 self.snippet_stack.push(snippet);
5345 }
5346 return true;
5347 }
5348 }
5349
5350 false
5351 }
5352
5353 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
5354 self.transact(cx, |this, cx| {
5355 this.select_all(&SelectAll, cx);
5356 this.insert("", cx);
5357 });
5358 }
5359
5360 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
5361 self.transact(cx, |this, cx| {
5362 this.select_autoclose_pair(cx);
5363 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
5364 if !this.linked_edit_ranges.is_empty() {
5365 let selections = this.selections.all::<MultiBufferPoint>(cx);
5366 let snapshot = this.buffer.read(cx).snapshot(cx);
5367
5368 for selection in selections.iter() {
5369 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
5370 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
5371 if selection_start.buffer_id != selection_end.buffer_id {
5372 continue;
5373 }
5374 if let Some(ranges) =
5375 this.linked_editing_ranges_for(selection_start..selection_end, cx)
5376 {
5377 for (buffer, entries) in ranges {
5378 linked_ranges.entry(buffer).or_default().extend(entries);
5379 }
5380 }
5381 }
5382 }
5383
5384 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
5385 if !this.selections.line_mode {
5386 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
5387 for selection in &mut selections {
5388 if selection.is_empty() {
5389 let old_head = selection.head();
5390 let mut new_head =
5391 movement::left(&display_map, old_head.to_display_point(&display_map))
5392 .to_point(&display_map);
5393 if let Some((buffer, line_buffer_range)) = display_map
5394 .buffer_snapshot
5395 .buffer_line_for_row(MultiBufferRow(old_head.row))
5396 {
5397 let indent_size =
5398 buffer.indent_size_for_line(line_buffer_range.start.row);
5399 let indent_len = match indent_size.kind {
5400 IndentKind::Space => {
5401 buffer.settings_at(line_buffer_range.start, cx).tab_size
5402 }
5403 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
5404 };
5405 if old_head.column <= indent_size.len && old_head.column > 0 {
5406 let indent_len = indent_len.get();
5407 new_head = cmp::min(
5408 new_head,
5409 MultiBufferPoint::new(
5410 old_head.row,
5411 ((old_head.column - 1) / indent_len) * indent_len,
5412 ),
5413 );
5414 }
5415 }
5416
5417 selection.set_head(new_head, SelectionGoal::None);
5418 }
5419 }
5420 }
5421
5422 this.signature_help_state.set_backspace_pressed(true);
5423 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5424 this.insert("", cx);
5425 let empty_str: Arc<str> = Arc::from("");
5426 for (buffer, edits) in linked_ranges {
5427 let snapshot = buffer.read(cx).snapshot();
5428 use text::ToPoint as TP;
5429
5430 let edits = edits
5431 .into_iter()
5432 .map(|range| {
5433 let end_point = TP::to_point(&range.end, &snapshot);
5434 let mut start_point = TP::to_point(&range.start, &snapshot);
5435
5436 if end_point == start_point {
5437 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
5438 .saturating_sub(1);
5439 start_point = TP::to_point(&offset, &snapshot);
5440 };
5441
5442 (start_point..end_point, empty_str.clone())
5443 })
5444 .sorted_by_key(|(range, _)| range.start)
5445 .collect::<Vec<_>>();
5446 buffer.update(cx, |this, cx| {
5447 this.edit(edits, None, cx);
5448 })
5449 }
5450 this.refresh_inline_completion(true, cx);
5451 linked_editing_ranges::refresh_linked_ranges(this, cx);
5452 });
5453 }
5454
5455 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
5456 self.transact(cx, |this, cx| {
5457 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5458 let line_mode = s.line_mode;
5459 s.move_with(|map, selection| {
5460 if selection.is_empty() && !line_mode {
5461 let cursor = movement::right(map, selection.head());
5462 selection.end = cursor;
5463 selection.reversed = true;
5464 selection.goal = SelectionGoal::None;
5465 }
5466 })
5467 });
5468 this.insert("", cx);
5469 this.refresh_inline_completion(true, cx);
5470 });
5471 }
5472
5473 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
5474 if self.move_to_prev_snippet_tabstop(cx) {
5475 return;
5476 }
5477
5478 self.outdent(&Outdent, cx);
5479 }
5480
5481 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
5482 if self.move_to_next_snippet_tabstop(cx) || self.read_only(cx) {
5483 return;
5484 }
5485
5486 let mut selections = self.selections.all_adjusted(cx);
5487 let buffer = self.buffer.read(cx);
5488 let snapshot = buffer.snapshot(cx);
5489 let rows_iter = selections.iter().map(|s| s.head().row);
5490 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
5491
5492 let mut edits = Vec::new();
5493 let mut prev_edited_row = 0;
5494 let mut row_delta = 0;
5495 for selection in &mut selections {
5496 if selection.start.row != prev_edited_row {
5497 row_delta = 0;
5498 }
5499 prev_edited_row = selection.end.row;
5500
5501 // If the selection is non-empty, then increase the indentation of the selected lines.
5502 if !selection.is_empty() {
5503 row_delta =
5504 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5505 continue;
5506 }
5507
5508 // If the selection is empty and the cursor is in the leading whitespace before the
5509 // suggested indentation, then auto-indent the line.
5510 let cursor = selection.head();
5511 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
5512 if let Some(suggested_indent) =
5513 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
5514 {
5515 if cursor.column < suggested_indent.len
5516 && cursor.column <= current_indent.len
5517 && current_indent.len <= suggested_indent.len
5518 {
5519 selection.start = Point::new(cursor.row, suggested_indent.len);
5520 selection.end = selection.start;
5521 if row_delta == 0 {
5522 edits.extend(Buffer::edit_for_indent_size_adjustment(
5523 cursor.row,
5524 current_indent,
5525 suggested_indent,
5526 ));
5527 row_delta = suggested_indent.len - current_indent.len;
5528 }
5529 continue;
5530 }
5531 }
5532
5533 // Otherwise, insert a hard or soft tab.
5534 let settings = buffer.settings_at(cursor, cx);
5535 let tab_size = if settings.hard_tabs {
5536 IndentSize::tab()
5537 } else {
5538 let tab_size = settings.tab_size.get();
5539 let char_column = snapshot
5540 .text_for_range(Point::new(cursor.row, 0)..cursor)
5541 .flat_map(str::chars)
5542 .count()
5543 + row_delta as usize;
5544 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
5545 IndentSize::spaces(chars_to_next_tab_stop)
5546 };
5547 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
5548 selection.end = selection.start;
5549 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
5550 row_delta += tab_size.len;
5551 }
5552
5553 self.transact(cx, |this, cx| {
5554 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5555 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5556 this.refresh_inline_completion(true, cx);
5557 });
5558 }
5559
5560 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
5561 if self.read_only(cx) {
5562 return;
5563 }
5564 let mut selections = self.selections.all::<Point>(cx);
5565 let mut prev_edited_row = 0;
5566 let mut row_delta = 0;
5567 let mut edits = Vec::new();
5568 let buffer = self.buffer.read(cx);
5569 let snapshot = buffer.snapshot(cx);
5570 for selection in &mut selections {
5571 if selection.start.row != prev_edited_row {
5572 row_delta = 0;
5573 }
5574 prev_edited_row = selection.end.row;
5575
5576 row_delta =
5577 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5578 }
5579
5580 self.transact(cx, |this, cx| {
5581 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5582 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5583 });
5584 }
5585
5586 fn indent_selection(
5587 buffer: &MultiBuffer,
5588 snapshot: &MultiBufferSnapshot,
5589 selection: &mut Selection<Point>,
5590 edits: &mut Vec<(Range<Point>, String)>,
5591 delta_for_start_row: u32,
5592 cx: &AppContext,
5593 ) -> u32 {
5594 let settings = buffer.settings_at(selection.start, cx);
5595 let tab_size = settings.tab_size.get();
5596 let indent_kind = if settings.hard_tabs {
5597 IndentKind::Tab
5598 } else {
5599 IndentKind::Space
5600 };
5601 let mut start_row = selection.start.row;
5602 let mut end_row = selection.end.row + 1;
5603
5604 // If a selection ends at the beginning of a line, don't indent
5605 // that last line.
5606 if selection.end.column == 0 && selection.end.row > selection.start.row {
5607 end_row -= 1;
5608 }
5609
5610 // Avoid re-indenting a row that has already been indented by a
5611 // previous selection, but still update this selection's column
5612 // to reflect that indentation.
5613 if delta_for_start_row > 0 {
5614 start_row += 1;
5615 selection.start.column += delta_for_start_row;
5616 if selection.end.row == selection.start.row {
5617 selection.end.column += delta_for_start_row;
5618 }
5619 }
5620
5621 let mut delta_for_end_row = 0;
5622 let has_multiple_rows = start_row + 1 != end_row;
5623 for row in start_row..end_row {
5624 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
5625 let indent_delta = match (current_indent.kind, indent_kind) {
5626 (IndentKind::Space, IndentKind::Space) => {
5627 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
5628 IndentSize::spaces(columns_to_next_tab_stop)
5629 }
5630 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
5631 (_, IndentKind::Tab) => IndentSize::tab(),
5632 };
5633
5634 let start = if has_multiple_rows || current_indent.len < selection.start.column {
5635 0
5636 } else {
5637 selection.start.column
5638 };
5639 let row_start = Point::new(row, start);
5640 edits.push((
5641 row_start..row_start,
5642 indent_delta.chars().collect::<String>(),
5643 ));
5644
5645 // Update this selection's endpoints to reflect the indentation.
5646 if row == selection.start.row {
5647 selection.start.column += indent_delta.len;
5648 }
5649 if row == selection.end.row {
5650 selection.end.column += indent_delta.len;
5651 delta_for_end_row = indent_delta.len;
5652 }
5653 }
5654
5655 if selection.start.row == selection.end.row {
5656 delta_for_start_row + delta_for_end_row
5657 } else {
5658 delta_for_end_row
5659 }
5660 }
5661
5662 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
5663 if self.read_only(cx) {
5664 return;
5665 }
5666 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5667 let selections = self.selections.all::<Point>(cx);
5668 let mut deletion_ranges = Vec::new();
5669 let mut last_outdent = None;
5670 {
5671 let buffer = self.buffer.read(cx);
5672 let snapshot = buffer.snapshot(cx);
5673 for selection in &selections {
5674 let settings = buffer.settings_at(selection.start, cx);
5675 let tab_size = settings.tab_size.get();
5676 let mut rows = selection.spanned_rows(false, &display_map);
5677
5678 // Avoid re-outdenting a row that has already been outdented by a
5679 // previous selection.
5680 if let Some(last_row) = last_outdent {
5681 if last_row == rows.start {
5682 rows.start = rows.start.next_row();
5683 }
5684 }
5685 let has_multiple_rows = rows.len() > 1;
5686 for row in rows.iter_rows() {
5687 let indent_size = snapshot.indent_size_for_line(row);
5688 if indent_size.len > 0 {
5689 let deletion_len = match indent_size.kind {
5690 IndentKind::Space => {
5691 let columns_to_prev_tab_stop = indent_size.len % tab_size;
5692 if columns_to_prev_tab_stop == 0 {
5693 tab_size
5694 } else {
5695 columns_to_prev_tab_stop
5696 }
5697 }
5698 IndentKind::Tab => 1,
5699 };
5700 let start = if has_multiple_rows
5701 || deletion_len > selection.start.column
5702 || indent_size.len < selection.start.column
5703 {
5704 0
5705 } else {
5706 selection.start.column - deletion_len
5707 };
5708 deletion_ranges.push(
5709 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
5710 );
5711 last_outdent = Some(row);
5712 }
5713 }
5714 }
5715 }
5716
5717 self.transact(cx, |this, cx| {
5718 this.buffer.update(cx, |buffer, cx| {
5719 let empty_str: Arc<str> = "".into();
5720 buffer.edit(
5721 deletion_ranges
5722 .into_iter()
5723 .map(|range| (range, empty_str.clone())),
5724 None,
5725 cx,
5726 );
5727 });
5728 let selections = this.selections.all::<usize>(cx);
5729 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5730 });
5731 }
5732
5733 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
5734 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5735 let selections = self.selections.all::<Point>(cx);
5736
5737 let mut new_cursors = Vec::new();
5738 let mut edit_ranges = Vec::new();
5739 let mut selections = selections.iter().peekable();
5740 while let Some(selection) = selections.next() {
5741 let mut rows = selection.spanned_rows(false, &display_map);
5742 let goal_display_column = selection.head().to_display_point(&display_map).column();
5743
5744 // Accumulate contiguous regions of rows that we want to delete.
5745 while let Some(next_selection) = selections.peek() {
5746 let next_rows = next_selection.spanned_rows(false, &display_map);
5747 if next_rows.start <= rows.end {
5748 rows.end = next_rows.end;
5749 selections.next().unwrap();
5750 } else {
5751 break;
5752 }
5753 }
5754
5755 let buffer = &display_map.buffer_snapshot;
5756 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
5757 let edit_end;
5758 let cursor_buffer_row;
5759 if buffer.max_point().row >= rows.end.0 {
5760 // If there's a line after the range, delete the \n from the end of the row range
5761 // and position the cursor on the next line.
5762 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
5763 cursor_buffer_row = rows.end;
5764 } else {
5765 // If there isn't a line after the range, delete the \n from the line before the
5766 // start of the row range and position the cursor there.
5767 edit_start = edit_start.saturating_sub(1);
5768 edit_end = buffer.len();
5769 cursor_buffer_row = rows.start.previous_row();
5770 }
5771
5772 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
5773 *cursor.column_mut() =
5774 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
5775
5776 new_cursors.push((
5777 selection.id,
5778 buffer.anchor_after(cursor.to_point(&display_map)),
5779 ));
5780 edit_ranges.push(edit_start..edit_end);
5781 }
5782
5783 self.transact(cx, |this, cx| {
5784 let buffer = this.buffer.update(cx, |buffer, cx| {
5785 let empty_str: Arc<str> = "".into();
5786 buffer.edit(
5787 edit_ranges
5788 .into_iter()
5789 .map(|range| (range, empty_str.clone())),
5790 None,
5791 cx,
5792 );
5793 buffer.snapshot(cx)
5794 });
5795 let new_selections = new_cursors
5796 .into_iter()
5797 .map(|(id, cursor)| {
5798 let cursor = cursor.to_point(&buffer);
5799 Selection {
5800 id,
5801 start: cursor,
5802 end: cursor,
5803 reversed: false,
5804 goal: SelectionGoal::None,
5805 }
5806 })
5807 .collect();
5808
5809 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5810 s.select(new_selections);
5811 });
5812 });
5813 }
5814
5815 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
5816 if self.read_only(cx) {
5817 return;
5818 }
5819 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
5820 for selection in self.selections.all::<Point>(cx) {
5821 let start = MultiBufferRow(selection.start.row);
5822 let end = if selection.start.row == selection.end.row {
5823 MultiBufferRow(selection.start.row + 1)
5824 } else {
5825 MultiBufferRow(selection.end.row)
5826 };
5827
5828 if let Some(last_row_range) = row_ranges.last_mut() {
5829 if start <= last_row_range.end {
5830 last_row_range.end = end;
5831 continue;
5832 }
5833 }
5834 row_ranges.push(start..end);
5835 }
5836
5837 let snapshot = self.buffer.read(cx).snapshot(cx);
5838 let mut cursor_positions = Vec::new();
5839 for row_range in &row_ranges {
5840 let anchor = snapshot.anchor_before(Point::new(
5841 row_range.end.previous_row().0,
5842 snapshot.line_len(row_range.end.previous_row()),
5843 ));
5844 cursor_positions.push(anchor..anchor);
5845 }
5846
5847 self.transact(cx, |this, cx| {
5848 for row_range in row_ranges.into_iter().rev() {
5849 for row in row_range.iter_rows().rev() {
5850 let end_of_line = Point::new(row.0, snapshot.line_len(row));
5851 let next_line_row = row.next_row();
5852 let indent = snapshot.indent_size_for_line(next_line_row);
5853 let start_of_next_line = Point::new(next_line_row.0, indent.len);
5854
5855 let replace = if snapshot.line_len(next_line_row) > indent.len {
5856 " "
5857 } else {
5858 ""
5859 };
5860
5861 this.buffer.update(cx, |buffer, cx| {
5862 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
5863 });
5864 }
5865 }
5866
5867 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5868 s.select_anchor_ranges(cursor_positions)
5869 });
5870 });
5871 }
5872
5873 pub fn sort_lines_case_sensitive(
5874 &mut self,
5875 _: &SortLinesCaseSensitive,
5876 cx: &mut ViewContext<Self>,
5877 ) {
5878 self.manipulate_lines(cx, |lines| lines.sort())
5879 }
5880
5881 pub fn sort_lines_case_insensitive(
5882 &mut self,
5883 _: &SortLinesCaseInsensitive,
5884 cx: &mut ViewContext<Self>,
5885 ) {
5886 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
5887 }
5888
5889 pub fn unique_lines_case_insensitive(
5890 &mut self,
5891 _: &UniqueLinesCaseInsensitive,
5892 cx: &mut ViewContext<Self>,
5893 ) {
5894 self.manipulate_lines(cx, |lines| {
5895 let mut seen = HashSet::default();
5896 lines.retain(|line| seen.insert(line.to_lowercase()));
5897 })
5898 }
5899
5900 pub fn unique_lines_case_sensitive(
5901 &mut self,
5902 _: &UniqueLinesCaseSensitive,
5903 cx: &mut ViewContext<Self>,
5904 ) {
5905 self.manipulate_lines(cx, |lines| {
5906 let mut seen = HashSet::default();
5907 lines.retain(|line| seen.insert(*line));
5908 })
5909 }
5910
5911 pub fn revert_selected_hunks(&mut self, _: &RevertSelectedHunks, cx: &mut ViewContext<Self>) {
5912 let revert_changes = self.gather_revert_changes(&self.selections.disjoint_anchors(), cx);
5913 if !revert_changes.is_empty() {
5914 self.transact(cx, |editor, cx| {
5915 editor.revert(revert_changes, cx);
5916 });
5917 }
5918 }
5919
5920 pub fn open_active_item_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
5921 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
5922 let project_path = buffer.read(cx).project_path(cx)?;
5923 let project = self.project.as_ref()?.read(cx);
5924 let entry = project.entry_for_path(&project_path, cx)?;
5925 let abs_path = project.absolute_path(&project_path, cx)?;
5926 let parent = if entry.is_symlink {
5927 abs_path.canonicalize().ok()?
5928 } else {
5929 abs_path
5930 }
5931 .parent()?
5932 .to_path_buf();
5933 Some(parent)
5934 }) {
5935 cx.dispatch_action(OpenTerminal { working_directory }.boxed_clone());
5936 }
5937 }
5938
5939 fn gather_revert_changes(
5940 &mut self,
5941 selections: &[Selection<Anchor>],
5942 cx: &mut ViewContext<'_, Editor>,
5943 ) -> HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>> {
5944 let mut revert_changes = HashMap::default();
5945 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
5946 for hunk in hunks_for_selections(&multi_buffer_snapshot, selections) {
5947 Self::prepare_revert_change(&mut revert_changes, self.buffer(), &hunk, cx);
5948 }
5949 revert_changes
5950 }
5951
5952 pub fn prepare_revert_change(
5953 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
5954 multi_buffer: &Model<MultiBuffer>,
5955 hunk: &DiffHunk<MultiBufferRow>,
5956 cx: &AppContext,
5957 ) -> Option<()> {
5958 let buffer = multi_buffer.read(cx).buffer(hunk.buffer_id)?;
5959 let buffer = buffer.read(cx);
5960 let original_text = buffer.diff_base()?.slice(hunk.diff_base_byte_range.clone());
5961 let buffer_snapshot = buffer.snapshot();
5962 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
5963 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
5964 probe
5965 .0
5966 .start
5967 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
5968 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
5969 }) {
5970 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
5971 Some(())
5972 } else {
5973 None
5974 }
5975 }
5976
5977 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
5978 self.manipulate_lines(cx, |lines| lines.reverse())
5979 }
5980
5981 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
5982 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
5983 }
5984
5985 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
5986 where
5987 Fn: FnMut(&mut Vec<&str>),
5988 {
5989 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5990 let buffer = self.buffer.read(cx).snapshot(cx);
5991
5992 let mut edits = Vec::new();
5993
5994 let selections = self.selections.all::<Point>(cx);
5995 let mut selections = selections.iter().peekable();
5996 let mut contiguous_row_selections = Vec::new();
5997 let mut new_selections = Vec::new();
5998 let mut added_lines = 0;
5999 let mut removed_lines = 0;
6000
6001 while let Some(selection) = selections.next() {
6002 let (start_row, end_row) = consume_contiguous_rows(
6003 &mut contiguous_row_selections,
6004 selection,
6005 &display_map,
6006 &mut selections,
6007 );
6008
6009 let start_point = Point::new(start_row.0, 0);
6010 let end_point = Point::new(
6011 end_row.previous_row().0,
6012 buffer.line_len(end_row.previous_row()),
6013 );
6014 let text = buffer
6015 .text_for_range(start_point..end_point)
6016 .collect::<String>();
6017
6018 let mut lines = text.split('\n').collect_vec();
6019
6020 let lines_before = lines.len();
6021 callback(&mut lines);
6022 let lines_after = lines.len();
6023
6024 edits.push((start_point..end_point, lines.join("\n")));
6025
6026 // Selections must change based on added and removed line count
6027 let start_row =
6028 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
6029 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
6030 new_selections.push(Selection {
6031 id: selection.id,
6032 start: start_row,
6033 end: end_row,
6034 goal: SelectionGoal::None,
6035 reversed: selection.reversed,
6036 });
6037
6038 if lines_after > lines_before {
6039 added_lines += lines_after - lines_before;
6040 } else if lines_before > lines_after {
6041 removed_lines += lines_before - lines_after;
6042 }
6043 }
6044
6045 self.transact(cx, |this, cx| {
6046 let buffer = this.buffer.update(cx, |buffer, cx| {
6047 buffer.edit(edits, None, cx);
6048 buffer.snapshot(cx)
6049 });
6050
6051 // Recalculate offsets on newly edited buffer
6052 let new_selections = new_selections
6053 .iter()
6054 .map(|s| {
6055 let start_point = Point::new(s.start.0, 0);
6056 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
6057 Selection {
6058 id: s.id,
6059 start: buffer.point_to_offset(start_point),
6060 end: buffer.point_to_offset(end_point),
6061 goal: s.goal,
6062 reversed: s.reversed,
6063 }
6064 })
6065 .collect();
6066
6067 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6068 s.select(new_selections);
6069 });
6070
6071 this.request_autoscroll(Autoscroll::fit(), cx);
6072 });
6073 }
6074
6075 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
6076 self.manipulate_text(cx, |text| text.to_uppercase())
6077 }
6078
6079 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
6080 self.manipulate_text(cx, |text| text.to_lowercase())
6081 }
6082
6083 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
6084 self.manipulate_text(cx, |text| {
6085 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6086 // https://github.com/rutrum/convert-case/issues/16
6087 text.split('\n')
6088 .map(|line| line.to_case(Case::Title))
6089 .join("\n")
6090 })
6091 }
6092
6093 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
6094 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
6095 }
6096
6097 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
6098 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
6099 }
6100
6101 pub fn convert_to_upper_camel_case(
6102 &mut self,
6103 _: &ConvertToUpperCamelCase,
6104 cx: &mut ViewContext<Self>,
6105 ) {
6106 self.manipulate_text(cx, |text| {
6107 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6108 // https://github.com/rutrum/convert-case/issues/16
6109 text.split('\n')
6110 .map(|line| line.to_case(Case::UpperCamel))
6111 .join("\n")
6112 })
6113 }
6114
6115 pub fn convert_to_lower_camel_case(
6116 &mut self,
6117 _: &ConvertToLowerCamelCase,
6118 cx: &mut ViewContext<Self>,
6119 ) {
6120 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
6121 }
6122
6123 pub fn convert_to_opposite_case(
6124 &mut self,
6125 _: &ConvertToOppositeCase,
6126 cx: &mut ViewContext<Self>,
6127 ) {
6128 self.manipulate_text(cx, |text| {
6129 text.chars()
6130 .fold(String::with_capacity(text.len()), |mut t, c| {
6131 if c.is_uppercase() {
6132 t.extend(c.to_lowercase());
6133 } else {
6134 t.extend(c.to_uppercase());
6135 }
6136 t
6137 })
6138 })
6139 }
6140
6141 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6142 where
6143 Fn: FnMut(&str) -> String,
6144 {
6145 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6146 let buffer = self.buffer.read(cx).snapshot(cx);
6147
6148 let mut new_selections = Vec::new();
6149 let mut edits = Vec::new();
6150 let mut selection_adjustment = 0i32;
6151
6152 for selection in self.selections.all::<usize>(cx) {
6153 let selection_is_empty = selection.is_empty();
6154
6155 let (start, end) = if selection_is_empty {
6156 let word_range = movement::surrounding_word(
6157 &display_map,
6158 selection.start.to_display_point(&display_map),
6159 );
6160 let start = word_range.start.to_offset(&display_map, Bias::Left);
6161 let end = word_range.end.to_offset(&display_map, Bias::Left);
6162 (start, end)
6163 } else {
6164 (selection.start, selection.end)
6165 };
6166
6167 let text = buffer.text_for_range(start..end).collect::<String>();
6168 let old_length = text.len() as i32;
6169 let text = callback(&text);
6170
6171 new_selections.push(Selection {
6172 start: (start as i32 - selection_adjustment) as usize,
6173 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
6174 goal: SelectionGoal::None,
6175 ..selection
6176 });
6177
6178 selection_adjustment += old_length - text.len() as i32;
6179
6180 edits.push((start..end, text));
6181 }
6182
6183 self.transact(cx, |this, cx| {
6184 this.buffer.update(cx, |buffer, cx| {
6185 buffer.edit(edits, None, cx);
6186 });
6187
6188 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6189 s.select(new_selections);
6190 });
6191
6192 this.request_autoscroll(Autoscroll::fit(), cx);
6193 });
6194 }
6195
6196 pub fn duplicate_line(&mut self, upwards: bool, cx: &mut ViewContext<Self>) {
6197 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6198 let buffer = &display_map.buffer_snapshot;
6199 let selections = self.selections.all::<Point>(cx);
6200
6201 let mut edits = Vec::new();
6202 let mut selections_iter = selections.iter().peekable();
6203 while let Some(selection) = selections_iter.next() {
6204 // Avoid duplicating the same lines twice.
6205 let mut rows = selection.spanned_rows(false, &display_map);
6206
6207 while let Some(next_selection) = selections_iter.peek() {
6208 let next_rows = next_selection.spanned_rows(false, &display_map);
6209 if next_rows.start < rows.end {
6210 rows.end = next_rows.end;
6211 selections_iter.next().unwrap();
6212 } else {
6213 break;
6214 }
6215 }
6216
6217 // Copy the text from the selected row region and splice it either at the start
6218 // or end of the region.
6219 let start = Point::new(rows.start.0, 0);
6220 let end = Point::new(
6221 rows.end.previous_row().0,
6222 buffer.line_len(rows.end.previous_row()),
6223 );
6224 let text = buffer
6225 .text_for_range(start..end)
6226 .chain(Some("\n"))
6227 .collect::<String>();
6228 let insert_location = if upwards {
6229 Point::new(rows.end.0, 0)
6230 } else {
6231 start
6232 };
6233 edits.push((insert_location..insert_location, text));
6234 }
6235
6236 self.transact(cx, |this, cx| {
6237 this.buffer.update(cx, |buffer, cx| {
6238 buffer.edit(edits, None, cx);
6239 });
6240
6241 this.request_autoscroll(Autoscroll::fit(), cx);
6242 });
6243 }
6244
6245 pub fn duplicate_line_up(&mut self, _: &DuplicateLineUp, cx: &mut ViewContext<Self>) {
6246 self.duplicate_line(true, cx);
6247 }
6248
6249 pub fn duplicate_line_down(&mut self, _: &DuplicateLineDown, cx: &mut ViewContext<Self>) {
6250 self.duplicate_line(false, cx);
6251 }
6252
6253 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
6254 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6255 let buffer = self.buffer.read(cx).snapshot(cx);
6256
6257 let mut edits = Vec::new();
6258 let mut unfold_ranges = Vec::new();
6259 let mut refold_ranges = Vec::new();
6260
6261 let selections = self.selections.all::<Point>(cx);
6262 let mut selections = selections.iter().peekable();
6263 let mut contiguous_row_selections = Vec::new();
6264 let mut new_selections = Vec::new();
6265
6266 while let Some(selection) = selections.next() {
6267 // Find all the selections that span a contiguous row range
6268 let (start_row, end_row) = consume_contiguous_rows(
6269 &mut contiguous_row_selections,
6270 selection,
6271 &display_map,
6272 &mut selections,
6273 );
6274
6275 // Move the text spanned by the row range to be before the line preceding the row range
6276 if start_row.0 > 0 {
6277 let range_to_move = Point::new(
6278 start_row.previous_row().0,
6279 buffer.line_len(start_row.previous_row()),
6280 )
6281 ..Point::new(
6282 end_row.previous_row().0,
6283 buffer.line_len(end_row.previous_row()),
6284 );
6285 let insertion_point = display_map
6286 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
6287 .0;
6288
6289 // Don't move lines across excerpts
6290 if buffer
6291 .excerpt_boundaries_in_range((
6292 Bound::Excluded(insertion_point),
6293 Bound::Included(range_to_move.end),
6294 ))
6295 .next()
6296 .is_none()
6297 {
6298 let text = buffer
6299 .text_for_range(range_to_move.clone())
6300 .flat_map(|s| s.chars())
6301 .skip(1)
6302 .chain(['\n'])
6303 .collect::<String>();
6304
6305 edits.push((
6306 buffer.anchor_after(range_to_move.start)
6307 ..buffer.anchor_before(range_to_move.end),
6308 String::new(),
6309 ));
6310 let insertion_anchor = buffer.anchor_after(insertion_point);
6311 edits.push((insertion_anchor..insertion_anchor, text));
6312
6313 let row_delta = range_to_move.start.row - insertion_point.row + 1;
6314
6315 // Move selections up
6316 new_selections.extend(contiguous_row_selections.drain(..).map(
6317 |mut selection| {
6318 selection.start.row -= row_delta;
6319 selection.end.row -= row_delta;
6320 selection
6321 },
6322 ));
6323
6324 // Move folds up
6325 unfold_ranges.push(range_to_move.clone());
6326 for fold in display_map.folds_in_range(
6327 buffer.anchor_before(range_to_move.start)
6328 ..buffer.anchor_after(range_to_move.end),
6329 ) {
6330 let mut start = fold.range.start.to_point(&buffer);
6331 let mut end = fold.range.end.to_point(&buffer);
6332 start.row -= row_delta;
6333 end.row -= row_delta;
6334 refold_ranges.push((start..end, fold.placeholder.clone()));
6335 }
6336 }
6337 }
6338
6339 // If we didn't move line(s), preserve the existing selections
6340 new_selections.append(&mut contiguous_row_selections);
6341 }
6342
6343 self.transact(cx, |this, cx| {
6344 this.unfold_ranges(unfold_ranges, true, true, cx);
6345 this.buffer.update(cx, |buffer, cx| {
6346 for (range, text) in edits {
6347 buffer.edit([(range, text)], None, cx);
6348 }
6349 });
6350 this.fold_ranges(refold_ranges, true, cx);
6351 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6352 s.select(new_selections);
6353 })
6354 });
6355 }
6356
6357 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
6358 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6359 let buffer = self.buffer.read(cx).snapshot(cx);
6360
6361 let mut edits = Vec::new();
6362 let mut unfold_ranges = Vec::new();
6363 let mut refold_ranges = Vec::new();
6364
6365 let selections = self.selections.all::<Point>(cx);
6366 let mut selections = selections.iter().peekable();
6367 let mut contiguous_row_selections = Vec::new();
6368 let mut new_selections = Vec::new();
6369
6370 while let Some(selection) = selections.next() {
6371 // Find all the selections that span a contiguous row range
6372 let (start_row, end_row) = consume_contiguous_rows(
6373 &mut contiguous_row_selections,
6374 selection,
6375 &display_map,
6376 &mut selections,
6377 );
6378
6379 // Move the text spanned by the row range to be after the last line of the row range
6380 if end_row.0 <= buffer.max_point().row {
6381 let range_to_move =
6382 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
6383 let insertion_point = display_map
6384 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
6385 .0;
6386
6387 // Don't move lines across excerpt boundaries
6388 if buffer
6389 .excerpt_boundaries_in_range((
6390 Bound::Excluded(range_to_move.start),
6391 Bound::Included(insertion_point),
6392 ))
6393 .next()
6394 .is_none()
6395 {
6396 let mut text = String::from("\n");
6397 text.extend(buffer.text_for_range(range_to_move.clone()));
6398 text.pop(); // Drop trailing newline
6399 edits.push((
6400 buffer.anchor_after(range_to_move.start)
6401 ..buffer.anchor_before(range_to_move.end),
6402 String::new(),
6403 ));
6404 let insertion_anchor = buffer.anchor_after(insertion_point);
6405 edits.push((insertion_anchor..insertion_anchor, text));
6406
6407 let row_delta = insertion_point.row - range_to_move.end.row + 1;
6408
6409 // Move selections down
6410 new_selections.extend(contiguous_row_selections.drain(..).map(
6411 |mut selection| {
6412 selection.start.row += row_delta;
6413 selection.end.row += row_delta;
6414 selection
6415 },
6416 ));
6417
6418 // Move folds down
6419 unfold_ranges.push(range_to_move.clone());
6420 for fold in display_map.folds_in_range(
6421 buffer.anchor_before(range_to_move.start)
6422 ..buffer.anchor_after(range_to_move.end),
6423 ) {
6424 let mut start = fold.range.start.to_point(&buffer);
6425 let mut end = fold.range.end.to_point(&buffer);
6426 start.row += row_delta;
6427 end.row += row_delta;
6428 refold_ranges.push((start..end, fold.placeholder.clone()));
6429 }
6430 }
6431 }
6432
6433 // If we didn't move line(s), preserve the existing selections
6434 new_selections.append(&mut contiguous_row_selections);
6435 }
6436
6437 self.transact(cx, |this, cx| {
6438 this.unfold_ranges(unfold_ranges, true, true, cx);
6439 this.buffer.update(cx, |buffer, cx| {
6440 for (range, text) in edits {
6441 buffer.edit([(range, text)], None, cx);
6442 }
6443 });
6444 this.fold_ranges(refold_ranges, true, cx);
6445 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
6446 });
6447 }
6448
6449 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
6450 let text_layout_details = &self.text_layout_details(cx);
6451 self.transact(cx, |this, cx| {
6452 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6453 let mut edits: Vec<(Range<usize>, String)> = Default::default();
6454 let line_mode = s.line_mode;
6455 s.move_with(|display_map, selection| {
6456 if !selection.is_empty() || line_mode {
6457 return;
6458 }
6459
6460 let mut head = selection.head();
6461 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
6462 if head.column() == display_map.line_len(head.row()) {
6463 transpose_offset = display_map
6464 .buffer_snapshot
6465 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6466 }
6467
6468 if transpose_offset == 0 {
6469 return;
6470 }
6471
6472 *head.column_mut() += 1;
6473 head = display_map.clip_point(head, Bias::Right);
6474 let goal = SelectionGoal::HorizontalPosition(
6475 display_map
6476 .x_for_display_point(head, &text_layout_details)
6477 .into(),
6478 );
6479 selection.collapse_to(head, goal);
6480
6481 let transpose_start = display_map
6482 .buffer_snapshot
6483 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6484 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
6485 let transpose_end = display_map
6486 .buffer_snapshot
6487 .clip_offset(transpose_offset + 1, Bias::Right);
6488 if let Some(ch) =
6489 display_map.buffer_snapshot.chars_at(transpose_start).next()
6490 {
6491 edits.push((transpose_start..transpose_offset, String::new()));
6492 edits.push((transpose_end..transpose_end, ch.to_string()));
6493 }
6494 }
6495 });
6496 edits
6497 });
6498 this.buffer
6499 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
6500 let selections = this.selections.all::<usize>(cx);
6501 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6502 s.select(selections);
6503 });
6504 });
6505 }
6506
6507 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
6508 let mut text = String::new();
6509 let buffer = self.buffer.read(cx).snapshot(cx);
6510 let mut selections = self.selections.all::<Point>(cx);
6511 let mut clipboard_selections = Vec::with_capacity(selections.len());
6512 {
6513 let max_point = buffer.max_point();
6514 let mut is_first = true;
6515 for selection in &mut selections {
6516 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6517 if is_entire_line {
6518 selection.start = Point::new(selection.start.row, 0);
6519 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
6520 selection.goal = SelectionGoal::None;
6521 }
6522 if is_first {
6523 is_first = false;
6524 } else {
6525 text += "\n";
6526 }
6527 let mut len = 0;
6528 for chunk in buffer.text_for_range(selection.start..selection.end) {
6529 text.push_str(chunk);
6530 len += chunk.len();
6531 }
6532 clipboard_selections.push(ClipboardSelection {
6533 len,
6534 is_entire_line,
6535 first_line_indent: buffer
6536 .indent_size_for_line(MultiBufferRow(selection.start.row))
6537 .len,
6538 });
6539 }
6540 }
6541
6542 self.transact(cx, |this, cx| {
6543 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6544 s.select(selections);
6545 });
6546 this.insert("", cx);
6547 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
6548 });
6549 }
6550
6551 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
6552 let selections = self.selections.all::<Point>(cx);
6553 let buffer = self.buffer.read(cx).read(cx);
6554 let mut text = String::new();
6555
6556 let mut clipboard_selections = Vec::with_capacity(selections.len());
6557 {
6558 let max_point = buffer.max_point();
6559 let mut is_first = true;
6560 for selection in selections.iter() {
6561 let mut start = selection.start;
6562 let mut end = selection.end;
6563 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6564 if is_entire_line {
6565 start = Point::new(start.row, 0);
6566 end = cmp::min(max_point, Point::new(end.row + 1, 0));
6567 }
6568 if is_first {
6569 is_first = false;
6570 } else {
6571 text += "\n";
6572 }
6573 let mut len = 0;
6574 for chunk in buffer.text_for_range(start..end) {
6575 text.push_str(chunk);
6576 len += chunk.len();
6577 }
6578 clipboard_selections.push(ClipboardSelection {
6579 len,
6580 is_entire_line,
6581 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
6582 });
6583 }
6584 }
6585
6586 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
6587 }
6588
6589 pub fn do_paste(
6590 &mut self,
6591 text: &String,
6592 clipboard_selections: Option<Vec<ClipboardSelection>>,
6593 handle_entire_lines: bool,
6594 cx: &mut ViewContext<Self>,
6595 ) {
6596 if self.read_only(cx) {
6597 return;
6598 }
6599
6600 let clipboard_text = Cow::Borrowed(text);
6601
6602 self.transact(cx, |this, cx| {
6603 if let Some(mut clipboard_selections) = clipboard_selections {
6604 let old_selections = this.selections.all::<usize>(cx);
6605 let all_selections_were_entire_line =
6606 clipboard_selections.iter().all(|s| s.is_entire_line);
6607 let first_selection_indent_column =
6608 clipboard_selections.first().map(|s| s.first_line_indent);
6609 if clipboard_selections.len() != old_selections.len() {
6610 clipboard_selections.drain(..);
6611 }
6612
6613 this.buffer.update(cx, |buffer, cx| {
6614 let snapshot = buffer.read(cx);
6615 let mut start_offset = 0;
6616 let mut edits = Vec::new();
6617 let mut original_indent_columns = Vec::new();
6618 for (ix, selection) in old_selections.iter().enumerate() {
6619 let to_insert;
6620 let entire_line;
6621 let original_indent_column;
6622 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
6623 let end_offset = start_offset + clipboard_selection.len;
6624 to_insert = &clipboard_text[start_offset..end_offset];
6625 entire_line = clipboard_selection.is_entire_line;
6626 start_offset = end_offset + 1;
6627 original_indent_column = Some(clipboard_selection.first_line_indent);
6628 } else {
6629 to_insert = clipboard_text.as_str();
6630 entire_line = all_selections_were_entire_line;
6631 original_indent_column = first_selection_indent_column
6632 }
6633
6634 // If the corresponding selection was empty when this slice of the
6635 // clipboard text was written, then the entire line containing the
6636 // selection was copied. If this selection is also currently empty,
6637 // then paste the line before the current line of the buffer.
6638 let range = if selection.is_empty() && handle_entire_lines && entire_line {
6639 let column = selection.start.to_point(&snapshot).column as usize;
6640 let line_start = selection.start - column;
6641 line_start..line_start
6642 } else {
6643 selection.range()
6644 };
6645
6646 edits.push((range, to_insert));
6647 original_indent_columns.extend(original_indent_column);
6648 }
6649 drop(snapshot);
6650
6651 buffer.edit(
6652 edits,
6653 Some(AutoindentMode::Block {
6654 original_indent_columns,
6655 }),
6656 cx,
6657 );
6658 });
6659
6660 let selections = this.selections.all::<usize>(cx);
6661 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6662 } else {
6663 this.insert(&clipboard_text, cx);
6664 }
6665 });
6666 }
6667
6668 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
6669 if let Some(item) = cx.read_from_clipboard() {
6670 self.do_paste(
6671 item.text(),
6672 item.metadata::<Vec<ClipboardSelection>>(),
6673 true,
6674 cx,
6675 )
6676 };
6677 }
6678
6679 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
6680 if self.read_only(cx) {
6681 return;
6682 }
6683
6684 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
6685 if let Some((selections, _)) =
6686 self.selection_history.transaction(transaction_id).cloned()
6687 {
6688 self.change_selections(None, cx, |s| {
6689 s.select_anchors(selections.to_vec());
6690 });
6691 }
6692 self.request_autoscroll(Autoscroll::fit(), cx);
6693 self.unmark_text(cx);
6694 self.refresh_inline_completion(true, cx);
6695 cx.emit(EditorEvent::Edited { transaction_id });
6696 cx.emit(EditorEvent::TransactionUndone { transaction_id });
6697 }
6698 }
6699
6700 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
6701 if self.read_only(cx) {
6702 return;
6703 }
6704
6705 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
6706 if let Some((_, Some(selections))) =
6707 self.selection_history.transaction(transaction_id).cloned()
6708 {
6709 self.change_selections(None, cx, |s| {
6710 s.select_anchors(selections.to_vec());
6711 });
6712 }
6713 self.request_autoscroll(Autoscroll::fit(), cx);
6714 self.unmark_text(cx);
6715 self.refresh_inline_completion(true, cx);
6716 cx.emit(EditorEvent::Edited { transaction_id });
6717 }
6718 }
6719
6720 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
6721 self.buffer
6722 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
6723 }
6724
6725 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut ViewContext<Self>) {
6726 self.buffer
6727 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
6728 }
6729
6730 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
6731 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6732 let line_mode = s.line_mode;
6733 s.move_with(|map, selection| {
6734 let cursor = if selection.is_empty() && !line_mode {
6735 movement::left(map, selection.start)
6736 } else {
6737 selection.start
6738 };
6739 selection.collapse_to(cursor, SelectionGoal::None);
6740 });
6741 })
6742 }
6743
6744 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
6745 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6746 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
6747 })
6748 }
6749
6750 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
6751 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6752 let line_mode = s.line_mode;
6753 s.move_with(|map, selection| {
6754 let cursor = if selection.is_empty() && !line_mode {
6755 movement::right(map, selection.end)
6756 } else {
6757 selection.end
6758 };
6759 selection.collapse_to(cursor, SelectionGoal::None)
6760 });
6761 })
6762 }
6763
6764 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
6765 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6766 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
6767 })
6768 }
6769
6770 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
6771 if self.take_rename(true, cx).is_some() {
6772 return;
6773 }
6774
6775 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6776 cx.propagate();
6777 return;
6778 }
6779
6780 let text_layout_details = &self.text_layout_details(cx);
6781 let selection_count = self.selections.count();
6782 let first_selection = self.selections.first_anchor();
6783
6784 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6785 let line_mode = s.line_mode;
6786 s.move_with(|map, selection| {
6787 if !selection.is_empty() && !line_mode {
6788 selection.goal = SelectionGoal::None;
6789 }
6790 let (cursor, goal) = movement::up(
6791 map,
6792 selection.start,
6793 selection.goal,
6794 false,
6795 &text_layout_details,
6796 );
6797 selection.collapse_to(cursor, goal);
6798 });
6799 });
6800
6801 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
6802 {
6803 cx.propagate();
6804 }
6805 }
6806
6807 pub fn move_up_by_lines(&mut self, action: &MoveUpByLines, cx: &mut ViewContext<Self>) {
6808 if self.take_rename(true, cx).is_some() {
6809 return;
6810 }
6811
6812 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6813 cx.propagate();
6814 return;
6815 }
6816
6817 let text_layout_details = &self.text_layout_details(cx);
6818
6819 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6820 let line_mode = s.line_mode;
6821 s.move_with(|map, selection| {
6822 if !selection.is_empty() && !line_mode {
6823 selection.goal = SelectionGoal::None;
6824 }
6825 let (cursor, goal) = movement::up_by_rows(
6826 map,
6827 selection.start,
6828 action.lines,
6829 selection.goal,
6830 false,
6831 &text_layout_details,
6832 );
6833 selection.collapse_to(cursor, goal);
6834 });
6835 })
6836 }
6837
6838 pub fn move_down_by_lines(&mut self, action: &MoveDownByLines, cx: &mut ViewContext<Self>) {
6839 if self.take_rename(true, cx).is_some() {
6840 return;
6841 }
6842
6843 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6844 cx.propagate();
6845 return;
6846 }
6847
6848 let text_layout_details = &self.text_layout_details(cx);
6849
6850 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6851 let line_mode = s.line_mode;
6852 s.move_with(|map, selection| {
6853 if !selection.is_empty() && !line_mode {
6854 selection.goal = SelectionGoal::None;
6855 }
6856 let (cursor, goal) = movement::down_by_rows(
6857 map,
6858 selection.start,
6859 action.lines,
6860 selection.goal,
6861 false,
6862 &text_layout_details,
6863 );
6864 selection.collapse_to(cursor, goal);
6865 });
6866 })
6867 }
6868
6869 pub fn select_down_by_lines(&mut self, action: &SelectDownByLines, cx: &mut ViewContext<Self>) {
6870 let text_layout_details = &self.text_layout_details(cx);
6871 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6872 s.move_heads_with(|map, head, goal| {
6873 movement::down_by_rows(map, head, action.lines, goal, false, &text_layout_details)
6874 })
6875 })
6876 }
6877
6878 pub fn select_up_by_lines(&mut self, action: &SelectUpByLines, cx: &mut ViewContext<Self>) {
6879 let text_layout_details = &self.text_layout_details(cx);
6880 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6881 s.move_heads_with(|map, head, goal| {
6882 movement::up_by_rows(map, head, action.lines, goal, false, &text_layout_details)
6883 })
6884 })
6885 }
6886
6887 pub fn select_page_up(&mut self, _: &SelectPageUp, cx: &mut ViewContext<Self>) {
6888 let Some(row_count) = self.visible_row_count() else {
6889 return;
6890 };
6891
6892 let text_layout_details = &self.text_layout_details(cx);
6893
6894 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6895 s.move_heads_with(|map, head, goal| {
6896 movement::up_by_rows(map, head, row_count, goal, false, &text_layout_details)
6897 })
6898 })
6899 }
6900
6901 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
6902 if self.take_rename(true, cx).is_some() {
6903 return;
6904 }
6905
6906 if self
6907 .context_menu
6908 .write()
6909 .as_mut()
6910 .map(|menu| menu.select_first(self.project.as_ref(), cx))
6911 .unwrap_or(false)
6912 {
6913 return;
6914 }
6915
6916 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6917 cx.propagate();
6918 return;
6919 }
6920
6921 let Some(row_count) = self.visible_row_count() else {
6922 return;
6923 };
6924
6925 let autoscroll = if action.center_cursor {
6926 Autoscroll::center()
6927 } else {
6928 Autoscroll::fit()
6929 };
6930
6931 let text_layout_details = &self.text_layout_details(cx);
6932
6933 self.change_selections(Some(autoscroll), cx, |s| {
6934 let line_mode = s.line_mode;
6935 s.move_with(|map, selection| {
6936 if !selection.is_empty() && !line_mode {
6937 selection.goal = SelectionGoal::None;
6938 }
6939 let (cursor, goal) = movement::up_by_rows(
6940 map,
6941 selection.end,
6942 row_count,
6943 selection.goal,
6944 false,
6945 &text_layout_details,
6946 );
6947 selection.collapse_to(cursor, goal);
6948 });
6949 });
6950 }
6951
6952 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
6953 let text_layout_details = &self.text_layout_details(cx);
6954 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6955 s.move_heads_with(|map, head, goal| {
6956 movement::up(map, head, goal, false, &text_layout_details)
6957 })
6958 })
6959 }
6960
6961 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
6962 self.take_rename(true, cx);
6963
6964 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6965 cx.propagate();
6966 return;
6967 }
6968
6969 let text_layout_details = &self.text_layout_details(cx);
6970 let selection_count = self.selections.count();
6971 let first_selection = self.selections.first_anchor();
6972
6973 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6974 let line_mode = s.line_mode;
6975 s.move_with(|map, selection| {
6976 if !selection.is_empty() && !line_mode {
6977 selection.goal = SelectionGoal::None;
6978 }
6979 let (cursor, goal) = movement::down(
6980 map,
6981 selection.end,
6982 selection.goal,
6983 false,
6984 &text_layout_details,
6985 );
6986 selection.collapse_to(cursor, goal);
6987 });
6988 });
6989
6990 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
6991 {
6992 cx.propagate();
6993 }
6994 }
6995
6996 pub fn select_page_down(&mut self, _: &SelectPageDown, cx: &mut ViewContext<Self>) {
6997 let Some(row_count) = self.visible_row_count() else {
6998 return;
6999 };
7000
7001 let text_layout_details = &self.text_layout_details(cx);
7002
7003 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7004 s.move_heads_with(|map, head, goal| {
7005 movement::down_by_rows(map, head, row_count, goal, false, &text_layout_details)
7006 })
7007 })
7008 }
7009
7010 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
7011 if self.take_rename(true, cx).is_some() {
7012 return;
7013 }
7014
7015 if self
7016 .context_menu
7017 .write()
7018 .as_mut()
7019 .map(|menu| menu.select_last(self.project.as_ref(), cx))
7020 .unwrap_or(false)
7021 {
7022 return;
7023 }
7024
7025 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7026 cx.propagate();
7027 return;
7028 }
7029
7030 let Some(row_count) = self.visible_row_count() else {
7031 return;
7032 };
7033
7034 let autoscroll = if action.center_cursor {
7035 Autoscroll::center()
7036 } else {
7037 Autoscroll::fit()
7038 };
7039
7040 let text_layout_details = &self.text_layout_details(cx);
7041 self.change_selections(Some(autoscroll), cx, |s| {
7042 let line_mode = s.line_mode;
7043 s.move_with(|map, selection| {
7044 if !selection.is_empty() && !line_mode {
7045 selection.goal = SelectionGoal::None;
7046 }
7047 let (cursor, goal) = movement::down_by_rows(
7048 map,
7049 selection.end,
7050 row_count,
7051 selection.goal,
7052 false,
7053 &text_layout_details,
7054 );
7055 selection.collapse_to(cursor, goal);
7056 });
7057 });
7058 }
7059
7060 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
7061 let text_layout_details = &self.text_layout_details(cx);
7062 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7063 s.move_heads_with(|map, head, goal| {
7064 movement::down(map, head, goal, false, &text_layout_details)
7065 })
7066 });
7067 }
7068
7069 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
7070 if let Some(context_menu) = self.context_menu.write().as_mut() {
7071 context_menu.select_first(self.project.as_ref(), cx);
7072 }
7073 }
7074
7075 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
7076 if let Some(context_menu) = self.context_menu.write().as_mut() {
7077 context_menu.select_prev(self.project.as_ref(), cx);
7078 }
7079 }
7080
7081 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
7082 if let Some(context_menu) = self.context_menu.write().as_mut() {
7083 context_menu.select_next(self.project.as_ref(), cx);
7084 }
7085 }
7086
7087 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
7088 if let Some(context_menu) = self.context_menu.write().as_mut() {
7089 context_menu.select_last(self.project.as_ref(), cx);
7090 }
7091 }
7092
7093 pub fn move_to_previous_word_start(
7094 &mut self,
7095 _: &MoveToPreviousWordStart,
7096 cx: &mut ViewContext<Self>,
7097 ) {
7098 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7099 s.move_cursors_with(|map, head, _| {
7100 (
7101 movement::previous_word_start(map, head),
7102 SelectionGoal::None,
7103 )
7104 });
7105 })
7106 }
7107
7108 pub fn move_to_previous_subword_start(
7109 &mut self,
7110 _: &MoveToPreviousSubwordStart,
7111 cx: &mut ViewContext<Self>,
7112 ) {
7113 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7114 s.move_cursors_with(|map, head, _| {
7115 (
7116 movement::previous_subword_start(map, head),
7117 SelectionGoal::None,
7118 )
7119 });
7120 })
7121 }
7122
7123 pub fn select_to_previous_word_start(
7124 &mut self,
7125 _: &SelectToPreviousWordStart,
7126 cx: &mut ViewContext<Self>,
7127 ) {
7128 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7129 s.move_heads_with(|map, head, _| {
7130 (
7131 movement::previous_word_start(map, head),
7132 SelectionGoal::None,
7133 )
7134 });
7135 })
7136 }
7137
7138 pub fn select_to_previous_subword_start(
7139 &mut self,
7140 _: &SelectToPreviousSubwordStart,
7141 cx: &mut ViewContext<Self>,
7142 ) {
7143 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7144 s.move_heads_with(|map, head, _| {
7145 (
7146 movement::previous_subword_start(map, head),
7147 SelectionGoal::None,
7148 )
7149 });
7150 })
7151 }
7152
7153 pub fn delete_to_previous_word_start(
7154 &mut self,
7155 _: &DeleteToPreviousWordStart,
7156 cx: &mut ViewContext<Self>,
7157 ) {
7158 self.transact(cx, |this, cx| {
7159 this.select_autoclose_pair(cx);
7160 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7161 let line_mode = s.line_mode;
7162 s.move_with(|map, selection| {
7163 if selection.is_empty() && !line_mode {
7164 let cursor = movement::previous_word_start(map, selection.head());
7165 selection.set_head(cursor, SelectionGoal::None);
7166 }
7167 });
7168 });
7169 this.insert("", cx);
7170 });
7171 }
7172
7173 pub fn delete_to_previous_subword_start(
7174 &mut self,
7175 _: &DeleteToPreviousSubwordStart,
7176 cx: &mut ViewContext<Self>,
7177 ) {
7178 self.transact(cx, |this, cx| {
7179 this.select_autoclose_pair(cx);
7180 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7181 let line_mode = s.line_mode;
7182 s.move_with(|map, selection| {
7183 if selection.is_empty() && !line_mode {
7184 let cursor = movement::previous_subword_start(map, selection.head());
7185 selection.set_head(cursor, SelectionGoal::None);
7186 }
7187 });
7188 });
7189 this.insert("", cx);
7190 });
7191 }
7192
7193 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
7194 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7195 s.move_cursors_with(|map, head, _| {
7196 (movement::next_word_end(map, head), SelectionGoal::None)
7197 });
7198 })
7199 }
7200
7201 pub fn move_to_next_subword_end(
7202 &mut self,
7203 _: &MoveToNextSubwordEnd,
7204 cx: &mut ViewContext<Self>,
7205 ) {
7206 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7207 s.move_cursors_with(|map, head, _| {
7208 (movement::next_subword_end(map, head), SelectionGoal::None)
7209 });
7210 })
7211 }
7212
7213 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
7214 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7215 s.move_heads_with(|map, head, _| {
7216 (movement::next_word_end(map, head), SelectionGoal::None)
7217 });
7218 })
7219 }
7220
7221 pub fn select_to_next_subword_end(
7222 &mut self,
7223 _: &SelectToNextSubwordEnd,
7224 cx: &mut ViewContext<Self>,
7225 ) {
7226 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7227 s.move_heads_with(|map, head, _| {
7228 (movement::next_subword_end(map, head), SelectionGoal::None)
7229 });
7230 })
7231 }
7232
7233 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
7234 self.transact(cx, |this, cx| {
7235 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7236 let line_mode = s.line_mode;
7237 s.move_with(|map, selection| {
7238 if selection.is_empty() && !line_mode {
7239 let cursor = movement::next_word_end(map, selection.head());
7240 selection.set_head(cursor, SelectionGoal::None);
7241 }
7242 });
7243 });
7244 this.insert("", cx);
7245 });
7246 }
7247
7248 pub fn delete_to_next_subword_end(
7249 &mut self,
7250 _: &DeleteToNextSubwordEnd,
7251 cx: &mut ViewContext<Self>,
7252 ) {
7253 self.transact(cx, |this, cx| {
7254 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7255 s.move_with(|map, selection| {
7256 if selection.is_empty() {
7257 let cursor = movement::next_subword_end(map, selection.head());
7258 selection.set_head(cursor, SelectionGoal::None);
7259 }
7260 });
7261 });
7262 this.insert("", cx);
7263 });
7264 }
7265
7266 pub fn move_to_beginning_of_line(
7267 &mut self,
7268 action: &MoveToBeginningOfLine,
7269 cx: &mut ViewContext<Self>,
7270 ) {
7271 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7272 s.move_cursors_with(|map, head, _| {
7273 (
7274 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7275 SelectionGoal::None,
7276 )
7277 });
7278 })
7279 }
7280
7281 pub fn select_to_beginning_of_line(
7282 &mut self,
7283 action: &SelectToBeginningOfLine,
7284 cx: &mut ViewContext<Self>,
7285 ) {
7286 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7287 s.move_heads_with(|map, head, _| {
7288 (
7289 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7290 SelectionGoal::None,
7291 )
7292 });
7293 });
7294 }
7295
7296 pub fn delete_to_beginning_of_line(
7297 &mut self,
7298 _: &DeleteToBeginningOfLine,
7299 cx: &mut ViewContext<Self>,
7300 ) {
7301 self.transact(cx, |this, cx| {
7302 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7303 s.move_with(|_, selection| {
7304 selection.reversed = true;
7305 });
7306 });
7307
7308 this.select_to_beginning_of_line(
7309 &SelectToBeginningOfLine {
7310 stop_at_soft_wraps: false,
7311 },
7312 cx,
7313 );
7314 this.backspace(&Backspace, cx);
7315 });
7316 }
7317
7318 pub fn move_to_end_of_line(&mut self, action: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
7319 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7320 s.move_cursors_with(|map, head, _| {
7321 (
7322 movement::line_end(map, head, action.stop_at_soft_wraps),
7323 SelectionGoal::None,
7324 )
7325 });
7326 })
7327 }
7328
7329 pub fn select_to_end_of_line(
7330 &mut self,
7331 action: &SelectToEndOfLine,
7332 cx: &mut ViewContext<Self>,
7333 ) {
7334 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7335 s.move_heads_with(|map, head, _| {
7336 (
7337 movement::line_end(map, head, action.stop_at_soft_wraps),
7338 SelectionGoal::None,
7339 )
7340 });
7341 })
7342 }
7343
7344 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
7345 self.transact(cx, |this, cx| {
7346 this.select_to_end_of_line(
7347 &SelectToEndOfLine {
7348 stop_at_soft_wraps: false,
7349 },
7350 cx,
7351 );
7352 this.delete(&Delete, cx);
7353 });
7354 }
7355
7356 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
7357 self.transact(cx, |this, cx| {
7358 this.select_to_end_of_line(
7359 &SelectToEndOfLine {
7360 stop_at_soft_wraps: false,
7361 },
7362 cx,
7363 );
7364 this.cut(&Cut, cx);
7365 });
7366 }
7367
7368 pub fn move_to_start_of_paragraph(
7369 &mut self,
7370 _: &MoveToStartOfParagraph,
7371 cx: &mut ViewContext<Self>,
7372 ) {
7373 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7374 cx.propagate();
7375 return;
7376 }
7377
7378 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7379 s.move_with(|map, selection| {
7380 selection.collapse_to(
7381 movement::start_of_paragraph(map, selection.head(), 1),
7382 SelectionGoal::None,
7383 )
7384 });
7385 })
7386 }
7387
7388 pub fn move_to_end_of_paragraph(
7389 &mut self,
7390 _: &MoveToEndOfParagraph,
7391 cx: &mut ViewContext<Self>,
7392 ) {
7393 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7394 cx.propagate();
7395 return;
7396 }
7397
7398 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7399 s.move_with(|map, selection| {
7400 selection.collapse_to(
7401 movement::end_of_paragraph(map, selection.head(), 1),
7402 SelectionGoal::None,
7403 )
7404 });
7405 })
7406 }
7407
7408 pub fn select_to_start_of_paragraph(
7409 &mut self,
7410 _: &SelectToStartOfParagraph,
7411 cx: &mut ViewContext<Self>,
7412 ) {
7413 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7414 cx.propagate();
7415 return;
7416 }
7417
7418 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7419 s.move_heads_with(|map, head, _| {
7420 (
7421 movement::start_of_paragraph(map, head, 1),
7422 SelectionGoal::None,
7423 )
7424 });
7425 })
7426 }
7427
7428 pub fn select_to_end_of_paragraph(
7429 &mut self,
7430 _: &SelectToEndOfParagraph,
7431 cx: &mut ViewContext<Self>,
7432 ) {
7433 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7434 cx.propagate();
7435 return;
7436 }
7437
7438 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7439 s.move_heads_with(|map, head, _| {
7440 (
7441 movement::end_of_paragraph(map, head, 1),
7442 SelectionGoal::None,
7443 )
7444 });
7445 })
7446 }
7447
7448 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
7449 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7450 cx.propagate();
7451 return;
7452 }
7453
7454 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7455 s.select_ranges(vec![0..0]);
7456 });
7457 }
7458
7459 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
7460 let mut selection = self.selections.last::<Point>(cx);
7461 selection.set_head(Point::zero(), SelectionGoal::None);
7462
7463 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7464 s.select(vec![selection]);
7465 });
7466 }
7467
7468 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
7469 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7470 cx.propagate();
7471 return;
7472 }
7473
7474 let cursor = self.buffer.read(cx).read(cx).len();
7475 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7476 s.select_ranges(vec![cursor..cursor])
7477 });
7478 }
7479
7480 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
7481 self.nav_history = nav_history;
7482 }
7483
7484 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
7485 self.nav_history.as_ref()
7486 }
7487
7488 fn push_to_nav_history(
7489 &mut self,
7490 cursor_anchor: Anchor,
7491 new_position: Option<Point>,
7492 cx: &mut ViewContext<Self>,
7493 ) {
7494 if let Some(nav_history) = self.nav_history.as_mut() {
7495 let buffer = self.buffer.read(cx).read(cx);
7496 let cursor_position = cursor_anchor.to_point(&buffer);
7497 let scroll_state = self.scroll_manager.anchor();
7498 let scroll_top_row = scroll_state.top_row(&buffer);
7499 drop(buffer);
7500
7501 if let Some(new_position) = new_position {
7502 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
7503 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
7504 return;
7505 }
7506 }
7507
7508 nav_history.push(
7509 Some(NavigationData {
7510 cursor_anchor,
7511 cursor_position,
7512 scroll_anchor: scroll_state,
7513 scroll_top_row,
7514 }),
7515 cx,
7516 );
7517 }
7518 }
7519
7520 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
7521 let buffer = self.buffer.read(cx).snapshot(cx);
7522 let mut selection = self.selections.first::<usize>(cx);
7523 selection.set_head(buffer.len(), SelectionGoal::None);
7524 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7525 s.select(vec![selection]);
7526 });
7527 }
7528
7529 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
7530 let end = self.buffer.read(cx).read(cx).len();
7531 self.change_selections(None, cx, |s| {
7532 s.select_ranges(vec![0..end]);
7533 });
7534 }
7535
7536 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
7537 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7538 let mut selections = self.selections.all::<Point>(cx);
7539 let max_point = display_map.buffer_snapshot.max_point();
7540 for selection in &mut selections {
7541 let rows = selection.spanned_rows(true, &display_map);
7542 selection.start = Point::new(rows.start.0, 0);
7543 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
7544 selection.reversed = false;
7545 }
7546 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7547 s.select(selections);
7548 });
7549 }
7550
7551 pub fn split_selection_into_lines(
7552 &mut self,
7553 _: &SplitSelectionIntoLines,
7554 cx: &mut ViewContext<Self>,
7555 ) {
7556 let mut to_unfold = Vec::new();
7557 let mut new_selection_ranges = Vec::new();
7558 {
7559 let selections = self.selections.all::<Point>(cx);
7560 let buffer = self.buffer.read(cx).read(cx);
7561 for selection in selections {
7562 for row in selection.start.row..selection.end.row {
7563 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
7564 new_selection_ranges.push(cursor..cursor);
7565 }
7566 new_selection_ranges.push(selection.end..selection.end);
7567 to_unfold.push(selection.start..selection.end);
7568 }
7569 }
7570 self.unfold_ranges(to_unfold, true, true, cx);
7571 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7572 s.select_ranges(new_selection_ranges);
7573 });
7574 }
7575
7576 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
7577 self.add_selection(true, cx);
7578 }
7579
7580 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
7581 self.add_selection(false, cx);
7582 }
7583
7584 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
7585 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7586 let mut selections = self.selections.all::<Point>(cx);
7587 let text_layout_details = self.text_layout_details(cx);
7588 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
7589 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
7590 let range = oldest_selection.display_range(&display_map).sorted();
7591
7592 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
7593 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
7594 let positions = start_x.min(end_x)..start_x.max(end_x);
7595
7596 selections.clear();
7597 let mut stack = Vec::new();
7598 for row in range.start.row().0..=range.end.row().0 {
7599 if let Some(selection) = self.selections.build_columnar_selection(
7600 &display_map,
7601 DisplayRow(row),
7602 &positions,
7603 oldest_selection.reversed,
7604 &text_layout_details,
7605 ) {
7606 stack.push(selection.id);
7607 selections.push(selection);
7608 }
7609 }
7610
7611 if above {
7612 stack.reverse();
7613 }
7614
7615 AddSelectionsState { above, stack }
7616 });
7617
7618 let last_added_selection = *state.stack.last().unwrap();
7619 let mut new_selections = Vec::new();
7620 if above == state.above {
7621 let end_row = if above {
7622 DisplayRow(0)
7623 } else {
7624 display_map.max_point().row()
7625 };
7626
7627 'outer: for selection in selections {
7628 if selection.id == last_added_selection {
7629 let range = selection.display_range(&display_map).sorted();
7630 debug_assert_eq!(range.start.row(), range.end.row());
7631 let mut row = range.start.row();
7632 let positions =
7633 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
7634 px(start)..px(end)
7635 } else {
7636 let start_x =
7637 display_map.x_for_display_point(range.start, &text_layout_details);
7638 let end_x =
7639 display_map.x_for_display_point(range.end, &text_layout_details);
7640 start_x.min(end_x)..start_x.max(end_x)
7641 };
7642
7643 while row != end_row {
7644 if above {
7645 row.0 -= 1;
7646 } else {
7647 row.0 += 1;
7648 }
7649
7650 if let Some(new_selection) = self.selections.build_columnar_selection(
7651 &display_map,
7652 row,
7653 &positions,
7654 selection.reversed,
7655 &text_layout_details,
7656 ) {
7657 state.stack.push(new_selection.id);
7658 if above {
7659 new_selections.push(new_selection);
7660 new_selections.push(selection);
7661 } else {
7662 new_selections.push(selection);
7663 new_selections.push(new_selection);
7664 }
7665
7666 continue 'outer;
7667 }
7668 }
7669 }
7670
7671 new_selections.push(selection);
7672 }
7673 } else {
7674 new_selections = selections;
7675 new_selections.retain(|s| s.id != last_added_selection);
7676 state.stack.pop();
7677 }
7678
7679 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7680 s.select(new_selections);
7681 });
7682 if state.stack.len() > 1 {
7683 self.add_selections_state = Some(state);
7684 }
7685 }
7686
7687 pub fn select_next_match_internal(
7688 &mut self,
7689 display_map: &DisplaySnapshot,
7690 replace_newest: bool,
7691 autoscroll: Option<Autoscroll>,
7692 cx: &mut ViewContext<Self>,
7693 ) -> Result<()> {
7694 fn select_next_match_ranges(
7695 this: &mut Editor,
7696 range: Range<usize>,
7697 replace_newest: bool,
7698 auto_scroll: Option<Autoscroll>,
7699 cx: &mut ViewContext<Editor>,
7700 ) {
7701 this.unfold_ranges([range.clone()], false, true, cx);
7702 this.change_selections(auto_scroll, cx, |s| {
7703 if replace_newest {
7704 s.delete(s.newest_anchor().id);
7705 }
7706 s.insert_range(range.clone());
7707 });
7708 }
7709
7710 let buffer = &display_map.buffer_snapshot;
7711 let mut selections = self.selections.all::<usize>(cx);
7712 if let Some(mut select_next_state) = self.select_next_state.take() {
7713 let query = &select_next_state.query;
7714 if !select_next_state.done {
7715 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
7716 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
7717 let mut next_selected_range = None;
7718
7719 let bytes_after_last_selection =
7720 buffer.bytes_in_range(last_selection.end..buffer.len());
7721 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
7722 let query_matches = query
7723 .stream_find_iter(bytes_after_last_selection)
7724 .map(|result| (last_selection.end, result))
7725 .chain(
7726 query
7727 .stream_find_iter(bytes_before_first_selection)
7728 .map(|result| (0, result)),
7729 );
7730
7731 for (start_offset, query_match) in query_matches {
7732 let query_match = query_match.unwrap(); // can only fail due to I/O
7733 let offset_range =
7734 start_offset + query_match.start()..start_offset + query_match.end();
7735 let display_range = offset_range.start.to_display_point(&display_map)
7736 ..offset_range.end.to_display_point(&display_map);
7737
7738 if !select_next_state.wordwise
7739 || (!movement::is_inside_word(&display_map, display_range.start)
7740 && !movement::is_inside_word(&display_map, display_range.end))
7741 {
7742 // TODO: This is n^2, because we might check all the selections
7743 if !selections
7744 .iter()
7745 .any(|selection| selection.range().overlaps(&offset_range))
7746 {
7747 next_selected_range = Some(offset_range);
7748 break;
7749 }
7750 }
7751 }
7752
7753 if let Some(next_selected_range) = next_selected_range {
7754 select_next_match_ranges(
7755 self,
7756 next_selected_range,
7757 replace_newest,
7758 autoscroll,
7759 cx,
7760 );
7761 } else {
7762 select_next_state.done = true;
7763 }
7764 }
7765
7766 self.select_next_state = Some(select_next_state);
7767 } else {
7768 let mut only_carets = true;
7769 let mut same_text_selected = true;
7770 let mut selected_text = None;
7771
7772 let mut selections_iter = selections.iter().peekable();
7773 while let Some(selection) = selections_iter.next() {
7774 if selection.start != selection.end {
7775 only_carets = false;
7776 }
7777
7778 if same_text_selected {
7779 if selected_text.is_none() {
7780 selected_text =
7781 Some(buffer.text_for_range(selection.range()).collect::<String>());
7782 }
7783
7784 if let Some(next_selection) = selections_iter.peek() {
7785 if next_selection.range().len() == selection.range().len() {
7786 let next_selected_text = buffer
7787 .text_for_range(next_selection.range())
7788 .collect::<String>();
7789 if Some(next_selected_text) != selected_text {
7790 same_text_selected = false;
7791 selected_text = None;
7792 }
7793 } else {
7794 same_text_selected = false;
7795 selected_text = None;
7796 }
7797 }
7798 }
7799 }
7800
7801 if only_carets {
7802 for selection in &mut selections {
7803 let word_range = movement::surrounding_word(
7804 &display_map,
7805 selection.start.to_display_point(&display_map),
7806 );
7807 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
7808 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
7809 selection.goal = SelectionGoal::None;
7810 selection.reversed = false;
7811 select_next_match_ranges(
7812 self,
7813 selection.start..selection.end,
7814 replace_newest,
7815 autoscroll,
7816 cx,
7817 );
7818 }
7819
7820 if selections.len() == 1 {
7821 let selection = selections
7822 .last()
7823 .expect("ensured that there's only one selection");
7824 let query = buffer
7825 .text_for_range(selection.start..selection.end)
7826 .collect::<String>();
7827 let is_empty = query.is_empty();
7828 let select_state = SelectNextState {
7829 query: AhoCorasick::new(&[query])?,
7830 wordwise: true,
7831 done: is_empty,
7832 };
7833 self.select_next_state = Some(select_state);
7834 } else {
7835 self.select_next_state = None;
7836 }
7837 } else if let Some(selected_text) = selected_text {
7838 self.select_next_state = Some(SelectNextState {
7839 query: AhoCorasick::new(&[selected_text])?,
7840 wordwise: false,
7841 done: false,
7842 });
7843 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
7844 }
7845 }
7846 Ok(())
7847 }
7848
7849 pub fn select_all_matches(
7850 &mut self,
7851 _action: &SelectAllMatches,
7852 cx: &mut ViewContext<Self>,
7853 ) -> Result<()> {
7854 self.push_to_selection_history();
7855 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7856
7857 self.select_next_match_internal(&display_map, false, None, cx)?;
7858 let Some(select_next_state) = self.select_next_state.as_mut() else {
7859 return Ok(());
7860 };
7861 if select_next_state.done {
7862 return Ok(());
7863 }
7864
7865 let mut new_selections = self.selections.all::<usize>(cx);
7866
7867 let buffer = &display_map.buffer_snapshot;
7868 let query_matches = select_next_state
7869 .query
7870 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
7871
7872 for query_match in query_matches {
7873 let query_match = query_match.unwrap(); // can only fail due to I/O
7874 let offset_range = query_match.start()..query_match.end();
7875 let display_range = offset_range.start.to_display_point(&display_map)
7876 ..offset_range.end.to_display_point(&display_map);
7877
7878 if !select_next_state.wordwise
7879 || (!movement::is_inside_word(&display_map, display_range.start)
7880 && !movement::is_inside_word(&display_map, display_range.end))
7881 {
7882 self.selections.change_with(cx, |selections| {
7883 new_selections.push(Selection {
7884 id: selections.new_selection_id(),
7885 start: offset_range.start,
7886 end: offset_range.end,
7887 reversed: false,
7888 goal: SelectionGoal::None,
7889 });
7890 });
7891 }
7892 }
7893
7894 new_selections.sort_by_key(|selection| selection.start);
7895 let mut ix = 0;
7896 while ix + 1 < new_selections.len() {
7897 let current_selection = &new_selections[ix];
7898 let next_selection = &new_selections[ix + 1];
7899 if current_selection.range().overlaps(&next_selection.range()) {
7900 if current_selection.id < next_selection.id {
7901 new_selections.remove(ix + 1);
7902 } else {
7903 new_selections.remove(ix);
7904 }
7905 } else {
7906 ix += 1;
7907 }
7908 }
7909
7910 select_next_state.done = true;
7911 self.unfold_ranges(
7912 new_selections.iter().map(|selection| selection.range()),
7913 false,
7914 false,
7915 cx,
7916 );
7917 self.change_selections(Some(Autoscroll::fit()), cx, |selections| {
7918 selections.select(new_selections)
7919 });
7920
7921 Ok(())
7922 }
7923
7924 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
7925 self.push_to_selection_history();
7926 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7927 self.select_next_match_internal(
7928 &display_map,
7929 action.replace_newest,
7930 Some(Autoscroll::newest()),
7931 cx,
7932 )?;
7933 Ok(())
7934 }
7935
7936 pub fn select_previous(
7937 &mut self,
7938 action: &SelectPrevious,
7939 cx: &mut ViewContext<Self>,
7940 ) -> Result<()> {
7941 self.push_to_selection_history();
7942 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7943 let buffer = &display_map.buffer_snapshot;
7944 let mut selections = self.selections.all::<usize>(cx);
7945 if let Some(mut select_prev_state) = self.select_prev_state.take() {
7946 let query = &select_prev_state.query;
7947 if !select_prev_state.done {
7948 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
7949 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
7950 let mut next_selected_range = None;
7951 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
7952 let bytes_before_last_selection =
7953 buffer.reversed_bytes_in_range(0..last_selection.start);
7954 let bytes_after_first_selection =
7955 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
7956 let query_matches = query
7957 .stream_find_iter(bytes_before_last_selection)
7958 .map(|result| (last_selection.start, result))
7959 .chain(
7960 query
7961 .stream_find_iter(bytes_after_first_selection)
7962 .map(|result| (buffer.len(), result)),
7963 );
7964 for (end_offset, query_match) in query_matches {
7965 let query_match = query_match.unwrap(); // can only fail due to I/O
7966 let offset_range =
7967 end_offset - query_match.end()..end_offset - query_match.start();
7968 let display_range = offset_range.start.to_display_point(&display_map)
7969 ..offset_range.end.to_display_point(&display_map);
7970
7971 if !select_prev_state.wordwise
7972 || (!movement::is_inside_word(&display_map, display_range.start)
7973 && !movement::is_inside_word(&display_map, display_range.end))
7974 {
7975 next_selected_range = Some(offset_range);
7976 break;
7977 }
7978 }
7979
7980 if let Some(next_selected_range) = next_selected_range {
7981 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
7982 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
7983 if action.replace_newest {
7984 s.delete(s.newest_anchor().id);
7985 }
7986 s.insert_range(next_selected_range);
7987 });
7988 } else {
7989 select_prev_state.done = true;
7990 }
7991 }
7992
7993 self.select_prev_state = Some(select_prev_state);
7994 } else {
7995 let mut only_carets = true;
7996 let mut same_text_selected = true;
7997 let mut selected_text = None;
7998
7999 let mut selections_iter = selections.iter().peekable();
8000 while let Some(selection) = selections_iter.next() {
8001 if selection.start != selection.end {
8002 only_carets = false;
8003 }
8004
8005 if same_text_selected {
8006 if selected_text.is_none() {
8007 selected_text =
8008 Some(buffer.text_for_range(selection.range()).collect::<String>());
8009 }
8010
8011 if let Some(next_selection) = selections_iter.peek() {
8012 if next_selection.range().len() == selection.range().len() {
8013 let next_selected_text = buffer
8014 .text_for_range(next_selection.range())
8015 .collect::<String>();
8016 if Some(next_selected_text) != selected_text {
8017 same_text_selected = false;
8018 selected_text = None;
8019 }
8020 } else {
8021 same_text_selected = false;
8022 selected_text = None;
8023 }
8024 }
8025 }
8026 }
8027
8028 if only_carets {
8029 for selection in &mut selections {
8030 let word_range = movement::surrounding_word(
8031 &display_map,
8032 selection.start.to_display_point(&display_map),
8033 );
8034 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
8035 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
8036 selection.goal = SelectionGoal::None;
8037 selection.reversed = false;
8038 }
8039 if selections.len() == 1 {
8040 let selection = selections
8041 .last()
8042 .expect("ensured that there's only one selection");
8043 let query = buffer
8044 .text_for_range(selection.start..selection.end)
8045 .collect::<String>();
8046 let is_empty = query.is_empty();
8047 let select_state = SelectNextState {
8048 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
8049 wordwise: true,
8050 done: is_empty,
8051 };
8052 self.select_prev_state = Some(select_state);
8053 } else {
8054 self.select_prev_state = None;
8055 }
8056
8057 self.unfold_ranges(
8058 selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
8059 false,
8060 true,
8061 cx,
8062 );
8063 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8064 s.select(selections);
8065 });
8066 } else if let Some(selected_text) = selected_text {
8067 self.select_prev_state = Some(SelectNextState {
8068 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
8069 wordwise: false,
8070 done: false,
8071 });
8072 self.select_previous(action, cx)?;
8073 }
8074 }
8075 Ok(())
8076 }
8077
8078 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
8079 let text_layout_details = &self.text_layout_details(cx);
8080 self.transact(cx, |this, cx| {
8081 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
8082 let mut edits = Vec::new();
8083 let mut selection_edit_ranges = Vec::new();
8084 let mut last_toggled_row = None;
8085 let snapshot = this.buffer.read(cx).read(cx);
8086 let empty_str: Arc<str> = "".into();
8087 let mut suffixes_inserted = Vec::new();
8088
8089 fn comment_prefix_range(
8090 snapshot: &MultiBufferSnapshot,
8091 row: MultiBufferRow,
8092 comment_prefix: &str,
8093 comment_prefix_whitespace: &str,
8094 ) -> Range<Point> {
8095 let start = Point::new(row.0, snapshot.indent_size_for_line(row).len);
8096
8097 let mut line_bytes = snapshot
8098 .bytes_in_range(start..snapshot.max_point())
8099 .flatten()
8100 .copied();
8101
8102 // If this line currently begins with the line comment prefix, then record
8103 // the range containing the prefix.
8104 if line_bytes
8105 .by_ref()
8106 .take(comment_prefix.len())
8107 .eq(comment_prefix.bytes())
8108 {
8109 // Include any whitespace that matches the comment prefix.
8110 let matching_whitespace_len = line_bytes
8111 .zip(comment_prefix_whitespace.bytes())
8112 .take_while(|(a, b)| a == b)
8113 .count() as u32;
8114 let end = Point::new(
8115 start.row,
8116 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
8117 );
8118 start..end
8119 } else {
8120 start..start
8121 }
8122 }
8123
8124 fn comment_suffix_range(
8125 snapshot: &MultiBufferSnapshot,
8126 row: MultiBufferRow,
8127 comment_suffix: &str,
8128 comment_suffix_has_leading_space: bool,
8129 ) -> Range<Point> {
8130 let end = Point::new(row.0, snapshot.line_len(row));
8131 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
8132
8133 let mut line_end_bytes = snapshot
8134 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
8135 .flatten()
8136 .copied();
8137
8138 let leading_space_len = if suffix_start_column > 0
8139 && line_end_bytes.next() == Some(b' ')
8140 && comment_suffix_has_leading_space
8141 {
8142 1
8143 } else {
8144 0
8145 };
8146
8147 // If this line currently begins with the line comment prefix, then record
8148 // the range containing the prefix.
8149 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
8150 let start = Point::new(end.row, suffix_start_column - leading_space_len);
8151 start..end
8152 } else {
8153 end..end
8154 }
8155 }
8156
8157 // TODO: Handle selections that cross excerpts
8158 for selection in &mut selections {
8159 let start_column = snapshot
8160 .indent_size_for_line(MultiBufferRow(selection.start.row))
8161 .len;
8162 let language = if let Some(language) =
8163 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
8164 {
8165 language
8166 } else {
8167 continue;
8168 };
8169
8170 selection_edit_ranges.clear();
8171
8172 // If multiple selections contain a given row, avoid processing that
8173 // row more than once.
8174 let mut start_row = MultiBufferRow(selection.start.row);
8175 if last_toggled_row == Some(start_row) {
8176 start_row = start_row.next_row();
8177 }
8178 let end_row =
8179 if selection.end.row > selection.start.row && selection.end.column == 0 {
8180 MultiBufferRow(selection.end.row - 1)
8181 } else {
8182 MultiBufferRow(selection.end.row)
8183 };
8184 last_toggled_row = Some(end_row);
8185
8186 if start_row > end_row {
8187 continue;
8188 }
8189
8190 // If the language has line comments, toggle those.
8191 let full_comment_prefixes = language.line_comment_prefixes();
8192 if !full_comment_prefixes.is_empty() {
8193 let first_prefix = full_comment_prefixes
8194 .first()
8195 .expect("prefixes is non-empty");
8196 let prefix_trimmed_lengths = full_comment_prefixes
8197 .iter()
8198 .map(|p| p.trim_end_matches(' ').len())
8199 .collect::<SmallVec<[usize; 4]>>();
8200
8201 let mut all_selection_lines_are_comments = true;
8202
8203 for row in start_row.0..=end_row.0 {
8204 let row = MultiBufferRow(row);
8205 if start_row < end_row && snapshot.is_line_blank(row) {
8206 continue;
8207 }
8208
8209 let prefix_range = full_comment_prefixes
8210 .iter()
8211 .zip(prefix_trimmed_lengths.iter().copied())
8212 .map(|(prefix, trimmed_prefix_len)| {
8213 comment_prefix_range(
8214 snapshot.deref(),
8215 row,
8216 &prefix[..trimmed_prefix_len],
8217 &prefix[trimmed_prefix_len..],
8218 )
8219 })
8220 .max_by_key(|range| range.end.column - range.start.column)
8221 .expect("prefixes is non-empty");
8222
8223 if prefix_range.is_empty() {
8224 all_selection_lines_are_comments = false;
8225 }
8226
8227 selection_edit_ranges.push(prefix_range);
8228 }
8229
8230 if all_selection_lines_are_comments {
8231 edits.extend(
8232 selection_edit_ranges
8233 .iter()
8234 .cloned()
8235 .map(|range| (range, empty_str.clone())),
8236 );
8237 } else {
8238 let min_column = selection_edit_ranges
8239 .iter()
8240 .map(|range| range.start.column)
8241 .min()
8242 .unwrap_or(0);
8243 edits.extend(selection_edit_ranges.iter().map(|range| {
8244 let position = Point::new(range.start.row, min_column);
8245 (position..position, first_prefix.clone())
8246 }));
8247 }
8248 } else if let Some((full_comment_prefix, comment_suffix)) =
8249 language.block_comment_delimiters()
8250 {
8251 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
8252 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
8253 let prefix_range = comment_prefix_range(
8254 snapshot.deref(),
8255 start_row,
8256 comment_prefix,
8257 comment_prefix_whitespace,
8258 );
8259 let suffix_range = comment_suffix_range(
8260 snapshot.deref(),
8261 end_row,
8262 comment_suffix.trim_start_matches(' '),
8263 comment_suffix.starts_with(' '),
8264 );
8265
8266 if prefix_range.is_empty() || suffix_range.is_empty() {
8267 edits.push((
8268 prefix_range.start..prefix_range.start,
8269 full_comment_prefix.clone(),
8270 ));
8271 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
8272 suffixes_inserted.push((end_row, comment_suffix.len()));
8273 } else {
8274 edits.push((prefix_range, empty_str.clone()));
8275 edits.push((suffix_range, empty_str.clone()));
8276 }
8277 } else {
8278 continue;
8279 }
8280 }
8281
8282 drop(snapshot);
8283 this.buffer.update(cx, |buffer, cx| {
8284 buffer.edit(edits, None, cx);
8285 });
8286
8287 // Adjust selections so that they end before any comment suffixes that
8288 // were inserted.
8289 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
8290 let mut selections = this.selections.all::<Point>(cx);
8291 let snapshot = this.buffer.read(cx).read(cx);
8292 for selection in &mut selections {
8293 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
8294 match row.cmp(&MultiBufferRow(selection.end.row)) {
8295 Ordering::Less => {
8296 suffixes_inserted.next();
8297 continue;
8298 }
8299 Ordering::Greater => break,
8300 Ordering::Equal => {
8301 if selection.end.column == snapshot.line_len(row) {
8302 if selection.is_empty() {
8303 selection.start.column -= suffix_len as u32;
8304 }
8305 selection.end.column -= suffix_len as u32;
8306 }
8307 break;
8308 }
8309 }
8310 }
8311 }
8312
8313 drop(snapshot);
8314 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
8315
8316 let selections = this.selections.all::<Point>(cx);
8317 let selections_on_single_row = selections.windows(2).all(|selections| {
8318 selections[0].start.row == selections[1].start.row
8319 && selections[0].end.row == selections[1].end.row
8320 && selections[0].start.row == selections[0].end.row
8321 });
8322 let selections_selecting = selections
8323 .iter()
8324 .any(|selection| selection.start != selection.end);
8325 let advance_downwards = action.advance_downwards
8326 && selections_on_single_row
8327 && !selections_selecting
8328 && !matches!(this.mode, EditorMode::SingleLine { .. });
8329
8330 if advance_downwards {
8331 let snapshot = this.buffer.read(cx).snapshot(cx);
8332
8333 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8334 s.move_cursors_with(|display_snapshot, display_point, _| {
8335 let mut point = display_point.to_point(display_snapshot);
8336 point.row += 1;
8337 point = snapshot.clip_point(point, Bias::Left);
8338 let display_point = point.to_display_point(display_snapshot);
8339 let goal = SelectionGoal::HorizontalPosition(
8340 display_snapshot
8341 .x_for_display_point(display_point, &text_layout_details)
8342 .into(),
8343 );
8344 (display_point, goal)
8345 })
8346 });
8347 }
8348 });
8349 }
8350
8351 pub fn select_enclosing_symbol(
8352 &mut self,
8353 _: &SelectEnclosingSymbol,
8354 cx: &mut ViewContext<Self>,
8355 ) {
8356 let buffer = self.buffer.read(cx).snapshot(cx);
8357 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8358
8359 fn update_selection(
8360 selection: &Selection<usize>,
8361 buffer_snap: &MultiBufferSnapshot,
8362 ) -> Option<Selection<usize>> {
8363 let cursor = selection.head();
8364 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
8365 for symbol in symbols.iter().rev() {
8366 let start = symbol.range.start.to_offset(&buffer_snap);
8367 let end = symbol.range.end.to_offset(&buffer_snap);
8368 let new_range = start..end;
8369 if start < selection.start || end > selection.end {
8370 return Some(Selection {
8371 id: selection.id,
8372 start: new_range.start,
8373 end: new_range.end,
8374 goal: SelectionGoal::None,
8375 reversed: selection.reversed,
8376 });
8377 }
8378 }
8379 None
8380 }
8381
8382 let mut selected_larger_symbol = false;
8383 let new_selections = old_selections
8384 .iter()
8385 .map(|selection| match update_selection(selection, &buffer) {
8386 Some(new_selection) => {
8387 if new_selection.range() != selection.range() {
8388 selected_larger_symbol = true;
8389 }
8390 new_selection
8391 }
8392 None => selection.clone(),
8393 })
8394 .collect::<Vec<_>>();
8395
8396 if selected_larger_symbol {
8397 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8398 s.select(new_selections);
8399 });
8400 }
8401 }
8402
8403 pub fn select_larger_syntax_node(
8404 &mut self,
8405 _: &SelectLargerSyntaxNode,
8406 cx: &mut ViewContext<Self>,
8407 ) {
8408 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8409 let buffer = self.buffer.read(cx).snapshot(cx);
8410 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8411
8412 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8413 let mut selected_larger_node = false;
8414 let new_selections = old_selections
8415 .iter()
8416 .map(|selection| {
8417 let old_range = selection.start..selection.end;
8418 let mut new_range = old_range.clone();
8419 while let Some(containing_range) =
8420 buffer.range_for_syntax_ancestor(new_range.clone())
8421 {
8422 new_range = containing_range;
8423 if !display_map.intersects_fold(new_range.start)
8424 && !display_map.intersects_fold(new_range.end)
8425 {
8426 break;
8427 }
8428 }
8429
8430 selected_larger_node |= new_range != old_range;
8431 Selection {
8432 id: selection.id,
8433 start: new_range.start,
8434 end: new_range.end,
8435 goal: SelectionGoal::None,
8436 reversed: selection.reversed,
8437 }
8438 })
8439 .collect::<Vec<_>>();
8440
8441 if selected_larger_node {
8442 stack.push(old_selections);
8443 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8444 s.select(new_selections);
8445 });
8446 }
8447 self.select_larger_syntax_node_stack = stack;
8448 }
8449
8450 pub fn select_smaller_syntax_node(
8451 &mut self,
8452 _: &SelectSmallerSyntaxNode,
8453 cx: &mut ViewContext<Self>,
8454 ) {
8455 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8456 if let Some(selections) = stack.pop() {
8457 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8458 s.select(selections.to_vec());
8459 });
8460 }
8461 self.select_larger_syntax_node_stack = stack;
8462 }
8463
8464 fn refresh_runnables(&mut self, cx: &mut ViewContext<Self>) -> Task<()> {
8465 if !EditorSettings::get_global(cx).gutter.runnables {
8466 self.clear_tasks();
8467 return Task::ready(());
8468 }
8469 let project = self.project.clone();
8470 cx.spawn(|this, mut cx| async move {
8471 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
8472 this.display_map.update(cx, |map, cx| map.snapshot(cx))
8473 }) else {
8474 return;
8475 };
8476
8477 let Some(project) = project else {
8478 return;
8479 };
8480
8481 let hide_runnables = project
8482 .update(&mut cx, |project, cx| {
8483 // Do not display any test indicators in non-dev server remote projects.
8484 project.is_remote() && project.ssh_connection_string(cx).is_none()
8485 })
8486 .unwrap_or(true);
8487 if hide_runnables {
8488 return;
8489 }
8490 let new_rows =
8491 cx.background_executor()
8492 .spawn({
8493 let snapshot = display_snapshot.clone();
8494 async move {
8495 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
8496 }
8497 })
8498 .await;
8499 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
8500
8501 this.update(&mut cx, |this, _| {
8502 this.clear_tasks();
8503 for (key, value) in rows {
8504 this.insert_tasks(key, value);
8505 }
8506 })
8507 .ok();
8508 })
8509 }
8510 fn fetch_runnable_ranges(
8511 snapshot: &DisplaySnapshot,
8512 range: Range<Anchor>,
8513 ) -> Vec<language::RunnableRange> {
8514 snapshot.buffer_snapshot.runnable_ranges(range).collect()
8515 }
8516
8517 fn runnable_rows(
8518 project: Model<Project>,
8519 snapshot: DisplaySnapshot,
8520 runnable_ranges: Vec<RunnableRange>,
8521 mut cx: AsyncWindowContext,
8522 ) -> Vec<((BufferId, u32), RunnableTasks)> {
8523 runnable_ranges
8524 .into_iter()
8525 .filter_map(|mut runnable| {
8526 let tasks = cx
8527 .update(|cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
8528 .ok()?;
8529 if tasks.is_empty() {
8530 return None;
8531 }
8532
8533 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
8534
8535 let row = snapshot
8536 .buffer_snapshot
8537 .buffer_line_for_row(MultiBufferRow(point.row))?
8538 .1
8539 .start
8540 .row;
8541
8542 let context_range =
8543 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
8544 Some((
8545 (runnable.buffer_id, row),
8546 RunnableTasks {
8547 templates: tasks,
8548 offset: MultiBufferOffset(runnable.run_range.start),
8549 context_range,
8550 column: point.column,
8551 extra_variables: runnable.extra_captures,
8552 },
8553 ))
8554 })
8555 .collect()
8556 }
8557
8558 fn templates_with_tags(
8559 project: &Model<Project>,
8560 runnable: &mut Runnable,
8561 cx: &WindowContext<'_>,
8562 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
8563 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
8564 let (worktree_id, file) = project
8565 .buffer_for_id(runnable.buffer, cx)
8566 .and_then(|buffer| buffer.read(cx).file())
8567 .map(|file| (WorktreeId::from_usize(file.worktree_id()), file.clone()))
8568 .unzip();
8569
8570 (project.task_inventory().clone(), worktree_id, file)
8571 });
8572
8573 let inventory = inventory.read(cx);
8574 let tags = mem::take(&mut runnable.tags);
8575 let mut tags: Vec<_> = tags
8576 .into_iter()
8577 .flat_map(|tag| {
8578 let tag = tag.0.clone();
8579 inventory
8580 .list_tasks(
8581 file.clone(),
8582 Some(runnable.language.clone()),
8583 worktree_id,
8584 cx,
8585 )
8586 .into_iter()
8587 .filter(move |(_, template)| {
8588 template.tags.iter().any(|source_tag| source_tag == &tag)
8589 })
8590 })
8591 .sorted_by_key(|(kind, _)| kind.to_owned())
8592 .collect();
8593 if let Some((leading_tag_source, _)) = tags.first() {
8594 // Strongest source wins; if we have worktree tag binding, prefer that to
8595 // global and language bindings;
8596 // if we have a global binding, prefer that to language binding.
8597 let first_mismatch = tags
8598 .iter()
8599 .position(|(tag_source, _)| tag_source != leading_tag_source);
8600 if let Some(index) = first_mismatch {
8601 tags.truncate(index);
8602 }
8603 }
8604
8605 tags
8606 }
8607
8608 pub fn move_to_enclosing_bracket(
8609 &mut self,
8610 _: &MoveToEnclosingBracket,
8611 cx: &mut ViewContext<Self>,
8612 ) {
8613 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8614 s.move_offsets_with(|snapshot, selection| {
8615 let Some(enclosing_bracket_ranges) =
8616 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
8617 else {
8618 return;
8619 };
8620
8621 let mut best_length = usize::MAX;
8622 let mut best_inside = false;
8623 let mut best_in_bracket_range = false;
8624 let mut best_destination = None;
8625 for (open, close) in enclosing_bracket_ranges {
8626 let close = close.to_inclusive();
8627 let length = close.end() - open.start;
8628 let inside = selection.start >= open.end && selection.end <= *close.start();
8629 let in_bracket_range = open.to_inclusive().contains(&selection.head())
8630 || close.contains(&selection.head());
8631
8632 // If best is next to a bracket and current isn't, skip
8633 if !in_bracket_range && best_in_bracket_range {
8634 continue;
8635 }
8636
8637 // Prefer smaller lengths unless best is inside and current isn't
8638 if length > best_length && (best_inside || !inside) {
8639 continue;
8640 }
8641
8642 best_length = length;
8643 best_inside = inside;
8644 best_in_bracket_range = in_bracket_range;
8645 best_destination = Some(
8646 if close.contains(&selection.start) && close.contains(&selection.end) {
8647 if inside {
8648 open.end
8649 } else {
8650 open.start
8651 }
8652 } else {
8653 if inside {
8654 *close.start()
8655 } else {
8656 *close.end()
8657 }
8658 },
8659 );
8660 }
8661
8662 if let Some(destination) = best_destination {
8663 selection.collapse_to(destination, SelectionGoal::None);
8664 }
8665 })
8666 });
8667 }
8668
8669 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
8670 self.end_selection(cx);
8671 self.selection_history.mode = SelectionHistoryMode::Undoing;
8672 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
8673 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8674 self.select_next_state = entry.select_next_state;
8675 self.select_prev_state = entry.select_prev_state;
8676 self.add_selections_state = entry.add_selections_state;
8677 self.request_autoscroll(Autoscroll::newest(), cx);
8678 }
8679 self.selection_history.mode = SelectionHistoryMode::Normal;
8680 }
8681
8682 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
8683 self.end_selection(cx);
8684 self.selection_history.mode = SelectionHistoryMode::Redoing;
8685 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
8686 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8687 self.select_next_state = entry.select_next_state;
8688 self.select_prev_state = entry.select_prev_state;
8689 self.add_selections_state = entry.add_selections_state;
8690 self.request_autoscroll(Autoscroll::newest(), cx);
8691 }
8692 self.selection_history.mode = SelectionHistoryMode::Normal;
8693 }
8694
8695 pub fn expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
8696 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
8697 }
8698
8699 pub fn expand_excerpts_down(
8700 &mut self,
8701 action: &ExpandExcerptsDown,
8702 cx: &mut ViewContext<Self>,
8703 ) {
8704 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
8705 }
8706
8707 pub fn expand_excerpts_up(&mut self, action: &ExpandExcerptsUp, cx: &mut ViewContext<Self>) {
8708 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
8709 }
8710
8711 pub fn expand_excerpts_for_direction(
8712 &mut self,
8713 lines: u32,
8714 direction: ExpandExcerptDirection,
8715 cx: &mut ViewContext<Self>,
8716 ) {
8717 let selections = self.selections.disjoint_anchors();
8718
8719 let lines = if lines == 0 {
8720 EditorSettings::get_global(cx).expand_excerpt_lines
8721 } else {
8722 lines
8723 };
8724
8725 self.buffer.update(cx, |buffer, cx| {
8726 buffer.expand_excerpts(
8727 selections
8728 .into_iter()
8729 .map(|selection| selection.head().excerpt_id)
8730 .dedup(),
8731 lines,
8732 direction,
8733 cx,
8734 )
8735 })
8736 }
8737
8738 pub fn expand_excerpt(
8739 &mut self,
8740 excerpt: ExcerptId,
8741 direction: ExpandExcerptDirection,
8742 cx: &mut ViewContext<Self>,
8743 ) {
8744 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
8745 self.buffer.update(cx, |buffer, cx| {
8746 buffer.expand_excerpts([excerpt], lines, direction, cx)
8747 })
8748 }
8749
8750 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
8751 self.go_to_diagnostic_impl(Direction::Next, cx)
8752 }
8753
8754 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
8755 self.go_to_diagnostic_impl(Direction::Prev, cx)
8756 }
8757
8758 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
8759 let buffer = self.buffer.read(cx).snapshot(cx);
8760 let selection = self.selections.newest::<usize>(cx);
8761
8762 // If there is an active Diagnostic Popover jump to its diagnostic instead.
8763 if direction == Direction::Next {
8764 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
8765 let (group_id, jump_to) = popover.activation_info();
8766 if self.activate_diagnostics(group_id, cx) {
8767 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8768 let mut new_selection = s.newest_anchor().clone();
8769 new_selection.collapse_to(jump_to, SelectionGoal::None);
8770 s.select_anchors(vec![new_selection.clone()]);
8771 });
8772 }
8773 return;
8774 }
8775 }
8776
8777 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
8778 active_diagnostics
8779 .primary_range
8780 .to_offset(&buffer)
8781 .to_inclusive()
8782 });
8783 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
8784 if active_primary_range.contains(&selection.head()) {
8785 *active_primary_range.start()
8786 } else {
8787 selection.head()
8788 }
8789 } else {
8790 selection.head()
8791 };
8792 let snapshot = self.snapshot(cx);
8793 loop {
8794 let diagnostics = if direction == Direction::Prev {
8795 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
8796 } else {
8797 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
8798 }
8799 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start));
8800 let group = diagnostics
8801 // relies on diagnostics_in_range to return diagnostics with the same starting range to
8802 // be sorted in a stable way
8803 // skip until we are at current active diagnostic, if it exists
8804 .skip_while(|entry| {
8805 (match direction {
8806 Direction::Prev => entry.range.start >= search_start,
8807 Direction::Next => entry.range.start <= search_start,
8808 }) && self
8809 .active_diagnostics
8810 .as_ref()
8811 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
8812 })
8813 .find_map(|entry| {
8814 if entry.diagnostic.is_primary
8815 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
8816 && !entry.range.is_empty()
8817 // if we match with the active diagnostic, skip it
8818 && Some(entry.diagnostic.group_id)
8819 != self.active_diagnostics.as_ref().map(|d| d.group_id)
8820 {
8821 Some((entry.range, entry.diagnostic.group_id))
8822 } else {
8823 None
8824 }
8825 });
8826
8827 if let Some((primary_range, group_id)) = group {
8828 if self.activate_diagnostics(group_id, cx) {
8829 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8830 s.select(vec![Selection {
8831 id: selection.id,
8832 start: primary_range.start,
8833 end: primary_range.start,
8834 reversed: false,
8835 goal: SelectionGoal::None,
8836 }]);
8837 });
8838 }
8839 break;
8840 } else {
8841 // Cycle around to the start of the buffer, potentially moving back to the start of
8842 // the currently active diagnostic.
8843 active_primary_range.take();
8844 if direction == Direction::Prev {
8845 if search_start == buffer.len() {
8846 break;
8847 } else {
8848 search_start = buffer.len();
8849 }
8850 } else if search_start == 0 {
8851 break;
8852 } else {
8853 search_start = 0;
8854 }
8855 }
8856 }
8857 }
8858
8859 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
8860 let snapshot = self
8861 .display_map
8862 .update(cx, |display_map, cx| display_map.snapshot(cx));
8863 let selection = self.selections.newest::<Point>(cx);
8864
8865 if !self.seek_in_direction(
8866 &snapshot,
8867 selection.head(),
8868 false,
8869 snapshot.buffer_snapshot.git_diff_hunks_in_range(
8870 MultiBufferRow(selection.head().row + 1)..MultiBufferRow::MAX,
8871 ),
8872 cx,
8873 ) {
8874 let wrapped_point = Point::zero();
8875 self.seek_in_direction(
8876 &snapshot,
8877 wrapped_point,
8878 true,
8879 snapshot.buffer_snapshot.git_diff_hunks_in_range(
8880 MultiBufferRow(wrapped_point.row + 1)..MultiBufferRow::MAX,
8881 ),
8882 cx,
8883 );
8884 }
8885 }
8886
8887 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
8888 let snapshot = self
8889 .display_map
8890 .update(cx, |display_map, cx| display_map.snapshot(cx));
8891 let selection = self.selections.newest::<Point>(cx);
8892
8893 if !self.seek_in_direction(
8894 &snapshot,
8895 selection.head(),
8896 false,
8897 snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
8898 MultiBufferRow(0)..MultiBufferRow(selection.head().row),
8899 ),
8900 cx,
8901 ) {
8902 let wrapped_point = snapshot.buffer_snapshot.max_point();
8903 self.seek_in_direction(
8904 &snapshot,
8905 wrapped_point,
8906 true,
8907 snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
8908 MultiBufferRow(0)..MultiBufferRow(wrapped_point.row),
8909 ),
8910 cx,
8911 );
8912 }
8913 }
8914
8915 fn seek_in_direction(
8916 &mut self,
8917 snapshot: &DisplaySnapshot,
8918 initial_point: Point,
8919 is_wrapped: bool,
8920 hunks: impl Iterator<Item = DiffHunk<MultiBufferRow>>,
8921 cx: &mut ViewContext<Editor>,
8922 ) -> bool {
8923 let display_point = initial_point.to_display_point(snapshot);
8924 let mut hunks = hunks
8925 .map(|hunk| diff_hunk_to_display(&hunk, &snapshot))
8926 .filter(|hunk| is_wrapped || !hunk.contains_display_row(display_point.row()))
8927 .dedup();
8928
8929 if let Some(hunk) = hunks.next() {
8930 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8931 let row = hunk.start_display_row();
8932 let point = DisplayPoint::new(row, 0);
8933 s.select_display_ranges([point..point]);
8934 });
8935
8936 true
8937 } else {
8938 false
8939 }
8940 }
8941
8942 pub fn go_to_definition(
8943 &mut self,
8944 _: &GoToDefinition,
8945 cx: &mut ViewContext<Self>,
8946 ) -> Task<Result<bool>> {
8947 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx)
8948 }
8949
8950 pub fn go_to_implementation(
8951 &mut self,
8952 _: &GoToImplementation,
8953 cx: &mut ViewContext<Self>,
8954 ) -> Task<Result<bool>> {
8955 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx)
8956 }
8957
8958 pub fn go_to_implementation_split(
8959 &mut self,
8960 _: &GoToImplementationSplit,
8961 cx: &mut ViewContext<Self>,
8962 ) -> Task<Result<bool>> {
8963 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx)
8964 }
8965
8966 pub fn go_to_type_definition(
8967 &mut self,
8968 _: &GoToTypeDefinition,
8969 cx: &mut ViewContext<Self>,
8970 ) -> Task<Result<bool>> {
8971 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx)
8972 }
8973
8974 pub fn go_to_definition_split(
8975 &mut self,
8976 _: &GoToDefinitionSplit,
8977 cx: &mut ViewContext<Self>,
8978 ) -> Task<Result<bool>> {
8979 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx)
8980 }
8981
8982 pub fn go_to_type_definition_split(
8983 &mut self,
8984 _: &GoToTypeDefinitionSplit,
8985 cx: &mut ViewContext<Self>,
8986 ) -> Task<Result<bool>> {
8987 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx)
8988 }
8989
8990 fn go_to_definition_of_kind(
8991 &mut self,
8992 kind: GotoDefinitionKind,
8993 split: bool,
8994 cx: &mut ViewContext<Self>,
8995 ) -> Task<Result<bool>> {
8996 let Some(workspace) = self.workspace() else {
8997 return Task::ready(Ok(false));
8998 };
8999 let buffer = self.buffer.read(cx);
9000 let head = self.selections.newest::<usize>(cx).head();
9001 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
9002 text_anchor
9003 } else {
9004 return Task::ready(Ok(false));
9005 };
9006
9007 let project = workspace.read(cx).project().clone();
9008 let definitions = project.update(cx, |project, cx| match kind {
9009 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
9010 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
9011 GotoDefinitionKind::Implementation => project.implementation(&buffer, head, cx),
9012 });
9013
9014 cx.spawn(|editor, mut cx| async move {
9015 let definitions = definitions.await?;
9016 let navigated = editor
9017 .update(&mut cx, |editor, cx| {
9018 editor.navigate_to_hover_links(
9019 Some(kind),
9020 definitions
9021 .into_iter()
9022 .filter(|location| {
9023 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
9024 })
9025 .map(HoverLink::Text)
9026 .collect::<Vec<_>>(),
9027 split,
9028 cx,
9029 )
9030 })?
9031 .await?;
9032 anyhow::Ok(navigated)
9033 })
9034 }
9035
9036 pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext<Self>) {
9037 let position = self.selections.newest_anchor().head();
9038 let Some((buffer, buffer_position)) =
9039 self.buffer.read(cx).text_anchor_for_position(position, cx)
9040 else {
9041 return;
9042 };
9043
9044 cx.spawn(|editor, mut cx| async move {
9045 if let Some((_, url)) = find_url(&buffer, buffer_position, cx.clone()) {
9046 editor.update(&mut cx, |_, cx| {
9047 cx.open_url(&url);
9048 })
9049 } else {
9050 Ok(())
9051 }
9052 })
9053 .detach();
9054 }
9055
9056 pub(crate) fn navigate_to_hover_links(
9057 &mut self,
9058 kind: Option<GotoDefinitionKind>,
9059 mut definitions: Vec<HoverLink>,
9060 split: bool,
9061 cx: &mut ViewContext<Editor>,
9062 ) -> Task<Result<bool>> {
9063 // If there is one definition, just open it directly
9064 if definitions.len() == 1 {
9065 let definition = definitions.pop().unwrap();
9066 let target_task = match definition {
9067 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
9068 HoverLink::InlayHint(lsp_location, server_id) => {
9069 self.compute_target_location(lsp_location, server_id, cx)
9070 }
9071 HoverLink::Url(url) => {
9072 cx.open_url(&url);
9073 Task::ready(Ok(None))
9074 }
9075 };
9076 cx.spawn(|editor, mut cx| async move {
9077 let target = target_task.await.context("target resolution task")?;
9078 if let Some(target) = target {
9079 editor.update(&mut cx, |editor, cx| {
9080 let Some(workspace) = editor.workspace() else {
9081 return false;
9082 };
9083 let pane = workspace.read(cx).active_pane().clone();
9084
9085 let range = target.range.to_offset(target.buffer.read(cx));
9086 let range = editor.range_for_match(&range);
9087
9088 /// If select range has more than one line, we
9089 /// just point the cursor to range.start.
9090 fn check_multiline_range(
9091 buffer: &Buffer,
9092 range: Range<usize>,
9093 ) -> Range<usize> {
9094 if buffer.offset_to_point(range.start).row
9095 == buffer.offset_to_point(range.end).row
9096 {
9097 range
9098 } else {
9099 range.start..range.start
9100 }
9101 }
9102
9103 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
9104 let buffer = target.buffer.read(cx);
9105 let range = check_multiline_range(buffer, range);
9106 editor.change_selections(Some(Autoscroll::focused()), cx, |s| {
9107 s.select_ranges([range]);
9108 });
9109 } else {
9110 cx.window_context().defer(move |cx| {
9111 let target_editor: View<Self> =
9112 workspace.update(cx, |workspace, cx| {
9113 let pane = if split {
9114 workspace.adjacent_pane(cx)
9115 } else {
9116 workspace.active_pane().clone()
9117 };
9118
9119 workspace.open_project_item(
9120 pane,
9121 target.buffer.clone(),
9122 true,
9123 true,
9124 cx,
9125 )
9126 });
9127 target_editor.update(cx, |target_editor, cx| {
9128 // When selecting a definition in a different buffer, disable the nav history
9129 // to avoid creating a history entry at the previous cursor location.
9130 pane.update(cx, |pane, _| pane.disable_history());
9131 let buffer = target.buffer.read(cx);
9132 let range = check_multiline_range(buffer, range);
9133 target_editor.change_selections(
9134 Some(Autoscroll::focused()),
9135 cx,
9136 |s| {
9137 s.select_ranges([range]);
9138 },
9139 );
9140 pane.update(cx, |pane, _| pane.enable_history());
9141 });
9142 });
9143 }
9144 true
9145 })
9146 } else {
9147 Ok(false)
9148 }
9149 })
9150 } else if !definitions.is_empty() {
9151 let replica_id = self.replica_id(cx);
9152 cx.spawn(|editor, mut cx| async move {
9153 let (title, location_tasks, workspace) = editor
9154 .update(&mut cx, |editor, cx| {
9155 let tab_kind = match kind {
9156 Some(GotoDefinitionKind::Implementation) => "Implementations",
9157 _ => "Definitions",
9158 };
9159 let title = definitions
9160 .iter()
9161 .find_map(|definition| match definition {
9162 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
9163 let buffer = origin.buffer.read(cx);
9164 format!(
9165 "{} for {}",
9166 tab_kind,
9167 buffer
9168 .text_for_range(origin.range.clone())
9169 .collect::<String>()
9170 )
9171 }),
9172 HoverLink::InlayHint(_, _) => None,
9173 HoverLink::Url(_) => None,
9174 })
9175 .unwrap_or(tab_kind.to_string());
9176 let location_tasks = definitions
9177 .into_iter()
9178 .map(|definition| match definition {
9179 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
9180 HoverLink::InlayHint(lsp_location, server_id) => {
9181 editor.compute_target_location(lsp_location, server_id, cx)
9182 }
9183 HoverLink::Url(_) => Task::ready(Ok(None)),
9184 })
9185 .collect::<Vec<_>>();
9186 (title, location_tasks, editor.workspace().clone())
9187 })
9188 .context("location tasks preparation")?;
9189
9190 let locations = futures::future::join_all(location_tasks)
9191 .await
9192 .into_iter()
9193 .filter_map(|location| location.transpose())
9194 .collect::<Result<_>>()
9195 .context("location tasks")?;
9196
9197 let Some(workspace) = workspace else {
9198 return Ok(false);
9199 };
9200 let opened = workspace
9201 .update(&mut cx, |workspace, cx| {
9202 Self::open_locations_in_multibuffer(
9203 workspace, locations, replica_id, title, split, cx,
9204 )
9205 })
9206 .ok();
9207
9208 anyhow::Ok(opened.is_some())
9209 })
9210 } else {
9211 Task::ready(Ok(false))
9212 }
9213 }
9214
9215 fn compute_target_location(
9216 &self,
9217 lsp_location: lsp::Location,
9218 server_id: LanguageServerId,
9219 cx: &mut ViewContext<Editor>,
9220 ) -> Task<anyhow::Result<Option<Location>>> {
9221 let Some(project) = self.project.clone() else {
9222 return Task::Ready(Some(Ok(None)));
9223 };
9224
9225 cx.spawn(move |editor, mut cx| async move {
9226 let location_task = editor.update(&mut cx, |editor, cx| {
9227 project.update(cx, |project, cx| {
9228 let language_server_name =
9229 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
9230 project
9231 .language_server_for_buffer(buffer.read(cx), server_id, cx)
9232 .map(|(lsp_adapter, _)| lsp_adapter.name.clone())
9233 });
9234 language_server_name.map(|language_server_name| {
9235 project.open_local_buffer_via_lsp(
9236 lsp_location.uri.clone(),
9237 server_id,
9238 language_server_name,
9239 cx,
9240 )
9241 })
9242 })
9243 })?;
9244 let location = match location_task {
9245 Some(task) => Some({
9246 let target_buffer_handle = task.await.context("open local buffer")?;
9247 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
9248 let target_start = target_buffer
9249 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
9250 let target_end = target_buffer
9251 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
9252 target_buffer.anchor_after(target_start)
9253 ..target_buffer.anchor_before(target_end)
9254 })?;
9255 Location {
9256 buffer: target_buffer_handle,
9257 range,
9258 }
9259 }),
9260 None => None,
9261 };
9262 Ok(location)
9263 })
9264 }
9265
9266 pub fn find_all_references(
9267 &mut self,
9268 _: &FindAllReferences,
9269 cx: &mut ViewContext<Self>,
9270 ) -> Option<Task<Result<()>>> {
9271 let multi_buffer = self.buffer.read(cx);
9272 let selection = self.selections.newest::<usize>(cx);
9273 let head = selection.head();
9274
9275 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
9276 let head_anchor = multi_buffer_snapshot.anchor_at(
9277 head,
9278 if head < selection.tail() {
9279 Bias::Right
9280 } else {
9281 Bias::Left
9282 },
9283 );
9284
9285 match self
9286 .find_all_references_task_sources
9287 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
9288 {
9289 Ok(_) => {
9290 log::info!(
9291 "Ignoring repeated FindAllReferences invocation with the position of already running task"
9292 );
9293 return None;
9294 }
9295 Err(i) => {
9296 self.find_all_references_task_sources.insert(i, head_anchor);
9297 }
9298 }
9299
9300 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
9301 let replica_id = self.replica_id(cx);
9302 let workspace = self.workspace()?;
9303 let project = workspace.read(cx).project().clone();
9304 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
9305 Some(cx.spawn(|editor, mut cx| async move {
9306 let _cleanup = defer({
9307 let mut cx = cx.clone();
9308 move || {
9309 let _ = editor.update(&mut cx, |editor, _| {
9310 if let Ok(i) =
9311 editor
9312 .find_all_references_task_sources
9313 .binary_search_by(|anchor| {
9314 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
9315 })
9316 {
9317 editor.find_all_references_task_sources.remove(i);
9318 }
9319 });
9320 }
9321 });
9322
9323 let locations = references.await?;
9324 if locations.is_empty() {
9325 return anyhow::Ok(());
9326 }
9327
9328 workspace.update(&mut cx, |workspace, cx| {
9329 let title = locations
9330 .first()
9331 .as_ref()
9332 .map(|location| {
9333 let buffer = location.buffer.read(cx);
9334 format!(
9335 "References to `{}`",
9336 buffer
9337 .text_for_range(location.range.clone())
9338 .collect::<String>()
9339 )
9340 })
9341 .unwrap();
9342 Self::open_locations_in_multibuffer(
9343 workspace, locations, replica_id, title, false, cx,
9344 );
9345 })
9346 }))
9347 }
9348
9349 /// Opens a multibuffer with the given project locations in it
9350 pub fn open_locations_in_multibuffer(
9351 workspace: &mut Workspace,
9352 mut locations: Vec<Location>,
9353 replica_id: ReplicaId,
9354 title: String,
9355 split: bool,
9356 cx: &mut ViewContext<Workspace>,
9357 ) {
9358 // If there are multiple definitions, open them in a multibuffer
9359 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
9360 let mut locations = locations.into_iter().peekable();
9361 let mut ranges_to_highlight = Vec::new();
9362 let capability = workspace.project().read(cx).capability();
9363
9364 let excerpt_buffer = cx.new_model(|cx| {
9365 let mut multibuffer = MultiBuffer::new(replica_id, capability);
9366 while let Some(location) = locations.next() {
9367 let buffer = location.buffer.read(cx);
9368 let mut ranges_for_buffer = Vec::new();
9369 let range = location.range.to_offset(buffer);
9370 ranges_for_buffer.push(range.clone());
9371
9372 while let Some(next_location) = locations.peek() {
9373 if next_location.buffer == location.buffer {
9374 ranges_for_buffer.push(next_location.range.to_offset(buffer));
9375 locations.next();
9376 } else {
9377 break;
9378 }
9379 }
9380
9381 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
9382 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
9383 location.buffer.clone(),
9384 ranges_for_buffer,
9385 DEFAULT_MULTIBUFFER_CONTEXT,
9386 cx,
9387 ))
9388 }
9389
9390 multibuffer.with_title(title)
9391 });
9392
9393 let editor = cx.new_view(|cx| {
9394 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), true, cx)
9395 });
9396 editor.update(cx, |editor, cx| {
9397 if let Some(first_range) = ranges_to_highlight.first() {
9398 editor.change_selections(None, cx, |selections| {
9399 selections.clear_disjoint();
9400 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
9401 });
9402 }
9403 editor.highlight_background::<Self>(
9404 &ranges_to_highlight,
9405 |theme| theme.editor_highlighted_line_background,
9406 cx,
9407 );
9408 });
9409
9410 let item = Box::new(editor);
9411 let item_id = item.item_id();
9412
9413 if split {
9414 workspace.split_item(SplitDirection::Right, item.clone(), cx);
9415 } else {
9416 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
9417 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
9418 pane.close_current_preview_item(cx)
9419 } else {
9420 None
9421 }
9422 });
9423 workspace.add_item_to_active_pane(item.clone(), destination_index, true, cx);
9424 }
9425 workspace.active_pane().update(cx, |pane, cx| {
9426 pane.set_preview_item_id(Some(item_id), cx);
9427 });
9428 }
9429
9430 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9431 use language::ToOffset as _;
9432
9433 let project = self.project.clone()?;
9434 let selection = self.selections.newest_anchor().clone();
9435 let (cursor_buffer, cursor_buffer_position) = self
9436 .buffer
9437 .read(cx)
9438 .text_anchor_for_position(selection.head(), cx)?;
9439 let (tail_buffer, cursor_buffer_position_end) = self
9440 .buffer
9441 .read(cx)
9442 .text_anchor_for_position(selection.tail(), cx)?;
9443 if tail_buffer != cursor_buffer {
9444 return None;
9445 }
9446
9447 let snapshot = cursor_buffer.read(cx).snapshot();
9448 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
9449 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
9450 let prepare_rename = project.update(cx, |project, cx| {
9451 project.prepare_rename(cursor_buffer.clone(), cursor_buffer_offset, cx)
9452 });
9453 drop(snapshot);
9454
9455 Some(cx.spawn(|this, mut cx| async move {
9456 let rename_range = if let Some(range) = prepare_rename.await? {
9457 Some(range)
9458 } else {
9459 this.update(&mut cx, |this, cx| {
9460 let buffer = this.buffer.read(cx).snapshot(cx);
9461 let mut buffer_highlights = this
9462 .document_highlights_for_position(selection.head(), &buffer)
9463 .filter(|highlight| {
9464 highlight.start.excerpt_id == selection.head().excerpt_id
9465 && highlight.end.excerpt_id == selection.head().excerpt_id
9466 });
9467 buffer_highlights
9468 .next()
9469 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
9470 })?
9471 };
9472 if let Some(rename_range) = rename_range {
9473 this.update(&mut cx, |this, cx| {
9474 let snapshot = cursor_buffer.read(cx).snapshot();
9475 let rename_buffer_range = rename_range.to_offset(&snapshot);
9476 let cursor_offset_in_rename_range =
9477 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
9478 let cursor_offset_in_rename_range_end =
9479 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
9480
9481 this.take_rename(false, cx);
9482 let buffer = this.buffer.read(cx).read(cx);
9483 let cursor_offset = selection.head().to_offset(&buffer);
9484 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
9485 let rename_end = rename_start + rename_buffer_range.len();
9486 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
9487 let mut old_highlight_id = None;
9488 let old_name: Arc<str> = buffer
9489 .chunks(rename_start..rename_end, true)
9490 .map(|chunk| {
9491 if old_highlight_id.is_none() {
9492 old_highlight_id = chunk.syntax_highlight_id;
9493 }
9494 chunk.text
9495 })
9496 .collect::<String>()
9497 .into();
9498
9499 drop(buffer);
9500
9501 // Position the selection in the rename editor so that it matches the current selection.
9502 this.show_local_selections = false;
9503 let rename_editor = cx.new_view(|cx| {
9504 let mut editor = Editor::single_line(cx);
9505 editor.buffer.update(cx, |buffer, cx| {
9506 buffer.edit([(0..0, old_name.clone())], None, cx)
9507 });
9508 let rename_selection_range = match cursor_offset_in_rename_range
9509 .cmp(&cursor_offset_in_rename_range_end)
9510 {
9511 Ordering::Equal => {
9512 editor.select_all(&SelectAll, cx);
9513 return editor;
9514 }
9515 Ordering::Less => {
9516 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
9517 }
9518 Ordering::Greater => {
9519 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
9520 }
9521 };
9522 if rename_selection_range.end > old_name.len() {
9523 editor.select_all(&SelectAll, cx);
9524 } else {
9525 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
9526 s.select_ranges([rename_selection_range]);
9527 });
9528 }
9529 editor
9530 });
9531 cx.subscribe(&rename_editor, |_, _, e, cx| match e {
9532 EditorEvent::Focused => cx.emit(EditorEvent::FocusedIn),
9533 _ => {}
9534 })
9535 .detach();
9536
9537 let write_highlights =
9538 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
9539 let read_highlights =
9540 this.clear_background_highlights::<DocumentHighlightRead>(cx);
9541 let ranges = write_highlights
9542 .iter()
9543 .flat_map(|(_, ranges)| ranges.iter())
9544 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
9545 .cloned()
9546 .collect();
9547
9548 this.highlight_text::<Rename>(
9549 ranges,
9550 HighlightStyle {
9551 fade_out: Some(0.6),
9552 ..Default::default()
9553 },
9554 cx,
9555 );
9556 let rename_focus_handle = rename_editor.focus_handle(cx);
9557 cx.focus(&rename_focus_handle);
9558 let block_id = this.insert_blocks(
9559 [BlockProperties {
9560 style: BlockStyle::Flex,
9561 position: range.start,
9562 height: 1,
9563 render: Box::new({
9564 let rename_editor = rename_editor.clone();
9565 move |cx: &mut BlockContext| {
9566 let mut text_style = cx.editor_style.text.clone();
9567 if let Some(highlight_style) = old_highlight_id
9568 .and_then(|h| h.style(&cx.editor_style.syntax))
9569 {
9570 text_style = text_style.highlight(highlight_style);
9571 }
9572 div()
9573 .pl(cx.anchor_x)
9574 .child(EditorElement::new(
9575 &rename_editor,
9576 EditorStyle {
9577 background: cx.theme().system().transparent,
9578 local_player: cx.editor_style.local_player,
9579 text: text_style,
9580 scrollbar_width: cx.editor_style.scrollbar_width,
9581 syntax: cx.editor_style.syntax.clone(),
9582 status: cx.editor_style.status.clone(),
9583 inlay_hints_style: HighlightStyle {
9584 color: Some(cx.theme().status().hint),
9585 font_weight: Some(FontWeight::BOLD),
9586 ..HighlightStyle::default()
9587 },
9588 suggestions_style: HighlightStyle {
9589 color: Some(cx.theme().status().predictive),
9590 ..HighlightStyle::default()
9591 },
9592 },
9593 ))
9594 .into_any_element()
9595 }
9596 }),
9597 disposition: BlockDisposition::Below,
9598 }],
9599 Some(Autoscroll::fit()),
9600 cx,
9601 )[0];
9602 this.pending_rename = Some(RenameState {
9603 range,
9604 old_name,
9605 editor: rename_editor,
9606 block_id,
9607 });
9608 })?;
9609 }
9610
9611 Ok(())
9612 }))
9613 }
9614
9615 pub fn confirm_rename(
9616 &mut self,
9617 _: &ConfirmRename,
9618 cx: &mut ViewContext<Self>,
9619 ) -> Option<Task<Result<()>>> {
9620 let rename = self.take_rename(false, cx)?;
9621 let workspace = self.workspace()?;
9622 let (start_buffer, start) = self
9623 .buffer
9624 .read(cx)
9625 .text_anchor_for_position(rename.range.start, cx)?;
9626 let (end_buffer, end) = self
9627 .buffer
9628 .read(cx)
9629 .text_anchor_for_position(rename.range.end, cx)?;
9630 if start_buffer != end_buffer {
9631 return None;
9632 }
9633
9634 let buffer = start_buffer;
9635 let range = start..end;
9636 let old_name = rename.old_name;
9637 let new_name = rename.editor.read(cx).text(cx);
9638
9639 let rename = workspace
9640 .read(cx)
9641 .project()
9642 .clone()
9643 .update(cx, |project, cx| {
9644 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
9645 });
9646 let workspace = workspace.downgrade();
9647
9648 Some(cx.spawn(|editor, mut cx| async move {
9649 let project_transaction = rename.await?;
9650 Self::open_project_transaction(
9651 &editor,
9652 workspace,
9653 project_transaction,
9654 format!("Rename: {} → {}", old_name, new_name),
9655 cx.clone(),
9656 )
9657 .await?;
9658
9659 editor.update(&mut cx, |editor, cx| {
9660 editor.refresh_document_highlights(cx);
9661 })?;
9662 Ok(())
9663 }))
9664 }
9665
9666 fn take_rename(
9667 &mut self,
9668 moving_cursor: bool,
9669 cx: &mut ViewContext<Self>,
9670 ) -> Option<RenameState> {
9671 let rename = self.pending_rename.take()?;
9672 if rename.editor.focus_handle(cx).is_focused(cx) {
9673 cx.focus(&self.focus_handle);
9674 }
9675
9676 self.remove_blocks(
9677 [rename.block_id].into_iter().collect(),
9678 Some(Autoscroll::fit()),
9679 cx,
9680 );
9681 self.clear_highlights::<Rename>(cx);
9682 self.show_local_selections = true;
9683
9684 if moving_cursor {
9685 let rename_editor = rename.editor.read(cx);
9686 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
9687
9688 // Update the selection to match the position of the selection inside
9689 // the rename editor.
9690 let snapshot = self.buffer.read(cx).read(cx);
9691 let rename_range = rename.range.to_offset(&snapshot);
9692 let cursor_in_editor = snapshot
9693 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
9694 .min(rename_range.end);
9695 drop(snapshot);
9696
9697 self.change_selections(None, cx, |s| {
9698 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
9699 });
9700 } else {
9701 self.refresh_document_highlights(cx);
9702 }
9703
9704 Some(rename)
9705 }
9706
9707 pub fn pending_rename(&self) -> Option<&RenameState> {
9708 self.pending_rename.as_ref()
9709 }
9710
9711 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9712 let project = match &self.project {
9713 Some(project) => project.clone(),
9714 None => return None,
9715 };
9716
9717 Some(self.perform_format(project, FormatTrigger::Manual, cx))
9718 }
9719
9720 fn perform_format(
9721 &mut self,
9722 project: Model<Project>,
9723 trigger: FormatTrigger,
9724 cx: &mut ViewContext<Self>,
9725 ) -> Task<Result<()>> {
9726 let buffer = self.buffer().clone();
9727 let mut buffers = buffer.read(cx).all_buffers();
9728 if trigger == FormatTrigger::Save {
9729 buffers.retain(|buffer| buffer.read(cx).is_dirty());
9730 }
9731
9732 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
9733 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
9734
9735 cx.spawn(|_, mut cx| async move {
9736 let transaction = futures::select_biased! {
9737 () = timeout => {
9738 log::warn!("timed out waiting for formatting");
9739 None
9740 }
9741 transaction = format.log_err().fuse() => transaction,
9742 };
9743
9744 buffer
9745 .update(&mut cx, |buffer, cx| {
9746 if let Some(transaction) = transaction {
9747 if !buffer.is_singleton() {
9748 buffer.push_transaction(&transaction.0, cx);
9749 }
9750 }
9751
9752 cx.notify();
9753 })
9754 .ok();
9755
9756 Ok(())
9757 })
9758 }
9759
9760 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
9761 if let Some(project) = self.project.clone() {
9762 self.buffer.update(cx, |multi_buffer, cx| {
9763 project.update(cx, |project, cx| {
9764 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
9765 });
9766 })
9767 }
9768 }
9769
9770 fn cancel_language_server_work(
9771 &mut self,
9772 _: &CancelLanguageServerWork,
9773 cx: &mut ViewContext<Self>,
9774 ) {
9775 if let Some(project) = self.project.clone() {
9776 self.buffer.update(cx, |multi_buffer, cx| {
9777 project.update(cx, |project, cx| {
9778 project.cancel_language_server_work_for_buffers(multi_buffer.all_buffers(), cx);
9779 });
9780 })
9781 }
9782 }
9783
9784 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
9785 cx.show_character_palette();
9786 }
9787
9788 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
9789 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
9790 let buffer = self.buffer.read(cx).snapshot(cx);
9791 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
9792 let is_valid = buffer
9793 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
9794 .any(|entry| {
9795 entry.diagnostic.is_primary
9796 && !entry.range.is_empty()
9797 && entry.range.start == primary_range_start
9798 && entry.diagnostic.message == active_diagnostics.primary_message
9799 });
9800
9801 if is_valid != active_diagnostics.is_valid {
9802 active_diagnostics.is_valid = is_valid;
9803 let mut new_styles = HashMap::default();
9804 for (block_id, diagnostic) in &active_diagnostics.blocks {
9805 new_styles.insert(
9806 *block_id,
9807 (
9808 None,
9809 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
9810 ),
9811 );
9812 }
9813 self.display_map.update(cx, |display_map, cx| {
9814 display_map.replace_blocks(new_styles, cx)
9815 });
9816 }
9817 }
9818 }
9819
9820 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
9821 self.dismiss_diagnostics(cx);
9822 let snapshot = self.snapshot(cx);
9823 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
9824 let buffer = self.buffer.read(cx).snapshot(cx);
9825
9826 let mut primary_range = None;
9827 let mut primary_message = None;
9828 let mut group_end = Point::zero();
9829 let diagnostic_group = buffer
9830 .diagnostic_group::<MultiBufferPoint>(group_id)
9831 .filter_map(|entry| {
9832 if snapshot.is_line_folded(MultiBufferRow(entry.range.start.row))
9833 && (entry.range.start.row == entry.range.end.row
9834 || snapshot.is_line_folded(MultiBufferRow(entry.range.end.row)))
9835 {
9836 return None;
9837 }
9838 if entry.range.end > group_end {
9839 group_end = entry.range.end;
9840 }
9841 if entry.diagnostic.is_primary {
9842 primary_range = Some(entry.range.clone());
9843 primary_message = Some(entry.diagnostic.message.clone());
9844 }
9845 Some(entry)
9846 })
9847 .collect::<Vec<_>>();
9848 let primary_range = primary_range?;
9849 let primary_message = primary_message?;
9850 let primary_range =
9851 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
9852
9853 let blocks = display_map
9854 .insert_blocks(
9855 diagnostic_group.iter().map(|entry| {
9856 let diagnostic = entry.diagnostic.clone();
9857 let message_height = diagnostic.message.matches('\n').count() as u8 + 1;
9858 BlockProperties {
9859 style: BlockStyle::Fixed,
9860 position: buffer.anchor_after(entry.range.start),
9861 height: message_height,
9862 render: diagnostic_block_renderer(diagnostic, None, true, true),
9863 disposition: BlockDisposition::Below,
9864 }
9865 }),
9866 cx,
9867 )
9868 .into_iter()
9869 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
9870 .collect();
9871
9872 Some(ActiveDiagnosticGroup {
9873 primary_range,
9874 primary_message,
9875 group_id,
9876 blocks,
9877 is_valid: true,
9878 })
9879 });
9880 self.active_diagnostics.is_some()
9881 }
9882
9883 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
9884 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
9885 self.display_map.update(cx, |display_map, cx| {
9886 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
9887 });
9888 cx.notify();
9889 }
9890 }
9891
9892 pub fn set_selections_from_remote(
9893 &mut self,
9894 selections: Vec<Selection<Anchor>>,
9895 pending_selection: Option<Selection<Anchor>>,
9896 cx: &mut ViewContext<Self>,
9897 ) {
9898 let old_cursor_position = self.selections.newest_anchor().head();
9899 self.selections.change_with(cx, |s| {
9900 s.select_anchors(selections);
9901 if let Some(pending_selection) = pending_selection {
9902 s.set_pending(pending_selection, SelectMode::Character);
9903 } else {
9904 s.clear_pending();
9905 }
9906 });
9907 self.selections_did_change(false, &old_cursor_position, true, cx);
9908 }
9909
9910 fn push_to_selection_history(&mut self) {
9911 self.selection_history.push(SelectionHistoryEntry {
9912 selections: self.selections.disjoint_anchors(),
9913 select_next_state: self.select_next_state.clone(),
9914 select_prev_state: self.select_prev_state.clone(),
9915 add_selections_state: self.add_selections_state.clone(),
9916 });
9917 }
9918
9919 pub fn transact(
9920 &mut self,
9921 cx: &mut ViewContext<Self>,
9922 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
9923 ) -> Option<TransactionId> {
9924 self.start_transaction_at(Instant::now(), cx);
9925 update(self, cx);
9926 self.end_transaction_at(Instant::now(), cx)
9927 }
9928
9929 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
9930 self.end_selection(cx);
9931 if let Some(tx_id) = self
9932 .buffer
9933 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
9934 {
9935 self.selection_history
9936 .insert_transaction(tx_id, self.selections.disjoint_anchors());
9937 cx.emit(EditorEvent::TransactionBegun {
9938 transaction_id: tx_id,
9939 })
9940 }
9941 }
9942
9943 fn end_transaction_at(
9944 &mut self,
9945 now: Instant,
9946 cx: &mut ViewContext<Self>,
9947 ) -> Option<TransactionId> {
9948 if let Some(transaction_id) = self
9949 .buffer
9950 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
9951 {
9952 if let Some((_, end_selections)) =
9953 self.selection_history.transaction_mut(transaction_id)
9954 {
9955 *end_selections = Some(self.selections.disjoint_anchors());
9956 } else {
9957 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
9958 }
9959
9960 cx.emit(EditorEvent::Edited { transaction_id });
9961 Some(transaction_id)
9962 } else {
9963 None
9964 }
9965 }
9966
9967 pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
9968 let mut fold_ranges = Vec::new();
9969
9970 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9971
9972 let selections = self.selections.all_adjusted(cx);
9973 for selection in selections {
9974 let range = selection.range().sorted();
9975 let buffer_start_row = range.start.row;
9976
9977 for row in (0..=range.end.row).rev() {
9978 if let Some((foldable_range, fold_text)) =
9979 display_map.foldable_range(MultiBufferRow(row))
9980 {
9981 if foldable_range.end.row >= buffer_start_row {
9982 fold_ranges.push((foldable_range, fold_text));
9983 if row <= range.start.row {
9984 break;
9985 }
9986 }
9987 }
9988 }
9989 }
9990
9991 self.fold_ranges(fold_ranges, true, cx);
9992 }
9993
9994 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
9995 let buffer_row = fold_at.buffer_row;
9996 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9997
9998 if let Some((fold_range, placeholder)) = display_map.foldable_range(buffer_row) {
9999 let autoscroll = self
10000 .selections
10001 .all::<Point>(cx)
10002 .iter()
10003 .any(|selection| fold_range.overlaps(&selection.range()));
10004
10005 self.fold_ranges([(fold_range, placeholder)], autoscroll, cx);
10006 }
10007 }
10008
10009 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
10010 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10011 let buffer = &display_map.buffer_snapshot;
10012 let selections = self.selections.all::<Point>(cx);
10013 let ranges = selections
10014 .iter()
10015 .map(|s| {
10016 let range = s.display_range(&display_map).sorted();
10017 let mut start = range.start.to_point(&display_map);
10018 let mut end = range.end.to_point(&display_map);
10019 start.column = 0;
10020 end.column = buffer.line_len(MultiBufferRow(end.row));
10021 start..end
10022 })
10023 .collect::<Vec<_>>();
10024
10025 self.unfold_ranges(ranges, true, true, cx);
10026 }
10027
10028 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
10029 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10030
10031 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
10032 ..Point::new(
10033 unfold_at.buffer_row.0,
10034 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
10035 );
10036
10037 let autoscroll = self
10038 .selections
10039 .all::<Point>(cx)
10040 .iter()
10041 .any(|selection| selection.range().overlaps(&intersection_range));
10042
10043 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
10044 }
10045
10046 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
10047 let selections = self.selections.all::<Point>(cx);
10048 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10049 let line_mode = self.selections.line_mode;
10050 let ranges = selections.into_iter().map(|s| {
10051 if line_mode {
10052 let start = Point::new(s.start.row, 0);
10053 let end = Point::new(
10054 s.end.row,
10055 display_map
10056 .buffer_snapshot
10057 .line_len(MultiBufferRow(s.end.row)),
10058 );
10059 (start..end, display_map.fold_placeholder.clone())
10060 } else {
10061 (s.start..s.end, display_map.fold_placeholder.clone())
10062 }
10063 });
10064 self.fold_ranges(ranges, true, cx);
10065 }
10066
10067 pub fn fold_ranges<T: ToOffset + Clone>(
10068 &mut self,
10069 ranges: impl IntoIterator<Item = (Range<T>, FoldPlaceholder)>,
10070 auto_scroll: bool,
10071 cx: &mut ViewContext<Self>,
10072 ) {
10073 let mut fold_ranges = Vec::new();
10074 let mut buffers_affected = HashMap::default();
10075 let multi_buffer = self.buffer().read(cx);
10076 for (fold_range, fold_text) in ranges {
10077 if let Some((_, buffer, _)) =
10078 multi_buffer.excerpt_containing(fold_range.start.clone(), cx)
10079 {
10080 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
10081 };
10082 fold_ranges.push((fold_range, fold_text));
10083 }
10084
10085 let mut ranges = fold_ranges.into_iter().peekable();
10086 if ranges.peek().is_some() {
10087 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
10088
10089 if auto_scroll {
10090 self.request_autoscroll(Autoscroll::fit(), cx);
10091 }
10092
10093 for buffer in buffers_affected.into_values() {
10094 self.sync_expanded_diff_hunks(buffer, cx);
10095 }
10096
10097 cx.notify();
10098
10099 if let Some(active_diagnostics) = self.active_diagnostics.take() {
10100 // Clear diagnostics block when folding a range that contains it.
10101 let snapshot = self.snapshot(cx);
10102 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
10103 drop(snapshot);
10104 self.active_diagnostics = Some(active_diagnostics);
10105 self.dismiss_diagnostics(cx);
10106 } else {
10107 self.active_diagnostics = Some(active_diagnostics);
10108 }
10109 }
10110
10111 self.scrollbar_marker_state.dirty = true;
10112 }
10113 }
10114
10115 pub fn unfold_ranges<T: ToOffset + Clone>(
10116 &mut self,
10117 ranges: impl IntoIterator<Item = Range<T>>,
10118 inclusive: bool,
10119 auto_scroll: bool,
10120 cx: &mut ViewContext<Self>,
10121 ) {
10122 let mut unfold_ranges = Vec::new();
10123 let mut buffers_affected = HashMap::default();
10124 let multi_buffer = self.buffer().read(cx);
10125 for range in ranges {
10126 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
10127 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
10128 };
10129 unfold_ranges.push(range);
10130 }
10131
10132 let mut ranges = unfold_ranges.into_iter().peekable();
10133 if ranges.peek().is_some() {
10134 self.display_map
10135 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
10136 if auto_scroll {
10137 self.request_autoscroll(Autoscroll::fit(), cx);
10138 }
10139
10140 for buffer in buffers_affected.into_values() {
10141 self.sync_expanded_diff_hunks(buffer, cx);
10142 }
10143
10144 cx.notify();
10145 self.scrollbar_marker_state.dirty = true;
10146 self.active_indent_guides_state.dirty = true;
10147 }
10148 }
10149
10150 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
10151 if hovered != self.gutter_hovered {
10152 self.gutter_hovered = hovered;
10153 cx.notify();
10154 }
10155 }
10156
10157 pub fn insert_blocks(
10158 &mut self,
10159 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
10160 autoscroll: Option<Autoscroll>,
10161 cx: &mut ViewContext<Self>,
10162 ) -> Vec<CustomBlockId> {
10163 let blocks = self
10164 .display_map
10165 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
10166 if let Some(autoscroll) = autoscroll {
10167 self.request_autoscroll(autoscroll, cx);
10168 }
10169 blocks
10170 }
10171
10172 pub fn replace_blocks(
10173 &mut self,
10174 blocks: HashMap<CustomBlockId, (Option<u8>, RenderBlock)>,
10175 autoscroll: Option<Autoscroll>,
10176 cx: &mut ViewContext<Self>,
10177 ) {
10178 self.display_map
10179 .update(cx, |display_map, cx| display_map.replace_blocks(blocks, cx));
10180 if let Some(autoscroll) = autoscroll {
10181 self.request_autoscroll(autoscroll, cx);
10182 }
10183 }
10184
10185 pub fn remove_blocks(
10186 &mut self,
10187 block_ids: HashSet<CustomBlockId>,
10188 autoscroll: Option<Autoscroll>,
10189 cx: &mut ViewContext<Self>,
10190 ) {
10191 self.display_map.update(cx, |display_map, cx| {
10192 display_map.remove_blocks(block_ids, cx)
10193 });
10194 if let Some(autoscroll) = autoscroll {
10195 self.request_autoscroll(autoscroll, cx);
10196 }
10197 }
10198
10199 pub fn row_for_block(
10200 &self,
10201 block_id: CustomBlockId,
10202 cx: &mut ViewContext<Self>,
10203 ) -> Option<DisplayRow> {
10204 self.display_map
10205 .update(cx, |map, cx| map.row_for_block(block_id, cx))
10206 }
10207
10208 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
10209 self.focused_block = Some(focused_block);
10210 }
10211
10212 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
10213 self.focused_block.take()
10214 }
10215
10216 pub fn insert_creases(
10217 &mut self,
10218 creases: impl IntoIterator<Item = Crease>,
10219 cx: &mut ViewContext<Self>,
10220 ) -> Vec<CreaseId> {
10221 self.display_map
10222 .update(cx, |map, cx| map.insert_creases(creases, cx))
10223 }
10224
10225 pub fn remove_creases(
10226 &mut self,
10227 ids: impl IntoIterator<Item = CreaseId>,
10228 cx: &mut ViewContext<Self>,
10229 ) {
10230 self.display_map
10231 .update(cx, |map, cx| map.remove_creases(ids, cx));
10232 }
10233
10234 pub fn longest_row(&self, cx: &mut AppContext) -> DisplayRow {
10235 self.display_map
10236 .update(cx, |map, cx| map.snapshot(cx))
10237 .longest_row()
10238 }
10239
10240 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
10241 self.display_map
10242 .update(cx, |map, cx| map.snapshot(cx))
10243 .max_point()
10244 }
10245
10246 pub fn text(&self, cx: &AppContext) -> String {
10247 self.buffer.read(cx).read(cx).text()
10248 }
10249
10250 pub fn text_option(&self, cx: &AppContext) -> Option<String> {
10251 let text = self.text(cx);
10252 let text = text.trim();
10253
10254 if text.is_empty() {
10255 return None;
10256 }
10257
10258 Some(text.to_string())
10259 }
10260
10261 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
10262 self.transact(cx, |this, cx| {
10263 this.buffer
10264 .read(cx)
10265 .as_singleton()
10266 .expect("you can only call set_text on editors for singleton buffers")
10267 .update(cx, |buffer, cx| buffer.set_text(text, cx));
10268 });
10269 }
10270
10271 pub fn display_text(&self, cx: &mut AppContext) -> String {
10272 self.display_map
10273 .update(cx, |map, cx| map.snapshot(cx))
10274 .text()
10275 }
10276
10277 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
10278 let mut wrap_guides = smallvec::smallvec![];
10279
10280 if self.show_wrap_guides == Some(false) {
10281 return wrap_guides;
10282 }
10283
10284 let settings = self.buffer.read(cx).settings_at(0, cx);
10285 if settings.show_wrap_guides {
10286 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
10287 wrap_guides.push((soft_wrap as usize, true));
10288 }
10289 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
10290 }
10291
10292 wrap_guides
10293 }
10294
10295 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
10296 let settings = self.buffer.read(cx).settings_at(0, cx);
10297 let mode = self
10298 .soft_wrap_mode_override
10299 .unwrap_or_else(|| settings.soft_wrap);
10300 match mode {
10301 language_settings::SoftWrap::None => SoftWrap::None,
10302 language_settings::SoftWrap::PreferLine => SoftWrap::PreferLine,
10303 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
10304 language_settings::SoftWrap::PreferredLineLength => {
10305 SoftWrap::Column(settings.preferred_line_length)
10306 }
10307 }
10308 }
10309
10310 pub fn set_soft_wrap_mode(
10311 &mut self,
10312 mode: language_settings::SoftWrap,
10313 cx: &mut ViewContext<Self>,
10314 ) {
10315 self.soft_wrap_mode_override = Some(mode);
10316 cx.notify();
10317 }
10318
10319 pub fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
10320 let rem_size = cx.rem_size();
10321 self.display_map.update(cx, |map, cx| {
10322 map.set_font(
10323 style.text.font(),
10324 style.text.font_size.to_pixels(rem_size),
10325 cx,
10326 )
10327 });
10328 self.style = Some(style);
10329 }
10330
10331 pub fn style(&self) -> Option<&EditorStyle> {
10332 self.style.as_ref()
10333 }
10334
10335 // Called by the element. This method is not designed to be called outside of the editor
10336 // element's layout code because it does not notify when rewrapping is computed synchronously.
10337 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
10338 self.display_map
10339 .update(cx, |map, cx| map.set_wrap_width(width, cx))
10340 }
10341
10342 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
10343 if self.soft_wrap_mode_override.is_some() {
10344 self.soft_wrap_mode_override.take();
10345 } else {
10346 let soft_wrap = match self.soft_wrap_mode(cx) {
10347 SoftWrap::None | SoftWrap::PreferLine => language_settings::SoftWrap::EditorWidth,
10348 SoftWrap::EditorWidth | SoftWrap::Column(_) => {
10349 language_settings::SoftWrap::PreferLine
10350 }
10351 };
10352 self.soft_wrap_mode_override = Some(soft_wrap);
10353 }
10354 cx.notify();
10355 }
10356
10357 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, cx: &mut ViewContext<Self>) {
10358 let Some(workspace) = self.workspace() else {
10359 return;
10360 };
10361 let fs = workspace.read(cx).app_state().fs.clone();
10362 let current_show = TabBarSettings::get_global(cx).show;
10363 update_settings_file::<TabBarSettings>(fs, cx, move |setting| {
10364 setting.show = Some(!current_show);
10365 });
10366 }
10367
10368 pub fn toggle_indent_guides(&mut self, _: &ToggleIndentGuides, cx: &mut ViewContext<Self>) {
10369 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
10370 self.buffer
10371 .read(cx)
10372 .settings_at(0, cx)
10373 .indent_guides
10374 .enabled
10375 });
10376 self.show_indent_guides = Some(!currently_enabled);
10377 cx.notify();
10378 }
10379
10380 fn should_show_indent_guides(&self) -> Option<bool> {
10381 self.show_indent_guides
10382 }
10383
10384 pub fn toggle_line_numbers(&mut self, _: &ToggleLineNumbers, cx: &mut ViewContext<Self>) {
10385 let mut editor_settings = EditorSettings::get_global(cx).clone();
10386 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
10387 EditorSettings::override_global(editor_settings, cx);
10388 }
10389
10390 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
10391 self.show_gutter = show_gutter;
10392 cx.notify();
10393 }
10394
10395 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut ViewContext<Self>) {
10396 self.show_line_numbers = Some(show_line_numbers);
10397 cx.notify();
10398 }
10399
10400 pub fn set_show_git_diff_gutter(
10401 &mut self,
10402 show_git_diff_gutter: bool,
10403 cx: &mut ViewContext<Self>,
10404 ) {
10405 self.show_git_diff_gutter = Some(show_git_diff_gutter);
10406 cx.notify();
10407 }
10408
10409 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut ViewContext<Self>) {
10410 self.show_code_actions = Some(show_code_actions);
10411 cx.notify();
10412 }
10413
10414 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut ViewContext<Self>) {
10415 self.show_runnables = Some(show_runnables);
10416 cx.notify();
10417 }
10418
10419 pub fn set_redact_all(&mut self, redact_all: bool, cx: &mut ViewContext<Self>) {
10420 self.redact_all = redact_all;
10421 cx.notify();
10422 }
10423
10424 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut ViewContext<Self>) {
10425 self.show_wrap_guides = Some(show_wrap_guides);
10426 cx.notify();
10427 }
10428
10429 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut ViewContext<Self>) {
10430 self.show_indent_guides = Some(show_indent_guides);
10431 cx.notify();
10432 }
10433
10434 pub fn working_directory(&self, cx: &WindowContext) -> Option<PathBuf> {
10435 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10436 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10437 if let Some(dir) = file.abs_path(cx).parent() {
10438 return Some(dir.to_owned());
10439 }
10440 }
10441
10442 if let Some(project_path) = buffer.read(cx).project_path(cx) {
10443 return Some(project_path.path.to_path_buf());
10444 }
10445 }
10446
10447 None
10448 }
10449
10450 pub fn reveal_in_finder(&mut self, _: &RevealInFileManager, cx: &mut ViewContext<Self>) {
10451 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10452 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10453 cx.reveal_path(&file.abs_path(cx));
10454 }
10455 }
10456 }
10457
10458 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
10459 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10460 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10461 if let Some(path) = file.abs_path(cx).to_str() {
10462 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
10463 }
10464 }
10465 }
10466 }
10467
10468 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
10469 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10470 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10471 if let Some(path) = file.path().to_str() {
10472 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
10473 }
10474 }
10475 }
10476 }
10477
10478 pub fn toggle_git_blame(&mut self, _: &ToggleGitBlame, cx: &mut ViewContext<Self>) {
10479 self.show_git_blame_gutter = !self.show_git_blame_gutter;
10480
10481 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
10482 self.start_git_blame(true, cx);
10483 }
10484
10485 cx.notify();
10486 }
10487
10488 pub fn toggle_git_blame_inline(
10489 &mut self,
10490 _: &ToggleGitBlameInline,
10491 cx: &mut ViewContext<Self>,
10492 ) {
10493 self.toggle_git_blame_inline_internal(true, cx);
10494 cx.notify();
10495 }
10496
10497 pub fn git_blame_inline_enabled(&self) -> bool {
10498 self.git_blame_inline_enabled
10499 }
10500
10501 pub fn toggle_selection_menu(&mut self, _: &ToggleSelectionMenu, cx: &mut ViewContext<Self>) {
10502 self.show_selection_menu = self
10503 .show_selection_menu
10504 .map(|show_selections_menu| !show_selections_menu)
10505 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
10506
10507 cx.notify();
10508 }
10509
10510 pub fn selection_menu_enabled(&self, cx: &AppContext) -> bool {
10511 self.show_selection_menu
10512 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
10513 }
10514
10515 fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
10516 if let Some(project) = self.project.as_ref() {
10517 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
10518 return;
10519 };
10520
10521 if buffer.read(cx).file().is_none() {
10522 return;
10523 }
10524
10525 let focused = self.focus_handle(cx).contains_focused(cx);
10526
10527 let project = project.clone();
10528 let blame =
10529 cx.new_model(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
10530 self.blame_subscription = Some(cx.observe(&blame, |_, _, cx| cx.notify()));
10531 self.blame = Some(blame);
10532 }
10533 }
10534
10535 fn toggle_git_blame_inline_internal(
10536 &mut self,
10537 user_triggered: bool,
10538 cx: &mut ViewContext<Self>,
10539 ) {
10540 if self.git_blame_inline_enabled {
10541 self.git_blame_inline_enabled = false;
10542 self.show_git_blame_inline = false;
10543 self.show_git_blame_inline_delay_task.take();
10544 } else {
10545 self.git_blame_inline_enabled = true;
10546 self.start_git_blame_inline(user_triggered, cx);
10547 }
10548
10549 cx.notify();
10550 }
10551
10552 fn start_git_blame_inline(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
10553 self.start_git_blame(user_triggered, cx);
10554
10555 if ProjectSettings::get_global(cx)
10556 .git
10557 .inline_blame_delay()
10558 .is_some()
10559 {
10560 self.start_inline_blame_timer(cx);
10561 } else {
10562 self.show_git_blame_inline = true
10563 }
10564 }
10565
10566 pub fn blame(&self) -> Option<&Model<GitBlame>> {
10567 self.blame.as_ref()
10568 }
10569
10570 pub fn render_git_blame_gutter(&mut self, cx: &mut WindowContext) -> bool {
10571 self.show_git_blame_gutter && self.has_blame_entries(cx)
10572 }
10573
10574 pub fn render_git_blame_inline(&mut self, cx: &mut WindowContext) -> bool {
10575 self.show_git_blame_inline
10576 && self.focus_handle.is_focused(cx)
10577 && !self.newest_selection_head_on_empty_line(cx)
10578 && self.has_blame_entries(cx)
10579 }
10580
10581 fn has_blame_entries(&self, cx: &mut WindowContext) -> bool {
10582 self.blame()
10583 .map_or(false, |blame| blame.read(cx).has_generated_entries())
10584 }
10585
10586 fn newest_selection_head_on_empty_line(&mut self, cx: &mut WindowContext) -> bool {
10587 let cursor_anchor = self.selections.newest_anchor().head();
10588
10589 let snapshot = self.buffer.read(cx).snapshot(cx);
10590 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
10591
10592 snapshot.line_len(buffer_row) == 0
10593 }
10594
10595 fn get_permalink_to_line(&mut self, cx: &mut ViewContext<Self>) -> Result<url::Url> {
10596 let (path, selection, repo) = maybe!({
10597 let project_handle = self.project.as_ref()?.clone();
10598 let project = project_handle.read(cx);
10599
10600 let selection = self.selections.newest::<Point>(cx);
10601 let selection_range = selection.range();
10602
10603 let (buffer, selection) = if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10604 (buffer, selection_range.start.row..selection_range.end.row)
10605 } else {
10606 let buffer_ranges = self
10607 .buffer()
10608 .read(cx)
10609 .range_to_buffer_ranges(selection_range, cx);
10610
10611 let (buffer, range, _) = if selection.reversed {
10612 buffer_ranges.first()
10613 } else {
10614 buffer_ranges.last()
10615 }?;
10616
10617 let snapshot = buffer.read(cx).snapshot();
10618 let selection = text::ToPoint::to_point(&range.start, &snapshot).row
10619 ..text::ToPoint::to_point(&range.end, &snapshot).row;
10620 (buffer.clone(), selection)
10621 };
10622
10623 let path = buffer
10624 .read(cx)
10625 .file()?
10626 .as_local()?
10627 .path()
10628 .to_str()?
10629 .to_string();
10630 let repo = project.get_repo(&buffer.read(cx).project_path(cx)?, cx)?;
10631 Some((path, selection, repo))
10632 })
10633 .ok_or_else(|| anyhow!("unable to open git repository"))?;
10634
10635 const REMOTE_NAME: &str = "origin";
10636 let origin_url = repo
10637 .remote_url(REMOTE_NAME)
10638 .ok_or_else(|| anyhow!("remote \"{REMOTE_NAME}\" not found"))?;
10639 let sha = repo
10640 .head_sha()
10641 .ok_or_else(|| anyhow!("failed to read HEAD SHA"))?;
10642
10643 let (provider, remote) =
10644 parse_git_remote_url(GitHostingProviderRegistry::default_global(cx), &origin_url)
10645 .ok_or_else(|| anyhow!("failed to parse Git remote URL"))?;
10646
10647 Ok(provider.build_permalink(
10648 remote,
10649 BuildPermalinkParams {
10650 sha: &sha,
10651 path: &path,
10652 selection: Some(selection),
10653 },
10654 ))
10655 }
10656
10657 pub fn copy_permalink_to_line(&mut self, _: &CopyPermalinkToLine, cx: &mut ViewContext<Self>) {
10658 let permalink = self.get_permalink_to_line(cx);
10659
10660 match permalink {
10661 Ok(permalink) => {
10662 cx.write_to_clipboard(ClipboardItem::new(permalink.to_string()));
10663 }
10664 Err(err) => {
10665 let message = format!("Failed to copy permalink: {err}");
10666
10667 Err::<(), anyhow::Error>(err).log_err();
10668
10669 if let Some(workspace) = self.workspace() {
10670 workspace.update(cx, |workspace, cx| {
10671 struct CopyPermalinkToLine;
10672
10673 workspace.show_toast(
10674 Toast::new(NotificationId::unique::<CopyPermalinkToLine>(), message),
10675 cx,
10676 )
10677 })
10678 }
10679 }
10680 }
10681 }
10682
10683 pub fn open_permalink_to_line(&mut self, _: &OpenPermalinkToLine, cx: &mut ViewContext<Self>) {
10684 let permalink = self.get_permalink_to_line(cx);
10685
10686 match permalink {
10687 Ok(permalink) => {
10688 cx.open_url(permalink.as_ref());
10689 }
10690 Err(err) => {
10691 let message = format!("Failed to open permalink: {err}");
10692
10693 Err::<(), anyhow::Error>(err).log_err();
10694
10695 if let Some(workspace) = self.workspace() {
10696 workspace.update(cx, |workspace, cx| {
10697 struct OpenPermalinkToLine;
10698
10699 workspace.show_toast(
10700 Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
10701 cx,
10702 )
10703 })
10704 }
10705 }
10706 }
10707 }
10708
10709 /// Adds or removes (on `None` color) a highlight for the rows corresponding to the anchor range given.
10710 /// On matching anchor range, replaces the old highlight; does not clear the other existing highlights.
10711 /// If multiple anchor ranges will produce highlights for the same row, the last range added will be used.
10712 pub fn highlight_rows<T: 'static>(
10713 &mut self,
10714 rows: RangeInclusive<Anchor>,
10715 color: Option<Hsla>,
10716 should_autoscroll: bool,
10717 cx: &mut ViewContext<Self>,
10718 ) {
10719 let snapshot = self.buffer().read(cx).snapshot(cx);
10720 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
10721 let existing_highlight_index = row_highlights.binary_search_by(|highlight| {
10722 highlight
10723 .range
10724 .start()
10725 .cmp(&rows.start(), &snapshot)
10726 .then(highlight.range.end().cmp(&rows.end(), &snapshot))
10727 });
10728 match (color, existing_highlight_index) {
10729 (Some(_), Ok(ix)) | (_, Err(ix)) => row_highlights.insert(
10730 ix,
10731 RowHighlight {
10732 index: post_inc(&mut self.highlight_order),
10733 range: rows,
10734 should_autoscroll,
10735 color,
10736 },
10737 ),
10738 (None, Ok(i)) => {
10739 row_highlights.remove(i);
10740 }
10741 }
10742 }
10743
10744 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
10745 pub fn clear_row_highlights<T: 'static>(&mut self) {
10746 self.highlighted_rows.remove(&TypeId::of::<T>());
10747 }
10748
10749 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
10750 pub fn highlighted_rows<T: 'static>(
10751 &self,
10752 ) -> Option<impl Iterator<Item = (&RangeInclusive<Anchor>, Option<&Hsla>)>> {
10753 Some(
10754 self.highlighted_rows
10755 .get(&TypeId::of::<T>())?
10756 .iter()
10757 .map(|highlight| (&highlight.range, highlight.color.as_ref())),
10758 )
10759 }
10760
10761 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
10762 /// Rerturns a map of display rows that are highlighted and their corresponding highlight color.
10763 /// Allows to ignore certain kinds of highlights.
10764 pub fn highlighted_display_rows(
10765 &mut self,
10766 cx: &mut WindowContext,
10767 ) -> BTreeMap<DisplayRow, Hsla> {
10768 let snapshot = self.snapshot(cx);
10769 let mut used_highlight_orders = HashMap::default();
10770 self.highlighted_rows
10771 .iter()
10772 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
10773 .fold(
10774 BTreeMap::<DisplayRow, Hsla>::new(),
10775 |mut unique_rows, highlight| {
10776 let start_row = highlight.range.start().to_display_point(&snapshot).row();
10777 let end_row = highlight.range.end().to_display_point(&snapshot).row();
10778 for row in start_row.0..=end_row.0 {
10779 let used_index =
10780 used_highlight_orders.entry(row).or_insert(highlight.index);
10781 if highlight.index >= *used_index {
10782 *used_index = highlight.index;
10783 match highlight.color {
10784 Some(hsla) => unique_rows.insert(DisplayRow(row), hsla),
10785 None => unique_rows.remove(&DisplayRow(row)),
10786 };
10787 }
10788 }
10789 unique_rows
10790 },
10791 )
10792 }
10793
10794 pub fn highlighted_display_row_for_autoscroll(
10795 &self,
10796 snapshot: &DisplaySnapshot,
10797 ) -> Option<DisplayRow> {
10798 self.highlighted_rows
10799 .values()
10800 .flat_map(|highlighted_rows| highlighted_rows.iter())
10801 .filter_map(|highlight| {
10802 if highlight.color.is_none() || !highlight.should_autoscroll {
10803 return None;
10804 }
10805 Some(highlight.range.start().to_display_point(&snapshot).row())
10806 })
10807 .min()
10808 }
10809
10810 pub fn set_search_within_ranges(
10811 &mut self,
10812 ranges: &[Range<Anchor>],
10813 cx: &mut ViewContext<Self>,
10814 ) {
10815 self.highlight_background::<SearchWithinRange>(
10816 ranges,
10817 |colors| colors.editor_document_highlight_read_background,
10818 cx,
10819 )
10820 }
10821
10822 pub fn set_breadcrumb_header(&mut self, new_header: String) {
10823 self.breadcrumb_header = Some(new_header);
10824 }
10825
10826 pub fn clear_search_within_ranges(&mut self, cx: &mut ViewContext<Self>) {
10827 self.clear_background_highlights::<SearchWithinRange>(cx);
10828 }
10829
10830 pub fn highlight_background<T: 'static>(
10831 &mut self,
10832 ranges: &[Range<Anchor>],
10833 color_fetcher: fn(&ThemeColors) -> Hsla,
10834 cx: &mut ViewContext<Self>,
10835 ) {
10836 let snapshot = self.snapshot(cx);
10837 // this is to try and catch a panic sooner
10838 for range in ranges {
10839 snapshot
10840 .buffer_snapshot
10841 .summary_for_anchor::<usize>(&range.start);
10842 snapshot
10843 .buffer_snapshot
10844 .summary_for_anchor::<usize>(&range.end);
10845 }
10846
10847 self.background_highlights
10848 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
10849 self.scrollbar_marker_state.dirty = true;
10850 cx.notify();
10851 }
10852
10853 pub fn clear_background_highlights<T: 'static>(
10854 &mut self,
10855 cx: &mut ViewContext<Self>,
10856 ) -> Option<BackgroundHighlight> {
10857 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
10858 if !text_highlights.1.is_empty() {
10859 self.scrollbar_marker_state.dirty = true;
10860 cx.notify();
10861 }
10862 Some(text_highlights)
10863 }
10864
10865 pub fn highlight_gutter<T: 'static>(
10866 &mut self,
10867 ranges: &[Range<Anchor>],
10868 color_fetcher: fn(&AppContext) -> Hsla,
10869 cx: &mut ViewContext<Self>,
10870 ) {
10871 self.gutter_highlights
10872 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
10873 cx.notify();
10874 }
10875
10876 pub fn clear_gutter_highlights<T: 'static>(
10877 &mut self,
10878 cx: &mut ViewContext<Self>,
10879 ) -> Option<GutterHighlight> {
10880 cx.notify();
10881 self.gutter_highlights.remove(&TypeId::of::<T>())
10882 }
10883
10884 #[cfg(feature = "test-support")]
10885 pub fn all_text_background_highlights(
10886 &mut self,
10887 cx: &mut ViewContext<Self>,
10888 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
10889 let snapshot = self.snapshot(cx);
10890 let buffer = &snapshot.buffer_snapshot;
10891 let start = buffer.anchor_before(0);
10892 let end = buffer.anchor_after(buffer.len());
10893 let theme = cx.theme().colors();
10894 self.background_highlights_in_range(start..end, &snapshot, theme)
10895 }
10896
10897 #[cfg(feature = "test-support")]
10898 pub fn search_background_highlights(
10899 &mut self,
10900 cx: &mut ViewContext<Self>,
10901 ) -> Vec<Range<Point>> {
10902 let snapshot = self.buffer().read(cx).snapshot(cx);
10903
10904 let highlights = self
10905 .background_highlights
10906 .get(&TypeId::of::<items::BufferSearchHighlights>());
10907
10908 if let Some((_color, ranges)) = highlights {
10909 ranges
10910 .iter()
10911 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
10912 .collect_vec()
10913 } else {
10914 vec![]
10915 }
10916 }
10917
10918 fn document_highlights_for_position<'a>(
10919 &'a self,
10920 position: Anchor,
10921 buffer: &'a MultiBufferSnapshot,
10922 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
10923 let read_highlights = self
10924 .background_highlights
10925 .get(&TypeId::of::<DocumentHighlightRead>())
10926 .map(|h| &h.1);
10927 let write_highlights = self
10928 .background_highlights
10929 .get(&TypeId::of::<DocumentHighlightWrite>())
10930 .map(|h| &h.1);
10931 let left_position = position.bias_left(buffer);
10932 let right_position = position.bias_right(buffer);
10933 read_highlights
10934 .into_iter()
10935 .chain(write_highlights)
10936 .flat_map(move |ranges| {
10937 let start_ix = match ranges.binary_search_by(|probe| {
10938 let cmp = probe.end.cmp(&left_position, buffer);
10939 if cmp.is_ge() {
10940 Ordering::Greater
10941 } else {
10942 Ordering::Less
10943 }
10944 }) {
10945 Ok(i) | Err(i) => i,
10946 };
10947
10948 ranges[start_ix..]
10949 .iter()
10950 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
10951 })
10952 }
10953
10954 pub fn has_background_highlights<T: 'static>(&self) -> bool {
10955 self.background_highlights
10956 .get(&TypeId::of::<T>())
10957 .map_or(false, |(_, highlights)| !highlights.is_empty())
10958 }
10959
10960 pub fn background_highlights_in_range(
10961 &self,
10962 search_range: Range<Anchor>,
10963 display_snapshot: &DisplaySnapshot,
10964 theme: &ThemeColors,
10965 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
10966 let mut results = Vec::new();
10967 for (color_fetcher, ranges) in self.background_highlights.values() {
10968 let color = color_fetcher(theme);
10969 let start_ix = match ranges.binary_search_by(|probe| {
10970 let cmp = probe
10971 .end
10972 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
10973 if cmp.is_gt() {
10974 Ordering::Greater
10975 } else {
10976 Ordering::Less
10977 }
10978 }) {
10979 Ok(i) | Err(i) => i,
10980 };
10981 for range in &ranges[start_ix..] {
10982 if range
10983 .start
10984 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
10985 .is_ge()
10986 {
10987 break;
10988 }
10989
10990 let start = range.start.to_display_point(&display_snapshot);
10991 let end = range.end.to_display_point(&display_snapshot);
10992 results.push((start..end, color))
10993 }
10994 }
10995 results
10996 }
10997
10998 pub fn background_highlight_row_ranges<T: 'static>(
10999 &self,
11000 search_range: Range<Anchor>,
11001 display_snapshot: &DisplaySnapshot,
11002 count: usize,
11003 ) -> Vec<RangeInclusive<DisplayPoint>> {
11004 let mut results = Vec::new();
11005 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
11006 return vec![];
11007 };
11008
11009 let start_ix = match ranges.binary_search_by(|probe| {
11010 let cmp = probe
11011 .end
11012 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11013 if cmp.is_gt() {
11014 Ordering::Greater
11015 } else {
11016 Ordering::Less
11017 }
11018 }) {
11019 Ok(i) | Err(i) => i,
11020 };
11021 let mut push_region = |start: Option<Point>, end: Option<Point>| {
11022 if let (Some(start_display), Some(end_display)) = (start, end) {
11023 results.push(
11024 start_display.to_display_point(display_snapshot)
11025 ..=end_display.to_display_point(display_snapshot),
11026 );
11027 }
11028 };
11029 let mut start_row: Option<Point> = None;
11030 let mut end_row: Option<Point> = None;
11031 if ranges.len() > count {
11032 return Vec::new();
11033 }
11034 for range in &ranges[start_ix..] {
11035 if range
11036 .start
11037 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11038 .is_ge()
11039 {
11040 break;
11041 }
11042 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
11043 if let Some(current_row) = &end_row {
11044 if end.row == current_row.row {
11045 continue;
11046 }
11047 }
11048 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
11049 if start_row.is_none() {
11050 assert_eq!(end_row, None);
11051 start_row = Some(start);
11052 end_row = Some(end);
11053 continue;
11054 }
11055 if let Some(current_end) = end_row.as_mut() {
11056 if start.row > current_end.row + 1 {
11057 push_region(start_row, end_row);
11058 start_row = Some(start);
11059 end_row = Some(end);
11060 } else {
11061 // Merge two hunks.
11062 *current_end = end;
11063 }
11064 } else {
11065 unreachable!();
11066 }
11067 }
11068 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
11069 push_region(start_row, end_row);
11070 results
11071 }
11072
11073 pub fn gutter_highlights_in_range(
11074 &self,
11075 search_range: Range<Anchor>,
11076 display_snapshot: &DisplaySnapshot,
11077 cx: &AppContext,
11078 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11079 let mut results = Vec::new();
11080 for (color_fetcher, ranges) in self.gutter_highlights.values() {
11081 let color = color_fetcher(cx);
11082 let start_ix = match ranges.binary_search_by(|probe| {
11083 let cmp = probe
11084 .end
11085 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11086 if cmp.is_gt() {
11087 Ordering::Greater
11088 } else {
11089 Ordering::Less
11090 }
11091 }) {
11092 Ok(i) | Err(i) => i,
11093 };
11094 for range in &ranges[start_ix..] {
11095 if range
11096 .start
11097 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11098 .is_ge()
11099 {
11100 break;
11101 }
11102
11103 let start = range.start.to_display_point(&display_snapshot);
11104 let end = range.end.to_display_point(&display_snapshot);
11105 results.push((start..end, color))
11106 }
11107 }
11108 results
11109 }
11110
11111 /// Get the text ranges corresponding to the redaction query
11112 pub fn redacted_ranges(
11113 &self,
11114 search_range: Range<Anchor>,
11115 display_snapshot: &DisplaySnapshot,
11116 cx: &WindowContext,
11117 ) -> Vec<Range<DisplayPoint>> {
11118 if self.redact_all {
11119 return vec![DisplayPoint::zero()..display_snapshot.max_point()];
11120 }
11121
11122 display_snapshot
11123 .buffer_snapshot
11124 .redacted_ranges(search_range, |file| {
11125 if let Some(file) = file {
11126 file.is_private()
11127 && EditorSettings::get(Some(file.as_ref().into()), cx).redact_private_values
11128 } else {
11129 false
11130 }
11131 })
11132 .map(|range| {
11133 range.start.to_display_point(display_snapshot)
11134 ..range.end.to_display_point(display_snapshot)
11135 })
11136 .collect()
11137 }
11138
11139 pub fn highlight_text<T: 'static>(
11140 &mut self,
11141 ranges: Vec<Range<Anchor>>,
11142 style: HighlightStyle,
11143 cx: &mut ViewContext<Self>,
11144 ) {
11145 self.display_map.update(cx, |map, _| {
11146 map.highlight_text(TypeId::of::<T>(), ranges, style)
11147 });
11148 cx.notify();
11149 }
11150
11151 pub(crate) fn highlight_inlays<T: 'static>(
11152 &mut self,
11153 highlights: Vec<InlayHighlight>,
11154 style: HighlightStyle,
11155 cx: &mut ViewContext<Self>,
11156 ) {
11157 self.display_map.update(cx, |map, _| {
11158 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
11159 });
11160 cx.notify();
11161 }
11162
11163 pub fn text_highlights<'a, T: 'static>(
11164 &'a self,
11165 cx: &'a AppContext,
11166 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
11167 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
11168 }
11169
11170 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
11171 let cleared = self
11172 .display_map
11173 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
11174 if cleared {
11175 cx.notify();
11176 }
11177 }
11178
11179 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
11180 (self.read_only(cx) || self.blink_manager.read(cx).visible())
11181 && self.focus_handle.is_focused(cx)
11182 }
11183
11184 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut ViewContext<Self>) {
11185 self.show_cursor_when_unfocused = is_enabled;
11186 cx.notify();
11187 }
11188
11189 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
11190 cx.notify();
11191 }
11192
11193 fn on_buffer_event(
11194 &mut self,
11195 multibuffer: Model<MultiBuffer>,
11196 event: &multi_buffer::Event,
11197 cx: &mut ViewContext<Self>,
11198 ) {
11199 match event {
11200 multi_buffer::Event::Edited {
11201 singleton_buffer_edited,
11202 } => {
11203 self.scrollbar_marker_state.dirty = true;
11204 self.active_indent_guides_state.dirty = true;
11205 self.refresh_active_diagnostics(cx);
11206 self.refresh_code_actions(cx);
11207 if self.has_active_inline_completion(cx) {
11208 self.update_visible_inline_completion(cx);
11209 }
11210 cx.emit(EditorEvent::BufferEdited);
11211 cx.emit(SearchEvent::MatchesInvalidated);
11212 if *singleton_buffer_edited {
11213 if let Some(project) = &self.project {
11214 let project = project.read(cx);
11215 #[allow(clippy::mutable_key_type)]
11216 let languages_affected = multibuffer
11217 .read(cx)
11218 .all_buffers()
11219 .into_iter()
11220 .filter_map(|buffer| {
11221 let buffer = buffer.read(cx);
11222 let language = buffer.language()?;
11223 if project.is_local()
11224 && project.language_servers_for_buffer(buffer, cx).count() == 0
11225 {
11226 None
11227 } else {
11228 Some(language)
11229 }
11230 })
11231 .cloned()
11232 .collect::<HashSet<_>>();
11233 if !languages_affected.is_empty() {
11234 self.refresh_inlay_hints(
11235 InlayHintRefreshReason::BufferEdited(languages_affected),
11236 cx,
11237 );
11238 }
11239 }
11240 }
11241
11242 let Some(project) = &self.project else { return };
11243 let telemetry = project.read(cx).client().telemetry().clone();
11244 refresh_linked_ranges(self, cx);
11245 telemetry.log_edit_event("editor");
11246 }
11247 multi_buffer::Event::ExcerptsAdded {
11248 buffer,
11249 predecessor,
11250 excerpts,
11251 } => {
11252 self.tasks_update_task = Some(self.refresh_runnables(cx));
11253 cx.emit(EditorEvent::ExcerptsAdded {
11254 buffer: buffer.clone(),
11255 predecessor: *predecessor,
11256 excerpts: excerpts.clone(),
11257 });
11258 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
11259 }
11260 multi_buffer::Event::ExcerptsRemoved { ids } => {
11261 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
11262 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
11263 }
11264 multi_buffer::Event::ExcerptsEdited { ids } => {
11265 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
11266 }
11267 multi_buffer::Event::ExcerptsExpanded { ids } => {
11268 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
11269 }
11270 multi_buffer::Event::Reparsed(buffer_id) => {
11271 self.tasks_update_task = Some(self.refresh_runnables(cx));
11272
11273 cx.emit(EditorEvent::Reparsed(*buffer_id));
11274 }
11275 multi_buffer::Event::LanguageChanged(buffer_id) => {
11276 linked_editing_ranges::refresh_linked_ranges(self, cx);
11277 cx.emit(EditorEvent::Reparsed(*buffer_id));
11278 cx.notify();
11279 }
11280 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
11281 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
11282 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
11283 cx.emit(EditorEvent::TitleChanged)
11284 }
11285 multi_buffer::Event::DiffBaseChanged => {
11286 self.scrollbar_marker_state.dirty = true;
11287 cx.emit(EditorEvent::DiffBaseChanged);
11288 cx.notify();
11289 }
11290 multi_buffer::Event::DiffUpdated { buffer } => {
11291 self.sync_expanded_diff_hunks(buffer.clone(), cx);
11292 cx.notify();
11293 }
11294 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
11295 multi_buffer::Event::DiagnosticsUpdated => {
11296 self.refresh_active_diagnostics(cx);
11297 self.scrollbar_marker_state.dirty = true;
11298 cx.notify();
11299 }
11300 _ => {}
11301 };
11302 }
11303
11304 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
11305 cx.notify();
11306 }
11307
11308 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
11309 self.tasks_update_task = Some(self.refresh_runnables(cx));
11310 self.refresh_inline_completion(true, cx);
11311 self.refresh_inlay_hints(
11312 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
11313 self.selections.newest_anchor().head(),
11314 &self.buffer.read(cx).snapshot(cx),
11315 cx,
11316 )),
11317 cx,
11318 );
11319 let editor_settings = EditorSettings::get_global(cx);
11320 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
11321 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
11322
11323 let project_settings = ProjectSettings::get_global(cx);
11324 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
11325
11326 if self.mode == EditorMode::Full {
11327 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
11328 if self.git_blame_inline_enabled != inline_blame_enabled {
11329 self.toggle_git_blame_inline_internal(false, cx);
11330 }
11331 }
11332
11333 cx.notify();
11334 }
11335
11336 pub fn set_searchable(&mut self, searchable: bool) {
11337 self.searchable = searchable;
11338 }
11339
11340 pub fn searchable(&self) -> bool {
11341 self.searchable
11342 }
11343
11344 fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext<Self>) {
11345 self.open_excerpts_common(true, cx)
11346 }
11347
11348 fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
11349 self.open_excerpts_common(false, cx)
11350 }
11351
11352 fn open_excerpts_common(&mut self, split: bool, cx: &mut ViewContext<Self>) {
11353 let buffer = self.buffer.read(cx);
11354 if buffer.is_singleton() {
11355 cx.propagate();
11356 return;
11357 }
11358
11359 let Some(workspace) = self.workspace() else {
11360 cx.propagate();
11361 return;
11362 };
11363
11364 let mut new_selections_by_buffer = HashMap::default();
11365 for selection in self.selections.all::<usize>(cx) {
11366 for (buffer, mut range, _) in
11367 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
11368 {
11369 if selection.reversed {
11370 mem::swap(&mut range.start, &mut range.end);
11371 }
11372 new_selections_by_buffer
11373 .entry(buffer)
11374 .or_insert(Vec::new())
11375 .push(range)
11376 }
11377 }
11378
11379 // We defer the pane interaction because we ourselves are a workspace item
11380 // and activating a new item causes the pane to call a method on us reentrantly,
11381 // which panics if we're on the stack.
11382 cx.window_context().defer(move |cx| {
11383 workspace.update(cx, |workspace, cx| {
11384 let pane = if split {
11385 workspace.adjacent_pane(cx)
11386 } else {
11387 workspace.active_pane().clone()
11388 };
11389
11390 for (buffer, ranges) in new_selections_by_buffer {
11391 let editor =
11392 workspace.open_project_item::<Self>(pane.clone(), buffer, true, true, cx);
11393 editor.update(cx, |editor, cx| {
11394 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
11395 s.select_ranges(ranges);
11396 });
11397 });
11398 }
11399 })
11400 });
11401 }
11402
11403 fn jump(
11404 &mut self,
11405 path: ProjectPath,
11406 position: Point,
11407 anchor: language::Anchor,
11408 offset_from_top: u32,
11409 cx: &mut ViewContext<Self>,
11410 ) {
11411 let workspace = self.workspace();
11412 cx.spawn(|_, mut cx| async move {
11413 let workspace = workspace.ok_or_else(|| anyhow!("cannot jump without workspace"))?;
11414 let editor = workspace.update(&mut cx, |workspace, cx| {
11415 // Reset the preview item id before opening the new item
11416 workspace.active_pane().update(cx, |pane, cx| {
11417 pane.set_preview_item_id(None, cx);
11418 });
11419 workspace.open_path_preview(path, None, true, true, cx)
11420 })?;
11421 let editor = editor
11422 .await?
11423 .downcast::<Editor>()
11424 .ok_or_else(|| anyhow!("opened item was not an editor"))?
11425 .downgrade();
11426 editor.update(&mut cx, |editor, cx| {
11427 let buffer = editor
11428 .buffer()
11429 .read(cx)
11430 .as_singleton()
11431 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
11432 let buffer = buffer.read(cx);
11433 let cursor = if buffer.can_resolve(&anchor) {
11434 language::ToPoint::to_point(&anchor, buffer)
11435 } else {
11436 buffer.clip_point(position, Bias::Left)
11437 };
11438
11439 let nav_history = editor.nav_history.take();
11440 editor.change_selections(
11441 Some(Autoscroll::top_relative(offset_from_top as usize)),
11442 cx,
11443 |s| {
11444 s.select_ranges([cursor..cursor]);
11445 },
11446 );
11447 editor.nav_history = nav_history;
11448
11449 anyhow::Ok(())
11450 })??;
11451
11452 anyhow::Ok(())
11453 })
11454 .detach_and_log_err(cx);
11455 }
11456
11457 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
11458 let snapshot = self.buffer.read(cx).read(cx);
11459 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
11460 Some(
11461 ranges
11462 .iter()
11463 .map(move |range| {
11464 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
11465 })
11466 .collect(),
11467 )
11468 }
11469
11470 fn selection_replacement_ranges(
11471 &self,
11472 range: Range<OffsetUtf16>,
11473 cx: &AppContext,
11474 ) -> Vec<Range<OffsetUtf16>> {
11475 let selections = self.selections.all::<OffsetUtf16>(cx);
11476 let newest_selection = selections
11477 .iter()
11478 .max_by_key(|selection| selection.id)
11479 .unwrap();
11480 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
11481 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
11482 let snapshot = self.buffer.read(cx).read(cx);
11483 selections
11484 .into_iter()
11485 .map(|mut selection| {
11486 selection.start.0 =
11487 (selection.start.0 as isize).saturating_add(start_delta) as usize;
11488 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
11489 snapshot.clip_offset_utf16(selection.start, Bias::Left)
11490 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
11491 })
11492 .collect()
11493 }
11494
11495 fn report_editor_event(
11496 &self,
11497 operation: &'static str,
11498 file_extension: Option<String>,
11499 cx: &AppContext,
11500 ) {
11501 if cfg!(any(test, feature = "test-support")) {
11502 return;
11503 }
11504
11505 let Some(project) = &self.project else { return };
11506
11507 // If None, we are in a file without an extension
11508 let file = self
11509 .buffer
11510 .read(cx)
11511 .as_singleton()
11512 .and_then(|b| b.read(cx).file());
11513 let file_extension = file_extension.or(file
11514 .as_ref()
11515 .and_then(|file| Path::new(file.file_name(cx)).extension())
11516 .and_then(|e| e.to_str())
11517 .map(|a| a.to_string()));
11518
11519 let vim_mode = cx
11520 .global::<SettingsStore>()
11521 .raw_user_settings()
11522 .get("vim_mode")
11523 == Some(&serde_json::Value::Bool(true));
11524
11525 let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
11526 == language::language_settings::InlineCompletionProvider::Copilot;
11527 let copilot_enabled_for_language = self
11528 .buffer
11529 .read(cx)
11530 .settings_at(0, cx)
11531 .show_inline_completions;
11532
11533 let telemetry = project.read(cx).client().telemetry().clone();
11534 telemetry.report_editor_event(
11535 file_extension,
11536 vim_mode,
11537 operation,
11538 copilot_enabled,
11539 copilot_enabled_for_language,
11540 )
11541 }
11542
11543 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
11544 /// with each line being an array of {text, highlight} objects.
11545 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
11546 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
11547 return;
11548 };
11549
11550 #[derive(Serialize)]
11551 struct Chunk<'a> {
11552 text: String,
11553 highlight: Option<&'a str>,
11554 }
11555
11556 let snapshot = buffer.read(cx).snapshot();
11557 let range = self
11558 .selected_text_range(cx)
11559 .and_then(|selected_range| {
11560 if selected_range.is_empty() {
11561 None
11562 } else {
11563 Some(selected_range)
11564 }
11565 })
11566 .unwrap_or_else(|| 0..snapshot.len());
11567
11568 let chunks = snapshot.chunks(range, true);
11569 let mut lines = Vec::new();
11570 let mut line: VecDeque<Chunk> = VecDeque::new();
11571
11572 let Some(style) = self.style.as_ref() else {
11573 return;
11574 };
11575
11576 for chunk in chunks {
11577 let highlight = chunk
11578 .syntax_highlight_id
11579 .and_then(|id| id.name(&style.syntax));
11580 let mut chunk_lines = chunk.text.split('\n').peekable();
11581 while let Some(text) = chunk_lines.next() {
11582 let mut merged_with_last_token = false;
11583 if let Some(last_token) = line.back_mut() {
11584 if last_token.highlight == highlight {
11585 last_token.text.push_str(text);
11586 merged_with_last_token = true;
11587 }
11588 }
11589
11590 if !merged_with_last_token {
11591 line.push_back(Chunk {
11592 text: text.into(),
11593 highlight,
11594 });
11595 }
11596
11597 if chunk_lines.peek().is_some() {
11598 if line.len() > 1 && line.front().unwrap().text.is_empty() {
11599 line.pop_front();
11600 }
11601 if line.len() > 1 && line.back().unwrap().text.is_empty() {
11602 line.pop_back();
11603 }
11604
11605 lines.push(mem::take(&mut line));
11606 }
11607 }
11608 }
11609
11610 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
11611 return;
11612 };
11613 cx.write_to_clipboard(ClipboardItem::new(lines));
11614 }
11615
11616 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
11617 &self.inlay_hint_cache
11618 }
11619
11620 pub fn replay_insert_event(
11621 &mut self,
11622 text: &str,
11623 relative_utf16_range: Option<Range<isize>>,
11624 cx: &mut ViewContext<Self>,
11625 ) {
11626 if !self.input_enabled {
11627 cx.emit(EditorEvent::InputIgnored { text: text.into() });
11628 return;
11629 }
11630 if let Some(relative_utf16_range) = relative_utf16_range {
11631 let selections = self.selections.all::<OffsetUtf16>(cx);
11632 self.change_selections(None, cx, |s| {
11633 let new_ranges = selections.into_iter().map(|range| {
11634 let start = OffsetUtf16(
11635 range
11636 .head()
11637 .0
11638 .saturating_add_signed(relative_utf16_range.start),
11639 );
11640 let end = OffsetUtf16(
11641 range
11642 .head()
11643 .0
11644 .saturating_add_signed(relative_utf16_range.end),
11645 );
11646 start..end
11647 });
11648 s.select_ranges(new_ranges);
11649 });
11650 }
11651
11652 self.handle_input(text, cx);
11653 }
11654
11655 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
11656 let Some(project) = self.project.as_ref() else {
11657 return false;
11658 };
11659 let project = project.read(cx);
11660
11661 let mut supports = false;
11662 self.buffer().read(cx).for_each_buffer(|buffer| {
11663 if !supports {
11664 supports = project
11665 .language_servers_for_buffer(buffer.read(cx), cx)
11666 .any(
11667 |(_, server)| match server.capabilities().inlay_hint_provider {
11668 Some(lsp::OneOf::Left(enabled)) => enabled,
11669 Some(lsp::OneOf::Right(_)) => true,
11670 None => false,
11671 },
11672 )
11673 }
11674 });
11675 supports
11676 }
11677
11678 pub fn focus(&self, cx: &mut WindowContext) {
11679 cx.focus(&self.focus_handle)
11680 }
11681
11682 pub fn is_focused(&self, cx: &WindowContext) -> bool {
11683 self.focus_handle.is_focused(cx)
11684 }
11685
11686 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
11687 cx.emit(EditorEvent::Focused);
11688
11689 if let Some(descendant) = self
11690 .last_focused_descendant
11691 .take()
11692 .and_then(|descendant| descendant.upgrade())
11693 {
11694 cx.focus(&descendant);
11695 } else {
11696 if let Some(blame) = self.blame.as_ref() {
11697 blame.update(cx, GitBlame::focus)
11698 }
11699
11700 self.blink_manager.update(cx, BlinkManager::enable);
11701 self.show_cursor_names(cx);
11702 self.buffer.update(cx, |buffer, cx| {
11703 buffer.finalize_last_transaction(cx);
11704 if self.leader_peer_id.is_none() {
11705 buffer.set_active_selections(
11706 &self.selections.disjoint_anchors(),
11707 self.selections.line_mode,
11708 self.cursor_shape,
11709 cx,
11710 );
11711 }
11712 });
11713 }
11714 }
11715
11716 fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
11717 cx.emit(EditorEvent::FocusedIn)
11718 }
11719
11720 fn handle_focus_out(&mut self, event: FocusOutEvent, _cx: &mut ViewContext<Self>) {
11721 if event.blurred != self.focus_handle {
11722 self.last_focused_descendant = Some(event.blurred);
11723 }
11724 }
11725
11726 pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
11727 self.blink_manager.update(cx, BlinkManager::disable);
11728 self.buffer
11729 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
11730
11731 if let Some(blame) = self.blame.as_ref() {
11732 blame.update(cx, GitBlame::blur)
11733 }
11734 if !self.hover_state.focused(cx) {
11735 hide_hover(self, cx);
11736 }
11737
11738 self.hide_context_menu(cx);
11739 cx.emit(EditorEvent::Blurred);
11740 cx.notify();
11741 }
11742
11743 pub fn register_action<A: Action>(
11744 &mut self,
11745 listener: impl Fn(&A, &mut WindowContext) + 'static,
11746 ) -> Subscription {
11747 let id = self.next_editor_action_id.post_inc();
11748 let listener = Arc::new(listener);
11749 self.editor_actions.borrow_mut().insert(
11750 id,
11751 Box::new(move |cx| {
11752 let _view = cx.view().clone();
11753 let cx = cx.window_context();
11754 let listener = listener.clone();
11755 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
11756 let action = action.downcast_ref().unwrap();
11757 if phase == DispatchPhase::Bubble {
11758 listener(action, cx)
11759 }
11760 })
11761 }),
11762 );
11763
11764 let editor_actions = self.editor_actions.clone();
11765 Subscription::new(move || {
11766 editor_actions.borrow_mut().remove(&id);
11767 })
11768 }
11769
11770 pub fn file_header_size(&self) -> u8 {
11771 self.file_header_size
11772 }
11773
11774 pub fn revert(
11775 &mut self,
11776 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
11777 cx: &mut ViewContext<Self>,
11778 ) {
11779 self.buffer().update(cx, |multi_buffer, cx| {
11780 for (buffer_id, changes) in revert_changes {
11781 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
11782 buffer.update(cx, |buffer, cx| {
11783 buffer.edit(
11784 changes.into_iter().map(|(range, text)| {
11785 (range, text.to_string().map(Arc::<str>::from))
11786 }),
11787 None,
11788 cx,
11789 );
11790 });
11791 }
11792 }
11793 });
11794 self.change_selections(None, cx, |selections| selections.refresh());
11795 }
11796
11797 pub fn to_pixel_point(
11798 &mut self,
11799 source: multi_buffer::Anchor,
11800 editor_snapshot: &EditorSnapshot,
11801 cx: &mut ViewContext<Self>,
11802 ) -> Option<gpui::Point<Pixels>> {
11803 let text_layout_details = self.text_layout_details(cx);
11804 let line_height = text_layout_details
11805 .editor_style
11806 .text
11807 .line_height_in_pixels(cx.rem_size());
11808 let source_point = source.to_display_point(editor_snapshot);
11809 let first_visible_line = text_layout_details
11810 .scroll_anchor
11811 .anchor
11812 .to_display_point(editor_snapshot);
11813 if first_visible_line > source_point {
11814 return None;
11815 }
11816 let source_x = editor_snapshot.x_for_display_point(source_point, &text_layout_details);
11817 let source_y = line_height
11818 * ((source_point.row() - first_visible_line.row()).0 as f32
11819 - text_layout_details.scroll_anchor.offset.y);
11820 Some(gpui::Point::new(source_x, source_y))
11821 }
11822
11823 pub fn display_to_pixel_point(
11824 &mut self,
11825 source: DisplayPoint,
11826 editor_snapshot: &EditorSnapshot,
11827 cx: &mut ViewContext<Self>,
11828 ) -> Option<gpui::Point<Pixels>> {
11829 let line_height = self.style()?.text.line_height_in_pixels(cx.rem_size());
11830 let text_layout_details = self.text_layout_details(cx);
11831 let first_visible_line = text_layout_details
11832 .scroll_anchor
11833 .anchor
11834 .to_display_point(editor_snapshot);
11835 if first_visible_line > source {
11836 return None;
11837 }
11838 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
11839 let source_y = line_height * (source.row() - first_visible_line.row()).0 as f32;
11840 Some(gpui::Point::new(source_x, source_y))
11841 }
11842}
11843
11844fn hunks_for_selections(
11845 multi_buffer_snapshot: &MultiBufferSnapshot,
11846 selections: &[Selection<Anchor>],
11847) -> Vec<DiffHunk<MultiBufferRow>> {
11848 let buffer_rows_for_selections = selections.iter().map(|selection| {
11849 let head = selection.head();
11850 let tail = selection.tail();
11851 let start = MultiBufferRow(tail.to_point(&multi_buffer_snapshot).row);
11852 let end = MultiBufferRow(head.to_point(&multi_buffer_snapshot).row);
11853 if start > end {
11854 end..start
11855 } else {
11856 start..end
11857 }
11858 });
11859
11860 hunks_for_rows(buffer_rows_for_selections, multi_buffer_snapshot)
11861}
11862
11863pub fn hunks_for_rows(
11864 rows: impl Iterator<Item = Range<MultiBufferRow>>,
11865 multi_buffer_snapshot: &MultiBufferSnapshot,
11866) -> Vec<DiffHunk<MultiBufferRow>> {
11867 let mut hunks = Vec::new();
11868 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
11869 HashMap::default();
11870 for selected_multi_buffer_rows in rows {
11871 let query_rows =
11872 selected_multi_buffer_rows.start..selected_multi_buffer_rows.end.next_row();
11873 for hunk in multi_buffer_snapshot.git_diff_hunks_in_range(query_rows.clone()) {
11874 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
11875 // when the caret is just above or just below the deleted hunk.
11876 let allow_adjacent = hunk_status(&hunk) == DiffHunkStatus::Removed;
11877 let related_to_selection = if allow_adjacent {
11878 hunk.associated_range.overlaps(&query_rows)
11879 || hunk.associated_range.start == query_rows.end
11880 || hunk.associated_range.end == query_rows.start
11881 } else {
11882 // `selected_multi_buffer_rows` are inclusive (e.g. [2..2] means 2nd row is selected)
11883 // `hunk.associated_range` is exclusive (e.g. [2..3] means 2nd row is selected)
11884 hunk.associated_range.overlaps(&selected_multi_buffer_rows)
11885 || selected_multi_buffer_rows.end == hunk.associated_range.start
11886 };
11887 if related_to_selection {
11888 if !processed_buffer_rows
11889 .entry(hunk.buffer_id)
11890 .or_default()
11891 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
11892 {
11893 continue;
11894 }
11895 hunks.push(hunk);
11896 }
11897 }
11898 }
11899
11900 hunks
11901}
11902
11903pub trait CollaborationHub {
11904 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
11905 fn user_participant_indices<'a>(
11906 &self,
11907 cx: &'a AppContext,
11908 ) -> &'a HashMap<u64, ParticipantIndex>;
11909 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
11910}
11911
11912impl CollaborationHub for Model<Project> {
11913 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
11914 self.read(cx).collaborators()
11915 }
11916
11917 fn user_participant_indices<'a>(
11918 &self,
11919 cx: &'a AppContext,
11920 ) -> &'a HashMap<u64, ParticipantIndex> {
11921 self.read(cx).user_store().read(cx).participant_indices()
11922 }
11923
11924 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
11925 let this = self.read(cx);
11926 let user_ids = this.collaborators().values().map(|c| c.user_id);
11927 this.user_store().read_with(cx, |user_store, cx| {
11928 user_store.participant_names(user_ids, cx)
11929 })
11930 }
11931}
11932
11933pub trait CompletionProvider {
11934 fn completions(
11935 &self,
11936 buffer: &Model<Buffer>,
11937 buffer_position: text::Anchor,
11938 trigger: CompletionContext,
11939 cx: &mut ViewContext<Editor>,
11940 ) -> Task<Result<Vec<Completion>>>;
11941
11942 fn resolve_completions(
11943 &self,
11944 buffer: Model<Buffer>,
11945 completion_indices: Vec<usize>,
11946 completions: Arc<RwLock<Box<[Completion]>>>,
11947 cx: &mut ViewContext<Editor>,
11948 ) -> Task<Result<bool>>;
11949
11950 fn apply_additional_edits_for_completion(
11951 &self,
11952 buffer: Model<Buffer>,
11953 completion: Completion,
11954 push_to_history: bool,
11955 cx: &mut ViewContext<Editor>,
11956 ) -> Task<Result<Option<language::Transaction>>>;
11957
11958 fn is_completion_trigger(
11959 &self,
11960 buffer: &Model<Buffer>,
11961 position: language::Anchor,
11962 text: &str,
11963 trigger_in_words: bool,
11964 cx: &mut ViewContext<Editor>,
11965 ) -> bool;
11966}
11967
11968fn snippet_completions(
11969 project: &Project,
11970 buffer: &Model<Buffer>,
11971 buffer_position: text::Anchor,
11972 cx: &mut AppContext,
11973) -> Vec<Completion> {
11974 let language = buffer.read(cx).language_at(buffer_position);
11975 let language_name = language.as_ref().map(|language| language.lsp_id());
11976 let snippet_store = project.snippets().read(cx);
11977 let snippets = snippet_store.snippets_for(language_name, cx);
11978
11979 if snippets.is_empty() {
11980 return vec![];
11981 }
11982 let snapshot = buffer.read(cx).text_snapshot();
11983 let chunks = snapshot.reversed_chunks_in_range(text::Anchor::MIN..buffer_position);
11984
11985 let mut lines = chunks.lines();
11986 let Some(line_at) = lines.next().filter(|line| !line.is_empty()) else {
11987 return vec![];
11988 };
11989
11990 let scope = language.map(|language| language.default_scope());
11991 let mut last_word = line_at
11992 .chars()
11993 .rev()
11994 .take_while(|c| char_kind(&scope, *c) == CharKind::Word)
11995 .collect::<String>();
11996 last_word = last_word.chars().rev().collect();
11997 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
11998 let to_lsp = |point: &text::Anchor| {
11999 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
12000 point_to_lsp(end)
12001 };
12002 let lsp_end = to_lsp(&buffer_position);
12003 snippets
12004 .into_iter()
12005 .filter_map(|snippet| {
12006 let matching_prefix = snippet
12007 .prefix
12008 .iter()
12009 .find(|prefix| prefix.starts_with(&last_word))?;
12010 let start = as_offset - last_word.len();
12011 let start = snapshot.anchor_before(start);
12012 let range = start..buffer_position;
12013 let lsp_start = to_lsp(&start);
12014 let lsp_range = lsp::Range {
12015 start: lsp_start,
12016 end: lsp_end,
12017 };
12018 Some(Completion {
12019 old_range: range,
12020 new_text: snippet.body.clone(),
12021 label: CodeLabel {
12022 text: matching_prefix.clone(),
12023 runs: vec![],
12024 filter_range: 0..matching_prefix.len(),
12025 },
12026 server_id: LanguageServerId(usize::MAX),
12027 documentation: snippet
12028 .description
12029 .clone()
12030 .map(|description| Documentation::SingleLine(description)),
12031 lsp_completion: lsp::CompletionItem {
12032 label: snippet.prefix.first().unwrap().clone(),
12033 kind: Some(CompletionItemKind::SNIPPET),
12034 label_details: snippet.description.as_ref().map(|description| {
12035 lsp::CompletionItemLabelDetails {
12036 detail: Some(description.clone()),
12037 description: None,
12038 }
12039 }),
12040 insert_text_format: Some(InsertTextFormat::SNIPPET),
12041 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
12042 lsp::InsertReplaceEdit {
12043 new_text: snippet.body.clone(),
12044 insert: lsp_range,
12045 replace: lsp_range,
12046 },
12047 )),
12048 filter_text: Some(snippet.body.clone()),
12049 sort_text: Some(char::MAX.to_string()),
12050 ..Default::default()
12051 },
12052 confirm: None,
12053 show_new_completions_on_confirm: false,
12054 })
12055 })
12056 .collect()
12057}
12058
12059impl CompletionProvider for Model<Project> {
12060 fn completions(
12061 &self,
12062 buffer: &Model<Buffer>,
12063 buffer_position: text::Anchor,
12064 options: CompletionContext,
12065 cx: &mut ViewContext<Editor>,
12066 ) -> Task<Result<Vec<Completion>>> {
12067 self.update(cx, |project, cx| {
12068 let snippets = snippet_completions(project, buffer, buffer_position, cx);
12069 let project_completions = project.completions(&buffer, buffer_position, options, cx);
12070 cx.background_executor().spawn(async move {
12071 let mut completions = project_completions.await?;
12072 //let snippets = snippets.into_iter().;
12073 completions.extend(snippets);
12074 Ok(completions)
12075 })
12076 })
12077 }
12078
12079 fn resolve_completions(
12080 &self,
12081 buffer: Model<Buffer>,
12082 completion_indices: Vec<usize>,
12083 completions: Arc<RwLock<Box<[Completion]>>>,
12084 cx: &mut ViewContext<Editor>,
12085 ) -> Task<Result<bool>> {
12086 self.update(cx, |project, cx| {
12087 project.resolve_completions(buffer, completion_indices, completions, cx)
12088 })
12089 }
12090
12091 fn apply_additional_edits_for_completion(
12092 &self,
12093 buffer: Model<Buffer>,
12094 completion: Completion,
12095 push_to_history: bool,
12096 cx: &mut ViewContext<Editor>,
12097 ) -> Task<Result<Option<language::Transaction>>> {
12098 self.update(cx, |project, cx| {
12099 project.apply_additional_edits_for_completion(buffer, completion, push_to_history, cx)
12100 })
12101 }
12102
12103 fn is_completion_trigger(
12104 &self,
12105 buffer: &Model<Buffer>,
12106 position: language::Anchor,
12107 text: &str,
12108 trigger_in_words: bool,
12109 cx: &mut ViewContext<Editor>,
12110 ) -> bool {
12111 if !EditorSettings::get_global(cx).show_completions_on_input {
12112 return false;
12113 }
12114
12115 let mut chars = text.chars();
12116 let char = if let Some(char) = chars.next() {
12117 char
12118 } else {
12119 return false;
12120 };
12121 if chars.next().is_some() {
12122 return false;
12123 }
12124
12125 let buffer = buffer.read(cx);
12126 let scope = buffer.snapshot().language_scope_at(position);
12127 if trigger_in_words && char_kind(&scope, char) == CharKind::Word {
12128 return true;
12129 }
12130
12131 buffer
12132 .completion_triggers()
12133 .iter()
12134 .any(|string| string == text)
12135 }
12136}
12137
12138fn inlay_hint_settings(
12139 location: Anchor,
12140 snapshot: &MultiBufferSnapshot,
12141 cx: &mut ViewContext<'_, Editor>,
12142) -> InlayHintSettings {
12143 let file = snapshot.file_at(location);
12144 let language = snapshot.language_at(location);
12145 let settings = all_language_settings(file, cx);
12146 settings
12147 .language(language.map(|l| l.name()).as_deref())
12148 .inlay_hints
12149}
12150
12151fn consume_contiguous_rows(
12152 contiguous_row_selections: &mut Vec<Selection<Point>>,
12153 selection: &Selection<Point>,
12154 display_map: &DisplaySnapshot,
12155 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
12156) -> (MultiBufferRow, MultiBufferRow) {
12157 contiguous_row_selections.push(selection.clone());
12158 let start_row = MultiBufferRow(selection.start.row);
12159 let mut end_row = ending_row(selection, display_map);
12160
12161 while let Some(next_selection) = selections.peek() {
12162 if next_selection.start.row <= end_row.0 {
12163 end_row = ending_row(next_selection, display_map);
12164 contiguous_row_selections.push(selections.next().unwrap().clone());
12165 } else {
12166 break;
12167 }
12168 }
12169 (start_row, end_row)
12170}
12171
12172fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
12173 if next_selection.end.column > 0 || next_selection.is_empty() {
12174 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
12175 } else {
12176 MultiBufferRow(next_selection.end.row)
12177 }
12178}
12179
12180impl EditorSnapshot {
12181 pub fn remote_selections_in_range<'a>(
12182 &'a self,
12183 range: &'a Range<Anchor>,
12184 collaboration_hub: &dyn CollaborationHub,
12185 cx: &'a AppContext,
12186 ) -> impl 'a + Iterator<Item = RemoteSelection> {
12187 let participant_names = collaboration_hub.user_names(cx);
12188 let participant_indices = collaboration_hub.user_participant_indices(cx);
12189 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
12190 let collaborators_by_replica_id = collaborators_by_peer_id
12191 .iter()
12192 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
12193 .collect::<HashMap<_, _>>();
12194 self.buffer_snapshot
12195 .selections_in_range(range, false)
12196 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
12197 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
12198 let participant_index = participant_indices.get(&collaborator.user_id).copied();
12199 let user_name = participant_names.get(&collaborator.user_id).cloned();
12200 Some(RemoteSelection {
12201 replica_id,
12202 selection,
12203 cursor_shape,
12204 line_mode,
12205 participant_index,
12206 peer_id: collaborator.peer_id,
12207 user_name,
12208 })
12209 })
12210 }
12211
12212 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
12213 self.display_snapshot.buffer_snapshot.language_at(position)
12214 }
12215
12216 pub fn is_focused(&self) -> bool {
12217 self.is_focused
12218 }
12219
12220 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
12221 self.placeholder_text.as_ref()
12222 }
12223
12224 pub fn scroll_position(&self) -> gpui::Point<f32> {
12225 self.scroll_anchor.scroll_position(&self.display_snapshot)
12226 }
12227
12228 pub fn gutter_dimensions(
12229 &self,
12230 font_id: FontId,
12231 font_size: Pixels,
12232 em_width: Pixels,
12233 max_line_number_width: Pixels,
12234 cx: &AppContext,
12235 ) -> GutterDimensions {
12236 if !self.show_gutter {
12237 return GutterDimensions::default();
12238 }
12239 let descent = cx.text_system().descent(font_id, font_size);
12240
12241 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
12242 matches!(
12243 ProjectSettings::get_global(cx).git.git_gutter,
12244 Some(GitGutterSetting::TrackedFiles)
12245 )
12246 });
12247 let gutter_settings = EditorSettings::get_global(cx).gutter;
12248 let show_line_numbers = self
12249 .show_line_numbers
12250 .unwrap_or(gutter_settings.line_numbers);
12251 let line_gutter_width = if show_line_numbers {
12252 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
12253 let min_width_for_number_on_gutter = em_width * 4.0;
12254 max_line_number_width.max(min_width_for_number_on_gutter)
12255 } else {
12256 0.0.into()
12257 };
12258
12259 let show_code_actions = self
12260 .show_code_actions
12261 .unwrap_or(gutter_settings.code_actions);
12262
12263 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
12264
12265 let git_blame_entries_width = self
12266 .render_git_blame_gutter
12267 .then_some(em_width * GIT_BLAME_GUTTER_WIDTH_CHARS);
12268
12269 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
12270 left_padding += if show_code_actions || show_runnables {
12271 em_width * 3.0
12272 } else if show_git_gutter && show_line_numbers {
12273 em_width * 2.0
12274 } else if show_git_gutter || show_line_numbers {
12275 em_width
12276 } else {
12277 px(0.)
12278 };
12279
12280 let right_padding = if gutter_settings.folds && show_line_numbers {
12281 em_width * 4.0
12282 } else if gutter_settings.folds {
12283 em_width * 3.0
12284 } else if show_line_numbers {
12285 em_width
12286 } else {
12287 px(0.)
12288 };
12289
12290 GutterDimensions {
12291 left_padding,
12292 right_padding,
12293 width: line_gutter_width + left_padding + right_padding,
12294 margin: -descent,
12295 git_blame_entries_width,
12296 }
12297 }
12298
12299 pub fn render_fold_toggle(
12300 &self,
12301 buffer_row: MultiBufferRow,
12302 row_contains_cursor: bool,
12303 editor: View<Editor>,
12304 cx: &mut WindowContext,
12305 ) -> Option<AnyElement> {
12306 let folded = self.is_line_folded(buffer_row);
12307
12308 if let Some(crease) = self
12309 .crease_snapshot
12310 .query_row(buffer_row, &self.buffer_snapshot)
12311 {
12312 let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
12313 if folded {
12314 editor.update(cx, |editor, cx| {
12315 editor.fold_at(&crate::FoldAt { buffer_row }, cx)
12316 });
12317 } else {
12318 editor.update(cx, |editor, cx| {
12319 editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
12320 });
12321 }
12322 });
12323
12324 Some((crease.render_toggle)(
12325 buffer_row,
12326 folded,
12327 toggle_callback,
12328 cx,
12329 ))
12330 } else if folded
12331 || (self.starts_indent(buffer_row) && (row_contains_cursor || self.gutter_hovered))
12332 {
12333 Some(
12334 Disclosure::new(("indent-fold-indicator", buffer_row.0), !folded)
12335 .selected(folded)
12336 .on_click(cx.listener_for(&editor, move |this, _e, cx| {
12337 if folded {
12338 this.unfold_at(&UnfoldAt { buffer_row }, cx);
12339 } else {
12340 this.fold_at(&FoldAt { buffer_row }, cx);
12341 }
12342 }))
12343 .into_any_element(),
12344 )
12345 } else {
12346 None
12347 }
12348 }
12349
12350 pub fn render_crease_trailer(
12351 &self,
12352 buffer_row: MultiBufferRow,
12353 cx: &mut WindowContext,
12354 ) -> Option<AnyElement> {
12355 let folded = self.is_line_folded(buffer_row);
12356 let crease = self
12357 .crease_snapshot
12358 .query_row(buffer_row, &self.buffer_snapshot)?;
12359 Some((crease.render_trailer)(buffer_row, folded, cx))
12360 }
12361}
12362
12363impl Deref for EditorSnapshot {
12364 type Target = DisplaySnapshot;
12365
12366 fn deref(&self) -> &Self::Target {
12367 &self.display_snapshot
12368 }
12369}
12370
12371#[derive(Clone, Debug, PartialEq, Eq)]
12372pub enum EditorEvent {
12373 InputIgnored {
12374 text: Arc<str>,
12375 },
12376 InputHandled {
12377 utf16_range_to_replace: Option<Range<isize>>,
12378 text: Arc<str>,
12379 },
12380 ExcerptsAdded {
12381 buffer: Model<Buffer>,
12382 predecessor: ExcerptId,
12383 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
12384 },
12385 ExcerptsRemoved {
12386 ids: Vec<ExcerptId>,
12387 },
12388 ExcerptsEdited {
12389 ids: Vec<ExcerptId>,
12390 },
12391 ExcerptsExpanded {
12392 ids: Vec<ExcerptId>,
12393 },
12394 BufferEdited,
12395 Edited {
12396 transaction_id: clock::Lamport,
12397 },
12398 Reparsed(BufferId),
12399 Focused,
12400 FocusedIn,
12401 Blurred,
12402 DirtyChanged,
12403 Saved,
12404 TitleChanged,
12405 DiffBaseChanged,
12406 SelectionsChanged {
12407 local: bool,
12408 },
12409 ScrollPositionChanged {
12410 local: bool,
12411 autoscroll: bool,
12412 },
12413 Closed,
12414 TransactionUndone {
12415 transaction_id: clock::Lamport,
12416 },
12417 TransactionBegun {
12418 transaction_id: clock::Lamport,
12419 },
12420}
12421
12422impl EventEmitter<EditorEvent> for Editor {}
12423
12424impl FocusableView for Editor {
12425 fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
12426 self.focus_handle.clone()
12427 }
12428}
12429
12430impl Render for Editor {
12431 fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
12432 let settings = ThemeSettings::get_global(cx);
12433
12434 let text_style = match self.mode {
12435 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
12436 color: cx.theme().colors().editor_foreground,
12437 font_family: settings.ui_font.family.clone(),
12438 font_features: settings.ui_font.features.clone(),
12439 font_size: rems(0.875).into(),
12440 font_weight: settings.ui_font.weight,
12441 font_style: FontStyle::Normal,
12442 line_height: relative(settings.buffer_line_height.value()),
12443 background_color: None,
12444 underline: None,
12445 strikethrough: None,
12446 white_space: WhiteSpace::Normal,
12447 },
12448 EditorMode::Full => TextStyle {
12449 color: cx.theme().colors().editor_foreground,
12450 font_family: settings.buffer_font.family.clone(),
12451 font_features: settings.buffer_font.features.clone(),
12452 font_size: settings.buffer_font_size(cx).into(),
12453 font_weight: settings.buffer_font.weight,
12454 font_style: FontStyle::Normal,
12455 line_height: relative(settings.buffer_line_height.value()),
12456 background_color: None,
12457 underline: None,
12458 strikethrough: None,
12459 white_space: WhiteSpace::Normal,
12460 },
12461 };
12462
12463 let background = match self.mode {
12464 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
12465 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
12466 EditorMode::Full => cx.theme().colors().editor_background,
12467 };
12468
12469 EditorElement::new(
12470 cx.view(),
12471 EditorStyle {
12472 background,
12473 local_player: cx.theme().players().local(),
12474 text: text_style,
12475 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
12476 syntax: cx.theme().syntax().clone(),
12477 status: cx.theme().status().clone(),
12478 inlay_hints_style: HighlightStyle {
12479 color: Some(cx.theme().status().hint),
12480 ..HighlightStyle::default()
12481 },
12482 suggestions_style: HighlightStyle {
12483 color: Some(cx.theme().status().predictive),
12484 ..HighlightStyle::default()
12485 },
12486 },
12487 )
12488 }
12489}
12490
12491impl ViewInputHandler for Editor {
12492 fn text_for_range(
12493 &mut self,
12494 range_utf16: Range<usize>,
12495 cx: &mut ViewContext<Self>,
12496 ) -> Option<String> {
12497 Some(
12498 self.buffer
12499 .read(cx)
12500 .read(cx)
12501 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
12502 .collect(),
12503 )
12504 }
12505
12506 fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
12507 // Prevent the IME menu from appearing when holding down an alphabetic key
12508 // while input is disabled.
12509 if !self.input_enabled {
12510 return None;
12511 }
12512
12513 let range = self.selections.newest::<OffsetUtf16>(cx).range();
12514 Some(range.start.0..range.end.0)
12515 }
12516
12517 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
12518 let snapshot = self.buffer.read(cx).read(cx);
12519 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
12520 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
12521 }
12522
12523 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
12524 self.clear_highlights::<InputComposition>(cx);
12525 self.ime_transaction.take();
12526 }
12527
12528 fn replace_text_in_range(
12529 &mut self,
12530 range_utf16: Option<Range<usize>>,
12531 text: &str,
12532 cx: &mut ViewContext<Self>,
12533 ) {
12534 if !self.input_enabled {
12535 cx.emit(EditorEvent::InputIgnored { text: text.into() });
12536 return;
12537 }
12538
12539 self.transact(cx, |this, cx| {
12540 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
12541 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
12542 Some(this.selection_replacement_ranges(range_utf16, cx))
12543 } else {
12544 this.marked_text_ranges(cx)
12545 };
12546
12547 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
12548 let newest_selection_id = this.selections.newest_anchor().id;
12549 this.selections
12550 .all::<OffsetUtf16>(cx)
12551 .iter()
12552 .zip(ranges_to_replace.iter())
12553 .find_map(|(selection, range)| {
12554 if selection.id == newest_selection_id {
12555 Some(
12556 (range.start.0 as isize - selection.head().0 as isize)
12557 ..(range.end.0 as isize - selection.head().0 as isize),
12558 )
12559 } else {
12560 None
12561 }
12562 })
12563 });
12564
12565 cx.emit(EditorEvent::InputHandled {
12566 utf16_range_to_replace: range_to_replace,
12567 text: text.into(),
12568 });
12569
12570 if let Some(new_selected_ranges) = new_selected_ranges {
12571 this.change_selections(None, cx, |selections| {
12572 selections.select_ranges(new_selected_ranges)
12573 });
12574 this.backspace(&Default::default(), cx);
12575 }
12576
12577 this.handle_input(text, cx);
12578 });
12579
12580 if let Some(transaction) = self.ime_transaction {
12581 self.buffer.update(cx, |buffer, cx| {
12582 buffer.group_until_transaction(transaction, cx);
12583 });
12584 }
12585
12586 self.unmark_text(cx);
12587 }
12588
12589 fn replace_and_mark_text_in_range(
12590 &mut self,
12591 range_utf16: Option<Range<usize>>,
12592 text: &str,
12593 new_selected_range_utf16: Option<Range<usize>>,
12594 cx: &mut ViewContext<Self>,
12595 ) {
12596 if !self.input_enabled {
12597 return;
12598 }
12599
12600 let transaction = self.transact(cx, |this, cx| {
12601 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
12602 let snapshot = this.buffer.read(cx).read(cx);
12603 if let Some(relative_range_utf16) = range_utf16.as_ref() {
12604 for marked_range in &mut marked_ranges {
12605 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
12606 marked_range.start.0 += relative_range_utf16.start;
12607 marked_range.start =
12608 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
12609 marked_range.end =
12610 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
12611 }
12612 }
12613 Some(marked_ranges)
12614 } else if let Some(range_utf16) = range_utf16 {
12615 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
12616 Some(this.selection_replacement_ranges(range_utf16, cx))
12617 } else {
12618 None
12619 };
12620
12621 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
12622 let newest_selection_id = this.selections.newest_anchor().id;
12623 this.selections
12624 .all::<OffsetUtf16>(cx)
12625 .iter()
12626 .zip(ranges_to_replace.iter())
12627 .find_map(|(selection, range)| {
12628 if selection.id == newest_selection_id {
12629 Some(
12630 (range.start.0 as isize - selection.head().0 as isize)
12631 ..(range.end.0 as isize - selection.head().0 as isize),
12632 )
12633 } else {
12634 None
12635 }
12636 })
12637 });
12638
12639 cx.emit(EditorEvent::InputHandled {
12640 utf16_range_to_replace: range_to_replace,
12641 text: text.into(),
12642 });
12643
12644 if let Some(ranges) = ranges_to_replace {
12645 this.change_selections(None, cx, |s| s.select_ranges(ranges));
12646 }
12647
12648 let marked_ranges = {
12649 let snapshot = this.buffer.read(cx).read(cx);
12650 this.selections
12651 .disjoint_anchors()
12652 .iter()
12653 .map(|selection| {
12654 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
12655 })
12656 .collect::<Vec<_>>()
12657 };
12658
12659 if text.is_empty() {
12660 this.unmark_text(cx);
12661 } else {
12662 this.highlight_text::<InputComposition>(
12663 marked_ranges.clone(),
12664 HighlightStyle {
12665 underline: Some(UnderlineStyle {
12666 thickness: px(1.),
12667 color: None,
12668 wavy: false,
12669 }),
12670 ..Default::default()
12671 },
12672 cx,
12673 );
12674 }
12675
12676 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
12677 let use_autoclose = this.use_autoclose;
12678 let use_auto_surround = this.use_auto_surround;
12679 this.set_use_autoclose(false);
12680 this.set_use_auto_surround(false);
12681 this.handle_input(text, cx);
12682 this.set_use_autoclose(use_autoclose);
12683 this.set_use_auto_surround(use_auto_surround);
12684
12685 if let Some(new_selected_range) = new_selected_range_utf16 {
12686 let snapshot = this.buffer.read(cx).read(cx);
12687 let new_selected_ranges = marked_ranges
12688 .into_iter()
12689 .map(|marked_range| {
12690 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
12691 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
12692 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
12693 snapshot.clip_offset_utf16(new_start, Bias::Left)
12694 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
12695 })
12696 .collect::<Vec<_>>();
12697
12698 drop(snapshot);
12699 this.change_selections(None, cx, |selections| {
12700 selections.select_ranges(new_selected_ranges)
12701 });
12702 }
12703 });
12704
12705 self.ime_transaction = self.ime_transaction.or(transaction);
12706 if let Some(transaction) = self.ime_transaction {
12707 self.buffer.update(cx, |buffer, cx| {
12708 buffer.group_until_transaction(transaction, cx);
12709 });
12710 }
12711
12712 if self.text_highlights::<InputComposition>(cx).is_none() {
12713 self.ime_transaction.take();
12714 }
12715 }
12716
12717 fn bounds_for_range(
12718 &mut self,
12719 range_utf16: Range<usize>,
12720 element_bounds: gpui::Bounds<Pixels>,
12721 cx: &mut ViewContext<Self>,
12722 ) -> Option<gpui::Bounds<Pixels>> {
12723 let text_layout_details = self.text_layout_details(cx);
12724 let style = &text_layout_details.editor_style;
12725 let font_id = cx.text_system().resolve_font(&style.text.font());
12726 let font_size = style.text.font_size.to_pixels(cx.rem_size());
12727 let line_height = style.text.line_height_in_pixels(cx.rem_size());
12728
12729 let em_width = cx
12730 .text_system()
12731 .typographic_bounds(font_id, font_size, 'm')
12732 .unwrap()
12733 .size
12734 .width;
12735
12736 let snapshot = self.snapshot(cx);
12737 let scroll_position = snapshot.scroll_position();
12738 let scroll_left = scroll_position.x * em_width;
12739
12740 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
12741 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
12742 + self.gutter_dimensions.width;
12743 let y = line_height * (start.row().as_f32() - scroll_position.y);
12744
12745 Some(Bounds {
12746 origin: element_bounds.origin + point(x, y),
12747 size: size(em_width, line_height),
12748 })
12749 }
12750}
12751
12752trait SelectionExt {
12753 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
12754 fn spanned_rows(
12755 &self,
12756 include_end_if_at_line_start: bool,
12757 map: &DisplaySnapshot,
12758 ) -> Range<MultiBufferRow>;
12759}
12760
12761impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
12762 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
12763 let start = self
12764 .start
12765 .to_point(&map.buffer_snapshot)
12766 .to_display_point(map);
12767 let end = self
12768 .end
12769 .to_point(&map.buffer_snapshot)
12770 .to_display_point(map);
12771 if self.reversed {
12772 end..start
12773 } else {
12774 start..end
12775 }
12776 }
12777
12778 fn spanned_rows(
12779 &self,
12780 include_end_if_at_line_start: bool,
12781 map: &DisplaySnapshot,
12782 ) -> Range<MultiBufferRow> {
12783 let start = self.start.to_point(&map.buffer_snapshot);
12784 let mut end = self.end.to_point(&map.buffer_snapshot);
12785 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
12786 end.row -= 1;
12787 }
12788
12789 let buffer_start = map.prev_line_boundary(start).0;
12790 let buffer_end = map.next_line_boundary(end).0;
12791 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
12792 }
12793}
12794
12795impl<T: InvalidationRegion> InvalidationStack<T> {
12796 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
12797 where
12798 S: Clone + ToOffset,
12799 {
12800 while let Some(region) = self.last() {
12801 let all_selections_inside_invalidation_ranges =
12802 if selections.len() == region.ranges().len() {
12803 selections
12804 .iter()
12805 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
12806 .all(|(selection, invalidation_range)| {
12807 let head = selection.head().to_offset(buffer);
12808 invalidation_range.start <= head && invalidation_range.end >= head
12809 })
12810 } else {
12811 false
12812 };
12813
12814 if all_selections_inside_invalidation_ranges {
12815 break;
12816 } else {
12817 self.pop();
12818 }
12819 }
12820 }
12821}
12822
12823impl<T> Default for InvalidationStack<T> {
12824 fn default() -> Self {
12825 Self(Default::default())
12826 }
12827}
12828
12829impl<T> Deref for InvalidationStack<T> {
12830 type Target = Vec<T>;
12831
12832 fn deref(&self) -> &Self::Target {
12833 &self.0
12834 }
12835}
12836
12837impl<T> DerefMut for InvalidationStack<T> {
12838 fn deref_mut(&mut self) -> &mut Self::Target {
12839 &mut self.0
12840 }
12841}
12842
12843impl InvalidationRegion for SnippetState {
12844 fn ranges(&self) -> &[Range<Anchor>] {
12845 &self.ranges[self.active_index]
12846 }
12847}
12848
12849pub fn diagnostic_block_renderer(
12850 diagnostic: Diagnostic,
12851 max_message_rows: Option<u8>,
12852 allow_closing: bool,
12853 _is_valid: bool,
12854) -> RenderBlock {
12855 let (text_without_backticks, code_ranges) =
12856 highlight_diagnostic_message(&diagnostic, max_message_rows);
12857
12858 Box::new(move |cx: &mut BlockContext| {
12859 let group_id: SharedString = cx.block_id.to_string().into();
12860
12861 let mut text_style = cx.text_style().clone();
12862 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
12863 let theme_settings = ThemeSettings::get_global(cx);
12864 text_style.font_family = theme_settings.buffer_font.family.clone();
12865 text_style.font_style = theme_settings.buffer_font.style;
12866 text_style.font_features = theme_settings.buffer_font.features.clone();
12867 text_style.font_weight = theme_settings.buffer_font.weight;
12868
12869 let multi_line_diagnostic = diagnostic.message.contains('\n');
12870
12871 let buttons = |diagnostic: &Diagnostic, block_id: BlockId| {
12872 if multi_line_diagnostic {
12873 v_flex()
12874 } else {
12875 h_flex()
12876 }
12877 .when(allow_closing, |div| {
12878 div.children(diagnostic.is_primary.then(|| {
12879 IconButton::new(("close-block", EntityId::from(block_id)), IconName::XCircle)
12880 .icon_color(Color::Muted)
12881 .size(ButtonSize::Compact)
12882 .style(ButtonStyle::Transparent)
12883 .visible_on_hover(group_id.clone())
12884 .on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
12885 .tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
12886 }))
12887 })
12888 .child(
12889 IconButton::new(("copy-block", EntityId::from(block_id)), IconName::Copy)
12890 .icon_color(Color::Muted)
12891 .size(ButtonSize::Compact)
12892 .style(ButtonStyle::Transparent)
12893 .visible_on_hover(group_id.clone())
12894 .on_click({
12895 let message = diagnostic.message.clone();
12896 move |_click, cx| cx.write_to_clipboard(ClipboardItem::new(message.clone()))
12897 })
12898 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
12899 )
12900 };
12901
12902 let icon_size = buttons(&diagnostic, cx.block_id)
12903 .into_any_element()
12904 .layout_as_root(AvailableSpace::min_size(), cx);
12905
12906 h_flex()
12907 .id(cx.block_id)
12908 .group(group_id.clone())
12909 .relative()
12910 .size_full()
12911 .pl(cx.gutter_dimensions.width)
12912 .w(cx.max_width + cx.gutter_dimensions.width)
12913 .child(
12914 div()
12915 .flex()
12916 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
12917 .flex_shrink(),
12918 )
12919 .child(buttons(&diagnostic, cx.block_id))
12920 .child(div().flex().flex_shrink_0().child(
12921 StyledText::new(text_without_backticks.clone()).with_highlights(
12922 &text_style,
12923 code_ranges.iter().map(|range| {
12924 (
12925 range.clone(),
12926 HighlightStyle {
12927 font_weight: Some(FontWeight::BOLD),
12928 ..Default::default()
12929 },
12930 )
12931 }),
12932 ),
12933 ))
12934 .into_any_element()
12935 })
12936}
12937
12938pub fn highlight_diagnostic_message(
12939 diagnostic: &Diagnostic,
12940 mut max_message_rows: Option<u8>,
12941) -> (SharedString, Vec<Range<usize>>) {
12942 let mut text_without_backticks = String::new();
12943 let mut code_ranges = Vec::new();
12944
12945 if let Some(source) = &diagnostic.source {
12946 text_without_backticks.push_str(&source);
12947 code_ranges.push(0..source.len());
12948 text_without_backticks.push_str(": ");
12949 }
12950
12951 let mut prev_offset = 0;
12952 let mut in_code_block = false;
12953 let has_row_limit = max_message_rows.is_some();
12954 let mut newline_indices = diagnostic
12955 .message
12956 .match_indices('\n')
12957 .filter(|_| has_row_limit)
12958 .map(|(ix, _)| ix)
12959 .fuse()
12960 .peekable();
12961
12962 for (quote_ix, _) in diagnostic
12963 .message
12964 .match_indices('`')
12965 .chain([(diagnostic.message.len(), "")])
12966 {
12967 let mut first_newline_ix = None;
12968 let mut last_newline_ix = None;
12969 while let Some(newline_ix) = newline_indices.peek() {
12970 if *newline_ix < quote_ix {
12971 if first_newline_ix.is_none() {
12972 first_newline_ix = Some(*newline_ix);
12973 }
12974 last_newline_ix = Some(*newline_ix);
12975
12976 if let Some(rows_left) = &mut max_message_rows {
12977 if *rows_left == 0 {
12978 break;
12979 } else {
12980 *rows_left -= 1;
12981 }
12982 }
12983 let _ = newline_indices.next();
12984 } else {
12985 break;
12986 }
12987 }
12988 let prev_len = text_without_backticks.len();
12989 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
12990 text_without_backticks.push_str(new_text);
12991 if in_code_block {
12992 code_ranges.push(prev_len..text_without_backticks.len());
12993 }
12994 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
12995 in_code_block = !in_code_block;
12996 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
12997 text_without_backticks.push_str("...");
12998 break;
12999 }
13000 }
13001
13002 (text_without_backticks.into(), code_ranges)
13003}
13004
13005fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
13006 match severity {
13007 DiagnosticSeverity::ERROR => colors.error,
13008 DiagnosticSeverity::WARNING => colors.warning,
13009 DiagnosticSeverity::INFORMATION => colors.info,
13010 DiagnosticSeverity::HINT => colors.info,
13011 _ => colors.ignored,
13012 }
13013}
13014
13015pub fn styled_runs_for_code_label<'a>(
13016 label: &'a CodeLabel,
13017 syntax_theme: &'a theme::SyntaxTheme,
13018) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
13019 let fade_out = HighlightStyle {
13020 fade_out: Some(0.35),
13021 ..Default::default()
13022 };
13023
13024 let mut prev_end = label.filter_range.end;
13025 label
13026 .runs
13027 .iter()
13028 .enumerate()
13029 .flat_map(move |(ix, (range, highlight_id))| {
13030 let style = if let Some(style) = highlight_id.style(syntax_theme) {
13031 style
13032 } else {
13033 return Default::default();
13034 };
13035 let mut muted_style = style;
13036 muted_style.highlight(fade_out);
13037
13038 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
13039 if range.start >= label.filter_range.end {
13040 if range.start > prev_end {
13041 runs.push((prev_end..range.start, fade_out));
13042 }
13043 runs.push((range.clone(), muted_style));
13044 } else if range.end <= label.filter_range.end {
13045 runs.push((range.clone(), style));
13046 } else {
13047 runs.push((range.start..label.filter_range.end, style));
13048 runs.push((label.filter_range.end..range.end, muted_style));
13049 }
13050 prev_end = cmp::max(prev_end, range.end);
13051
13052 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
13053 runs.push((prev_end..label.text.len(), fade_out));
13054 }
13055
13056 runs
13057 })
13058}
13059
13060pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
13061 let mut prev_index = 0;
13062 let mut prev_codepoint: Option<char> = None;
13063 text.char_indices()
13064 .chain([(text.len(), '\0')])
13065 .filter_map(move |(index, codepoint)| {
13066 let prev_codepoint = prev_codepoint.replace(codepoint)?;
13067 let is_boundary = index == text.len()
13068 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
13069 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
13070 if is_boundary {
13071 let chunk = &text[prev_index..index];
13072 prev_index = index;
13073 Some(chunk)
13074 } else {
13075 None
13076 }
13077 })
13078}
13079
13080pub trait RangeToAnchorExt: Sized {
13081 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
13082
13083 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
13084 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
13085 anchor_range.start.to_display_point(&snapshot)..anchor_range.end.to_display_point(&snapshot)
13086 }
13087}
13088
13089impl<T: ToOffset> RangeToAnchorExt for Range<T> {
13090 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
13091 let start_offset = self.start.to_offset(snapshot);
13092 let end_offset = self.end.to_offset(snapshot);
13093 if start_offset == end_offset {
13094 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
13095 } else {
13096 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
13097 }
13098 }
13099}
13100
13101pub trait RowExt {
13102 fn as_f32(&self) -> f32;
13103
13104 fn next_row(&self) -> Self;
13105
13106 fn previous_row(&self) -> Self;
13107
13108 fn minus(&self, other: Self) -> u32;
13109}
13110
13111impl RowExt for DisplayRow {
13112 fn as_f32(&self) -> f32 {
13113 self.0 as f32
13114 }
13115
13116 fn next_row(&self) -> Self {
13117 Self(self.0 + 1)
13118 }
13119
13120 fn previous_row(&self) -> Self {
13121 Self(self.0.saturating_sub(1))
13122 }
13123
13124 fn minus(&self, other: Self) -> u32 {
13125 self.0 - other.0
13126 }
13127}
13128
13129impl RowExt for MultiBufferRow {
13130 fn as_f32(&self) -> f32 {
13131 self.0 as f32
13132 }
13133
13134 fn next_row(&self) -> Self {
13135 Self(self.0 + 1)
13136 }
13137
13138 fn previous_row(&self) -> Self {
13139 Self(self.0.saturating_sub(1))
13140 }
13141
13142 fn minus(&self, other: Self) -> u32 {
13143 self.0 - other.0
13144 }
13145}
13146
13147trait RowRangeExt {
13148 type Row;
13149
13150 fn len(&self) -> usize;
13151
13152 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
13153}
13154
13155impl RowRangeExt for Range<MultiBufferRow> {
13156 type Row = MultiBufferRow;
13157
13158 fn len(&self) -> usize {
13159 (self.end.0 - self.start.0) as usize
13160 }
13161
13162 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
13163 (self.start.0..self.end.0).map(MultiBufferRow)
13164 }
13165}
13166
13167impl RowRangeExt for Range<DisplayRow> {
13168 type Row = DisplayRow;
13169
13170 fn len(&self) -> usize {
13171 (self.end.0 - self.start.0) as usize
13172 }
13173
13174 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
13175 (self.start.0..self.end.0).map(DisplayRow)
13176 }
13177}
13178
13179fn hunk_status(hunk: &DiffHunk<MultiBufferRow>) -> DiffHunkStatus {
13180 if hunk.diff_base_byte_range.is_empty() {
13181 DiffHunkStatus::Added
13182 } else if hunk.associated_range.is_empty() {
13183 DiffHunkStatus::Removed
13184 } else {
13185 DiffHunkStatus::Modified
13186 }
13187}