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 behavior.
15pub mod actions;
16mod blame_entry_tooltip;
17mod blink_manager;
18mod clangd_ext;
19mod debounced_delay;
20pub mod display_map;
21mod editor_settings;
22mod editor_settings_controls;
23mod element;
24mod git;
25mod highlight_matching_bracket;
26mod hover_links;
27mod hover_popover;
28mod hunk_diff;
29mod indent_guides;
30mod inlay_hint_cache;
31mod inline_completion_provider;
32pub mod items;
33mod linked_editing_ranges;
34mod lsp_ext;
35mod mouse_context_menu;
36pub mod movement;
37mod persistence;
38mod rust_analyzer_ext;
39pub mod scroll;
40mod selections_collection;
41pub mod tasks;
42
43#[cfg(test)]
44mod editor_tests;
45mod signature_help;
46#[cfg(any(test, feature = "test-support"))]
47pub mod test;
48
49use ::git::diff::{DiffHunk, DiffHunkStatus};
50use ::git::{parse_git_remote_url, BuildPermalinkParams, GitHostingProviderRegistry};
51pub(crate) use actions::*;
52use aho_corasick::AhoCorasick;
53use anyhow::{anyhow, Context as _, Result};
54use blink_manager::BlinkManager;
55use client::{Collaborator, ParticipantIndex};
56use clock::ReplicaId;
57use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
58use convert_case::{Case, Casing};
59use debounced_delay::DebouncedDelay;
60use display_map::*;
61pub use display_map::{DisplayPoint, FoldPlaceholder};
62pub use editor_settings::{CurrentLineHighlight, EditorSettings, ScrollBeyondLastLine};
63pub use editor_settings_controls::*;
64use element::LineWithInvisibles;
65pub use element::{
66 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
67};
68use futures::FutureExt;
69use fuzzy::{StringMatch, StringMatchCandidate};
70use git::blame::GitBlame;
71use git::diff_hunk_to_display;
72use gpui::{
73 div, impl_actions, point, prelude::*, px, relative, size, uniform_list, Action, AnyElement,
74 AppContext, AsyncWindowContext, AvailableSpace, BackgroundExecutor, Bounds, ClipboardEntry,
75 ClipboardItem, Context, DispatchPhase, ElementId, EntityId, EventEmitter, FocusHandle,
76 FocusOutEvent, FocusableView, FontId, FontWeight, HighlightStyle, Hsla, InteractiveText,
77 KeyContext, ListSizingBehavior, Model, MouseButton, PaintQuad, ParentElement, Pixels, Render,
78 SharedString, Size, StrikethroughStyle, Styled, StyledText, Subscription, Task, TextStyle,
79 UTF16Selection, UnderlineStyle, UniformListScrollHandle, View, ViewContext, ViewInputHandler,
80 VisualContext, WeakFocusHandle, WeakView, WindowContext,
81};
82use highlight_matching_bracket::refresh_matching_bracket_highlights;
83use hover_popover::{hide_hover, HoverState};
84use hunk_diff::ExpandedHunks;
85pub(crate) use hunk_diff::HoveredHunk;
86use indent_guides::ActiveIndentGuidesState;
87use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
88pub use inline_completion_provider::*;
89pub use items::MAX_TAB_TITLE_LEN;
90use itertools::Itertools;
91use language::{
92 char_kind,
93 language_settings::{self, all_language_settings, InlayHintSettings},
94 markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
95 CursorShape, Diagnostic, Documentation, IndentKind, IndentSize, Language, OffsetRangeExt,
96 Point, Selection, SelectionGoal, TransactionId,
97};
98use language::{point_to_lsp, BufferRow, Runnable, RunnableRange};
99use linked_editing_ranges::refresh_linked_ranges;
100use task::{ResolvedTask, TaskTemplate, TaskVariables};
101
102use hover_links::{find_file, HoverLink, HoveredLinkState, InlayHighlight};
103pub use lsp::CompletionContext;
104use lsp::{
105 CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity, InsertTextFormat,
106 LanguageServerId,
107};
108use mouse_context_menu::MouseContextMenu;
109use movement::TextLayoutDetails;
110pub use multi_buffer::{
111 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
112 ToPoint,
113};
114use multi_buffer::{ExpandExcerptDirection, MultiBufferPoint, MultiBufferRow, ToOffsetUtf16};
115use ordered_float::OrderedFloat;
116use parking_lot::{Mutex, RwLock};
117use project::project_settings::{GitGutterSetting, ProjectSettings};
118use project::{
119 CodeAction, Completion, CompletionIntent, FormatTrigger, Item, Location, Project, ProjectPath,
120 ProjectTransaction, TaskSourceKind, WorktreeId,
121};
122use rand::prelude::*;
123use rpc::{proto::*, ErrorExt};
124use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
125use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
126use serde::{Deserialize, Serialize};
127use settings::{update_settings_file, Settings, SettingsStore};
128use smallvec::SmallVec;
129use snippet::Snippet;
130use std::{
131 any::TypeId,
132 borrow::Cow,
133 cell::RefCell,
134 cmp::{self, Ordering, Reverse},
135 mem,
136 num::NonZeroU32,
137 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
138 path::{Path, PathBuf},
139 rc::Rc,
140 sync::Arc,
141 time::{Duration, Instant},
142};
143pub use sum_tree::Bias;
144use sum_tree::TreeMap;
145use text::{BufferId, OffsetUtf16, Rope};
146use theme::{
147 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
148 ThemeColors, ThemeSettings,
149};
150use ui::{
151 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize,
152 ListItem, Popover, Tooltip,
153};
154use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
155use workspace::item::{ItemHandle, PreviewTabsSettings};
156use workspace::notifications::{DetachAndPromptErr, NotificationId};
157use workspace::{
158 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
159};
160use workspace::{OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
161
162use crate::hover_links::find_url;
163use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
164
165pub const FILE_HEADER_HEIGHT: u32 = 1;
166pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
167pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u32 = 1;
168pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
169const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
170const MAX_LINE_LEN: usize = 1024;
171const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
172const MAX_SELECTION_HISTORY_LEN: usize = 1024;
173pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
174#[doc(hidden)]
175pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
176#[doc(hidden)]
177pub const DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
178
179pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
180pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
181
182pub fn render_parsed_markdown(
183 element_id: impl Into<ElementId>,
184 parsed: &language::ParsedMarkdown,
185 editor_style: &EditorStyle,
186 workspace: Option<WeakView<Workspace>>,
187 cx: &mut WindowContext,
188) -> InteractiveText {
189 let code_span_background_color = cx
190 .theme()
191 .colors()
192 .editor_document_highlight_read_background;
193
194 let highlights = gpui::combine_highlights(
195 parsed.highlights.iter().filter_map(|(range, highlight)| {
196 let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
197 Some((range.clone(), highlight))
198 }),
199 parsed
200 .regions
201 .iter()
202 .zip(&parsed.region_ranges)
203 .filter_map(|(region, range)| {
204 if region.code {
205 Some((
206 range.clone(),
207 HighlightStyle {
208 background_color: Some(code_span_background_color),
209 ..Default::default()
210 },
211 ))
212 } else {
213 None
214 }
215 }),
216 );
217
218 let mut links = Vec::new();
219 let mut link_ranges = Vec::new();
220 for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
221 if let Some(link) = region.link.clone() {
222 links.push(link);
223 link_ranges.push(range.clone());
224 }
225 }
226
227 InteractiveText::new(
228 element_id,
229 StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
230 )
231 .on_click(link_ranges, move |clicked_range_ix, cx| {
232 match &links[clicked_range_ix] {
233 markdown::Link::Web { url } => cx.open_url(url),
234 markdown::Link::Path { path } => {
235 if let Some(workspace) = &workspace {
236 _ = workspace.update(cx, |workspace, cx| {
237 workspace.open_abs_path(path.clone(), false, cx).detach();
238 });
239 }
240 }
241 }
242 })
243}
244
245#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
246pub(crate) enum InlayId {
247 Suggestion(usize),
248 Hint(usize),
249}
250
251impl InlayId {
252 fn id(&self) -> usize {
253 match self {
254 Self::Suggestion(id) => *id,
255 Self::Hint(id) => *id,
256 }
257 }
258}
259
260enum DiffRowHighlight {}
261enum DocumentHighlightRead {}
262enum DocumentHighlightWrite {}
263enum InputComposition {}
264
265#[derive(Copy, Clone, PartialEq, Eq)]
266pub enum Direction {
267 Prev,
268 Next,
269}
270
271#[derive(Debug, Copy, Clone, PartialEq, Eq)]
272pub enum Navigated {
273 Yes,
274 No,
275}
276
277impl Navigated {
278 pub fn from_bool(yes: bool) -> Navigated {
279 if yes {
280 Navigated::Yes
281 } else {
282 Navigated::No
283 }
284 }
285}
286
287pub fn init_settings(cx: &mut AppContext) {
288 EditorSettings::register(cx);
289}
290
291pub fn init(cx: &mut AppContext) {
292 init_settings(cx);
293
294 workspace::register_project_item::<Editor>(cx);
295 workspace::FollowableViewRegistry::register::<Editor>(cx);
296 workspace::register_serializable_item::<Editor>(cx);
297
298 cx.observe_new_views(
299 |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
300 workspace.register_action(Editor::new_file);
301 workspace.register_action(Editor::new_file_vertical);
302 workspace.register_action(Editor::new_file_horizontal);
303 },
304 )
305 .detach();
306
307 cx.on_action(move |_: &workspace::NewFile, cx| {
308 let app_state = workspace::AppState::global(cx);
309 if let Some(app_state) = app_state.upgrade() {
310 workspace::open_new(Default::default(), app_state, cx, |workspace, cx| {
311 Editor::new_file(workspace, &Default::default(), cx)
312 })
313 .detach();
314 }
315 });
316 cx.on_action(move |_: &workspace::NewWindow, cx| {
317 let app_state = workspace::AppState::global(cx);
318 if let Some(app_state) = app_state.upgrade() {
319 workspace::open_new(Default::default(), app_state, cx, |workspace, cx| {
320 Editor::new_file(workspace, &Default::default(), cx)
321 })
322 .detach();
323 }
324 });
325}
326
327pub struct SearchWithinRange;
328
329trait InvalidationRegion {
330 fn ranges(&self) -> &[Range<Anchor>];
331}
332
333#[derive(Clone, Debug, PartialEq)]
334pub enum SelectPhase {
335 Begin {
336 position: DisplayPoint,
337 add: bool,
338 click_count: usize,
339 },
340 BeginColumnar {
341 position: DisplayPoint,
342 reset: bool,
343 goal_column: u32,
344 },
345 Extend {
346 position: DisplayPoint,
347 click_count: usize,
348 },
349 Update {
350 position: DisplayPoint,
351 goal_column: u32,
352 scroll_delta: gpui::Point<f32>,
353 },
354 End,
355}
356
357#[derive(Clone, Debug)]
358pub enum SelectMode {
359 Character,
360 Word(Range<Anchor>),
361 Line(Range<Anchor>),
362 All,
363}
364
365#[derive(Copy, Clone, PartialEq, Eq, Debug)]
366pub enum EditorMode {
367 SingleLine { auto_width: bool },
368 AutoHeight { max_lines: usize },
369 Full,
370}
371
372#[derive(Clone, Debug)]
373pub enum SoftWrap {
374 None,
375 PreferLine,
376 EditorWidth,
377 Column(u32),
378 Bounded(u32),
379}
380
381#[derive(Clone)]
382pub struct EditorStyle {
383 pub background: Hsla,
384 pub local_player: PlayerColor,
385 pub text: TextStyle,
386 pub scrollbar_width: Pixels,
387 pub syntax: Arc<SyntaxTheme>,
388 pub status: StatusColors,
389 pub inlay_hints_style: HighlightStyle,
390 pub suggestions_style: HighlightStyle,
391 pub unnecessary_code_fade: f32,
392}
393
394impl Default for EditorStyle {
395 fn default() -> Self {
396 Self {
397 background: Hsla::default(),
398 local_player: PlayerColor::default(),
399 text: TextStyle::default(),
400 scrollbar_width: Pixels::default(),
401 syntax: Default::default(),
402 // HACK: Status colors don't have a real default.
403 // We should look into removing the status colors from the editor
404 // style and retrieve them directly from the theme.
405 status: StatusColors::dark(),
406 inlay_hints_style: HighlightStyle::default(),
407 suggestions_style: HighlightStyle::default(),
408 unnecessary_code_fade: Default::default(),
409 }
410 }
411}
412
413type CompletionId = usize;
414
415#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
416struct EditorActionId(usize);
417
418impl EditorActionId {
419 pub fn post_inc(&mut self) -> Self {
420 let answer = self.0;
421
422 *self = Self(answer + 1);
423
424 Self(answer)
425 }
426}
427
428// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
429// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
430
431type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
432type GutterHighlight = (fn(&AppContext) -> Hsla, Arc<[Range<Anchor>]>);
433
434#[derive(Default)]
435struct ScrollbarMarkerState {
436 scrollbar_size: Size<Pixels>,
437 dirty: bool,
438 markers: Arc<[PaintQuad]>,
439 pending_refresh: Option<Task<Result<()>>>,
440}
441
442impl ScrollbarMarkerState {
443 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
444 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
445 }
446}
447
448#[derive(Clone, Debug)]
449struct RunnableTasks {
450 templates: Vec<(TaskSourceKind, TaskTemplate)>,
451 offset: MultiBufferOffset,
452 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
453 column: u32,
454 // Values of all named captures, including those starting with '_'
455 extra_variables: HashMap<String, String>,
456 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
457 context_range: Range<BufferOffset>,
458}
459
460#[derive(Clone)]
461struct ResolvedTasks {
462 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
463 position: Anchor,
464}
465#[derive(Copy, Clone, Debug)]
466struct MultiBufferOffset(usize);
467#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
468struct BufferOffset(usize);
469
470// Addons allow storing per-editor state in other crates (e.g. Vim)
471pub trait Addon: 'static {
472 fn extend_key_context(&self, _: &mut KeyContext, _: &AppContext) {}
473
474 fn to_any(&self) -> &dyn std::any::Any;
475}
476
477/// Zed's primary text input `View`, allowing users to edit a [`MultiBuffer`]
478///
479/// See the [module level documentation](self) for more information.
480pub struct Editor {
481 focus_handle: FocusHandle,
482 last_focused_descendant: Option<WeakFocusHandle>,
483 /// The text buffer being edited
484 buffer: Model<MultiBuffer>,
485 /// Map of how text in the buffer should be displayed.
486 /// Handles soft wraps, folds, fake inlay text insertions, etc.
487 pub display_map: Model<DisplayMap>,
488 pub selections: SelectionsCollection,
489 pub scroll_manager: ScrollManager,
490 /// When inline assist editors are linked, they all render cursors because
491 /// typing enters text into each of them, even the ones that aren't focused.
492 pub(crate) show_cursor_when_unfocused: bool,
493 columnar_selection_tail: Option<Anchor>,
494 add_selections_state: Option<AddSelectionsState>,
495 select_next_state: Option<SelectNextState>,
496 select_prev_state: Option<SelectNextState>,
497 selection_history: SelectionHistory,
498 autoclose_regions: Vec<AutocloseRegion>,
499 snippet_stack: InvalidationStack<SnippetState>,
500 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
501 ime_transaction: Option<TransactionId>,
502 active_diagnostics: Option<ActiveDiagnosticGroup>,
503 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
504 project: Option<Model<Project>>,
505 completion_provider: Option<Box<dyn CompletionProvider>>,
506 collaboration_hub: Option<Box<dyn CollaborationHub>>,
507 blink_manager: Model<BlinkManager>,
508 show_cursor_names: bool,
509 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
510 pub show_local_selections: bool,
511 mode: EditorMode,
512 show_breadcrumbs: bool,
513 show_gutter: bool,
514 show_line_numbers: Option<bool>,
515 use_relative_line_numbers: Option<bool>,
516 show_git_diff_gutter: Option<bool>,
517 show_code_actions: Option<bool>,
518 show_runnables: Option<bool>,
519 show_wrap_guides: Option<bool>,
520 show_indent_guides: Option<bool>,
521 placeholder_text: Option<Arc<str>>,
522 highlight_order: usize,
523 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
524 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
525 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
526 scrollbar_marker_state: ScrollbarMarkerState,
527 active_indent_guides_state: ActiveIndentGuidesState,
528 nav_history: Option<ItemNavHistory>,
529 context_menu: RwLock<Option<ContextMenu>>,
530 mouse_context_menu: Option<MouseContextMenu>,
531 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
532 signature_help_state: SignatureHelpState,
533 auto_signature_help: Option<bool>,
534 find_all_references_task_sources: Vec<Anchor>,
535 next_completion_id: CompletionId,
536 completion_documentation_pre_resolve_debounce: DebouncedDelay,
537 available_code_actions: Option<(Location, Arc<[CodeAction]>)>,
538 code_actions_task: Option<Task<()>>,
539 document_highlights_task: Option<Task<()>>,
540 linked_editing_range_task: Option<Task<Option<()>>>,
541 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
542 pending_rename: Option<RenameState>,
543 searchable: bool,
544 cursor_shape: CursorShape,
545 current_line_highlight: Option<CurrentLineHighlight>,
546 collapse_matches: bool,
547 autoindent_mode: Option<AutoindentMode>,
548 workspace: Option<(WeakView<Workspace>, Option<WorkspaceId>)>,
549 input_enabled: bool,
550 use_modal_editing: bool,
551 read_only: bool,
552 leader_peer_id: Option<PeerId>,
553 remote_id: Option<ViewId>,
554 hover_state: HoverState,
555 gutter_hovered: bool,
556 hovered_link_state: Option<HoveredLinkState>,
557 inline_completion_provider: Option<RegisteredInlineCompletionProvider>,
558 active_inline_completion: Option<(Inlay, Option<Range<Anchor>>)>,
559 // enable_inline_completions is a switch that Vim can use to disable
560 // inline completions based on its mode.
561 enable_inline_completions: bool,
562 show_inline_completions_override: Option<bool>,
563 inlay_hint_cache: InlayHintCache,
564 expanded_hunks: ExpandedHunks,
565 next_inlay_id: usize,
566 _subscriptions: Vec<Subscription>,
567 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
568 gutter_dimensions: GutterDimensions,
569 style: Option<EditorStyle>,
570 next_editor_action_id: EditorActionId,
571 editor_actions: Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut ViewContext<Self>)>>>>,
572 use_autoclose: bool,
573 use_auto_surround: bool,
574 auto_replace_emoji_shortcode: bool,
575 show_git_blame_gutter: bool,
576 show_git_blame_inline: bool,
577 show_git_blame_inline_delay_task: Option<Task<()>>,
578 git_blame_inline_enabled: bool,
579 serialize_dirty_buffers: bool,
580 show_selection_menu: Option<bool>,
581 blame: Option<Model<GitBlame>>,
582 blame_subscription: Option<Subscription>,
583 custom_context_menu: Option<
584 Box<
585 dyn 'static
586 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
587 >,
588 >,
589 last_bounds: Option<Bounds<Pixels>>,
590 expect_bounds_change: Option<Bounds<Pixels>>,
591 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
592 tasks_update_task: Option<Task<()>>,
593 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
594 file_header_size: u32,
595 breadcrumb_header: Option<String>,
596 focused_block: Option<FocusedBlock>,
597 next_scroll_position: NextScrollCursorCenterTopBottom,
598 addons: HashMap<TypeId, Box<dyn Addon>>,
599 _scroll_cursor_center_top_bottom_task: Task<()>,
600}
601
602#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
603enum NextScrollCursorCenterTopBottom {
604 #[default]
605 Center,
606 Top,
607 Bottom,
608}
609
610impl NextScrollCursorCenterTopBottom {
611 fn next(&self) -> Self {
612 match self {
613 Self::Center => Self::Top,
614 Self::Top => Self::Bottom,
615 Self::Bottom => Self::Center,
616 }
617 }
618}
619
620#[derive(Clone)]
621pub struct EditorSnapshot {
622 pub mode: EditorMode,
623 show_gutter: bool,
624 show_line_numbers: Option<bool>,
625 show_git_diff_gutter: Option<bool>,
626 show_code_actions: Option<bool>,
627 show_runnables: Option<bool>,
628 render_git_blame_gutter: bool,
629 pub display_snapshot: DisplaySnapshot,
630 pub placeholder_text: Option<Arc<str>>,
631 is_focused: bool,
632 scroll_anchor: ScrollAnchor,
633 ongoing_scroll: OngoingScroll,
634 current_line_highlight: CurrentLineHighlight,
635 gutter_hovered: bool,
636}
637
638const GIT_BLAME_GUTTER_WIDTH_CHARS: f32 = 53.;
639
640#[derive(Default, Debug, Clone, Copy)]
641pub struct GutterDimensions {
642 pub left_padding: Pixels,
643 pub right_padding: Pixels,
644 pub width: Pixels,
645 pub margin: Pixels,
646 pub git_blame_entries_width: Option<Pixels>,
647}
648
649impl GutterDimensions {
650 /// The full width of the space taken up by the gutter.
651 pub fn full_width(&self) -> Pixels {
652 self.margin + self.width
653 }
654
655 /// The width of the space reserved for the fold indicators,
656 /// use alongside 'justify_end' and `gutter_width` to
657 /// right align content with the line numbers
658 pub fn fold_area_width(&self) -> Pixels {
659 self.margin + self.right_padding
660 }
661}
662
663#[derive(Debug)]
664pub struct RemoteSelection {
665 pub replica_id: ReplicaId,
666 pub selection: Selection<Anchor>,
667 pub cursor_shape: CursorShape,
668 pub peer_id: PeerId,
669 pub line_mode: bool,
670 pub participant_index: Option<ParticipantIndex>,
671 pub user_name: Option<SharedString>,
672}
673
674#[derive(Clone, Debug)]
675struct SelectionHistoryEntry {
676 selections: Arc<[Selection<Anchor>]>,
677 select_next_state: Option<SelectNextState>,
678 select_prev_state: Option<SelectNextState>,
679 add_selections_state: Option<AddSelectionsState>,
680}
681
682enum SelectionHistoryMode {
683 Normal,
684 Undoing,
685 Redoing,
686}
687
688#[derive(Clone, PartialEq, Eq, Hash)]
689struct HoveredCursor {
690 replica_id: u16,
691 selection_id: usize,
692}
693
694impl Default for SelectionHistoryMode {
695 fn default() -> Self {
696 Self::Normal
697 }
698}
699
700#[derive(Default)]
701struct SelectionHistory {
702 #[allow(clippy::type_complexity)]
703 selections_by_transaction:
704 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
705 mode: SelectionHistoryMode,
706 undo_stack: VecDeque<SelectionHistoryEntry>,
707 redo_stack: VecDeque<SelectionHistoryEntry>,
708}
709
710impl SelectionHistory {
711 fn insert_transaction(
712 &mut self,
713 transaction_id: TransactionId,
714 selections: Arc<[Selection<Anchor>]>,
715 ) {
716 self.selections_by_transaction
717 .insert(transaction_id, (selections, None));
718 }
719
720 #[allow(clippy::type_complexity)]
721 fn transaction(
722 &self,
723 transaction_id: TransactionId,
724 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
725 self.selections_by_transaction.get(&transaction_id)
726 }
727
728 #[allow(clippy::type_complexity)]
729 fn transaction_mut(
730 &mut self,
731 transaction_id: TransactionId,
732 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
733 self.selections_by_transaction.get_mut(&transaction_id)
734 }
735
736 fn push(&mut self, entry: SelectionHistoryEntry) {
737 if !entry.selections.is_empty() {
738 match self.mode {
739 SelectionHistoryMode::Normal => {
740 self.push_undo(entry);
741 self.redo_stack.clear();
742 }
743 SelectionHistoryMode::Undoing => self.push_redo(entry),
744 SelectionHistoryMode::Redoing => self.push_undo(entry),
745 }
746 }
747 }
748
749 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
750 if self
751 .undo_stack
752 .back()
753 .map_or(true, |e| e.selections != entry.selections)
754 {
755 self.undo_stack.push_back(entry);
756 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
757 self.undo_stack.pop_front();
758 }
759 }
760 }
761
762 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
763 if self
764 .redo_stack
765 .back()
766 .map_or(true, |e| e.selections != entry.selections)
767 {
768 self.redo_stack.push_back(entry);
769 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
770 self.redo_stack.pop_front();
771 }
772 }
773 }
774}
775
776struct RowHighlight {
777 index: usize,
778 range: RangeInclusive<Anchor>,
779 color: Option<Hsla>,
780 should_autoscroll: bool,
781}
782
783#[derive(Clone, Debug)]
784struct AddSelectionsState {
785 above: bool,
786 stack: Vec<usize>,
787}
788
789#[derive(Clone)]
790struct SelectNextState {
791 query: AhoCorasick,
792 wordwise: bool,
793 done: bool,
794}
795
796impl std::fmt::Debug for SelectNextState {
797 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
798 f.debug_struct(std::any::type_name::<Self>())
799 .field("wordwise", &self.wordwise)
800 .field("done", &self.done)
801 .finish()
802 }
803}
804
805#[derive(Debug)]
806struct AutocloseRegion {
807 selection_id: usize,
808 range: Range<Anchor>,
809 pair: BracketPair,
810}
811
812#[derive(Debug)]
813struct SnippetState {
814 ranges: Vec<Vec<Range<Anchor>>>,
815 active_index: usize,
816}
817
818#[doc(hidden)]
819pub struct RenameState {
820 pub range: Range<Anchor>,
821 pub old_name: Arc<str>,
822 pub editor: View<Editor>,
823 block_id: CustomBlockId,
824}
825
826struct InvalidationStack<T>(Vec<T>);
827
828struct RegisteredInlineCompletionProvider {
829 provider: Arc<dyn InlineCompletionProviderHandle>,
830 _subscription: Subscription,
831}
832
833enum ContextMenu {
834 Completions(CompletionsMenu),
835 CodeActions(CodeActionsMenu),
836}
837
838impl ContextMenu {
839 fn select_first(
840 &mut self,
841 project: Option<&Model<Project>>,
842 cx: &mut ViewContext<Editor>,
843 ) -> bool {
844 if self.visible() {
845 match self {
846 ContextMenu::Completions(menu) => menu.select_first(project, cx),
847 ContextMenu::CodeActions(menu) => menu.select_first(cx),
848 }
849 true
850 } else {
851 false
852 }
853 }
854
855 fn select_prev(
856 &mut self,
857 project: Option<&Model<Project>>,
858 cx: &mut ViewContext<Editor>,
859 ) -> bool {
860 if self.visible() {
861 match self {
862 ContextMenu::Completions(menu) => menu.select_prev(project, cx),
863 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
864 }
865 true
866 } else {
867 false
868 }
869 }
870
871 fn select_next(
872 &mut self,
873 project: Option<&Model<Project>>,
874 cx: &mut ViewContext<Editor>,
875 ) -> bool {
876 if self.visible() {
877 match self {
878 ContextMenu::Completions(menu) => menu.select_next(project, cx),
879 ContextMenu::CodeActions(menu) => menu.select_next(cx),
880 }
881 true
882 } else {
883 false
884 }
885 }
886
887 fn select_last(
888 &mut self,
889 project: Option<&Model<Project>>,
890 cx: &mut ViewContext<Editor>,
891 ) -> bool {
892 if self.visible() {
893 match self {
894 ContextMenu::Completions(menu) => menu.select_last(project, cx),
895 ContextMenu::CodeActions(menu) => menu.select_last(cx),
896 }
897 true
898 } else {
899 false
900 }
901 }
902
903 fn visible(&self) -> bool {
904 match self {
905 ContextMenu::Completions(menu) => menu.visible(),
906 ContextMenu::CodeActions(menu) => menu.visible(),
907 }
908 }
909
910 fn render(
911 &self,
912 cursor_position: DisplayPoint,
913 style: &EditorStyle,
914 max_height: Pixels,
915 workspace: Option<WeakView<Workspace>>,
916 cx: &mut ViewContext<Editor>,
917 ) -> (ContextMenuOrigin, AnyElement) {
918 match self {
919 ContextMenu::Completions(menu) => (
920 ContextMenuOrigin::EditorPoint(cursor_position),
921 menu.render(style, max_height, workspace, cx),
922 ),
923 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, max_height, cx),
924 }
925 }
926}
927
928enum ContextMenuOrigin {
929 EditorPoint(DisplayPoint),
930 GutterIndicator(DisplayRow),
931}
932
933#[derive(Clone)]
934struct CompletionsMenu {
935 id: CompletionId,
936 sort_completions: bool,
937 initial_position: Anchor,
938 buffer: Model<Buffer>,
939 completions: Arc<RwLock<Box<[Completion]>>>,
940 match_candidates: Arc<[StringMatchCandidate]>,
941 matches: Arc<[StringMatch]>,
942 selected_item: usize,
943 scroll_handle: UniformListScrollHandle,
944 selected_completion_documentation_resolve_debounce: Arc<Mutex<DebouncedDelay>>,
945}
946
947impl CompletionsMenu {
948 fn select_first(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
949 self.selected_item = 0;
950 self.scroll_handle.scroll_to_item(self.selected_item);
951 self.attempt_resolve_selected_completion_documentation(project, cx);
952 cx.notify();
953 }
954
955 fn select_prev(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
956 if self.selected_item > 0 {
957 self.selected_item -= 1;
958 } else {
959 self.selected_item = self.matches.len() - 1;
960 }
961 self.scroll_handle.scroll_to_item(self.selected_item);
962 self.attempt_resolve_selected_completion_documentation(project, cx);
963 cx.notify();
964 }
965
966 fn select_next(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
967 if self.selected_item + 1 < self.matches.len() {
968 self.selected_item += 1;
969 } else {
970 self.selected_item = 0;
971 }
972 self.scroll_handle.scroll_to_item(self.selected_item);
973 self.attempt_resolve_selected_completion_documentation(project, cx);
974 cx.notify();
975 }
976
977 fn select_last(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
978 self.selected_item = self.matches.len() - 1;
979 self.scroll_handle.scroll_to_item(self.selected_item);
980 self.attempt_resolve_selected_completion_documentation(project, cx);
981 cx.notify();
982 }
983
984 fn pre_resolve_completion_documentation(
985 buffer: Model<Buffer>,
986 completions: Arc<RwLock<Box<[Completion]>>>,
987 matches: Arc<[StringMatch]>,
988 editor: &Editor,
989 cx: &mut ViewContext<Editor>,
990 ) -> Task<()> {
991 let settings = EditorSettings::get_global(cx);
992 if !settings.show_completion_documentation {
993 return Task::ready(());
994 }
995
996 let Some(provider) = editor.completion_provider.as_ref() else {
997 return Task::ready(());
998 };
999
1000 let resolve_task = provider.resolve_completions(
1001 buffer,
1002 matches.iter().map(|m| m.candidate_id).collect(),
1003 completions.clone(),
1004 cx,
1005 );
1006
1007 return cx.spawn(move |this, mut cx| async move {
1008 if let Some(true) = resolve_task.await.log_err() {
1009 this.update(&mut cx, |_, cx| cx.notify()).ok();
1010 }
1011 });
1012 }
1013
1014 fn attempt_resolve_selected_completion_documentation(
1015 &mut self,
1016 project: Option<&Model<Project>>,
1017 cx: &mut ViewContext<Editor>,
1018 ) {
1019 let settings = EditorSettings::get_global(cx);
1020 if !settings.show_completion_documentation {
1021 return;
1022 }
1023
1024 let completion_index = self.matches[self.selected_item].candidate_id;
1025 let Some(project) = project else {
1026 return;
1027 };
1028
1029 let resolve_task = project.update(cx, |project, cx| {
1030 project.resolve_completions(
1031 self.buffer.clone(),
1032 vec![completion_index],
1033 self.completions.clone(),
1034 cx,
1035 )
1036 });
1037
1038 let delay_ms =
1039 EditorSettings::get_global(cx).completion_documentation_secondary_query_debounce;
1040 let delay = Duration::from_millis(delay_ms);
1041
1042 self.selected_completion_documentation_resolve_debounce
1043 .lock()
1044 .fire_new(delay, cx, |_, cx| {
1045 cx.spawn(move |this, mut cx| async move {
1046 if let Some(true) = resolve_task.await.log_err() {
1047 this.update(&mut cx, |_, cx| cx.notify()).ok();
1048 }
1049 })
1050 });
1051 }
1052
1053 fn visible(&self) -> bool {
1054 !self.matches.is_empty()
1055 }
1056
1057 fn render(
1058 &self,
1059 style: &EditorStyle,
1060 max_height: Pixels,
1061 workspace: Option<WeakView<Workspace>>,
1062 cx: &mut ViewContext<Editor>,
1063 ) -> AnyElement {
1064 let settings = EditorSettings::get_global(cx);
1065 let show_completion_documentation = settings.show_completion_documentation;
1066
1067 let widest_completion_ix = self
1068 .matches
1069 .iter()
1070 .enumerate()
1071 .max_by_key(|(_, mat)| {
1072 let completions = self.completions.read();
1073 let completion = &completions[mat.candidate_id];
1074 let documentation = &completion.documentation;
1075
1076 let mut len = completion.label.text.chars().count();
1077 if let Some(Documentation::SingleLine(text)) = documentation {
1078 if show_completion_documentation {
1079 len += text.chars().count();
1080 }
1081 }
1082
1083 len
1084 })
1085 .map(|(ix, _)| ix);
1086
1087 let completions = self.completions.clone();
1088 let matches = self.matches.clone();
1089 let selected_item = self.selected_item;
1090 let style = style.clone();
1091
1092 let multiline_docs = if show_completion_documentation {
1093 let mat = &self.matches[selected_item];
1094 let multiline_docs = match &self.completions.read()[mat.candidate_id].documentation {
1095 Some(Documentation::MultiLinePlainText(text)) => {
1096 Some(div().child(SharedString::from(text.clone())))
1097 }
1098 Some(Documentation::MultiLineMarkdown(parsed)) if !parsed.text.is_empty() => {
1099 Some(div().child(render_parsed_markdown(
1100 "completions_markdown",
1101 parsed,
1102 &style,
1103 workspace,
1104 cx,
1105 )))
1106 }
1107 _ => None,
1108 };
1109 multiline_docs.map(|div| {
1110 div.id("multiline_docs")
1111 .max_h(max_height)
1112 .flex_1()
1113 .px_1p5()
1114 .py_1()
1115 .min_w(px(260.))
1116 .max_w(px(640.))
1117 .w(px(500.))
1118 .overflow_y_scroll()
1119 .occlude()
1120 })
1121 } else {
1122 None
1123 };
1124
1125 let list = uniform_list(
1126 cx.view().clone(),
1127 "completions",
1128 matches.len(),
1129 move |_editor, range, cx| {
1130 let start_ix = range.start;
1131 let completions_guard = completions.read();
1132
1133 matches[range]
1134 .iter()
1135 .enumerate()
1136 .map(|(ix, mat)| {
1137 let item_ix = start_ix + ix;
1138 let candidate_id = mat.candidate_id;
1139 let completion = &completions_guard[candidate_id];
1140
1141 let documentation = if show_completion_documentation {
1142 &completion.documentation
1143 } else {
1144 &None
1145 };
1146
1147 let highlights = gpui::combine_highlights(
1148 mat.ranges().map(|range| (range, FontWeight::BOLD.into())),
1149 styled_runs_for_code_label(&completion.label, &style.syntax).map(
1150 |(range, mut highlight)| {
1151 // Ignore font weight for syntax highlighting, as we'll use it
1152 // for fuzzy matches.
1153 highlight.font_weight = None;
1154
1155 if completion.lsp_completion.deprecated.unwrap_or(false) {
1156 highlight.strikethrough = Some(StrikethroughStyle {
1157 thickness: 1.0.into(),
1158 ..Default::default()
1159 });
1160 highlight.color = Some(cx.theme().colors().text_muted);
1161 }
1162
1163 (range, highlight)
1164 },
1165 ),
1166 );
1167 let completion_label = StyledText::new(completion.label.text.clone())
1168 .with_highlights(&style.text, highlights);
1169 let documentation_label =
1170 if let Some(Documentation::SingleLine(text)) = documentation {
1171 if text.trim().is_empty() {
1172 None
1173 } else {
1174 Some(
1175 Label::new(text.clone())
1176 .ml_4()
1177 .size(LabelSize::Small)
1178 .color(Color::Muted),
1179 )
1180 }
1181 } else {
1182 None
1183 };
1184
1185 div().min_w(px(220.)).max_w(px(540.)).child(
1186 ListItem::new(mat.candidate_id)
1187 .inset(true)
1188 .selected(item_ix == selected_item)
1189 .on_click(cx.listener(move |editor, _event, cx| {
1190 cx.stop_propagation();
1191 if let Some(task) = editor.confirm_completion(
1192 &ConfirmCompletion {
1193 item_ix: Some(item_ix),
1194 },
1195 cx,
1196 ) {
1197 task.detach_and_log_err(cx)
1198 }
1199 }))
1200 .child(h_flex().overflow_hidden().child(completion_label))
1201 .end_slot::<Label>(documentation_label),
1202 )
1203 })
1204 .collect()
1205 },
1206 )
1207 .occlude()
1208 .max_h(max_height)
1209 .track_scroll(self.scroll_handle.clone())
1210 .with_width_from_item(widest_completion_ix)
1211 .with_sizing_behavior(ListSizingBehavior::Infer);
1212
1213 Popover::new()
1214 .child(list)
1215 .when_some(multiline_docs, |popover, multiline_docs| {
1216 popover.aside(multiline_docs)
1217 })
1218 .into_any_element()
1219 }
1220
1221 pub async fn filter(&mut self, query: Option<&str>, executor: BackgroundExecutor) {
1222 let mut matches = if let Some(query) = query {
1223 fuzzy::match_strings(
1224 &self.match_candidates,
1225 query,
1226 query.chars().any(|c| c.is_uppercase()),
1227 100,
1228 &Default::default(),
1229 executor,
1230 )
1231 .await
1232 } else {
1233 self.match_candidates
1234 .iter()
1235 .enumerate()
1236 .map(|(candidate_id, candidate)| StringMatch {
1237 candidate_id,
1238 score: Default::default(),
1239 positions: Default::default(),
1240 string: candidate.string.clone(),
1241 })
1242 .collect()
1243 };
1244
1245 // Remove all candidates where the query's start does not match the start of any word in the candidate
1246 if let Some(query) = query {
1247 if let Some(query_start) = query.chars().next() {
1248 matches.retain(|string_match| {
1249 split_words(&string_match.string).any(|word| {
1250 // Check that the first codepoint of the word as lowercase matches the first
1251 // codepoint of the query as lowercase
1252 word.chars()
1253 .flat_map(|codepoint| codepoint.to_lowercase())
1254 .zip(query_start.to_lowercase())
1255 .all(|(word_cp, query_cp)| word_cp == query_cp)
1256 })
1257 });
1258 }
1259 }
1260
1261 let completions = self.completions.read();
1262 if self.sort_completions {
1263 matches.sort_unstable_by_key(|mat| {
1264 // We do want to strike a balance here between what the language server tells us
1265 // to sort by (the sort_text) and what are "obvious" good matches (i.e. when you type
1266 // `Creat` and there is a local variable called `CreateComponent`).
1267 // So what we do is: we bucket all matches into two buckets
1268 // - Strong matches
1269 // - Weak matches
1270 // Strong matches are the ones with a high fuzzy-matcher score (the "obvious" matches)
1271 // and the Weak matches are the rest.
1272 //
1273 // For the strong matches, we sort by the language-servers score first and for the weak
1274 // matches, we prefer our fuzzy finder first.
1275 //
1276 // The thinking behind that: it's useless to take the sort_text the language-server gives
1277 // us into account when it's obviously a bad match.
1278
1279 #[derive(PartialEq, Eq, PartialOrd, Ord)]
1280 enum MatchScore<'a> {
1281 Strong {
1282 sort_text: Option<&'a str>,
1283 score: Reverse<OrderedFloat<f64>>,
1284 sort_key: (usize, &'a str),
1285 },
1286 Weak {
1287 score: Reverse<OrderedFloat<f64>>,
1288 sort_text: Option<&'a str>,
1289 sort_key: (usize, &'a str),
1290 },
1291 }
1292
1293 let completion = &completions[mat.candidate_id];
1294 let sort_key = completion.sort_key();
1295 let sort_text = completion.lsp_completion.sort_text.as_deref();
1296 let score = Reverse(OrderedFloat(mat.score));
1297
1298 if mat.score >= 0.2 {
1299 MatchScore::Strong {
1300 sort_text,
1301 score,
1302 sort_key,
1303 }
1304 } else {
1305 MatchScore::Weak {
1306 score,
1307 sort_text,
1308 sort_key,
1309 }
1310 }
1311 });
1312 }
1313
1314 for mat in &mut matches {
1315 let completion = &completions[mat.candidate_id];
1316 mat.string.clone_from(&completion.label.text);
1317 for position in &mut mat.positions {
1318 *position += completion.label.filter_range.start;
1319 }
1320 }
1321 drop(completions);
1322
1323 self.matches = matches.into();
1324 self.selected_item = 0;
1325 }
1326}
1327
1328#[derive(Clone)]
1329struct CodeActionContents {
1330 tasks: Option<Arc<ResolvedTasks>>,
1331 actions: Option<Arc<[CodeAction]>>,
1332}
1333
1334impl CodeActionContents {
1335 fn len(&self) -> usize {
1336 match (&self.tasks, &self.actions) {
1337 (Some(tasks), Some(actions)) => actions.len() + tasks.templates.len(),
1338 (Some(tasks), None) => tasks.templates.len(),
1339 (None, Some(actions)) => actions.len(),
1340 (None, None) => 0,
1341 }
1342 }
1343
1344 fn is_empty(&self) -> bool {
1345 match (&self.tasks, &self.actions) {
1346 (Some(tasks), Some(actions)) => actions.is_empty() && tasks.templates.is_empty(),
1347 (Some(tasks), None) => tasks.templates.is_empty(),
1348 (None, Some(actions)) => actions.is_empty(),
1349 (None, None) => true,
1350 }
1351 }
1352
1353 fn iter(&self) -> impl Iterator<Item = CodeActionsItem> + '_ {
1354 self.tasks
1355 .iter()
1356 .flat_map(|tasks| {
1357 tasks
1358 .templates
1359 .iter()
1360 .map(|(kind, task)| CodeActionsItem::Task(kind.clone(), task.clone()))
1361 })
1362 .chain(self.actions.iter().flat_map(|actions| {
1363 actions
1364 .iter()
1365 .map(|action| CodeActionsItem::CodeAction(action.clone()))
1366 }))
1367 }
1368 fn get(&self, index: usize) -> Option<CodeActionsItem> {
1369 match (&self.tasks, &self.actions) {
1370 (Some(tasks), Some(actions)) => {
1371 if index < tasks.templates.len() {
1372 tasks
1373 .templates
1374 .get(index)
1375 .cloned()
1376 .map(|(kind, task)| CodeActionsItem::Task(kind, task))
1377 } else {
1378 actions
1379 .get(index - tasks.templates.len())
1380 .cloned()
1381 .map(CodeActionsItem::CodeAction)
1382 }
1383 }
1384 (Some(tasks), None) => tasks
1385 .templates
1386 .get(index)
1387 .cloned()
1388 .map(|(kind, task)| CodeActionsItem::Task(kind, task)),
1389 (None, Some(actions)) => actions.get(index).cloned().map(CodeActionsItem::CodeAction),
1390 (None, None) => None,
1391 }
1392 }
1393}
1394
1395#[allow(clippy::large_enum_variant)]
1396#[derive(Clone)]
1397enum CodeActionsItem {
1398 Task(TaskSourceKind, ResolvedTask),
1399 CodeAction(CodeAction),
1400}
1401
1402impl CodeActionsItem {
1403 fn as_task(&self) -> Option<&ResolvedTask> {
1404 let Self::Task(_, task) = self else {
1405 return None;
1406 };
1407 Some(task)
1408 }
1409 fn as_code_action(&self) -> Option<&CodeAction> {
1410 let Self::CodeAction(action) = self else {
1411 return None;
1412 };
1413 Some(action)
1414 }
1415 fn label(&self) -> String {
1416 match self {
1417 Self::CodeAction(action) => action.lsp_action.title.clone(),
1418 Self::Task(_, task) => task.resolved_label.clone(),
1419 }
1420 }
1421}
1422
1423struct CodeActionsMenu {
1424 actions: CodeActionContents,
1425 buffer: Model<Buffer>,
1426 selected_item: usize,
1427 scroll_handle: UniformListScrollHandle,
1428 deployed_from_indicator: Option<DisplayRow>,
1429}
1430
1431impl CodeActionsMenu {
1432 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
1433 self.selected_item = 0;
1434 self.scroll_handle.scroll_to_item(self.selected_item);
1435 cx.notify()
1436 }
1437
1438 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
1439 if self.selected_item > 0 {
1440 self.selected_item -= 1;
1441 } else {
1442 self.selected_item = self.actions.len() - 1;
1443 }
1444 self.scroll_handle.scroll_to_item(self.selected_item);
1445 cx.notify();
1446 }
1447
1448 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
1449 if self.selected_item + 1 < self.actions.len() {
1450 self.selected_item += 1;
1451 } else {
1452 self.selected_item = 0;
1453 }
1454 self.scroll_handle.scroll_to_item(self.selected_item);
1455 cx.notify();
1456 }
1457
1458 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
1459 self.selected_item = self.actions.len() - 1;
1460 self.scroll_handle.scroll_to_item(self.selected_item);
1461 cx.notify()
1462 }
1463
1464 fn visible(&self) -> bool {
1465 !self.actions.is_empty()
1466 }
1467
1468 fn render(
1469 &self,
1470 cursor_position: DisplayPoint,
1471 _style: &EditorStyle,
1472 max_height: Pixels,
1473 cx: &mut ViewContext<Editor>,
1474 ) -> (ContextMenuOrigin, AnyElement) {
1475 let actions = self.actions.clone();
1476 let selected_item = self.selected_item;
1477 let element = uniform_list(
1478 cx.view().clone(),
1479 "code_actions_menu",
1480 self.actions.len(),
1481 move |_this, range, cx| {
1482 actions
1483 .iter()
1484 .skip(range.start)
1485 .take(range.end - range.start)
1486 .enumerate()
1487 .map(|(ix, action)| {
1488 let item_ix = range.start + ix;
1489 let selected = selected_item == item_ix;
1490 let colors = cx.theme().colors();
1491 div()
1492 .px_2()
1493 .text_color(colors.text)
1494 .when(selected, |style| {
1495 style
1496 .bg(colors.element_active)
1497 .text_color(colors.text_accent)
1498 })
1499 .hover(|style| {
1500 style
1501 .bg(colors.element_hover)
1502 .text_color(colors.text_accent)
1503 })
1504 .whitespace_nowrap()
1505 .when_some(action.as_code_action(), |this, action| {
1506 this.on_mouse_down(
1507 MouseButton::Left,
1508 cx.listener(move |editor, _, cx| {
1509 cx.stop_propagation();
1510 if let Some(task) = editor.confirm_code_action(
1511 &ConfirmCodeAction {
1512 item_ix: Some(item_ix),
1513 },
1514 cx,
1515 ) {
1516 task.detach_and_log_err(cx)
1517 }
1518 }),
1519 )
1520 // TASK: It would be good to make lsp_action.title a SharedString to avoid allocating here.
1521 .child(SharedString::from(action.lsp_action.title.clone()))
1522 })
1523 .when_some(action.as_task(), |this, task| {
1524 this.on_mouse_down(
1525 MouseButton::Left,
1526 cx.listener(move |editor, _, cx| {
1527 cx.stop_propagation();
1528 if let Some(task) = editor.confirm_code_action(
1529 &ConfirmCodeAction {
1530 item_ix: Some(item_ix),
1531 },
1532 cx,
1533 ) {
1534 task.detach_and_log_err(cx)
1535 }
1536 }),
1537 )
1538 .child(SharedString::from(task.resolved_label.clone()))
1539 })
1540 })
1541 .collect()
1542 },
1543 )
1544 .elevation_1(cx)
1545 .px_2()
1546 .py_1()
1547 .max_h(max_height)
1548 .occlude()
1549 .track_scroll(self.scroll_handle.clone())
1550 .with_width_from_item(
1551 self.actions
1552 .iter()
1553 .enumerate()
1554 .max_by_key(|(_, action)| match action {
1555 CodeActionsItem::Task(_, task) => task.resolved_label.chars().count(),
1556 CodeActionsItem::CodeAction(action) => action.lsp_action.title.chars().count(),
1557 })
1558 .map(|(ix, _)| ix),
1559 )
1560 .with_sizing_behavior(ListSizingBehavior::Infer)
1561 .into_any_element();
1562
1563 let cursor_position = if let Some(row) = self.deployed_from_indicator {
1564 ContextMenuOrigin::GutterIndicator(row)
1565 } else {
1566 ContextMenuOrigin::EditorPoint(cursor_position)
1567 };
1568
1569 (cursor_position, element)
1570 }
1571}
1572
1573#[derive(Debug)]
1574struct ActiveDiagnosticGroup {
1575 primary_range: Range<Anchor>,
1576 primary_message: String,
1577 group_id: usize,
1578 blocks: HashMap<CustomBlockId, Diagnostic>,
1579 is_valid: bool,
1580}
1581
1582#[derive(Serialize, Deserialize, Clone, Debug)]
1583pub struct ClipboardSelection {
1584 pub len: usize,
1585 pub is_entire_line: bool,
1586 pub first_line_indent: u32,
1587}
1588
1589#[derive(Debug)]
1590pub(crate) struct NavigationData {
1591 cursor_anchor: Anchor,
1592 cursor_position: Point,
1593 scroll_anchor: ScrollAnchor,
1594 scroll_top_row: u32,
1595}
1596
1597#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1598enum GotoDefinitionKind {
1599 Symbol,
1600 Declaration,
1601 Type,
1602 Implementation,
1603}
1604
1605#[derive(Debug, Clone)]
1606enum InlayHintRefreshReason {
1607 Toggle(bool),
1608 SettingsChange(InlayHintSettings),
1609 NewLinesShown,
1610 BufferEdited(HashSet<Arc<Language>>),
1611 RefreshRequested,
1612 ExcerptsRemoved(Vec<ExcerptId>),
1613}
1614
1615impl InlayHintRefreshReason {
1616 fn description(&self) -> &'static str {
1617 match self {
1618 Self::Toggle(_) => "toggle",
1619 Self::SettingsChange(_) => "settings change",
1620 Self::NewLinesShown => "new lines shown",
1621 Self::BufferEdited(_) => "buffer edited",
1622 Self::RefreshRequested => "refresh requested",
1623 Self::ExcerptsRemoved(_) => "excerpts removed",
1624 }
1625 }
1626}
1627
1628pub(crate) struct FocusedBlock {
1629 id: BlockId,
1630 focus_handle: WeakFocusHandle,
1631}
1632
1633impl Editor {
1634 pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
1635 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1636 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1637 Self::new(
1638 EditorMode::SingleLine { auto_width: false },
1639 buffer,
1640 None,
1641 false,
1642 cx,
1643 )
1644 }
1645
1646 pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
1647 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1648 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1649 Self::new(EditorMode::Full, buffer, None, false, cx)
1650 }
1651
1652 pub fn auto_width(cx: &mut ViewContext<Self>) -> Self {
1653 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1654 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1655 Self::new(
1656 EditorMode::SingleLine { auto_width: true },
1657 buffer,
1658 None,
1659 false,
1660 cx,
1661 )
1662 }
1663
1664 pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
1665 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1666 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1667 Self::new(
1668 EditorMode::AutoHeight { max_lines },
1669 buffer,
1670 None,
1671 false,
1672 cx,
1673 )
1674 }
1675
1676 pub fn for_buffer(
1677 buffer: Model<Buffer>,
1678 project: Option<Model<Project>>,
1679 cx: &mut ViewContext<Self>,
1680 ) -> Self {
1681 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1682 Self::new(EditorMode::Full, buffer, project, false, cx)
1683 }
1684
1685 pub fn for_multibuffer(
1686 buffer: Model<MultiBuffer>,
1687 project: Option<Model<Project>>,
1688 show_excerpt_controls: bool,
1689 cx: &mut ViewContext<Self>,
1690 ) -> Self {
1691 Self::new(EditorMode::Full, buffer, project, show_excerpt_controls, cx)
1692 }
1693
1694 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1695 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1696 let mut clone = Self::new(
1697 self.mode,
1698 self.buffer.clone(),
1699 self.project.clone(),
1700 show_excerpt_controls,
1701 cx,
1702 );
1703 self.display_map.update(cx, |display_map, cx| {
1704 let snapshot = display_map.snapshot(cx);
1705 clone.display_map.update(cx, |display_map, cx| {
1706 display_map.set_state(&snapshot, cx);
1707 });
1708 });
1709 clone.selections.clone_state(&self.selections);
1710 clone.scroll_manager.clone_state(&self.scroll_manager);
1711 clone.searchable = self.searchable;
1712 clone
1713 }
1714
1715 pub fn new(
1716 mode: EditorMode,
1717 buffer: Model<MultiBuffer>,
1718 project: Option<Model<Project>>,
1719 show_excerpt_controls: bool,
1720 cx: &mut ViewContext<Self>,
1721 ) -> Self {
1722 let style = cx.text_style();
1723 let font_size = style.font_size.to_pixels(cx.rem_size());
1724 let editor = cx.view().downgrade();
1725 let fold_placeholder = FoldPlaceholder {
1726 constrain_width: true,
1727 render: Arc::new(move |fold_id, fold_range, cx| {
1728 let editor = editor.clone();
1729 div()
1730 .id(fold_id)
1731 .bg(cx.theme().colors().ghost_element_background)
1732 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1733 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1734 .rounded_sm()
1735 .size_full()
1736 .cursor_pointer()
1737 .child("⋯")
1738 .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
1739 .on_click(move |_, cx| {
1740 editor
1741 .update(cx, |editor, cx| {
1742 editor.unfold_ranges(
1743 [fold_range.start..fold_range.end],
1744 true,
1745 false,
1746 cx,
1747 );
1748 cx.stop_propagation();
1749 })
1750 .ok();
1751 })
1752 .into_any()
1753 }),
1754 merge_adjacent: true,
1755 };
1756 let file_header_size = if show_excerpt_controls { 3 } else { 2 };
1757 let display_map = cx.new_model(|cx| {
1758 DisplayMap::new(
1759 buffer.clone(),
1760 style.font(),
1761 font_size,
1762 None,
1763 show_excerpt_controls,
1764 file_header_size,
1765 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1766 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1767 fold_placeholder,
1768 cx,
1769 )
1770 });
1771
1772 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1773
1774 let blink_manager = cx.new_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1775
1776 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1777 .then(|| language_settings::SoftWrap::PreferLine);
1778
1779 let mut project_subscriptions = Vec::new();
1780 if mode == EditorMode::Full {
1781 if let Some(project) = project.as_ref() {
1782 if buffer.read(cx).is_singleton() {
1783 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1784 cx.emit(EditorEvent::TitleChanged);
1785 }));
1786 }
1787 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1788 if let project::Event::RefreshInlayHints = event {
1789 editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1790 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1791 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1792 let focus_handle = editor.focus_handle(cx);
1793 if focus_handle.is_focused(cx) {
1794 let snapshot = buffer.read(cx).snapshot();
1795 for (range, snippet) in snippet_edits {
1796 let editor_range =
1797 language::range_from_lsp(*range).to_offset(&snapshot);
1798 editor
1799 .insert_snippet(&[editor_range], snippet.clone(), cx)
1800 .ok();
1801 }
1802 }
1803 }
1804 }
1805 }));
1806 let task_inventory = project.read(cx).task_inventory().clone();
1807 project_subscriptions.push(cx.observe(&task_inventory, |editor, _, cx| {
1808 editor.tasks_update_task = Some(editor.refresh_runnables(cx));
1809 }));
1810 }
1811 }
1812
1813 let inlay_hint_settings = inlay_hint_settings(
1814 selections.newest_anchor().head(),
1815 &buffer.read(cx).snapshot(cx),
1816 cx,
1817 );
1818 let focus_handle = cx.focus_handle();
1819 cx.on_focus(&focus_handle, Self::handle_focus).detach();
1820 cx.on_focus_in(&focus_handle, Self::handle_focus_in)
1821 .detach();
1822 cx.on_focus_out(&focus_handle, Self::handle_focus_out)
1823 .detach();
1824 cx.on_blur(&focus_handle, Self::handle_blur).detach();
1825
1826 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1827 Some(false)
1828 } else {
1829 None
1830 };
1831
1832 let mut this = Self {
1833 focus_handle,
1834 show_cursor_when_unfocused: false,
1835 last_focused_descendant: None,
1836 buffer: buffer.clone(),
1837 display_map: display_map.clone(),
1838 selections,
1839 scroll_manager: ScrollManager::new(cx),
1840 columnar_selection_tail: None,
1841 add_selections_state: None,
1842 select_next_state: None,
1843 select_prev_state: None,
1844 selection_history: Default::default(),
1845 autoclose_regions: Default::default(),
1846 snippet_stack: Default::default(),
1847 select_larger_syntax_node_stack: Vec::new(),
1848 ime_transaction: Default::default(),
1849 active_diagnostics: None,
1850 soft_wrap_mode_override,
1851 completion_provider: project.clone().map(|project| Box::new(project) as _),
1852 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1853 project,
1854 blink_manager: blink_manager.clone(),
1855 show_local_selections: true,
1856 mode,
1857 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1858 show_gutter: mode == EditorMode::Full,
1859 show_line_numbers: None,
1860 use_relative_line_numbers: None,
1861 show_git_diff_gutter: None,
1862 show_code_actions: None,
1863 show_runnables: None,
1864 show_wrap_guides: None,
1865 show_indent_guides,
1866 placeholder_text: None,
1867 highlight_order: 0,
1868 highlighted_rows: HashMap::default(),
1869 background_highlights: Default::default(),
1870 gutter_highlights: TreeMap::default(),
1871 scrollbar_marker_state: ScrollbarMarkerState::default(),
1872 active_indent_guides_state: ActiveIndentGuidesState::default(),
1873 nav_history: None,
1874 context_menu: RwLock::new(None),
1875 mouse_context_menu: None,
1876 completion_tasks: Default::default(),
1877 signature_help_state: SignatureHelpState::default(),
1878 auto_signature_help: None,
1879 find_all_references_task_sources: Vec::new(),
1880 next_completion_id: 0,
1881 completion_documentation_pre_resolve_debounce: DebouncedDelay::new(),
1882 next_inlay_id: 0,
1883 available_code_actions: Default::default(),
1884 code_actions_task: Default::default(),
1885 document_highlights_task: Default::default(),
1886 linked_editing_range_task: Default::default(),
1887 pending_rename: Default::default(),
1888 searchable: true,
1889 cursor_shape: Default::default(),
1890 current_line_highlight: None,
1891 autoindent_mode: Some(AutoindentMode::EachLine),
1892 collapse_matches: false,
1893 workspace: None,
1894 input_enabled: true,
1895 use_modal_editing: mode == EditorMode::Full,
1896 read_only: false,
1897 use_autoclose: true,
1898 use_auto_surround: true,
1899 auto_replace_emoji_shortcode: false,
1900 leader_peer_id: None,
1901 remote_id: None,
1902 hover_state: Default::default(),
1903 hovered_link_state: Default::default(),
1904 inline_completion_provider: None,
1905 active_inline_completion: None,
1906 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1907 expanded_hunks: ExpandedHunks::default(),
1908 gutter_hovered: false,
1909 pixel_position_of_newest_cursor: None,
1910 last_bounds: None,
1911 expect_bounds_change: None,
1912 gutter_dimensions: GutterDimensions::default(),
1913 style: None,
1914 show_cursor_names: false,
1915 hovered_cursors: Default::default(),
1916 next_editor_action_id: EditorActionId::default(),
1917 editor_actions: Rc::default(),
1918 show_inline_completions_override: None,
1919 enable_inline_completions: true,
1920 custom_context_menu: None,
1921 show_git_blame_gutter: false,
1922 show_git_blame_inline: false,
1923 show_selection_menu: None,
1924 show_git_blame_inline_delay_task: None,
1925 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1926 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1927 .session
1928 .restore_unsaved_buffers,
1929 blame: None,
1930 blame_subscription: None,
1931 file_header_size,
1932 tasks: Default::default(),
1933 _subscriptions: vec![
1934 cx.observe(&buffer, Self::on_buffer_changed),
1935 cx.subscribe(&buffer, Self::on_buffer_event),
1936 cx.observe(&display_map, Self::on_display_map_changed),
1937 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1938 cx.observe_global::<SettingsStore>(Self::settings_changed),
1939 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1940 cx.observe_window_activation(|editor, cx| {
1941 let active = cx.is_window_active();
1942 editor.blink_manager.update(cx, |blink_manager, cx| {
1943 if active {
1944 blink_manager.enable(cx);
1945 } else {
1946 blink_manager.disable(cx);
1947 }
1948 });
1949 }),
1950 ],
1951 tasks_update_task: None,
1952 linked_edit_ranges: Default::default(),
1953 previous_search_ranges: None,
1954 breadcrumb_header: None,
1955 focused_block: None,
1956 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1957 addons: HashMap::default(),
1958 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1959 };
1960 this.tasks_update_task = Some(this.refresh_runnables(cx));
1961 this._subscriptions.extend(project_subscriptions);
1962
1963 this.end_selection(cx);
1964 this.scroll_manager.show_scrollbar(cx);
1965
1966 if mode == EditorMode::Full {
1967 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1968 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1969
1970 if this.git_blame_inline_enabled {
1971 this.git_blame_inline_enabled = true;
1972 this.start_git_blame_inline(false, cx);
1973 }
1974 }
1975
1976 this.report_editor_event("open", None, cx);
1977 this
1978 }
1979
1980 pub fn mouse_menu_is_focused(&self, cx: &WindowContext) -> bool {
1981 self.mouse_context_menu
1982 .as_ref()
1983 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(cx))
1984 }
1985
1986 fn key_context(&self, cx: &ViewContext<Self>) -> KeyContext {
1987 let mut key_context = KeyContext::new_with_defaults();
1988 key_context.add("Editor");
1989 let mode = match self.mode {
1990 EditorMode::SingleLine { .. } => "single_line",
1991 EditorMode::AutoHeight { .. } => "auto_height",
1992 EditorMode::Full => "full",
1993 };
1994
1995 if EditorSettings::jupyter_enabled(cx) {
1996 key_context.add("jupyter");
1997 }
1998
1999 key_context.set("mode", mode);
2000 if self.pending_rename.is_some() {
2001 key_context.add("renaming");
2002 }
2003 if self.context_menu_visible() {
2004 match self.context_menu.read().as_ref() {
2005 Some(ContextMenu::Completions(_)) => {
2006 key_context.add("menu");
2007 key_context.add("showing_completions")
2008 }
2009 Some(ContextMenu::CodeActions(_)) => {
2010 key_context.add("menu");
2011 key_context.add("showing_code_actions")
2012 }
2013 None => {}
2014 }
2015 }
2016
2017 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
2018 if !self.focus_handle(cx).contains_focused(cx)
2019 || (self.is_focused(cx) || self.mouse_menu_is_focused(cx))
2020 {
2021 for addon in self.addons.values() {
2022 addon.extend_key_context(&mut key_context, cx)
2023 }
2024 }
2025
2026 if let Some(extension) = self
2027 .buffer
2028 .read(cx)
2029 .as_singleton()
2030 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
2031 {
2032 key_context.set("extension", extension.to_string());
2033 }
2034
2035 if self.has_active_inline_completion(cx) {
2036 key_context.add("copilot_suggestion");
2037 key_context.add("inline_completion");
2038 }
2039
2040 key_context
2041 }
2042
2043 pub fn new_file(
2044 workspace: &mut Workspace,
2045 _: &workspace::NewFile,
2046 cx: &mut ViewContext<Workspace>,
2047 ) {
2048 Self::new_in_workspace(workspace, cx).detach_and_prompt_err(
2049 "Failed to create buffer",
2050 cx,
2051 |e, _| match e.error_code() {
2052 ErrorCode::RemoteUpgradeRequired => Some(format!(
2053 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2054 e.error_tag("required").unwrap_or("the latest version")
2055 )),
2056 _ => None,
2057 },
2058 );
2059 }
2060
2061 pub fn new_in_workspace(
2062 workspace: &mut Workspace,
2063 cx: &mut ViewContext<Workspace>,
2064 ) -> Task<Result<View<Editor>>> {
2065 let project = workspace.project().clone();
2066 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2067
2068 cx.spawn(|workspace, mut cx| async move {
2069 let buffer = create.await?;
2070 workspace.update(&mut cx, |workspace, cx| {
2071 let editor =
2072 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx));
2073 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
2074 editor
2075 })
2076 })
2077 }
2078
2079 fn new_file_vertical(
2080 workspace: &mut Workspace,
2081 _: &workspace::NewFileSplitVertical,
2082 cx: &mut ViewContext<Workspace>,
2083 ) {
2084 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), cx)
2085 }
2086
2087 fn new_file_horizontal(
2088 workspace: &mut Workspace,
2089 _: &workspace::NewFileSplitHorizontal,
2090 cx: &mut ViewContext<Workspace>,
2091 ) {
2092 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), cx)
2093 }
2094
2095 fn new_file_in_direction(
2096 workspace: &mut Workspace,
2097 direction: SplitDirection,
2098 cx: &mut ViewContext<Workspace>,
2099 ) {
2100 let project = workspace.project().clone();
2101 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2102
2103 cx.spawn(|workspace, mut cx| async move {
2104 let buffer = create.await?;
2105 workspace.update(&mut cx, move |workspace, cx| {
2106 workspace.split_item(
2107 direction,
2108 Box::new(
2109 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)),
2110 ),
2111 cx,
2112 )
2113 })?;
2114 anyhow::Ok(())
2115 })
2116 .detach_and_prompt_err("Failed to create buffer", cx, |e, _| match e.error_code() {
2117 ErrorCode::RemoteUpgradeRequired => Some(format!(
2118 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2119 e.error_tag("required").unwrap_or("the latest version")
2120 )),
2121 _ => None,
2122 });
2123 }
2124
2125 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
2126 self.buffer.read(cx).replica_id()
2127 }
2128
2129 pub fn leader_peer_id(&self) -> Option<PeerId> {
2130 self.leader_peer_id
2131 }
2132
2133 pub fn buffer(&self) -> &Model<MultiBuffer> {
2134 &self.buffer
2135 }
2136
2137 pub fn workspace(&self) -> Option<View<Workspace>> {
2138 self.workspace.as_ref()?.0.upgrade()
2139 }
2140
2141 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
2142 self.buffer().read(cx).title(cx)
2143 }
2144
2145 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
2146 EditorSnapshot {
2147 mode: self.mode,
2148 show_gutter: self.show_gutter,
2149 show_line_numbers: self.show_line_numbers,
2150 show_git_diff_gutter: self.show_git_diff_gutter,
2151 show_code_actions: self.show_code_actions,
2152 show_runnables: self.show_runnables,
2153 render_git_blame_gutter: self.render_git_blame_gutter(cx),
2154 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
2155 scroll_anchor: self.scroll_manager.anchor(),
2156 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
2157 placeholder_text: self.placeholder_text.clone(),
2158 is_focused: self.focus_handle.is_focused(cx),
2159 current_line_highlight: self
2160 .current_line_highlight
2161 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
2162 gutter_hovered: self.gutter_hovered,
2163 }
2164 }
2165
2166 pub fn language_at<T: ToOffset>(&self, point: T, cx: &AppContext) -> Option<Arc<Language>> {
2167 self.buffer.read(cx).language_at(point, cx)
2168 }
2169
2170 pub fn file_at<T: ToOffset>(
2171 &self,
2172 point: T,
2173 cx: &AppContext,
2174 ) -> Option<Arc<dyn language::File>> {
2175 self.buffer.read(cx).read(cx).file_at(point).cloned()
2176 }
2177
2178 pub fn active_excerpt(
2179 &self,
2180 cx: &AppContext,
2181 ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
2182 self.buffer
2183 .read(cx)
2184 .excerpt_containing(self.selections.newest_anchor().head(), cx)
2185 }
2186
2187 pub fn mode(&self) -> EditorMode {
2188 self.mode
2189 }
2190
2191 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
2192 self.collaboration_hub.as_deref()
2193 }
2194
2195 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
2196 self.collaboration_hub = Some(hub);
2197 }
2198
2199 pub fn set_custom_context_menu(
2200 &mut self,
2201 f: impl 'static
2202 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
2203 ) {
2204 self.custom_context_menu = Some(Box::new(f))
2205 }
2206
2207 pub fn set_completion_provider(&mut self, provider: Box<dyn CompletionProvider>) {
2208 self.completion_provider = Some(provider);
2209 }
2210
2211 pub fn set_inline_completion_provider<T>(
2212 &mut self,
2213 provider: Option<Model<T>>,
2214 cx: &mut ViewContext<Self>,
2215 ) where
2216 T: InlineCompletionProvider,
2217 {
2218 self.inline_completion_provider =
2219 provider.map(|provider| RegisteredInlineCompletionProvider {
2220 _subscription: cx.observe(&provider, |this, _, cx| {
2221 if this.focus_handle.is_focused(cx) {
2222 this.update_visible_inline_completion(cx);
2223 }
2224 }),
2225 provider: Arc::new(provider),
2226 });
2227 self.refresh_inline_completion(false, false, cx);
2228 }
2229
2230 pub fn placeholder_text(&self, _cx: &WindowContext) -> Option<&str> {
2231 self.placeholder_text.as_deref()
2232 }
2233
2234 pub fn set_placeholder_text(
2235 &mut self,
2236 placeholder_text: impl Into<Arc<str>>,
2237 cx: &mut ViewContext<Self>,
2238 ) {
2239 let placeholder_text = Some(placeholder_text.into());
2240 if self.placeholder_text != placeholder_text {
2241 self.placeholder_text = placeholder_text;
2242 cx.notify();
2243 }
2244 }
2245
2246 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
2247 self.cursor_shape = cursor_shape;
2248
2249 // Disrupt blink for immediate user feedback that the cursor shape has changed
2250 self.blink_manager.update(cx, BlinkManager::show_cursor);
2251
2252 cx.notify();
2253 }
2254
2255 pub fn set_current_line_highlight(
2256 &mut self,
2257 current_line_highlight: Option<CurrentLineHighlight>,
2258 ) {
2259 self.current_line_highlight = current_line_highlight;
2260 }
2261
2262 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
2263 self.collapse_matches = collapse_matches;
2264 }
2265
2266 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
2267 if self.collapse_matches {
2268 return range.start..range.start;
2269 }
2270 range.clone()
2271 }
2272
2273 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
2274 if self.display_map.read(cx).clip_at_line_ends != clip {
2275 self.display_map
2276 .update(cx, |map, _| map.clip_at_line_ends = clip);
2277 }
2278 }
2279
2280 pub fn set_input_enabled(&mut self, input_enabled: bool) {
2281 self.input_enabled = input_enabled;
2282 }
2283
2284 pub fn set_inline_completions_enabled(&mut self, enabled: bool) {
2285 self.enable_inline_completions = enabled;
2286 }
2287
2288 pub fn set_autoindent(&mut self, autoindent: bool) {
2289 if autoindent {
2290 self.autoindent_mode = Some(AutoindentMode::EachLine);
2291 } else {
2292 self.autoindent_mode = None;
2293 }
2294 }
2295
2296 pub fn read_only(&self, cx: &AppContext) -> bool {
2297 self.read_only || self.buffer.read(cx).read_only()
2298 }
2299
2300 pub fn set_read_only(&mut self, read_only: bool) {
2301 self.read_only = read_only;
2302 }
2303
2304 pub fn set_use_autoclose(&mut self, autoclose: bool) {
2305 self.use_autoclose = autoclose;
2306 }
2307
2308 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
2309 self.use_auto_surround = auto_surround;
2310 }
2311
2312 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
2313 self.auto_replace_emoji_shortcode = auto_replace;
2314 }
2315
2316 pub fn toggle_inline_completions(
2317 &mut self,
2318 _: &ToggleInlineCompletions,
2319 cx: &mut ViewContext<Self>,
2320 ) {
2321 if self.show_inline_completions_override.is_some() {
2322 self.set_show_inline_completions(None, cx);
2323 } else {
2324 let cursor = self.selections.newest_anchor().head();
2325 if let Some((buffer, cursor_buffer_position)) =
2326 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
2327 {
2328 let show_inline_completions =
2329 !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx);
2330 self.set_show_inline_completions(Some(show_inline_completions), cx);
2331 }
2332 }
2333 }
2334
2335 pub fn set_show_inline_completions(
2336 &mut self,
2337 show_inline_completions: Option<bool>,
2338 cx: &mut ViewContext<Self>,
2339 ) {
2340 self.show_inline_completions_override = show_inline_completions;
2341 self.refresh_inline_completion(false, true, cx);
2342 }
2343
2344 fn should_show_inline_completions(
2345 &self,
2346 buffer: &Model<Buffer>,
2347 buffer_position: language::Anchor,
2348 cx: &AppContext,
2349 ) -> bool {
2350 if let Some(provider) = self.inline_completion_provider() {
2351 if let Some(show_inline_completions) = self.show_inline_completions_override {
2352 show_inline_completions
2353 } else {
2354 self.mode == EditorMode::Full && provider.is_enabled(&buffer, buffer_position, cx)
2355 }
2356 } else {
2357 false
2358 }
2359 }
2360
2361 pub fn set_use_modal_editing(&mut self, to: bool) {
2362 self.use_modal_editing = to;
2363 }
2364
2365 pub fn use_modal_editing(&self) -> bool {
2366 self.use_modal_editing
2367 }
2368
2369 fn selections_did_change(
2370 &mut self,
2371 local: bool,
2372 old_cursor_position: &Anchor,
2373 show_completions: bool,
2374 cx: &mut ViewContext<Self>,
2375 ) {
2376 cx.invalidate_character_coordinates();
2377
2378 // Copy selections to primary selection buffer
2379 #[cfg(target_os = "linux")]
2380 if local {
2381 let selections = self.selections.all::<usize>(cx);
2382 let buffer_handle = self.buffer.read(cx).read(cx);
2383
2384 let mut text = String::new();
2385 for (index, selection) in selections.iter().enumerate() {
2386 let text_for_selection = buffer_handle
2387 .text_for_range(selection.start..selection.end)
2388 .collect::<String>();
2389
2390 text.push_str(&text_for_selection);
2391 if index != selections.len() - 1 {
2392 text.push('\n');
2393 }
2394 }
2395
2396 if !text.is_empty() {
2397 cx.write_to_primary(ClipboardItem::new_string(text));
2398 }
2399 }
2400
2401 if self.focus_handle.is_focused(cx) && self.leader_peer_id.is_none() {
2402 self.buffer.update(cx, |buffer, cx| {
2403 buffer.set_active_selections(
2404 &self.selections.disjoint_anchors(),
2405 self.selections.line_mode,
2406 self.cursor_shape,
2407 cx,
2408 )
2409 });
2410 }
2411 let display_map = self
2412 .display_map
2413 .update(cx, |display_map, cx| display_map.snapshot(cx));
2414 let buffer = &display_map.buffer_snapshot;
2415 self.add_selections_state = None;
2416 self.select_next_state = None;
2417 self.select_prev_state = None;
2418 self.select_larger_syntax_node_stack.clear();
2419 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2420 self.snippet_stack
2421 .invalidate(&self.selections.disjoint_anchors(), buffer);
2422 self.take_rename(false, cx);
2423
2424 let new_cursor_position = self.selections.newest_anchor().head();
2425
2426 self.push_to_nav_history(
2427 *old_cursor_position,
2428 Some(new_cursor_position.to_point(buffer)),
2429 cx,
2430 );
2431
2432 if local {
2433 let new_cursor_position = self.selections.newest_anchor().head();
2434 let mut context_menu = self.context_menu.write();
2435 let completion_menu = match context_menu.as_ref() {
2436 Some(ContextMenu::Completions(menu)) => Some(menu),
2437
2438 _ => {
2439 *context_menu = None;
2440 None
2441 }
2442 };
2443
2444 if let Some(completion_menu) = completion_menu {
2445 let cursor_position = new_cursor_position.to_offset(buffer);
2446 let (word_range, kind) = buffer.surrounding_word(completion_menu.initial_position);
2447 if kind == Some(CharKind::Word)
2448 && word_range.to_inclusive().contains(&cursor_position)
2449 {
2450 let mut completion_menu = completion_menu.clone();
2451 drop(context_menu);
2452
2453 let query = Self::completion_query(buffer, cursor_position);
2454 cx.spawn(move |this, mut cx| async move {
2455 completion_menu
2456 .filter(query.as_deref(), cx.background_executor().clone())
2457 .await;
2458
2459 this.update(&mut cx, |this, cx| {
2460 let mut context_menu = this.context_menu.write();
2461 let Some(ContextMenu::Completions(menu)) = context_menu.as_ref() else {
2462 return;
2463 };
2464
2465 if menu.id > completion_menu.id {
2466 return;
2467 }
2468
2469 *context_menu = Some(ContextMenu::Completions(completion_menu));
2470 drop(context_menu);
2471 cx.notify();
2472 })
2473 })
2474 .detach();
2475
2476 if show_completions {
2477 self.show_completions(&ShowCompletions { trigger: None }, cx);
2478 }
2479 } else {
2480 drop(context_menu);
2481 self.hide_context_menu(cx);
2482 }
2483 } else {
2484 drop(context_menu);
2485 }
2486
2487 hide_hover(self, cx);
2488
2489 if old_cursor_position.to_display_point(&display_map).row()
2490 != new_cursor_position.to_display_point(&display_map).row()
2491 {
2492 self.available_code_actions.take();
2493 }
2494 self.refresh_code_actions(cx);
2495 self.refresh_document_highlights(cx);
2496 refresh_matching_bracket_highlights(self, cx);
2497 self.discard_inline_completion(false, cx);
2498 linked_editing_ranges::refresh_linked_ranges(self, cx);
2499 if self.git_blame_inline_enabled {
2500 self.start_inline_blame_timer(cx);
2501 }
2502 }
2503
2504 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2505 cx.emit(EditorEvent::SelectionsChanged { local });
2506
2507 if self.selections.disjoint_anchors().len() == 1 {
2508 cx.emit(SearchEvent::ActiveMatchChanged)
2509 }
2510 cx.notify();
2511 }
2512
2513 pub fn change_selections<R>(
2514 &mut self,
2515 autoscroll: Option<Autoscroll>,
2516 cx: &mut ViewContext<Self>,
2517 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2518 ) -> R {
2519 self.change_selections_inner(autoscroll, true, cx, change)
2520 }
2521
2522 pub fn change_selections_inner<R>(
2523 &mut self,
2524 autoscroll: Option<Autoscroll>,
2525 request_completions: bool,
2526 cx: &mut ViewContext<Self>,
2527 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2528 ) -> R {
2529 let old_cursor_position = self.selections.newest_anchor().head();
2530 self.push_to_selection_history();
2531
2532 let (changed, result) = self.selections.change_with(cx, change);
2533
2534 if changed {
2535 if let Some(autoscroll) = autoscroll {
2536 self.request_autoscroll(autoscroll, cx);
2537 }
2538 self.selections_did_change(true, &old_cursor_position, request_completions, cx);
2539
2540 if self.should_open_signature_help_automatically(
2541 &old_cursor_position,
2542 self.signature_help_state.backspace_pressed(),
2543 cx,
2544 ) {
2545 self.show_signature_help(&ShowSignatureHelp, cx);
2546 }
2547 self.signature_help_state.set_backspace_pressed(false);
2548 }
2549
2550 result
2551 }
2552
2553 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2554 where
2555 I: IntoIterator<Item = (Range<S>, T)>,
2556 S: ToOffset,
2557 T: Into<Arc<str>>,
2558 {
2559 if self.read_only(cx) {
2560 return;
2561 }
2562
2563 self.buffer
2564 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2565 }
2566
2567 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2568 where
2569 I: IntoIterator<Item = (Range<S>, T)>,
2570 S: ToOffset,
2571 T: Into<Arc<str>>,
2572 {
2573 if self.read_only(cx) {
2574 return;
2575 }
2576
2577 self.buffer.update(cx, |buffer, cx| {
2578 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2579 });
2580 }
2581
2582 pub fn edit_with_block_indent<I, S, T>(
2583 &mut self,
2584 edits: I,
2585 original_indent_columns: Vec<u32>,
2586 cx: &mut ViewContext<Self>,
2587 ) where
2588 I: IntoIterator<Item = (Range<S>, T)>,
2589 S: ToOffset,
2590 T: Into<Arc<str>>,
2591 {
2592 if self.read_only(cx) {
2593 return;
2594 }
2595
2596 self.buffer.update(cx, |buffer, cx| {
2597 buffer.edit(
2598 edits,
2599 Some(AutoindentMode::Block {
2600 original_indent_columns,
2601 }),
2602 cx,
2603 )
2604 });
2605 }
2606
2607 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
2608 self.hide_context_menu(cx);
2609
2610 match phase {
2611 SelectPhase::Begin {
2612 position,
2613 add,
2614 click_count,
2615 } => self.begin_selection(position, add, click_count, cx),
2616 SelectPhase::BeginColumnar {
2617 position,
2618 goal_column,
2619 reset,
2620 } => self.begin_columnar_selection(position, goal_column, reset, cx),
2621 SelectPhase::Extend {
2622 position,
2623 click_count,
2624 } => self.extend_selection(position, click_count, cx),
2625 SelectPhase::Update {
2626 position,
2627 goal_column,
2628 scroll_delta,
2629 } => self.update_selection(position, goal_column, scroll_delta, cx),
2630 SelectPhase::End => self.end_selection(cx),
2631 }
2632 }
2633
2634 fn extend_selection(
2635 &mut self,
2636 position: DisplayPoint,
2637 click_count: usize,
2638 cx: &mut ViewContext<Self>,
2639 ) {
2640 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2641 let tail = self.selections.newest::<usize>(cx).tail();
2642 self.begin_selection(position, false, click_count, cx);
2643
2644 let position = position.to_offset(&display_map, Bias::Left);
2645 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2646
2647 let mut pending_selection = self
2648 .selections
2649 .pending_anchor()
2650 .expect("extend_selection not called with pending selection");
2651 if position >= tail {
2652 pending_selection.start = tail_anchor;
2653 } else {
2654 pending_selection.end = tail_anchor;
2655 pending_selection.reversed = true;
2656 }
2657
2658 let mut pending_mode = self.selections.pending_mode().unwrap();
2659 match &mut pending_mode {
2660 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2661 _ => {}
2662 }
2663
2664 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
2665 s.set_pending(pending_selection, pending_mode)
2666 });
2667 }
2668
2669 fn begin_selection(
2670 &mut self,
2671 position: DisplayPoint,
2672 add: bool,
2673 click_count: usize,
2674 cx: &mut ViewContext<Self>,
2675 ) {
2676 if !self.focus_handle.is_focused(cx) {
2677 self.last_focused_descendant = None;
2678 cx.focus(&self.focus_handle);
2679 }
2680
2681 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2682 let buffer = &display_map.buffer_snapshot;
2683 let newest_selection = self.selections.newest_anchor().clone();
2684 let position = display_map.clip_point(position, Bias::Left);
2685
2686 let start;
2687 let end;
2688 let mode;
2689 let auto_scroll;
2690 match click_count {
2691 1 => {
2692 start = buffer.anchor_before(position.to_point(&display_map));
2693 end = start;
2694 mode = SelectMode::Character;
2695 auto_scroll = true;
2696 }
2697 2 => {
2698 let range = movement::surrounding_word(&display_map, position);
2699 start = buffer.anchor_before(range.start.to_point(&display_map));
2700 end = buffer.anchor_before(range.end.to_point(&display_map));
2701 mode = SelectMode::Word(start..end);
2702 auto_scroll = true;
2703 }
2704 3 => {
2705 let position = display_map
2706 .clip_point(position, Bias::Left)
2707 .to_point(&display_map);
2708 let line_start = display_map.prev_line_boundary(position).0;
2709 let next_line_start = buffer.clip_point(
2710 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2711 Bias::Left,
2712 );
2713 start = buffer.anchor_before(line_start);
2714 end = buffer.anchor_before(next_line_start);
2715 mode = SelectMode::Line(start..end);
2716 auto_scroll = true;
2717 }
2718 _ => {
2719 start = buffer.anchor_before(0);
2720 end = buffer.anchor_before(buffer.len());
2721 mode = SelectMode::All;
2722 auto_scroll = false;
2723 }
2724 }
2725
2726 let point_to_delete: Option<usize> = {
2727 let selected_points: Vec<Selection<Point>> =
2728 self.selections.disjoint_in_range(start..end, cx);
2729
2730 if !add || click_count > 1 {
2731 None
2732 } else if selected_points.len() > 0 {
2733 Some(selected_points[0].id)
2734 } else {
2735 let clicked_point_already_selected =
2736 self.selections.disjoint.iter().find(|selection| {
2737 selection.start.to_point(buffer) == start.to_point(buffer)
2738 || selection.end.to_point(buffer) == end.to_point(buffer)
2739 });
2740
2741 if let Some(selection) = clicked_point_already_selected {
2742 Some(selection.id)
2743 } else {
2744 None
2745 }
2746 }
2747 };
2748
2749 let selections_count = self.selections.count();
2750
2751 self.change_selections(auto_scroll.then(|| Autoscroll::newest()), cx, |s| {
2752 if let Some(point_to_delete) = point_to_delete {
2753 s.delete(point_to_delete);
2754
2755 if selections_count == 1 {
2756 s.set_pending_anchor_range(start..end, mode);
2757 }
2758 } else {
2759 if !add {
2760 s.clear_disjoint();
2761 } else if click_count > 1 {
2762 s.delete(newest_selection.id)
2763 }
2764
2765 s.set_pending_anchor_range(start..end, mode);
2766 }
2767 });
2768 }
2769
2770 fn begin_columnar_selection(
2771 &mut self,
2772 position: DisplayPoint,
2773 goal_column: u32,
2774 reset: bool,
2775 cx: &mut ViewContext<Self>,
2776 ) {
2777 if !self.focus_handle.is_focused(cx) {
2778 self.last_focused_descendant = None;
2779 cx.focus(&self.focus_handle);
2780 }
2781
2782 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2783
2784 if reset {
2785 let pointer_position = display_map
2786 .buffer_snapshot
2787 .anchor_before(position.to_point(&display_map));
2788
2789 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
2790 s.clear_disjoint();
2791 s.set_pending_anchor_range(
2792 pointer_position..pointer_position,
2793 SelectMode::Character,
2794 );
2795 });
2796 }
2797
2798 let tail = self.selections.newest::<Point>(cx).tail();
2799 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2800
2801 if !reset {
2802 self.select_columns(
2803 tail.to_display_point(&display_map),
2804 position,
2805 goal_column,
2806 &display_map,
2807 cx,
2808 );
2809 }
2810 }
2811
2812 fn update_selection(
2813 &mut self,
2814 position: DisplayPoint,
2815 goal_column: u32,
2816 scroll_delta: gpui::Point<f32>,
2817 cx: &mut ViewContext<Self>,
2818 ) {
2819 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2820
2821 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2822 let tail = tail.to_display_point(&display_map);
2823 self.select_columns(tail, position, goal_column, &display_map, cx);
2824 } else if let Some(mut pending) = self.selections.pending_anchor() {
2825 let buffer = self.buffer.read(cx).snapshot(cx);
2826 let head;
2827 let tail;
2828 let mode = self.selections.pending_mode().unwrap();
2829 match &mode {
2830 SelectMode::Character => {
2831 head = position.to_point(&display_map);
2832 tail = pending.tail().to_point(&buffer);
2833 }
2834 SelectMode::Word(original_range) => {
2835 let original_display_range = original_range.start.to_display_point(&display_map)
2836 ..original_range.end.to_display_point(&display_map);
2837 let original_buffer_range = original_display_range.start.to_point(&display_map)
2838 ..original_display_range.end.to_point(&display_map);
2839 if movement::is_inside_word(&display_map, position)
2840 || original_display_range.contains(&position)
2841 {
2842 let word_range = movement::surrounding_word(&display_map, position);
2843 if word_range.start < original_display_range.start {
2844 head = word_range.start.to_point(&display_map);
2845 } else {
2846 head = word_range.end.to_point(&display_map);
2847 }
2848 } else {
2849 head = position.to_point(&display_map);
2850 }
2851
2852 if head <= original_buffer_range.start {
2853 tail = original_buffer_range.end;
2854 } else {
2855 tail = original_buffer_range.start;
2856 }
2857 }
2858 SelectMode::Line(original_range) => {
2859 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2860
2861 let position = display_map
2862 .clip_point(position, Bias::Left)
2863 .to_point(&display_map);
2864 let line_start = display_map.prev_line_boundary(position).0;
2865 let next_line_start = buffer.clip_point(
2866 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2867 Bias::Left,
2868 );
2869
2870 if line_start < original_range.start {
2871 head = line_start
2872 } else {
2873 head = next_line_start
2874 }
2875
2876 if head <= original_range.start {
2877 tail = original_range.end;
2878 } else {
2879 tail = original_range.start;
2880 }
2881 }
2882 SelectMode::All => {
2883 return;
2884 }
2885 };
2886
2887 if head < tail {
2888 pending.start = buffer.anchor_before(head);
2889 pending.end = buffer.anchor_before(tail);
2890 pending.reversed = true;
2891 } else {
2892 pending.start = buffer.anchor_before(tail);
2893 pending.end = buffer.anchor_before(head);
2894 pending.reversed = false;
2895 }
2896
2897 self.change_selections(None, cx, |s| {
2898 s.set_pending(pending, mode);
2899 });
2900 } else {
2901 log::error!("update_selection dispatched with no pending selection");
2902 return;
2903 }
2904
2905 self.apply_scroll_delta(scroll_delta, cx);
2906 cx.notify();
2907 }
2908
2909 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
2910 self.columnar_selection_tail.take();
2911 if self.selections.pending_anchor().is_some() {
2912 let selections = self.selections.all::<usize>(cx);
2913 self.change_selections(None, cx, |s| {
2914 s.select(selections);
2915 s.clear_pending();
2916 });
2917 }
2918 }
2919
2920 fn select_columns(
2921 &mut self,
2922 tail: DisplayPoint,
2923 head: DisplayPoint,
2924 goal_column: u32,
2925 display_map: &DisplaySnapshot,
2926 cx: &mut ViewContext<Self>,
2927 ) {
2928 let start_row = cmp::min(tail.row(), head.row());
2929 let end_row = cmp::max(tail.row(), head.row());
2930 let start_column = cmp::min(tail.column(), goal_column);
2931 let end_column = cmp::max(tail.column(), goal_column);
2932 let reversed = start_column < tail.column();
2933
2934 let selection_ranges = (start_row.0..=end_row.0)
2935 .map(DisplayRow)
2936 .filter_map(|row| {
2937 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2938 let start = display_map
2939 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2940 .to_point(display_map);
2941 let end = display_map
2942 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2943 .to_point(display_map);
2944 if reversed {
2945 Some(end..start)
2946 } else {
2947 Some(start..end)
2948 }
2949 } else {
2950 None
2951 }
2952 })
2953 .collect::<Vec<_>>();
2954
2955 self.change_selections(None, cx, |s| {
2956 s.select_ranges(selection_ranges);
2957 });
2958 cx.notify();
2959 }
2960
2961 pub fn has_pending_nonempty_selection(&self) -> bool {
2962 let pending_nonempty_selection = match self.selections.pending_anchor() {
2963 Some(Selection { start, end, .. }) => start != end,
2964 None => false,
2965 };
2966
2967 pending_nonempty_selection
2968 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2969 }
2970
2971 pub fn has_pending_selection(&self) -> bool {
2972 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2973 }
2974
2975 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
2976 if self.clear_clicked_diff_hunks(cx) {
2977 cx.notify();
2978 return;
2979 }
2980 if self.dismiss_menus_and_popups(true, cx) {
2981 return;
2982 }
2983
2984 if self.mode == EditorMode::Full {
2985 if self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel()) {
2986 return;
2987 }
2988 }
2989
2990 cx.propagate();
2991 }
2992
2993 pub fn dismiss_menus_and_popups(
2994 &mut self,
2995 should_report_inline_completion_event: bool,
2996 cx: &mut ViewContext<Self>,
2997 ) -> bool {
2998 if self.take_rename(false, cx).is_some() {
2999 return true;
3000 }
3001
3002 if hide_hover(self, cx) {
3003 return true;
3004 }
3005
3006 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
3007 return true;
3008 }
3009
3010 if self.hide_context_menu(cx).is_some() {
3011 return true;
3012 }
3013
3014 if self.mouse_context_menu.take().is_some() {
3015 return true;
3016 }
3017
3018 if self.discard_inline_completion(should_report_inline_completion_event, cx) {
3019 return true;
3020 }
3021
3022 if self.snippet_stack.pop().is_some() {
3023 return true;
3024 }
3025
3026 if self.mode == EditorMode::Full {
3027 if self.active_diagnostics.is_some() {
3028 self.dismiss_diagnostics(cx);
3029 return true;
3030 }
3031 }
3032
3033 false
3034 }
3035
3036 fn linked_editing_ranges_for(
3037 &self,
3038 selection: Range<text::Anchor>,
3039 cx: &AppContext,
3040 ) -> Option<HashMap<Model<Buffer>, Vec<Range<text::Anchor>>>> {
3041 if self.linked_edit_ranges.is_empty() {
3042 return None;
3043 }
3044 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
3045 selection.end.buffer_id.and_then(|end_buffer_id| {
3046 if selection.start.buffer_id != Some(end_buffer_id) {
3047 return None;
3048 }
3049 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
3050 let snapshot = buffer.read(cx).snapshot();
3051 self.linked_edit_ranges
3052 .get(end_buffer_id, selection.start..selection.end, &snapshot)
3053 .map(|ranges| (ranges, snapshot, buffer))
3054 })?;
3055 use text::ToOffset as TO;
3056 // find offset from the start of current range to current cursor position
3057 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
3058
3059 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
3060 let start_difference = start_offset - start_byte_offset;
3061 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
3062 let end_difference = end_offset - start_byte_offset;
3063 // Current range has associated linked ranges.
3064 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3065 for range in linked_ranges.iter() {
3066 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
3067 let end_offset = start_offset + end_difference;
3068 let start_offset = start_offset + start_difference;
3069 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
3070 continue;
3071 }
3072 if self.selections.disjoint_anchor_ranges().iter().any(|s| {
3073 if s.start.buffer_id != selection.start.buffer_id
3074 || s.end.buffer_id != selection.end.buffer_id
3075 {
3076 return false;
3077 }
3078 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
3079 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
3080 }) {
3081 continue;
3082 }
3083 let start = buffer_snapshot.anchor_after(start_offset);
3084 let end = buffer_snapshot.anchor_after(end_offset);
3085 linked_edits
3086 .entry(buffer.clone())
3087 .or_default()
3088 .push(start..end);
3089 }
3090 Some(linked_edits)
3091 }
3092
3093 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3094 let text: Arc<str> = text.into();
3095
3096 if self.read_only(cx) {
3097 return;
3098 }
3099
3100 let selections = self.selections.all_adjusted(cx);
3101 let mut bracket_inserted = false;
3102 let mut edits = Vec::new();
3103 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3104 let mut new_selections = Vec::with_capacity(selections.len());
3105 let mut new_autoclose_regions = Vec::new();
3106 let snapshot = self.buffer.read(cx).read(cx);
3107
3108 for (selection, autoclose_region) in
3109 self.selections_with_autoclose_regions(selections, &snapshot)
3110 {
3111 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
3112 // Determine if the inserted text matches the opening or closing
3113 // bracket of any of this language's bracket pairs.
3114 let mut bracket_pair = None;
3115 let mut is_bracket_pair_start = false;
3116 let mut is_bracket_pair_end = false;
3117 if !text.is_empty() {
3118 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
3119 // and they are removing the character that triggered IME popup.
3120 for (pair, enabled) in scope.brackets() {
3121 if !pair.close && !pair.surround {
3122 continue;
3123 }
3124
3125 if enabled && pair.start.ends_with(text.as_ref()) {
3126 bracket_pair = Some(pair.clone());
3127 is_bracket_pair_start = true;
3128 break;
3129 }
3130 if pair.end.as_str() == text.as_ref() {
3131 bracket_pair = Some(pair.clone());
3132 is_bracket_pair_end = true;
3133 break;
3134 }
3135 }
3136 }
3137
3138 if let Some(bracket_pair) = bracket_pair {
3139 let snapshot_settings = snapshot.settings_at(selection.start, cx);
3140 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
3141 let auto_surround =
3142 self.use_auto_surround && snapshot_settings.use_auto_surround;
3143 if selection.is_empty() {
3144 if is_bracket_pair_start {
3145 let prefix_len = bracket_pair.start.len() - text.len();
3146
3147 // If the inserted text is a suffix of an opening bracket and the
3148 // selection is preceded by the rest of the opening bracket, then
3149 // insert the closing bracket.
3150 let following_text_allows_autoclose = snapshot
3151 .chars_at(selection.start)
3152 .next()
3153 .map_or(true, |c| scope.should_autoclose_before(c));
3154 let preceding_text_matches_prefix = prefix_len == 0
3155 || (selection.start.column >= (prefix_len as u32)
3156 && snapshot.contains_str_at(
3157 Point::new(
3158 selection.start.row,
3159 selection.start.column - (prefix_len as u32),
3160 ),
3161 &bracket_pair.start[..prefix_len],
3162 ));
3163
3164 if autoclose
3165 && bracket_pair.close
3166 && following_text_allows_autoclose
3167 && preceding_text_matches_prefix
3168 {
3169 let anchor = snapshot.anchor_before(selection.end);
3170 new_selections.push((selection.map(|_| anchor), text.len()));
3171 new_autoclose_regions.push((
3172 anchor,
3173 text.len(),
3174 selection.id,
3175 bracket_pair.clone(),
3176 ));
3177 edits.push((
3178 selection.range(),
3179 format!("{}{}", text, bracket_pair.end).into(),
3180 ));
3181 bracket_inserted = true;
3182 continue;
3183 }
3184 }
3185
3186 if let Some(region) = autoclose_region {
3187 // If the selection is followed by an auto-inserted closing bracket,
3188 // then don't insert that closing bracket again; just move the selection
3189 // past the closing bracket.
3190 let should_skip = selection.end == region.range.end.to_point(&snapshot)
3191 && text.as_ref() == region.pair.end.as_str();
3192 if should_skip {
3193 let anchor = snapshot.anchor_after(selection.end);
3194 new_selections
3195 .push((selection.map(|_| anchor), region.pair.end.len()));
3196 continue;
3197 }
3198 }
3199
3200 let always_treat_brackets_as_autoclosed = snapshot
3201 .settings_at(selection.start, cx)
3202 .always_treat_brackets_as_autoclosed;
3203 if always_treat_brackets_as_autoclosed
3204 && is_bracket_pair_end
3205 && snapshot.contains_str_at(selection.end, text.as_ref())
3206 {
3207 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
3208 // and the inserted text is a closing bracket and the selection is followed
3209 // by the closing bracket then move the selection past the closing bracket.
3210 let anchor = snapshot.anchor_after(selection.end);
3211 new_selections.push((selection.map(|_| anchor), text.len()));
3212 continue;
3213 }
3214 }
3215 // If an opening bracket is 1 character long and is typed while
3216 // text is selected, then surround that text with the bracket pair.
3217 else if auto_surround
3218 && bracket_pair.surround
3219 && is_bracket_pair_start
3220 && bracket_pair.start.chars().count() == 1
3221 {
3222 edits.push((selection.start..selection.start, text.clone()));
3223 edits.push((
3224 selection.end..selection.end,
3225 bracket_pair.end.as_str().into(),
3226 ));
3227 bracket_inserted = true;
3228 new_selections.push((
3229 Selection {
3230 id: selection.id,
3231 start: snapshot.anchor_after(selection.start),
3232 end: snapshot.anchor_before(selection.end),
3233 reversed: selection.reversed,
3234 goal: selection.goal,
3235 },
3236 0,
3237 ));
3238 continue;
3239 }
3240 }
3241 }
3242
3243 if self.auto_replace_emoji_shortcode
3244 && selection.is_empty()
3245 && text.as_ref().ends_with(':')
3246 {
3247 if let Some(possible_emoji_short_code) =
3248 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
3249 {
3250 if !possible_emoji_short_code.is_empty() {
3251 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
3252 let emoji_shortcode_start = Point::new(
3253 selection.start.row,
3254 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
3255 );
3256
3257 // Remove shortcode from buffer
3258 edits.push((
3259 emoji_shortcode_start..selection.start,
3260 "".to_string().into(),
3261 ));
3262 new_selections.push((
3263 Selection {
3264 id: selection.id,
3265 start: snapshot.anchor_after(emoji_shortcode_start),
3266 end: snapshot.anchor_before(selection.start),
3267 reversed: selection.reversed,
3268 goal: selection.goal,
3269 },
3270 0,
3271 ));
3272
3273 // Insert emoji
3274 let selection_start_anchor = snapshot.anchor_after(selection.start);
3275 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3276 edits.push((selection.start..selection.end, emoji.to_string().into()));
3277
3278 continue;
3279 }
3280 }
3281 }
3282 }
3283
3284 // If not handling any auto-close operation, then just replace the selected
3285 // text with the given input and move the selection to the end of the
3286 // newly inserted text.
3287 let anchor = snapshot.anchor_after(selection.end);
3288 if !self.linked_edit_ranges.is_empty() {
3289 let start_anchor = snapshot.anchor_before(selection.start);
3290
3291 let is_word_char = text.chars().next().map_or(true, |char| {
3292 let scope = snapshot.language_scope_at(start_anchor.to_offset(&snapshot));
3293 let kind = char_kind(&scope, char);
3294
3295 kind == CharKind::Word
3296 });
3297
3298 if is_word_char {
3299 if let Some(ranges) = self
3300 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3301 {
3302 for (buffer, edits) in ranges {
3303 linked_edits
3304 .entry(buffer.clone())
3305 .or_default()
3306 .extend(edits.into_iter().map(|range| (range, text.clone())));
3307 }
3308 }
3309 }
3310 }
3311
3312 new_selections.push((selection.map(|_| anchor), 0));
3313 edits.push((selection.start..selection.end, text.clone()));
3314 }
3315
3316 drop(snapshot);
3317
3318 self.transact(cx, |this, cx| {
3319 this.buffer.update(cx, |buffer, cx| {
3320 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3321 });
3322 for (buffer, edits) in linked_edits {
3323 buffer.update(cx, |buffer, cx| {
3324 let snapshot = buffer.snapshot();
3325 let edits = edits
3326 .into_iter()
3327 .map(|(range, text)| {
3328 use text::ToPoint as TP;
3329 let end_point = TP::to_point(&range.end, &snapshot);
3330 let start_point = TP::to_point(&range.start, &snapshot);
3331 (start_point..end_point, text)
3332 })
3333 .sorted_by_key(|(range, _)| range.start)
3334 .collect::<Vec<_>>();
3335 buffer.edit(edits, None, cx);
3336 })
3337 }
3338 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3339 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3340 let snapshot = this.buffer.read(cx).read(cx);
3341 let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
3342 .zip(new_selection_deltas)
3343 .map(|(selection, delta)| Selection {
3344 id: selection.id,
3345 start: selection.start + delta,
3346 end: selection.end + delta,
3347 reversed: selection.reversed,
3348 goal: SelectionGoal::None,
3349 })
3350 .collect::<Vec<_>>();
3351
3352 let mut i = 0;
3353 for (position, delta, selection_id, pair) in new_autoclose_regions {
3354 let position = position.to_offset(&snapshot) + delta;
3355 let start = snapshot.anchor_before(position);
3356 let end = snapshot.anchor_after(position);
3357 while let Some(existing_state) = this.autoclose_regions.get(i) {
3358 match existing_state.range.start.cmp(&start, &snapshot) {
3359 Ordering::Less => i += 1,
3360 Ordering::Greater => break,
3361 Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
3362 Ordering::Less => i += 1,
3363 Ordering::Equal => break,
3364 Ordering::Greater => break,
3365 },
3366 }
3367 }
3368 this.autoclose_regions.insert(
3369 i,
3370 AutocloseRegion {
3371 selection_id,
3372 range: start..end,
3373 pair,
3374 },
3375 );
3376 }
3377
3378 drop(snapshot);
3379 let had_active_inline_completion = this.has_active_inline_completion(cx);
3380 this.change_selections_inner(Some(Autoscroll::fit()), false, cx, |s| {
3381 s.select(new_selections)
3382 });
3383
3384 if !bracket_inserted && EditorSettings::get_global(cx).use_on_type_format {
3385 if let Some(on_type_format_task) =
3386 this.trigger_on_type_formatting(text.to_string(), cx)
3387 {
3388 on_type_format_task.detach_and_log_err(cx);
3389 }
3390 }
3391
3392 let editor_settings = EditorSettings::get_global(cx);
3393 if bracket_inserted
3394 && (editor_settings.auto_signature_help
3395 || editor_settings.show_signature_help_after_edits)
3396 {
3397 this.show_signature_help(&ShowSignatureHelp, cx);
3398 }
3399
3400 let trigger_in_words = !had_active_inline_completion;
3401 this.trigger_completion_on_input(&text, trigger_in_words, cx);
3402 linked_editing_ranges::refresh_linked_ranges(this, cx);
3403 this.refresh_inline_completion(true, false, cx);
3404 });
3405 }
3406
3407 fn find_possible_emoji_shortcode_at_position(
3408 snapshot: &MultiBufferSnapshot,
3409 position: Point,
3410 ) -> Option<String> {
3411 let mut chars = Vec::new();
3412 let mut found_colon = false;
3413 for char in snapshot.reversed_chars_at(position).take(100) {
3414 // Found a possible emoji shortcode in the middle of the buffer
3415 if found_colon {
3416 if char.is_whitespace() {
3417 chars.reverse();
3418 return Some(chars.iter().collect());
3419 }
3420 // If the previous character is not a whitespace, we are in the middle of a word
3421 // and we only want to complete the shortcode if the word is made up of other emojis
3422 let mut containing_word = String::new();
3423 for ch in snapshot
3424 .reversed_chars_at(position)
3425 .skip(chars.len() + 1)
3426 .take(100)
3427 {
3428 if ch.is_whitespace() {
3429 break;
3430 }
3431 containing_word.push(ch);
3432 }
3433 let containing_word = containing_word.chars().rev().collect::<String>();
3434 if util::word_consists_of_emojis(containing_word.as_str()) {
3435 chars.reverse();
3436 return Some(chars.iter().collect());
3437 }
3438 }
3439
3440 if char.is_whitespace() || !char.is_ascii() {
3441 return None;
3442 }
3443 if char == ':' {
3444 found_colon = true;
3445 } else {
3446 chars.push(char);
3447 }
3448 }
3449 // Found a possible emoji shortcode at the beginning of the buffer
3450 chars.reverse();
3451 Some(chars.iter().collect())
3452 }
3453
3454 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
3455 self.transact(cx, |this, cx| {
3456 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3457 let selections = this.selections.all::<usize>(cx);
3458 let multi_buffer = this.buffer.read(cx);
3459 let buffer = multi_buffer.snapshot(cx);
3460 selections
3461 .iter()
3462 .map(|selection| {
3463 let start_point = selection.start.to_point(&buffer);
3464 let mut indent =
3465 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3466 indent.len = cmp::min(indent.len, start_point.column);
3467 let start = selection.start;
3468 let end = selection.end;
3469 let selection_is_empty = start == end;
3470 let language_scope = buffer.language_scope_at(start);
3471 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3472 &language_scope
3473 {
3474 let leading_whitespace_len = buffer
3475 .reversed_chars_at(start)
3476 .take_while(|c| c.is_whitespace() && *c != '\n')
3477 .map(|c| c.len_utf8())
3478 .sum::<usize>();
3479
3480 let trailing_whitespace_len = buffer
3481 .chars_at(end)
3482 .take_while(|c| c.is_whitespace() && *c != '\n')
3483 .map(|c| c.len_utf8())
3484 .sum::<usize>();
3485
3486 let insert_extra_newline =
3487 language.brackets().any(|(pair, enabled)| {
3488 let pair_start = pair.start.trim_end();
3489 let pair_end = pair.end.trim_start();
3490
3491 enabled
3492 && pair.newline
3493 && buffer.contains_str_at(
3494 end + trailing_whitespace_len,
3495 pair_end,
3496 )
3497 && buffer.contains_str_at(
3498 (start - leading_whitespace_len)
3499 .saturating_sub(pair_start.len()),
3500 pair_start,
3501 )
3502 });
3503
3504 // Comment extension on newline is allowed only for cursor selections
3505 let comment_delimiter = maybe!({
3506 if !selection_is_empty {
3507 return None;
3508 }
3509
3510 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
3511 return None;
3512 }
3513
3514 let delimiters = language.line_comment_prefixes();
3515 let max_len_of_delimiter =
3516 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3517 let (snapshot, range) =
3518 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3519
3520 let mut index_of_first_non_whitespace = 0;
3521 let comment_candidate = snapshot
3522 .chars_for_range(range)
3523 .skip_while(|c| {
3524 let should_skip = c.is_whitespace();
3525 if should_skip {
3526 index_of_first_non_whitespace += 1;
3527 }
3528 should_skip
3529 })
3530 .take(max_len_of_delimiter)
3531 .collect::<String>();
3532 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3533 comment_candidate.starts_with(comment_prefix.as_ref())
3534 })?;
3535 let cursor_is_placed_after_comment_marker =
3536 index_of_first_non_whitespace + comment_prefix.len()
3537 <= start_point.column as usize;
3538 if cursor_is_placed_after_comment_marker {
3539 Some(comment_prefix.clone())
3540 } else {
3541 None
3542 }
3543 });
3544 (comment_delimiter, insert_extra_newline)
3545 } else {
3546 (None, false)
3547 };
3548
3549 let capacity_for_delimiter = comment_delimiter
3550 .as_deref()
3551 .map(str::len)
3552 .unwrap_or_default();
3553 let mut new_text =
3554 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3555 new_text.push_str("\n");
3556 new_text.extend(indent.chars());
3557 if let Some(delimiter) = &comment_delimiter {
3558 new_text.push_str(&delimiter);
3559 }
3560 if insert_extra_newline {
3561 new_text = new_text.repeat(2);
3562 }
3563
3564 let anchor = buffer.anchor_after(end);
3565 let new_selection = selection.map(|_| anchor);
3566 (
3567 (start..end, new_text),
3568 (insert_extra_newline, new_selection),
3569 )
3570 })
3571 .unzip()
3572 };
3573
3574 this.edit_with_autoindent(edits, cx);
3575 let buffer = this.buffer.read(cx).snapshot(cx);
3576 let new_selections = selection_fixup_info
3577 .into_iter()
3578 .map(|(extra_newline_inserted, new_selection)| {
3579 let mut cursor = new_selection.end.to_point(&buffer);
3580 if extra_newline_inserted {
3581 cursor.row -= 1;
3582 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3583 }
3584 new_selection.map(|_| cursor)
3585 })
3586 .collect();
3587
3588 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
3589 this.refresh_inline_completion(true, false, cx);
3590 });
3591 }
3592
3593 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
3594 let buffer = self.buffer.read(cx);
3595 let snapshot = buffer.snapshot(cx);
3596
3597 let mut edits = Vec::new();
3598 let mut rows = Vec::new();
3599
3600 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3601 let cursor = selection.head();
3602 let row = cursor.row;
3603
3604 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3605
3606 let newline = "\n".to_string();
3607 edits.push((start_of_line..start_of_line, newline));
3608
3609 rows.push(row + rows_inserted as u32);
3610 }
3611
3612 self.transact(cx, |editor, cx| {
3613 editor.edit(edits, cx);
3614
3615 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3616 let mut index = 0;
3617 s.move_cursors_with(|map, _, _| {
3618 let row = rows[index];
3619 index += 1;
3620
3621 let point = Point::new(row, 0);
3622 let boundary = map.next_line_boundary(point).1;
3623 let clipped = map.clip_point(boundary, Bias::Left);
3624
3625 (clipped, SelectionGoal::None)
3626 });
3627 });
3628
3629 let mut indent_edits = Vec::new();
3630 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3631 for row in rows {
3632 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3633 for (row, indent) in indents {
3634 if indent.len == 0 {
3635 continue;
3636 }
3637
3638 let text = match indent.kind {
3639 IndentKind::Space => " ".repeat(indent.len as usize),
3640 IndentKind::Tab => "\t".repeat(indent.len as usize),
3641 };
3642 let point = Point::new(row.0, 0);
3643 indent_edits.push((point..point, text));
3644 }
3645 }
3646 editor.edit(indent_edits, cx);
3647 });
3648 }
3649
3650 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
3651 let buffer = self.buffer.read(cx);
3652 let snapshot = buffer.snapshot(cx);
3653
3654 let mut edits = Vec::new();
3655 let mut rows = Vec::new();
3656 let mut rows_inserted = 0;
3657
3658 for selection in self.selections.all_adjusted(cx) {
3659 let cursor = selection.head();
3660 let row = cursor.row;
3661
3662 let point = Point::new(row + 1, 0);
3663 let start_of_line = snapshot.clip_point(point, Bias::Left);
3664
3665 let newline = "\n".to_string();
3666 edits.push((start_of_line..start_of_line, newline));
3667
3668 rows_inserted += 1;
3669 rows.push(row + rows_inserted);
3670 }
3671
3672 self.transact(cx, |editor, cx| {
3673 editor.edit(edits, cx);
3674
3675 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3676 let mut index = 0;
3677 s.move_cursors_with(|map, _, _| {
3678 let row = rows[index];
3679 index += 1;
3680
3681 let point = Point::new(row, 0);
3682 let boundary = map.next_line_boundary(point).1;
3683 let clipped = map.clip_point(boundary, Bias::Left);
3684
3685 (clipped, SelectionGoal::None)
3686 });
3687 });
3688
3689 let mut indent_edits = Vec::new();
3690 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3691 for row in rows {
3692 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3693 for (row, indent) in indents {
3694 if indent.len == 0 {
3695 continue;
3696 }
3697
3698 let text = match indent.kind {
3699 IndentKind::Space => " ".repeat(indent.len as usize),
3700 IndentKind::Tab => "\t".repeat(indent.len as usize),
3701 };
3702 let point = Point::new(row.0, 0);
3703 indent_edits.push((point..point, text));
3704 }
3705 }
3706 editor.edit(indent_edits, cx);
3707 });
3708 }
3709
3710 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3711 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3712 original_indent_columns: Vec::new(),
3713 });
3714 self.insert_with_autoindent_mode(text, autoindent, cx);
3715 }
3716
3717 fn insert_with_autoindent_mode(
3718 &mut self,
3719 text: &str,
3720 autoindent_mode: Option<AutoindentMode>,
3721 cx: &mut ViewContext<Self>,
3722 ) {
3723 if self.read_only(cx) {
3724 return;
3725 }
3726
3727 let text: Arc<str> = text.into();
3728 self.transact(cx, |this, cx| {
3729 let old_selections = this.selections.all_adjusted(cx);
3730 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3731 let anchors = {
3732 let snapshot = buffer.read(cx);
3733 old_selections
3734 .iter()
3735 .map(|s| {
3736 let anchor = snapshot.anchor_after(s.head());
3737 s.map(|_| anchor)
3738 })
3739 .collect::<Vec<_>>()
3740 };
3741 buffer.edit(
3742 old_selections
3743 .iter()
3744 .map(|s| (s.start..s.end, text.clone())),
3745 autoindent_mode,
3746 cx,
3747 );
3748 anchors
3749 });
3750
3751 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3752 s.select_anchors(selection_anchors);
3753 })
3754 });
3755 }
3756
3757 fn trigger_completion_on_input(
3758 &mut self,
3759 text: &str,
3760 trigger_in_words: bool,
3761 cx: &mut ViewContext<Self>,
3762 ) {
3763 if self.is_completion_trigger(text, trigger_in_words, cx) {
3764 self.show_completions(
3765 &ShowCompletions {
3766 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3767 },
3768 cx,
3769 );
3770 } else {
3771 self.hide_context_menu(cx);
3772 }
3773 }
3774
3775 fn is_completion_trigger(
3776 &self,
3777 text: &str,
3778 trigger_in_words: bool,
3779 cx: &mut ViewContext<Self>,
3780 ) -> bool {
3781 let position = self.selections.newest_anchor().head();
3782 let multibuffer = self.buffer.read(cx);
3783 let Some(buffer) = position
3784 .buffer_id
3785 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3786 else {
3787 return false;
3788 };
3789
3790 if let Some(completion_provider) = &self.completion_provider {
3791 completion_provider.is_completion_trigger(
3792 &buffer,
3793 position.text_anchor,
3794 text,
3795 trigger_in_words,
3796 cx,
3797 )
3798 } else {
3799 false
3800 }
3801 }
3802
3803 /// If any empty selections is touching the start of its innermost containing autoclose
3804 /// region, expand it to select the brackets.
3805 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
3806 let selections = self.selections.all::<usize>(cx);
3807 let buffer = self.buffer.read(cx).read(cx);
3808 let new_selections = self
3809 .selections_with_autoclose_regions(selections, &buffer)
3810 .map(|(mut selection, region)| {
3811 if !selection.is_empty() {
3812 return selection;
3813 }
3814
3815 if let Some(region) = region {
3816 let mut range = region.range.to_offset(&buffer);
3817 if selection.start == range.start && range.start >= region.pair.start.len() {
3818 range.start -= region.pair.start.len();
3819 if buffer.contains_str_at(range.start, ®ion.pair.start)
3820 && buffer.contains_str_at(range.end, ®ion.pair.end)
3821 {
3822 range.end += region.pair.end.len();
3823 selection.start = range.start;
3824 selection.end = range.end;
3825
3826 return selection;
3827 }
3828 }
3829 }
3830
3831 let always_treat_brackets_as_autoclosed = buffer
3832 .settings_at(selection.start, cx)
3833 .always_treat_brackets_as_autoclosed;
3834
3835 if !always_treat_brackets_as_autoclosed {
3836 return selection;
3837 }
3838
3839 if let Some(scope) = buffer.language_scope_at(selection.start) {
3840 for (pair, enabled) in scope.brackets() {
3841 if !enabled || !pair.close {
3842 continue;
3843 }
3844
3845 if buffer.contains_str_at(selection.start, &pair.end) {
3846 let pair_start_len = pair.start.len();
3847 if buffer.contains_str_at(selection.start - pair_start_len, &pair.start)
3848 {
3849 selection.start -= pair_start_len;
3850 selection.end += pair.end.len();
3851
3852 return selection;
3853 }
3854 }
3855 }
3856 }
3857
3858 selection
3859 })
3860 .collect();
3861
3862 drop(buffer);
3863 self.change_selections(None, cx, |selections| selections.select(new_selections));
3864 }
3865
3866 /// Iterate the given selections, and for each one, find the smallest surrounding
3867 /// autoclose region. This uses the ordering of the selections and the autoclose
3868 /// regions to avoid repeated comparisons.
3869 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3870 &'a self,
3871 selections: impl IntoIterator<Item = Selection<D>>,
3872 buffer: &'a MultiBufferSnapshot,
3873 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3874 let mut i = 0;
3875 let mut regions = self.autoclose_regions.as_slice();
3876 selections.into_iter().map(move |selection| {
3877 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3878
3879 let mut enclosing = None;
3880 while let Some(pair_state) = regions.get(i) {
3881 if pair_state.range.end.to_offset(buffer) < range.start {
3882 regions = ®ions[i + 1..];
3883 i = 0;
3884 } else if pair_state.range.start.to_offset(buffer) > range.end {
3885 break;
3886 } else {
3887 if pair_state.selection_id == selection.id {
3888 enclosing = Some(pair_state);
3889 }
3890 i += 1;
3891 }
3892 }
3893
3894 (selection.clone(), enclosing)
3895 })
3896 }
3897
3898 /// Remove any autoclose regions that no longer contain their selection.
3899 fn invalidate_autoclose_regions(
3900 &mut self,
3901 mut selections: &[Selection<Anchor>],
3902 buffer: &MultiBufferSnapshot,
3903 ) {
3904 self.autoclose_regions.retain(|state| {
3905 let mut i = 0;
3906 while let Some(selection) = selections.get(i) {
3907 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3908 selections = &selections[1..];
3909 continue;
3910 }
3911 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3912 break;
3913 }
3914 if selection.id == state.selection_id {
3915 return true;
3916 } else {
3917 i += 1;
3918 }
3919 }
3920 false
3921 });
3922 }
3923
3924 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3925 let offset = position.to_offset(buffer);
3926 let (word_range, kind) = buffer.surrounding_word(offset);
3927 if offset > word_range.start && kind == Some(CharKind::Word) {
3928 Some(
3929 buffer
3930 .text_for_range(word_range.start..offset)
3931 .collect::<String>(),
3932 )
3933 } else {
3934 None
3935 }
3936 }
3937
3938 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
3939 self.refresh_inlay_hints(
3940 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
3941 cx,
3942 );
3943 }
3944
3945 pub fn inlay_hints_enabled(&self) -> bool {
3946 self.inlay_hint_cache.enabled
3947 }
3948
3949 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
3950 if self.project.is_none() || self.mode != EditorMode::Full {
3951 return;
3952 }
3953
3954 let reason_description = reason.description();
3955 let ignore_debounce = matches!(
3956 reason,
3957 InlayHintRefreshReason::SettingsChange(_)
3958 | InlayHintRefreshReason::Toggle(_)
3959 | InlayHintRefreshReason::ExcerptsRemoved(_)
3960 );
3961 let (invalidate_cache, required_languages) = match reason {
3962 InlayHintRefreshReason::Toggle(enabled) => {
3963 self.inlay_hint_cache.enabled = enabled;
3964 if enabled {
3965 (InvalidationStrategy::RefreshRequested, None)
3966 } else {
3967 self.inlay_hint_cache.clear();
3968 self.splice_inlays(
3969 self.visible_inlay_hints(cx)
3970 .iter()
3971 .map(|inlay| inlay.id)
3972 .collect(),
3973 Vec::new(),
3974 cx,
3975 );
3976 return;
3977 }
3978 }
3979 InlayHintRefreshReason::SettingsChange(new_settings) => {
3980 match self.inlay_hint_cache.update_settings(
3981 &self.buffer,
3982 new_settings,
3983 self.visible_inlay_hints(cx),
3984 cx,
3985 ) {
3986 ControlFlow::Break(Some(InlaySplice {
3987 to_remove,
3988 to_insert,
3989 })) => {
3990 self.splice_inlays(to_remove, to_insert, cx);
3991 return;
3992 }
3993 ControlFlow::Break(None) => return,
3994 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3995 }
3996 }
3997 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3998 if let Some(InlaySplice {
3999 to_remove,
4000 to_insert,
4001 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
4002 {
4003 self.splice_inlays(to_remove, to_insert, cx);
4004 }
4005 return;
4006 }
4007 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
4008 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
4009 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
4010 }
4011 InlayHintRefreshReason::RefreshRequested => {
4012 (InvalidationStrategy::RefreshRequested, None)
4013 }
4014 };
4015
4016 if let Some(InlaySplice {
4017 to_remove,
4018 to_insert,
4019 }) = self.inlay_hint_cache.spawn_hint_refresh(
4020 reason_description,
4021 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
4022 invalidate_cache,
4023 ignore_debounce,
4024 cx,
4025 ) {
4026 self.splice_inlays(to_remove, to_insert, cx);
4027 }
4028 }
4029
4030 fn visible_inlay_hints(&self, cx: &ViewContext<'_, Editor>) -> Vec<Inlay> {
4031 self.display_map
4032 .read(cx)
4033 .current_inlays()
4034 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
4035 .cloned()
4036 .collect()
4037 }
4038
4039 pub fn excerpts_for_inlay_hints_query(
4040 &self,
4041 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
4042 cx: &mut ViewContext<Editor>,
4043 ) -> HashMap<ExcerptId, (Model<Buffer>, clock::Global, Range<usize>)> {
4044 let Some(project) = self.project.as_ref() else {
4045 return HashMap::default();
4046 };
4047 let project = project.read(cx);
4048 let multi_buffer = self.buffer().read(cx);
4049 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
4050 let multi_buffer_visible_start = self
4051 .scroll_manager
4052 .anchor()
4053 .anchor
4054 .to_point(&multi_buffer_snapshot);
4055 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
4056 multi_buffer_visible_start
4057 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
4058 Bias::Left,
4059 );
4060 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
4061 multi_buffer
4062 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
4063 .into_iter()
4064 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
4065 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
4066 let buffer = buffer_handle.read(cx);
4067 let buffer_file = project::File::from_dyn(buffer.file())?;
4068 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
4069 let worktree_entry = buffer_worktree
4070 .read(cx)
4071 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
4072 if worktree_entry.is_ignored {
4073 return None;
4074 }
4075
4076 let language = buffer.language()?;
4077 if let Some(restrict_to_languages) = restrict_to_languages {
4078 if !restrict_to_languages.contains(language) {
4079 return None;
4080 }
4081 }
4082 Some((
4083 excerpt_id,
4084 (
4085 buffer_handle,
4086 buffer.version().clone(),
4087 excerpt_visible_range,
4088 ),
4089 ))
4090 })
4091 .collect()
4092 }
4093
4094 pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
4095 TextLayoutDetails {
4096 text_system: cx.text_system().clone(),
4097 editor_style: self.style.clone().unwrap(),
4098 rem_size: cx.rem_size(),
4099 scroll_anchor: self.scroll_manager.anchor(),
4100 visible_rows: self.visible_line_count(),
4101 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
4102 }
4103 }
4104
4105 fn splice_inlays(
4106 &self,
4107 to_remove: Vec<InlayId>,
4108 to_insert: Vec<Inlay>,
4109 cx: &mut ViewContext<Self>,
4110 ) {
4111 self.display_map.update(cx, |display_map, cx| {
4112 display_map.splice_inlays(to_remove, to_insert, cx);
4113 });
4114 cx.notify();
4115 }
4116
4117 fn trigger_on_type_formatting(
4118 &self,
4119 input: String,
4120 cx: &mut ViewContext<Self>,
4121 ) -> Option<Task<Result<()>>> {
4122 if input.len() != 1 {
4123 return None;
4124 }
4125
4126 let project = self.project.as_ref()?;
4127 let position = self.selections.newest_anchor().head();
4128 let (buffer, buffer_position) = self
4129 .buffer
4130 .read(cx)
4131 .text_anchor_for_position(position, cx)?;
4132
4133 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
4134 // hence we do LSP request & edit on host side only — add formats to host's history.
4135 let push_to_lsp_host_history = true;
4136 // If this is not the host, append its history with new edits.
4137 let push_to_client_history = project.read(cx).is_via_collab();
4138
4139 let on_type_formatting = project.update(cx, |project, cx| {
4140 project.on_type_format(
4141 buffer.clone(),
4142 buffer_position,
4143 input,
4144 push_to_lsp_host_history,
4145 cx,
4146 )
4147 });
4148 Some(cx.spawn(|editor, mut cx| async move {
4149 if let Some(transaction) = on_type_formatting.await? {
4150 if push_to_client_history {
4151 buffer
4152 .update(&mut cx, |buffer, _| {
4153 buffer.push_transaction(transaction, Instant::now());
4154 })
4155 .ok();
4156 }
4157 editor.update(&mut cx, |editor, cx| {
4158 editor.refresh_document_highlights(cx);
4159 })?;
4160 }
4161 Ok(())
4162 }))
4163 }
4164
4165 pub fn show_completions(&mut self, options: &ShowCompletions, cx: &mut ViewContext<Self>) {
4166 if self.pending_rename.is_some() {
4167 return;
4168 }
4169
4170 let Some(provider) = self.completion_provider.as_ref() else {
4171 return;
4172 };
4173
4174 let position = self.selections.newest_anchor().head();
4175 let (buffer, buffer_position) =
4176 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
4177 output
4178 } else {
4179 return;
4180 };
4181
4182 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
4183 let is_followup_invoke = {
4184 let context_menu_state = self.context_menu.read();
4185 matches!(
4186 context_menu_state.deref(),
4187 Some(ContextMenu::Completions(_))
4188 )
4189 };
4190 let trigger_kind = match (&options.trigger, is_followup_invoke) {
4191 (_, true) => CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS,
4192 (Some(trigger), _) if buffer.read(cx).completion_triggers().contains(&trigger) => {
4193 CompletionTriggerKind::TRIGGER_CHARACTER
4194 }
4195
4196 _ => CompletionTriggerKind::INVOKED,
4197 };
4198 let completion_context = CompletionContext {
4199 trigger_character: options.trigger.as_ref().and_then(|trigger| {
4200 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
4201 Some(String::from(trigger))
4202 } else {
4203 None
4204 }
4205 }),
4206 trigger_kind,
4207 };
4208 let completions = provider.completions(&buffer, buffer_position, completion_context, cx);
4209 let sort_completions = provider.sort_completions();
4210
4211 let id = post_inc(&mut self.next_completion_id);
4212 let task = cx.spawn(|this, mut cx| {
4213 async move {
4214 this.update(&mut cx, |this, _| {
4215 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
4216 })?;
4217 let completions = completions.await.log_err();
4218 let menu = if let Some(completions) = completions {
4219 let mut menu = CompletionsMenu {
4220 id,
4221 sort_completions,
4222 initial_position: position,
4223 match_candidates: completions
4224 .iter()
4225 .enumerate()
4226 .map(|(id, completion)| {
4227 StringMatchCandidate::new(
4228 id,
4229 completion.label.text[completion.label.filter_range.clone()]
4230 .into(),
4231 )
4232 })
4233 .collect(),
4234 buffer: buffer.clone(),
4235 completions: Arc::new(RwLock::new(completions.into())),
4236 matches: Vec::new().into(),
4237 selected_item: 0,
4238 scroll_handle: UniformListScrollHandle::new(),
4239 selected_completion_documentation_resolve_debounce: Arc::new(Mutex::new(
4240 DebouncedDelay::new(),
4241 )),
4242 };
4243 menu.filter(query.as_deref(), cx.background_executor().clone())
4244 .await;
4245
4246 if menu.matches.is_empty() {
4247 None
4248 } else {
4249 this.update(&mut cx, |editor, cx| {
4250 let completions = menu.completions.clone();
4251 let matches = menu.matches.clone();
4252
4253 let delay_ms = EditorSettings::get_global(cx)
4254 .completion_documentation_secondary_query_debounce;
4255 let delay = Duration::from_millis(delay_ms);
4256 editor
4257 .completion_documentation_pre_resolve_debounce
4258 .fire_new(delay, cx, |editor, cx| {
4259 CompletionsMenu::pre_resolve_completion_documentation(
4260 buffer,
4261 completions,
4262 matches,
4263 editor,
4264 cx,
4265 )
4266 });
4267 })
4268 .ok();
4269 Some(menu)
4270 }
4271 } else {
4272 None
4273 };
4274
4275 this.update(&mut cx, |this, cx| {
4276 let mut context_menu = this.context_menu.write();
4277 match context_menu.as_ref() {
4278 None => {}
4279
4280 Some(ContextMenu::Completions(prev_menu)) => {
4281 if prev_menu.id > id {
4282 return;
4283 }
4284 }
4285
4286 _ => return,
4287 }
4288
4289 if this.focus_handle.is_focused(cx) && menu.is_some() {
4290 let menu = menu.unwrap();
4291 *context_menu = Some(ContextMenu::Completions(menu));
4292 drop(context_menu);
4293 this.discard_inline_completion(false, cx);
4294 cx.notify();
4295 } else if this.completion_tasks.len() <= 1 {
4296 // If there are no more completion tasks and the last menu was
4297 // empty, we should hide it. If it was already hidden, we should
4298 // also show the copilot completion when available.
4299 drop(context_menu);
4300 if this.hide_context_menu(cx).is_none() {
4301 this.update_visible_inline_completion(cx);
4302 }
4303 }
4304 })?;
4305
4306 Ok::<_, anyhow::Error>(())
4307 }
4308 .log_err()
4309 });
4310
4311 self.completion_tasks.push((id, task));
4312 }
4313
4314 pub fn confirm_completion(
4315 &mut self,
4316 action: &ConfirmCompletion,
4317 cx: &mut ViewContext<Self>,
4318 ) -> Option<Task<Result<()>>> {
4319 self.do_completion(action.item_ix, CompletionIntent::Complete, cx)
4320 }
4321
4322 pub fn compose_completion(
4323 &mut self,
4324 action: &ComposeCompletion,
4325 cx: &mut ViewContext<Self>,
4326 ) -> Option<Task<Result<()>>> {
4327 self.do_completion(action.item_ix, CompletionIntent::Compose, cx)
4328 }
4329
4330 fn do_completion(
4331 &mut self,
4332 item_ix: Option<usize>,
4333 intent: CompletionIntent,
4334 cx: &mut ViewContext<Editor>,
4335 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
4336 use language::ToOffset as _;
4337
4338 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
4339 menu
4340 } else {
4341 return None;
4342 };
4343
4344 let mat = completions_menu
4345 .matches
4346 .get(item_ix.unwrap_or(completions_menu.selected_item))?;
4347 let buffer_handle = completions_menu.buffer;
4348 let completions = completions_menu.completions.read();
4349 let completion = completions.get(mat.candidate_id)?;
4350 cx.stop_propagation();
4351
4352 let snippet;
4353 let text;
4354
4355 if completion.is_snippet() {
4356 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4357 text = snippet.as_ref().unwrap().text.clone();
4358 } else {
4359 snippet = None;
4360 text = completion.new_text.clone();
4361 };
4362 let selections = self.selections.all::<usize>(cx);
4363 let buffer = buffer_handle.read(cx);
4364 let old_range = completion.old_range.to_offset(buffer);
4365 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4366
4367 let newest_selection = self.selections.newest_anchor();
4368 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4369 return None;
4370 }
4371
4372 let lookbehind = newest_selection
4373 .start
4374 .text_anchor
4375 .to_offset(buffer)
4376 .saturating_sub(old_range.start);
4377 let lookahead = old_range
4378 .end
4379 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4380 let mut common_prefix_len = old_text
4381 .bytes()
4382 .zip(text.bytes())
4383 .take_while(|(a, b)| a == b)
4384 .count();
4385
4386 let snapshot = self.buffer.read(cx).snapshot(cx);
4387 let mut range_to_replace: Option<Range<isize>> = None;
4388 let mut ranges = Vec::new();
4389 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4390 for selection in &selections {
4391 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4392 let start = selection.start.saturating_sub(lookbehind);
4393 let end = selection.end + lookahead;
4394 if selection.id == newest_selection.id {
4395 range_to_replace = Some(
4396 ((start + common_prefix_len) as isize - selection.start as isize)
4397 ..(end as isize - selection.start as isize),
4398 );
4399 }
4400 ranges.push(start + common_prefix_len..end);
4401 } else {
4402 common_prefix_len = 0;
4403 ranges.clear();
4404 ranges.extend(selections.iter().map(|s| {
4405 if s.id == newest_selection.id {
4406 range_to_replace = Some(
4407 old_range.start.to_offset_utf16(&snapshot).0 as isize
4408 - selection.start as isize
4409 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4410 - selection.start as isize,
4411 );
4412 old_range.clone()
4413 } else {
4414 s.start..s.end
4415 }
4416 }));
4417 break;
4418 }
4419 if !self.linked_edit_ranges.is_empty() {
4420 let start_anchor = snapshot.anchor_before(selection.head());
4421 let end_anchor = snapshot.anchor_after(selection.tail());
4422 if let Some(ranges) = self
4423 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4424 {
4425 for (buffer, edits) in ranges {
4426 linked_edits.entry(buffer.clone()).or_default().extend(
4427 edits
4428 .into_iter()
4429 .map(|range| (range, text[common_prefix_len..].to_owned())),
4430 );
4431 }
4432 }
4433 }
4434 }
4435 let text = &text[common_prefix_len..];
4436
4437 cx.emit(EditorEvent::InputHandled {
4438 utf16_range_to_replace: range_to_replace,
4439 text: text.into(),
4440 });
4441
4442 self.transact(cx, |this, cx| {
4443 if let Some(mut snippet) = snippet {
4444 snippet.text = text.to_string();
4445 for tabstop in snippet.tabstops.iter_mut().flatten() {
4446 tabstop.start -= common_prefix_len as isize;
4447 tabstop.end -= common_prefix_len as isize;
4448 }
4449
4450 this.insert_snippet(&ranges, snippet, cx).log_err();
4451 } else {
4452 this.buffer.update(cx, |buffer, cx| {
4453 buffer.edit(
4454 ranges.iter().map(|range| (range.clone(), text)),
4455 this.autoindent_mode.clone(),
4456 cx,
4457 );
4458 });
4459 }
4460 for (buffer, edits) in linked_edits {
4461 buffer.update(cx, |buffer, cx| {
4462 let snapshot = buffer.snapshot();
4463 let edits = edits
4464 .into_iter()
4465 .map(|(range, text)| {
4466 use text::ToPoint as TP;
4467 let end_point = TP::to_point(&range.end, &snapshot);
4468 let start_point = TP::to_point(&range.start, &snapshot);
4469 (start_point..end_point, text)
4470 })
4471 .sorted_by_key(|(range, _)| range.start)
4472 .collect::<Vec<_>>();
4473 buffer.edit(edits, None, cx);
4474 })
4475 }
4476
4477 this.refresh_inline_completion(true, false, cx);
4478 });
4479
4480 let show_new_completions_on_confirm = completion
4481 .confirm
4482 .as_ref()
4483 .map_or(false, |confirm| confirm(intent, cx));
4484 if show_new_completions_on_confirm {
4485 self.show_completions(&ShowCompletions { trigger: None }, cx);
4486 }
4487
4488 let provider = self.completion_provider.as_ref()?;
4489 let apply_edits = provider.apply_additional_edits_for_completion(
4490 buffer_handle,
4491 completion.clone(),
4492 true,
4493 cx,
4494 );
4495
4496 let editor_settings = EditorSettings::get_global(cx);
4497 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4498 // After the code completion is finished, users often want to know what signatures are needed.
4499 // so we should automatically call signature_help
4500 self.show_signature_help(&ShowSignatureHelp, cx);
4501 }
4502
4503 Some(cx.foreground_executor().spawn(async move {
4504 apply_edits.await?;
4505 Ok(())
4506 }))
4507 }
4508
4509 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
4510 let mut context_menu = self.context_menu.write();
4511 if let Some(ContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4512 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4513 // Toggle if we're selecting the same one
4514 *context_menu = None;
4515 cx.notify();
4516 return;
4517 } else {
4518 // Otherwise, clear it and start a new one
4519 *context_menu = None;
4520 cx.notify();
4521 }
4522 }
4523 drop(context_menu);
4524 let snapshot = self.snapshot(cx);
4525 let deployed_from_indicator = action.deployed_from_indicator;
4526 let mut task = self.code_actions_task.take();
4527 let action = action.clone();
4528 cx.spawn(|editor, mut cx| async move {
4529 while let Some(prev_task) = task {
4530 prev_task.await;
4531 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4532 }
4533
4534 let spawned_test_task = editor.update(&mut cx, |editor, cx| {
4535 if editor.focus_handle.is_focused(cx) {
4536 let multibuffer_point = action
4537 .deployed_from_indicator
4538 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4539 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4540 let (buffer, buffer_row) = snapshot
4541 .buffer_snapshot
4542 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4543 .and_then(|(buffer_snapshot, range)| {
4544 editor
4545 .buffer
4546 .read(cx)
4547 .buffer(buffer_snapshot.remote_id())
4548 .map(|buffer| (buffer, range.start.row))
4549 })?;
4550 let (_, code_actions) = editor
4551 .available_code_actions
4552 .clone()
4553 .and_then(|(location, code_actions)| {
4554 let snapshot = location.buffer.read(cx).snapshot();
4555 let point_range = location.range.to_point(&snapshot);
4556 let point_range = point_range.start.row..=point_range.end.row;
4557 if point_range.contains(&buffer_row) {
4558 Some((location, code_actions))
4559 } else {
4560 None
4561 }
4562 })
4563 .unzip();
4564 let buffer_id = buffer.read(cx).remote_id();
4565 let tasks = editor
4566 .tasks
4567 .get(&(buffer_id, buffer_row))
4568 .map(|t| Arc::new(t.to_owned()));
4569 if tasks.is_none() && code_actions.is_none() {
4570 return None;
4571 }
4572
4573 editor.completion_tasks.clear();
4574 editor.discard_inline_completion(false, cx);
4575 let task_context =
4576 tasks
4577 .as_ref()
4578 .zip(editor.project.clone())
4579 .map(|(tasks, project)| {
4580 let position = Point::new(buffer_row, tasks.column);
4581 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
4582 let location = Location {
4583 buffer: buffer.clone(),
4584 range: range_start..range_start,
4585 };
4586 // Fill in the environmental variables from the tree-sitter captures
4587 let mut captured_task_variables = TaskVariables::default();
4588 for (capture_name, value) in tasks.extra_variables.clone() {
4589 captured_task_variables.insert(
4590 task::VariableName::Custom(capture_name.into()),
4591 value.clone(),
4592 );
4593 }
4594 project.update(cx, |project, cx| {
4595 project.task_context_for_location(
4596 captured_task_variables,
4597 location,
4598 cx,
4599 )
4600 })
4601 });
4602
4603 Some(cx.spawn(|editor, mut cx| async move {
4604 let task_context = match task_context {
4605 Some(task_context) => task_context.await,
4606 None => None,
4607 };
4608 let resolved_tasks =
4609 tasks.zip(task_context).map(|(tasks, task_context)| {
4610 Arc::new(ResolvedTasks {
4611 templates: tasks
4612 .templates
4613 .iter()
4614 .filter_map(|(kind, template)| {
4615 template
4616 .resolve_task(&kind.to_id_base(), &task_context)
4617 .map(|task| (kind.clone(), task))
4618 })
4619 .collect(),
4620 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4621 multibuffer_point.row,
4622 tasks.column,
4623 )),
4624 })
4625 });
4626 let spawn_straight_away = resolved_tasks
4627 .as_ref()
4628 .map_or(false, |tasks| tasks.templates.len() == 1)
4629 && code_actions
4630 .as_ref()
4631 .map_or(true, |actions| actions.is_empty());
4632 if let Some(task) = editor
4633 .update(&mut cx, |editor, cx| {
4634 *editor.context_menu.write() =
4635 Some(ContextMenu::CodeActions(CodeActionsMenu {
4636 buffer,
4637 actions: CodeActionContents {
4638 tasks: resolved_tasks,
4639 actions: code_actions,
4640 },
4641 selected_item: Default::default(),
4642 scroll_handle: UniformListScrollHandle::default(),
4643 deployed_from_indicator,
4644 }));
4645 if spawn_straight_away {
4646 if let Some(task) = editor.confirm_code_action(
4647 &ConfirmCodeAction { item_ix: Some(0) },
4648 cx,
4649 ) {
4650 cx.notify();
4651 return task;
4652 }
4653 }
4654 cx.notify();
4655 Task::ready(Ok(()))
4656 })
4657 .ok()
4658 {
4659 task.await
4660 } else {
4661 Ok(())
4662 }
4663 }))
4664 } else {
4665 Some(Task::ready(Ok(())))
4666 }
4667 })?;
4668 if let Some(task) = spawned_test_task {
4669 task.await?;
4670 }
4671
4672 Ok::<_, anyhow::Error>(())
4673 })
4674 .detach_and_log_err(cx);
4675 }
4676
4677 pub fn confirm_code_action(
4678 &mut self,
4679 action: &ConfirmCodeAction,
4680 cx: &mut ViewContext<Self>,
4681 ) -> Option<Task<Result<()>>> {
4682 let actions_menu = if let ContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
4683 menu
4684 } else {
4685 return None;
4686 };
4687 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4688 let action = actions_menu.actions.get(action_ix)?;
4689 let title = action.label();
4690 let buffer = actions_menu.buffer;
4691 let workspace = self.workspace()?;
4692
4693 match action {
4694 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4695 workspace.update(cx, |workspace, cx| {
4696 workspace::tasks::schedule_resolved_task(
4697 workspace,
4698 task_source_kind,
4699 resolved_task,
4700 false,
4701 cx,
4702 );
4703
4704 Some(Task::ready(Ok(())))
4705 })
4706 }
4707 CodeActionsItem::CodeAction(action) => {
4708 let apply_code_actions = workspace
4709 .read(cx)
4710 .project()
4711 .clone()
4712 .update(cx, |project, cx| {
4713 project.apply_code_action(buffer, action, true, cx)
4714 });
4715 let workspace = workspace.downgrade();
4716 Some(cx.spawn(|editor, cx| async move {
4717 let project_transaction = apply_code_actions.await?;
4718 Self::open_project_transaction(
4719 &editor,
4720 workspace,
4721 project_transaction,
4722 title,
4723 cx,
4724 )
4725 .await
4726 }))
4727 }
4728 }
4729 }
4730
4731 pub async fn open_project_transaction(
4732 this: &WeakView<Editor>,
4733 workspace: WeakView<Workspace>,
4734 transaction: ProjectTransaction,
4735 title: String,
4736 mut cx: AsyncWindowContext,
4737 ) -> Result<()> {
4738 let replica_id = this.update(&mut cx, |this, cx| this.replica_id(cx))?;
4739
4740 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4741 cx.update(|cx| {
4742 entries.sort_unstable_by_key(|(buffer, _)| {
4743 buffer.read(cx).file().map(|f| f.path().clone())
4744 });
4745 })?;
4746
4747 // If the project transaction's edits are all contained within this editor, then
4748 // avoid opening a new editor to display them.
4749
4750 if let Some((buffer, transaction)) = entries.first() {
4751 if entries.len() == 1 {
4752 let excerpt = this.update(&mut cx, |editor, cx| {
4753 editor
4754 .buffer()
4755 .read(cx)
4756 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4757 })?;
4758 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4759 if excerpted_buffer == *buffer {
4760 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4761 let excerpt_range = excerpt_range.to_offset(buffer);
4762 buffer
4763 .edited_ranges_for_transaction::<usize>(transaction)
4764 .all(|range| {
4765 excerpt_range.start <= range.start
4766 && excerpt_range.end >= range.end
4767 })
4768 })?;
4769
4770 if all_edits_within_excerpt {
4771 return Ok(());
4772 }
4773 }
4774 }
4775 }
4776 } else {
4777 return Ok(());
4778 }
4779
4780 let mut ranges_to_highlight = Vec::new();
4781 let excerpt_buffer = cx.new_model(|cx| {
4782 let mut multibuffer =
4783 MultiBuffer::new(replica_id, Capability::ReadWrite).with_title(title);
4784 for (buffer_handle, transaction) in &entries {
4785 let buffer = buffer_handle.read(cx);
4786 ranges_to_highlight.extend(
4787 multibuffer.push_excerpts_with_context_lines(
4788 buffer_handle.clone(),
4789 buffer
4790 .edited_ranges_for_transaction::<usize>(transaction)
4791 .collect(),
4792 DEFAULT_MULTIBUFFER_CONTEXT,
4793 cx,
4794 ),
4795 );
4796 }
4797 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4798 multibuffer
4799 })?;
4800
4801 workspace.update(&mut cx, |workspace, cx| {
4802 let project = workspace.project().clone();
4803 let editor =
4804 cx.new_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, cx));
4805 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
4806 editor.update(cx, |editor, cx| {
4807 editor.highlight_background::<Self>(
4808 &ranges_to_highlight,
4809 |theme| theme.editor_highlighted_line_background,
4810 cx,
4811 );
4812 });
4813 })?;
4814
4815 Ok(())
4816 }
4817
4818 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4819 let project = self.project.clone()?;
4820 let buffer = self.buffer.read(cx);
4821 let newest_selection = self.selections.newest_anchor().clone();
4822 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4823 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4824 if start_buffer != end_buffer {
4825 return None;
4826 }
4827
4828 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
4829 cx.background_executor()
4830 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4831 .await;
4832
4833 let actions = if let Ok(code_actions) = project.update(&mut cx, |project, cx| {
4834 project.code_actions(&start_buffer, start..end, cx)
4835 }) {
4836 code_actions.await
4837 } else {
4838 Vec::new()
4839 };
4840
4841 this.update(&mut cx, |this, cx| {
4842 this.available_code_actions = if actions.is_empty() {
4843 None
4844 } else {
4845 Some((
4846 Location {
4847 buffer: start_buffer,
4848 range: start..end,
4849 },
4850 actions.into(),
4851 ))
4852 };
4853 cx.notify();
4854 })
4855 .log_err();
4856 }));
4857 None
4858 }
4859
4860 fn start_inline_blame_timer(&mut self, cx: &mut ViewContext<Self>) {
4861 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4862 self.show_git_blame_inline = false;
4863
4864 self.show_git_blame_inline_delay_task = Some(cx.spawn(|this, mut cx| async move {
4865 cx.background_executor().timer(delay).await;
4866
4867 this.update(&mut cx, |this, cx| {
4868 this.show_git_blame_inline = true;
4869 cx.notify();
4870 })
4871 .log_err();
4872 }));
4873 }
4874 }
4875
4876 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4877 if self.pending_rename.is_some() {
4878 return None;
4879 }
4880
4881 let project = self.project.clone()?;
4882 let buffer = self.buffer.read(cx);
4883 let newest_selection = self.selections.newest_anchor().clone();
4884 let cursor_position = newest_selection.head();
4885 let (cursor_buffer, cursor_buffer_position) =
4886 buffer.text_anchor_for_position(cursor_position, cx)?;
4887 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4888 if cursor_buffer != tail_buffer {
4889 return None;
4890 }
4891
4892 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4893 cx.background_executor()
4894 .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT)
4895 .await;
4896
4897 let highlights = if let Some(highlights) = project
4898 .update(&mut cx, |project, cx| {
4899 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4900 })
4901 .log_err()
4902 {
4903 highlights.await.log_err()
4904 } else {
4905 None
4906 };
4907
4908 if let Some(highlights) = highlights {
4909 this.update(&mut cx, |this, cx| {
4910 if this.pending_rename.is_some() {
4911 return;
4912 }
4913
4914 let buffer_id = cursor_position.buffer_id;
4915 let buffer = this.buffer.read(cx);
4916 if !buffer
4917 .text_anchor_for_position(cursor_position, cx)
4918 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4919 {
4920 return;
4921 }
4922
4923 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4924 let mut write_ranges = Vec::new();
4925 let mut read_ranges = Vec::new();
4926 for highlight in highlights {
4927 for (excerpt_id, excerpt_range) in
4928 buffer.excerpts_for_buffer(&cursor_buffer, cx)
4929 {
4930 let start = highlight
4931 .range
4932 .start
4933 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4934 let end = highlight
4935 .range
4936 .end
4937 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4938 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4939 continue;
4940 }
4941
4942 let range = Anchor {
4943 buffer_id,
4944 excerpt_id,
4945 text_anchor: start,
4946 }..Anchor {
4947 buffer_id,
4948 excerpt_id,
4949 text_anchor: end,
4950 };
4951 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4952 write_ranges.push(range);
4953 } else {
4954 read_ranges.push(range);
4955 }
4956 }
4957 }
4958
4959 this.highlight_background::<DocumentHighlightRead>(
4960 &read_ranges,
4961 |theme| theme.editor_document_highlight_read_background,
4962 cx,
4963 );
4964 this.highlight_background::<DocumentHighlightWrite>(
4965 &write_ranges,
4966 |theme| theme.editor_document_highlight_write_background,
4967 cx,
4968 );
4969 cx.notify();
4970 })
4971 .log_err();
4972 }
4973 }));
4974 None
4975 }
4976
4977 pub fn refresh_inline_completion(
4978 &mut self,
4979 debounce: bool,
4980 user_requested: bool,
4981 cx: &mut ViewContext<Self>,
4982 ) -> Option<()> {
4983 let provider = self.inline_completion_provider()?;
4984 let cursor = self.selections.newest_anchor().head();
4985 let (buffer, cursor_buffer_position) =
4986 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4987 if !user_requested
4988 && self.enable_inline_completions
4989 && !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
4990 {
4991 self.discard_inline_completion(false, cx);
4992 return None;
4993 }
4994
4995 self.update_visible_inline_completion(cx);
4996 provider.refresh(buffer, cursor_buffer_position, debounce, cx);
4997 Some(())
4998 }
4999
5000 fn cycle_inline_completion(
5001 &mut self,
5002 direction: Direction,
5003 cx: &mut ViewContext<Self>,
5004 ) -> Option<()> {
5005 let provider = self.inline_completion_provider()?;
5006 let cursor = self.selections.newest_anchor().head();
5007 let (buffer, cursor_buffer_position) =
5008 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5009 if !self.enable_inline_completions
5010 || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
5011 {
5012 return None;
5013 }
5014
5015 provider.cycle(buffer, cursor_buffer_position, direction, cx);
5016 self.update_visible_inline_completion(cx);
5017
5018 Some(())
5019 }
5020
5021 pub fn show_inline_completion(&mut self, _: &ShowInlineCompletion, cx: &mut ViewContext<Self>) {
5022 if !self.has_active_inline_completion(cx) {
5023 self.refresh_inline_completion(false, true, cx);
5024 return;
5025 }
5026
5027 self.update_visible_inline_completion(cx);
5028 }
5029
5030 pub fn display_cursor_names(&mut self, _: &DisplayCursorNames, cx: &mut ViewContext<Self>) {
5031 self.show_cursor_names(cx);
5032 }
5033
5034 fn show_cursor_names(&mut self, cx: &mut ViewContext<Self>) {
5035 self.show_cursor_names = true;
5036 cx.notify();
5037 cx.spawn(|this, mut cx| async move {
5038 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5039 this.update(&mut cx, |this, cx| {
5040 this.show_cursor_names = false;
5041 cx.notify()
5042 })
5043 .ok()
5044 })
5045 .detach();
5046 }
5047
5048 pub fn next_inline_completion(&mut self, _: &NextInlineCompletion, cx: &mut ViewContext<Self>) {
5049 if self.has_active_inline_completion(cx) {
5050 self.cycle_inline_completion(Direction::Next, cx);
5051 } else {
5052 let is_copilot_disabled = self.refresh_inline_completion(false, true, cx).is_none();
5053 if is_copilot_disabled {
5054 cx.propagate();
5055 }
5056 }
5057 }
5058
5059 pub fn previous_inline_completion(
5060 &mut self,
5061 _: &PreviousInlineCompletion,
5062 cx: &mut ViewContext<Self>,
5063 ) {
5064 if self.has_active_inline_completion(cx) {
5065 self.cycle_inline_completion(Direction::Prev, cx);
5066 } else {
5067 let is_copilot_disabled = self.refresh_inline_completion(false, true, cx).is_none();
5068 if is_copilot_disabled {
5069 cx.propagate();
5070 }
5071 }
5072 }
5073
5074 pub fn accept_inline_completion(
5075 &mut self,
5076 _: &AcceptInlineCompletion,
5077 cx: &mut ViewContext<Self>,
5078 ) {
5079 let Some((completion, delete_range)) = self.take_active_inline_completion(cx) else {
5080 return;
5081 };
5082 if let Some(provider) = self.inline_completion_provider() {
5083 provider.accept(cx);
5084 }
5085
5086 cx.emit(EditorEvent::InputHandled {
5087 utf16_range_to_replace: None,
5088 text: completion.text.to_string().into(),
5089 });
5090
5091 if let Some(range) = delete_range {
5092 self.change_selections(None, cx, |s| s.select_ranges([range]))
5093 }
5094 self.insert_with_autoindent_mode(&completion.text.to_string(), None, cx);
5095 self.refresh_inline_completion(true, true, cx);
5096 cx.notify();
5097 }
5098
5099 pub fn accept_partial_inline_completion(
5100 &mut self,
5101 _: &AcceptPartialInlineCompletion,
5102 cx: &mut ViewContext<Self>,
5103 ) {
5104 if self.selections.count() == 1 && self.has_active_inline_completion(cx) {
5105 if let Some((completion, delete_range)) = self.take_active_inline_completion(cx) {
5106 let mut partial_completion = completion
5107 .text
5108 .chars()
5109 .by_ref()
5110 .take_while(|c| c.is_alphabetic())
5111 .collect::<String>();
5112 if partial_completion.is_empty() {
5113 partial_completion = completion
5114 .text
5115 .chars()
5116 .by_ref()
5117 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5118 .collect::<String>();
5119 }
5120
5121 cx.emit(EditorEvent::InputHandled {
5122 utf16_range_to_replace: None,
5123 text: partial_completion.clone().into(),
5124 });
5125
5126 if let Some(range) = delete_range {
5127 self.change_selections(None, cx, |s| s.select_ranges([range]))
5128 }
5129 self.insert_with_autoindent_mode(&partial_completion, None, cx);
5130
5131 self.refresh_inline_completion(true, true, cx);
5132 cx.notify();
5133 }
5134 }
5135 }
5136
5137 fn discard_inline_completion(
5138 &mut self,
5139 should_report_inline_completion_event: bool,
5140 cx: &mut ViewContext<Self>,
5141 ) -> bool {
5142 if let Some(provider) = self.inline_completion_provider() {
5143 provider.discard(should_report_inline_completion_event, cx);
5144 }
5145
5146 self.take_active_inline_completion(cx).is_some()
5147 }
5148
5149 pub fn has_active_inline_completion(&self, cx: &AppContext) -> bool {
5150 if let Some(completion) = self.active_inline_completion.as_ref() {
5151 let buffer = self.buffer.read(cx).read(cx);
5152 completion.0.position.is_valid(&buffer)
5153 } else {
5154 false
5155 }
5156 }
5157
5158 fn take_active_inline_completion(
5159 &mut self,
5160 cx: &mut ViewContext<Self>,
5161 ) -> Option<(Inlay, Option<Range<Anchor>>)> {
5162 let completion = self.active_inline_completion.take()?;
5163 self.display_map.update(cx, |map, cx| {
5164 map.splice_inlays(vec![completion.0.id], Default::default(), cx);
5165 });
5166 let buffer = self.buffer.read(cx).read(cx);
5167
5168 if completion.0.position.is_valid(&buffer) {
5169 Some(completion)
5170 } else {
5171 None
5172 }
5173 }
5174
5175 fn update_visible_inline_completion(&mut self, cx: &mut ViewContext<Self>) {
5176 let selection = self.selections.newest_anchor();
5177 let cursor = selection.head();
5178
5179 let excerpt_id = cursor.excerpt_id;
5180
5181 if self.context_menu.read().is_none()
5182 && self.completion_tasks.is_empty()
5183 && selection.start == selection.end
5184 {
5185 if let Some(provider) = self.inline_completion_provider() {
5186 if let Some((buffer, cursor_buffer_position)) =
5187 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5188 {
5189 if let Some((text, text_anchor_range)) =
5190 provider.active_completion_text(&buffer, cursor_buffer_position, cx)
5191 {
5192 let text = Rope::from(text);
5193 let mut to_remove = Vec::new();
5194 if let Some(completion) = self.active_inline_completion.take() {
5195 to_remove.push(completion.0.id);
5196 }
5197
5198 let completion_inlay =
5199 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
5200
5201 let multibuffer_anchor_range = text_anchor_range.and_then(|range| {
5202 let snapshot = self.buffer.read(cx).snapshot(cx);
5203 Some(
5204 snapshot.anchor_in_excerpt(excerpt_id, range.start)?
5205 ..snapshot.anchor_in_excerpt(excerpt_id, range.end)?,
5206 )
5207 });
5208 self.active_inline_completion =
5209 Some((completion_inlay.clone(), multibuffer_anchor_range));
5210
5211 self.display_map.update(cx, move |map, cx| {
5212 map.splice_inlays(to_remove, vec![completion_inlay], cx)
5213 });
5214 cx.notify();
5215 return;
5216 }
5217 }
5218 }
5219 }
5220
5221 self.discard_inline_completion(false, cx);
5222 }
5223
5224 fn inline_completion_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5225 Some(self.inline_completion_provider.as_ref()?.provider.clone())
5226 }
5227
5228 fn render_code_actions_indicator(
5229 &self,
5230 _style: &EditorStyle,
5231 row: DisplayRow,
5232 is_active: bool,
5233 cx: &mut ViewContext<Self>,
5234 ) -> Option<IconButton> {
5235 if self.available_code_actions.is_some() {
5236 Some(
5237 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5238 .shape(ui::IconButtonShape::Square)
5239 .icon_size(IconSize::XSmall)
5240 .icon_color(Color::Muted)
5241 .selected(is_active)
5242 .on_click(cx.listener(move |editor, _e, cx| {
5243 editor.focus(cx);
5244 editor.toggle_code_actions(
5245 &ToggleCodeActions {
5246 deployed_from_indicator: Some(row),
5247 },
5248 cx,
5249 );
5250 })),
5251 )
5252 } else {
5253 None
5254 }
5255 }
5256
5257 fn clear_tasks(&mut self) {
5258 self.tasks.clear()
5259 }
5260
5261 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5262 if let Some(_) = self.tasks.insert(key, value) {
5263 // This case should hopefully be rare, but just in case...
5264 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5265 }
5266 }
5267
5268 fn render_run_indicator(
5269 &self,
5270 _style: &EditorStyle,
5271 is_active: bool,
5272 row: DisplayRow,
5273 cx: &mut ViewContext<Self>,
5274 ) -> IconButton {
5275 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5276 .shape(ui::IconButtonShape::Square)
5277 .icon_size(IconSize::XSmall)
5278 .icon_color(Color::Muted)
5279 .selected(is_active)
5280 .on_click(cx.listener(move |editor, _e, cx| {
5281 editor.focus(cx);
5282 editor.toggle_code_actions(
5283 &ToggleCodeActions {
5284 deployed_from_indicator: Some(row),
5285 },
5286 cx,
5287 );
5288 }))
5289 }
5290
5291 fn close_hunk_diff_button(
5292 &self,
5293 hunk: HoveredHunk,
5294 row: DisplayRow,
5295 cx: &mut ViewContext<Self>,
5296 ) -> IconButton {
5297 IconButton::new(
5298 ("close_hunk_diff_indicator", row.0 as usize),
5299 ui::IconName::Close,
5300 )
5301 .shape(ui::IconButtonShape::Square)
5302 .icon_size(IconSize::XSmall)
5303 .icon_color(Color::Muted)
5304 .tooltip(|cx| Tooltip::for_action("Close hunk diff", &ToggleHunkDiff, cx))
5305 .on_click(cx.listener(move |editor, _e, cx| editor.toggle_hovered_hunk(&hunk, cx)))
5306 }
5307
5308 pub fn context_menu_visible(&self) -> bool {
5309 self.context_menu
5310 .read()
5311 .as_ref()
5312 .map_or(false, |menu| menu.visible())
5313 }
5314
5315 fn render_context_menu(
5316 &self,
5317 cursor_position: DisplayPoint,
5318 style: &EditorStyle,
5319 max_height: Pixels,
5320 cx: &mut ViewContext<Editor>,
5321 ) -> Option<(ContextMenuOrigin, AnyElement)> {
5322 self.context_menu.read().as_ref().map(|menu| {
5323 menu.render(
5324 cursor_position,
5325 style,
5326 max_height,
5327 self.workspace.as_ref().map(|(w, _)| w.clone()),
5328 cx,
5329 )
5330 })
5331 }
5332
5333 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
5334 cx.notify();
5335 self.completion_tasks.clear();
5336 let context_menu = self.context_menu.write().take();
5337 if context_menu.is_some() {
5338 self.update_visible_inline_completion(cx);
5339 }
5340 context_menu
5341 }
5342
5343 pub fn insert_snippet(
5344 &mut self,
5345 insertion_ranges: &[Range<usize>],
5346 snippet: Snippet,
5347 cx: &mut ViewContext<Self>,
5348 ) -> Result<()> {
5349 struct Tabstop<T> {
5350 is_end_tabstop: bool,
5351 ranges: Vec<Range<T>>,
5352 }
5353
5354 let tabstops = self.buffer.update(cx, |buffer, cx| {
5355 let snippet_text: Arc<str> = snippet.text.clone().into();
5356 buffer.edit(
5357 insertion_ranges
5358 .iter()
5359 .cloned()
5360 .map(|range| (range, snippet_text.clone())),
5361 Some(AutoindentMode::EachLine),
5362 cx,
5363 );
5364
5365 let snapshot = &*buffer.read(cx);
5366 let snippet = &snippet;
5367 snippet
5368 .tabstops
5369 .iter()
5370 .map(|tabstop| {
5371 let is_end_tabstop = tabstop.first().map_or(false, |tabstop| {
5372 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
5373 });
5374 let mut tabstop_ranges = tabstop
5375 .iter()
5376 .flat_map(|tabstop_range| {
5377 let mut delta = 0_isize;
5378 insertion_ranges.iter().map(move |insertion_range| {
5379 let insertion_start = insertion_range.start as isize + delta;
5380 delta +=
5381 snippet.text.len() as isize - insertion_range.len() as isize;
5382
5383 let start = ((insertion_start + tabstop_range.start) as usize)
5384 .min(snapshot.len());
5385 let end = ((insertion_start + tabstop_range.end) as usize)
5386 .min(snapshot.len());
5387 snapshot.anchor_before(start)..snapshot.anchor_after(end)
5388 })
5389 })
5390 .collect::<Vec<_>>();
5391 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
5392
5393 Tabstop {
5394 is_end_tabstop,
5395 ranges: tabstop_ranges,
5396 }
5397 })
5398 .collect::<Vec<_>>()
5399 });
5400 if let Some(tabstop) = tabstops.first() {
5401 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5402 s.select_ranges(tabstop.ranges.iter().cloned());
5403 });
5404
5405 // If we're already at the last tabstop and it's at the end of the snippet,
5406 // we're done, we don't need to keep the state around.
5407 if !tabstop.is_end_tabstop {
5408 let ranges = tabstops
5409 .into_iter()
5410 .map(|tabstop| tabstop.ranges)
5411 .collect::<Vec<_>>();
5412 self.snippet_stack.push(SnippetState {
5413 active_index: 0,
5414 ranges,
5415 });
5416 }
5417
5418 // Check whether the just-entered snippet ends with an auto-closable bracket.
5419 if self.autoclose_regions.is_empty() {
5420 let snapshot = self.buffer.read(cx).snapshot(cx);
5421 for selection in &mut self.selections.all::<Point>(cx) {
5422 let selection_head = selection.head();
5423 let Some(scope) = snapshot.language_scope_at(selection_head) else {
5424 continue;
5425 };
5426
5427 let mut bracket_pair = None;
5428 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
5429 let prev_chars = snapshot
5430 .reversed_chars_at(selection_head)
5431 .collect::<String>();
5432 for (pair, enabled) in scope.brackets() {
5433 if enabled
5434 && pair.close
5435 && prev_chars.starts_with(pair.start.as_str())
5436 && next_chars.starts_with(pair.end.as_str())
5437 {
5438 bracket_pair = Some(pair.clone());
5439 break;
5440 }
5441 }
5442 if let Some(pair) = bracket_pair {
5443 let start = snapshot.anchor_after(selection_head);
5444 let end = snapshot.anchor_after(selection_head);
5445 self.autoclose_regions.push(AutocloseRegion {
5446 selection_id: selection.id,
5447 range: start..end,
5448 pair,
5449 });
5450 }
5451 }
5452 }
5453 }
5454 Ok(())
5455 }
5456
5457 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5458 self.move_to_snippet_tabstop(Bias::Right, cx)
5459 }
5460
5461 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5462 self.move_to_snippet_tabstop(Bias::Left, cx)
5463 }
5464
5465 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
5466 if let Some(mut snippet) = self.snippet_stack.pop() {
5467 match bias {
5468 Bias::Left => {
5469 if snippet.active_index > 0 {
5470 snippet.active_index -= 1;
5471 } else {
5472 self.snippet_stack.push(snippet);
5473 return false;
5474 }
5475 }
5476 Bias::Right => {
5477 if snippet.active_index + 1 < snippet.ranges.len() {
5478 snippet.active_index += 1;
5479 } else {
5480 self.snippet_stack.push(snippet);
5481 return false;
5482 }
5483 }
5484 }
5485 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
5486 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5487 s.select_anchor_ranges(current_ranges.iter().cloned())
5488 });
5489 // If snippet state is not at the last tabstop, push it back on the stack
5490 if snippet.active_index + 1 < snippet.ranges.len() {
5491 self.snippet_stack.push(snippet);
5492 }
5493 return true;
5494 }
5495 }
5496
5497 false
5498 }
5499
5500 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
5501 self.transact(cx, |this, cx| {
5502 this.select_all(&SelectAll, cx);
5503 this.insert("", cx);
5504 });
5505 }
5506
5507 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
5508 self.transact(cx, |this, cx| {
5509 this.select_autoclose_pair(cx);
5510 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
5511 if !this.linked_edit_ranges.is_empty() {
5512 let selections = this.selections.all::<MultiBufferPoint>(cx);
5513 let snapshot = this.buffer.read(cx).snapshot(cx);
5514
5515 for selection in selections.iter() {
5516 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
5517 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
5518 if selection_start.buffer_id != selection_end.buffer_id {
5519 continue;
5520 }
5521 if let Some(ranges) =
5522 this.linked_editing_ranges_for(selection_start..selection_end, cx)
5523 {
5524 for (buffer, entries) in ranges {
5525 linked_ranges.entry(buffer).or_default().extend(entries);
5526 }
5527 }
5528 }
5529 }
5530
5531 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
5532 if !this.selections.line_mode {
5533 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
5534 for selection in &mut selections {
5535 if selection.is_empty() {
5536 let old_head = selection.head();
5537 let mut new_head =
5538 movement::left(&display_map, old_head.to_display_point(&display_map))
5539 .to_point(&display_map);
5540 if let Some((buffer, line_buffer_range)) = display_map
5541 .buffer_snapshot
5542 .buffer_line_for_row(MultiBufferRow(old_head.row))
5543 {
5544 let indent_size =
5545 buffer.indent_size_for_line(line_buffer_range.start.row);
5546 let indent_len = match indent_size.kind {
5547 IndentKind::Space => {
5548 buffer.settings_at(line_buffer_range.start, cx).tab_size
5549 }
5550 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
5551 };
5552 if old_head.column <= indent_size.len && old_head.column > 0 {
5553 let indent_len = indent_len.get();
5554 new_head = cmp::min(
5555 new_head,
5556 MultiBufferPoint::new(
5557 old_head.row,
5558 ((old_head.column - 1) / indent_len) * indent_len,
5559 ),
5560 );
5561 }
5562 }
5563
5564 selection.set_head(new_head, SelectionGoal::None);
5565 }
5566 }
5567 }
5568
5569 this.signature_help_state.set_backspace_pressed(true);
5570 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5571 this.insert("", cx);
5572 let empty_str: Arc<str> = Arc::from("");
5573 for (buffer, edits) in linked_ranges {
5574 let snapshot = buffer.read(cx).snapshot();
5575 use text::ToPoint as TP;
5576
5577 let edits = edits
5578 .into_iter()
5579 .map(|range| {
5580 let end_point = TP::to_point(&range.end, &snapshot);
5581 let mut start_point = TP::to_point(&range.start, &snapshot);
5582
5583 if end_point == start_point {
5584 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
5585 .saturating_sub(1);
5586 start_point = TP::to_point(&offset, &snapshot);
5587 };
5588
5589 (start_point..end_point, empty_str.clone())
5590 })
5591 .sorted_by_key(|(range, _)| range.start)
5592 .collect::<Vec<_>>();
5593 buffer.update(cx, |this, cx| {
5594 this.edit(edits, None, cx);
5595 })
5596 }
5597 this.refresh_inline_completion(true, false, cx);
5598 linked_editing_ranges::refresh_linked_ranges(this, cx);
5599 });
5600 }
5601
5602 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
5603 self.transact(cx, |this, cx| {
5604 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5605 let line_mode = s.line_mode;
5606 s.move_with(|map, selection| {
5607 if selection.is_empty() && !line_mode {
5608 let cursor = movement::right(map, selection.head());
5609 selection.end = cursor;
5610 selection.reversed = true;
5611 selection.goal = SelectionGoal::None;
5612 }
5613 })
5614 });
5615 this.insert("", cx);
5616 this.refresh_inline_completion(true, false, cx);
5617 });
5618 }
5619
5620 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
5621 if self.move_to_prev_snippet_tabstop(cx) {
5622 return;
5623 }
5624
5625 self.outdent(&Outdent, cx);
5626 }
5627
5628 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
5629 if self.move_to_next_snippet_tabstop(cx) || self.read_only(cx) {
5630 return;
5631 }
5632
5633 let mut selections = self.selections.all_adjusted(cx);
5634 let buffer = self.buffer.read(cx);
5635 let snapshot = buffer.snapshot(cx);
5636 let rows_iter = selections.iter().map(|s| s.head().row);
5637 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
5638
5639 let mut edits = Vec::new();
5640 let mut prev_edited_row = 0;
5641 let mut row_delta = 0;
5642 for selection in &mut selections {
5643 if selection.start.row != prev_edited_row {
5644 row_delta = 0;
5645 }
5646 prev_edited_row = selection.end.row;
5647
5648 // If the selection is non-empty, then increase the indentation of the selected lines.
5649 if !selection.is_empty() {
5650 row_delta =
5651 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5652 continue;
5653 }
5654
5655 // If the selection is empty and the cursor is in the leading whitespace before the
5656 // suggested indentation, then auto-indent the line.
5657 let cursor = selection.head();
5658 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
5659 if let Some(suggested_indent) =
5660 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
5661 {
5662 if cursor.column < suggested_indent.len
5663 && cursor.column <= current_indent.len
5664 && current_indent.len <= suggested_indent.len
5665 {
5666 selection.start = Point::new(cursor.row, suggested_indent.len);
5667 selection.end = selection.start;
5668 if row_delta == 0 {
5669 edits.extend(Buffer::edit_for_indent_size_adjustment(
5670 cursor.row,
5671 current_indent,
5672 suggested_indent,
5673 ));
5674 row_delta = suggested_indent.len - current_indent.len;
5675 }
5676 continue;
5677 }
5678 }
5679
5680 // Otherwise, insert a hard or soft tab.
5681 let settings = buffer.settings_at(cursor, cx);
5682 let tab_size = if settings.hard_tabs {
5683 IndentSize::tab()
5684 } else {
5685 let tab_size = settings.tab_size.get();
5686 let char_column = snapshot
5687 .text_for_range(Point::new(cursor.row, 0)..cursor)
5688 .flat_map(str::chars)
5689 .count()
5690 + row_delta as usize;
5691 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
5692 IndentSize::spaces(chars_to_next_tab_stop)
5693 };
5694 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
5695 selection.end = selection.start;
5696 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
5697 row_delta += tab_size.len;
5698 }
5699
5700 self.transact(cx, |this, cx| {
5701 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5702 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5703 this.refresh_inline_completion(true, false, cx);
5704 });
5705 }
5706
5707 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
5708 if self.read_only(cx) {
5709 return;
5710 }
5711 let mut selections = self.selections.all::<Point>(cx);
5712 let mut prev_edited_row = 0;
5713 let mut row_delta = 0;
5714 let mut edits = Vec::new();
5715 let buffer = self.buffer.read(cx);
5716 let snapshot = buffer.snapshot(cx);
5717 for selection in &mut selections {
5718 if selection.start.row != prev_edited_row {
5719 row_delta = 0;
5720 }
5721 prev_edited_row = selection.end.row;
5722
5723 row_delta =
5724 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5725 }
5726
5727 self.transact(cx, |this, cx| {
5728 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5729 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5730 });
5731 }
5732
5733 fn indent_selection(
5734 buffer: &MultiBuffer,
5735 snapshot: &MultiBufferSnapshot,
5736 selection: &mut Selection<Point>,
5737 edits: &mut Vec<(Range<Point>, String)>,
5738 delta_for_start_row: u32,
5739 cx: &AppContext,
5740 ) -> u32 {
5741 let settings = buffer.settings_at(selection.start, cx);
5742 let tab_size = settings.tab_size.get();
5743 let indent_kind = if settings.hard_tabs {
5744 IndentKind::Tab
5745 } else {
5746 IndentKind::Space
5747 };
5748 let mut start_row = selection.start.row;
5749 let mut end_row = selection.end.row + 1;
5750
5751 // If a selection ends at the beginning of a line, don't indent
5752 // that last line.
5753 if selection.end.column == 0 && selection.end.row > selection.start.row {
5754 end_row -= 1;
5755 }
5756
5757 // Avoid re-indenting a row that has already been indented by a
5758 // previous selection, but still update this selection's column
5759 // to reflect that indentation.
5760 if delta_for_start_row > 0 {
5761 start_row += 1;
5762 selection.start.column += delta_for_start_row;
5763 if selection.end.row == selection.start.row {
5764 selection.end.column += delta_for_start_row;
5765 }
5766 }
5767
5768 let mut delta_for_end_row = 0;
5769 let has_multiple_rows = start_row + 1 != end_row;
5770 for row in start_row..end_row {
5771 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
5772 let indent_delta = match (current_indent.kind, indent_kind) {
5773 (IndentKind::Space, IndentKind::Space) => {
5774 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
5775 IndentSize::spaces(columns_to_next_tab_stop)
5776 }
5777 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
5778 (_, IndentKind::Tab) => IndentSize::tab(),
5779 };
5780
5781 let start = if has_multiple_rows || current_indent.len < selection.start.column {
5782 0
5783 } else {
5784 selection.start.column
5785 };
5786 let row_start = Point::new(row, start);
5787 edits.push((
5788 row_start..row_start,
5789 indent_delta.chars().collect::<String>(),
5790 ));
5791
5792 // Update this selection's endpoints to reflect the indentation.
5793 if row == selection.start.row {
5794 selection.start.column += indent_delta.len;
5795 }
5796 if row == selection.end.row {
5797 selection.end.column += indent_delta.len;
5798 delta_for_end_row = indent_delta.len;
5799 }
5800 }
5801
5802 if selection.start.row == selection.end.row {
5803 delta_for_start_row + delta_for_end_row
5804 } else {
5805 delta_for_end_row
5806 }
5807 }
5808
5809 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
5810 if self.read_only(cx) {
5811 return;
5812 }
5813 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5814 let selections = self.selections.all::<Point>(cx);
5815 let mut deletion_ranges = Vec::new();
5816 let mut last_outdent = None;
5817 {
5818 let buffer = self.buffer.read(cx);
5819 let snapshot = buffer.snapshot(cx);
5820 for selection in &selections {
5821 let settings = buffer.settings_at(selection.start, cx);
5822 let tab_size = settings.tab_size.get();
5823 let mut rows = selection.spanned_rows(false, &display_map);
5824
5825 // Avoid re-outdenting a row that has already been outdented by a
5826 // previous selection.
5827 if let Some(last_row) = last_outdent {
5828 if last_row == rows.start {
5829 rows.start = rows.start.next_row();
5830 }
5831 }
5832 let has_multiple_rows = rows.len() > 1;
5833 for row in rows.iter_rows() {
5834 let indent_size = snapshot.indent_size_for_line(row);
5835 if indent_size.len > 0 {
5836 let deletion_len = match indent_size.kind {
5837 IndentKind::Space => {
5838 let columns_to_prev_tab_stop = indent_size.len % tab_size;
5839 if columns_to_prev_tab_stop == 0 {
5840 tab_size
5841 } else {
5842 columns_to_prev_tab_stop
5843 }
5844 }
5845 IndentKind::Tab => 1,
5846 };
5847 let start = if has_multiple_rows
5848 || deletion_len > selection.start.column
5849 || indent_size.len < selection.start.column
5850 {
5851 0
5852 } else {
5853 selection.start.column - deletion_len
5854 };
5855 deletion_ranges.push(
5856 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
5857 );
5858 last_outdent = Some(row);
5859 }
5860 }
5861 }
5862 }
5863
5864 self.transact(cx, |this, cx| {
5865 this.buffer.update(cx, |buffer, cx| {
5866 let empty_str: Arc<str> = Arc::default();
5867 buffer.edit(
5868 deletion_ranges
5869 .into_iter()
5870 .map(|range| (range, empty_str.clone())),
5871 None,
5872 cx,
5873 );
5874 });
5875 let selections = this.selections.all::<usize>(cx);
5876 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5877 });
5878 }
5879
5880 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
5881 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5882 let selections = self.selections.all::<Point>(cx);
5883
5884 let mut new_cursors = Vec::new();
5885 let mut edit_ranges = Vec::new();
5886 let mut selections = selections.iter().peekable();
5887 while let Some(selection) = selections.next() {
5888 let mut rows = selection.spanned_rows(false, &display_map);
5889 let goal_display_column = selection.head().to_display_point(&display_map).column();
5890
5891 // Accumulate contiguous regions of rows that we want to delete.
5892 while let Some(next_selection) = selections.peek() {
5893 let next_rows = next_selection.spanned_rows(false, &display_map);
5894 if next_rows.start <= rows.end {
5895 rows.end = next_rows.end;
5896 selections.next().unwrap();
5897 } else {
5898 break;
5899 }
5900 }
5901
5902 let buffer = &display_map.buffer_snapshot;
5903 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
5904 let edit_end;
5905 let cursor_buffer_row;
5906 if buffer.max_point().row >= rows.end.0 {
5907 // If there's a line after the range, delete the \n from the end of the row range
5908 // and position the cursor on the next line.
5909 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
5910 cursor_buffer_row = rows.end;
5911 } else {
5912 // If there isn't a line after the range, delete the \n from the line before the
5913 // start of the row range and position the cursor there.
5914 edit_start = edit_start.saturating_sub(1);
5915 edit_end = buffer.len();
5916 cursor_buffer_row = rows.start.previous_row();
5917 }
5918
5919 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
5920 *cursor.column_mut() =
5921 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
5922
5923 new_cursors.push((
5924 selection.id,
5925 buffer.anchor_after(cursor.to_point(&display_map)),
5926 ));
5927 edit_ranges.push(edit_start..edit_end);
5928 }
5929
5930 self.transact(cx, |this, cx| {
5931 let buffer = this.buffer.update(cx, |buffer, cx| {
5932 let empty_str: Arc<str> = Arc::default();
5933 buffer.edit(
5934 edit_ranges
5935 .into_iter()
5936 .map(|range| (range, empty_str.clone())),
5937 None,
5938 cx,
5939 );
5940 buffer.snapshot(cx)
5941 });
5942 let new_selections = new_cursors
5943 .into_iter()
5944 .map(|(id, cursor)| {
5945 let cursor = cursor.to_point(&buffer);
5946 Selection {
5947 id,
5948 start: cursor,
5949 end: cursor,
5950 reversed: false,
5951 goal: SelectionGoal::None,
5952 }
5953 })
5954 .collect();
5955
5956 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5957 s.select(new_selections);
5958 });
5959 });
5960 }
5961
5962 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
5963 if self.read_only(cx) {
5964 return;
5965 }
5966 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
5967 for selection in self.selections.all::<Point>(cx) {
5968 let start = MultiBufferRow(selection.start.row);
5969 let end = if selection.start.row == selection.end.row {
5970 MultiBufferRow(selection.start.row + 1)
5971 } else {
5972 MultiBufferRow(selection.end.row)
5973 };
5974
5975 if let Some(last_row_range) = row_ranges.last_mut() {
5976 if start <= last_row_range.end {
5977 last_row_range.end = end;
5978 continue;
5979 }
5980 }
5981 row_ranges.push(start..end);
5982 }
5983
5984 let snapshot = self.buffer.read(cx).snapshot(cx);
5985 let mut cursor_positions = Vec::new();
5986 for row_range in &row_ranges {
5987 let anchor = snapshot.anchor_before(Point::new(
5988 row_range.end.previous_row().0,
5989 snapshot.line_len(row_range.end.previous_row()),
5990 ));
5991 cursor_positions.push(anchor..anchor);
5992 }
5993
5994 self.transact(cx, |this, cx| {
5995 for row_range in row_ranges.into_iter().rev() {
5996 for row in row_range.iter_rows().rev() {
5997 let end_of_line = Point::new(row.0, snapshot.line_len(row));
5998 let next_line_row = row.next_row();
5999 let indent = snapshot.indent_size_for_line(next_line_row);
6000 let start_of_next_line = Point::new(next_line_row.0, indent.len);
6001
6002 let replace = if snapshot.line_len(next_line_row) > indent.len {
6003 " "
6004 } else {
6005 ""
6006 };
6007
6008 this.buffer.update(cx, |buffer, cx| {
6009 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
6010 });
6011 }
6012 }
6013
6014 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6015 s.select_anchor_ranges(cursor_positions)
6016 });
6017 });
6018 }
6019
6020 pub fn sort_lines_case_sensitive(
6021 &mut self,
6022 _: &SortLinesCaseSensitive,
6023 cx: &mut ViewContext<Self>,
6024 ) {
6025 self.manipulate_lines(cx, |lines| lines.sort())
6026 }
6027
6028 pub fn sort_lines_case_insensitive(
6029 &mut self,
6030 _: &SortLinesCaseInsensitive,
6031 cx: &mut ViewContext<Self>,
6032 ) {
6033 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
6034 }
6035
6036 pub fn unique_lines_case_insensitive(
6037 &mut self,
6038 _: &UniqueLinesCaseInsensitive,
6039 cx: &mut ViewContext<Self>,
6040 ) {
6041 self.manipulate_lines(cx, |lines| {
6042 let mut seen = HashSet::default();
6043 lines.retain(|line| seen.insert(line.to_lowercase()));
6044 })
6045 }
6046
6047 pub fn unique_lines_case_sensitive(
6048 &mut self,
6049 _: &UniqueLinesCaseSensitive,
6050 cx: &mut ViewContext<Self>,
6051 ) {
6052 self.manipulate_lines(cx, |lines| {
6053 let mut seen = HashSet::default();
6054 lines.retain(|line| seen.insert(*line));
6055 })
6056 }
6057
6058 pub fn revert_file(&mut self, _: &RevertFile, cx: &mut ViewContext<Self>) {
6059 let mut revert_changes = HashMap::default();
6060 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
6061 for hunk in hunks_for_rows(
6062 Some(MultiBufferRow(0)..multi_buffer_snapshot.max_buffer_row()).into_iter(),
6063 &multi_buffer_snapshot,
6064 ) {
6065 Self::prepare_revert_change(&mut revert_changes, &self.buffer(), &hunk, cx);
6066 }
6067 if !revert_changes.is_empty() {
6068 self.transact(cx, |editor, cx| {
6069 editor.revert(revert_changes, cx);
6070 });
6071 }
6072 }
6073
6074 pub fn revert_selected_hunks(&mut self, _: &RevertSelectedHunks, cx: &mut ViewContext<Self>) {
6075 let revert_changes = self.gather_revert_changes(&self.selections.disjoint_anchors(), cx);
6076 if !revert_changes.is_empty() {
6077 self.transact(cx, |editor, cx| {
6078 editor.revert(revert_changes, cx);
6079 });
6080 }
6081 }
6082
6083 pub fn open_active_item_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
6084 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
6085 let project_path = buffer.read(cx).project_path(cx)?;
6086 let project = self.project.as_ref()?.read(cx);
6087 let entry = project.entry_for_path(&project_path, cx)?;
6088 let abs_path = project.absolute_path(&project_path, cx)?;
6089 let parent = if entry.is_symlink {
6090 abs_path.canonicalize().ok()?
6091 } else {
6092 abs_path
6093 }
6094 .parent()?
6095 .to_path_buf();
6096 Some(parent)
6097 }) {
6098 cx.dispatch_action(OpenTerminal { working_directory }.boxed_clone());
6099 }
6100 }
6101
6102 fn gather_revert_changes(
6103 &mut self,
6104 selections: &[Selection<Anchor>],
6105 cx: &mut ViewContext<'_, Editor>,
6106 ) -> HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>> {
6107 let mut revert_changes = HashMap::default();
6108 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
6109 for hunk in hunks_for_selections(&multi_buffer_snapshot, selections) {
6110 Self::prepare_revert_change(&mut revert_changes, self.buffer(), &hunk, cx);
6111 }
6112 revert_changes
6113 }
6114
6115 pub fn prepare_revert_change(
6116 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
6117 multi_buffer: &Model<MultiBuffer>,
6118 hunk: &DiffHunk<MultiBufferRow>,
6119 cx: &AppContext,
6120 ) -> Option<()> {
6121 let buffer = multi_buffer.read(cx).buffer(hunk.buffer_id)?;
6122 let buffer = buffer.read(cx);
6123 let original_text = buffer.diff_base()?.slice(hunk.diff_base_byte_range.clone());
6124 let buffer_snapshot = buffer.snapshot();
6125 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
6126 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
6127 probe
6128 .0
6129 .start
6130 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
6131 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
6132 }) {
6133 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
6134 Some(())
6135 } else {
6136 None
6137 }
6138 }
6139
6140 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
6141 self.manipulate_lines(cx, |lines| lines.reverse())
6142 }
6143
6144 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
6145 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
6146 }
6147
6148 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6149 where
6150 Fn: FnMut(&mut Vec<&str>),
6151 {
6152 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6153 let buffer = self.buffer.read(cx).snapshot(cx);
6154
6155 let mut edits = Vec::new();
6156
6157 let selections = self.selections.all::<Point>(cx);
6158 let mut selections = selections.iter().peekable();
6159 let mut contiguous_row_selections = Vec::new();
6160 let mut new_selections = Vec::new();
6161 let mut added_lines = 0;
6162 let mut removed_lines = 0;
6163
6164 while let Some(selection) = selections.next() {
6165 let (start_row, end_row) = consume_contiguous_rows(
6166 &mut contiguous_row_selections,
6167 selection,
6168 &display_map,
6169 &mut selections,
6170 );
6171
6172 let start_point = Point::new(start_row.0, 0);
6173 let end_point = Point::new(
6174 end_row.previous_row().0,
6175 buffer.line_len(end_row.previous_row()),
6176 );
6177 let text = buffer
6178 .text_for_range(start_point..end_point)
6179 .collect::<String>();
6180
6181 let mut lines = text.split('\n').collect_vec();
6182
6183 let lines_before = lines.len();
6184 callback(&mut lines);
6185 let lines_after = lines.len();
6186
6187 edits.push((start_point..end_point, lines.join("\n")));
6188
6189 // Selections must change based on added and removed line count
6190 let start_row =
6191 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
6192 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
6193 new_selections.push(Selection {
6194 id: selection.id,
6195 start: start_row,
6196 end: end_row,
6197 goal: SelectionGoal::None,
6198 reversed: selection.reversed,
6199 });
6200
6201 if lines_after > lines_before {
6202 added_lines += lines_after - lines_before;
6203 } else if lines_before > lines_after {
6204 removed_lines += lines_before - lines_after;
6205 }
6206 }
6207
6208 self.transact(cx, |this, cx| {
6209 let buffer = this.buffer.update(cx, |buffer, cx| {
6210 buffer.edit(edits, None, cx);
6211 buffer.snapshot(cx)
6212 });
6213
6214 // Recalculate offsets on newly edited buffer
6215 let new_selections = new_selections
6216 .iter()
6217 .map(|s| {
6218 let start_point = Point::new(s.start.0, 0);
6219 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
6220 Selection {
6221 id: s.id,
6222 start: buffer.point_to_offset(start_point),
6223 end: buffer.point_to_offset(end_point),
6224 goal: s.goal,
6225 reversed: s.reversed,
6226 }
6227 })
6228 .collect();
6229
6230 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6231 s.select(new_selections);
6232 });
6233
6234 this.request_autoscroll(Autoscroll::fit(), cx);
6235 });
6236 }
6237
6238 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
6239 self.manipulate_text(cx, |text| text.to_uppercase())
6240 }
6241
6242 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
6243 self.manipulate_text(cx, |text| text.to_lowercase())
6244 }
6245
6246 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
6247 self.manipulate_text(cx, |text| {
6248 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6249 // https://github.com/rutrum/convert-case/issues/16
6250 text.split('\n')
6251 .map(|line| line.to_case(Case::Title))
6252 .join("\n")
6253 })
6254 }
6255
6256 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
6257 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
6258 }
6259
6260 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
6261 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
6262 }
6263
6264 pub fn convert_to_upper_camel_case(
6265 &mut self,
6266 _: &ConvertToUpperCamelCase,
6267 cx: &mut ViewContext<Self>,
6268 ) {
6269 self.manipulate_text(cx, |text| {
6270 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6271 // https://github.com/rutrum/convert-case/issues/16
6272 text.split('\n')
6273 .map(|line| line.to_case(Case::UpperCamel))
6274 .join("\n")
6275 })
6276 }
6277
6278 pub fn convert_to_lower_camel_case(
6279 &mut self,
6280 _: &ConvertToLowerCamelCase,
6281 cx: &mut ViewContext<Self>,
6282 ) {
6283 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
6284 }
6285
6286 pub fn convert_to_opposite_case(
6287 &mut self,
6288 _: &ConvertToOppositeCase,
6289 cx: &mut ViewContext<Self>,
6290 ) {
6291 self.manipulate_text(cx, |text| {
6292 text.chars()
6293 .fold(String::with_capacity(text.len()), |mut t, c| {
6294 if c.is_uppercase() {
6295 t.extend(c.to_lowercase());
6296 } else {
6297 t.extend(c.to_uppercase());
6298 }
6299 t
6300 })
6301 })
6302 }
6303
6304 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6305 where
6306 Fn: FnMut(&str) -> String,
6307 {
6308 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6309 let buffer = self.buffer.read(cx).snapshot(cx);
6310
6311 let mut new_selections = Vec::new();
6312 let mut edits = Vec::new();
6313 let mut selection_adjustment = 0i32;
6314
6315 for selection in self.selections.all::<usize>(cx) {
6316 let selection_is_empty = selection.is_empty();
6317
6318 let (start, end) = if selection_is_empty {
6319 let word_range = movement::surrounding_word(
6320 &display_map,
6321 selection.start.to_display_point(&display_map),
6322 );
6323 let start = word_range.start.to_offset(&display_map, Bias::Left);
6324 let end = word_range.end.to_offset(&display_map, Bias::Left);
6325 (start, end)
6326 } else {
6327 (selection.start, selection.end)
6328 };
6329
6330 let text = buffer.text_for_range(start..end).collect::<String>();
6331 let old_length = text.len() as i32;
6332 let text = callback(&text);
6333
6334 new_selections.push(Selection {
6335 start: (start as i32 - selection_adjustment) as usize,
6336 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
6337 goal: SelectionGoal::None,
6338 ..selection
6339 });
6340
6341 selection_adjustment += old_length - text.len() as i32;
6342
6343 edits.push((start..end, text));
6344 }
6345
6346 self.transact(cx, |this, cx| {
6347 this.buffer.update(cx, |buffer, cx| {
6348 buffer.edit(edits, None, cx);
6349 });
6350
6351 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6352 s.select(new_selections);
6353 });
6354
6355 this.request_autoscroll(Autoscroll::fit(), cx);
6356 });
6357 }
6358
6359 pub fn duplicate_line(&mut self, upwards: bool, cx: &mut ViewContext<Self>) {
6360 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6361 let buffer = &display_map.buffer_snapshot;
6362 let selections = self.selections.all::<Point>(cx);
6363
6364 let mut edits = Vec::new();
6365 let mut selections_iter = selections.iter().peekable();
6366 while let Some(selection) = selections_iter.next() {
6367 // Avoid duplicating the same lines twice.
6368 let mut rows = selection.spanned_rows(false, &display_map);
6369
6370 while let Some(next_selection) = selections_iter.peek() {
6371 let next_rows = next_selection.spanned_rows(false, &display_map);
6372 if next_rows.start < rows.end {
6373 rows.end = next_rows.end;
6374 selections_iter.next().unwrap();
6375 } else {
6376 break;
6377 }
6378 }
6379
6380 // Copy the text from the selected row region and splice it either at the start
6381 // or end of the region.
6382 let start = Point::new(rows.start.0, 0);
6383 let end = Point::new(
6384 rows.end.previous_row().0,
6385 buffer.line_len(rows.end.previous_row()),
6386 );
6387 let text = buffer
6388 .text_for_range(start..end)
6389 .chain(Some("\n"))
6390 .collect::<String>();
6391 let insert_location = if upwards {
6392 Point::new(rows.end.0, 0)
6393 } else {
6394 start
6395 };
6396 edits.push((insert_location..insert_location, text));
6397 }
6398
6399 self.transact(cx, |this, cx| {
6400 this.buffer.update(cx, |buffer, cx| {
6401 buffer.edit(edits, None, cx);
6402 });
6403
6404 this.request_autoscroll(Autoscroll::fit(), cx);
6405 });
6406 }
6407
6408 pub fn duplicate_line_up(&mut self, _: &DuplicateLineUp, cx: &mut ViewContext<Self>) {
6409 self.duplicate_line(true, cx);
6410 }
6411
6412 pub fn duplicate_line_down(&mut self, _: &DuplicateLineDown, cx: &mut ViewContext<Self>) {
6413 self.duplicate_line(false, cx);
6414 }
6415
6416 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
6417 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6418 let buffer = self.buffer.read(cx).snapshot(cx);
6419
6420 let mut edits = Vec::new();
6421 let mut unfold_ranges = Vec::new();
6422 let mut refold_ranges = Vec::new();
6423
6424 let selections = self.selections.all::<Point>(cx);
6425 let mut selections = selections.iter().peekable();
6426 let mut contiguous_row_selections = Vec::new();
6427 let mut new_selections = Vec::new();
6428
6429 while let Some(selection) = selections.next() {
6430 // Find all the selections that span a contiguous row range
6431 let (start_row, end_row) = consume_contiguous_rows(
6432 &mut contiguous_row_selections,
6433 selection,
6434 &display_map,
6435 &mut selections,
6436 );
6437
6438 // Move the text spanned by the row range to be before the line preceding the row range
6439 if start_row.0 > 0 {
6440 let range_to_move = Point::new(
6441 start_row.previous_row().0,
6442 buffer.line_len(start_row.previous_row()),
6443 )
6444 ..Point::new(
6445 end_row.previous_row().0,
6446 buffer.line_len(end_row.previous_row()),
6447 );
6448 let insertion_point = display_map
6449 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
6450 .0;
6451
6452 // Don't move lines across excerpts
6453 if buffer
6454 .excerpt_boundaries_in_range((
6455 Bound::Excluded(insertion_point),
6456 Bound::Included(range_to_move.end),
6457 ))
6458 .next()
6459 .is_none()
6460 {
6461 let text = buffer
6462 .text_for_range(range_to_move.clone())
6463 .flat_map(|s| s.chars())
6464 .skip(1)
6465 .chain(['\n'])
6466 .collect::<String>();
6467
6468 edits.push((
6469 buffer.anchor_after(range_to_move.start)
6470 ..buffer.anchor_before(range_to_move.end),
6471 String::new(),
6472 ));
6473 let insertion_anchor = buffer.anchor_after(insertion_point);
6474 edits.push((insertion_anchor..insertion_anchor, text));
6475
6476 let row_delta = range_to_move.start.row - insertion_point.row + 1;
6477
6478 // Move selections up
6479 new_selections.extend(contiguous_row_selections.drain(..).map(
6480 |mut selection| {
6481 selection.start.row -= row_delta;
6482 selection.end.row -= row_delta;
6483 selection
6484 },
6485 ));
6486
6487 // Move folds up
6488 unfold_ranges.push(range_to_move.clone());
6489 for fold in display_map.folds_in_range(
6490 buffer.anchor_before(range_to_move.start)
6491 ..buffer.anchor_after(range_to_move.end),
6492 ) {
6493 let mut start = fold.range.start.to_point(&buffer);
6494 let mut end = fold.range.end.to_point(&buffer);
6495 start.row -= row_delta;
6496 end.row -= row_delta;
6497 refold_ranges.push((start..end, fold.placeholder.clone()));
6498 }
6499 }
6500 }
6501
6502 // If we didn't move line(s), preserve the existing selections
6503 new_selections.append(&mut contiguous_row_selections);
6504 }
6505
6506 self.transact(cx, |this, cx| {
6507 this.unfold_ranges(unfold_ranges, true, true, cx);
6508 this.buffer.update(cx, |buffer, cx| {
6509 for (range, text) in edits {
6510 buffer.edit([(range, text)], None, cx);
6511 }
6512 });
6513 this.fold_ranges(refold_ranges, true, cx);
6514 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6515 s.select(new_selections);
6516 })
6517 });
6518 }
6519
6520 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
6521 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6522 let buffer = self.buffer.read(cx).snapshot(cx);
6523
6524 let mut edits = Vec::new();
6525 let mut unfold_ranges = Vec::new();
6526 let mut refold_ranges = Vec::new();
6527
6528 let selections = self.selections.all::<Point>(cx);
6529 let mut selections = selections.iter().peekable();
6530 let mut contiguous_row_selections = Vec::new();
6531 let mut new_selections = Vec::new();
6532
6533 while let Some(selection) = selections.next() {
6534 // Find all the selections that span a contiguous row range
6535 let (start_row, end_row) = consume_contiguous_rows(
6536 &mut contiguous_row_selections,
6537 selection,
6538 &display_map,
6539 &mut selections,
6540 );
6541
6542 // Move the text spanned by the row range to be after the last line of the row range
6543 if end_row.0 <= buffer.max_point().row {
6544 let range_to_move =
6545 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
6546 let insertion_point = display_map
6547 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
6548 .0;
6549
6550 // Don't move lines across excerpt boundaries
6551 if buffer
6552 .excerpt_boundaries_in_range((
6553 Bound::Excluded(range_to_move.start),
6554 Bound::Included(insertion_point),
6555 ))
6556 .next()
6557 .is_none()
6558 {
6559 let mut text = String::from("\n");
6560 text.extend(buffer.text_for_range(range_to_move.clone()));
6561 text.pop(); // Drop trailing newline
6562 edits.push((
6563 buffer.anchor_after(range_to_move.start)
6564 ..buffer.anchor_before(range_to_move.end),
6565 String::new(),
6566 ));
6567 let insertion_anchor = buffer.anchor_after(insertion_point);
6568 edits.push((insertion_anchor..insertion_anchor, text));
6569
6570 let row_delta = insertion_point.row - range_to_move.end.row + 1;
6571
6572 // Move selections down
6573 new_selections.extend(contiguous_row_selections.drain(..).map(
6574 |mut selection| {
6575 selection.start.row += row_delta;
6576 selection.end.row += row_delta;
6577 selection
6578 },
6579 ));
6580
6581 // Move folds down
6582 unfold_ranges.push(range_to_move.clone());
6583 for fold in display_map.folds_in_range(
6584 buffer.anchor_before(range_to_move.start)
6585 ..buffer.anchor_after(range_to_move.end),
6586 ) {
6587 let mut start = fold.range.start.to_point(&buffer);
6588 let mut end = fold.range.end.to_point(&buffer);
6589 start.row += row_delta;
6590 end.row += row_delta;
6591 refold_ranges.push((start..end, fold.placeholder.clone()));
6592 }
6593 }
6594 }
6595
6596 // If we didn't move line(s), preserve the existing selections
6597 new_selections.append(&mut contiguous_row_selections);
6598 }
6599
6600 self.transact(cx, |this, cx| {
6601 this.unfold_ranges(unfold_ranges, true, true, cx);
6602 this.buffer.update(cx, |buffer, cx| {
6603 for (range, text) in edits {
6604 buffer.edit([(range, text)], None, cx);
6605 }
6606 });
6607 this.fold_ranges(refold_ranges, true, cx);
6608 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
6609 });
6610 }
6611
6612 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
6613 let text_layout_details = &self.text_layout_details(cx);
6614 self.transact(cx, |this, cx| {
6615 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6616 let mut edits: Vec<(Range<usize>, String)> = Default::default();
6617 let line_mode = s.line_mode;
6618 s.move_with(|display_map, selection| {
6619 if !selection.is_empty() || line_mode {
6620 return;
6621 }
6622
6623 let mut head = selection.head();
6624 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
6625 if head.column() == display_map.line_len(head.row()) {
6626 transpose_offset = display_map
6627 .buffer_snapshot
6628 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6629 }
6630
6631 if transpose_offset == 0 {
6632 return;
6633 }
6634
6635 *head.column_mut() += 1;
6636 head = display_map.clip_point(head, Bias::Right);
6637 let goal = SelectionGoal::HorizontalPosition(
6638 display_map
6639 .x_for_display_point(head, &text_layout_details)
6640 .into(),
6641 );
6642 selection.collapse_to(head, goal);
6643
6644 let transpose_start = display_map
6645 .buffer_snapshot
6646 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6647 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
6648 let transpose_end = display_map
6649 .buffer_snapshot
6650 .clip_offset(transpose_offset + 1, Bias::Right);
6651 if let Some(ch) =
6652 display_map.buffer_snapshot.chars_at(transpose_start).next()
6653 {
6654 edits.push((transpose_start..transpose_offset, String::new()));
6655 edits.push((transpose_end..transpose_end, ch.to_string()));
6656 }
6657 }
6658 });
6659 edits
6660 });
6661 this.buffer
6662 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
6663 let selections = this.selections.all::<usize>(cx);
6664 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6665 s.select(selections);
6666 });
6667 });
6668 }
6669
6670 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
6671 let mut text = String::new();
6672 let buffer = self.buffer.read(cx).snapshot(cx);
6673 let mut selections = self.selections.all::<Point>(cx);
6674 let mut clipboard_selections = Vec::with_capacity(selections.len());
6675 {
6676 let max_point = buffer.max_point();
6677 let mut is_first = true;
6678 for selection in &mut selections {
6679 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6680 if is_entire_line {
6681 selection.start = Point::new(selection.start.row, 0);
6682 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
6683 selection.goal = SelectionGoal::None;
6684 }
6685 if is_first {
6686 is_first = false;
6687 } else {
6688 text += "\n";
6689 }
6690 let mut len = 0;
6691 for chunk in buffer.text_for_range(selection.start..selection.end) {
6692 text.push_str(chunk);
6693 len += chunk.len();
6694 }
6695 clipboard_selections.push(ClipboardSelection {
6696 len,
6697 is_entire_line,
6698 first_line_indent: buffer
6699 .indent_size_for_line(MultiBufferRow(selection.start.row))
6700 .len,
6701 });
6702 }
6703 }
6704
6705 self.transact(cx, |this, cx| {
6706 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6707 s.select(selections);
6708 });
6709 this.insert("", cx);
6710 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
6711 text,
6712 clipboard_selections,
6713 ));
6714 });
6715 }
6716
6717 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
6718 let selections = self.selections.all::<Point>(cx);
6719 let buffer = self.buffer.read(cx).read(cx);
6720 let mut text = String::new();
6721
6722 let mut clipboard_selections = Vec::with_capacity(selections.len());
6723 {
6724 let max_point = buffer.max_point();
6725 let mut is_first = true;
6726 for selection in selections.iter() {
6727 let mut start = selection.start;
6728 let mut end = selection.end;
6729 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6730 if is_entire_line {
6731 start = Point::new(start.row, 0);
6732 end = cmp::min(max_point, Point::new(end.row + 1, 0));
6733 }
6734 if is_first {
6735 is_first = false;
6736 } else {
6737 text += "\n";
6738 }
6739 let mut len = 0;
6740 for chunk in buffer.text_for_range(start..end) {
6741 text.push_str(chunk);
6742 len += chunk.len();
6743 }
6744 clipboard_selections.push(ClipboardSelection {
6745 len,
6746 is_entire_line,
6747 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
6748 });
6749 }
6750 }
6751
6752 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
6753 text,
6754 clipboard_selections,
6755 ));
6756 }
6757
6758 pub fn do_paste(
6759 &mut self,
6760 text: &String,
6761 clipboard_selections: Option<Vec<ClipboardSelection>>,
6762 handle_entire_lines: bool,
6763 cx: &mut ViewContext<Self>,
6764 ) {
6765 if self.read_only(cx) {
6766 return;
6767 }
6768
6769 let clipboard_text = Cow::Borrowed(text);
6770
6771 self.transact(cx, |this, cx| {
6772 if let Some(mut clipboard_selections) = clipboard_selections {
6773 let old_selections = this.selections.all::<usize>(cx);
6774 let all_selections_were_entire_line =
6775 clipboard_selections.iter().all(|s| s.is_entire_line);
6776 let first_selection_indent_column =
6777 clipboard_selections.first().map(|s| s.first_line_indent);
6778 if clipboard_selections.len() != old_selections.len() {
6779 clipboard_selections.drain(..);
6780 }
6781
6782 this.buffer.update(cx, |buffer, cx| {
6783 let snapshot = buffer.read(cx);
6784 let mut start_offset = 0;
6785 let mut edits = Vec::new();
6786 let mut original_indent_columns = Vec::new();
6787 for (ix, selection) in old_selections.iter().enumerate() {
6788 let to_insert;
6789 let entire_line;
6790 let original_indent_column;
6791 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
6792 let end_offset = start_offset + clipboard_selection.len;
6793 to_insert = &clipboard_text[start_offset..end_offset];
6794 entire_line = clipboard_selection.is_entire_line;
6795 start_offset = end_offset + 1;
6796 original_indent_column = Some(clipboard_selection.first_line_indent);
6797 } else {
6798 to_insert = clipboard_text.as_str();
6799 entire_line = all_selections_were_entire_line;
6800 original_indent_column = first_selection_indent_column
6801 }
6802
6803 // If the corresponding selection was empty when this slice of the
6804 // clipboard text was written, then the entire line containing the
6805 // selection was copied. If this selection is also currently empty,
6806 // then paste the line before the current line of the buffer.
6807 let range = if selection.is_empty() && handle_entire_lines && entire_line {
6808 let column = selection.start.to_point(&snapshot).column as usize;
6809 let line_start = selection.start - column;
6810 line_start..line_start
6811 } else {
6812 selection.range()
6813 };
6814
6815 edits.push((range, to_insert));
6816 original_indent_columns.extend(original_indent_column);
6817 }
6818 drop(snapshot);
6819
6820 buffer.edit(
6821 edits,
6822 Some(AutoindentMode::Block {
6823 original_indent_columns,
6824 }),
6825 cx,
6826 );
6827 });
6828
6829 let selections = this.selections.all::<usize>(cx);
6830 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6831 } else {
6832 this.insert(&clipboard_text, cx);
6833 }
6834 });
6835 }
6836
6837 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
6838 if let Some(item) = cx.read_from_clipboard() {
6839 let entries = item.entries();
6840
6841 match entries.first() {
6842 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
6843 // of all the pasted entries.
6844 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
6845 .do_paste(
6846 clipboard_string.text(),
6847 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
6848 true,
6849 cx,
6850 ),
6851 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, cx),
6852 }
6853 }
6854 }
6855
6856 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
6857 if self.read_only(cx) {
6858 return;
6859 }
6860
6861 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
6862 if let Some((selections, _)) =
6863 self.selection_history.transaction(transaction_id).cloned()
6864 {
6865 self.change_selections(None, cx, |s| {
6866 s.select_anchors(selections.to_vec());
6867 });
6868 }
6869 self.request_autoscroll(Autoscroll::fit(), cx);
6870 self.unmark_text(cx);
6871 self.refresh_inline_completion(true, false, cx);
6872 cx.emit(EditorEvent::Edited { transaction_id });
6873 cx.emit(EditorEvent::TransactionUndone { transaction_id });
6874 }
6875 }
6876
6877 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
6878 if self.read_only(cx) {
6879 return;
6880 }
6881
6882 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
6883 if let Some((_, Some(selections))) =
6884 self.selection_history.transaction(transaction_id).cloned()
6885 {
6886 self.change_selections(None, cx, |s| {
6887 s.select_anchors(selections.to_vec());
6888 });
6889 }
6890 self.request_autoscroll(Autoscroll::fit(), cx);
6891 self.unmark_text(cx);
6892 self.refresh_inline_completion(true, false, cx);
6893 cx.emit(EditorEvent::Edited { transaction_id });
6894 }
6895 }
6896
6897 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
6898 self.buffer
6899 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
6900 }
6901
6902 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut ViewContext<Self>) {
6903 self.buffer
6904 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
6905 }
6906
6907 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
6908 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6909 let line_mode = s.line_mode;
6910 s.move_with(|map, selection| {
6911 let cursor = if selection.is_empty() && !line_mode {
6912 movement::left(map, selection.start)
6913 } else {
6914 selection.start
6915 };
6916 selection.collapse_to(cursor, SelectionGoal::None);
6917 });
6918 })
6919 }
6920
6921 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
6922 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6923 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
6924 })
6925 }
6926
6927 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
6928 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6929 let line_mode = s.line_mode;
6930 s.move_with(|map, selection| {
6931 let cursor = if selection.is_empty() && !line_mode {
6932 movement::right(map, selection.end)
6933 } else {
6934 selection.end
6935 };
6936 selection.collapse_to(cursor, SelectionGoal::None)
6937 });
6938 })
6939 }
6940
6941 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
6942 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6943 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
6944 })
6945 }
6946
6947 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
6948 if self.take_rename(true, cx).is_some() {
6949 return;
6950 }
6951
6952 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6953 cx.propagate();
6954 return;
6955 }
6956
6957 let text_layout_details = &self.text_layout_details(cx);
6958 let selection_count = self.selections.count();
6959 let first_selection = self.selections.first_anchor();
6960
6961 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6962 let line_mode = s.line_mode;
6963 s.move_with(|map, selection| {
6964 if !selection.is_empty() && !line_mode {
6965 selection.goal = SelectionGoal::None;
6966 }
6967 let (cursor, goal) = movement::up(
6968 map,
6969 selection.start,
6970 selection.goal,
6971 false,
6972 &text_layout_details,
6973 );
6974 selection.collapse_to(cursor, goal);
6975 });
6976 });
6977
6978 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
6979 {
6980 cx.propagate();
6981 }
6982 }
6983
6984 pub fn move_up_by_lines(&mut self, action: &MoveUpByLines, cx: &mut ViewContext<Self>) {
6985 if self.take_rename(true, cx).is_some() {
6986 return;
6987 }
6988
6989 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6990 cx.propagate();
6991 return;
6992 }
6993
6994 let text_layout_details = &self.text_layout_details(cx);
6995
6996 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6997 let line_mode = s.line_mode;
6998 s.move_with(|map, selection| {
6999 if !selection.is_empty() && !line_mode {
7000 selection.goal = SelectionGoal::None;
7001 }
7002 let (cursor, goal) = movement::up_by_rows(
7003 map,
7004 selection.start,
7005 action.lines,
7006 selection.goal,
7007 false,
7008 &text_layout_details,
7009 );
7010 selection.collapse_to(cursor, goal);
7011 });
7012 })
7013 }
7014
7015 pub fn move_down_by_lines(&mut self, action: &MoveDownByLines, cx: &mut ViewContext<Self>) {
7016 if self.take_rename(true, cx).is_some() {
7017 return;
7018 }
7019
7020 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7021 cx.propagate();
7022 return;
7023 }
7024
7025 let text_layout_details = &self.text_layout_details(cx);
7026
7027 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7028 let line_mode = s.line_mode;
7029 s.move_with(|map, selection| {
7030 if !selection.is_empty() && !line_mode {
7031 selection.goal = SelectionGoal::None;
7032 }
7033 let (cursor, goal) = movement::down_by_rows(
7034 map,
7035 selection.start,
7036 action.lines,
7037 selection.goal,
7038 false,
7039 &text_layout_details,
7040 );
7041 selection.collapse_to(cursor, goal);
7042 });
7043 })
7044 }
7045
7046 pub fn select_down_by_lines(&mut self, action: &SelectDownByLines, cx: &mut ViewContext<Self>) {
7047 let text_layout_details = &self.text_layout_details(cx);
7048 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7049 s.move_heads_with(|map, head, goal| {
7050 movement::down_by_rows(map, head, action.lines, goal, false, &text_layout_details)
7051 })
7052 })
7053 }
7054
7055 pub fn select_up_by_lines(&mut self, action: &SelectUpByLines, cx: &mut ViewContext<Self>) {
7056 let text_layout_details = &self.text_layout_details(cx);
7057 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7058 s.move_heads_with(|map, head, goal| {
7059 movement::up_by_rows(map, head, action.lines, goal, false, &text_layout_details)
7060 })
7061 })
7062 }
7063
7064 pub fn select_page_up(&mut self, _: &SelectPageUp, cx: &mut ViewContext<Self>) {
7065 let Some(row_count) = self.visible_row_count() else {
7066 return;
7067 };
7068
7069 let text_layout_details = &self.text_layout_details(cx);
7070
7071 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7072 s.move_heads_with(|map, head, goal| {
7073 movement::up_by_rows(map, head, row_count, goal, false, &text_layout_details)
7074 })
7075 })
7076 }
7077
7078 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
7079 if self.take_rename(true, cx).is_some() {
7080 return;
7081 }
7082
7083 if self
7084 .context_menu
7085 .write()
7086 .as_mut()
7087 .map(|menu| menu.select_first(self.project.as_ref(), cx))
7088 .unwrap_or(false)
7089 {
7090 return;
7091 }
7092
7093 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7094 cx.propagate();
7095 return;
7096 }
7097
7098 let Some(row_count) = self.visible_row_count() else {
7099 return;
7100 };
7101
7102 let autoscroll = if action.center_cursor {
7103 Autoscroll::center()
7104 } else {
7105 Autoscroll::fit()
7106 };
7107
7108 let text_layout_details = &self.text_layout_details(cx);
7109
7110 self.change_selections(Some(autoscroll), cx, |s| {
7111 let line_mode = s.line_mode;
7112 s.move_with(|map, selection| {
7113 if !selection.is_empty() && !line_mode {
7114 selection.goal = SelectionGoal::None;
7115 }
7116 let (cursor, goal) = movement::up_by_rows(
7117 map,
7118 selection.end,
7119 row_count,
7120 selection.goal,
7121 false,
7122 &text_layout_details,
7123 );
7124 selection.collapse_to(cursor, goal);
7125 });
7126 });
7127 }
7128
7129 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
7130 let text_layout_details = &self.text_layout_details(cx);
7131 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7132 s.move_heads_with(|map, head, goal| {
7133 movement::up(map, head, goal, false, &text_layout_details)
7134 })
7135 })
7136 }
7137
7138 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
7139 self.take_rename(true, cx);
7140
7141 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7142 cx.propagate();
7143 return;
7144 }
7145
7146 let text_layout_details = &self.text_layout_details(cx);
7147 let selection_count = self.selections.count();
7148 let first_selection = self.selections.first_anchor();
7149
7150 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7151 let line_mode = s.line_mode;
7152 s.move_with(|map, selection| {
7153 if !selection.is_empty() && !line_mode {
7154 selection.goal = SelectionGoal::None;
7155 }
7156 let (cursor, goal) = movement::down(
7157 map,
7158 selection.end,
7159 selection.goal,
7160 false,
7161 &text_layout_details,
7162 );
7163 selection.collapse_to(cursor, goal);
7164 });
7165 });
7166
7167 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7168 {
7169 cx.propagate();
7170 }
7171 }
7172
7173 pub fn select_page_down(&mut self, _: &SelectPageDown, cx: &mut ViewContext<Self>) {
7174 let Some(row_count) = self.visible_row_count() else {
7175 return;
7176 };
7177
7178 let text_layout_details = &self.text_layout_details(cx);
7179
7180 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7181 s.move_heads_with(|map, head, goal| {
7182 movement::down_by_rows(map, head, row_count, goal, false, &text_layout_details)
7183 })
7184 })
7185 }
7186
7187 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
7188 if self.take_rename(true, cx).is_some() {
7189 return;
7190 }
7191
7192 if self
7193 .context_menu
7194 .write()
7195 .as_mut()
7196 .map(|menu| menu.select_last(self.project.as_ref(), cx))
7197 .unwrap_or(false)
7198 {
7199 return;
7200 }
7201
7202 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7203 cx.propagate();
7204 return;
7205 }
7206
7207 let Some(row_count) = self.visible_row_count() else {
7208 return;
7209 };
7210
7211 let autoscroll = if action.center_cursor {
7212 Autoscroll::center()
7213 } else {
7214 Autoscroll::fit()
7215 };
7216
7217 let text_layout_details = &self.text_layout_details(cx);
7218 self.change_selections(Some(autoscroll), cx, |s| {
7219 let line_mode = s.line_mode;
7220 s.move_with(|map, selection| {
7221 if !selection.is_empty() && !line_mode {
7222 selection.goal = SelectionGoal::None;
7223 }
7224 let (cursor, goal) = movement::down_by_rows(
7225 map,
7226 selection.end,
7227 row_count,
7228 selection.goal,
7229 false,
7230 &text_layout_details,
7231 );
7232 selection.collapse_to(cursor, goal);
7233 });
7234 });
7235 }
7236
7237 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
7238 let text_layout_details = &self.text_layout_details(cx);
7239 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7240 s.move_heads_with(|map, head, goal| {
7241 movement::down(map, head, goal, false, &text_layout_details)
7242 })
7243 });
7244 }
7245
7246 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
7247 if let Some(context_menu) = self.context_menu.write().as_mut() {
7248 context_menu.select_first(self.project.as_ref(), cx);
7249 }
7250 }
7251
7252 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
7253 if let Some(context_menu) = self.context_menu.write().as_mut() {
7254 context_menu.select_prev(self.project.as_ref(), cx);
7255 }
7256 }
7257
7258 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
7259 if let Some(context_menu) = self.context_menu.write().as_mut() {
7260 context_menu.select_next(self.project.as_ref(), cx);
7261 }
7262 }
7263
7264 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
7265 if let Some(context_menu) = self.context_menu.write().as_mut() {
7266 context_menu.select_last(self.project.as_ref(), cx);
7267 }
7268 }
7269
7270 pub fn move_to_previous_word_start(
7271 &mut self,
7272 _: &MoveToPreviousWordStart,
7273 cx: &mut ViewContext<Self>,
7274 ) {
7275 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7276 s.move_cursors_with(|map, head, _| {
7277 (
7278 movement::previous_word_start(map, head),
7279 SelectionGoal::None,
7280 )
7281 });
7282 })
7283 }
7284
7285 pub fn move_to_previous_subword_start(
7286 &mut self,
7287 _: &MoveToPreviousSubwordStart,
7288 cx: &mut ViewContext<Self>,
7289 ) {
7290 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7291 s.move_cursors_with(|map, head, _| {
7292 (
7293 movement::previous_subword_start(map, head),
7294 SelectionGoal::None,
7295 )
7296 });
7297 })
7298 }
7299
7300 pub fn select_to_previous_word_start(
7301 &mut self,
7302 _: &SelectToPreviousWordStart,
7303 cx: &mut ViewContext<Self>,
7304 ) {
7305 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7306 s.move_heads_with(|map, head, _| {
7307 (
7308 movement::previous_word_start(map, head),
7309 SelectionGoal::None,
7310 )
7311 });
7312 })
7313 }
7314
7315 pub fn select_to_previous_subword_start(
7316 &mut self,
7317 _: &SelectToPreviousSubwordStart,
7318 cx: &mut ViewContext<Self>,
7319 ) {
7320 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7321 s.move_heads_with(|map, head, _| {
7322 (
7323 movement::previous_subword_start(map, head),
7324 SelectionGoal::None,
7325 )
7326 });
7327 })
7328 }
7329
7330 pub fn delete_to_previous_word_start(
7331 &mut self,
7332 _: &DeleteToPreviousWordStart,
7333 cx: &mut ViewContext<Self>,
7334 ) {
7335 self.transact(cx, |this, cx| {
7336 this.select_autoclose_pair(cx);
7337 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7338 let line_mode = s.line_mode;
7339 s.move_with(|map, selection| {
7340 if selection.is_empty() && !line_mode {
7341 let cursor = movement::previous_word_start(map, selection.head());
7342 selection.set_head(cursor, SelectionGoal::None);
7343 }
7344 });
7345 });
7346 this.insert("", cx);
7347 });
7348 }
7349
7350 pub fn delete_to_previous_subword_start(
7351 &mut self,
7352 _: &DeleteToPreviousSubwordStart,
7353 cx: &mut ViewContext<Self>,
7354 ) {
7355 self.transact(cx, |this, cx| {
7356 this.select_autoclose_pair(cx);
7357 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7358 let line_mode = s.line_mode;
7359 s.move_with(|map, selection| {
7360 if selection.is_empty() && !line_mode {
7361 let cursor = movement::previous_subword_start(map, selection.head());
7362 selection.set_head(cursor, SelectionGoal::None);
7363 }
7364 });
7365 });
7366 this.insert("", cx);
7367 });
7368 }
7369
7370 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
7371 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7372 s.move_cursors_with(|map, head, _| {
7373 (movement::next_word_end(map, head), SelectionGoal::None)
7374 });
7375 })
7376 }
7377
7378 pub fn move_to_next_subword_end(
7379 &mut self,
7380 _: &MoveToNextSubwordEnd,
7381 cx: &mut ViewContext<Self>,
7382 ) {
7383 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7384 s.move_cursors_with(|map, head, _| {
7385 (movement::next_subword_end(map, head), SelectionGoal::None)
7386 });
7387 })
7388 }
7389
7390 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
7391 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7392 s.move_heads_with(|map, head, _| {
7393 (movement::next_word_end(map, head), SelectionGoal::None)
7394 });
7395 })
7396 }
7397
7398 pub fn select_to_next_subword_end(
7399 &mut self,
7400 _: &SelectToNextSubwordEnd,
7401 cx: &mut ViewContext<Self>,
7402 ) {
7403 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7404 s.move_heads_with(|map, head, _| {
7405 (movement::next_subword_end(map, head), SelectionGoal::None)
7406 });
7407 })
7408 }
7409
7410 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
7411 self.transact(cx, |this, cx| {
7412 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7413 let line_mode = s.line_mode;
7414 s.move_with(|map, selection| {
7415 if selection.is_empty() && !line_mode {
7416 let cursor = movement::next_word_end(map, selection.head());
7417 selection.set_head(cursor, SelectionGoal::None);
7418 }
7419 });
7420 });
7421 this.insert("", cx);
7422 });
7423 }
7424
7425 pub fn delete_to_next_subword_end(
7426 &mut self,
7427 _: &DeleteToNextSubwordEnd,
7428 cx: &mut ViewContext<Self>,
7429 ) {
7430 self.transact(cx, |this, cx| {
7431 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7432 s.move_with(|map, selection| {
7433 if selection.is_empty() {
7434 let cursor = movement::next_subword_end(map, selection.head());
7435 selection.set_head(cursor, SelectionGoal::None);
7436 }
7437 });
7438 });
7439 this.insert("", cx);
7440 });
7441 }
7442
7443 pub fn move_to_beginning_of_line(
7444 &mut self,
7445 action: &MoveToBeginningOfLine,
7446 cx: &mut ViewContext<Self>,
7447 ) {
7448 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7449 s.move_cursors_with(|map, head, _| {
7450 (
7451 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7452 SelectionGoal::None,
7453 )
7454 });
7455 })
7456 }
7457
7458 pub fn select_to_beginning_of_line(
7459 &mut self,
7460 action: &SelectToBeginningOfLine,
7461 cx: &mut ViewContext<Self>,
7462 ) {
7463 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7464 s.move_heads_with(|map, head, _| {
7465 (
7466 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7467 SelectionGoal::None,
7468 )
7469 });
7470 });
7471 }
7472
7473 pub fn delete_to_beginning_of_line(
7474 &mut self,
7475 _: &DeleteToBeginningOfLine,
7476 cx: &mut ViewContext<Self>,
7477 ) {
7478 self.transact(cx, |this, cx| {
7479 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7480 s.move_with(|_, selection| {
7481 selection.reversed = true;
7482 });
7483 });
7484
7485 this.select_to_beginning_of_line(
7486 &SelectToBeginningOfLine {
7487 stop_at_soft_wraps: false,
7488 },
7489 cx,
7490 );
7491 this.backspace(&Backspace, cx);
7492 });
7493 }
7494
7495 pub fn move_to_end_of_line(&mut self, action: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
7496 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7497 s.move_cursors_with(|map, head, _| {
7498 (
7499 movement::line_end(map, head, action.stop_at_soft_wraps),
7500 SelectionGoal::None,
7501 )
7502 });
7503 })
7504 }
7505
7506 pub fn select_to_end_of_line(
7507 &mut self,
7508 action: &SelectToEndOfLine,
7509 cx: &mut ViewContext<Self>,
7510 ) {
7511 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7512 s.move_heads_with(|map, head, _| {
7513 (
7514 movement::line_end(map, head, action.stop_at_soft_wraps),
7515 SelectionGoal::None,
7516 )
7517 });
7518 })
7519 }
7520
7521 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
7522 self.transact(cx, |this, cx| {
7523 this.select_to_end_of_line(
7524 &SelectToEndOfLine {
7525 stop_at_soft_wraps: false,
7526 },
7527 cx,
7528 );
7529 this.delete(&Delete, cx);
7530 });
7531 }
7532
7533 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
7534 self.transact(cx, |this, cx| {
7535 this.select_to_end_of_line(
7536 &SelectToEndOfLine {
7537 stop_at_soft_wraps: false,
7538 },
7539 cx,
7540 );
7541 this.cut(&Cut, cx);
7542 });
7543 }
7544
7545 pub fn move_to_start_of_paragraph(
7546 &mut self,
7547 _: &MoveToStartOfParagraph,
7548 cx: &mut ViewContext<Self>,
7549 ) {
7550 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7551 cx.propagate();
7552 return;
7553 }
7554
7555 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7556 s.move_with(|map, selection| {
7557 selection.collapse_to(
7558 movement::start_of_paragraph(map, selection.head(), 1),
7559 SelectionGoal::None,
7560 )
7561 });
7562 })
7563 }
7564
7565 pub fn move_to_end_of_paragraph(
7566 &mut self,
7567 _: &MoveToEndOfParagraph,
7568 cx: &mut ViewContext<Self>,
7569 ) {
7570 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7571 cx.propagate();
7572 return;
7573 }
7574
7575 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7576 s.move_with(|map, selection| {
7577 selection.collapse_to(
7578 movement::end_of_paragraph(map, selection.head(), 1),
7579 SelectionGoal::None,
7580 )
7581 });
7582 })
7583 }
7584
7585 pub fn select_to_start_of_paragraph(
7586 &mut self,
7587 _: &SelectToStartOfParagraph,
7588 cx: &mut ViewContext<Self>,
7589 ) {
7590 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7591 cx.propagate();
7592 return;
7593 }
7594
7595 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7596 s.move_heads_with(|map, head, _| {
7597 (
7598 movement::start_of_paragraph(map, head, 1),
7599 SelectionGoal::None,
7600 )
7601 });
7602 })
7603 }
7604
7605 pub fn select_to_end_of_paragraph(
7606 &mut self,
7607 _: &SelectToEndOfParagraph,
7608 cx: &mut ViewContext<Self>,
7609 ) {
7610 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7611 cx.propagate();
7612 return;
7613 }
7614
7615 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7616 s.move_heads_with(|map, head, _| {
7617 (
7618 movement::end_of_paragraph(map, head, 1),
7619 SelectionGoal::None,
7620 )
7621 });
7622 })
7623 }
7624
7625 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
7626 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7627 cx.propagate();
7628 return;
7629 }
7630
7631 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7632 s.select_ranges(vec![0..0]);
7633 });
7634 }
7635
7636 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
7637 let mut selection = self.selections.last::<Point>(cx);
7638 selection.set_head(Point::zero(), SelectionGoal::None);
7639
7640 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7641 s.select(vec![selection]);
7642 });
7643 }
7644
7645 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
7646 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7647 cx.propagate();
7648 return;
7649 }
7650
7651 let cursor = self.buffer.read(cx).read(cx).len();
7652 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7653 s.select_ranges(vec![cursor..cursor])
7654 });
7655 }
7656
7657 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
7658 self.nav_history = nav_history;
7659 }
7660
7661 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
7662 self.nav_history.as_ref()
7663 }
7664
7665 fn push_to_nav_history(
7666 &mut self,
7667 cursor_anchor: Anchor,
7668 new_position: Option<Point>,
7669 cx: &mut ViewContext<Self>,
7670 ) {
7671 if let Some(nav_history) = self.nav_history.as_mut() {
7672 let buffer = self.buffer.read(cx).read(cx);
7673 let cursor_position = cursor_anchor.to_point(&buffer);
7674 let scroll_state = self.scroll_manager.anchor();
7675 let scroll_top_row = scroll_state.top_row(&buffer);
7676 drop(buffer);
7677
7678 if let Some(new_position) = new_position {
7679 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
7680 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
7681 return;
7682 }
7683 }
7684
7685 nav_history.push(
7686 Some(NavigationData {
7687 cursor_anchor,
7688 cursor_position,
7689 scroll_anchor: scroll_state,
7690 scroll_top_row,
7691 }),
7692 cx,
7693 );
7694 }
7695 }
7696
7697 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
7698 let buffer = self.buffer.read(cx).snapshot(cx);
7699 let mut selection = self.selections.first::<usize>(cx);
7700 selection.set_head(buffer.len(), SelectionGoal::None);
7701 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7702 s.select(vec![selection]);
7703 });
7704 }
7705
7706 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
7707 let end = self.buffer.read(cx).read(cx).len();
7708 self.change_selections(None, cx, |s| {
7709 s.select_ranges(vec![0..end]);
7710 });
7711 }
7712
7713 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
7714 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7715 let mut selections = self.selections.all::<Point>(cx);
7716 let max_point = display_map.buffer_snapshot.max_point();
7717 for selection in &mut selections {
7718 let rows = selection.spanned_rows(true, &display_map);
7719 selection.start = Point::new(rows.start.0, 0);
7720 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
7721 selection.reversed = false;
7722 }
7723 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7724 s.select(selections);
7725 });
7726 }
7727
7728 pub fn split_selection_into_lines(
7729 &mut self,
7730 _: &SplitSelectionIntoLines,
7731 cx: &mut ViewContext<Self>,
7732 ) {
7733 let mut to_unfold = Vec::new();
7734 let mut new_selection_ranges = Vec::new();
7735 {
7736 let selections = self.selections.all::<Point>(cx);
7737 let buffer = self.buffer.read(cx).read(cx);
7738 for selection in selections {
7739 for row in selection.start.row..selection.end.row {
7740 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
7741 new_selection_ranges.push(cursor..cursor);
7742 }
7743 new_selection_ranges.push(selection.end..selection.end);
7744 to_unfold.push(selection.start..selection.end);
7745 }
7746 }
7747 self.unfold_ranges(to_unfold, true, true, cx);
7748 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7749 s.select_ranges(new_selection_ranges);
7750 });
7751 }
7752
7753 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
7754 self.add_selection(true, cx);
7755 }
7756
7757 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
7758 self.add_selection(false, cx);
7759 }
7760
7761 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
7762 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7763 let mut selections = self.selections.all::<Point>(cx);
7764 let text_layout_details = self.text_layout_details(cx);
7765 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
7766 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
7767 let range = oldest_selection.display_range(&display_map).sorted();
7768
7769 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
7770 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
7771 let positions = start_x.min(end_x)..start_x.max(end_x);
7772
7773 selections.clear();
7774 let mut stack = Vec::new();
7775 for row in range.start.row().0..=range.end.row().0 {
7776 if let Some(selection) = self.selections.build_columnar_selection(
7777 &display_map,
7778 DisplayRow(row),
7779 &positions,
7780 oldest_selection.reversed,
7781 &text_layout_details,
7782 ) {
7783 stack.push(selection.id);
7784 selections.push(selection);
7785 }
7786 }
7787
7788 if above {
7789 stack.reverse();
7790 }
7791
7792 AddSelectionsState { above, stack }
7793 });
7794
7795 let last_added_selection = *state.stack.last().unwrap();
7796 let mut new_selections = Vec::new();
7797 if above == state.above {
7798 let end_row = if above {
7799 DisplayRow(0)
7800 } else {
7801 display_map.max_point().row()
7802 };
7803
7804 'outer: for selection in selections {
7805 if selection.id == last_added_selection {
7806 let range = selection.display_range(&display_map).sorted();
7807 debug_assert_eq!(range.start.row(), range.end.row());
7808 let mut row = range.start.row();
7809 let positions =
7810 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
7811 px(start)..px(end)
7812 } else {
7813 let start_x =
7814 display_map.x_for_display_point(range.start, &text_layout_details);
7815 let end_x =
7816 display_map.x_for_display_point(range.end, &text_layout_details);
7817 start_x.min(end_x)..start_x.max(end_x)
7818 };
7819
7820 while row != end_row {
7821 if above {
7822 row.0 -= 1;
7823 } else {
7824 row.0 += 1;
7825 }
7826
7827 if let Some(new_selection) = self.selections.build_columnar_selection(
7828 &display_map,
7829 row,
7830 &positions,
7831 selection.reversed,
7832 &text_layout_details,
7833 ) {
7834 state.stack.push(new_selection.id);
7835 if above {
7836 new_selections.push(new_selection);
7837 new_selections.push(selection);
7838 } else {
7839 new_selections.push(selection);
7840 new_selections.push(new_selection);
7841 }
7842
7843 continue 'outer;
7844 }
7845 }
7846 }
7847
7848 new_selections.push(selection);
7849 }
7850 } else {
7851 new_selections = selections;
7852 new_selections.retain(|s| s.id != last_added_selection);
7853 state.stack.pop();
7854 }
7855
7856 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7857 s.select(new_selections);
7858 });
7859 if state.stack.len() > 1 {
7860 self.add_selections_state = Some(state);
7861 }
7862 }
7863
7864 pub fn select_next_match_internal(
7865 &mut self,
7866 display_map: &DisplaySnapshot,
7867 replace_newest: bool,
7868 autoscroll: Option<Autoscroll>,
7869 cx: &mut ViewContext<Self>,
7870 ) -> Result<()> {
7871 fn select_next_match_ranges(
7872 this: &mut Editor,
7873 range: Range<usize>,
7874 replace_newest: bool,
7875 auto_scroll: Option<Autoscroll>,
7876 cx: &mut ViewContext<Editor>,
7877 ) {
7878 this.unfold_ranges([range.clone()], false, true, cx);
7879 this.change_selections(auto_scroll, cx, |s| {
7880 if replace_newest {
7881 s.delete(s.newest_anchor().id);
7882 }
7883 s.insert_range(range.clone());
7884 });
7885 }
7886
7887 let buffer = &display_map.buffer_snapshot;
7888 let mut selections = self.selections.all::<usize>(cx);
7889 if let Some(mut select_next_state) = self.select_next_state.take() {
7890 let query = &select_next_state.query;
7891 if !select_next_state.done {
7892 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
7893 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
7894 let mut next_selected_range = None;
7895
7896 let bytes_after_last_selection =
7897 buffer.bytes_in_range(last_selection.end..buffer.len());
7898 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
7899 let query_matches = query
7900 .stream_find_iter(bytes_after_last_selection)
7901 .map(|result| (last_selection.end, result))
7902 .chain(
7903 query
7904 .stream_find_iter(bytes_before_first_selection)
7905 .map(|result| (0, result)),
7906 );
7907
7908 for (start_offset, query_match) in query_matches {
7909 let query_match = query_match.unwrap(); // can only fail due to I/O
7910 let offset_range =
7911 start_offset + query_match.start()..start_offset + query_match.end();
7912 let display_range = offset_range.start.to_display_point(&display_map)
7913 ..offset_range.end.to_display_point(&display_map);
7914
7915 if !select_next_state.wordwise
7916 || (!movement::is_inside_word(&display_map, display_range.start)
7917 && !movement::is_inside_word(&display_map, display_range.end))
7918 {
7919 // TODO: This is n^2, because we might check all the selections
7920 if !selections
7921 .iter()
7922 .any(|selection| selection.range().overlaps(&offset_range))
7923 {
7924 next_selected_range = Some(offset_range);
7925 break;
7926 }
7927 }
7928 }
7929
7930 if let Some(next_selected_range) = next_selected_range {
7931 select_next_match_ranges(
7932 self,
7933 next_selected_range,
7934 replace_newest,
7935 autoscroll,
7936 cx,
7937 );
7938 } else {
7939 select_next_state.done = true;
7940 }
7941 }
7942
7943 self.select_next_state = Some(select_next_state);
7944 } else {
7945 let mut only_carets = true;
7946 let mut same_text_selected = true;
7947 let mut selected_text = None;
7948
7949 let mut selections_iter = selections.iter().peekable();
7950 while let Some(selection) = selections_iter.next() {
7951 if selection.start != selection.end {
7952 only_carets = false;
7953 }
7954
7955 if same_text_selected {
7956 if selected_text.is_none() {
7957 selected_text =
7958 Some(buffer.text_for_range(selection.range()).collect::<String>());
7959 }
7960
7961 if let Some(next_selection) = selections_iter.peek() {
7962 if next_selection.range().len() == selection.range().len() {
7963 let next_selected_text = buffer
7964 .text_for_range(next_selection.range())
7965 .collect::<String>();
7966 if Some(next_selected_text) != selected_text {
7967 same_text_selected = false;
7968 selected_text = None;
7969 }
7970 } else {
7971 same_text_selected = false;
7972 selected_text = None;
7973 }
7974 }
7975 }
7976 }
7977
7978 if only_carets {
7979 for selection in &mut selections {
7980 let word_range = movement::surrounding_word(
7981 &display_map,
7982 selection.start.to_display_point(&display_map),
7983 );
7984 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
7985 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
7986 selection.goal = SelectionGoal::None;
7987 selection.reversed = false;
7988 select_next_match_ranges(
7989 self,
7990 selection.start..selection.end,
7991 replace_newest,
7992 autoscroll,
7993 cx,
7994 );
7995 }
7996
7997 if selections.len() == 1 {
7998 let selection = selections
7999 .last()
8000 .expect("ensured that there's only one selection");
8001 let query = buffer
8002 .text_for_range(selection.start..selection.end)
8003 .collect::<String>();
8004 let is_empty = query.is_empty();
8005 let select_state = SelectNextState {
8006 query: AhoCorasick::new(&[query])?,
8007 wordwise: true,
8008 done: is_empty,
8009 };
8010 self.select_next_state = Some(select_state);
8011 } else {
8012 self.select_next_state = None;
8013 }
8014 } else if let Some(selected_text) = selected_text {
8015 self.select_next_state = Some(SelectNextState {
8016 query: AhoCorasick::new(&[selected_text])?,
8017 wordwise: false,
8018 done: false,
8019 });
8020 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
8021 }
8022 }
8023 Ok(())
8024 }
8025
8026 pub fn select_all_matches(
8027 &mut self,
8028 _action: &SelectAllMatches,
8029 cx: &mut ViewContext<Self>,
8030 ) -> Result<()> {
8031 self.push_to_selection_history();
8032 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8033
8034 self.select_next_match_internal(&display_map, false, None, cx)?;
8035 let Some(select_next_state) = self.select_next_state.as_mut() else {
8036 return Ok(());
8037 };
8038 if select_next_state.done {
8039 return Ok(());
8040 }
8041
8042 let mut new_selections = self.selections.all::<usize>(cx);
8043
8044 let buffer = &display_map.buffer_snapshot;
8045 let query_matches = select_next_state
8046 .query
8047 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
8048
8049 for query_match in query_matches {
8050 let query_match = query_match.unwrap(); // can only fail due to I/O
8051 let offset_range = query_match.start()..query_match.end();
8052 let display_range = offset_range.start.to_display_point(&display_map)
8053 ..offset_range.end.to_display_point(&display_map);
8054
8055 if !select_next_state.wordwise
8056 || (!movement::is_inside_word(&display_map, display_range.start)
8057 && !movement::is_inside_word(&display_map, display_range.end))
8058 {
8059 self.selections.change_with(cx, |selections| {
8060 new_selections.push(Selection {
8061 id: selections.new_selection_id(),
8062 start: offset_range.start,
8063 end: offset_range.end,
8064 reversed: false,
8065 goal: SelectionGoal::None,
8066 });
8067 });
8068 }
8069 }
8070
8071 new_selections.sort_by_key(|selection| selection.start);
8072 let mut ix = 0;
8073 while ix + 1 < new_selections.len() {
8074 let current_selection = &new_selections[ix];
8075 let next_selection = &new_selections[ix + 1];
8076 if current_selection.range().overlaps(&next_selection.range()) {
8077 if current_selection.id < next_selection.id {
8078 new_selections.remove(ix + 1);
8079 } else {
8080 new_selections.remove(ix);
8081 }
8082 } else {
8083 ix += 1;
8084 }
8085 }
8086
8087 select_next_state.done = true;
8088 self.unfold_ranges(
8089 new_selections.iter().map(|selection| selection.range()),
8090 false,
8091 false,
8092 cx,
8093 );
8094 self.change_selections(Some(Autoscroll::fit()), cx, |selections| {
8095 selections.select(new_selections)
8096 });
8097
8098 Ok(())
8099 }
8100
8101 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
8102 self.push_to_selection_history();
8103 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8104 self.select_next_match_internal(
8105 &display_map,
8106 action.replace_newest,
8107 Some(Autoscroll::newest()),
8108 cx,
8109 )?;
8110 Ok(())
8111 }
8112
8113 pub fn select_previous(
8114 &mut self,
8115 action: &SelectPrevious,
8116 cx: &mut ViewContext<Self>,
8117 ) -> Result<()> {
8118 self.push_to_selection_history();
8119 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8120 let buffer = &display_map.buffer_snapshot;
8121 let mut selections = self.selections.all::<usize>(cx);
8122 if let Some(mut select_prev_state) = self.select_prev_state.take() {
8123 let query = &select_prev_state.query;
8124 if !select_prev_state.done {
8125 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
8126 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
8127 let mut next_selected_range = None;
8128 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
8129 let bytes_before_last_selection =
8130 buffer.reversed_bytes_in_range(0..last_selection.start);
8131 let bytes_after_first_selection =
8132 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
8133 let query_matches = query
8134 .stream_find_iter(bytes_before_last_selection)
8135 .map(|result| (last_selection.start, result))
8136 .chain(
8137 query
8138 .stream_find_iter(bytes_after_first_selection)
8139 .map(|result| (buffer.len(), result)),
8140 );
8141 for (end_offset, query_match) in query_matches {
8142 let query_match = query_match.unwrap(); // can only fail due to I/O
8143 let offset_range =
8144 end_offset - query_match.end()..end_offset - query_match.start();
8145 let display_range = offset_range.start.to_display_point(&display_map)
8146 ..offset_range.end.to_display_point(&display_map);
8147
8148 if !select_prev_state.wordwise
8149 || (!movement::is_inside_word(&display_map, display_range.start)
8150 && !movement::is_inside_word(&display_map, display_range.end))
8151 {
8152 next_selected_range = Some(offset_range);
8153 break;
8154 }
8155 }
8156
8157 if let Some(next_selected_range) = next_selected_range {
8158 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
8159 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8160 if action.replace_newest {
8161 s.delete(s.newest_anchor().id);
8162 }
8163 s.insert_range(next_selected_range);
8164 });
8165 } else {
8166 select_prev_state.done = true;
8167 }
8168 }
8169
8170 self.select_prev_state = Some(select_prev_state);
8171 } else {
8172 let mut only_carets = true;
8173 let mut same_text_selected = true;
8174 let mut selected_text = None;
8175
8176 let mut selections_iter = selections.iter().peekable();
8177 while let Some(selection) = selections_iter.next() {
8178 if selection.start != selection.end {
8179 only_carets = false;
8180 }
8181
8182 if same_text_selected {
8183 if selected_text.is_none() {
8184 selected_text =
8185 Some(buffer.text_for_range(selection.range()).collect::<String>());
8186 }
8187
8188 if let Some(next_selection) = selections_iter.peek() {
8189 if next_selection.range().len() == selection.range().len() {
8190 let next_selected_text = buffer
8191 .text_for_range(next_selection.range())
8192 .collect::<String>();
8193 if Some(next_selected_text) != selected_text {
8194 same_text_selected = false;
8195 selected_text = None;
8196 }
8197 } else {
8198 same_text_selected = false;
8199 selected_text = None;
8200 }
8201 }
8202 }
8203 }
8204
8205 if only_carets {
8206 for selection in &mut selections {
8207 let word_range = movement::surrounding_word(
8208 &display_map,
8209 selection.start.to_display_point(&display_map),
8210 );
8211 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
8212 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
8213 selection.goal = SelectionGoal::None;
8214 selection.reversed = false;
8215 }
8216 if selections.len() == 1 {
8217 let selection = selections
8218 .last()
8219 .expect("ensured that there's only one selection");
8220 let query = buffer
8221 .text_for_range(selection.start..selection.end)
8222 .collect::<String>();
8223 let is_empty = query.is_empty();
8224 let select_state = SelectNextState {
8225 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
8226 wordwise: true,
8227 done: is_empty,
8228 };
8229 self.select_prev_state = Some(select_state);
8230 } else {
8231 self.select_prev_state = None;
8232 }
8233
8234 self.unfold_ranges(
8235 selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
8236 false,
8237 true,
8238 cx,
8239 );
8240 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8241 s.select(selections);
8242 });
8243 } else if let Some(selected_text) = selected_text {
8244 self.select_prev_state = Some(SelectNextState {
8245 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
8246 wordwise: false,
8247 done: false,
8248 });
8249 self.select_previous(action, cx)?;
8250 }
8251 }
8252 Ok(())
8253 }
8254
8255 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
8256 let text_layout_details = &self.text_layout_details(cx);
8257 self.transact(cx, |this, cx| {
8258 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
8259 let mut edits = Vec::new();
8260 let mut selection_edit_ranges = Vec::new();
8261 let mut last_toggled_row = None;
8262 let snapshot = this.buffer.read(cx).read(cx);
8263 let empty_str: Arc<str> = Arc::default();
8264 let mut suffixes_inserted = Vec::new();
8265
8266 fn comment_prefix_range(
8267 snapshot: &MultiBufferSnapshot,
8268 row: MultiBufferRow,
8269 comment_prefix: &str,
8270 comment_prefix_whitespace: &str,
8271 ) -> Range<Point> {
8272 let start = Point::new(row.0, snapshot.indent_size_for_line(row).len);
8273
8274 let mut line_bytes = snapshot
8275 .bytes_in_range(start..snapshot.max_point())
8276 .flatten()
8277 .copied();
8278
8279 // If this line currently begins with the line comment prefix, then record
8280 // the range containing the prefix.
8281 if line_bytes
8282 .by_ref()
8283 .take(comment_prefix.len())
8284 .eq(comment_prefix.bytes())
8285 {
8286 // Include any whitespace that matches the comment prefix.
8287 let matching_whitespace_len = line_bytes
8288 .zip(comment_prefix_whitespace.bytes())
8289 .take_while(|(a, b)| a == b)
8290 .count() as u32;
8291 let end = Point::new(
8292 start.row,
8293 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
8294 );
8295 start..end
8296 } else {
8297 start..start
8298 }
8299 }
8300
8301 fn comment_suffix_range(
8302 snapshot: &MultiBufferSnapshot,
8303 row: MultiBufferRow,
8304 comment_suffix: &str,
8305 comment_suffix_has_leading_space: bool,
8306 ) -> Range<Point> {
8307 let end = Point::new(row.0, snapshot.line_len(row));
8308 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
8309
8310 let mut line_end_bytes = snapshot
8311 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
8312 .flatten()
8313 .copied();
8314
8315 let leading_space_len = if suffix_start_column > 0
8316 && line_end_bytes.next() == Some(b' ')
8317 && comment_suffix_has_leading_space
8318 {
8319 1
8320 } else {
8321 0
8322 };
8323
8324 // If this line currently begins with the line comment prefix, then record
8325 // the range containing the prefix.
8326 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
8327 let start = Point::new(end.row, suffix_start_column - leading_space_len);
8328 start..end
8329 } else {
8330 end..end
8331 }
8332 }
8333
8334 // TODO: Handle selections that cross excerpts
8335 for selection in &mut selections {
8336 let start_column = snapshot
8337 .indent_size_for_line(MultiBufferRow(selection.start.row))
8338 .len;
8339 let language = if let Some(language) =
8340 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
8341 {
8342 language
8343 } else {
8344 continue;
8345 };
8346
8347 selection_edit_ranges.clear();
8348
8349 // If multiple selections contain a given row, avoid processing that
8350 // row more than once.
8351 let mut start_row = MultiBufferRow(selection.start.row);
8352 if last_toggled_row == Some(start_row) {
8353 start_row = start_row.next_row();
8354 }
8355 let end_row =
8356 if selection.end.row > selection.start.row && selection.end.column == 0 {
8357 MultiBufferRow(selection.end.row - 1)
8358 } else {
8359 MultiBufferRow(selection.end.row)
8360 };
8361 last_toggled_row = Some(end_row);
8362
8363 if start_row > end_row {
8364 continue;
8365 }
8366
8367 // If the language has line comments, toggle those.
8368 let full_comment_prefixes = language.line_comment_prefixes();
8369 if !full_comment_prefixes.is_empty() {
8370 let first_prefix = full_comment_prefixes
8371 .first()
8372 .expect("prefixes is non-empty");
8373 let prefix_trimmed_lengths = full_comment_prefixes
8374 .iter()
8375 .map(|p| p.trim_end_matches(' ').len())
8376 .collect::<SmallVec<[usize; 4]>>();
8377
8378 let mut all_selection_lines_are_comments = true;
8379
8380 for row in start_row.0..=end_row.0 {
8381 let row = MultiBufferRow(row);
8382 if start_row < end_row && snapshot.is_line_blank(row) {
8383 continue;
8384 }
8385
8386 let prefix_range = full_comment_prefixes
8387 .iter()
8388 .zip(prefix_trimmed_lengths.iter().copied())
8389 .map(|(prefix, trimmed_prefix_len)| {
8390 comment_prefix_range(
8391 snapshot.deref(),
8392 row,
8393 &prefix[..trimmed_prefix_len],
8394 &prefix[trimmed_prefix_len..],
8395 )
8396 })
8397 .max_by_key(|range| range.end.column - range.start.column)
8398 .expect("prefixes is non-empty");
8399
8400 if prefix_range.is_empty() {
8401 all_selection_lines_are_comments = false;
8402 }
8403
8404 selection_edit_ranges.push(prefix_range);
8405 }
8406
8407 if all_selection_lines_are_comments {
8408 edits.extend(
8409 selection_edit_ranges
8410 .iter()
8411 .cloned()
8412 .map(|range| (range, empty_str.clone())),
8413 );
8414 } else {
8415 let min_column = selection_edit_ranges
8416 .iter()
8417 .map(|range| range.start.column)
8418 .min()
8419 .unwrap_or(0);
8420 edits.extend(selection_edit_ranges.iter().map(|range| {
8421 let position = Point::new(range.start.row, min_column);
8422 (position..position, first_prefix.clone())
8423 }));
8424 }
8425 } else if let Some((full_comment_prefix, comment_suffix)) =
8426 language.block_comment_delimiters()
8427 {
8428 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
8429 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
8430 let prefix_range = comment_prefix_range(
8431 snapshot.deref(),
8432 start_row,
8433 comment_prefix,
8434 comment_prefix_whitespace,
8435 );
8436 let suffix_range = comment_suffix_range(
8437 snapshot.deref(),
8438 end_row,
8439 comment_suffix.trim_start_matches(' '),
8440 comment_suffix.starts_with(' '),
8441 );
8442
8443 if prefix_range.is_empty() || suffix_range.is_empty() {
8444 edits.push((
8445 prefix_range.start..prefix_range.start,
8446 full_comment_prefix.clone(),
8447 ));
8448 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
8449 suffixes_inserted.push((end_row, comment_suffix.len()));
8450 } else {
8451 edits.push((prefix_range, empty_str.clone()));
8452 edits.push((suffix_range, empty_str.clone()));
8453 }
8454 } else {
8455 continue;
8456 }
8457 }
8458
8459 drop(snapshot);
8460 this.buffer.update(cx, |buffer, cx| {
8461 buffer.edit(edits, None, cx);
8462 });
8463
8464 // Adjust selections so that they end before any comment suffixes that
8465 // were inserted.
8466 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
8467 let mut selections = this.selections.all::<Point>(cx);
8468 let snapshot = this.buffer.read(cx).read(cx);
8469 for selection in &mut selections {
8470 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
8471 match row.cmp(&MultiBufferRow(selection.end.row)) {
8472 Ordering::Less => {
8473 suffixes_inserted.next();
8474 continue;
8475 }
8476 Ordering::Greater => break,
8477 Ordering::Equal => {
8478 if selection.end.column == snapshot.line_len(row) {
8479 if selection.is_empty() {
8480 selection.start.column -= suffix_len as u32;
8481 }
8482 selection.end.column -= suffix_len as u32;
8483 }
8484 break;
8485 }
8486 }
8487 }
8488 }
8489
8490 drop(snapshot);
8491 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
8492
8493 let selections = this.selections.all::<Point>(cx);
8494 let selections_on_single_row = selections.windows(2).all(|selections| {
8495 selections[0].start.row == selections[1].start.row
8496 && selections[0].end.row == selections[1].end.row
8497 && selections[0].start.row == selections[0].end.row
8498 });
8499 let selections_selecting = selections
8500 .iter()
8501 .any(|selection| selection.start != selection.end);
8502 let advance_downwards = action.advance_downwards
8503 && selections_on_single_row
8504 && !selections_selecting
8505 && !matches!(this.mode, EditorMode::SingleLine { .. });
8506
8507 if advance_downwards {
8508 let snapshot = this.buffer.read(cx).snapshot(cx);
8509
8510 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8511 s.move_cursors_with(|display_snapshot, display_point, _| {
8512 let mut point = display_point.to_point(display_snapshot);
8513 point.row += 1;
8514 point = snapshot.clip_point(point, Bias::Left);
8515 let display_point = point.to_display_point(display_snapshot);
8516 let goal = SelectionGoal::HorizontalPosition(
8517 display_snapshot
8518 .x_for_display_point(display_point, &text_layout_details)
8519 .into(),
8520 );
8521 (display_point, goal)
8522 })
8523 });
8524 }
8525 });
8526 }
8527
8528 pub fn select_enclosing_symbol(
8529 &mut self,
8530 _: &SelectEnclosingSymbol,
8531 cx: &mut ViewContext<Self>,
8532 ) {
8533 let buffer = self.buffer.read(cx).snapshot(cx);
8534 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8535
8536 fn update_selection(
8537 selection: &Selection<usize>,
8538 buffer_snap: &MultiBufferSnapshot,
8539 ) -> Option<Selection<usize>> {
8540 let cursor = selection.head();
8541 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
8542 for symbol in symbols.iter().rev() {
8543 let start = symbol.range.start.to_offset(&buffer_snap);
8544 let end = symbol.range.end.to_offset(&buffer_snap);
8545 let new_range = start..end;
8546 if start < selection.start || end > selection.end {
8547 return Some(Selection {
8548 id: selection.id,
8549 start: new_range.start,
8550 end: new_range.end,
8551 goal: SelectionGoal::None,
8552 reversed: selection.reversed,
8553 });
8554 }
8555 }
8556 None
8557 }
8558
8559 let mut selected_larger_symbol = false;
8560 let new_selections = old_selections
8561 .iter()
8562 .map(|selection| match update_selection(selection, &buffer) {
8563 Some(new_selection) => {
8564 if new_selection.range() != selection.range() {
8565 selected_larger_symbol = true;
8566 }
8567 new_selection
8568 }
8569 None => selection.clone(),
8570 })
8571 .collect::<Vec<_>>();
8572
8573 if selected_larger_symbol {
8574 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8575 s.select(new_selections);
8576 });
8577 }
8578 }
8579
8580 pub fn select_larger_syntax_node(
8581 &mut self,
8582 _: &SelectLargerSyntaxNode,
8583 cx: &mut ViewContext<Self>,
8584 ) {
8585 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8586 let buffer = self.buffer.read(cx).snapshot(cx);
8587 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8588
8589 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8590 let mut selected_larger_node = false;
8591 let new_selections = old_selections
8592 .iter()
8593 .map(|selection| {
8594 let old_range = selection.start..selection.end;
8595 let mut new_range = old_range.clone();
8596 while let Some(containing_range) =
8597 buffer.range_for_syntax_ancestor(new_range.clone())
8598 {
8599 new_range = containing_range;
8600 if !display_map.intersects_fold(new_range.start)
8601 && !display_map.intersects_fold(new_range.end)
8602 {
8603 break;
8604 }
8605 }
8606
8607 selected_larger_node |= new_range != old_range;
8608 Selection {
8609 id: selection.id,
8610 start: new_range.start,
8611 end: new_range.end,
8612 goal: SelectionGoal::None,
8613 reversed: selection.reversed,
8614 }
8615 })
8616 .collect::<Vec<_>>();
8617
8618 if selected_larger_node {
8619 stack.push(old_selections);
8620 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8621 s.select(new_selections);
8622 });
8623 }
8624 self.select_larger_syntax_node_stack = stack;
8625 }
8626
8627 pub fn select_smaller_syntax_node(
8628 &mut self,
8629 _: &SelectSmallerSyntaxNode,
8630 cx: &mut ViewContext<Self>,
8631 ) {
8632 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8633 if let Some(selections) = stack.pop() {
8634 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8635 s.select(selections.to_vec());
8636 });
8637 }
8638 self.select_larger_syntax_node_stack = stack;
8639 }
8640
8641 fn refresh_runnables(&mut self, cx: &mut ViewContext<Self>) -> Task<()> {
8642 if !EditorSettings::get_global(cx).gutter.runnables {
8643 self.clear_tasks();
8644 return Task::ready(());
8645 }
8646 let project = self.project.clone();
8647 cx.spawn(|this, mut cx| async move {
8648 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
8649 this.display_map.update(cx, |map, cx| map.snapshot(cx))
8650 }) else {
8651 return;
8652 };
8653
8654 let Some(project) = project else {
8655 return;
8656 };
8657
8658 let hide_runnables = project
8659 .update(&mut cx, |project, cx| {
8660 // Do not display any test indicators in non-dev server remote projects.
8661 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
8662 })
8663 .unwrap_or(true);
8664 if hide_runnables {
8665 return;
8666 }
8667 let new_rows =
8668 cx.background_executor()
8669 .spawn({
8670 let snapshot = display_snapshot.clone();
8671 async move {
8672 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
8673 }
8674 })
8675 .await;
8676 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
8677
8678 this.update(&mut cx, |this, _| {
8679 this.clear_tasks();
8680 for (key, value) in rows {
8681 this.insert_tasks(key, value);
8682 }
8683 })
8684 .ok();
8685 })
8686 }
8687 fn fetch_runnable_ranges(
8688 snapshot: &DisplaySnapshot,
8689 range: Range<Anchor>,
8690 ) -> Vec<language::RunnableRange> {
8691 snapshot.buffer_snapshot.runnable_ranges(range).collect()
8692 }
8693
8694 fn runnable_rows(
8695 project: Model<Project>,
8696 snapshot: DisplaySnapshot,
8697 runnable_ranges: Vec<RunnableRange>,
8698 mut cx: AsyncWindowContext,
8699 ) -> Vec<((BufferId, u32), RunnableTasks)> {
8700 runnable_ranges
8701 .into_iter()
8702 .filter_map(|mut runnable| {
8703 let tasks = cx
8704 .update(|cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
8705 .ok()?;
8706 if tasks.is_empty() {
8707 return None;
8708 }
8709
8710 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
8711
8712 let row = snapshot
8713 .buffer_snapshot
8714 .buffer_line_for_row(MultiBufferRow(point.row))?
8715 .1
8716 .start
8717 .row;
8718
8719 let context_range =
8720 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
8721 Some((
8722 (runnable.buffer_id, row),
8723 RunnableTasks {
8724 templates: tasks,
8725 offset: MultiBufferOffset(runnable.run_range.start),
8726 context_range,
8727 column: point.column,
8728 extra_variables: runnable.extra_captures,
8729 },
8730 ))
8731 })
8732 .collect()
8733 }
8734
8735 fn templates_with_tags(
8736 project: &Model<Project>,
8737 runnable: &mut Runnable,
8738 cx: &WindowContext<'_>,
8739 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
8740 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
8741 let (worktree_id, file) = project
8742 .buffer_for_id(runnable.buffer, cx)
8743 .and_then(|buffer| buffer.read(cx).file())
8744 .map(|file| (WorktreeId::from_usize(file.worktree_id()), file.clone()))
8745 .unzip();
8746
8747 (project.task_inventory().clone(), worktree_id, file)
8748 });
8749
8750 let inventory = inventory.read(cx);
8751 let tags = mem::take(&mut runnable.tags);
8752 let mut tags: Vec<_> = tags
8753 .into_iter()
8754 .flat_map(|tag| {
8755 let tag = tag.0.clone();
8756 inventory
8757 .list_tasks(
8758 file.clone(),
8759 Some(runnable.language.clone()),
8760 worktree_id,
8761 cx,
8762 )
8763 .into_iter()
8764 .filter(move |(_, template)| {
8765 template.tags.iter().any(|source_tag| source_tag == &tag)
8766 })
8767 })
8768 .sorted_by_key(|(kind, _)| kind.to_owned())
8769 .collect();
8770 if let Some((leading_tag_source, _)) = tags.first() {
8771 // Strongest source wins; if we have worktree tag binding, prefer that to
8772 // global and language bindings;
8773 // if we have a global binding, prefer that to language binding.
8774 let first_mismatch = tags
8775 .iter()
8776 .position(|(tag_source, _)| tag_source != leading_tag_source);
8777 if let Some(index) = first_mismatch {
8778 tags.truncate(index);
8779 }
8780 }
8781
8782 tags
8783 }
8784
8785 pub fn move_to_enclosing_bracket(
8786 &mut self,
8787 _: &MoveToEnclosingBracket,
8788 cx: &mut ViewContext<Self>,
8789 ) {
8790 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8791 s.move_offsets_with(|snapshot, selection| {
8792 let Some(enclosing_bracket_ranges) =
8793 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
8794 else {
8795 return;
8796 };
8797
8798 let mut best_length = usize::MAX;
8799 let mut best_inside = false;
8800 let mut best_in_bracket_range = false;
8801 let mut best_destination = None;
8802 for (open, close) in enclosing_bracket_ranges {
8803 let close = close.to_inclusive();
8804 let length = close.end() - open.start;
8805 let inside = selection.start >= open.end && selection.end <= *close.start();
8806 let in_bracket_range = open.to_inclusive().contains(&selection.head())
8807 || close.contains(&selection.head());
8808
8809 // If best is next to a bracket and current isn't, skip
8810 if !in_bracket_range && best_in_bracket_range {
8811 continue;
8812 }
8813
8814 // Prefer smaller lengths unless best is inside and current isn't
8815 if length > best_length && (best_inside || !inside) {
8816 continue;
8817 }
8818
8819 best_length = length;
8820 best_inside = inside;
8821 best_in_bracket_range = in_bracket_range;
8822 best_destination = Some(
8823 if close.contains(&selection.start) && close.contains(&selection.end) {
8824 if inside {
8825 open.end
8826 } else {
8827 open.start
8828 }
8829 } else {
8830 if inside {
8831 *close.start()
8832 } else {
8833 *close.end()
8834 }
8835 },
8836 );
8837 }
8838
8839 if let Some(destination) = best_destination {
8840 selection.collapse_to(destination, SelectionGoal::None);
8841 }
8842 })
8843 });
8844 }
8845
8846 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
8847 self.end_selection(cx);
8848 self.selection_history.mode = SelectionHistoryMode::Undoing;
8849 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
8850 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8851 self.select_next_state = entry.select_next_state;
8852 self.select_prev_state = entry.select_prev_state;
8853 self.add_selections_state = entry.add_selections_state;
8854 self.request_autoscroll(Autoscroll::newest(), cx);
8855 }
8856 self.selection_history.mode = SelectionHistoryMode::Normal;
8857 }
8858
8859 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
8860 self.end_selection(cx);
8861 self.selection_history.mode = SelectionHistoryMode::Redoing;
8862 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
8863 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8864 self.select_next_state = entry.select_next_state;
8865 self.select_prev_state = entry.select_prev_state;
8866 self.add_selections_state = entry.add_selections_state;
8867 self.request_autoscroll(Autoscroll::newest(), cx);
8868 }
8869 self.selection_history.mode = SelectionHistoryMode::Normal;
8870 }
8871
8872 pub fn expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
8873 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
8874 }
8875
8876 pub fn expand_excerpts_down(
8877 &mut self,
8878 action: &ExpandExcerptsDown,
8879 cx: &mut ViewContext<Self>,
8880 ) {
8881 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
8882 }
8883
8884 pub fn expand_excerpts_up(&mut self, action: &ExpandExcerptsUp, cx: &mut ViewContext<Self>) {
8885 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
8886 }
8887
8888 pub fn expand_excerpts_for_direction(
8889 &mut self,
8890 lines: u32,
8891 direction: ExpandExcerptDirection,
8892 cx: &mut ViewContext<Self>,
8893 ) {
8894 let selections = self.selections.disjoint_anchors();
8895
8896 let lines = if lines == 0 {
8897 EditorSettings::get_global(cx).expand_excerpt_lines
8898 } else {
8899 lines
8900 };
8901
8902 self.buffer.update(cx, |buffer, cx| {
8903 buffer.expand_excerpts(
8904 selections
8905 .into_iter()
8906 .map(|selection| selection.head().excerpt_id)
8907 .dedup(),
8908 lines,
8909 direction,
8910 cx,
8911 )
8912 })
8913 }
8914
8915 pub fn expand_excerpt(
8916 &mut self,
8917 excerpt: ExcerptId,
8918 direction: ExpandExcerptDirection,
8919 cx: &mut ViewContext<Self>,
8920 ) {
8921 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
8922 self.buffer.update(cx, |buffer, cx| {
8923 buffer.expand_excerpts([excerpt], lines, direction, cx)
8924 })
8925 }
8926
8927 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
8928 self.go_to_diagnostic_impl(Direction::Next, cx)
8929 }
8930
8931 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
8932 self.go_to_diagnostic_impl(Direction::Prev, cx)
8933 }
8934
8935 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
8936 let buffer = self.buffer.read(cx).snapshot(cx);
8937 let selection = self.selections.newest::<usize>(cx);
8938
8939 // If there is an active Diagnostic Popover jump to its diagnostic instead.
8940 if direction == Direction::Next {
8941 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
8942 let (group_id, jump_to) = popover.activation_info();
8943 if self.activate_diagnostics(group_id, cx) {
8944 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8945 let mut new_selection = s.newest_anchor().clone();
8946 new_selection.collapse_to(jump_to, SelectionGoal::None);
8947 s.select_anchors(vec![new_selection.clone()]);
8948 });
8949 }
8950 return;
8951 }
8952 }
8953
8954 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
8955 active_diagnostics
8956 .primary_range
8957 .to_offset(&buffer)
8958 .to_inclusive()
8959 });
8960 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
8961 if active_primary_range.contains(&selection.head()) {
8962 *active_primary_range.start()
8963 } else {
8964 selection.head()
8965 }
8966 } else {
8967 selection.head()
8968 };
8969 let snapshot = self.snapshot(cx);
8970 loop {
8971 let diagnostics = if direction == Direction::Prev {
8972 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
8973 } else {
8974 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
8975 }
8976 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start));
8977 let group = diagnostics
8978 // relies on diagnostics_in_range to return diagnostics with the same starting range to
8979 // be sorted in a stable way
8980 // skip until we are at current active diagnostic, if it exists
8981 .skip_while(|entry| {
8982 (match direction {
8983 Direction::Prev => entry.range.start >= search_start,
8984 Direction::Next => entry.range.start <= search_start,
8985 }) && self
8986 .active_diagnostics
8987 .as_ref()
8988 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
8989 })
8990 .find_map(|entry| {
8991 if entry.diagnostic.is_primary
8992 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
8993 && !entry.range.is_empty()
8994 // if we match with the active diagnostic, skip it
8995 && Some(entry.diagnostic.group_id)
8996 != self.active_diagnostics.as_ref().map(|d| d.group_id)
8997 {
8998 Some((entry.range, entry.diagnostic.group_id))
8999 } else {
9000 None
9001 }
9002 });
9003
9004 if let Some((primary_range, group_id)) = group {
9005 if self.activate_diagnostics(group_id, cx) {
9006 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9007 s.select(vec![Selection {
9008 id: selection.id,
9009 start: primary_range.start,
9010 end: primary_range.start,
9011 reversed: false,
9012 goal: SelectionGoal::None,
9013 }]);
9014 });
9015 }
9016 break;
9017 } else {
9018 // Cycle around to the start of the buffer, potentially moving back to the start of
9019 // the currently active diagnostic.
9020 active_primary_range.take();
9021 if direction == Direction::Prev {
9022 if search_start == buffer.len() {
9023 break;
9024 } else {
9025 search_start = buffer.len();
9026 }
9027 } else if search_start == 0 {
9028 break;
9029 } else {
9030 search_start = 0;
9031 }
9032 }
9033 }
9034 }
9035
9036 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
9037 let snapshot = self
9038 .display_map
9039 .update(cx, |display_map, cx| display_map.snapshot(cx));
9040 let selection = self.selections.newest::<Point>(cx);
9041
9042 if !self.seek_in_direction(
9043 &snapshot,
9044 selection.head(),
9045 false,
9046 snapshot.buffer_snapshot.git_diff_hunks_in_range(
9047 MultiBufferRow(selection.head().row + 1)..MultiBufferRow::MAX,
9048 ),
9049 cx,
9050 ) {
9051 let wrapped_point = Point::zero();
9052 self.seek_in_direction(
9053 &snapshot,
9054 wrapped_point,
9055 true,
9056 snapshot.buffer_snapshot.git_diff_hunks_in_range(
9057 MultiBufferRow(wrapped_point.row + 1)..MultiBufferRow::MAX,
9058 ),
9059 cx,
9060 );
9061 }
9062 }
9063
9064 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
9065 let snapshot = self
9066 .display_map
9067 .update(cx, |display_map, cx| display_map.snapshot(cx));
9068 let selection = self.selections.newest::<Point>(cx);
9069
9070 if !self.seek_in_direction(
9071 &snapshot,
9072 selection.head(),
9073 false,
9074 snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
9075 MultiBufferRow(0)..MultiBufferRow(selection.head().row),
9076 ),
9077 cx,
9078 ) {
9079 let wrapped_point = snapshot.buffer_snapshot.max_point();
9080 self.seek_in_direction(
9081 &snapshot,
9082 wrapped_point,
9083 true,
9084 snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
9085 MultiBufferRow(0)..MultiBufferRow(wrapped_point.row),
9086 ),
9087 cx,
9088 );
9089 }
9090 }
9091
9092 fn seek_in_direction(
9093 &mut self,
9094 snapshot: &DisplaySnapshot,
9095 initial_point: Point,
9096 is_wrapped: bool,
9097 hunks: impl Iterator<Item = DiffHunk<MultiBufferRow>>,
9098 cx: &mut ViewContext<Editor>,
9099 ) -> bool {
9100 let display_point = initial_point.to_display_point(snapshot);
9101 let mut hunks = hunks
9102 .map(|hunk| diff_hunk_to_display(&hunk, &snapshot))
9103 .filter(|hunk| is_wrapped || !hunk.contains_display_row(display_point.row()))
9104 .dedup();
9105
9106 if let Some(hunk) = hunks.next() {
9107 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9108 let row = hunk.start_display_row();
9109 let point = DisplayPoint::new(row, 0);
9110 s.select_display_ranges([point..point]);
9111 });
9112
9113 true
9114 } else {
9115 false
9116 }
9117 }
9118
9119 pub fn go_to_definition(
9120 &mut self,
9121 _: &GoToDefinition,
9122 cx: &mut ViewContext<Self>,
9123 ) -> Task<Result<Navigated>> {
9124 let definition = self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
9125 cx.spawn(|editor, mut cx| async move {
9126 if definition.await? == Navigated::Yes {
9127 return Ok(Navigated::Yes);
9128 }
9129 match editor.update(&mut cx, |editor, cx| {
9130 editor.find_all_references(&FindAllReferences, cx)
9131 })? {
9132 Some(references) => references.await,
9133 None => Ok(Navigated::No),
9134 }
9135 })
9136 }
9137
9138 pub fn go_to_declaration(
9139 &mut self,
9140 _: &GoToDeclaration,
9141 cx: &mut ViewContext<Self>,
9142 ) -> Task<Result<Navigated>> {
9143 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, cx)
9144 }
9145
9146 pub fn go_to_declaration_split(
9147 &mut self,
9148 _: &GoToDeclaration,
9149 cx: &mut ViewContext<Self>,
9150 ) -> Task<Result<Navigated>> {
9151 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, cx)
9152 }
9153
9154 pub fn go_to_implementation(
9155 &mut self,
9156 _: &GoToImplementation,
9157 cx: &mut ViewContext<Self>,
9158 ) -> Task<Result<Navigated>> {
9159 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx)
9160 }
9161
9162 pub fn go_to_implementation_split(
9163 &mut self,
9164 _: &GoToImplementationSplit,
9165 cx: &mut ViewContext<Self>,
9166 ) -> Task<Result<Navigated>> {
9167 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx)
9168 }
9169
9170 pub fn go_to_type_definition(
9171 &mut self,
9172 _: &GoToTypeDefinition,
9173 cx: &mut ViewContext<Self>,
9174 ) -> Task<Result<Navigated>> {
9175 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx)
9176 }
9177
9178 pub fn go_to_definition_split(
9179 &mut self,
9180 _: &GoToDefinitionSplit,
9181 cx: &mut ViewContext<Self>,
9182 ) -> Task<Result<Navigated>> {
9183 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx)
9184 }
9185
9186 pub fn go_to_type_definition_split(
9187 &mut self,
9188 _: &GoToTypeDefinitionSplit,
9189 cx: &mut ViewContext<Self>,
9190 ) -> Task<Result<Navigated>> {
9191 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx)
9192 }
9193
9194 fn go_to_definition_of_kind(
9195 &mut self,
9196 kind: GotoDefinitionKind,
9197 split: bool,
9198 cx: &mut ViewContext<Self>,
9199 ) -> Task<Result<Navigated>> {
9200 let Some(workspace) = self.workspace() else {
9201 return Task::ready(Ok(Navigated::No));
9202 };
9203 let buffer = self.buffer.read(cx);
9204 let head = self.selections.newest::<usize>(cx).head();
9205 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
9206 text_anchor
9207 } else {
9208 return Task::ready(Ok(Navigated::No));
9209 };
9210
9211 let project = workspace.read(cx).project().clone();
9212 let definitions = project.update(cx, |project, cx| match kind {
9213 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
9214 GotoDefinitionKind::Declaration => project.declaration(&buffer, head, cx),
9215 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
9216 GotoDefinitionKind::Implementation => project.implementation(&buffer, head, cx),
9217 });
9218
9219 cx.spawn(|editor, mut cx| async move {
9220 let definitions = definitions.await?;
9221 let navigated = editor
9222 .update(&mut cx, |editor, cx| {
9223 editor.navigate_to_hover_links(
9224 Some(kind),
9225 definitions
9226 .into_iter()
9227 .filter(|location| {
9228 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
9229 })
9230 .map(HoverLink::Text)
9231 .collect::<Vec<_>>(),
9232 split,
9233 cx,
9234 )
9235 })?
9236 .await?;
9237 anyhow::Ok(navigated)
9238 })
9239 }
9240
9241 pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext<Self>) {
9242 let position = self.selections.newest_anchor().head();
9243 let Some((buffer, buffer_position)) =
9244 self.buffer.read(cx).text_anchor_for_position(position, cx)
9245 else {
9246 return;
9247 };
9248
9249 cx.spawn(|editor, mut cx| async move {
9250 if let Some((_, url)) = find_url(&buffer, buffer_position, cx.clone()) {
9251 editor.update(&mut cx, |_, cx| {
9252 cx.open_url(&url);
9253 })
9254 } else {
9255 Ok(())
9256 }
9257 })
9258 .detach();
9259 }
9260
9261 pub fn open_file(&mut self, _: &OpenFile, cx: &mut ViewContext<Self>) {
9262 let Some(workspace) = self.workspace() else {
9263 return;
9264 };
9265
9266 let position = self.selections.newest_anchor().head();
9267
9268 let Some((buffer, buffer_position)) =
9269 self.buffer.read(cx).text_anchor_for_position(position, cx)
9270 else {
9271 return;
9272 };
9273
9274 let Some(project) = self.project.clone() else {
9275 return;
9276 };
9277
9278 cx.spawn(|_, mut cx| async move {
9279 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
9280
9281 if let Some((_, path)) = result {
9282 workspace
9283 .update(&mut cx, |workspace, cx| {
9284 workspace.open_resolved_path(path, cx)
9285 })?
9286 .await?;
9287 }
9288 anyhow::Ok(())
9289 })
9290 .detach();
9291 }
9292
9293 pub(crate) fn navigate_to_hover_links(
9294 &mut self,
9295 kind: Option<GotoDefinitionKind>,
9296 mut definitions: Vec<HoverLink>,
9297 split: bool,
9298 cx: &mut ViewContext<Editor>,
9299 ) -> Task<Result<Navigated>> {
9300 // If there is one definition, just open it directly
9301 if definitions.len() == 1 {
9302 let definition = definitions.pop().unwrap();
9303
9304 enum TargetTaskResult {
9305 Location(Option<Location>),
9306 AlreadyNavigated,
9307 }
9308
9309 let target_task = match definition {
9310 HoverLink::Text(link) => {
9311 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
9312 }
9313 HoverLink::InlayHint(lsp_location, server_id) => {
9314 let computation = self.compute_target_location(lsp_location, server_id, cx);
9315 cx.background_executor().spawn(async move {
9316 let location = computation.await?;
9317 Ok(TargetTaskResult::Location(location))
9318 })
9319 }
9320 HoverLink::Url(url) => {
9321 cx.open_url(&url);
9322 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
9323 }
9324 HoverLink::File(path) => {
9325 if let Some(workspace) = self.workspace() {
9326 cx.spawn(|_, mut cx| async move {
9327 workspace
9328 .update(&mut cx, |workspace, cx| {
9329 workspace.open_resolved_path(path, cx)
9330 })?
9331 .await
9332 .map(|_| TargetTaskResult::AlreadyNavigated)
9333 })
9334 } else {
9335 Task::ready(Ok(TargetTaskResult::Location(None)))
9336 }
9337 }
9338 };
9339 cx.spawn(|editor, mut cx| async move {
9340 let target = match target_task.await.context("target resolution task")? {
9341 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
9342 TargetTaskResult::Location(None) => return Ok(Navigated::No),
9343 TargetTaskResult::Location(Some(target)) => target,
9344 };
9345
9346 editor.update(&mut cx, |editor, cx| {
9347 let Some(workspace) = editor.workspace() else {
9348 return Navigated::No;
9349 };
9350 let pane = workspace.read(cx).active_pane().clone();
9351
9352 let range = target.range.to_offset(target.buffer.read(cx));
9353 let range = editor.range_for_match(&range);
9354
9355 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
9356 let buffer = target.buffer.read(cx);
9357 let range = check_multiline_range(buffer, range);
9358 editor.change_selections(Some(Autoscroll::focused()), cx, |s| {
9359 s.select_ranges([range]);
9360 });
9361 } else {
9362 cx.window_context().defer(move |cx| {
9363 let target_editor: View<Self> =
9364 workspace.update(cx, |workspace, cx| {
9365 let pane = if split {
9366 workspace.adjacent_pane(cx)
9367 } else {
9368 workspace.active_pane().clone()
9369 };
9370
9371 workspace.open_project_item(
9372 pane,
9373 target.buffer.clone(),
9374 true,
9375 true,
9376 cx,
9377 )
9378 });
9379 target_editor.update(cx, |target_editor, cx| {
9380 // When selecting a definition in a different buffer, disable the nav history
9381 // to avoid creating a history entry at the previous cursor location.
9382 pane.update(cx, |pane, _| pane.disable_history());
9383 let buffer = target.buffer.read(cx);
9384 let range = check_multiline_range(buffer, range);
9385 target_editor.change_selections(
9386 Some(Autoscroll::focused()),
9387 cx,
9388 |s| {
9389 s.select_ranges([range]);
9390 },
9391 );
9392 pane.update(cx, |pane, _| pane.enable_history());
9393 });
9394 });
9395 }
9396 Navigated::Yes
9397 })
9398 })
9399 } else if !definitions.is_empty() {
9400 let replica_id = self.replica_id(cx);
9401 cx.spawn(|editor, mut cx| async move {
9402 let (title, location_tasks, workspace) = editor
9403 .update(&mut cx, |editor, cx| {
9404 let tab_kind = match kind {
9405 Some(GotoDefinitionKind::Implementation) => "Implementations",
9406 _ => "Definitions",
9407 };
9408 let title = definitions
9409 .iter()
9410 .find_map(|definition| match definition {
9411 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
9412 let buffer = origin.buffer.read(cx);
9413 format!(
9414 "{} for {}",
9415 tab_kind,
9416 buffer
9417 .text_for_range(origin.range.clone())
9418 .collect::<String>()
9419 )
9420 }),
9421 HoverLink::InlayHint(_, _) => None,
9422 HoverLink::Url(_) => None,
9423 HoverLink::File(_) => None,
9424 })
9425 .unwrap_or(tab_kind.to_string());
9426 let location_tasks = definitions
9427 .into_iter()
9428 .map(|definition| match definition {
9429 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
9430 HoverLink::InlayHint(lsp_location, server_id) => {
9431 editor.compute_target_location(lsp_location, server_id, cx)
9432 }
9433 HoverLink::Url(_) => Task::ready(Ok(None)),
9434 HoverLink::File(_) => Task::ready(Ok(None)),
9435 })
9436 .collect::<Vec<_>>();
9437 (title, location_tasks, editor.workspace().clone())
9438 })
9439 .context("location tasks preparation")?;
9440
9441 let locations = futures::future::join_all(location_tasks)
9442 .await
9443 .into_iter()
9444 .filter_map(|location| location.transpose())
9445 .collect::<Result<_>>()
9446 .context("location tasks")?;
9447
9448 let Some(workspace) = workspace else {
9449 return Ok(Navigated::No);
9450 };
9451 let opened = workspace
9452 .update(&mut cx, |workspace, cx| {
9453 Self::open_locations_in_multibuffer(
9454 workspace, locations, replica_id, title, split, cx,
9455 )
9456 })
9457 .ok();
9458
9459 anyhow::Ok(Navigated::from_bool(opened.is_some()))
9460 })
9461 } else {
9462 Task::ready(Ok(Navigated::No))
9463 }
9464 }
9465
9466 fn compute_target_location(
9467 &self,
9468 lsp_location: lsp::Location,
9469 server_id: LanguageServerId,
9470 cx: &mut ViewContext<Editor>,
9471 ) -> Task<anyhow::Result<Option<Location>>> {
9472 let Some(project) = self.project.clone() else {
9473 return Task::Ready(Some(Ok(None)));
9474 };
9475
9476 cx.spawn(move |editor, mut cx| async move {
9477 let location_task = editor.update(&mut cx, |editor, cx| {
9478 project.update(cx, |project, cx| {
9479 let language_server_name =
9480 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
9481 project
9482 .language_server_for_buffer(buffer.read(cx), server_id, cx)
9483 .map(|(lsp_adapter, _)| lsp_adapter.name.clone())
9484 });
9485 language_server_name.map(|language_server_name| {
9486 project.open_local_buffer_via_lsp(
9487 lsp_location.uri.clone(),
9488 server_id,
9489 language_server_name,
9490 cx,
9491 )
9492 })
9493 })
9494 })?;
9495 let location = match location_task {
9496 Some(task) => Some({
9497 let target_buffer_handle = task.await.context("open local buffer")?;
9498 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
9499 let target_start = target_buffer
9500 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
9501 let target_end = target_buffer
9502 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
9503 target_buffer.anchor_after(target_start)
9504 ..target_buffer.anchor_before(target_end)
9505 })?;
9506 Location {
9507 buffer: target_buffer_handle,
9508 range,
9509 }
9510 }),
9511 None => None,
9512 };
9513 Ok(location)
9514 })
9515 }
9516
9517 pub fn find_all_references(
9518 &mut self,
9519 _: &FindAllReferences,
9520 cx: &mut ViewContext<Self>,
9521 ) -> Option<Task<Result<Navigated>>> {
9522 let multi_buffer = self.buffer.read(cx);
9523 let selection = self.selections.newest::<usize>(cx);
9524 let head = selection.head();
9525
9526 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
9527 let head_anchor = multi_buffer_snapshot.anchor_at(
9528 head,
9529 if head < selection.tail() {
9530 Bias::Right
9531 } else {
9532 Bias::Left
9533 },
9534 );
9535
9536 match self
9537 .find_all_references_task_sources
9538 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
9539 {
9540 Ok(_) => {
9541 log::info!(
9542 "Ignoring repeated FindAllReferences invocation with the position of already running task"
9543 );
9544 return None;
9545 }
9546 Err(i) => {
9547 self.find_all_references_task_sources.insert(i, head_anchor);
9548 }
9549 }
9550
9551 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
9552 let replica_id = self.replica_id(cx);
9553 let workspace = self.workspace()?;
9554 let project = workspace.read(cx).project().clone();
9555 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
9556 Some(cx.spawn(|editor, mut cx| async move {
9557 let _cleanup = defer({
9558 let mut cx = cx.clone();
9559 move || {
9560 let _ = editor.update(&mut cx, |editor, _| {
9561 if let Ok(i) =
9562 editor
9563 .find_all_references_task_sources
9564 .binary_search_by(|anchor| {
9565 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
9566 })
9567 {
9568 editor.find_all_references_task_sources.remove(i);
9569 }
9570 });
9571 }
9572 });
9573
9574 let locations = references.await?;
9575 if locations.is_empty() {
9576 return anyhow::Ok(Navigated::No);
9577 }
9578
9579 workspace.update(&mut cx, |workspace, cx| {
9580 let title = locations
9581 .first()
9582 .as_ref()
9583 .map(|location| {
9584 let buffer = location.buffer.read(cx);
9585 format!(
9586 "References to `{}`",
9587 buffer
9588 .text_for_range(location.range.clone())
9589 .collect::<String>()
9590 )
9591 })
9592 .unwrap();
9593 Self::open_locations_in_multibuffer(
9594 workspace, locations, replica_id, title, false, cx,
9595 );
9596 Navigated::Yes
9597 })
9598 }))
9599 }
9600
9601 /// Opens a multibuffer with the given project locations in it
9602 pub fn open_locations_in_multibuffer(
9603 workspace: &mut Workspace,
9604 mut locations: Vec<Location>,
9605 replica_id: ReplicaId,
9606 title: String,
9607 split: bool,
9608 cx: &mut ViewContext<Workspace>,
9609 ) {
9610 // If there are multiple definitions, open them in a multibuffer
9611 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
9612 let mut locations = locations.into_iter().peekable();
9613 let mut ranges_to_highlight = Vec::new();
9614 let capability = workspace.project().read(cx).capability();
9615
9616 let excerpt_buffer = cx.new_model(|cx| {
9617 let mut multibuffer = MultiBuffer::new(replica_id, capability);
9618 while let Some(location) = locations.next() {
9619 let buffer = location.buffer.read(cx);
9620 let mut ranges_for_buffer = Vec::new();
9621 let range = location.range.to_offset(buffer);
9622 ranges_for_buffer.push(range.clone());
9623
9624 while let Some(next_location) = locations.peek() {
9625 if next_location.buffer == location.buffer {
9626 ranges_for_buffer.push(next_location.range.to_offset(buffer));
9627 locations.next();
9628 } else {
9629 break;
9630 }
9631 }
9632
9633 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
9634 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
9635 location.buffer.clone(),
9636 ranges_for_buffer,
9637 DEFAULT_MULTIBUFFER_CONTEXT,
9638 cx,
9639 ))
9640 }
9641
9642 multibuffer.with_title(title)
9643 });
9644
9645 let editor = cx.new_view(|cx| {
9646 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), true, cx)
9647 });
9648 editor.update(cx, |editor, cx| {
9649 if let Some(first_range) = ranges_to_highlight.first() {
9650 editor.change_selections(None, cx, |selections| {
9651 selections.clear_disjoint();
9652 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
9653 });
9654 }
9655 editor.highlight_background::<Self>(
9656 &ranges_to_highlight,
9657 |theme| theme.editor_highlighted_line_background,
9658 cx,
9659 );
9660 });
9661
9662 let item = Box::new(editor);
9663 let item_id = item.item_id();
9664
9665 if split {
9666 workspace.split_item(SplitDirection::Right, item.clone(), cx);
9667 } else {
9668 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
9669 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
9670 pane.close_current_preview_item(cx)
9671 } else {
9672 None
9673 }
9674 });
9675 workspace.add_item_to_active_pane(item.clone(), destination_index, true, cx);
9676 }
9677 workspace.active_pane().update(cx, |pane, cx| {
9678 pane.set_preview_item_id(Some(item_id), cx);
9679 });
9680 }
9681
9682 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9683 use language::ToOffset as _;
9684
9685 let project = self.project.clone()?;
9686 let selection = self.selections.newest_anchor().clone();
9687 let (cursor_buffer, cursor_buffer_position) = self
9688 .buffer
9689 .read(cx)
9690 .text_anchor_for_position(selection.head(), cx)?;
9691 let (tail_buffer, cursor_buffer_position_end) = self
9692 .buffer
9693 .read(cx)
9694 .text_anchor_for_position(selection.tail(), cx)?;
9695 if tail_buffer != cursor_buffer {
9696 return None;
9697 }
9698
9699 let snapshot = cursor_buffer.read(cx).snapshot();
9700 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
9701 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
9702 let prepare_rename = project.update(cx, |project, cx| {
9703 project.prepare_rename(cursor_buffer.clone(), cursor_buffer_offset, cx)
9704 });
9705 drop(snapshot);
9706
9707 Some(cx.spawn(|this, mut cx| async move {
9708 let rename_range = if let Some(range) = prepare_rename.await? {
9709 Some(range)
9710 } else {
9711 this.update(&mut cx, |this, cx| {
9712 let buffer = this.buffer.read(cx).snapshot(cx);
9713 let mut buffer_highlights = this
9714 .document_highlights_for_position(selection.head(), &buffer)
9715 .filter(|highlight| {
9716 highlight.start.excerpt_id == selection.head().excerpt_id
9717 && highlight.end.excerpt_id == selection.head().excerpt_id
9718 });
9719 buffer_highlights
9720 .next()
9721 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
9722 })?
9723 };
9724 if let Some(rename_range) = rename_range {
9725 this.update(&mut cx, |this, cx| {
9726 let snapshot = cursor_buffer.read(cx).snapshot();
9727 let rename_buffer_range = rename_range.to_offset(&snapshot);
9728 let cursor_offset_in_rename_range =
9729 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
9730 let cursor_offset_in_rename_range_end =
9731 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
9732
9733 this.take_rename(false, cx);
9734 let buffer = this.buffer.read(cx).read(cx);
9735 let cursor_offset = selection.head().to_offset(&buffer);
9736 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
9737 let rename_end = rename_start + rename_buffer_range.len();
9738 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
9739 let mut old_highlight_id = None;
9740 let old_name: Arc<str> = buffer
9741 .chunks(rename_start..rename_end, true)
9742 .map(|chunk| {
9743 if old_highlight_id.is_none() {
9744 old_highlight_id = chunk.syntax_highlight_id;
9745 }
9746 chunk.text
9747 })
9748 .collect::<String>()
9749 .into();
9750
9751 drop(buffer);
9752
9753 // Position the selection in the rename editor so that it matches the current selection.
9754 this.show_local_selections = false;
9755 let rename_editor = cx.new_view(|cx| {
9756 let mut editor = Editor::single_line(cx);
9757 editor.buffer.update(cx, |buffer, cx| {
9758 buffer.edit([(0..0, old_name.clone())], None, cx)
9759 });
9760 let rename_selection_range = match cursor_offset_in_rename_range
9761 .cmp(&cursor_offset_in_rename_range_end)
9762 {
9763 Ordering::Equal => {
9764 editor.select_all(&SelectAll, cx);
9765 return editor;
9766 }
9767 Ordering::Less => {
9768 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
9769 }
9770 Ordering::Greater => {
9771 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
9772 }
9773 };
9774 if rename_selection_range.end > old_name.len() {
9775 editor.select_all(&SelectAll, cx);
9776 } else {
9777 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
9778 s.select_ranges([rename_selection_range]);
9779 });
9780 }
9781 editor
9782 });
9783 cx.subscribe(&rename_editor, |_, _, e, cx| match e {
9784 EditorEvent::Focused => cx.emit(EditorEvent::FocusedIn),
9785 _ => {}
9786 })
9787 .detach();
9788
9789 let write_highlights =
9790 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
9791 let read_highlights =
9792 this.clear_background_highlights::<DocumentHighlightRead>(cx);
9793 let ranges = write_highlights
9794 .iter()
9795 .flat_map(|(_, ranges)| ranges.iter())
9796 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
9797 .cloned()
9798 .collect();
9799
9800 this.highlight_text::<Rename>(
9801 ranges,
9802 HighlightStyle {
9803 fade_out: Some(0.6),
9804 ..Default::default()
9805 },
9806 cx,
9807 );
9808 let rename_focus_handle = rename_editor.focus_handle(cx);
9809 cx.focus(&rename_focus_handle);
9810 let block_id = this.insert_blocks(
9811 [BlockProperties {
9812 style: BlockStyle::Flex,
9813 position: range.start,
9814 height: 1,
9815 render: Box::new({
9816 let rename_editor = rename_editor.clone();
9817 move |cx: &mut BlockContext| {
9818 let mut text_style = cx.editor_style.text.clone();
9819 if let Some(highlight_style) = old_highlight_id
9820 .and_then(|h| h.style(&cx.editor_style.syntax))
9821 {
9822 text_style = text_style.highlight(highlight_style);
9823 }
9824 div()
9825 .pl(cx.anchor_x)
9826 .child(EditorElement::new(
9827 &rename_editor,
9828 EditorStyle {
9829 background: cx.theme().system().transparent,
9830 local_player: cx.editor_style.local_player,
9831 text: text_style,
9832 scrollbar_width: cx.editor_style.scrollbar_width,
9833 syntax: cx.editor_style.syntax.clone(),
9834 status: cx.editor_style.status.clone(),
9835 inlay_hints_style: HighlightStyle {
9836 color: Some(cx.theme().status().hint),
9837 font_weight: Some(FontWeight::BOLD),
9838 ..HighlightStyle::default()
9839 },
9840 suggestions_style: HighlightStyle {
9841 color: Some(cx.theme().status().predictive),
9842 ..HighlightStyle::default()
9843 },
9844 ..EditorStyle::default()
9845 },
9846 ))
9847 .into_any_element()
9848 }
9849 }),
9850 disposition: BlockDisposition::Below,
9851 priority: 0,
9852 }],
9853 Some(Autoscroll::fit()),
9854 cx,
9855 )[0];
9856 this.pending_rename = Some(RenameState {
9857 range,
9858 old_name,
9859 editor: rename_editor,
9860 block_id,
9861 });
9862 })?;
9863 }
9864
9865 Ok(())
9866 }))
9867 }
9868
9869 pub fn confirm_rename(
9870 &mut self,
9871 _: &ConfirmRename,
9872 cx: &mut ViewContext<Self>,
9873 ) -> Option<Task<Result<()>>> {
9874 let rename = self.take_rename(false, cx)?;
9875 let workspace = self.workspace()?;
9876 let (start_buffer, start) = self
9877 .buffer
9878 .read(cx)
9879 .text_anchor_for_position(rename.range.start, cx)?;
9880 let (end_buffer, end) = self
9881 .buffer
9882 .read(cx)
9883 .text_anchor_for_position(rename.range.end, cx)?;
9884 if start_buffer != end_buffer {
9885 return None;
9886 }
9887
9888 let buffer = start_buffer;
9889 let range = start..end;
9890 let old_name = rename.old_name;
9891 let new_name = rename.editor.read(cx).text(cx);
9892
9893 let rename = workspace
9894 .read(cx)
9895 .project()
9896 .clone()
9897 .update(cx, |project, cx| {
9898 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
9899 });
9900 let workspace = workspace.downgrade();
9901
9902 Some(cx.spawn(|editor, mut cx| async move {
9903 let project_transaction = rename.await?;
9904 Self::open_project_transaction(
9905 &editor,
9906 workspace,
9907 project_transaction,
9908 format!("Rename: {} → {}", old_name, new_name),
9909 cx.clone(),
9910 )
9911 .await?;
9912
9913 editor.update(&mut cx, |editor, cx| {
9914 editor.refresh_document_highlights(cx);
9915 })?;
9916 Ok(())
9917 }))
9918 }
9919
9920 fn take_rename(
9921 &mut self,
9922 moving_cursor: bool,
9923 cx: &mut ViewContext<Self>,
9924 ) -> Option<RenameState> {
9925 let rename = self.pending_rename.take()?;
9926 if rename.editor.focus_handle(cx).is_focused(cx) {
9927 cx.focus(&self.focus_handle);
9928 }
9929
9930 self.remove_blocks(
9931 [rename.block_id].into_iter().collect(),
9932 Some(Autoscroll::fit()),
9933 cx,
9934 );
9935 self.clear_highlights::<Rename>(cx);
9936 self.show_local_selections = true;
9937
9938 if moving_cursor {
9939 let rename_editor = rename.editor.read(cx);
9940 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
9941
9942 // Update the selection to match the position of the selection inside
9943 // the rename editor.
9944 let snapshot = self.buffer.read(cx).read(cx);
9945 let rename_range = rename.range.to_offset(&snapshot);
9946 let cursor_in_editor = snapshot
9947 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
9948 .min(rename_range.end);
9949 drop(snapshot);
9950
9951 self.change_selections(None, cx, |s| {
9952 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
9953 });
9954 } else {
9955 self.refresh_document_highlights(cx);
9956 }
9957
9958 Some(rename)
9959 }
9960
9961 pub fn pending_rename(&self) -> Option<&RenameState> {
9962 self.pending_rename.as_ref()
9963 }
9964
9965 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9966 let project = match &self.project {
9967 Some(project) => project.clone(),
9968 None => return None,
9969 };
9970
9971 Some(self.perform_format(project, FormatTrigger::Manual, cx))
9972 }
9973
9974 fn perform_format(
9975 &mut self,
9976 project: Model<Project>,
9977 trigger: FormatTrigger,
9978 cx: &mut ViewContext<Self>,
9979 ) -> Task<Result<()>> {
9980 let buffer = self.buffer().clone();
9981 let mut buffers = buffer.read(cx).all_buffers();
9982 if trigger == FormatTrigger::Save {
9983 buffers.retain(|buffer| buffer.read(cx).is_dirty());
9984 }
9985
9986 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
9987 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
9988
9989 cx.spawn(|_, mut cx| async move {
9990 let transaction = futures::select_biased! {
9991 () = timeout => {
9992 log::warn!("timed out waiting for formatting");
9993 None
9994 }
9995 transaction = format.log_err().fuse() => transaction,
9996 };
9997
9998 buffer
9999 .update(&mut cx, |buffer, cx| {
10000 if let Some(transaction) = transaction {
10001 if !buffer.is_singleton() {
10002 buffer.push_transaction(&transaction.0, cx);
10003 }
10004 }
10005
10006 cx.notify();
10007 })
10008 .ok();
10009
10010 Ok(())
10011 })
10012 }
10013
10014 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
10015 if let Some(project) = self.project.clone() {
10016 self.buffer.update(cx, |multi_buffer, cx| {
10017 project.update(cx, |project, cx| {
10018 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
10019 });
10020 })
10021 }
10022 }
10023
10024 fn cancel_language_server_work(
10025 &mut self,
10026 _: &CancelLanguageServerWork,
10027 cx: &mut ViewContext<Self>,
10028 ) {
10029 if let Some(project) = self.project.clone() {
10030 self.buffer.update(cx, |multi_buffer, cx| {
10031 project.update(cx, |project, cx| {
10032 project.cancel_language_server_work_for_buffers(multi_buffer.all_buffers(), cx);
10033 });
10034 })
10035 }
10036 }
10037
10038 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
10039 cx.show_character_palette();
10040 }
10041
10042 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
10043 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
10044 let buffer = self.buffer.read(cx).snapshot(cx);
10045 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
10046 let is_valid = buffer
10047 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
10048 .any(|entry| {
10049 entry.diagnostic.is_primary
10050 && !entry.range.is_empty()
10051 && entry.range.start == primary_range_start
10052 && entry.diagnostic.message == active_diagnostics.primary_message
10053 });
10054
10055 if is_valid != active_diagnostics.is_valid {
10056 active_diagnostics.is_valid = is_valid;
10057 let mut new_styles = HashMap::default();
10058 for (block_id, diagnostic) in &active_diagnostics.blocks {
10059 new_styles.insert(
10060 *block_id,
10061 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
10062 );
10063 }
10064 self.display_map.update(cx, |display_map, _cx| {
10065 display_map.replace_blocks(new_styles)
10066 });
10067 }
10068 }
10069 }
10070
10071 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
10072 self.dismiss_diagnostics(cx);
10073 let snapshot = self.snapshot(cx);
10074 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
10075 let buffer = self.buffer.read(cx).snapshot(cx);
10076
10077 let mut primary_range = None;
10078 let mut primary_message = None;
10079 let mut group_end = Point::zero();
10080 let diagnostic_group = buffer
10081 .diagnostic_group::<MultiBufferPoint>(group_id)
10082 .filter_map(|entry| {
10083 if snapshot.is_line_folded(MultiBufferRow(entry.range.start.row))
10084 && (entry.range.start.row == entry.range.end.row
10085 || snapshot.is_line_folded(MultiBufferRow(entry.range.end.row)))
10086 {
10087 return None;
10088 }
10089 if entry.range.end > group_end {
10090 group_end = entry.range.end;
10091 }
10092 if entry.diagnostic.is_primary {
10093 primary_range = Some(entry.range.clone());
10094 primary_message = Some(entry.diagnostic.message.clone());
10095 }
10096 Some(entry)
10097 })
10098 .collect::<Vec<_>>();
10099 let primary_range = primary_range?;
10100 let primary_message = primary_message?;
10101 let primary_range =
10102 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
10103
10104 let blocks = display_map
10105 .insert_blocks(
10106 diagnostic_group.iter().map(|entry| {
10107 let diagnostic = entry.diagnostic.clone();
10108 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
10109 BlockProperties {
10110 style: BlockStyle::Fixed,
10111 position: buffer.anchor_after(entry.range.start),
10112 height: message_height,
10113 render: diagnostic_block_renderer(diagnostic, None, true, true),
10114 disposition: BlockDisposition::Below,
10115 priority: 0,
10116 }
10117 }),
10118 cx,
10119 )
10120 .into_iter()
10121 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
10122 .collect();
10123
10124 Some(ActiveDiagnosticGroup {
10125 primary_range,
10126 primary_message,
10127 group_id,
10128 blocks,
10129 is_valid: true,
10130 })
10131 });
10132 self.active_diagnostics.is_some()
10133 }
10134
10135 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
10136 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
10137 self.display_map.update(cx, |display_map, cx| {
10138 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
10139 });
10140 cx.notify();
10141 }
10142 }
10143
10144 pub fn set_selections_from_remote(
10145 &mut self,
10146 selections: Vec<Selection<Anchor>>,
10147 pending_selection: Option<Selection<Anchor>>,
10148 cx: &mut ViewContext<Self>,
10149 ) {
10150 let old_cursor_position = self.selections.newest_anchor().head();
10151 self.selections.change_with(cx, |s| {
10152 s.select_anchors(selections);
10153 if let Some(pending_selection) = pending_selection {
10154 s.set_pending(pending_selection, SelectMode::Character);
10155 } else {
10156 s.clear_pending();
10157 }
10158 });
10159 self.selections_did_change(false, &old_cursor_position, true, cx);
10160 }
10161
10162 fn push_to_selection_history(&mut self) {
10163 self.selection_history.push(SelectionHistoryEntry {
10164 selections: self.selections.disjoint_anchors(),
10165 select_next_state: self.select_next_state.clone(),
10166 select_prev_state: self.select_prev_state.clone(),
10167 add_selections_state: self.add_selections_state.clone(),
10168 });
10169 }
10170
10171 pub fn transact(
10172 &mut self,
10173 cx: &mut ViewContext<Self>,
10174 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
10175 ) -> Option<TransactionId> {
10176 self.start_transaction_at(Instant::now(), cx);
10177 update(self, cx);
10178 self.end_transaction_at(Instant::now(), cx)
10179 }
10180
10181 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
10182 self.end_selection(cx);
10183 if let Some(tx_id) = self
10184 .buffer
10185 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
10186 {
10187 self.selection_history
10188 .insert_transaction(tx_id, self.selections.disjoint_anchors());
10189 cx.emit(EditorEvent::TransactionBegun {
10190 transaction_id: tx_id,
10191 })
10192 }
10193 }
10194
10195 fn end_transaction_at(
10196 &mut self,
10197 now: Instant,
10198 cx: &mut ViewContext<Self>,
10199 ) -> Option<TransactionId> {
10200 if let Some(transaction_id) = self
10201 .buffer
10202 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
10203 {
10204 if let Some((_, end_selections)) =
10205 self.selection_history.transaction_mut(transaction_id)
10206 {
10207 *end_selections = Some(self.selections.disjoint_anchors());
10208 } else {
10209 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
10210 }
10211
10212 cx.emit(EditorEvent::Edited { transaction_id });
10213 Some(transaction_id)
10214 } else {
10215 None
10216 }
10217 }
10218
10219 pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
10220 let mut fold_ranges = Vec::new();
10221
10222 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10223
10224 let selections = self.selections.all_adjusted(cx);
10225 for selection in selections {
10226 let range = selection.range().sorted();
10227 let buffer_start_row = range.start.row;
10228
10229 for row in (0..=range.end.row).rev() {
10230 if let Some((foldable_range, fold_text)) =
10231 display_map.foldable_range(MultiBufferRow(row))
10232 {
10233 if foldable_range.end.row >= buffer_start_row {
10234 fold_ranges.push((foldable_range, fold_text));
10235 if row <= range.start.row {
10236 break;
10237 }
10238 }
10239 }
10240 }
10241 }
10242
10243 self.fold_ranges(fold_ranges, true, cx);
10244 }
10245
10246 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
10247 let buffer_row = fold_at.buffer_row;
10248 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10249
10250 if let Some((fold_range, placeholder)) = display_map.foldable_range(buffer_row) {
10251 let autoscroll = self
10252 .selections
10253 .all::<Point>(cx)
10254 .iter()
10255 .any(|selection| fold_range.overlaps(&selection.range()));
10256
10257 self.fold_ranges([(fold_range, placeholder)], autoscroll, cx);
10258 }
10259 }
10260
10261 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
10262 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10263 let buffer = &display_map.buffer_snapshot;
10264 let selections = self.selections.all::<Point>(cx);
10265 let ranges = selections
10266 .iter()
10267 .map(|s| {
10268 let range = s.display_range(&display_map).sorted();
10269 let mut start = range.start.to_point(&display_map);
10270 let mut end = range.end.to_point(&display_map);
10271 start.column = 0;
10272 end.column = buffer.line_len(MultiBufferRow(end.row));
10273 start..end
10274 })
10275 .collect::<Vec<_>>();
10276
10277 self.unfold_ranges(ranges, true, true, cx);
10278 }
10279
10280 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
10281 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10282
10283 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
10284 ..Point::new(
10285 unfold_at.buffer_row.0,
10286 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
10287 );
10288
10289 let autoscroll = self
10290 .selections
10291 .all::<Point>(cx)
10292 .iter()
10293 .any(|selection| selection.range().overlaps(&intersection_range));
10294
10295 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
10296 }
10297
10298 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
10299 let selections = self.selections.all::<Point>(cx);
10300 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10301 let line_mode = self.selections.line_mode;
10302 let ranges = selections.into_iter().map(|s| {
10303 if line_mode {
10304 let start = Point::new(s.start.row, 0);
10305 let end = Point::new(
10306 s.end.row,
10307 display_map
10308 .buffer_snapshot
10309 .line_len(MultiBufferRow(s.end.row)),
10310 );
10311 (start..end, display_map.fold_placeholder.clone())
10312 } else {
10313 (s.start..s.end, display_map.fold_placeholder.clone())
10314 }
10315 });
10316 self.fold_ranges(ranges, true, cx);
10317 }
10318
10319 pub fn fold_ranges<T: ToOffset + Clone>(
10320 &mut self,
10321 ranges: impl IntoIterator<Item = (Range<T>, FoldPlaceholder)>,
10322 auto_scroll: bool,
10323 cx: &mut ViewContext<Self>,
10324 ) {
10325 let mut fold_ranges = Vec::new();
10326 let mut buffers_affected = HashMap::default();
10327 let multi_buffer = self.buffer().read(cx);
10328 for (fold_range, fold_text) in ranges {
10329 if let Some((_, buffer, _)) =
10330 multi_buffer.excerpt_containing(fold_range.start.clone(), cx)
10331 {
10332 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
10333 };
10334 fold_ranges.push((fold_range, fold_text));
10335 }
10336
10337 let mut ranges = fold_ranges.into_iter().peekable();
10338 if ranges.peek().is_some() {
10339 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
10340
10341 if auto_scroll {
10342 self.request_autoscroll(Autoscroll::fit(), cx);
10343 }
10344
10345 for buffer in buffers_affected.into_values() {
10346 self.sync_expanded_diff_hunks(buffer, cx);
10347 }
10348
10349 cx.notify();
10350
10351 if let Some(active_diagnostics) = self.active_diagnostics.take() {
10352 // Clear diagnostics block when folding a range that contains it.
10353 let snapshot = self.snapshot(cx);
10354 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
10355 drop(snapshot);
10356 self.active_diagnostics = Some(active_diagnostics);
10357 self.dismiss_diagnostics(cx);
10358 } else {
10359 self.active_diagnostics = Some(active_diagnostics);
10360 }
10361 }
10362
10363 self.scrollbar_marker_state.dirty = true;
10364 }
10365 }
10366
10367 pub fn unfold_ranges<T: ToOffset + Clone>(
10368 &mut self,
10369 ranges: impl IntoIterator<Item = Range<T>>,
10370 inclusive: bool,
10371 auto_scroll: bool,
10372 cx: &mut ViewContext<Self>,
10373 ) {
10374 let mut unfold_ranges = Vec::new();
10375 let mut buffers_affected = HashMap::default();
10376 let multi_buffer = self.buffer().read(cx);
10377 for range in ranges {
10378 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
10379 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
10380 };
10381 unfold_ranges.push(range);
10382 }
10383
10384 let mut ranges = unfold_ranges.into_iter().peekable();
10385 if ranges.peek().is_some() {
10386 self.display_map
10387 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
10388 if auto_scroll {
10389 self.request_autoscroll(Autoscroll::fit(), cx);
10390 }
10391
10392 for buffer in buffers_affected.into_values() {
10393 self.sync_expanded_diff_hunks(buffer, cx);
10394 }
10395
10396 cx.notify();
10397 self.scrollbar_marker_state.dirty = true;
10398 self.active_indent_guides_state.dirty = true;
10399 }
10400 }
10401
10402 pub fn default_fold_placeholder(&self, cx: &AppContext) -> FoldPlaceholder {
10403 self.display_map.read(cx).fold_placeholder.clone()
10404 }
10405
10406 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
10407 if hovered != self.gutter_hovered {
10408 self.gutter_hovered = hovered;
10409 cx.notify();
10410 }
10411 }
10412
10413 pub fn insert_blocks(
10414 &mut self,
10415 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
10416 autoscroll: Option<Autoscroll>,
10417 cx: &mut ViewContext<Self>,
10418 ) -> Vec<CustomBlockId> {
10419 let blocks = self
10420 .display_map
10421 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
10422 if let Some(autoscroll) = autoscroll {
10423 self.request_autoscroll(autoscroll, cx);
10424 }
10425 cx.notify();
10426 blocks
10427 }
10428
10429 pub fn resize_blocks(
10430 &mut self,
10431 heights: HashMap<CustomBlockId, u32>,
10432 autoscroll: Option<Autoscroll>,
10433 cx: &mut ViewContext<Self>,
10434 ) {
10435 self.display_map
10436 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
10437 if let Some(autoscroll) = autoscroll {
10438 self.request_autoscroll(autoscroll, cx);
10439 }
10440 cx.notify();
10441 }
10442
10443 pub fn replace_blocks(
10444 &mut self,
10445 renderers: HashMap<CustomBlockId, RenderBlock>,
10446 autoscroll: Option<Autoscroll>,
10447 cx: &mut ViewContext<Self>,
10448 ) {
10449 self.display_map
10450 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
10451 if let Some(autoscroll) = autoscroll {
10452 self.request_autoscroll(autoscroll, cx);
10453 }
10454 cx.notify();
10455 }
10456
10457 pub fn remove_blocks(
10458 &mut self,
10459 block_ids: HashSet<CustomBlockId>,
10460 autoscroll: Option<Autoscroll>,
10461 cx: &mut ViewContext<Self>,
10462 ) {
10463 self.display_map.update(cx, |display_map, cx| {
10464 display_map.remove_blocks(block_ids, cx)
10465 });
10466 if let Some(autoscroll) = autoscroll {
10467 self.request_autoscroll(autoscroll, cx);
10468 }
10469 cx.notify();
10470 }
10471
10472 pub fn row_for_block(
10473 &self,
10474 block_id: CustomBlockId,
10475 cx: &mut ViewContext<Self>,
10476 ) -> Option<DisplayRow> {
10477 self.display_map
10478 .update(cx, |map, cx| map.row_for_block(block_id, cx))
10479 }
10480
10481 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
10482 self.focused_block = Some(focused_block);
10483 }
10484
10485 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
10486 self.focused_block.take()
10487 }
10488
10489 pub fn insert_creases(
10490 &mut self,
10491 creases: impl IntoIterator<Item = Crease>,
10492 cx: &mut ViewContext<Self>,
10493 ) -> Vec<CreaseId> {
10494 self.display_map
10495 .update(cx, |map, cx| map.insert_creases(creases, cx))
10496 }
10497
10498 pub fn remove_creases(
10499 &mut self,
10500 ids: impl IntoIterator<Item = CreaseId>,
10501 cx: &mut ViewContext<Self>,
10502 ) {
10503 self.display_map
10504 .update(cx, |map, cx| map.remove_creases(ids, cx));
10505 }
10506
10507 pub fn longest_row(&self, cx: &mut AppContext) -> DisplayRow {
10508 self.display_map
10509 .update(cx, |map, cx| map.snapshot(cx))
10510 .longest_row()
10511 }
10512
10513 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
10514 self.display_map
10515 .update(cx, |map, cx| map.snapshot(cx))
10516 .max_point()
10517 }
10518
10519 pub fn text(&self, cx: &AppContext) -> String {
10520 self.buffer.read(cx).read(cx).text()
10521 }
10522
10523 pub fn text_option(&self, cx: &AppContext) -> Option<String> {
10524 let text = self.text(cx);
10525 let text = text.trim();
10526
10527 if text.is_empty() {
10528 return None;
10529 }
10530
10531 Some(text.to_string())
10532 }
10533
10534 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
10535 self.transact(cx, |this, cx| {
10536 this.buffer
10537 .read(cx)
10538 .as_singleton()
10539 .expect("you can only call set_text on editors for singleton buffers")
10540 .update(cx, |buffer, cx| buffer.set_text(text, cx));
10541 });
10542 }
10543
10544 pub fn display_text(&self, cx: &mut AppContext) -> String {
10545 self.display_map
10546 .update(cx, |map, cx| map.snapshot(cx))
10547 .text()
10548 }
10549
10550 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
10551 let mut wrap_guides = smallvec::smallvec![];
10552
10553 if self.show_wrap_guides == Some(false) {
10554 return wrap_guides;
10555 }
10556
10557 let settings = self.buffer.read(cx).settings_at(0, cx);
10558 if settings.show_wrap_guides {
10559 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
10560 wrap_guides.push((soft_wrap as usize, true));
10561 } else if let SoftWrap::Bounded(soft_wrap) = self.soft_wrap_mode(cx) {
10562 wrap_guides.push((soft_wrap as usize, true));
10563 }
10564 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
10565 }
10566
10567 wrap_guides
10568 }
10569
10570 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
10571 let settings = self.buffer.read(cx).settings_at(0, cx);
10572 let mode = self
10573 .soft_wrap_mode_override
10574 .unwrap_or_else(|| settings.soft_wrap);
10575 match mode {
10576 language_settings::SoftWrap::None => SoftWrap::None,
10577 language_settings::SoftWrap::PreferLine => SoftWrap::PreferLine,
10578 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
10579 language_settings::SoftWrap::PreferredLineLength => {
10580 SoftWrap::Column(settings.preferred_line_length)
10581 }
10582 language_settings::SoftWrap::Bounded => {
10583 SoftWrap::Bounded(settings.preferred_line_length)
10584 }
10585 }
10586 }
10587
10588 pub fn set_soft_wrap_mode(
10589 &mut self,
10590 mode: language_settings::SoftWrap,
10591 cx: &mut ViewContext<Self>,
10592 ) {
10593 self.soft_wrap_mode_override = Some(mode);
10594 cx.notify();
10595 }
10596
10597 pub fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
10598 let rem_size = cx.rem_size();
10599 self.display_map.update(cx, |map, cx| {
10600 map.set_font(
10601 style.text.font(),
10602 style.text.font_size.to_pixels(rem_size),
10603 cx,
10604 )
10605 });
10606 self.style = Some(style);
10607 }
10608
10609 pub fn style(&self) -> Option<&EditorStyle> {
10610 self.style.as_ref()
10611 }
10612
10613 // Called by the element. This method is not designed to be called outside of the editor
10614 // element's layout code because it does not notify when rewrapping is computed synchronously.
10615 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
10616 self.display_map
10617 .update(cx, |map, cx| map.set_wrap_width(width, cx))
10618 }
10619
10620 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
10621 if self.soft_wrap_mode_override.is_some() {
10622 self.soft_wrap_mode_override.take();
10623 } else {
10624 let soft_wrap = match self.soft_wrap_mode(cx) {
10625 SoftWrap::None | SoftWrap::PreferLine => language_settings::SoftWrap::EditorWidth,
10626 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
10627 language_settings::SoftWrap::PreferLine
10628 }
10629 };
10630 self.soft_wrap_mode_override = Some(soft_wrap);
10631 }
10632 cx.notify();
10633 }
10634
10635 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, cx: &mut ViewContext<Self>) {
10636 let Some(workspace) = self.workspace() else {
10637 return;
10638 };
10639 let fs = workspace.read(cx).app_state().fs.clone();
10640 let current_show = TabBarSettings::get_global(cx).show;
10641 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
10642 setting.show = Some(!current_show);
10643 });
10644 }
10645
10646 pub fn toggle_indent_guides(&mut self, _: &ToggleIndentGuides, cx: &mut ViewContext<Self>) {
10647 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
10648 self.buffer
10649 .read(cx)
10650 .settings_at(0, cx)
10651 .indent_guides
10652 .enabled
10653 });
10654 self.show_indent_guides = Some(!currently_enabled);
10655 cx.notify();
10656 }
10657
10658 fn should_show_indent_guides(&self) -> Option<bool> {
10659 self.show_indent_guides
10660 }
10661
10662 pub fn toggle_line_numbers(&mut self, _: &ToggleLineNumbers, cx: &mut ViewContext<Self>) {
10663 let mut editor_settings = EditorSettings::get_global(cx).clone();
10664 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
10665 EditorSettings::override_global(editor_settings, cx);
10666 }
10667
10668 pub fn should_use_relative_line_numbers(&self, cx: &WindowContext) -> bool {
10669 self.use_relative_line_numbers
10670 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
10671 }
10672
10673 pub fn toggle_relative_line_numbers(
10674 &mut self,
10675 _: &ToggleRelativeLineNumbers,
10676 cx: &mut ViewContext<Self>,
10677 ) {
10678 let is_relative = self.should_use_relative_line_numbers(cx);
10679 self.set_relative_line_number(Some(!is_relative), cx)
10680 }
10681
10682 pub fn set_relative_line_number(
10683 &mut self,
10684 is_relative: Option<bool>,
10685 cx: &mut ViewContext<Self>,
10686 ) {
10687 self.use_relative_line_numbers = is_relative;
10688 cx.notify();
10689 }
10690
10691 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
10692 self.show_gutter = show_gutter;
10693 cx.notify();
10694 }
10695
10696 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut ViewContext<Self>) {
10697 self.show_line_numbers = Some(show_line_numbers);
10698 cx.notify();
10699 }
10700
10701 pub fn set_show_git_diff_gutter(
10702 &mut self,
10703 show_git_diff_gutter: bool,
10704 cx: &mut ViewContext<Self>,
10705 ) {
10706 self.show_git_diff_gutter = Some(show_git_diff_gutter);
10707 cx.notify();
10708 }
10709
10710 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut ViewContext<Self>) {
10711 self.show_code_actions = Some(show_code_actions);
10712 cx.notify();
10713 }
10714
10715 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut ViewContext<Self>) {
10716 self.show_runnables = Some(show_runnables);
10717 cx.notify();
10718 }
10719
10720 pub fn set_masked(&mut self, masked: bool, cx: &mut ViewContext<Self>) {
10721 if self.display_map.read(cx).masked != masked {
10722 self.display_map.update(cx, |map, _| map.masked = masked);
10723 }
10724 cx.notify()
10725 }
10726
10727 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut ViewContext<Self>) {
10728 self.show_wrap_guides = Some(show_wrap_guides);
10729 cx.notify();
10730 }
10731
10732 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut ViewContext<Self>) {
10733 self.show_indent_guides = Some(show_indent_guides);
10734 cx.notify();
10735 }
10736
10737 pub fn working_directory(&self, cx: &WindowContext) -> Option<PathBuf> {
10738 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10739 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10740 if let Some(dir) = file.abs_path(cx).parent() {
10741 return Some(dir.to_owned());
10742 }
10743 }
10744
10745 if let Some(project_path) = buffer.read(cx).project_path(cx) {
10746 return Some(project_path.path.to_path_buf());
10747 }
10748 }
10749
10750 None
10751 }
10752
10753 pub fn reveal_in_finder(&mut self, _: &RevealInFileManager, cx: &mut ViewContext<Self>) {
10754 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10755 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10756 cx.reveal_path(&file.abs_path(cx));
10757 }
10758 }
10759 }
10760
10761 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
10762 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10763 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10764 if let Some(path) = file.abs_path(cx).to_str() {
10765 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
10766 }
10767 }
10768 }
10769 }
10770
10771 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
10772 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10773 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10774 if let Some(path) = file.path().to_str() {
10775 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
10776 }
10777 }
10778 }
10779 }
10780
10781 pub fn toggle_git_blame(&mut self, _: &ToggleGitBlame, cx: &mut ViewContext<Self>) {
10782 self.show_git_blame_gutter = !self.show_git_blame_gutter;
10783
10784 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
10785 self.start_git_blame(true, cx);
10786 }
10787
10788 cx.notify();
10789 }
10790
10791 pub fn toggle_git_blame_inline(
10792 &mut self,
10793 _: &ToggleGitBlameInline,
10794 cx: &mut ViewContext<Self>,
10795 ) {
10796 self.toggle_git_blame_inline_internal(true, cx);
10797 cx.notify();
10798 }
10799
10800 pub fn git_blame_inline_enabled(&self) -> bool {
10801 self.git_blame_inline_enabled
10802 }
10803
10804 pub fn toggle_selection_menu(&mut self, _: &ToggleSelectionMenu, cx: &mut ViewContext<Self>) {
10805 self.show_selection_menu = self
10806 .show_selection_menu
10807 .map(|show_selections_menu| !show_selections_menu)
10808 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
10809
10810 cx.notify();
10811 }
10812
10813 pub fn selection_menu_enabled(&self, cx: &AppContext) -> bool {
10814 self.show_selection_menu
10815 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
10816 }
10817
10818 fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
10819 if let Some(project) = self.project.as_ref() {
10820 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
10821 return;
10822 };
10823
10824 if buffer.read(cx).file().is_none() {
10825 return;
10826 }
10827
10828 let focused = self.focus_handle(cx).contains_focused(cx);
10829
10830 let project = project.clone();
10831 let blame =
10832 cx.new_model(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
10833 self.blame_subscription = Some(cx.observe(&blame, |_, _, cx| cx.notify()));
10834 self.blame = Some(blame);
10835 }
10836 }
10837
10838 fn toggle_git_blame_inline_internal(
10839 &mut self,
10840 user_triggered: bool,
10841 cx: &mut ViewContext<Self>,
10842 ) {
10843 if self.git_blame_inline_enabled {
10844 self.git_blame_inline_enabled = false;
10845 self.show_git_blame_inline = false;
10846 self.show_git_blame_inline_delay_task.take();
10847 } else {
10848 self.git_blame_inline_enabled = true;
10849 self.start_git_blame_inline(user_triggered, cx);
10850 }
10851
10852 cx.notify();
10853 }
10854
10855 fn start_git_blame_inline(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
10856 self.start_git_blame(user_triggered, cx);
10857
10858 if ProjectSettings::get_global(cx)
10859 .git
10860 .inline_blame_delay()
10861 .is_some()
10862 {
10863 self.start_inline_blame_timer(cx);
10864 } else {
10865 self.show_git_blame_inline = true
10866 }
10867 }
10868
10869 pub fn blame(&self) -> Option<&Model<GitBlame>> {
10870 self.blame.as_ref()
10871 }
10872
10873 pub fn render_git_blame_gutter(&mut self, cx: &mut WindowContext) -> bool {
10874 self.show_git_blame_gutter && self.has_blame_entries(cx)
10875 }
10876
10877 pub fn render_git_blame_inline(&mut self, cx: &mut WindowContext) -> bool {
10878 self.show_git_blame_inline
10879 && self.focus_handle.is_focused(cx)
10880 && !self.newest_selection_head_on_empty_line(cx)
10881 && self.has_blame_entries(cx)
10882 }
10883
10884 fn has_blame_entries(&self, cx: &mut WindowContext) -> bool {
10885 self.blame()
10886 .map_or(false, |blame| blame.read(cx).has_generated_entries())
10887 }
10888
10889 fn newest_selection_head_on_empty_line(&mut self, cx: &mut WindowContext) -> bool {
10890 let cursor_anchor = self.selections.newest_anchor().head();
10891
10892 let snapshot = self.buffer.read(cx).snapshot(cx);
10893 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
10894
10895 snapshot.line_len(buffer_row) == 0
10896 }
10897
10898 fn get_permalink_to_line(&mut self, cx: &mut ViewContext<Self>) -> Result<url::Url> {
10899 let (path, selection, repo) = maybe!({
10900 let project_handle = self.project.as_ref()?.clone();
10901 let project = project_handle.read(cx);
10902
10903 let selection = self.selections.newest::<Point>(cx);
10904 let selection_range = selection.range();
10905
10906 let (buffer, selection) = if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10907 (buffer, selection_range.start.row..selection_range.end.row)
10908 } else {
10909 let buffer_ranges = self
10910 .buffer()
10911 .read(cx)
10912 .range_to_buffer_ranges(selection_range, cx);
10913
10914 let (buffer, range, _) = if selection.reversed {
10915 buffer_ranges.first()
10916 } else {
10917 buffer_ranges.last()
10918 }?;
10919
10920 let snapshot = buffer.read(cx).snapshot();
10921 let selection = text::ToPoint::to_point(&range.start, &snapshot).row
10922 ..text::ToPoint::to_point(&range.end, &snapshot).row;
10923 (buffer.clone(), selection)
10924 };
10925
10926 let path = buffer
10927 .read(cx)
10928 .file()?
10929 .as_local()?
10930 .path()
10931 .to_str()?
10932 .to_string();
10933 let repo = project.get_repo(&buffer.read(cx).project_path(cx)?, cx)?;
10934 Some((path, selection, repo))
10935 })
10936 .ok_or_else(|| anyhow!("unable to open git repository"))?;
10937
10938 const REMOTE_NAME: &str = "origin";
10939 let origin_url = repo
10940 .remote_url(REMOTE_NAME)
10941 .ok_or_else(|| anyhow!("remote \"{REMOTE_NAME}\" not found"))?;
10942 let sha = repo
10943 .head_sha()
10944 .ok_or_else(|| anyhow!("failed to read HEAD SHA"))?;
10945
10946 let (provider, remote) =
10947 parse_git_remote_url(GitHostingProviderRegistry::default_global(cx), &origin_url)
10948 .ok_or_else(|| anyhow!("failed to parse Git remote URL"))?;
10949
10950 Ok(provider.build_permalink(
10951 remote,
10952 BuildPermalinkParams {
10953 sha: &sha,
10954 path: &path,
10955 selection: Some(selection),
10956 },
10957 ))
10958 }
10959
10960 pub fn copy_permalink_to_line(&mut self, _: &CopyPermalinkToLine, cx: &mut ViewContext<Self>) {
10961 let permalink = self.get_permalink_to_line(cx);
10962
10963 match permalink {
10964 Ok(permalink) => {
10965 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
10966 }
10967 Err(err) => {
10968 let message = format!("Failed to copy permalink: {err}");
10969
10970 Err::<(), anyhow::Error>(err).log_err();
10971
10972 if let Some(workspace) = self.workspace() {
10973 workspace.update(cx, |workspace, cx| {
10974 struct CopyPermalinkToLine;
10975
10976 workspace.show_toast(
10977 Toast::new(NotificationId::unique::<CopyPermalinkToLine>(), message),
10978 cx,
10979 )
10980 })
10981 }
10982 }
10983 }
10984 }
10985
10986 pub fn copy_file_location(&mut self, _: &CopyFileLocation, cx: &mut ViewContext<Self>) {
10987 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10988 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10989 if let Some(path) = file.path().to_str() {
10990 let selection = self.selections.newest::<Point>(cx).start.row + 1;
10991 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
10992 }
10993 }
10994 }
10995 }
10996
10997 pub fn open_permalink_to_line(&mut self, _: &OpenPermalinkToLine, cx: &mut ViewContext<Self>) {
10998 let permalink = self.get_permalink_to_line(cx);
10999
11000 match permalink {
11001 Ok(permalink) => {
11002 cx.open_url(permalink.as_ref());
11003 }
11004 Err(err) => {
11005 let message = format!("Failed to open permalink: {err}");
11006
11007 Err::<(), anyhow::Error>(err).log_err();
11008
11009 if let Some(workspace) = self.workspace() {
11010 workspace.update(cx, |workspace, cx| {
11011 struct OpenPermalinkToLine;
11012
11013 workspace.show_toast(
11014 Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
11015 cx,
11016 )
11017 })
11018 }
11019 }
11020 }
11021 }
11022
11023 /// Adds or removes (on `None` color) a highlight for the rows corresponding to the anchor range given.
11024 /// On matching anchor range, replaces the old highlight; does not clear the other existing highlights.
11025 /// If multiple anchor ranges will produce highlights for the same row, the last range added will be used.
11026 pub fn highlight_rows<T: 'static>(
11027 &mut self,
11028 rows: RangeInclusive<Anchor>,
11029 color: Option<Hsla>,
11030 should_autoscroll: bool,
11031 cx: &mut ViewContext<Self>,
11032 ) {
11033 let snapshot = self.buffer().read(cx).snapshot(cx);
11034 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
11035 let existing_highlight_index = row_highlights.binary_search_by(|highlight| {
11036 highlight
11037 .range
11038 .start()
11039 .cmp(&rows.start(), &snapshot)
11040 .then(highlight.range.end().cmp(&rows.end(), &snapshot))
11041 });
11042 match (color, existing_highlight_index) {
11043 (Some(_), Ok(ix)) | (_, Err(ix)) => row_highlights.insert(
11044 ix,
11045 RowHighlight {
11046 index: post_inc(&mut self.highlight_order),
11047 range: rows,
11048 should_autoscroll,
11049 color,
11050 },
11051 ),
11052 (None, Ok(i)) => {
11053 row_highlights.remove(i);
11054 }
11055 }
11056 }
11057
11058 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
11059 pub fn clear_row_highlights<T: 'static>(&mut self) {
11060 self.highlighted_rows.remove(&TypeId::of::<T>());
11061 }
11062
11063 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
11064 pub fn highlighted_rows<T: 'static>(
11065 &self,
11066 ) -> Option<impl Iterator<Item = (&RangeInclusive<Anchor>, Option<&Hsla>)>> {
11067 Some(
11068 self.highlighted_rows
11069 .get(&TypeId::of::<T>())?
11070 .iter()
11071 .map(|highlight| (&highlight.range, highlight.color.as_ref())),
11072 )
11073 }
11074
11075 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
11076 /// Rerturns a map of display rows that are highlighted and their corresponding highlight color.
11077 /// Allows to ignore certain kinds of highlights.
11078 pub fn highlighted_display_rows(
11079 &mut self,
11080 cx: &mut WindowContext,
11081 ) -> BTreeMap<DisplayRow, Hsla> {
11082 let snapshot = self.snapshot(cx);
11083 let mut used_highlight_orders = HashMap::default();
11084 self.highlighted_rows
11085 .iter()
11086 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
11087 .fold(
11088 BTreeMap::<DisplayRow, Hsla>::new(),
11089 |mut unique_rows, highlight| {
11090 let start_row = highlight.range.start().to_display_point(&snapshot).row();
11091 let end_row = highlight.range.end().to_display_point(&snapshot).row();
11092 for row in start_row.0..=end_row.0 {
11093 let used_index =
11094 used_highlight_orders.entry(row).or_insert(highlight.index);
11095 if highlight.index >= *used_index {
11096 *used_index = highlight.index;
11097 match highlight.color {
11098 Some(hsla) => unique_rows.insert(DisplayRow(row), hsla),
11099 None => unique_rows.remove(&DisplayRow(row)),
11100 };
11101 }
11102 }
11103 unique_rows
11104 },
11105 )
11106 }
11107
11108 pub fn highlighted_display_row_for_autoscroll(
11109 &self,
11110 snapshot: &DisplaySnapshot,
11111 ) -> Option<DisplayRow> {
11112 self.highlighted_rows
11113 .values()
11114 .flat_map(|highlighted_rows| highlighted_rows.iter())
11115 .filter_map(|highlight| {
11116 if highlight.color.is_none() || !highlight.should_autoscroll {
11117 return None;
11118 }
11119 Some(highlight.range.start().to_display_point(&snapshot).row())
11120 })
11121 .min()
11122 }
11123
11124 pub fn set_search_within_ranges(
11125 &mut self,
11126 ranges: &[Range<Anchor>],
11127 cx: &mut ViewContext<Self>,
11128 ) {
11129 self.highlight_background::<SearchWithinRange>(
11130 ranges,
11131 |colors| colors.editor_document_highlight_read_background,
11132 cx,
11133 )
11134 }
11135
11136 pub fn set_breadcrumb_header(&mut self, new_header: String) {
11137 self.breadcrumb_header = Some(new_header);
11138 }
11139
11140 pub fn clear_search_within_ranges(&mut self, cx: &mut ViewContext<Self>) {
11141 self.clear_background_highlights::<SearchWithinRange>(cx);
11142 }
11143
11144 pub fn highlight_background<T: 'static>(
11145 &mut self,
11146 ranges: &[Range<Anchor>],
11147 color_fetcher: fn(&ThemeColors) -> Hsla,
11148 cx: &mut ViewContext<Self>,
11149 ) {
11150 self.background_highlights
11151 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
11152 self.scrollbar_marker_state.dirty = true;
11153 cx.notify();
11154 }
11155
11156 pub fn clear_background_highlights<T: 'static>(
11157 &mut self,
11158 cx: &mut ViewContext<Self>,
11159 ) -> Option<BackgroundHighlight> {
11160 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
11161 if !text_highlights.1.is_empty() {
11162 self.scrollbar_marker_state.dirty = true;
11163 cx.notify();
11164 }
11165 Some(text_highlights)
11166 }
11167
11168 pub fn highlight_gutter<T: 'static>(
11169 &mut self,
11170 ranges: &[Range<Anchor>],
11171 color_fetcher: fn(&AppContext) -> Hsla,
11172 cx: &mut ViewContext<Self>,
11173 ) {
11174 self.gutter_highlights
11175 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
11176 cx.notify();
11177 }
11178
11179 pub fn clear_gutter_highlights<T: 'static>(
11180 &mut self,
11181 cx: &mut ViewContext<Self>,
11182 ) -> Option<GutterHighlight> {
11183 cx.notify();
11184 self.gutter_highlights.remove(&TypeId::of::<T>())
11185 }
11186
11187 #[cfg(feature = "test-support")]
11188 pub fn all_text_background_highlights(
11189 &mut self,
11190 cx: &mut ViewContext<Self>,
11191 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11192 let snapshot = self.snapshot(cx);
11193 let buffer = &snapshot.buffer_snapshot;
11194 let start = buffer.anchor_before(0);
11195 let end = buffer.anchor_after(buffer.len());
11196 let theme = cx.theme().colors();
11197 self.background_highlights_in_range(start..end, &snapshot, theme)
11198 }
11199
11200 #[cfg(feature = "test-support")]
11201 pub fn search_background_highlights(
11202 &mut self,
11203 cx: &mut ViewContext<Self>,
11204 ) -> Vec<Range<Point>> {
11205 let snapshot = self.buffer().read(cx).snapshot(cx);
11206
11207 let highlights = self
11208 .background_highlights
11209 .get(&TypeId::of::<items::BufferSearchHighlights>());
11210
11211 if let Some((_color, ranges)) = highlights {
11212 ranges
11213 .iter()
11214 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
11215 .collect_vec()
11216 } else {
11217 vec![]
11218 }
11219 }
11220
11221 fn document_highlights_for_position<'a>(
11222 &'a self,
11223 position: Anchor,
11224 buffer: &'a MultiBufferSnapshot,
11225 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
11226 let read_highlights = self
11227 .background_highlights
11228 .get(&TypeId::of::<DocumentHighlightRead>())
11229 .map(|h| &h.1);
11230 let write_highlights = self
11231 .background_highlights
11232 .get(&TypeId::of::<DocumentHighlightWrite>())
11233 .map(|h| &h.1);
11234 let left_position = position.bias_left(buffer);
11235 let right_position = position.bias_right(buffer);
11236 read_highlights
11237 .into_iter()
11238 .chain(write_highlights)
11239 .flat_map(move |ranges| {
11240 let start_ix = match ranges.binary_search_by(|probe| {
11241 let cmp = probe.end.cmp(&left_position, buffer);
11242 if cmp.is_ge() {
11243 Ordering::Greater
11244 } else {
11245 Ordering::Less
11246 }
11247 }) {
11248 Ok(i) | Err(i) => i,
11249 };
11250
11251 ranges[start_ix..]
11252 .iter()
11253 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
11254 })
11255 }
11256
11257 pub fn has_background_highlights<T: 'static>(&self) -> bool {
11258 self.background_highlights
11259 .get(&TypeId::of::<T>())
11260 .map_or(false, |(_, highlights)| !highlights.is_empty())
11261 }
11262
11263 pub fn background_highlights_in_range(
11264 &self,
11265 search_range: Range<Anchor>,
11266 display_snapshot: &DisplaySnapshot,
11267 theme: &ThemeColors,
11268 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11269 let mut results = Vec::new();
11270 for (color_fetcher, ranges) in self.background_highlights.values() {
11271 let color = color_fetcher(theme);
11272 let start_ix = match ranges.binary_search_by(|probe| {
11273 let cmp = probe
11274 .end
11275 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11276 if cmp.is_gt() {
11277 Ordering::Greater
11278 } else {
11279 Ordering::Less
11280 }
11281 }) {
11282 Ok(i) | Err(i) => i,
11283 };
11284 for range in &ranges[start_ix..] {
11285 if range
11286 .start
11287 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11288 .is_ge()
11289 {
11290 break;
11291 }
11292
11293 let start = range.start.to_display_point(&display_snapshot);
11294 let end = range.end.to_display_point(&display_snapshot);
11295 results.push((start..end, color))
11296 }
11297 }
11298 results
11299 }
11300
11301 pub fn background_highlight_row_ranges<T: 'static>(
11302 &self,
11303 search_range: Range<Anchor>,
11304 display_snapshot: &DisplaySnapshot,
11305 count: usize,
11306 ) -> Vec<RangeInclusive<DisplayPoint>> {
11307 let mut results = Vec::new();
11308 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
11309 return vec![];
11310 };
11311
11312 let start_ix = match ranges.binary_search_by(|probe| {
11313 let cmp = probe
11314 .end
11315 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11316 if cmp.is_gt() {
11317 Ordering::Greater
11318 } else {
11319 Ordering::Less
11320 }
11321 }) {
11322 Ok(i) | Err(i) => i,
11323 };
11324 let mut push_region = |start: Option<Point>, end: Option<Point>| {
11325 if let (Some(start_display), Some(end_display)) = (start, end) {
11326 results.push(
11327 start_display.to_display_point(display_snapshot)
11328 ..=end_display.to_display_point(display_snapshot),
11329 );
11330 }
11331 };
11332 let mut start_row: Option<Point> = None;
11333 let mut end_row: Option<Point> = None;
11334 if ranges.len() > count {
11335 return Vec::new();
11336 }
11337 for range in &ranges[start_ix..] {
11338 if range
11339 .start
11340 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11341 .is_ge()
11342 {
11343 break;
11344 }
11345 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
11346 if let Some(current_row) = &end_row {
11347 if end.row == current_row.row {
11348 continue;
11349 }
11350 }
11351 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
11352 if start_row.is_none() {
11353 assert_eq!(end_row, None);
11354 start_row = Some(start);
11355 end_row = Some(end);
11356 continue;
11357 }
11358 if let Some(current_end) = end_row.as_mut() {
11359 if start.row > current_end.row + 1 {
11360 push_region(start_row, end_row);
11361 start_row = Some(start);
11362 end_row = Some(end);
11363 } else {
11364 // Merge two hunks.
11365 *current_end = end;
11366 }
11367 } else {
11368 unreachable!();
11369 }
11370 }
11371 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
11372 push_region(start_row, end_row);
11373 results
11374 }
11375
11376 pub fn gutter_highlights_in_range(
11377 &self,
11378 search_range: Range<Anchor>,
11379 display_snapshot: &DisplaySnapshot,
11380 cx: &AppContext,
11381 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11382 let mut results = Vec::new();
11383 for (color_fetcher, ranges) in self.gutter_highlights.values() {
11384 let color = color_fetcher(cx);
11385 let start_ix = match ranges.binary_search_by(|probe| {
11386 let cmp = probe
11387 .end
11388 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11389 if cmp.is_gt() {
11390 Ordering::Greater
11391 } else {
11392 Ordering::Less
11393 }
11394 }) {
11395 Ok(i) | Err(i) => i,
11396 };
11397 for range in &ranges[start_ix..] {
11398 if range
11399 .start
11400 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11401 .is_ge()
11402 {
11403 break;
11404 }
11405
11406 let start = range.start.to_display_point(&display_snapshot);
11407 let end = range.end.to_display_point(&display_snapshot);
11408 results.push((start..end, color))
11409 }
11410 }
11411 results
11412 }
11413
11414 /// Get the text ranges corresponding to the redaction query
11415 pub fn redacted_ranges(
11416 &self,
11417 search_range: Range<Anchor>,
11418 display_snapshot: &DisplaySnapshot,
11419 cx: &WindowContext,
11420 ) -> Vec<Range<DisplayPoint>> {
11421 display_snapshot
11422 .buffer_snapshot
11423 .redacted_ranges(search_range, |file| {
11424 if let Some(file) = file {
11425 file.is_private()
11426 && EditorSettings::get(Some(file.as_ref().into()), cx).redact_private_values
11427 } else {
11428 false
11429 }
11430 })
11431 .map(|range| {
11432 range.start.to_display_point(display_snapshot)
11433 ..range.end.to_display_point(display_snapshot)
11434 })
11435 .collect()
11436 }
11437
11438 pub fn highlight_text<T: 'static>(
11439 &mut self,
11440 ranges: Vec<Range<Anchor>>,
11441 style: HighlightStyle,
11442 cx: &mut ViewContext<Self>,
11443 ) {
11444 self.display_map.update(cx, |map, _| {
11445 map.highlight_text(TypeId::of::<T>(), ranges, style)
11446 });
11447 cx.notify();
11448 }
11449
11450 pub(crate) fn highlight_inlays<T: 'static>(
11451 &mut self,
11452 highlights: Vec<InlayHighlight>,
11453 style: HighlightStyle,
11454 cx: &mut ViewContext<Self>,
11455 ) {
11456 self.display_map.update(cx, |map, _| {
11457 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
11458 });
11459 cx.notify();
11460 }
11461
11462 pub fn text_highlights<'a, T: 'static>(
11463 &'a self,
11464 cx: &'a AppContext,
11465 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
11466 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
11467 }
11468
11469 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
11470 let cleared = self
11471 .display_map
11472 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
11473 if cleared {
11474 cx.notify();
11475 }
11476 }
11477
11478 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
11479 (self.read_only(cx) || self.blink_manager.read(cx).visible())
11480 && self.focus_handle.is_focused(cx)
11481 }
11482
11483 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut ViewContext<Self>) {
11484 self.show_cursor_when_unfocused = is_enabled;
11485 cx.notify();
11486 }
11487
11488 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
11489 cx.notify();
11490 }
11491
11492 fn on_buffer_event(
11493 &mut self,
11494 multibuffer: Model<MultiBuffer>,
11495 event: &multi_buffer::Event,
11496 cx: &mut ViewContext<Self>,
11497 ) {
11498 match event {
11499 multi_buffer::Event::Edited {
11500 singleton_buffer_edited,
11501 } => {
11502 self.scrollbar_marker_state.dirty = true;
11503 self.active_indent_guides_state.dirty = true;
11504 self.refresh_active_diagnostics(cx);
11505 self.refresh_code_actions(cx);
11506 if self.has_active_inline_completion(cx) {
11507 self.update_visible_inline_completion(cx);
11508 }
11509 cx.emit(EditorEvent::BufferEdited);
11510 cx.emit(SearchEvent::MatchesInvalidated);
11511 if *singleton_buffer_edited {
11512 if let Some(project) = &self.project {
11513 let project = project.read(cx);
11514 #[allow(clippy::mutable_key_type)]
11515 let languages_affected = multibuffer
11516 .read(cx)
11517 .all_buffers()
11518 .into_iter()
11519 .filter_map(|buffer| {
11520 let buffer = buffer.read(cx);
11521 let language = buffer.language()?;
11522 if project.is_local_or_ssh()
11523 && project.language_servers_for_buffer(buffer, cx).count() == 0
11524 {
11525 None
11526 } else {
11527 Some(language)
11528 }
11529 })
11530 .cloned()
11531 .collect::<HashSet<_>>();
11532 if !languages_affected.is_empty() {
11533 self.refresh_inlay_hints(
11534 InlayHintRefreshReason::BufferEdited(languages_affected),
11535 cx,
11536 );
11537 }
11538 }
11539 }
11540
11541 let Some(project) = &self.project else { return };
11542 let telemetry = project.read(cx).client().telemetry().clone();
11543 refresh_linked_ranges(self, cx);
11544 telemetry.log_edit_event("editor");
11545 }
11546 multi_buffer::Event::ExcerptsAdded {
11547 buffer,
11548 predecessor,
11549 excerpts,
11550 } => {
11551 self.tasks_update_task = Some(self.refresh_runnables(cx));
11552 cx.emit(EditorEvent::ExcerptsAdded {
11553 buffer: buffer.clone(),
11554 predecessor: *predecessor,
11555 excerpts: excerpts.clone(),
11556 });
11557 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
11558 }
11559 multi_buffer::Event::ExcerptsRemoved { ids } => {
11560 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
11561 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
11562 }
11563 multi_buffer::Event::ExcerptsEdited { ids } => {
11564 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
11565 }
11566 multi_buffer::Event::ExcerptsExpanded { ids } => {
11567 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
11568 }
11569 multi_buffer::Event::Reparsed(buffer_id) => {
11570 self.tasks_update_task = Some(self.refresh_runnables(cx));
11571
11572 cx.emit(EditorEvent::Reparsed(*buffer_id));
11573 }
11574 multi_buffer::Event::LanguageChanged(buffer_id) => {
11575 linked_editing_ranges::refresh_linked_ranges(self, cx);
11576 cx.emit(EditorEvent::Reparsed(*buffer_id));
11577 cx.notify();
11578 }
11579 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
11580 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
11581 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
11582 cx.emit(EditorEvent::TitleChanged)
11583 }
11584 multi_buffer::Event::DiffBaseChanged => {
11585 self.scrollbar_marker_state.dirty = true;
11586 cx.emit(EditorEvent::DiffBaseChanged);
11587 cx.notify();
11588 }
11589 multi_buffer::Event::DiffUpdated { buffer } => {
11590 self.sync_expanded_diff_hunks(buffer.clone(), cx);
11591 cx.notify();
11592 }
11593 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
11594 multi_buffer::Event::DiagnosticsUpdated => {
11595 self.refresh_active_diagnostics(cx);
11596 self.scrollbar_marker_state.dirty = true;
11597 cx.notify();
11598 }
11599 _ => {}
11600 };
11601 }
11602
11603 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
11604 cx.notify();
11605 }
11606
11607 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
11608 self.tasks_update_task = Some(self.refresh_runnables(cx));
11609 self.refresh_inline_completion(true, false, cx);
11610 self.refresh_inlay_hints(
11611 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
11612 self.selections.newest_anchor().head(),
11613 &self.buffer.read(cx).snapshot(cx),
11614 cx,
11615 )),
11616 cx,
11617 );
11618 let editor_settings = EditorSettings::get_global(cx);
11619 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
11620 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
11621
11622 let project_settings = ProjectSettings::get_global(cx);
11623 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
11624
11625 if self.mode == EditorMode::Full {
11626 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
11627 if self.git_blame_inline_enabled != inline_blame_enabled {
11628 self.toggle_git_blame_inline_internal(false, cx);
11629 }
11630 }
11631
11632 cx.notify();
11633 }
11634
11635 pub fn set_searchable(&mut self, searchable: bool) {
11636 self.searchable = searchable;
11637 }
11638
11639 pub fn searchable(&self) -> bool {
11640 self.searchable
11641 }
11642
11643 fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext<Self>) {
11644 self.open_excerpts_common(true, cx)
11645 }
11646
11647 fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
11648 self.open_excerpts_common(false, cx)
11649 }
11650
11651 fn open_excerpts_common(&mut self, split: bool, cx: &mut ViewContext<Self>) {
11652 let buffer = self.buffer.read(cx);
11653 if buffer.is_singleton() {
11654 cx.propagate();
11655 return;
11656 }
11657
11658 let Some(workspace) = self.workspace() else {
11659 cx.propagate();
11660 return;
11661 };
11662
11663 let mut new_selections_by_buffer = HashMap::default();
11664 for selection in self.selections.all::<usize>(cx) {
11665 for (buffer, mut range, _) in
11666 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
11667 {
11668 if selection.reversed {
11669 mem::swap(&mut range.start, &mut range.end);
11670 }
11671 new_selections_by_buffer
11672 .entry(buffer)
11673 .or_insert(Vec::new())
11674 .push(range)
11675 }
11676 }
11677
11678 // We defer the pane interaction because we ourselves are a workspace item
11679 // and activating a new item causes the pane to call a method on us reentrantly,
11680 // which panics if we're on the stack.
11681 cx.window_context().defer(move |cx| {
11682 workspace.update(cx, |workspace, cx| {
11683 let pane = if split {
11684 workspace.adjacent_pane(cx)
11685 } else {
11686 workspace.active_pane().clone()
11687 };
11688
11689 for (buffer, ranges) in new_selections_by_buffer {
11690 let editor =
11691 workspace.open_project_item::<Self>(pane.clone(), buffer, true, true, cx);
11692 editor.update(cx, |editor, cx| {
11693 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
11694 s.select_ranges(ranges);
11695 });
11696 });
11697 }
11698 })
11699 });
11700 }
11701
11702 fn jump(
11703 &mut self,
11704 path: ProjectPath,
11705 position: Point,
11706 anchor: language::Anchor,
11707 offset_from_top: u32,
11708 cx: &mut ViewContext<Self>,
11709 ) {
11710 let workspace = self.workspace();
11711 cx.spawn(|_, mut cx| async move {
11712 let workspace = workspace.ok_or_else(|| anyhow!("cannot jump without workspace"))?;
11713 let editor = workspace.update(&mut cx, |workspace, cx| {
11714 // Reset the preview item id before opening the new item
11715 workspace.active_pane().update(cx, |pane, cx| {
11716 pane.set_preview_item_id(None, cx);
11717 });
11718 workspace.open_path_preview(path, None, true, true, cx)
11719 })?;
11720 let editor = editor
11721 .await?
11722 .downcast::<Editor>()
11723 .ok_or_else(|| anyhow!("opened item was not an editor"))?
11724 .downgrade();
11725 editor.update(&mut cx, |editor, cx| {
11726 let buffer = editor
11727 .buffer()
11728 .read(cx)
11729 .as_singleton()
11730 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
11731 let buffer = buffer.read(cx);
11732 let cursor = if buffer.can_resolve(&anchor) {
11733 language::ToPoint::to_point(&anchor, buffer)
11734 } else {
11735 buffer.clip_point(position, Bias::Left)
11736 };
11737
11738 let nav_history = editor.nav_history.take();
11739 editor.change_selections(
11740 Some(Autoscroll::top_relative(offset_from_top as usize)),
11741 cx,
11742 |s| {
11743 s.select_ranges([cursor..cursor]);
11744 },
11745 );
11746 editor.nav_history = nav_history;
11747
11748 anyhow::Ok(())
11749 })??;
11750
11751 anyhow::Ok(())
11752 })
11753 .detach_and_log_err(cx);
11754 }
11755
11756 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
11757 let snapshot = self.buffer.read(cx).read(cx);
11758 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
11759 Some(
11760 ranges
11761 .iter()
11762 .map(move |range| {
11763 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
11764 })
11765 .collect(),
11766 )
11767 }
11768
11769 fn selection_replacement_ranges(
11770 &self,
11771 range: Range<OffsetUtf16>,
11772 cx: &AppContext,
11773 ) -> Vec<Range<OffsetUtf16>> {
11774 let selections = self.selections.all::<OffsetUtf16>(cx);
11775 let newest_selection = selections
11776 .iter()
11777 .max_by_key(|selection| selection.id)
11778 .unwrap();
11779 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
11780 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
11781 let snapshot = self.buffer.read(cx).read(cx);
11782 selections
11783 .into_iter()
11784 .map(|mut selection| {
11785 selection.start.0 =
11786 (selection.start.0 as isize).saturating_add(start_delta) as usize;
11787 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
11788 snapshot.clip_offset_utf16(selection.start, Bias::Left)
11789 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
11790 })
11791 .collect()
11792 }
11793
11794 fn report_editor_event(
11795 &self,
11796 operation: &'static str,
11797 file_extension: Option<String>,
11798 cx: &AppContext,
11799 ) {
11800 if cfg!(any(test, feature = "test-support")) {
11801 return;
11802 }
11803
11804 let Some(project) = &self.project else { return };
11805
11806 // If None, we are in a file without an extension
11807 let file = self
11808 .buffer
11809 .read(cx)
11810 .as_singleton()
11811 .and_then(|b| b.read(cx).file());
11812 let file_extension = file_extension.or(file
11813 .as_ref()
11814 .and_then(|file| Path::new(file.file_name(cx)).extension())
11815 .and_then(|e| e.to_str())
11816 .map(|a| a.to_string()));
11817
11818 let vim_mode = cx
11819 .global::<SettingsStore>()
11820 .raw_user_settings()
11821 .get("vim_mode")
11822 == Some(&serde_json::Value::Bool(true));
11823
11824 let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
11825 == language::language_settings::InlineCompletionProvider::Copilot;
11826 let copilot_enabled_for_language = self
11827 .buffer
11828 .read(cx)
11829 .settings_at(0, cx)
11830 .show_inline_completions;
11831
11832 let telemetry = project.read(cx).client().telemetry().clone();
11833 telemetry.report_editor_event(
11834 file_extension,
11835 vim_mode,
11836 operation,
11837 copilot_enabled,
11838 copilot_enabled_for_language,
11839 )
11840 }
11841
11842 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
11843 /// with each line being an array of {text, highlight} objects.
11844 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
11845 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
11846 return;
11847 };
11848
11849 #[derive(Serialize)]
11850 struct Chunk<'a> {
11851 text: String,
11852 highlight: Option<&'a str>,
11853 }
11854
11855 let snapshot = buffer.read(cx).snapshot();
11856 let range = self
11857 .selected_text_range(false, cx)
11858 .and_then(|selection| {
11859 if selection.range.is_empty() {
11860 None
11861 } else {
11862 Some(selection.range)
11863 }
11864 })
11865 .unwrap_or_else(|| 0..snapshot.len());
11866
11867 let chunks = snapshot.chunks(range, true);
11868 let mut lines = Vec::new();
11869 let mut line: VecDeque<Chunk> = VecDeque::new();
11870
11871 let Some(style) = self.style.as_ref() else {
11872 return;
11873 };
11874
11875 for chunk in chunks {
11876 let highlight = chunk
11877 .syntax_highlight_id
11878 .and_then(|id| id.name(&style.syntax));
11879 let mut chunk_lines = chunk.text.split('\n').peekable();
11880 while let Some(text) = chunk_lines.next() {
11881 let mut merged_with_last_token = false;
11882 if let Some(last_token) = line.back_mut() {
11883 if last_token.highlight == highlight {
11884 last_token.text.push_str(text);
11885 merged_with_last_token = true;
11886 }
11887 }
11888
11889 if !merged_with_last_token {
11890 line.push_back(Chunk {
11891 text: text.into(),
11892 highlight,
11893 });
11894 }
11895
11896 if chunk_lines.peek().is_some() {
11897 if line.len() > 1 && line.front().unwrap().text.is_empty() {
11898 line.pop_front();
11899 }
11900 if line.len() > 1 && line.back().unwrap().text.is_empty() {
11901 line.pop_back();
11902 }
11903
11904 lines.push(mem::take(&mut line));
11905 }
11906 }
11907 }
11908
11909 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
11910 return;
11911 };
11912 cx.write_to_clipboard(ClipboardItem::new_string(lines));
11913 }
11914
11915 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
11916 &self.inlay_hint_cache
11917 }
11918
11919 pub fn replay_insert_event(
11920 &mut self,
11921 text: &str,
11922 relative_utf16_range: Option<Range<isize>>,
11923 cx: &mut ViewContext<Self>,
11924 ) {
11925 if !self.input_enabled {
11926 cx.emit(EditorEvent::InputIgnored { text: text.into() });
11927 return;
11928 }
11929 if let Some(relative_utf16_range) = relative_utf16_range {
11930 let selections = self.selections.all::<OffsetUtf16>(cx);
11931 self.change_selections(None, cx, |s| {
11932 let new_ranges = selections.into_iter().map(|range| {
11933 let start = OffsetUtf16(
11934 range
11935 .head()
11936 .0
11937 .saturating_add_signed(relative_utf16_range.start),
11938 );
11939 let end = OffsetUtf16(
11940 range
11941 .head()
11942 .0
11943 .saturating_add_signed(relative_utf16_range.end),
11944 );
11945 start..end
11946 });
11947 s.select_ranges(new_ranges);
11948 });
11949 }
11950
11951 self.handle_input(text, cx);
11952 }
11953
11954 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
11955 let Some(project) = self.project.as_ref() else {
11956 return false;
11957 };
11958 let project = project.read(cx);
11959
11960 let mut supports = false;
11961 self.buffer().read(cx).for_each_buffer(|buffer| {
11962 if !supports {
11963 supports = project
11964 .language_servers_for_buffer(buffer.read(cx), cx)
11965 .any(
11966 |(_, server)| match server.capabilities().inlay_hint_provider {
11967 Some(lsp::OneOf::Left(enabled)) => enabled,
11968 Some(lsp::OneOf::Right(_)) => true,
11969 None => false,
11970 },
11971 )
11972 }
11973 });
11974 supports
11975 }
11976
11977 pub fn focus(&self, cx: &mut WindowContext) {
11978 cx.focus(&self.focus_handle)
11979 }
11980
11981 pub fn is_focused(&self, cx: &WindowContext) -> bool {
11982 self.focus_handle.is_focused(cx)
11983 }
11984
11985 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
11986 cx.emit(EditorEvent::Focused);
11987
11988 if let Some(descendant) = self
11989 .last_focused_descendant
11990 .take()
11991 .and_then(|descendant| descendant.upgrade())
11992 {
11993 cx.focus(&descendant);
11994 } else {
11995 if let Some(blame) = self.blame.as_ref() {
11996 blame.update(cx, GitBlame::focus)
11997 }
11998
11999 self.blink_manager.update(cx, BlinkManager::enable);
12000 self.show_cursor_names(cx);
12001 self.buffer.update(cx, |buffer, cx| {
12002 buffer.finalize_last_transaction(cx);
12003 if self.leader_peer_id.is_none() {
12004 buffer.set_active_selections(
12005 &self.selections.disjoint_anchors(),
12006 self.selections.line_mode,
12007 self.cursor_shape,
12008 cx,
12009 );
12010 }
12011 });
12012 }
12013 }
12014
12015 fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
12016 cx.emit(EditorEvent::FocusedIn)
12017 }
12018
12019 fn handle_focus_out(&mut self, event: FocusOutEvent, _cx: &mut ViewContext<Self>) {
12020 if event.blurred != self.focus_handle {
12021 self.last_focused_descendant = Some(event.blurred);
12022 }
12023 }
12024
12025 pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
12026 self.blink_manager.update(cx, BlinkManager::disable);
12027 self.buffer
12028 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
12029
12030 if let Some(blame) = self.blame.as_ref() {
12031 blame.update(cx, GitBlame::blur)
12032 }
12033 if !self.hover_state.focused(cx) {
12034 hide_hover(self, cx);
12035 }
12036
12037 self.hide_context_menu(cx);
12038 cx.emit(EditorEvent::Blurred);
12039 cx.notify();
12040 }
12041
12042 pub fn register_action<A: Action>(
12043 &mut self,
12044 listener: impl Fn(&A, &mut WindowContext) + 'static,
12045 ) -> Subscription {
12046 let id = self.next_editor_action_id.post_inc();
12047 let listener = Arc::new(listener);
12048 self.editor_actions.borrow_mut().insert(
12049 id,
12050 Box::new(move |cx| {
12051 let cx = cx.window_context();
12052 let listener = listener.clone();
12053 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
12054 let action = action.downcast_ref().unwrap();
12055 if phase == DispatchPhase::Bubble {
12056 listener(action, cx)
12057 }
12058 })
12059 }),
12060 );
12061
12062 let editor_actions = self.editor_actions.clone();
12063 Subscription::new(move || {
12064 editor_actions.borrow_mut().remove(&id);
12065 })
12066 }
12067
12068 pub fn file_header_size(&self) -> u32 {
12069 self.file_header_size
12070 }
12071
12072 pub fn revert(
12073 &mut self,
12074 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
12075 cx: &mut ViewContext<Self>,
12076 ) {
12077 self.buffer().update(cx, |multi_buffer, cx| {
12078 for (buffer_id, changes) in revert_changes {
12079 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
12080 buffer.update(cx, |buffer, cx| {
12081 buffer.edit(
12082 changes.into_iter().map(|(range, text)| {
12083 (range, text.to_string().map(Arc::<str>::from))
12084 }),
12085 None,
12086 cx,
12087 );
12088 });
12089 }
12090 }
12091 });
12092 self.change_selections(None, cx, |selections| selections.refresh());
12093 }
12094
12095 pub fn to_pixel_point(
12096 &mut self,
12097 source: multi_buffer::Anchor,
12098 editor_snapshot: &EditorSnapshot,
12099 cx: &mut ViewContext<Self>,
12100 ) -> Option<gpui::Point<Pixels>> {
12101 let source_point = source.to_display_point(editor_snapshot);
12102 self.display_to_pixel_point(source_point, editor_snapshot, cx)
12103 }
12104
12105 pub fn display_to_pixel_point(
12106 &mut self,
12107 source: DisplayPoint,
12108 editor_snapshot: &EditorSnapshot,
12109 cx: &mut ViewContext<Self>,
12110 ) -> Option<gpui::Point<Pixels>> {
12111 let line_height = self.style()?.text.line_height_in_pixels(cx.rem_size());
12112 let text_layout_details = self.text_layout_details(cx);
12113 let scroll_top = text_layout_details
12114 .scroll_anchor
12115 .scroll_position(editor_snapshot)
12116 .y;
12117
12118 if source.row().as_f32() < scroll_top.floor() {
12119 return None;
12120 }
12121 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
12122 let source_y = line_height * (source.row().as_f32() - scroll_top);
12123 Some(gpui::Point::new(source_x, source_y))
12124 }
12125
12126 fn gutter_bounds(&self) -> Option<Bounds<Pixels>> {
12127 let bounds = self.last_bounds?;
12128 Some(element::gutter_bounds(bounds, self.gutter_dimensions))
12129 }
12130
12131 pub fn has_active_completions_menu(&self) -> bool {
12132 self.context_menu.read().as_ref().map_or(false, |menu| {
12133 menu.visible() && matches!(menu, ContextMenu::Completions(_))
12134 })
12135 }
12136
12137 pub fn register_addon<T: Addon>(&mut self, instance: T) {
12138 self.addons
12139 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
12140 }
12141
12142 pub fn unregister_addon<T: Addon>(&mut self) {
12143 self.addons.remove(&std::any::TypeId::of::<T>());
12144 }
12145
12146 pub fn addon<T: Addon>(&self) -> Option<&T> {
12147 let type_id = std::any::TypeId::of::<T>();
12148 self.addons
12149 .get(&type_id)
12150 .and_then(|item| item.to_any().downcast_ref::<T>())
12151 }
12152}
12153
12154fn hunks_for_selections(
12155 multi_buffer_snapshot: &MultiBufferSnapshot,
12156 selections: &[Selection<Anchor>],
12157) -> Vec<DiffHunk<MultiBufferRow>> {
12158 let buffer_rows_for_selections = selections.iter().map(|selection| {
12159 let head = selection.head();
12160 let tail = selection.tail();
12161 let start = MultiBufferRow(tail.to_point(&multi_buffer_snapshot).row);
12162 let end = MultiBufferRow(head.to_point(&multi_buffer_snapshot).row);
12163 if start > end {
12164 end..start
12165 } else {
12166 start..end
12167 }
12168 });
12169
12170 hunks_for_rows(buffer_rows_for_selections, multi_buffer_snapshot)
12171}
12172
12173pub fn hunks_for_rows(
12174 rows: impl Iterator<Item = Range<MultiBufferRow>>,
12175 multi_buffer_snapshot: &MultiBufferSnapshot,
12176) -> Vec<DiffHunk<MultiBufferRow>> {
12177 let mut hunks = Vec::new();
12178 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
12179 HashMap::default();
12180 for selected_multi_buffer_rows in rows {
12181 let query_rows =
12182 selected_multi_buffer_rows.start..selected_multi_buffer_rows.end.next_row();
12183 for hunk in multi_buffer_snapshot.git_diff_hunks_in_range(query_rows.clone()) {
12184 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
12185 // when the caret is just above or just below the deleted hunk.
12186 let allow_adjacent = hunk_status(&hunk) == DiffHunkStatus::Removed;
12187 let related_to_selection = if allow_adjacent {
12188 hunk.associated_range.overlaps(&query_rows)
12189 || hunk.associated_range.start == query_rows.end
12190 || hunk.associated_range.end == query_rows.start
12191 } else {
12192 // `selected_multi_buffer_rows` are inclusive (e.g. [2..2] means 2nd row is selected)
12193 // `hunk.associated_range` is exclusive (e.g. [2..3] means 2nd row is selected)
12194 hunk.associated_range.overlaps(&selected_multi_buffer_rows)
12195 || selected_multi_buffer_rows.end == hunk.associated_range.start
12196 };
12197 if related_to_selection {
12198 if !processed_buffer_rows
12199 .entry(hunk.buffer_id)
12200 .or_default()
12201 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
12202 {
12203 continue;
12204 }
12205 hunks.push(hunk);
12206 }
12207 }
12208 }
12209
12210 hunks
12211}
12212
12213pub trait CollaborationHub {
12214 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
12215 fn user_participant_indices<'a>(
12216 &self,
12217 cx: &'a AppContext,
12218 ) -> &'a HashMap<u64, ParticipantIndex>;
12219 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
12220}
12221
12222impl CollaborationHub for Model<Project> {
12223 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
12224 self.read(cx).collaborators()
12225 }
12226
12227 fn user_participant_indices<'a>(
12228 &self,
12229 cx: &'a AppContext,
12230 ) -> &'a HashMap<u64, ParticipantIndex> {
12231 self.read(cx).user_store().read(cx).participant_indices()
12232 }
12233
12234 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
12235 let this = self.read(cx);
12236 let user_ids = this.collaborators().values().map(|c| c.user_id);
12237 this.user_store().read_with(cx, |user_store, cx| {
12238 user_store.participant_names(user_ids, cx)
12239 })
12240 }
12241}
12242
12243pub trait CompletionProvider {
12244 fn completions(
12245 &self,
12246 buffer: &Model<Buffer>,
12247 buffer_position: text::Anchor,
12248 trigger: CompletionContext,
12249 cx: &mut ViewContext<Editor>,
12250 ) -> Task<Result<Vec<Completion>>>;
12251
12252 fn resolve_completions(
12253 &self,
12254 buffer: Model<Buffer>,
12255 completion_indices: Vec<usize>,
12256 completions: Arc<RwLock<Box<[Completion]>>>,
12257 cx: &mut ViewContext<Editor>,
12258 ) -> Task<Result<bool>>;
12259
12260 fn apply_additional_edits_for_completion(
12261 &self,
12262 buffer: Model<Buffer>,
12263 completion: Completion,
12264 push_to_history: bool,
12265 cx: &mut ViewContext<Editor>,
12266 ) -> Task<Result<Option<language::Transaction>>>;
12267
12268 fn is_completion_trigger(
12269 &self,
12270 buffer: &Model<Buffer>,
12271 position: language::Anchor,
12272 text: &str,
12273 trigger_in_words: bool,
12274 cx: &mut ViewContext<Editor>,
12275 ) -> bool;
12276
12277 fn sort_completions(&self) -> bool {
12278 true
12279 }
12280}
12281
12282fn snippet_completions(
12283 project: &Project,
12284 buffer: &Model<Buffer>,
12285 buffer_position: text::Anchor,
12286 cx: &mut AppContext,
12287) -> Vec<Completion> {
12288 let language = buffer.read(cx).language_at(buffer_position);
12289 let language_name = language.as_ref().map(|language| language.lsp_id());
12290 let snippet_store = project.snippets().read(cx);
12291 let snippets = snippet_store.snippets_for(language_name, cx);
12292
12293 if snippets.is_empty() {
12294 return vec![];
12295 }
12296 let snapshot = buffer.read(cx).text_snapshot();
12297 let chunks = snapshot.reversed_chunks_in_range(text::Anchor::MIN..buffer_position);
12298
12299 let mut lines = chunks.lines();
12300 let Some(line_at) = lines.next().filter(|line| !line.is_empty()) else {
12301 return vec![];
12302 };
12303
12304 let scope = language.map(|language| language.default_scope());
12305 let mut last_word = line_at
12306 .chars()
12307 .rev()
12308 .take_while(|c| char_kind(&scope, *c) == CharKind::Word)
12309 .collect::<String>();
12310 last_word = last_word.chars().rev().collect();
12311 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
12312 let to_lsp = |point: &text::Anchor| {
12313 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
12314 point_to_lsp(end)
12315 };
12316 let lsp_end = to_lsp(&buffer_position);
12317 snippets
12318 .into_iter()
12319 .filter_map(|snippet| {
12320 let matching_prefix = snippet
12321 .prefix
12322 .iter()
12323 .find(|prefix| prefix.starts_with(&last_word))?;
12324 let start = as_offset - last_word.len();
12325 let start = snapshot.anchor_before(start);
12326 let range = start..buffer_position;
12327 let lsp_start = to_lsp(&start);
12328 let lsp_range = lsp::Range {
12329 start: lsp_start,
12330 end: lsp_end,
12331 };
12332 Some(Completion {
12333 old_range: range,
12334 new_text: snippet.body.clone(),
12335 label: CodeLabel {
12336 text: matching_prefix.clone(),
12337 runs: vec![],
12338 filter_range: 0..matching_prefix.len(),
12339 },
12340 server_id: LanguageServerId(usize::MAX),
12341 documentation: snippet
12342 .description
12343 .clone()
12344 .map(|description| Documentation::SingleLine(description)),
12345 lsp_completion: lsp::CompletionItem {
12346 label: snippet.prefix.first().unwrap().clone(),
12347 kind: Some(CompletionItemKind::SNIPPET),
12348 label_details: snippet.description.as_ref().map(|description| {
12349 lsp::CompletionItemLabelDetails {
12350 detail: Some(description.clone()),
12351 description: None,
12352 }
12353 }),
12354 insert_text_format: Some(InsertTextFormat::SNIPPET),
12355 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
12356 lsp::InsertReplaceEdit {
12357 new_text: snippet.body.clone(),
12358 insert: lsp_range,
12359 replace: lsp_range,
12360 },
12361 )),
12362 filter_text: Some(snippet.body.clone()),
12363 sort_text: Some(char::MAX.to_string()),
12364 ..Default::default()
12365 },
12366 confirm: None,
12367 })
12368 })
12369 .collect()
12370}
12371
12372impl CompletionProvider for Model<Project> {
12373 fn completions(
12374 &self,
12375 buffer: &Model<Buffer>,
12376 buffer_position: text::Anchor,
12377 options: CompletionContext,
12378 cx: &mut ViewContext<Editor>,
12379 ) -> Task<Result<Vec<Completion>>> {
12380 self.update(cx, |project, cx| {
12381 let snippets = snippet_completions(project, buffer, buffer_position, cx);
12382 let project_completions = project.completions(&buffer, buffer_position, options, cx);
12383 cx.background_executor().spawn(async move {
12384 let mut completions = project_completions.await?;
12385 //let snippets = snippets.into_iter().;
12386 completions.extend(snippets);
12387 Ok(completions)
12388 })
12389 })
12390 }
12391
12392 fn resolve_completions(
12393 &self,
12394 buffer: Model<Buffer>,
12395 completion_indices: Vec<usize>,
12396 completions: Arc<RwLock<Box<[Completion]>>>,
12397 cx: &mut ViewContext<Editor>,
12398 ) -> Task<Result<bool>> {
12399 self.update(cx, |project, cx| {
12400 project.resolve_completions(buffer, completion_indices, completions, cx)
12401 })
12402 }
12403
12404 fn apply_additional_edits_for_completion(
12405 &self,
12406 buffer: Model<Buffer>,
12407 completion: Completion,
12408 push_to_history: bool,
12409 cx: &mut ViewContext<Editor>,
12410 ) -> Task<Result<Option<language::Transaction>>> {
12411 self.update(cx, |project, cx| {
12412 project.apply_additional_edits_for_completion(buffer, completion, push_to_history, cx)
12413 })
12414 }
12415
12416 fn is_completion_trigger(
12417 &self,
12418 buffer: &Model<Buffer>,
12419 position: language::Anchor,
12420 text: &str,
12421 trigger_in_words: bool,
12422 cx: &mut ViewContext<Editor>,
12423 ) -> bool {
12424 if !EditorSettings::get_global(cx).show_completions_on_input {
12425 return false;
12426 }
12427
12428 let mut chars = text.chars();
12429 let char = if let Some(char) = chars.next() {
12430 char
12431 } else {
12432 return false;
12433 };
12434 if chars.next().is_some() {
12435 return false;
12436 }
12437
12438 let buffer = buffer.read(cx);
12439 let scope = buffer.snapshot().language_scope_at(position);
12440 if trigger_in_words && char_kind(&scope, char) == CharKind::Word {
12441 return true;
12442 }
12443
12444 buffer
12445 .completion_triggers()
12446 .iter()
12447 .any(|string| string == text)
12448 }
12449}
12450
12451fn inlay_hint_settings(
12452 location: Anchor,
12453 snapshot: &MultiBufferSnapshot,
12454 cx: &mut ViewContext<'_, Editor>,
12455) -> InlayHintSettings {
12456 let file = snapshot.file_at(location);
12457 let language = snapshot.language_at(location);
12458 let settings = all_language_settings(file, cx);
12459 settings
12460 .language(language.map(|l| l.name()).as_deref())
12461 .inlay_hints
12462}
12463
12464fn consume_contiguous_rows(
12465 contiguous_row_selections: &mut Vec<Selection<Point>>,
12466 selection: &Selection<Point>,
12467 display_map: &DisplaySnapshot,
12468 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
12469) -> (MultiBufferRow, MultiBufferRow) {
12470 contiguous_row_selections.push(selection.clone());
12471 let start_row = MultiBufferRow(selection.start.row);
12472 let mut end_row = ending_row(selection, display_map);
12473
12474 while let Some(next_selection) = selections.peek() {
12475 if next_selection.start.row <= end_row.0 {
12476 end_row = ending_row(next_selection, display_map);
12477 contiguous_row_selections.push(selections.next().unwrap().clone());
12478 } else {
12479 break;
12480 }
12481 }
12482 (start_row, end_row)
12483}
12484
12485fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
12486 if next_selection.end.column > 0 || next_selection.is_empty() {
12487 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
12488 } else {
12489 MultiBufferRow(next_selection.end.row)
12490 }
12491}
12492
12493impl EditorSnapshot {
12494 pub fn remote_selections_in_range<'a>(
12495 &'a self,
12496 range: &'a Range<Anchor>,
12497 collaboration_hub: &dyn CollaborationHub,
12498 cx: &'a AppContext,
12499 ) -> impl 'a + Iterator<Item = RemoteSelection> {
12500 let participant_names = collaboration_hub.user_names(cx);
12501 let participant_indices = collaboration_hub.user_participant_indices(cx);
12502 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
12503 let collaborators_by_replica_id = collaborators_by_peer_id
12504 .iter()
12505 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
12506 .collect::<HashMap<_, _>>();
12507 self.buffer_snapshot
12508 .selections_in_range(range, false)
12509 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
12510 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
12511 let participant_index = participant_indices.get(&collaborator.user_id).copied();
12512 let user_name = participant_names.get(&collaborator.user_id).cloned();
12513 Some(RemoteSelection {
12514 replica_id,
12515 selection,
12516 cursor_shape,
12517 line_mode,
12518 participant_index,
12519 peer_id: collaborator.peer_id,
12520 user_name,
12521 })
12522 })
12523 }
12524
12525 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
12526 self.display_snapshot.buffer_snapshot.language_at(position)
12527 }
12528
12529 pub fn is_focused(&self) -> bool {
12530 self.is_focused
12531 }
12532
12533 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
12534 self.placeholder_text.as_ref()
12535 }
12536
12537 pub fn scroll_position(&self) -> gpui::Point<f32> {
12538 self.scroll_anchor.scroll_position(&self.display_snapshot)
12539 }
12540
12541 fn gutter_dimensions(
12542 &self,
12543 font_id: FontId,
12544 font_size: Pixels,
12545 em_width: Pixels,
12546 max_line_number_width: Pixels,
12547 cx: &AppContext,
12548 ) -> GutterDimensions {
12549 if !self.show_gutter {
12550 return GutterDimensions::default();
12551 }
12552 let descent = cx.text_system().descent(font_id, font_size);
12553
12554 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
12555 matches!(
12556 ProjectSettings::get_global(cx).git.git_gutter,
12557 Some(GitGutterSetting::TrackedFiles)
12558 )
12559 });
12560 let gutter_settings = EditorSettings::get_global(cx).gutter;
12561 let show_line_numbers = self
12562 .show_line_numbers
12563 .unwrap_or(gutter_settings.line_numbers);
12564 let line_gutter_width = if show_line_numbers {
12565 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
12566 let min_width_for_number_on_gutter = em_width * 4.0;
12567 max_line_number_width.max(min_width_for_number_on_gutter)
12568 } else {
12569 0.0.into()
12570 };
12571
12572 let show_code_actions = self
12573 .show_code_actions
12574 .unwrap_or(gutter_settings.code_actions);
12575
12576 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
12577
12578 let git_blame_entries_width = self
12579 .render_git_blame_gutter
12580 .then_some(em_width * GIT_BLAME_GUTTER_WIDTH_CHARS);
12581
12582 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
12583 left_padding += if show_code_actions || show_runnables {
12584 em_width * 3.0
12585 } else if show_git_gutter && show_line_numbers {
12586 em_width * 2.0
12587 } else if show_git_gutter || show_line_numbers {
12588 em_width
12589 } else {
12590 px(0.)
12591 };
12592
12593 let right_padding = if gutter_settings.folds && show_line_numbers {
12594 em_width * 4.0
12595 } else if gutter_settings.folds {
12596 em_width * 3.0
12597 } else if show_line_numbers {
12598 em_width
12599 } else {
12600 px(0.)
12601 };
12602
12603 GutterDimensions {
12604 left_padding,
12605 right_padding,
12606 width: line_gutter_width + left_padding + right_padding,
12607 margin: -descent,
12608 git_blame_entries_width,
12609 }
12610 }
12611
12612 pub fn render_fold_toggle(
12613 &self,
12614 buffer_row: MultiBufferRow,
12615 row_contains_cursor: bool,
12616 editor: View<Editor>,
12617 cx: &mut WindowContext,
12618 ) -> Option<AnyElement> {
12619 let folded = self.is_line_folded(buffer_row);
12620
12621 if let Some(crease) = self
12622 .crease_snapshot
12623 .query_row(buffer_row, &self.buffer_snapshot)
12624 {
12625 let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
12626 if folded {
12627 editor.update(cx, |editor, cx| {
12628 editor.fold_at(&crate::FoldAt { buffer_row }, cx)
12629 });
12630 } else {
12631 editor.update(cx, |editor, cx| {
12632 editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
12633 });
12634 }
12635 });
12636
12637 Some((crease.render_toggle)(
12638 buffer_row,
12639 folded,
12640 toggle_callback,
12641 cx,
12642 ))
12643 } else if folded
12644 || (self.starts_indent(buffer_row) && (row_contains_cursor || self.gutter_hovered))
12645 {
12646 Some(
12647 Disclosure::new(("indent-fold-indicator", buffer_row.0), !folded)
12648 .selected(folded)
12649 .on_click(cx.listener_for(&editor, move |this, _e, cx| {
12650 if folded {
12651 this.unfold_at(&UnfoldAt { buffer_row }, cx);
12652 } else {
12653 this.fold_at(&FoldAt { buffer_row }, cx);
12654 }
12655 }))
12656 .into_any_element(),
12657 )
12658 } else {
12659 None
12660 }
12661 }
12662
12663 pub fn render_crease_trailer(
12664 &self,
12665 buffer_row: MultiBufferRow,
12666 cx: &mut WindowContext,
12667 ) -> Option<AnyElement> {
12668 let folded = self.is_line_folded(buffer_row);
12669 let crease = self
12670 .crease_snapshot
12671 .query_row(buffer_row, &self.buffer_snapshot)?;
12672 Some((crease.render_trailer)(buffer_row, folded, cx))
12673 }
12674}
12675
12676impl Deref for EditorSnapshot {
12677 type Target = DisplaySnapshot;
12678
12679 fn deref(&self) -> &Self::Target {
12680 &self.display_snapshot
12681 }
12682}
12683
12684#[derive(Clone, Debug, PartialEq, Eq)]
12685pub enum EditorEvent {
12686 InputIgnored {
12687 text: Arc<str>,
12688 },
12689 InputHandled {
12690 utf16_range_to_replace: Option<Range<isize>>,
12691 text: Arc<str>,
12692 },
12693 ExcerptsAdded {
12694 buffer: Model<Buffer>,
12695 predecessor: ExcerptId,
12696 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
12697 },
12698 ExcerptsRemoved {
12699 ids: Vec<ExcerptId>,
12700 },
12701 ExcerptsEdited {
12702 ids: Vec<ExcerptId>,
12703 },
12704 ExcerptsExpanded {
12705 ids: Vec<ExcerptId>,
12706 },
12707 BufferEdited,
12708 Edited {
12709 transaction_id: clock::Lamport,
12710 },
12711 Reparsed(BufferId),
12712 Focused,
12713 FocusedIn,
12714 Blurred,
12715 DirtyChanged,
12716 Saved,
12717 TitleChanged,
12718 DiffBaseChanged,
12719 SelectionsChanged {
12720 local: bool,
12721 },
12722 ScrollPositionChanged {
12723 local: bool,
12724 autoscroll: bool,
12725 },
12726 Closed,
12727 TransactionUndone {
12728 transaction_id: clock::Lamport,
12729 },
12730 TransactionBegun {
12731 transaction_id: clock::Lamport,
12732 },
12733}
12734
12735impl EventEmitter<EditorEvent> for Editor {}
12736
12737impl FocusableView for Editor {
12738 fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
12739 self.focus_handle.clone()
12740 }
12741}
12742
12743impl Render for Editor {
12744 fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
12745 let settings = ThemeSettings::get_global(cx);
12746
12747 let text_style = match self.mode {
12748 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
12749 color: cx.theme().colors().editor_foreground,
12750 font_family: settings.ui_font.family.clone(),
12751 font_features: settings.ui_font.features.clone(),
12752 font_fallbacks: settings.ui_font.fallbacks.clone(),
12753 font_size: rems(0.875).into(),
12754 font_weight: settings.ui_font.weight,
12755 line_height: relative(settings.buffer_line_height.value()),
12756 ..Default::default()
12757 },
12758 EditorMode::Full => TextStyle {
12759 color: cx.theme().colors().editor_foreground,
12760 font_family: settings.buffer_font.family.clone(),
12761 font_features: settings.buffer_font.features.clone(),
12762 font_fallbacks: settings.buffer_font.fallbacks.clone(),
12763 font_size: settings.buffer_font_size(cx).into(),
12764 font_weight: settings.buffer_font.weight,
12765 line_height: relative(settings.buffer_line_height.value()),
12766 ..Default::default()
12767 },
12768 };
12769
12770 let background = match self.mode {
12771 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
12772 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
12773 EditorMode::Full => cx.theme().colors().editor_background,
12774 };
12775
12776 EditorElement::new(
12777 cx.view(),
12778 EditorStyle {
12779 background,
12780 local_player: cx.theme().players().local(),
12781 text: text_style,
12782 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
12783 syntax: cx.theme().syntax().clone(),
12784 status: cx.theme().status().clone(),
12785 inlay_hints_style: HighlightStyle {
12786 color: Some(cx.theme().status().hint),
12787 ..HighlightStyle::default()
12788 },
12789 suggestions_style: HighlightStyle {
12790 color: Some(cx.theme().status().predictive),
12791 ..HighlightStyle::default()
12792 },
12793 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
12794 },
12795 )
12796 }
12797}
12798
12799impl ViewInputHandler for Editor {
12800 fn text_for_range(
12801 &mut self,
12802 range_utf16: Range<usize>,
12803 cx: &mut ViewContext<Self>,
12804 ) -> Option<String> {
12805 Some(
12806 self.buffer
12807 .read(cx)
12808 .read(cx)
12809 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
12810 .collect(),
12811 )
12812 }
12813
12814 fn selected_text_range(
12815 &mut self,
12816 ignore_disabled_input: bool,
12817 cx: &mut ViewContext<Self>,
12818 ) -> Option<UTF16Selection> {
12819 // Prevent the IME menu from appearing when holding down an alphabetic key
12820 // while input is disabled.
12821 if !ignore_disabled_input && !self.input_enabled {
12822 return None;
12823 }
12824
12825 let selection = self.selections.newest::<OffsetUtf16>(cx);
12826 let range = selection.range();
12827
12828 Some(UTF16Selection {
12829 range: range.start.0..range.end.0,
12830 reversed: selection.reversed,
12831 })
12832 }
12833
12834 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
12835 let snapshot = self.buffer.read(cx).read(cx);
12836 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
12837 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
12838 }
12839
12840 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
12841 self.clear_highlights::<InputComposition>(cx);
12842 self.ime_transaction.take();
12843 }
12844
12845 fn replace_text_in_range(
12846 &mut self,
12847 range_utf16: Option<Range<usize>>,
12848 text: &str,
12849 cx: &mut ViewContext<Self>,
12850 ) {
12851 if !self.input_enabled {
12852 cx.emit(EditorEvent::InputIgnored { text: text.into() });
12853 return;
12854 }
12855
12856 self.transact(cx, |this, cx| {
12857 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
12858 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
12859 Some(this.selection_replacement_ranges(range_utf16, cx))
12860 } else {
12861 this.marked_text_ranges(cx)
12862 };
12863
12864 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
12865 let newest_selection_id = this.selections.newest_anchor().id;
12866 this.selections
12867 .all::<OffsetUtf16>(cx)
12868 .iter()
12869 .zip(ranges_to_replace.iter())
12870 .find_map(|(selection, range)| {
12871 if selection.id == newest_selection_id {
12872 Some(
12873 (range.start.0 as isize - selection.head().0 as isize)
12874 ..(range.end.0 as isize - selection.head().0 as isize),
12875 )
12876 } else {
12877 None
12878 }
12879 })
12880 });
12881
12882 cx.emit(EditorEvent::InputHandled {
12883 utf16_range_to_replace: range_to_replace,
12884 text: text.into(),
12885 });
12886
12887 if let Some(new_selected_ranges) = new_selected_ranges {
12888 this.change_selections(None, cx, |selections| {
12889 selections.select_ranges(new_selected_ranges)
12890 });
12891 this.backspace(&Default::default(), cx);
12892 }
12893
12894 this.handle_input(text, cx);
12895 });
12896
12897 if let Some(transaction) = self.ime_transaction {
12898 self.buffer.update(cx, |buffer, cx| {
12899 buffer.group_until_transaction(transaction, cx);
12900 });
12901 }
12902
12903 self.unmark_text(cx);
12904 }
12905
12906 fn replace_and_mark_text_in_range(
12907 &mut self,
12908 range_utf16: Option<Range<usize>>,
12909 text: &str,
12910 new_selected_range_utf16: Option<Range<usize>>,
12911 cx: &mut ViewContext<Self>,
12912 ) {
12913 if !self.input_enabled {
12914 return;
12915 }
12916
12917 let transaction = self.transact(cx, |this, cx| {
12918 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
12919 let snapshot = this.buffer.read(cx).read(cx);
12920 if let Some(relative_range_utf16) = range_utf16.as_ref() {
12921 for marked_range in &mut marked_ranges {
12922 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
12923 marked_range.start.0 += relative_range_utf16.start;
12924 marked_range.start =
12925 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
12926 marked_range.end =
12927 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
12928 }
12929 }
12930 Some(marked_ranges)
12931 } else if let Some(range_utf16) = range_utf16 {
12932 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
12933 Some(this.selection_replacement_ranges(range_utf16, cx))
12934 } else {
12935 None
12936 };
12937
12938 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
12939 let newest_selection_id = this.selections.newest_anchor().id;
12940 this.selections
12941 .all::<OffsetUtf16>(cx)
12942 .iter()
12943 .zip(ranges_to_replace.iter())
12944 .find_map(|(selection, range)| {
12945 if selection.id == newest_selection_id {
12946 Some(
12947 (range.start.0 as isize - selection.head().0 as isize)
12948 ..(range.end.0 as isize - selection.head().0 as isize),
12949 )
12950 } else {
12951 None
12952 }
12953 })
12954 });
12955
12956 cx.emit(EditorEvent::InputHandled {
12957 utf16_range_to_replace: range_to_replace,
12958 text: text.into(),
12959 });
12960
12961 if let Some(ranges) = ranges_to_replace {
12962 this.change_selections(None, cx, |s| s.select_ranges(ranges));
12963 }
12964
12965 let marked_ranges = {
12966 let snapshot = this.buffer.read(cx).read(cx);
12967 this.selections
12968 .disjoint_anchors()
12969 .iter()
12970 .map(|selection| {
12971 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
12972 })
12973 .collect::<Vec<_>>()
12974 };
12975
12976 if text.is_empty() {
12977 this.unmark_text(cx);
12978 } else {
12979 this.highlight_text::<InputComposition>(
12980 marked_ranges.clone(),
12981 HighlightStyle {
12982 underline: Some(UnderlineStyle {
12983 thickness: px(1.),
12984 color: None,
12985 wavy: false,
12986 }),
12987 ..Default::default()
12988 },
12989 cx,
12990 );
12991 }
12992
12993 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
12994 let use_autoclose = this.use_autoclose;
12995 let use_auto_surround = this.use_auto_surround;
12996 this.set_use_autoclose(false);
12997 this.set_use_auto_surround(false);
12998 this.handle_input(text, cx);
12999 this.set_use_autoclose(use_autoclose);
13000 this.set_use_auto_surround(use_auto_surround);
13001
13002 if let Some(new_selected_range) = new_selected_range_utf16 {
13003 let snapshot = this.buffer.read(cx).read(cx);
13004 let new_selected_ranges = marked_ranges
13005 .into_iter()
13006 .map(|marked_range| {
13007 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
13008 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
13009 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
13010 snapshot.clip_offset_utf16(new_start, Bias::Left)
13011 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
13012 })
13013 .collect::<Vec<_>>();
13014
13015 drop(snapshot);
13016 this.change_selections(None, cx, |selections| {
13017 selections.select_ranges(new_selected_ranges)
13018 });
13019 }
13020 });
13021
13022 self.ime_transaction = self.ime_transaction.or(transaction);
13023 if let Some(transaction) = self.ime_transaction {
13024 self.buffer.update(cx, |buffer, cx| {
13025 buffer.group_until_transaction(transaction, cx);
13026 });
13027 }
13028
13029 if self.text_highlights::<InputComposition>(cx).is_none() {
13030 self.ime_transaction.take();
13031 }
13032 }
13033
13034 fn bounds_for_range(
13035 &mut self,
13036 range_utf16: Range<usize>,
13037 element_bounds: gpui::Bounds<Pixels>,
13038 cx: &mut ViewContext<Self>,
13039 ) -> Option<gpui::Bounds<Pixels>> {
13040 let text_layout_details = self.text_layout_details(cx);
13041 let style = &text_layout_details.editor_style;
13042 let font_id = cx.text_system().resolve_font(&style.text.font());
13043 let font_size = style.text.font_size.to_pixels(cx.rem_size());
13044 let line_height = style.text.line_height_in_pixels(cx.rem_size());
13045
13046 let em_width = cx
13047 .text_system()
13048 .typographic_bounds(font_id, font_size, 'm')
13049 .unwrap()
13050 .size
13051 .width;
13052
13053 let snapshot = self.snapshot(cx);
13054 let scroll_position = snapshot.scroll_position();
13055 let scroll_left = scroll_position.x * em_width;
13056
13057 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
13058 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
13059 + self.gutter_dimensions.width;
13060 let y = line_height * (start.row().as_f32() - scroll_position.y);
13061
13062 Some(Bounds {
13063 origin: element_bounds.origin + point(x, y),
13064 size: size(em_width, line_height),
13065 })
13066 }
13067}
13068
13069trait SelectionExt {
13070 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
13071 fn spanned_rows(
13072 &self,
13073 include_end_if_at_line_start: bool,
13074 map: &DisplaySnapshot,
13075 ) -> Range<MultiBufferRow>;
13076}
13077
13078impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
13079 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
13080 let start = self
13081 .start
13082 .to_point(&map.buffer_snapshot)
13083 .to_display_point(map);
13084 let end = self
13085 .end
13086 .to_point(&map.buffer_snapshot)
13087 .to_display_point(map);
13088 if self.reversed {
13089 end..start
13090 } else {
13091 start..end
13092 }
13093 }
13094
13095 fn spanned_rows(
13096 &self,
13097 include_end_if_at_line_start: bool,
13098 map: &DisplaySnapshot,
13099 ) -> Range<MultiBufferRow> {
13100 let start = self.start.to_point(&map.buffer_snapshot);
13101 let mut end = self.end.to_point(&map.buffer_snapshot);
13102 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
13103 end.row -= 1;
13104 }
13105
13106 let buffer_start = map.prev_line_boundary(start).0;
13107 let buffer_end = map.next_line_boundary(end).0;
13108 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
13109 }
13110}
13111
13112impl<T: InvalidationRegion> InvalidationStack<T> {
13113 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
13114 where
13115 S: Clone + ToOffset,
13116 {
13117 while let Some(region) = self.last() {
13118 let all_selections_inside_invalidation_ranges =
13119 if selections.len() == region.ranges().len() {
13120 selections
13121 .iter()
13122 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
13123 .all(|(selection, invalidation_range)| {
13124 let head = selection.head().to_offset(buffer);
13125 invalidation_range.start <= head && invalidation_range.end >= head
13126 })
13127 } else {
13128 false
13129 };
13130
13131 if all_selections_inside_invalidation_ranges {
13132 break;
13133 } else {
13134 self.pop();
13135 }
13136 }
13137 }
13138}
13139
13140impl<T> Default for InvalidationStack<T> {
13141 fn default() -> Self {
13142 Self(Default::default())
13143 }
13144}
13145
13146impl<T> Deref for InvalidationStack<T> {
13147 type Target = Vec<T>;
13148
13149 fn deref(&self) -> &Self::Target {
13150 &self.0
13151 }
13152}
13153
13154impl<T> DerefMut for InvalidationStack<T> {
13155 fn deref_mut(&mut self) -> &mut Self::Target {
13156 &mut self.0
13157 }
13158}
13159
13160impl InvalidationRegion for SnippetState {
13161 fn ranges(&self) -> &[Range<Anchor>] {
13162 &self.ranges[self.active_index]
13163 }
13164}
13165
13166pub fn diagnostic_block_renderer(
13167 diagnostic: Diagnostic,
13168 max_message_rows: Option<u8>,
13169 allow_closing: bool,
13170 _is_valid: bool,
13171) -> RenderBlock {
13172 let (text_without_backticks, code_ranges) =
13173 highlight_diagnostic_message(&diagnostic, max_message_rows);
13174
13175 Box::new(move |cx: &mut BlockContext| {
13176 let group_id: SharedString = cx.block_id.to_string().into();
13177
13178 let mut text_style = cx.text_style().clone();
13179 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
13180 let theme_settings = ThemeSettings::get_global(cx);
13181 text_style.font_family = theme_settings.buffer_font.family.clone();
13182 text_style.font_style = theme_settings.buffer_font.style;
13183 text_style.font_features = theme_settings.buffer_font.features.clone();
13184 text_style.font_weight = theme_settings.buffer_font.weight;
13185
13186 let multi_line_diagnostic = diagnostic.message.contains('\n');
13187
13188 let buttons = |diagnostic: &Diagnostic, block_id: BlockId| {
13189 if multi_line_diagnostic {
13190 v_flex()
13191 } else {
13192 h_flex()
13193 }
13194 .when(allow_closing, |div| {
13195 div.children(diagnostic.is_primary.then(|| {
13196 IconButton::new(("close-block", EntityId::from(block_id)), IconName::XCircle)
13197 .icon_color(Color::Muted)
13198 .size(ButtonSize::Compact)
13199 .style(ButtonStyle::Transparent)
13200 .visible_on_hover(group_id.clone())
13201 .on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
13202 .tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
13203 }))
13204 })
13205 .child(
13206 IconButton::new(("copy-block", EntityId::from(block_id)), IconName::Copy)
13207 .icon_color(Color::Muted)
13208 .size(ButtonSize::Compact)
13209 .style(ButtonStyle::Transparent)
13210 .visible_on_hover(group_id.clone())
13211 .on_click({
13212 let message = diagnostic.message.clone();
13213 move |_click, cx| {
13214 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
13215 }
13216 })
13217 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
13218 )
13219 };
13220
13221 let icon_size = buttons(&diagnostic, cx.block_id)
13222 .into_any_element()
13223 .layout_as_root(AvailableSpace::min_size(), cx);
13224
13225 h_flex()
13226 .id(cx.block_id)
13227 .group(group_id.clone())
13228 .relative()
13229 .size_full()
13230 .pl(cx.gutter_dimensions.width)
13231 .w(cx.max_width + cx.gutter_dimensions.width)
13232 .child(
13233 div()
13234 .flex()
13235 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
13236 .flex_shrink(),
13237 )
13238 .child(buttons(&diagnostic, cx.block_id))
13239 .child(div().flex().flex_shrink_0().child(
13240 StyledText::new(text_without_backticks.clone()).with_highlights(
13241 &text_style,
13242 code_ranges.iter().map(|range| {
13243 (
13244 range.clone(),
13245 HighlightStyle {
13246 font_weight: Some(FontWeight::BOLD),
13247 ..Default::default()
13248 },
13249 )
13250 }),
13251 ),
13252 ))
13253 .into_any_element()
13254 })
13255}
13256
13257pub fn highlight_diagnostic_message(
13258 diagnostic: &Diagnostic,
13259 mut max_message_rows: Option<u8>,
13260) -> (SharedString, Vec<Range<usize>>) {
13261 let mut text_without_backticks = String::new();
13262 let mut code_ranges = Vec::new();
13263
13264 if let Some(source) = &diagnostic.source {
13265 text_without_backticks.push_str(&source);
13266 code_ranges.push(0..source.len());
13267 text_without_backticks.push_str(": ");
13268 }
13269
13270 let mut prev_offset = 0;
13271 let mut in_code_block = false;
13272 let has_row_limit = max_message_rows.is_some();
13273 let mut newline_indices = diagnostic
13274 .message
13275 .match_indices('\n')
13276 .filter(|_| has_row_limit)
13277 .map(|(ix, _)| ix)
13278 .fuse()
13279 .peekable();
13280
13281 for (quote_ix, _) in diagnostic
13282 .message
13283 .match_indices('`')
13284 .chain([(diagnostic.message.len(), "")])
13285 {
13286 let mut first_newline_ix = None;
13287 let mut last_newline_ix = None;
13288 while let Some(newline_ix) = newline_indices.peek() {
13289 if *newline_ix < quote_ix {
13290 if first_newline_ix.is_none() {
13291 first_newline_ix = Some(*newline_ix);
13292 }
13293 last_newline_ix = Some(*newline_ix);
13294
13295 if let Some(rows_left) = &mut max_message_rows {
13296 if *rows_left == 0 {
13297 break;
13298 } else {
13299 *rows_left -= 1;
13300 }
13301 }
13302 let _ = newline_indices.next();
13303 } else {
13304 break;
13305 }
13306 }
13307 let prev_len = text_without_backticks.len();
13308 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
13309 text_without_backticks.push_str(new_text);
13310 if in_code_block {
13311 code_ranges.push(prev_len..text_without_backticks.len());
13312 }
13313 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
13314 in_code_block = !in_code_block;
13315 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
13316 text_without_backticks.push_str("...");
13317 break;
13318 }
13319 }
13320
13321 (text_without_backticks.into(), code_ranges)
13322}
13323
13324fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
13325 match severity {
13326 DiagnosticSeverity::ERROR => colors.error,
13327 DiagnosticSeverity::WARNING => colors.warning,
13328 DiagnosticSeverity::INFORMATION => colors.info,
13329 DiagnosticSeverity::HINT => colors.info,
13330 _ => colors.ignored,
13331 }
13332}
13333
13334pub fn styled_runs_for_code_label<'a>(
13335 label: &'a CodeLabel,
13336 syntax_theme: &'a theme::SyntaxTheme,
13337) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
13338 let fade_out = HighlightStyle {
13339 fade_out: Some(0.35),
13340 ..Default::default()
13341 };
13342
13343 let mut prev_end = label.filter_range.end;
13344 label
13345 .runs
13346 .iter()
13347 .enumerate()
13348 .flat_map(move |(ix, (range, highlight_id))| {
13349 let style = if let Some(style) = highlight_id.style(syntax_theme) {
13350 style
13351 } else {
13352 return Default::default();
13353 };
13354 let mut muted_style = style;
13355 muted_style.highlight(fade_out);
13356
13357 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
13358 if range.start >= label.filter_range.end {
13359 if range.start > prev_end {
13360 runs.push((prev_end..range.start, fade_out));
13361 }
13362 runs.push((range.clone(), muted_style));
13363 } else if range.end <= label.filter_range.end {
13364 runs.push((range.clone(), style));
13365 } else {
13366 runs.push((range.start..label.filter_range.end, style));
13367 runs.push((label.filter_range.end..range.end, muted_style));
13368 }
13369 prev_end = cmp::max(prev_end, range.end);
13370
13371 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
13372 runs.push((prev_end..label.text.len(), fade_out));
13373 }
13374
13375 runs
13376 })
13377}
13378
13379pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
13380 let mut prev_index = 0;
13381 let mut prev_codepoint: Option<char> = None;
13382 text.char_indices()
13383 .chain([(text.len(), '\0')])
13384 .filter_map(move |(index, codepoint)| {
13385 let prev_codepoint = prev_codepoint.replace(codepoint)?;
13386 let is_boundary = index == text.len()
13387 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
13388 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
13389 if is_boundary {
13390 let chunk = &text[prev_index..index];
13391 prev_index = index;
13392 Some(chunk)
13393 } else {
13394 None
13395 }
13396 })
13397}
13398
13399pub trait RangeToAnchorExt: Sized {
13400 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
13401
13402 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
13403 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
13404 anchor_range.start.to_display_point(&snapshot)..anchor_range.end.to_display_point(&snapshot)
13405 }
13406}
13407
13408impl<T: ToOffset> RangeToAnchorExt for Range<T> {
13409 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
13410 let start_offset = self.start.to_offset(snapshot);
13411 let end_offset = self.end.to_offset(snapshot);
13412 if start_offset == end_offset {
13413 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
13414 } else {
13415 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
13416 }
13417 }
13418}
13419
13420pub trait RowExt {
13421 fn as_f32(&self) -> f32;
13422
13423 fn next_row(&self) -> Self;
13424
13425 fn previous_row(&self) -> Self;
13426
13427 fn minus(&self, other: Self) -> u32;
13428}
13429
13430impl RowExt for DisplayRow {
13431 fn as_f32(&self) -> f32 {
13432 self.0 as f32
13433 }
13434
13435 fn next_row(&self) -> Self {
13436 Self(self.0 + 1)
13437 }
13438
13439 fn previous_row(&self) -> Self {
13440 Self(self.0.saturating_sub(1))
13441 }
13442
13443 fn minus(&self, other: Self) -> u32 {
13444 self.0 - other.0
13445 }
13446}
13447
13448impl RowExt for MultiBufferRow {
13449 fn as_f32(&self) -> f32 {
13450 self.0 as f32
13451 }
13452
13453 fn next_row(&self) -> Self {
13454 Self(self.0 + 1)
13455 }
13456
13457 fn previous_row(&self) -> Self {
13458 Self(self.0.saturating_sub(1))
13459 }
13460
13461 fn minus(&self, other: Self) -> u32 {
13462 self.0 - other.0
13463 }
13464}
13465
13466trait RowRangeExt {
13467 type Row;
13468
13469 fn len(&self) -> usize;
13470
13471 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
13472}
13473
13474impl RowRangeExt for Range<MultiBufferRow> {
13475 type Row = MultiBufferRow;
13476
13477 fn len(&self) -> usize {
13478 (self.end.0 - self.start.0) as usize
13479 }
13480
13481 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
13482 (self.start.0..self.end.0).map(MultiBufferRow)
13483 }
13484}
13485
13486impl RowRangeExt for Range<DisplayRow> {
13487 type Row = DisplayRow;
13488
13489 fn len(&self) -> usize {
13490 (self.end.0 - self.start.0) as usize
13491 }
13492
13493 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
13494 (self.start.0..self.end.0).map(DisplayRow)
13495 }
13496}
13497
13498fn hunk_status(hunk: &DiffHunk<MultiBufferRow>) -> DiffHunkStatus {
13499 if hunk.diff_base_byte_range.is_empty() {
13500 DiffHunkStatus::Added
13501 } else if hunk.associated_range.is_empty() {
13502 DiffHunkStatus::Removed
13503 } else {
13504 DiffHunkStatus::Modified
13505 }
13506}
13507
13508/// If select range has more than one line, we
13509/// just point the cursor to range.start.
13510fn check_multiline_range(buffer: &Buffer, range: Range<usize>) -> Range<usize> {
13511 if buffer.offset_to_point(range.start).row == buffer.offset_to_point(range.end).row {
13512 range
13513 } else {
13514 range.start..range.start
13515 }
13516}