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 UnderlineStyle, UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext,
80 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(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(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 show_inline_completions_override: Option<bool>,
560 inlay_hint_cache: InlayHintCache,
561 expanded_hunks: ExpandedHunks,
562 next_inlay_id: usize,
563 _subscriptions: Vec<Subscription>,
564 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
565 gutter_dimensions: GutterDimensions,
566 style: Option<EditorStyle>,
567 next_editor_action_id: EditorActionId,
568 editor_actions: Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut ViewContext<Self>)>>>>,
569 use_autoclose: bool,
570 use_auto_surround: bool,
571 auto_replace_emoji_shortcode: bool,
572 show_git_blame_gutter: bool,
573 show_git_blame_inline: bool,
574 show_git_blame_inline_delay_task: Option<Task<()>>,
575 git_blame_inline_enabled: bool,
576 serialize_dirty_buffers: bool,
577 show_selection_menu: Option<bool>,
578 blame: Option<Model<GitBlame>>,
579 blame_subscription: Option<Subscription>,
580 custom_context_menu: Option<
581 Box<
582 dyn 'static
583 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
584 >,
585 >,
586 last_bounds: Option<Bounds<Pixels>>,
587 expect_bounds_change: Option<Bounds<Pixels>>,
588 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
589 tasks_update_task: Option<Task<()>>,
590 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
591 file_header_size: u32,
592 breadcrumb_header: Option<String>,
593 focused_block: Option<FocusedBlock>,
594 next_scroll_position: NextScrollCursorCenterTopBottom,
595 addons: HashMap<TypeId, Box<dyn Addon>>,
596 _scroll_cursor_center_top_bottom_task: Task<()>,
597}
598
599#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
600enum NextScrollCursorCenterTopBottom {
601 #[default]
602 Center,
603 Top,
604 Bottom,
605}
606
607impl NextScrollCursorCenterTopBottom {
608 fn next(&self) -> Self {
609 match self {
610 Self::Center => Self::Top,
611 Self::Top => Self::Bottom,
612 Self::Bottom => Self::Center,
613 }
614 }
615}
616
617#[derive(Clone)]
618pub struct EditorSnapshot {
619 pub mode: EditorMode,
620 show_gutter: bool,
621 show_line_numbers: Option<bool>,
622 show_git_diff_gutter: Option<bool>,
623 show_code_actions: Option<bool>,
624 show_runnables: Option<bool>,
625 render_git_blame_gutter: bool,
626 pub display_snapshot: DisplaySnapshot,
627 pub placeholder_text: Option<Arc<str>>,
628 is_focused: bool,
629 scroll_anchor: ScrollAnchor,
630 ongoing_scroll: OngoingScroll,
631 current_line_highlight: CurrentLineHighlight,
632 gutter_hovered: bool,
633}
634
635const GIT_BLAME_GUTTER_WIDTH_CHARS: f32 = 53.;
636
637#[derive(Default, Debug, Clone, Copy)]
638pub struct GutterDimensions {
639 pub left_padding: Pixels,
640 pub right_padding: Pixels,
641 pub width: Pixels,
642 pub margin: Pixels,
643 pub git_blame_entries_width: Option<Pixels>,
644}
645
646impl GutterDimensions {
647 /// The full width of the space taken up by the gutter.
648 pub fn full_width(&self) -> Pixels {
649 self.margin + self.width
650 }
651
652 /// The width of the space reserved for the fold indicators,
653 /// use alongside 'justify_end' and `gutter_width` to
654 /// right align content with the line numbers
655 pub fn fold_area_width(&self) -> Pixels {
656 self.margin + self.right_padding
657 }
658}
659
660#[derive(Debug)]
661pub struct RemoteSelection {
662 pub replica_id: ReplicaId,
663 pub selection: Selection<Anchor>,
664 pub cursor_shape: CursorShape,
665 pub peer_id: PeerId,
666 pub line_mode: bool,
667 pub participant_index: Option<ParticipantIndex>,
668 pub user_name: Option<SharedString>,
669}
670
671#[derive(Clone, Debug)]
672struct SelectionHistoryEntry {
673 selections: Arc<[Selection<Anchor>]>,
674 select_next_state: Option<SelectNextState>,
675 select_prev_state: Option<SelectNextState>,
676 add_selections_state: Option<AddSelectionsState>,
677}
678
679enum SelectionHistoryMode {
680 Normal,
681 Undoing,
682 Redoing,
683}
684
685#[derive(Clone, PartialEq, Eq, Hash)]
686struct HoveredCursor {
687 replica_id: u16,
688 selection_id: usize,
689}
690
691impl Default for SelectionHistoryMode {
692 fn default() -> Self {
693 Self::Normal
694 }
695}
696
697#[derive(Default)]
698struct SelectionHistory {
699 #[allow(clippy::type_complexity)]
700 selections_by_transaction:
701 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
702 mode: SelectionHistoryMode,
703 undo_stack: VecDeque<SelectionHistoryEntry>,
704 redo_stack: VecDeque<SelectionHistoryEntry>,
705}
706
707impl SelectionHistory {
708 fn insert_transaction(
709 &mut self,
710 transaction_id: TransactionId,
711 selections: Arc<[Selection<Anchor>]>,
712 ) {
713 self.selections_by_transaction
714 .insert(transaction_id, (selections, None));
715 }
716
717 #[allow(clippy::type_complexity)]
718 fn transaction(
719 &self,
720 transaction_id: TransactionId,
721 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
722 self.selections_by_transaction.get(&transaction_id)
723 }
724
725 #[allow(clippy::type_complexity)]
726 fn transaction_mut(
727 &mut self,
728 transaction_id: TransactionId,
729 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
730 self.selections_by_transaction.get_mut(&transaction_id)
731 }
732
733 fn push(&mut self, entry: SelectionHistoryEntry) {
734 if !entry.selections.is_empty() {
735 match self.mode {
736 SelectionHistoryMode::Normal => {
737 self.push_undo(entry);
738 self.redo_stack.clear();
739 }
740 SelectionHistoryMode::Undoing => self.push_redo(entry),
741 SelectionHistoryMode::Redoing => self.push_undo(entry),
742 }
743 }
744 }
745
746 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
747 if self
748 .undo_stack
749 .back()
750 .map_or(true, |e| e.selections != entry.selections)
751 {
752 self.undo_stack.push_back(entry);
753 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
754 self.undo_stack.pop_front();
755 }
756 }
757 }
758
759 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
760 if self
761 .redo_stack
762 .back()
763 .map_or(true, |e| e.selections != entry.selections)
764 {
765 self.redo_stack.push_back(entry);
766 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
767 self.redo_stack.pop_front();
768 }
769 }
770 }
771}
772
773struct RowHighlight {
774 index: usize,
775 range: RangeInclusive<Anchor>,
776 color: Option<Hsla>,
777 should_autoscroll: bool,
778}
779
780#[derive(Clone, Debug)]
781struct AddSelectionsState {
782 above: bool,
783 stack: Vec<usize>,
784}
785
786#[derive(Clone)]
787struct SelectNextState {
788 query: AhoCorasick,
789 wordwise: bool,
790 done: bool,
791}
792
793impl std::fmt::Debug for SelectNextState {
794 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
795 f.debug_struct(std::any::type_name::<Self>())
796 .field("wordwise", &self.wordwise)
797 .field("done", &self.done)
798 .finish()
799 }
800}
801
802#[derive(Debug)]
803struct AutocloseRegion {
804 selection_id: usize,
805 range: Range<Anchor>,
806 pair: BracketPair,
807}
808
809#[derive(Debug)]
810struct SnippetState {
811 ranges: Vec<Vec<Range<Anchor>>>,
812 active_index: usize,
813}
814
815#[doc(hidden)]
816pub struct RenameState {
817 pub range: Range<Anchor>,
818 pub old_name: Arc<str>,
819 pub editor: View<Editor>,
820 block_id: CustomBlockId,
821}
822
823struct InvalidationStack<T>(Vec<T>);
824
825struct RegisteredInlineCompletionProvider {
826 provider: Arc<dyn InlineCompletionProviderHandle>,
827 _subscription: Subscription,
828}
829
830enum ContextMenu {
831 Completions(CompletionsMenu),
832 CodeActions(CodeActionsMenu),
833}
834
835impl ContextMenu {
836 fn select_first(
837 &mut self,
838 project: Option<&Model<Project>>,
839 cx: &mut ViewContext<Editor>,
840 ) -> bool {
841 if self.visible() {
842 match self {
843 ContextMenu::Completions(menu) => menu.select_first(project, cx),
844 ContextMenu::CodeActions(menu) => menu.select_first(cx),
845 }
846 true
847 } else {
848 false
849 }
850 }
851
852 fn select_prev(
853 &mut self,
854 project: Option<&Model<Project>>,
855 cx: &mut ViewContext<Editor>,
856 ) -> bool {
857 if self.visible() {
858 match self {
859 ContextMenu::Completions(menu) => menu.select_prev(project, cx),
860 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
861 }
862 true
863 } else {
864 false
865 }
866 }
867
868 fn select_next(
869 &mut self,
870 project: Option<&Model<Project>>,
871 cx: &mut ViewContext<Editor>,
872 ) -> bool {
873 if self.visible() {
874 match self {
875 ContextMenu::Completions(menu) => menu.select_next(project, cx),
876 ContextMenu::CodeActions(menu) => menu.select_next(cx),
877 }
878 true
879 } else {
880 false
881 }
882 }
883
884 fn select_last(
885 &mut self,
886 project: Option<&Model<Project>>,
887 cx: &mut ViewContext<Editor>,
888 ) -> bool {
889 if self.visible() {
890 match self {
891 ContextMenu::Completions(menu) => menu.select_last(project, cx),
892 ContextMenu::CodeActions(menu) => menu.select_last(cx),
893 }
894 true
895 } else {
896 false
897 }
898 }
899
900 fn visible(&self) -> bool {
901 match self {
902 ContextMenu::Completions(menu) => menu.visible(),
903 ContextMenu::CodeActions(menu) => menu.visible(),
904 }
905 }
906
907 fn render(
908 &self,
909 cursor_position: DisplayPoint,
910 style: &EditorStyle,
911 max_height: Pixels,
912 workspace: Option<WeakView<Workspace>>,
913 cx: &mut ViewContext<Editor>,
914 ) -> (ContextMenuOrigin, AnyElement) {
915 match self {
916 ContextMenu::Completions(menu) => (
917 ContextMenuOrigin::EditorPoint(cursor_position),
918 menu.render(style, max_height, workspace, cx),
919 ),
920 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, max_height, cx),
921 }
922 }
923}
924
925enum ContextMenuOrigin {
926 EditorPoint(DisplayPoint),
927 GutterIndicator(DisplayRow),
928}
929
930#[derive(Clone)]
931struct CompletionsMenu {
932 id: CompletionId,
933 sort_completions: bool,
934 initial_position: Anchor,
935 buffer: Model<Buffer>,
936 completions: Arc<RwLock<Box<[Completion]>>>,
937 match_candidates: Arc<[StringMatchCandidate]>,
938 matches: Arc<[StringMatch]>,
939 selected_item: usize,
940 scroll_handle: UniformListScrollHandle,
941 selected_completion_documentation_resolve_debounce: Arc<Mutex<DebouncedDelay>>,
942}
943
944impl CompletionsMenu {
945 fn select_first(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
946 self.selected_item = 0;
947 self.scroll_handle.scroll_to_item(self.selected_item);
948 self.attempt_resolve_selected_completion_documentation(project, cx);
949 cx.notify();
950 }
951
952 fn select_prev(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
953 if self.selected_item > 0 {
954 self.selected_item -= 1;
955 } else {
956 self.selected_item = self.matches.len() - 1;
957 }
958 self.scroll_handle.scroll_to_item(self.selected_item);
959 self.attempt_resolve_selected_completion_documentation(project, cx);
960 cx.notify();
961 }
962
963 fn select_next(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
964 if self.selected_item + 1 < self.matches.len() {
965 self.selected_item += 1;
966 } else {
967 self.selected_item = 0;
968 }
969 self.scroll_handle.scroll_to_item(self.selected_item);
970 self.attempt_resolve_selected_completion_documentation(project, cx);
971 cx.notify();
972 }
973
974 fn select_last(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
975 self.selected_item = self.matches.len() - 1;
976 self.scroll_handle.scroll_to_item(self.selected_item);
977 self.attempt_resolve_selected_completion_documentation(project, cx);
978 cx.notify();
979 }
980
981 fn pre_resolve_completion_documentation(
982 buffer: Model<Buffer>,
983 completions: Arc<RwLock<Box<[Completion]>>>,
984 matches: Arc<[StringMatch]>,
985 editor: &Editor,
986 cx: &mut ViewContext<Editor>,
987 ) -> Task<()> {
988 let settings = EditorSettings::get_global(cx);
989 if !settings.show_completion_documentation {
990 return Task::ready(());
991 }
992
993 let Some(provider) = editor.completion_provider.as_ref() else {
994 return Task::ready(());
995 };
996
997 let resolve_task = provider.resolve_completions(
998 buffer,
999 matches.iter().map(|m| m.candidate_id).collect(),
1000 completions.clone(),
1001 cx,
1002 );
1003
1004 return cx.spawn(move |this, mut cx| async move {
1005 if let Some(true) = resolve_task.await.log_err() {
1006 this.update(&mut cx, |_, cx| cx.notify()).ok();
1007 }
1008 });
1009 }
1010
1011 fn attempt_resolve_selected_completion_documentation(
1012 &mut self,
1013 project: Option<&Model<Project>>,
1014 cx: &mut ViewContext<Editor>,
1015 ) {
1016 let settings = EditorSettings::get_global(cx);
1017 if !settings.show_completion_documentation {
1018 return;
1019 }
1020
1021 let completion_index = self.matches[self.selected_item].candidate_id;
1022 let Some(project) = project else {
1023 return;
1024 };
1025
1026 let resolve_task = project.update(cx, |project, cx| {
1027 project.resolve_completions(
1028 self.buffer.clone(),
1029 vec![completion_index],
1030 self.completions.clone(),
1031 cx,
1032 )
1033 });
1034
1035 let delay_ms =
1036 EditorSettings::get_global(cx).completion_documentation_secondary_query_debounce;
1037 let delay = Duration::from_millis(delay_ms);
1038
1039 self.selected_completion_documentation_resolve_debounce
1040 .lock()
1041 .fire_new(delay, cx, |_, cx| {
1042 cx.spawn(move |this, mut cx| async move {
1043 if let Some(true) = resolve_task.await.log_err() {
1044 this.update(&mut cx, |_, cx| cx.notify()).ok();
1045 }
1046 })
1047 });
1048 }
1049
1050 fn visible(&self) -> bool {
1051 !self.matches.is_empty()
1052 }
1053
1054 fn render(
1055 &self,
1056 style: &EditorStyle,
1057 max_height: Pixels,
1058 workspace: Option<WeakView<Workspace>>,
1059 cx: &mut ViewContext<Editor>,
1060 ) -> AnyElement {
1061 let settings = EditorSettings::get_global(cx);
1062 let show_completion_documentation = settings.show_completion_documentation;
1063
1064 let widest_completion_ix = self
1065 .matches
1066 .iter()
1067 .enumerate()
1068 .max_by_key(|(_, mat)| {
1069 let completions = self.completions.read();
1070 let completion = &completions[mat.candidate_id];
1071 let documentation = &completion.documentation;
1072
1073 let mut len = completion.label.text.chars().count();
1074 if let Some(Documentation::SingleLine(text)) = documentation {
1075 if show_completion_documentation {
1076 len += text.chars().count();
1077 }
1078 }
1079
1080 len
1081 })
1082 .map(|(ix, _)| ix);
1083
1084 let completions = self.completions.clone();
1085 let matches = self.matches.clone();
1086 let selected_item = self.selected_item;
1087 let style = style.clone();
1088
1089 let multiline_docs = if show_completion_documentation {
1090 let mat = &self.matches[selected_item];
1091 let multiline_docs = match &self.completions.read()[mat.candidate_id].documentation {
1092 Some(Documentation::MultiLinePlainText(text)) => {
1093 Some(div().child(SharedString::from(text.clone())))
1094 }
1095 Some(Documentation::MultiLineMarkdown(parsed)) if !parsed.text.is_empty() => {
1096 Some(div().child(render_parsed_markdown(
1097 "completions_markdown",
1098 parsed,
1099 &style,
1100 workspace,
1101 cx,
1102 )))
1103 }
1104 _ => None,
1105 };
1106 multiline_docs.map(|div| {
1107 div.id("multiline_docs")
1108 .max_h(max_height)
1109 .flex_1()
1110 .px_1p5()
1111 .py_1()
1112 .min_w(px(260.))
1113 .max_w(px(640.))
1114 .w(px(500.))
1115 .overflow_y_scroll()
1116 .occlude()
1117 })
1118 } else {
1119 None
1120 };
1121
1122 let list = uniform_list(
1123 cx.view().clone(),
1124 "completions",
1125 matches.len(),
1126 move |_editor, range, cx| {
1127 let start_ix = range.start;
1128 let completions_guard = completions.read();
1129
1130 matches[range]
1131 .iter()
1132 .enumerate()
1133 .map(|(ix, mat)| {
1134 let item_ix = start_ix + ix;
1135 let candidate_id = mat.candidate_id;
1136 let completion = &completions_guard[candidate_id];
1137
1138 let documentation = if show_completion_documentation {
1139 &completion.documentation
1140 } else {
1141 &None
1142 };
1143
1144 let highlights = gpui::combine_highlights(
1145 mat.ranges().map(|range| (range, FontWeight::BOLD.into())),
1146 styled_runs_for_code_label(&completion.label, &style.syntax).map(
1147 |(range, mut highlight)| {
1148 // Ignore font weight for syntax highlighting, as we'll use it
1149 // for fuzzy matches.
1150 highlight.font_weight = None;
1151
1152 if completion.lsp_completion.deprecated.unwrap_or(false) {
1153 highlight.strikethrough = Some(StrikethroughStyle {
1154 thickness: 1.0.into(),
1155 ..Default::default()
1156 });
1157 highlight.color = Some(cx.theme().colors().text_muted);
1158 }
1159
1160 (range, highlight)
1161 },
1162 ),
1163 );
1164 let completion_label = StyledText::new(completion.label.text.clone())
1165 .with_highlights(&style.text, highlights);
1166 let documentation_label =
1167 if let Some(Documentation::SingleLine(text)) = documentation {
1168 if text.trim().is_empty() {
1169 None
1170 } else {
1171 Some(
1172 Label::new(text.clone())
1173 .ml_4()
1174 .size(LabelSize::Small)
1175 .color(Color::Muted),
1176 )
1177 }
1178 } else {
1179 None
1180 };
1181
1182 div().min_w(px(220.)).max_w(px(540.)).child(
1183 ListItem::new(mat.candidate_id)
1184 .inset(true)
1185 .selected(item_ix == selected_item)
1186 .on_click(cx.listener(move |editor, _event, cx| {
1187 cx.stop_propagation();
1188 if let Some(task) = editor.confirm_completion(
1189 &ConfirmCompletion {
1190 item_ix: Some(item_ix),
1191 },
1192 cx,
1193 ) {
1194 task.detach_and_log_err(cx)
1195 }
1196 }))
1197 .child(h_flex().overflow_hidden().child(completion_label))
1198 .end_slot::<Label>(documentation_label),
1199 )
1200 })
1201 .collect()
1202 },
1203 )
1204 .occlude()
1205 .max_h(max_height)
1206 .track_scroll(self.scroll_handle.clone())
1207 .with_width_from_item(widest_completion_ix)
1208 .with_sizing_behavior(ListSizingBehavior::Infer);
1209
1210 Popover::new()
1211 .child(list)
1212 .when_some(multiline_docs, |popover, multiline_docs| {
1213 popover.aside(multiline_docs)
1214 })
1215 .into_any_element()
1216 }
1217
1218 pub async fn filter(&mut self, query: Option<&str>, executor: BackgroundExecutor) {
1219 let mut matches = if let Some(query) = query {
1220 fuzzy::match_strings(
1221 &self.match_candidates,
1222 query,
1223 query.chars().any(|c| c.is_uppercase()),
1224 100,
1225 &Default::default(),
1226 executor,
1227 )
1228 .await
1229 } else {
1230 self.match_candidates
1231 .iter()
1232 .enumerate()
1233 .map(|(candidate_id, candidate)| StringMatch {
1234 candidate_id,
1235 score: Default::default(),
1236 positions: Default::default(),
1237 string: candidate.string.clone(),
1238 })
1239 .collect()
1240 };
1241
1242 // Remove all candidates where the query's start does not match the start of any word in the candidate
1243 if let Some(query) = query {
1244 if let Some(query_start) = query.chars().next() {
1245 matches.retain(|string_match| {
1246 split_words(&string_match.string).any(|word| {
1247 // Check that the first codepoint of the word as lowercase matches the first
1248 // codepoint of the query as lowercase
1249 word.chars()
1250 .flat_map(|codepoint| codepoint.to_lowercase())
1251 .zip(query_start.to_lowercase())
1252 .all(|(word_cp, query_cp)| word_cp == query_cp)
1253 })
1254 });
1255 }
1256 }
1257
1258 let completions = self.completions.read();
1259 if self.sort_completions {
1260 matches.sort_unstable_by_key(|mat| {
1261 // We do want to strike a balance here between what the language server tells us
1262 // to sort by (the sort_text) and what are "obvious" good matches (i.e. when you type
1263 // `Creat` and there is a local variable called `CreateComponent`).
1264 // So what we do is: we bucket all matches into two buckets
1265 // - Strong matches
1266 // - Weak matches
1267 // Strong matches are the ones with a high fuzzy-matcher score (the "obvious" matches)
1268 // and the Weak matches are the rest.
1269 //
1270 // For the strong matches, we sort by the language-servers score first and for the weak
1271 // matches, we prefer our fuzzy finder first.
1272 //
1273 // The thinking behind that: it's useless to take the sort_text the language-server gives
1274 // us into account when it's obviously a bad match.
1275
1276 #[derive(PartialEq, Eq, PartialOrd, Ord)]
1277 enum MatchScore<'a> {
1278 Strong {
1279 sort_text: Option<&'a str>,
1280 score: Reverse<OrderedFloat<f64>>,
1281 sort_key: (usize, &'a str),
1282 },
1283 Weak {
1284 score: Reverse<OrderedFloat<f64>>,
1285 sort_text: Option<&'a str>,
1286 sort_key: (usize, &'a str),
1287 },
1288 }
1289
1290 let completion = &completions[mat.candidate_id];
1291 let sort_key = completion.sort_key();
1292 let sort_text = completion.lsp_completion.sort_text.as_deref();
1293 let score = Reverse(OrderedFloat(mat.score));
1294
1295 if mat.score >= 0.2 {
1296 MatchScore::Strong {
1297 sort_text,
1298 score,
1299 sort_key,
1300 }
1301 } else {
1302 MatchScore::Weak {
1303 score,
1304 sort_text,
1305 sort_key,
1306 }
1307 }
1308 });
1309 }
1310
1311 for mat in &mut matches {
1312 let completion = &completions[mat.candidate_id];
1313 mat.string.clone_from(&completion.label.text);
1314 for position in &mut mat.positions {
1315 *position += completion.label.filter_range.start;
1316 }
1317 }
1318 drop(completions);
1319
1320 self.matches = matches.into();
1321 self.selected_item = 0;
1322 }
1323}
1324
1325#[derive(Clone)]
1326struct CodeActionContents {
1327 tasks: Option<Arc<ResolvedTasks>>,
1328 actions: Option<Arc<[CodeAction]>>,
1329}
1330
1331impl CodeActionContents {
1332 fn len(&self) -> usize {
1333 match (&self.tasks, &self.actions) {
1334 (Some(tasks), Some(actions)) => actions.len() + tasks.templates.len(),
1335 (Some(tasks), None) => tasks.templates.len(),
1336 (None, Some(actions)) => actions.len(),
1337 (None, None) => 0,
1338 }
1339 }
1340
1341 fn is_empty(&self) -> bool {
1342 match (&self.tasks, &self.actions) {
1343 (Some(tasks), Some(actions)) => actions.is_empty() && tasks.templates.is_empty(),
1344 (Some(tasks), None) => tasks.templates.is_empty(),
1345 (None, Some(actions)) => actions.is_empty(),
1346 (None, None) => true,
1347 }
1348 }
1349
1350 fn iter(&self) -> impl Iterator<Item = CodeActionsItem> + '_ {
1351 self.tasks
1352 .iter()
1353 .flat_map(|tasks| {
1354 tasks
1355 .templates
1356 .iter()
1357 .map(|(kind, task)| CodeActionsItem::Task(kind.clone(), task.clone()))
1358 })
1359 .chain(self.actions.iter().flat_map(|actions| {
1360 actions
1361 .iter()
1362 .map(|action| CodeActionsItem::CodeAction(action.clone()))
1363 }))
1364 }
1365 fn get(&self, index: usize) -> Option<CodeActionsItem> {
1366 match (&self.tasks, &self.actions) {
1367 (Some(tasks), Some(actions)) => {
1368 if index < tasks.templates.len() {
1369 tasks
1370 .templates
1371 .get(index)
1372 .cloned()
1373 .map(|(kind, task)| CodeActionsItem::Task(kind, task))
1374 } else {
1375 actions
1376 .get(index - tasks.templates.len())
1377 .cloned()
1378 .map(CodeActionsItem::CodeAction)
1379 }
1380 }
1381 (Some(tasks), None) => tasks
1382 .templates
1383 .get(index)
1384 .cloned()
1385 .map(|(kind, task)| CodeActionsItem::Task(kind, task)),
1386 (None, Some(actions)) => actions.get(index).cloned().map(CodeActionsItem::CodeAction),
1387 (None, None) => None,
1388 }
1389 }
1390}
1391
1392#[allow(clippy::large_enum_variant)]
1393#[derive(Clone)]
1394enum CodeActionsItem {
1395 Task(TaskSourceKind, ResolvedTask),
1396 CodeAction(CodeAction),
1397}
1398
1399impl CodeActionsItem {
1400 fn as_task(&self) -> Option<&ResolvedTask> {
1401 let Self::Task(_, task) = self else {
1402 return None;
1403 };
1404 Some(task)
1405 }
1406 fn as_code_action(&self) -> Option<&CodeAction> {
1407 let Self::CodeAction(action) = self else {
1408 return None;
1409 };
1410 Some(action)
1411 }
1412 fn label(&self) -> String {
1413 match self {
1414 Self::CodeAction(action) => action.lsp_action.title.clone(),
1415 Self::Task(_, task) => task.resolved_label.clone(),
1416 }
1417 }
1418}
1419
1420struct CodeActionsMenu {
1421 actions: CodeActionContents,
1422 buffer: Model<Buffer>,
1423 selected_item: usize,
1424 scroll_handle: UniformListScrollHandle,
1425 deployed_from_indicator: Option<DisplayRow>,
1426}
1427
1428impl CodeActionsMenu {
1429 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
1430 self.selected_item = 0;
1431 self.scroll_handle.scroll_to_item(self.selected_item);
1432 cx.notify()
1433 }
1434
1435 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
1436 if self.selected_item > 0 {
1437 self.selected_item -= 1;
1438 } else {
1439 self.selected_item = self.actions.len() - 1;
1440 }
1441 self.scroll_handle.scroll_to_item(self.selected_item);
1442 cx.notify();
1443 }
1444
1445 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
1446 if self.selected_item + 1 < self.actions.len() {
1447 self.selected_item += 1;
1448 } else {
1449 self.selected_item = 0;
1450 }
1451 self.scroll_handle.scroll_to_item(self.selected_item);
1452 cx.notify();
1453 }
1454
1455 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
1456 self.selected_item = self.actions.len() - 1;
1457 self.scroll_handle.scroll_to_item(self.selected_item);
1458 cx.notify()
1459 }
1460
1461 fn visible(&self) -> bool {
1462 !self.actions.is_empty()
1463 }
1464
1465 fn render(
1466 &self,
1467 cursor_position: DisplayPoint,
1468 _style: &EditorStyle,
1469 max_height: Pixels,
1470 cx: &mut ViewContext<Editor>,
1471 ) -> (ContextMenuOrigin, AnyElement) {
1472 let actions = self.actions.clone();
1473 let selected_item = self.selected_item;
1474 let element = uniform_list(
1475 cx.view().clone(),
1476 "code_actions_menu",
1477 self.actions.len(),
1478 move |_this, range, cx| {
1479 actions
1480 .iter()
1481 .skip(range.start)
1482 .take(range.end - range.start)
1483 .enumerate()
1484 .map(|(ix, action)| {
1485 let item_ix = range.start + ix;
1486 let selected = selected_item == item_ix;
1487 let colors = cx.theme().colors();
1488 div()
1489 .px_2()
1490 .text_color(colors.text)
1491 .when(selected, |style| {
1492 style
1493 .bg(colors.element_active)
1494 .text_color(colors.text_accent)
1495 })
1496 .hover(|style| {
1497 style
1498 .bg(colors.element_hover)
1499 .text_color(colors.text_accent)
1500 })
1501 .whitespace_nowrap()
1502 .when_some(action.as_code_action(), |this, action| {
1503 this.on_mouse_down(
1504 MouseButton::Left,
1505 cx.listener(move |editor, _, cx| {
1506 cx.stop_propagation();
1507 if let Some(task) = editor.confirm_code_action(
1508 &ConfirmCodeAction {
1509 item_ix: Some(item_ix),
1510 },
1511 cx,
1512 ) {
1513 task.detach_and_log_err(cx)
1514 }
1515 }),
1516 )
1517 // TASK: It would be good to make lsp_action.title a SharedString to avoid allocating here.
1518 .child(SharedString::from(action.lsp_action.title.clone()))
1519 })
1520 .when_some(action.as_task(), |this, task| {
1521 this.on_mouse_down(
1522 MouseButton::Left,
1523 cx.listener(move |editor, _, cx| {
1524 cx.stop_propagation();
1525 if let Some(task) = editor.confirm_code_action(
1526 &ConfirmCodeAction {
1527 item_ix: Some(item_ix),
1528 },
1529 cx,
1530 ) {
1531 task.detach_and_log_err(cx)
1532 }
1533 }),
1534 )
1535 .child(SharedString::from(task.resolved_label.clone()))
1536 })
1537 })
1538 .collect()
1539 },
1540 )
1541 .elevation_1(cx)
1542 .px_2()
1543 .py_1()
1544 .max_h(max_height)
1545 .occlude()
1546 .track_scroll(self.scroll_handle.clone())
1547 .with_width_from_item(
1548 self.actions
1549 .iter()
1550 .enumerate()
1551 .max_by_key(|(_, action)| match action {
1552 CodeActionsItem::Task(_, task) => task.resolved_label.chars().count(),
1553 CodeActionsItem::CodeAction(action) => action.lsp_action.title.chars().count(),
1554 })
1555 .map(|(ix, _)| ix),
1556 )
1557 .with_sizing_behavior(ListSizingBehavior::Infer)
1558 .into_any_element();
1559
1560 let cursor_position = if let Some(row) = self.deployed_from_indicator {
1561 ContextMenuOrigin::GutterIndicator(row)
1562 } else {
1563 ContextMenuOrigin::EditorPoint(cursor_position)
1564 };
1565
1566 (cursor_position, element)
1567 }
1568}
1569
1570#[derive(Debug)]
1571struct ActiveDiagnosticGroup {
1572 primary_range: Range<Anchor>,
1573 primary_message: String,
1574 group_id: usize,
1575 blocks: HashMap<CustomBlockId, Diagnostic>,
1576 is_valid: bool,
1577}
1578
1579#[derive(Serialize, Deserialize, Clone, Debug)]
1580pub struct ClipboardSelection {
1581 pub len: usize,
1582 pub is_entire_line: bool,
1583 pub first_line_indent: u32,
1584}
1585
1586#[derive(Debug)]
1587pub(crate) struct NavigationData {
1588 cursor_anchor: Anchor,
1589 cursor_position: Point,
1590 scroll_anchor: ScrollAnchor,
1591 scroll_top_row: u32,
1592}
1593
1594#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1595enum GotoDefinitionKind {
1596 Symbol,
1597 Declaration,
1598 Type,
1599 Implementation,
1600}
1601
1602#[derive(Debug, Clone)]
1603enum InlayHintRefreshReason {
1604 Toggle(bool),
1605 SettingsChange(InlayHintSettings),
1606 NewLinesShown,
1607 BufferEdited(HashSet<Arc<Language>>),
1608 RefreshRequested,
1609 ExcerptsRemoved(Vec<ExcerptId>),
1610}
1611
1612impl InlayHintRefreshReason {
1613 fn description(&self) -> &'static str {
1614 match self {
1615 Self::Toggle(_) => "toggle",
1616 Self::SettingsChange(_) => "settings change",
1617 Self::NewLinesShown => "new lines shown",
1618 Self::BufferEdited(_) => "buffer edited",
1619 Self::RefreshRequested => "refresh requested",
1620 Self::ExcerptsRemoved(_) => "excerpts removed",
1621 }
1622 }
1623}
1624
1625pub(crate) struct FocusedBlock {
1626 id: BlockId,
1627 focus_handle: WeakFocusHandle,
1628}
1629
1630impl Editor {
1631 pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
1632 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1633 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1634 Self::new(
1635 EditorMode::SingleLine { auto_width: false },
1636 buffer,
1637 None,
1638 false,
1639 cx,
1640 )
1641 }
1642
1643 pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
1644 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1645 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1646 Self::new(EditorMode::Full, buffer, None, false, cx)
1647 }
1648
1649 pub fn auto_width(cx: &mut ViewContext<Self>) -> Self {
1650 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1651 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1652 Self::new(
1653 EditorMode::SingleLine { auto_width: true },
1654 buffer,
1655 None,
1656 false,
1657 cx,
1658 )
1659 }
1660
1661 pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
1662 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1663 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1664 Self::new(
1665 EditorMode::AutoHeight { max_lines },
1666 buffer,
1667 None,
1668 false,
1669 cx,
1670 )
1671 }
1672
1673 pub fn for_buffer(
1674 buffer: Model<Buffer>,
1675 project: Option<Model<Project>>,
1676 cx: &mut ViewContext<Self>,
1677 ) -> Self {
1678 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1679 Self::new(EditorMode::Full, buffer, project, false, cx)
1680 }
1681
1682 pub fn for_multibuffer(
1683 buffer: Model<MultiBuffer>,
1684 project: Option<Model<Project>>,
1685 show_excerpt_controls: bool,
1686 cx: &mut ViewContext<Self>,
1687 ) -> Self {
1688 Self::new(EditorMode::Full, buffer, project, show_excerpt_controls, cx)
1689 }
1690
1691 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1692 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1693 let mut clone = Self::new(
1694 self.mode,
1695 self.buffer.clone(),
1696 self.project.clone(),
1697 show_excerpt_controls,
1698 cx,
1699 );
1700 self.display_map.update(cx, |display_map, cx| {
1701 let snapshot = display_map.snapshot(cx);
1702 clone.display_map.update(cx, |display_map, cx| {
1703 display_map.set_state(&snapshot, cx);
1704 });
1705 });
1706 clone.selections.clone_state(&self.selections);
1707 clone.scroll_manager.clone_state(&self.scroll_manager);
1708 clone.searchable = self.searchable;
1709 clone
1710 }
1711
1712 pub fn new(
1713 mode: EditorMode,
1714 buffer: Model<MultiBuffer>,
1715 project: Option<Model<Project>>,
1716 show_excerpt_controls: bool,
1717 cx: &mut ViewContext<Self>,
1718 ) -> Self {
1719 let style = cx.text_style();
1720 let font_size = style.font_size.to_pixels(cx.rem_size());
1721 let editor = cx.view().downgrade();
1722 let fold_placeholder = FoldPlaceholder {
1723 constrain_width: true,
1724 render: Arc::new(move |fold_id, fold_range, cx| {
1725 let editor = editor.clone();
1726 div()
1727 .id(fold_id)
1728 .bg(cx.theme().colors().ghost_element_background)
1729 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1730 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1731 .rounded_sm()
1732 .size_full()
1733 .cursor_pointer()
1734 .child("⋯")
1735 .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
1736 .on_click(move |_, cx| {
1737 editor
1738 .update(cx, |editor, cx| {
1739 editor.unfold_ranges(
1740 [fold_range.start..fold_range.end],
1741 true,
1742 false,
1743 cx,
1744 );
1745 cx.stop_propagation();
1746 })
1747 .ok();
1748 })
1749 .into_any()
1750 }),
1751 merge_adjacent: true,
1752 };
1753 let file_header_size = if show_excerpt_controls { 3 } else { 2 };
1754 let display_map = cx.new_model(|cx| {
1755 DisplayMap::new(
1756 buffer.clone(),
1757 style.font(),
1758 font_size,
1759 None,
1760 show_excerpt_controls,
1761 file_header_size,
1762 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1763 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1764 fold_placeholder,
1765 cx,
1766 )
1767 });
1768
1769 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1770
1771 let blink_manager = cx.new_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1772
1773 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1774 .then(|| language_settings::SoftWrap::PreferLine);
1775
1776 let mut project_subscriptions = Vec::new();
1777 if mode == EditorMode::Full {
1778 if let Some(project) = project.as_ref() {
1779 if buffer.read(cx).is_singleton() {
1780 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1781 cx.emit(EditorEvent::TitleChanged);
1782 }));
1783 }
1784 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1785 if let project::Event::RefreshInlayHints = event {
1786 editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1787 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1788 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1789 let focus_handle = editor.focus_handle(cx);
1790 if focus_handle.is_focused(cx) {
1791 let snapshot = buffer.read(cx).snapshot();
1792 for (range, snippet) in snippet_edits {
1793 let editor_range =
1794 language::range_from_lsp(*range).to_offset(&snapshot);
1795 editor
1796 .insert_snippet(&[editor_range], snippet.clone(), cx)
1797 .ok();
1798 }
1799 }
1800 }
1801 }
1802 }));
1803 let task_inventory = project.read(cx).task_inventory().clone();
1804 project_subscriptions.push(cx.observe(&task_inventory, |editor, _, cx| {
1805 editor.tasks_update_task = Some(editor.refresh_runnables(cx));
1806 }));
1807 }
1808 }
1809
1810 let inlay_hint_settings = inlay_hint_settings(
1811 selections.newest_anchor().head(),
1812 &buffer.read(cx).snapshot(cx),
1813 cx,
1814 );
1815 let focus_handle = cx.focus_handle();
1816 cx.on_focus(&focus_handle, Self::handle_focus).detach();
1817 cx.on_focus_in(&focus_handle, Self::handle_focus_in)
1818 .detach();
1819 cx.on_focus_out(&focus_handle, Self::handle_focus_out)
1820 .detach();
1821 cx.on_blur(&focus_handle, Self::handle_blur).detach();
1822
1823 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1824 Some(false)
1825 } else {
1826 None
1827 };
1828
1829 let mut this = Self {
1830 focus_handle,
1831 show_cursor_when_unfocused: false,
1832 last_focused_descendant: None,
1833 buffer: buffer.clone(),
1834 display_map: display_map.clone(),
1835 selections,
1836 scroll_manager: ScrollManager::new(cx),
1837 columnar_selection_tail: None,
1838 add_selections_state: None,
1839 select_next_state: None,
1840 select_prev_state: None,
1841 selection_history: Default::default(),
1842 autoclose_regions: Default::default(),
1843 snippet_stack: Default::default(),
1844 select_larger_syntax_node_stack: Vec::new(),
1845 ime_transaction: Default::default(),
1846 active_diagnostics: None,
1847 soft_wrap_mode_override,
1848 completion_provider: project.clone().map(|project| Box::new(project) as _),
1849 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1850 project,
1851 blink_manager: blink_manager.clone(),
1852 show_local_selections: true,
1853 mode,
1854 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1855 show_gutter: mode == EditorMode::Full,
1856 show_line_numbers: None,
1857 use_relative_line_numbers: None,
1858 show_git_diff_gutter: None,
1859 show_code_actions: None,
1860 show_runnables: None,
1861 show_wrap_guides: None,
1862 show_indent_guides,
1863 placeholder_text: None,
1864 highlight_order: 0,
1865 highlighted_rows: HashMap::default(),
1866 background_highlights: Default::default(),
1867 gutter_highlights: TreeMap::default(),
1868 scrollbar_marker_state: ScrollbarMarkerState::default(),
1869 active_indent_guides_state: ActiveIndentGuidesState::default(),
1870 nav_history: None,
1871 context_menu: RwLock::new(None),
1872 mouse_context_menu: None,
1873 completion_tasks: Default::default(),
1874 signature_help_state: SignatureHelpState::default(),
1875 auto_signature_help: None,
1876 find_all_references_task_sources: Vec::new(),
1877 next_completion_id: 0,
1878 completion_documentation_pre_resolve_debounce: DebouncedDelay::new(),
1879 next_inlay_id: 0,
1880 available_code_actions: Default::default(),
1881 code_actions_task: Default::default(),
1882 document_highlights_task: Default::default(),
1883 linked_editing_range_task: Default::default(),
1884 pending_rename: Default::default(),
1885 searchable: true,
1886 cursor_shape: Default::default(),
1887 current_line_highlight: None,
1888 autoindent_mode: Some(AutoindentMode::EachLine),
1889 collapse_matches: false,
1890 workspace: None,
1891 input_enabled: true,
1892 use_modal_editing: mode == EditorMode::Full,
1893 read_only: false,
1894 use_autoclose: true,
1895 use_auto_surround: true,
1896 auto_replace_emoji_shortcode: false,
1897 leader_peer_id: None,
1898 remote_id: None,
1899 hover_state: Default::default(),
1900 hovered_link_state: Default::default(),
1901 inline_completion_provider: None,
1902 active_inline_completion: None,
1903 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1904 expanded_hunks: ExpandedHunks::default(),
1905 gutter_hovered: false,
1906 pixel_position_of_newest_cursor: None,
1907 last_bounds: None,
1908 expect_bounds_change: None,
1909 gutter_dimensions: GutterDimensions::default(),
1910 style: None,
1911 show_cursor_names: false,
1912 hovered_cursors: Default::default(),
1913 next_editor_action_id: EditorActionId::default(),
1914 editor_actions: Rc::default(),
1915 show_inline_completions_override: None,
1916 custom_context_menu: None,
1917 show_git_blame_gutter: false,
1918 show_git_blame_inline: false,
1919 show_selection_menu: None,
1920 show_git_blame_inline_delay_task: None,
1921 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1922 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1923 .session
1924 .restore_unsaved_buffers,
1925 blame: None,
1926 blame_subscription: None,
1927 file_header_size,
1928 tasks: Default::default(),
1929 _subscriptions: vec![
1930 cx.observe(&buffer, Self::on_buffer_changed),
1931 cx.subscribe(&buffer, Self::on_buffer_event),
1932 cx.observe(&display_map, Self::on_display_map_changed),
1933 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1934 cx.observe_global::<SettingsStore>(Self::settings_changed),
1935 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1936 cx.observe_window_activation(|editor, cx| {
1937 let active = cx.is_window_active();
1938 editor.blink_manager.update(cx, |blink_manager, cx| {
1939 if active {
1940 blink_manager.enable(cx);
1941 } else {
1942 blink_manager.disable(cx);
1943 }
1944 });
1945 }),
1946 ],
1947 tasks_update_task: None,
1948 linked_edit_ranges: Default::default(),
1949 previous_search_ranges: None,
1950 breadcrumb_header: None,
1951 focused_block: None,
1952 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1953 addons: HashMap::default(),
1954 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1955 };
1956 this.tasks_update_task = Some(this.refresh_runnables(cx));
1957 this._subscriptions.extend(project_subscriptions);
1958
1959 this.end_selection(cx);
1960 this.scroll_manager.show_scrollbar(cx);
1961
1962 if mode == EditorMode::Full {
1963 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1964 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1965
1966 if this.git_blame_inline_enabled {
1967 this.git_blame_inline_enabled = true;
1968 this.start_git_blame_inline(false, cx);
1969 }
1970 }
1971
1972 this.report_editor_event("open", None, cx);
1973 this
1974 }
1975
1976 pub fn mouse_menu_is_focused(&self, cx: &WindowContext) -> bool {
1977 self.mouse_context_menu
1978 .as_ref()
1979 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(cx))
1980 }
1981
1982 fn key_context(&self, cx: &ViewContext<Self>) -> KeyContext {
1983 let mut key_context = KeyContext::new_with_defaults();
1984 key_context.add("Editor");
1985 let mode = match self.mode {
1986 EditorMode::SingleLine { .. } => "single_line",
1987 EditorMode::AutoHeight { .. } => "auto_height",
1988 EditorMode::Full => "full",
1989 };
1990
1991 if EditorSettings::jupyter_enabled(cx) {
1992 key_context.add("jupyter");
1993 }
1994
1995 key_context.set("mode", mode);
1996 if self.pending_rename.is_some() {
1997 key_context.add("renaming");
1998 }
1999 if self.context_menu_visible() {
2000 match self.context_menu.read().as_ref() {
2001 Some(ContextMenu::Completions(_)) => {
2002 key_context.add("menu");
2003 key_context.add("showing_completions")
2004 }
2005 Some(ContextMenu::CodeActions(_)) => {
2006 key_context.add("menu");
2007 key_context.add("showing_code_actions")
2008 }
2009 None => {}
2010 }
2011 }
2012
2013 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
2014 if !self.focus_handle(cx).contains_focused(cx)
2015 || (self.is_focused(cx) || self.mouse_menu_is_focused(cx))
2016 {
2017 for addon in self.addons.values() {
2018 addon.extend_key_context(&mut key_context, cx)
2019 }
2020 }
2021
2022 if let Some(extension) = self
2023 .buffer
2024 .read(cx)
2025 .as_singleton()
2026 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
2027 {
2028 key_context.set("extension", extension.to_string());
2029 }
2030
2031 if self.has_active_inline_completion(cx) {
2032 key_context.add("copilot_suggestion");
2033 key_context.add("inline_completion");
2034 }
2035
2036 key_context
2037 }
2038
2039 pub fn new_file(
2040 workspace: &mut Workspace,
2041 _: &workspace::NewFile,
2042 cx: &mut ViewContext<Workspace>,
2043 ) {
2044 Self::new_in_workspace(workspace, cx).detach_and_prompt_err(
2045 "Failed to create buffer",
2046 cx,
2047 |e, _| match e.error_code() {
2048 ErrorCode::RemoteUpgradeRequired => Some(format!(
2049 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2050 e.error_tag("required").unwrap_or("the latest version")
2051 )),
2052 _ => None,
2053 },
2054 );
2055 }
2056
2057 pub fn new_in_workspace(
2058 workspace: &mut Workspace,
2059 cx: &mut ViewContext<Workspace>,
2060 ) -> Task<Result<View<Editor>>> {
2061 let project = workspace.project().clone();
2062 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2063
2064 cx.spawn(|workspace, mut cx| async move {
2065 let buffer = create.await?;
2066 workspace.update(&mut cx, |workspace, cx| {
2067 let editor =
2068 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx));
2069 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
2070 editor
2071 })
2072 })
2073 }
2074
2075 fn new_file_vertical(
2076 workspace: &mut Workspace,
2077 _: &workspace::NewFileSplitVertical,
2078 cx: &mut ViewContext<Workspace>,
2079 ) {
2080 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), cx)
2081 }
2082
2083 fn new_file_horizontal(
2084 workspace: &mut Workspace,
2085 _: &workspace::NewFileSplitHorizontal,
2086 cx: &mut ViewContext<Workspace>,
2087 ) {
2088 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), cx)
2089 }
2090
2091 fn new_file_in_direction(
2092 workspace: &mut Workspace,
2093 direction: SplitDirection,
2094 cx: &mut ViewContext<Workspace>,
2095 ) {
2096 let project = workspace.project().clone();
2097 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2098
2099 cx.spawn(|workspace, mut cx| async move {
2100 let buffer = create.await?;
2101 workspace.update(&mut cx, move |workspace, cx| {
2102 workspace.split_item(
2103 direction,
2104 Box::new(
2105 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)),
2106 ),
2107 cx,
2108 )
2109 })?;
2110 anyhow::Ok(())
2111 })
2112 .detach_and_prompt_err("Failed to create buffer", cx, |e, _| match e.error_code() {
2113 ErrorCode::RemoteUpgradeRequired => Some(format!(
2114 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2115 e.error_tag("required").unwrap_or("the latest version")
2116 )),
2117 _ => None,
2118 });
2119 }
2120
2121 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
2122 self.buffer.read(cx).replica_id()
2123 }
2124
2125 pub fn leader_peer_id(&self) -> Option<PeerId> {
2126 self.leader_peer_id
2127 }
2128
2129 pub fn buffer(&self) -> &Model<MultiBuffer> {
2130 &self.buffer
2131 }
2132
2133 pub fn workspace(&self) -> Option<View<Workspace>> {
2134 self.workspace.as_ref()?.0.upgrade()
2135 }
2136
2137 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
2138 self.buffer().read(cx).title(cx)
2139 }
2140
2141 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
2142 EditorSnapshot {
2143 mode: self.mode,
2144 show_gutter: self.show_gutter,
2145 show_line_numbers: self.show_line_numbers,
2146 show_git_diff_gutter: self.show_git_diff_gutter,
2147 show_code_actions: self.show_code_actions,
2148 show_runnables: self.show_runnables,
2149 render_git_blame_gutter: self.render_git_blame_gutter(cx),
2150 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
2151 scroll_anchor: self.scroll_manager.anchor(),
2152 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
2153 placeholder_text: self.placeholder_text.clone(),
2154 is_focused: self.focus_handle.is_focused(cx),
2155 current_line_highlight: self
2156 .current_line_highlight
2157 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
2158 gutter_hovered: self.gutter_hovered,
2159 }
2160 }
2161
2162 pub fn language_at<T: ToOffset>(&self, point: T, cx: &AppContext) -> Option<Arc<Language>> {
2163 self.buffer.read(cx).language_at(point, cx)
2164 }
2165
2166 pub fn file_at<T: ToOffset>(
2167 &self,
2168 point: T,
2169 cx: &AppContext,
2170 ) -> Option<Arc<dyn language::File>> {
2171 self.buffer.read(cx).read(cx).file_at(point).cloned()
2172 }
2173
2174 pub fn active_excerpt(
2175 &self,
2176 cx: &AppContext,
2177 ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
2178 self.buffer
2179 .read(cx)
2180 .excerpt_containing(self.selections.newest_anchor().head(), cx)
2181 }
2182
2183 pub fn mode(&self) -> EditorMode {
2184 self.mode
2185 }
2186
2187 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
2188 self.collaboration_hub.as_deref()
2189 }
2190
2191 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
2192 self.collaboration_hub = Some(hub);
2193 }
2194
2195 pub fn set_custom_context_menu(
2196 &mut self,
2197 f: impl 'static
2198 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
2199 ) {
2200 self.custom_context_menu = Some(Box::new(f))
2201 }
2202
2203 pub fn set_completion_provider(&mut self, provider: Box<dyn CompletionProvider>) {
2204 self.completion_provider = Some(provider);
2205 }
2206
2207 pub fn set_inline_completion_provider<T>(
2208 &mut self,
2209 provider: Option<Model<T>>,
2210 cx: &mut ViewContext<Self>,
2211 ) where
2212 T: InlineCompletionProvider,
2213 {
2214 self.inline_completion_provider =
2215 provider.map(|provider| RegisteredInlineCompletionProvider {
2216 _subscription: cx.observe(&provider, |this, _, cx| {
2217 if this.focus_handle.is_focused(cx) {
2218 this.update_visible_inline_completion(cx);
2219 }
2220 }),
2221 provider: Arc::new(provider),
2222 });
2223 self.refresh_inline_completion(false, false, cx);
2224 }
2225
2226 pub fn placeholder_text(&self, _cx: &WindowContext) -> Option<&str> {
2227 self.placeholder_text.as_deref()
2228 }
2229
2230 pub fn set_placeholder_text(
2231 &mut self,
2232 placeholder_text: impl Into<Arc<str>>,
2233 cx: &mut ViewContext<Self>,
2234 ) {
2235 let placeholder_text = Some(placeholder_text.into());
2236 if self.placeholder_text != placeholder_text {
2237 self.placeholder_text = placeholder_text;
2238 cx.notify();
2239 }
2240 }
2241
2242 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
2243 self.cursor_shape = cursor_shape;
2244
2245 // Disrupt blink for immediate user feedback that the cursor shape has changed
2246 self.blink_manager.update(cx, BlinkManager::show_cursor);
2247
2248 cx.notify();
2249 }
2250
2251 pub fn set_current_line_highlight(
2252 &mut self,
2253 current_line_highlight: Option<CurrentLineHighlight>,
2254 ) {
2255 self.current_line_highlight = current_line_highlight;
2256 }
2257
2258 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
2259 self.collapse_matches = collapse_matches;
2260 }
2261
2262 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
2263 if self.collapse_matches {
2264 return range.start..range.start;
2265 }
2266 range.clone()
2267 }
2268
2269 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
2270 if self.display_map.read(cx).clip_at_line_ends != clip {
2271 self.display_map
2272 .update(cx, |map, _| map.clip_at_line_ends = clip);
2273 }
2274 }
2275
2276 pub fn set_input_enabled(&mut self, input_enabled: bool) {
2277 self.input_enabled = input_enabled;
2278 }
2279
2280 pub fn set_autoindent(&mut self, autoindent: bool) {
2281 if autoindent {
2282 self.autoindent_mode = Some(AutoindentMode::EachLine);
2283 } else {
2284 self.autoindent_mode = None;
2285 }
2286 }
2287
2288 pub fn read_only(&self, cx: &AppContext) -> bool {
2289 self.read_only || self.buffer.read(cx).read_only()
2290 }
2291
2292 pub fn set_read_only(&mut self, read_only: bool) {
2293 self.read_only = read_only;
2294 }
2295
2296 pub fn set_use_autoclose(&mut self, autoclose: bool) {
2297 self.use_autoclose = autoclose;
2298 }
2299
2300 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
2301 self.use_auto_surround = auto_surround;
2302 }
2303
2304 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
2305 self.auto_replace_emoji_shortcode = auto_replace;
2306 }
2307
2308 pub fn toggle_inline_completions(
2309 &mut self,
2310 _: &ToggleInlineCompletions,
2311 cx: &mut ViewContext<Self>,
2312 ) {
2313 if self.show_inline_completions_override.is_some() {
2314 self.set_show_inline_completions(None, cx);
2315 } else {
2316 let cursor = self.selections.newest_anchor().head();
2317 if let Some((buffer, cursor_buffer_position)) =
2318 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
2319 {
2320 let show_inline_completions =
2321 !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx);
2322 self.set_show_inline_completions(Some(show_inline_completions), cx);
2323 }
2324 }
2325 }
2326
2327 pub fn set_show_inline_completions(
2328 &mut self,
2329 show_inline_completions: Option<bool>,
2330 cx: &mut ViewContext<Self>,
2331 ) {
2332 self.show_inline_completions_override = show_inline_completions;
2333 self.refresh_inline_completion(false, true, cx);
2334 }
2335
2336 fn should_show_inline_completions(
2337 &self,
2338 buffer: &Model<Buffer>,
2339 buffer_position: language::Anchor,
2340 cx: &AppContext,
2341 ) -> bool {
2342 if let Some(provider) = self.inline_completion_provider() {
2343 if let Some(show_inline_completions) = self.show_inline_completions_override {
2344 show_inline_completions
2345 } else {
2346 self.mode == EditorMode::Full && provider.is_enabled(&buffer, buffer_position, cx)
2347 }
2348 } else {
2349 false
2350 }
2351 }
2352
2353 pub fn set_use_modal_editing(&mut self, to: bool) {
2354 self.use_modal_editing = to;
2355 }
2356
2357 pub fn use_modal_editing(&self) -> bool {
2358 self.use_modal_editing
2359 }
2360
2361 fn selections_did_change(
2362 &mut self,
2363 local: bool,
2364 old_cursor_position: &Anchor,
2365 show_completions: bool,
2366 cx: &mut ViewContext<Self>,
2367 ) {
2368 // Copy selections to primary selection buffer
2369 #[cfg(target_os = "linux")]
2370 if local {
2371 let selections = self.selections.all::<usize>(cx);
2372 let buffer_handle = self.buffer.read(cx).read(cx);
2373
2374 let mut text = String::new();
2375 for (index, selection) in selections.iter().enumerate() {
2376 let text_for_selection = buffer_handle
2377 .text_for_range(selection.start..selection.end)
2378 .collect::<String>();
2379
2380 text.push_str(&text_for_selection);
2381 if index != selections.len() - 1 {
2382 text.push('\n');
2383 }
2384 }
2385
2386 if !text.is_empty() {
2387 cx.write_to_primary(ClipboardItem::new_string(text));
2388 }
2389 }
2390
2391 if self.focus_handle.is_focused(cx) && self.leader_peer_id.is_none() {
2392 self.buffer.update(cx, |buffer, cx| {
2393 buffer.set_active_selections(
2394 &self.selections.disjoint_anchors(),
2395 self.selections.line_mode,
2396 self.cursor_shape,
2397 cx,
2398 )
2399 });
2400 }
2401 let display_map = self
2402 .display_map
2403 .update(cx, |display_map, cx| display_map.snapshot(cx));
2404 let buffer = &display_map.buffer_snapshot;
2405 self.add_selections_state = None;
2406 self.select_next_state = None;
2407 self.select_prev_state = None;
2408 self.select_larger_syntax_node_stack.clear();
2409 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2410 self.snippet_stack
2411 .invalidate(&self.selections.disjoint_anchors(), buffer);
2412 self.take_rename(false, cx);
2413
2414 let new_cursor_position = self.selections.newest_anchor().head();
2415
2416 self.push_to_nav_history(
2417 *old_cursor_position,
2418 Some(new_cursor_position.to_point(buffer)),
2419 cx,
2420 );
2421
2422 if local {
2423 let new_cursor_position = self.selections.newest_anchor().head();
2424 let mut context_menu = self.context_menu.write();
2425 let completion_menu = match context_menu.as_ref() {
2426 Some(ContextMenu::Completions(menu)) => Some(menu),
2427
2428 _ => {
2429 *context_menu = None;
2430 None
2431 }
2432 };
2433
2434 if let Some(completion_menu) = completion_menu {
2435 let cursor_position = new_cursor_position.to_offset(buffer);
2436 let (word_range, kind) = buffer.surrounding_word(completion_menu.initial_position);
2437 if kind == Some(CharKind::Word)
2438 && word_range.to_inclusive().contains(&cursor_position)
2439 {
2440 let mut completion_menu = completion_menu.clone();
2441 drop(context_menu);
2442
2443 let query = Self::completion_query(buffer, cursor_position);
2444 cx.spawn(move |this, mut cx| async move {
2445 completion_menu
2446 .filter(query.as_deref(), cx.background_executor().clone())
2447 .await;
2448
2449 this.update(&mut cx, |this, cx| {
2450 let mut context_menu = this.context_menu.write();
2451 let Some(ContextMenu::Completions(menu)) = context_menu.as_ref() else {
2452 return;
2453 };
2454
2455 if menu.id > completion_menu.id {
2456 return;
2457 }
2458
2459 *context_menu = Some(ContextMenu::Completions(completion_menu));
2460 drop(context_menu);
2461 cx.notify();
2462 })
2463 })
2464 .detach();
2465
2466 if show_completions {
2467 self.show_completions(&ShowCompletions { trigger: None }, cx);
2468 }
2469 } else {
2470 drop(context_menu);
2471 self.hide_context_menu(cx);
2472 }
2473 } else {
2474 drop(context_menu);
2475 }
2476
2477 hide_hover(self, cx);
2478
2479 if old_cursor_position.to_display_point(&display_map).row()
2480 != new_cursor_position.to_display_point(&display_map).row()
2481 {
2482 self.available_code_actions.take();
2483 }
2484 self.refresh_code_actions(cx);
2485 self.refresh_document_highlights(cx);
2486 refresh_matching_bracket_highlights(self, cx);
2487 self.discard_inline_completion(false, cx);
2488 linked_editing_ranges::refresh_linked_ranges(self, cx);
2489 if self.git_blame_inline_enabled {
2490 self.start_inline_blame_timer(cx);
2491 }
2492 }
2493
2494 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2495 cx.emit(EditorEvent::SelectionsChanged { local });
2496
2497 if self.selections.disjoint_anchors().len() == 1 {
2498 cx.emit(SearchEvent::ActiveMatchChanged)
2499 }
2500 cx.notify();
2501 }
2502
2503 pub fn change_selections<R>(
2504 &mut self,
2505 autoscroll: Option<Autoscroll>,
2506 cx: &mut ViewContext<Self>,
2507 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2508 ) -> R {
2509 self.change_selections_inner(autoscroll, true, cx, change)
2510 }
2511
2512 pub fn change_selections_inner<R>(
2513 &mut self,
2514 autoscroll: Option<Autoscroll>,
2515 request_completions: bool,
2516 cx: &mut ViewContext<Self>,
2517 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2518 ) -> R {
2519 let old_cursor_position = self.selections.newest_anchor().head();
2520 self.push_to_selection_history();
2521
2522 let (changed, result) = self.selections.change_with(cx, change);
2523
2524 if changed {
2525 if let Some(autoscroll) = autoscroll {
2526 self.request_autoscroll(autoscroll, cx);
2527 }
2528 self.selections_did_change(true, &old_cursor_position, request_completions, cx);
2529
2530 if self.should_open_signature_help_automatically(
2531 &old_cursor_position,
2532 self.signature_help_state.backspace_pressed(),
2533 cx,
2534 ) {
2535 self.show_signature_help(&ShowSignatureHelp, cx);
2536 }
2537 self.signature_help_state.set_backspace_pressed(false);
2538 }
2539
2540 result
2541 }
2542
2543 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2544 where
2545 I: IntoIterator<Item = (Range<S>, T)>,
2546 S: ToOffset,
2547 T: Into<Arc<str>>,
2548 {
2549 if self.read_only(cx) {
2550 return;
2551 }
2552
2553 self.buffer
2554 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2555 }
2556
2557 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2558 where
2559 I: IntoIterator<Item = (Range<S>, T)>,
2560 S: ToOffset,
2561 T: Into<Arc<str>>,
2562 {
2563 if self.read_only(cx) {
2564 return;
2565 }
2566
2567 self.buffer.update(cx, |buffer, cx| {
2568 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2569 });
2570 }
2571
2572 pub fn edit_with_block_indent<I, S, T>(
2573 &mut self,
2574 edits: I,
2575 original_indent_columns: Vec<u32>,
2576 cx: &mut ViewContext<Self>,
2577 ) where
2578 I: IntoIterator<Item = (Range<S>, T)>,
2579 S: ToOffset,
2580 T: Into<Arc<str>>,
2581 {
2582 if self.read_only(cx) {
2583 return;
2584 }
2585
2586 self.buffer.update(cx, |buffer, cx| {
2587 buffer.edit(
2588 edits,
2589 Some(AutoindentMode::Block {
2590 original_indent_columns,
2591 }),
2592 cx,
2593 )
2594 });
2595 }
2596
2597 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
2598 self.hide_context_menu(cx);
2599
2600 match phase {
2601 SelectPhase::Begin {
2602 position,
2603 add,
2604 click_count,
2605 } => self.begin_selection(position, add, click_count, cx),
2606 SelectPhase::BeginColumnar {
2607 position,
2608 goal_column,
2609 reset,
2610 } => self.begin_columnar_selection(position, goal_column, reset, cx),
2611 SelectPhase::Extend {
2612 position,
2613 click_count,
2614 } => self.extend_selection(position, click_count, cx),
2615 SelectPhase::Update {
2616 position,
2617 goal_column,
2618 scroll_delta,
2619 } => self.update_selection(position, goal_column, scroll_delta, cx),
2620 SelectPhase::End => self.end_selection(cx),
2621 }
2622 }
2623
2624 fn extend_selection(
2625 &mut self,
2626 position: DisplayPoint,
2627 click_count: usize,
2628 cx: &mut ViewContext<Self>,
2629 ) {
2630 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2631 let tail = self.selections.newest::<usize>(cx).tail();
2632 self.begin_selection(position, false, click_count, cx);
2633
2634 let position = position.to_offset(&display_map, Bias::Left);
2635 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2636
2637 let mut pending_selection = self
2638 .selections
2639 .pending_anchor()
2640 .expect("extend_selection not called with pending selection");
2641 if position >= tail {
2642 pending_selection.start = tail_anchor;
2643 } else {
2644 pending_selection.end = tail_anchor;
2645 pending_selection.reversed = true;
2646 }
2647
2648 let mut pending_mode = self.selections.pending_mode().unwrap();
2649 match &mut pending_mode {
2650 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2651 _ => {}
2652 }
2653
2654 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
2655 s.set_pending(pending_selection, pending_mode)
2656 });
2657 }
2658
2659 fn begin_selection(
2660 &mut self,
2661 position: DisplayPoint,
2662 add: bool,
2663 click_count: usize,
2664 cx: &mut ViewContext<Self>,
2665 ) {
2666 if !self.focus_handle.is_focused(cx) {
2667 self.last_focused_descendant = None;
2668 cx.focus(&self.focus_handle);
2669 }
2670
2671 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2672 let buffer = &display_map.buffer_snapshot;
2673 let newest_selection = self.selections.newest_anchor().clone();
2674 let position = display_map.clip_point(position, Bias::Left);
2675
2676 let start;
2677 let end;
2678 let mode;
2679 let auto_scroll;
2680 match click_count {
2681 1 => {
2682 start = buffer.anchor_before(position.to_point(&display_map));
2683 end = start;
2684 mode = SelectMode::Character;
2685 auto_scroll = true;
2686 }
2687 2 => {
2688 let range = movement::surrounding_word(&display_map, position);
2689 start = buffer.anchor_before(range.start.to_point(&display_map));
2690 end = buffer.anchor_before(range.end.to_point(&display_map));
2691 mode = SelectMode::Word(start..end);
2692 auto_scroll = true;
2693 }
2694 3 => {
2695 let position = display_map
2696 .clip_point(position, Bias::Left)
2697 .to_point(&display_map);
2698 let line_start = display_map.prev_line_boundary(position).0;
2699 let next_line_start = buffer.clip_point(
2700 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2701 Bias::Left,
2702 );
2703 start = buffer.anchor_before(line_start);
2704 end = buffer.anchor_before(next_line_start);
2705 mode = SelectMode::Line(start..end);
2706 auto_scroll = true;
2707 }
2708 _ => {
2709 start = buffer.anchor_before(0);
2710 end = buffer.anchor_before(buffer.len());
2711 mode = SelectMode::All;
2712 auto_scroll = false;
2713 }
2714 }
2715
2716 let point_to_delete: Option<usize> = {
2717 let selected_points: Vec<Selection<Point>> =
2718 self.selections.disjoint_in_range(start..end, cx);
2719
2720 if !add || click_count > 1 {
2721 None
2722 } else if selected_points.len() > 0 {
2723 Some(selected_points[0].id)
2724 } else {
2725 let clicked_point_already_selected =
2726 self.selections.disjoint.iter().find(|selection| {
2727 selection.start.to_point(buffer) == start.to_point(buffer)
2728 || selection.end.to_point(buffer) == end.to_point(buffer)
2729 });
2730
2731 if let Some(selection) = clicked_point_already_selected {
2732 Some(selection.id)
2733 } else {
2734 None
2735 }
2736 }
2737 };
2738
2739 let selections_count = self.selections.count();
2740
2741 self.change_selections(auto_scroll.then(|| Autoscroll::newest()), cx, |s| {
2742 if let Some(point_to_delete) = point_to_delete {
2743 s.delete(point_to_delete);
2744
2745 if selections_count == 1 {
2746 s.set_pending_anchor_range(start..end, mode);
2747 }
2748 } else {
2749 if !add {
2750 s.clear_disjoint();
2751 } else if click_count > 1 {
2752 s.delete(newest_selection.id)
2753 }
2754
2755 s.set_pending_anchor_range(start..end, mode);
2756 }
2757 });
2758 }
2759
2760 fn begin_columnar_selection(
2761 &mut self,
2762 position: DisplayPoint,
2763 goal_column: u32,
2764 reset: bool,
2765 cx: &mut ViewContext<Self>,
2766 ) {
2767 if !self.focus_handle.is_focused(cx) {
2768 self.last_focused_descendant = None;
2769 cx.focus(&self.focus_handle);
2770 }
2771
2772 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2773
2774 if reset {
2775 let pointer_position = display_map
2776 .buffer_snapshot
2777 .anchor_before(position.to_point(&display_map));
2778
2779 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
2780 s.clear_disjoint();
2781 s.set_pending_anchor_range(
2782 pointer_position..pointer_position,
2783 SelectMode::Character,
2784 );
2785 });
2786 }
2787
2788 let tail = self.selections.newest::<Point>(cx).tail();
2789 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2790
2791 if !reset {
2792 self.select_columns(
2793 tail.to_display_point(&display_map),
2794 position,
2795 goal_column,
2796 &display_map,
2797 cx,
2798 );
2799 }
2800 }
2801
2802 fn update_selection(
2803 &mut self,
2804 position: DisplayPoint,
2805 goal_column: u32,
2806 scroll_delta: gpui::Point<f32>,
2807 cx: &mut ViewContext<Self>,
2808 ) {
2809 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2810
2811 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2812 let tail = tail.to_display_point(&display_map);
2813 self.select_columns(tail, position, goal_column, &display_map, cx);
2814 } else if let Some(mut pending) = self.selections.pending_anchor() {
2815 let buffer = self.buffer.read(cx).snapshot(cx);
2816 let head;
2817 let tail;
2818 let mode = self.selections.pending_mode().unwrap();
2819 match &mode {
2820 SelectMode::Character => {
2821 head = position.to_point(&display_map);
2822 tail = pending.tail().to_point(&buffer);
2823 }
2824 SelectMode::Word(original_range) => {
2825 let original_display_range = original_range.start.to_display_point(&display_map)
2826 ..original_range.end.to_display_point(&display_map);
2827 let original_buffer_range = original_display_range.start.to_point(&display_map)
2828 ..original_display_range.end.to_point(&display_map);
2829 if movement::is_inside_word(&display_map, position)
2830 || original_display_range.contains(&position)
2831 {
2832 let word_range = movement::surrounding_word(&display_map, position);
2833 if word_range.start < original_display_range.start {
2834 head = word_range.start.to_point(&display_map);
2835 } else {
2836 head = word_range.end.to_point(&display_map);
2837 }
2838 } else {
2839 head = position.to_point(&display_map);
2840 }
2841
2842 if head <= original_buffer_range.start {
2843 tail = original_buffer_range.end;
2844 } else {
2845 tail = original_buffer_range.start;
2846 }
2847 }
2848 SelectMode::Line(original_range) => {
2849 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2850
2851 let position = display_map
2852 .clip_point(position, Bias::Left)
2853 .to_point(&display_map);
2854 let line_start = display_map.prev_line_boundary(position).0;
2855 let next_line_start = buffer.clip_point(
2856 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2857 Bias::Left,
2858 );
2859
2860 if line_start < original_range.start {
2861 head = line_start
2862 } else {
2863 head = next_line_start
2864 }
2865
2866 if head <= original_range.start {
2867 tail = original_range.end;
2868 } else {
2869 tail = original_range.start;
2870 }
2871 }
2872 SelectMode::All => {
2873 return;
2874 }
2875 };
2876
2877 if head < tail {
2878 pending.start = buffer.anchor_before(head);
2879 pending.end = buffer.anchor_before(tail);
2880 pending.reversed = true;
2881 } else {
2882 pending.start = buffer.anchor_before(tail);
2883 pending.end = buffer.anchor_before(head);
2884 pending.reversed = false;
2885 }
2886
2887 self.change_selections(None, cx, |s| {
2888 s.set_pending(pending, mode);
2889 });
2890 } else {
2891 log::error!("update_selection dispatched with no pending selection");
2892 return;
2893 }
2894
2895 self.apply_scroll_delta(scroll_delta, cx);
2896 cx.notify();
2897 }
2898
2899 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
2900 self.columnar_selection_tail.take();
2901 if self.selections.pending_anchor().is_some() {
2902 let selections = self.selections.all::<usize>(cx);
2903 self.change_selections(None, cx, |s| {
2904 s.select(selections);
2905 s.clear_pending();
2906 });
2907 }
2908 }
2909
2910 fn select_columns(
2911 &mut self,
2912 tail: DisplayPoint,
2913 head: DisplayPoint,
2914 goal_column: u32,
2915 display_map: &DisplaySnapshot,
2916 cx: &mut ViewContext<Self>,
2917 ) {
2918 let start_row = cmp::min(tail.row(), head.row());
2919 let end_row = cmp::max(tail.row(), head.row());
2920 let start_column = cmp::min(tail.column(), goal_column);
2921 let end_column = cmp::max(tail.column(), goal_column);
2922 let reversed = start_column < tail.column();
2923
2924 let selection_ranges = (start_row.0..=end_row.0)
2925 .map(DisplayRow)
2926 .filter_map(|row| {
2927 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2928 let start = display_map
2929 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2930 .to_point(display_map);
2931 let end = display_map
2932 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2933 .to_point(display_map);
2934 if reversed {
2935 Some(end..start)
2936 } else {
2937 Some(start..end)
2938 }
2939 } else {
2940 None
2941 }
2942 })
2943 .collect::<Vec<_>>();
2944
2945 self.change_selections(None, cx, |s| {
2946 s.select_ranges(selection_ranges);
2947 });
2948 cx.notify();
2949 }
2950
2951 pub fn has_pending_nonempty_selection(&self) -> bool {
2952 let pending_nonempty_selection = match self.selections.pending_anchor() {
2953 Some(Selection { start, end, .. }) => start != end,
2954 None => false,
2955 };
2956
2957 pending_nonempty_selection
2958 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2959 }
2960
2961 pub fn has_pending_selection(&self) -> bool {
2962 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2963 }
2964
2965 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
2966 if self.clear_clicked_diff_hunks(cx) {
2967 cx.notify();
2968 return;
2969 }
2970 if self.dismiss_menus_and_popups(true, cx) {
2971 return;
2972 }
2973
2974 if self.mode == EditorMode::Full {
2975 if self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel()) {
2976 return;
2977 }
2978 }
2979
2980 cx.propagate();
2981 }
2982
2983 pub fn dismiss_menus_and_popups(
2984 &mut self,
2985 should_report_inline_completion_event: bool,
2986 cx: &mut ViewContext<Self>,
2987 ) -> bool {
2988 if self.take_rename(false, cx).is_some() {
2989 return true;
2990 }
2991
2992 if hide_hover(self, cx) {
2993 return true;
2994 }
2995
2996 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2997 return true;
2998 }
2999
3000 if self.hide_context_menu(cx).is_some() {
3001 return true;
3002 }
3003
3004 if self.mouse_context_menu.take().is_some() {
3005 return true;
3006 }
3007
3008 if self.discard_inline_completion(should_report_inline_completion_event, cx) {
3009 return true;
3010 }
3011
3012 if self.snippet_stack.pop().is_some() {
3013 return true;
3014 }
3015
3016 if self.mode == EditorMode::Full {
3017 if self.active_diagnostics.is_some() {
3018 self.dismiss_diagnostics(cx);
3019 return true;
3020 }
3021 }
3022
3023 false
3024 }
3025
3026 fn linked_editing_ranges_for(
3027 &self,
3028 selection: Range<text::Anchor>,
3029 cx: &AppContext,
3030 ) -> Option<HashMap<Model<Buffer>, Vec<Range<text::Anchor>>>> {
3031 if self.linked_edit_ranges.is_empty() {
3032 return None;
3033 }
3034 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
3035 selection.end.buffer_id.and_then(|end_buffer_id| {
3036 if selection.start.buffer_id != Some(end_buffer_id) {
3037 return None;
3038 }
3039 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
3040 let snapshot = buffer.read(cx).snapshot();
3041 self.linked_edit_ranges
3042 .get(end_buffer_id, selection.start..selection.end, &snapshot)
3043 .map(|ranges| (ranges, snapshot, buffer))
3044 })?;
3045 use text::ToOffset as TO;
3046 // find offset from the start of current range to current cursor position
3047 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
3048
3049 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
3050 let start_difference = start_offset - start_byte_offset;
3051 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
3052 let end_difference = end_offset - start_byte_offset;
3053 // Current range has associated linked ranges.
3054 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3055 for range in linked_ranges.iter() {
3056 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
3057 let end_offset = start_offset + end_difference;
3058 let start_offset = start_offset + start_difference;
3059 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
3060 continue;
3061 }
3062 if self.selections.disjoint_anchor_ranges().iter().any(|s| {
3063 if s.start.buffer_id != selection.start.buffer_id
3064 || s.end.buffer_id != selection.end.buffer_id
3065 {
3066 return false;
3067 }
3068 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
3069 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
3070 }) {
3071 continue;
3072 }
3073 let start = buffer_snapshot.anchor_after(start_offset);
3074 let end = buffer_snapshot.anchor_after(end_offset);
3075 linked_edits
3076 .entry(buffer.clone())
3077 .or_default()
3078 .push(start..end);
3079 }
3080 Some(linked_edits)
3081 }
3082
3083 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3084 let text: Arc<str> = text.into();
3085
3086 if self.read_only(cx) {
3087 return;
3088 }
3089
3090 let selections = self.selections.all_adjusted(cx);
3091 let mut bracket_inserted = false;
3092 let mut edits = Vec::new();
3093 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3094 let mut new_selections = Vec::with_capacity(selections.len());
3095 let mut new_autoclose_regions = Vec::new();
3096 let snapshot = self.buffer.read(cx).read(cx);
3097
3098 for (selection, autoclose_region) in
3099 self.selections_with_autoclose_regions(selections, &snapshot)
3100 {
3101 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
3102 // Determine if the inserted text matches the opening or closing
3103 // bracket of any of this language's bracket pairs.
3104 let mut bracket_pair = None;
3105 let mut is_bracket_pair_start = false;
3106 let mut is_bracket_pair_end = false;
3107 if !text.is_empty() {
3108 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
3109 // and they are removing the character that triggered IME popup.
3110 for (pair, enabled) in scope.brackets() {
3111 if !pair.close && !pair.surround {
3112 continue;
3113 }
3114
3115 if enabled && pair.start.ends_with(text.as_ref()) {
3116 bracket_pair = Some(pair.clone());
3117 is_bracket_pair_start = true;
3118 break;
3119 }
3120 if pair.end.as_str() == text.as_ref() {
3121 bracket_pair = Some(pair.clone());
3122 is_bracket_pair_end = true;
3123 break;
3124 }
3125 }
3126 }
3127
3128 if let Some(bracket_pair) = bracket_pair {
3129 let snapshot_settings = snapshot.settings_at(selection.start, cx);
3130 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
3131 let auto_surround =
3132 self.use_auto_surround && snapshot_settings.use_auto_surround;
3133 if selection.is_empty() {
3134 if is_bracket_pair_start {
3135 let prefix_len = bracket_pair.start.len() - text.len();
3136
3137 // If the inserted text is a suffix of an opening bracket and the
3138 // selection is preceded by the rest of the opening bracket, then
3139 // insert the closing bracket.
3140 let following_text_allows_autoclose = snapshot
3141 .chars_at(selection.start)
3142 .next()
3143 .map_or(true, |c| scope.should_autoclose_before(c));
3144 let preceding_text_matches_prefix = prefix_len == 0
3145 || (selection.start.column >= (prefix_len as u32)
3146 && snapshot.contains_str_at(
3147 Point::new(
3148 selection.start.row,
3149 selection.start.column - (prefix_len as u32),
3150 ),
3151 &bracket_pair.start[..prefix_len],
3152 ));
3153
3154 if autoclose
3155 && bracket_pair.close
3156 && following_text_allows_autoclose
3157 && preceding_text_matches_prefix
3158 {
3159 let anchor = snapshot.anchor_before(selection.end);
3160 new_selections.push((selection.map(|_| anchor), text.len()));
3161 new_autoclose_regions.push((
3162 anchor,
3163 text.len(),
3164 selection.id,
3165 bracket_pair.clone(),
3166 ));
3167 edits.push((
3168 selection.range(),
3169 format!("{}{}", text, bracket_pair.end).into(),
3170 ));
3171 bracket_inserted = true;
3172 continue;
3173 }
3174 }
3175
3176 if let Some(region) = autoclose_region {
3177 // If the selection is followed by an auto-inserted closing bracket,
3178 // then don't insert that closing bracket again; just move the selection
3179 // past the closing bracket.
3180 let should_skip = selection.end == region.range.end.to_point(&snapshot)
3181 && text.as_ref() == region.pair.end.as_str();
3182 if should_skip {
3183 let anchor = snapshot.anchor_after(selection.end);
3184 new_selections
3185 .push((selection.map(|_| anchor), region.pair.end.len()));
3186 continue;
3187 }
3188 }
3189
3190 let always_treat_brackets_as_autoclosed = snapshot
3191 .settings_at(selection.start, cx)
3192 .always_treat_brackets_as_autoclosed;
3193 if always_treat_brackets_as_autoclosed
3194 && is_bracket_pair_end
3195 && snapshot.contains_str_at(selection.end, text.as_ref())
3196 {
3197 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
3198 // and the inserted text is a closing bracket and the selection is followed
3199 // by the closing bracket then move the selection past the closing bracket.
3200 let anchor = snapshot.anchor_after(selection.end);
3201 new_selections.push((selection.map(|_| anchor), text.len()));
3202 continue;
3203 }
3204 }
3205 // If an opening bracket is 1 character long and is typed while
3206 // text is selected, then surround that text with the bracket pair.
3207 else if auto_surround
3208 && bracket_pair.surround
3209 && is_bracket_pair_start
3210 && bracket_pair.start.chars().count() == 1
3211 {
3212 edits.push((selection.start..selection.start, text.clone()));
3213 edits.push((
3214 selection.end..selection.end,
3215 bracket_pair.end.as_str().into(),
3216 ));
3217 bracket_inserted = true;
3218 new_selections.push((
3219 Selection {
3220 id: selection.id,
3221 start: snapshot.anchor_after(selection.start),
3222 end: snapshot.anchor_before(selection.end),
3223 reversed: selection.reversed,
3224 goal: selection.goal,
3225 },
3226 0,
3227 ));
3228 continue;
3229 }
3230 }
3231 }
3232
3233 if self.auto_replace_emoji_shortcode
3234 && selection.is_empty()
3235 && text.as_ref().ends_with(':')
3236 {
3237 if let Some(possible_emoji_short_code) =
3238 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
3239 {
3240 if !possible_emoji_short_code.is_empty() {
3241 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
3242 let emoji_shortcode_start = Point::new(
3243 selection.start.row,
3244 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
3245 );
3246
3247 // Remove shortcode from buffer
3248 edits.push((
3249 emoji_shortcode_start..selection.start,
3250 "".to_string().into(),
3251 ));
3252 new_selections.push((
3253 Selection {
3254 id: selection.id,
3255 start: snapshot.anchor_after(emoji_shortcode_start),
3256 end: snapshot.anchor_before(selection.start),
3257 reversed: selection.reversed,
3258 goal: selection.goal,
3259 },
3260 0,
3261 ));
3262
3263 // Insert emoji
3264 let selection_start_anchor = snapshot.anchor_after(selection.start);
3265 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3266 edits.push((selection.start..selection.end, emoji.to_string().into()));
3267
3268 continue;
3269 }
3270 }
3271 }
3272 }
3273
3274 // If not handling any auto-close operation, then just replace the selected
3275 // text with the given input and move the selection to the end of the
3276 // newly inserted text.
3277 let anchor = snapshot.anchor_after(selection.end);
3278 if !self.linked_edit_ranges.is_empty() {
3279 let start_anchor = snapshot.anchor_before(selection.start);
3280
3281 let is_word_char = text.chars().next().map_or(true, |char| {
3282 let scope = snapshot.language_scope_at(start_anchor.to_offset(&snapshot));
3283 let kind = char_kind(&scope, char);
3284
3285 kind == CharKind::Word
3286 });
3287
3288 if is_word_char {
3289 if let Some(ranges) = self
3290 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3291 {
3292 for (buffer, edits) in ranges {
3293 linked_edits
3294 .entry(buffer.clone())
3295 .or_default()
3296 .extend(edits.into_iter().map(|range| (range, text.clone())));
3297 }
3298 }
3299 }
3300 }
3301
3302 new_selections.push((selection.map(|_| anchor), 0));
3303 edits.push((selection.start..selection.end, text.clone()));
3304 }
3305
3306 drop(snapshot);
3307
3308 self.transact(cx, |this, cx| {
3309 this.buffer.update(cx, |buffer, cx| {
3310 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3311 });
3312 for (buffer, edits) in linked_edits {
3313 buffer.update(cx, |buffer, cx| {
3314 let snapshot = buffer.snapshot();
3315 let edits = edits
3316 .into_iter()
3317 .map(|(range, text)| {
3318 use text::ToPoint as TP;
3319 let end_point = TP::to_point(&range.end, &snapshot);
3320 let start_point = TP::to_point(&range.start, &snapshot);
3321 (start_point..end_point, text)
3322 })
3323 .sorted_by_key(|(range, _)| range.start)
3324 .collect::<Vec<_>>();
3325 buffer.edit(edits, None, cx);
3326 })
3327 }
3328 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3329 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3330 let snapshot = this.buffer.read(cx).read(cx);
3331 let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
3332 .zip(new_selection_deltas)
3333 .map(|(selection, delta)| Selection {
3334 id: selection.id,
3335 start: selection.start + delta,
3336 end: selection.end + delta,
3337 reversed: selection.reversed,
3338 goal: SelectionGoal::None,
3339 })
3340 .collect::<Vec<_>>();
3341
3342 let mut i = 0;
3343 for (position, delta, selection_id, pair) in new_autoclose_regions {
3344 let position = position.to_offset(&snapshot) + delta;
3345 let start = snapshot.anchor_before(position);
3346 let end = snapshot.anchor_after(position);
3347 while let Some(existing_state) = this.autoclose_regions.get(i) {
3348 match existing_state.range.start.cmp(&start, &snapshot) {
3349 Ordering::Less => i += 1,
3350 Ordering::Greater => break,
3351 Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
3352 Ordering::Less => i += 1,
3353 Ordering::Equal => break,
3354 Ordering::Greater => break,
3355 },
3356 }
3357 }
3358 this.autoclose_regions.insert(
3359 i,
3360 AutocloseRegion {
3361 selection_id,
3362 range: start..end,
3363 pair,
3364 },
3365 );
3366 }
3367
3368 drop(snapshot);
3369 let had_active_inline_completion = this.has_active_inline_completion(cx);
3370 this.change_selections_inner(Some(Autoscroll::fit()), false, cx, |s| {
3371 s.select(new_selections)
3372 });
3373
3374 if !bracket_inserted && EditorSettings::get_global(cx).use_on_type_format {
3375 if let Some(on_type_format_task) =
3376 this.trigger_on_type_formatting(text.to_string(), cx)
3377 {
3378 on_type_format_task.detach_and_log_err(cx);
3379 }
3380 }
3381
3382 let editor_settings = EditorSettings::get_global(cx);
3383 if bracket_inserted
3384 && (editor_settings.auto_signature_help
3385 || editor_settings.show_signature_help_after_edits)
3386 {
3387 this.show_signature_help(&ShowSignatureHelp, cx);
3388 }
3389
3390 let trigger_in_words = !had_active_inline_completion;
3391 this.trigger_completion_on_input(&text, trigger_in_words, cx);
3392 linked_editing_ranges::refresh_linked_ranges(this, cx);
3393 this.refresh_inline_completion(true, false, cx);
3394 });
3395 }
3396
3397 fn find_possible_emoji_shortcode_at_position(
3398 snapshot: &MultiBufferSnapshot,
3399 position: Point,
3400 ) -> Option<String> {
3401 let mut chars = Vec::new();
3402 let mut found_colon = false;
3403 for char in snapshot.reversed_chars_at(position).take(100) {
3404 // Found a possible emoji shortcode in the middle of the buffer
3405 if found_colon {
3406 if char.is_whitespace() {
3407 chars.reverse();
3408 return Some(chars.iter().collect());
3409 }
3410 // If the previous character is not a whitespace, we are in the middle of a word
3411 // and we only want to complete the shortcode if the word is made up of other emojis
3412 let mut containing_word = String::new();
3413 for ch in snapshot
3414 .reversed_chars_at(position)
3415 .skip(chars.len() + 1)
3416 .take(100)
3417 {
3418 if ch.is_whitespace() {
3419 break;
3420 }
3421 containing_word.push(ch);
3422 }
3423 let containing_word = containing_word.chars().rev().collect::<String>();
3424 if util::word_consists_of_emojis(containing_word.as_str()) {
3425 chars.reverse();
3426 return Some(chars.iter().collect());
3427 }
3428 }
3429
3430 if char.is_whitespace() || !char.is_ascii() {
3431 return None;
3432 }
3433 if char == ':' {
3434 found_colon = true;
3435 } else {
3436 chars.push(char);
3437 }
3438 }
3439 // Found a possible emoji shortcode at the beginning of the buffer
3440 chars.reverse();
3441 Some(chars.iter().collect())
3442 }
3443
3444 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
3445 self.transact(cx, |this, cx| {
3446 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3447 let selections = this.selections.all::<usize>(cx);
3448 let multi_buffer = this.buffer.read(cx);
3449 let buffer = multi_buffer.snapshot(cx);
3450 selections
3451 .iter()
3452 .map(|selection| {
3453 let start_point = selection.start.to_point(&buffer);
3454 let mut indent =
3455 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3456 indent.len = cmp::min(indent.len, start_point.column);
3457 let start = selection.start;
3458 let end = selection.end;
3459 let selection_is_empty = start == end;
3460 let language_scope = buffer.language_scope_at(start);
3461 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3462 &language_scope
3463 {
3464 let leading_whitespace_len = buffer
3465 .reversed_chars_at(start)
3466 .take_while(|c| c.is_whitespace() && *c != '\n')
3467 .map(|c| c.len_utf8())
3468 .sum::<usize>();
3469
3470 let trailing_whitespace_len = buffer
3471 .chars_at(end)
3472 .take_while(|c| c.is_whitespace() && *c != '\n')
3473 .map(|c| c.len_utf8())
3474 .sum::<usize>();
3475
3476 let insert_extra_newline =
3477 language.brackets().any(|(pair, enabled)| {
3478 let pair_start = pair.start.trim_end();
3479 let pair_end = pair.end.trim_start();
3480
3481 enabled
3482 && pair.newline
3483 && buffer.contains_str_at(
3484 end + trailing_whitespace_len,
3485 pair_end,
3486 )
3487 && buffer.contains_str_at(
3488 (start - leading_whitespace_len)
3489 .saturating_sub(pair_start.len()),
3490 pair_start,
3491 )
3492 });
3493
3494 // Comment extension on newline is allowed only for cursor selections
3495 let comment_delimiter = maybe!({
3496 if !selection_is_empty {
3497 return None;
3498 }
3499
3500 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
3501 return None;
3502 }
3503
3504 let delimiters = language.line_comment_prefixes();
3505 let max_len_of_delimiter =
3506 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3507 let (snapshot, range) =
3508 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3509
3510 let mut index_of_first_non_whitespace = 0;
3511 let comment_candidate = snapshot
3512 .chars_for_range(range)
3513 .skip_while(|c| {
3514 let should_skip = c.is_whitespace();
3515 if should_skip {
3516 index_of_first_non_whitespace += 1;
3517 }
3518 should_skip
3519 })
3520 .take(max_len_of_delimiter)
3521 .collect::<String>();
3522 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3523 comment_candidate.starts_with(comment_prefix.as_ref())
3524 })?;
3525 let cursor_is_placed_after_comment_marker =
3526 index_of_first_non_whitespace + comment_prefix.len()
3527 <= start_point.column as usize;
3528 if cursor_is_placed_after_comment_marker {
3529 Some(comment_prefix.clone())
3530 } else {
3531 None
3532 }
3533 });
3534 (comment_delimiter, insert_extra_newline)
3535 } else {
3536 (None, false)
3537 };
3538
3539 let capacity_for_delimiter = comment_delimiter
3540 .as_deref()
3541 .map(str::len)
3542 .unwrap_or_default();
3543 let mut new_text =
3544 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3545 new_text.push_str("\n");
3546 new_text.extend(indent.chars());
3547 if let Some(delimiter) = &comment_delimiter {
3548 new_text.push_str(&delimiter);
3549 }
3550 if insert_extra_newline {
3551 new_text = new_text.repeat(2);
3552 }
3553
3554 let anchor = buffer.anchor_after(end);
3555 let new_selection = selection.map(|_| anchor);
3556 (
3557 (start..end, new_text),
3558 (insert_extra_newline, new_selection),
3559 )
3560 })
3561 .unzip()
3562 };
3563
3564 this.edit_with_autoindent(edits, cx);
3565 let buffer = this.buffer.read(cx).snapshot(cx);
3566 let new_selections = selection_fixup_info
3567 .into_iter()
3568 .map(|(extra_newline_inserted, new_selection)| {
3569 let mut cursor = new_selection.end.to_point(&buffer);
3570 if extra_newline_inserted {
3571 cursor.row -= 1;
3572 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3573 }
3574 new_selection.map(|_| cursor)
3575 })
3576 .collect();
3577
3578 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
3579 this.refresh_inline_completion(true, false, cx);
3580 });
3581 }
3582
3583 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
3584 let buffer = self.buffer.read(cx);
3585 let snapshot = buffer.snapshot(cx);
3586
3587 let mut edits = Vec::new();
3588 let mut rows = Vec::new();
3589
3590 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3591 let cursor = selection.head();
3592 let row = cursor.row;
3593
3594 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3595
3596 let newline = "\n".to_string();
3597 edits.push((start_of_line..start_of_line, newline));
3598
3599 rows.push(row + rows_inserted as u32);
3600 }
3601
3602 self.transact(cx, |editor, cx| {
3603 editor.edit(edits, cx);
3604
3605 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3606 let mut index = 0;
3607 s.move_cursors_with(|map, _, _| {
3608 let row = rows[index];
3609 index += 1;
3610
3611 let point = Point::new(row, 0);
3612 let boundary = map.next_line_boundary(point).1;
3613 let clipped = map.clip_point(boundary, Bias::Left);
3614
3615 (clipped, SelectionGoal::None)
3616 });
3617 });
3618
3619 let mut indent_edits = Vec::new();
3620 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3621 for row in rows {
3622 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3623 for (row, indent) in indents {
3624 if indent.len == 0 {
3625 continue;
3626 }
3627
3628 let text = match indent.kind {
3629 IndentKind::Space => " ".repeat(indent.len as usize),
3630 IndentKind::Tab => "\t".repeat(indent.len as usize),
3631 };
3632 let point = Point::new(row.0, 0);
3633 indent_edits.push((point..point, text));
3634 }
3635 }
3636 editor.edit(indent_edits, cx);
3637 });
3638 }
3639
3640 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
3641 let buffer = self.buffer.read(cx);
3642 let snapshot = buffer.snapshot(cx);
3643
3644 let mut edits = Vec::new();
3645 let mut rows = Vec::new();
3646 let mut rows_inserted = 0;
3647
3648 for selection in self.selections.all_adjusted(cx) {
3649 let cursor = selection.head();
3650 let row = cursor.row;
3651
3652 let point = Point::new(row + 1, 0);
3653 let start_of_line = snapshot.clip_point(point, Bias::Left);
3654
3655 let newline = "\n".to_string();
3656 edits.push((start_of_line..start_of_line, newline));
3657
3658 rows_inserted += 1;
3659 rows.push(row + rows_inserted);
3660 }
3661
3662 self.transact(cx, |editor, cx| {
3663 editor.edit(edits, cx);
3664
3665 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3666 let mut index = 0;
3667 s.move_cursors_with(|map, _, _| {
3668 let row = rows[index];
3669 index += 1;
3670
3671 let point = Point::new(row, 0);
3672 let boundary = map.next_line_boundary(point).1;
3673 let clipped = map.clip_point(boundary, Bias::Left);
3674
3675 (clipped, SelectionGoal::None)
3676 });
3677 });
3678
3679 let mut indent_edits = Vec::new();
3680 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3681 for row in rows {
3682 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3683 for (row, indent) in indents {
3684 if indent.len == 0 {
3685 continue;
3686 }
3687
3688 let text = match indent.kind {
3689 IndentKind::Space => " ".repeat(indent.len as usize),
3690 IndentKind::Tab => "\t".repeat(indent.len as usize),
3691 };
3692 let point = Point::new(row.0, 0);
3693 indent_edits.push((point..point, text));
3694 }
3695 }
3696 editor.edit(indent_edits, cx);
3697 });
3698 }
3699
3700 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3701 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3702 original_indent_columns: Vec::new(),
3703 });
3704 self.insert_with_autoindent_mode(text, autoindent, cx);
3705 }
3706
3707 fn insert_with_autoindent_mode(
3708 &mut self,
3709 text: &str,
3710 autoindent_mode: Option<AutoindentMode>,
3711 cx: &mut ViewContext<Self>,
3712 ) {
3713 if self.read_only(cx) {
3714 return;
3715 }
3716
3717 let text: Arc<str> = text.into();
3718 self.transact(cx, |this, cx| {
3719 let old_selections = this.selections.all_adjusted(cx);
3720 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3721 let anchors = {
3722 let snapshot = buffer.read(cx);
3723 old_selections
3724 .iter()
3725 .map(|s| {
3726 let anchor = snapshot.anchor_after(s.head());
3727 s.map(|_| anchor)
3728 })
3729 .collect::<Vec<_>>()
3730 };
3731 buffer.edit(
3732 old_selections
3733 .iter()
3734 .map(|s| (s.start..s.end, text.clone())),
3735 autoindent_mode,
3736 cx,
3737 );
3738 anchors
3739 });
3740
3741 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3742 s.select_anchors(selection_anchors);
3743 })
3744 });
3745 }
3746
3747 fn trigger_completion_on_input(
3748 &mut self,
3749 text: &str,
3750 trigger_in_words: bool,
3751 cx: &mut ViewContext<Self>,
3752 ) {
3753 if self.is_completion_trigger(text, trigger_in_words, cx) {
3754 self.show_completions(
3755 &ShowCompletions {
3756 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3757 },
3758 cx,
3759 );
3760 } else {
3761 self.hide_context_menu(cx);
3762 }
3763 }
3764
3765 fn is_completion_trigger(
3766 &self,
3767 text: &str,
3768 trigger_in_words: bool,
3769 cx: &mut ViewContext<Self>,
3770 ) -> bool {
3771 let position = self.selections.newest_anchor().head();
3772 let multibuffer = self.buffer.read(cx);
3773 let Some(buffer) = position
3774 .buffer_id
3775 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3776 else {
3777 return false;
3778 };
3779
3780 if let Some(completion_provider) = &self.completion_provider {
3781 completion_provider.is_completion_trigger(
3782 &buffer,
3783 position.text_anchor,
3784 text,
3785 trigger_in_words,
3786 cx,
3787 )
3788 } else {
3789 false
3790 }
3791 }
3792
3793 /// If any empty selections is touching the start of its innermost containing autoclose
3794 /// region, expand it to select the brackets.
3795 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
3796 let selections = self.selections.all::<usize>(cx);
3797 let buffer = self.buffer.read(cx).read(cx);
3798 let new_selections = self
3799 .selections_with_autoclose_regions(selections, &buffer)
3800 .map(|(mut selection, region)| {
3801 if !selection.is_empty() {
3802 return selection;
3803 }
3804
3805 if let Some(region) = region {
3806 let mut range = region.range.to_offset(&buffer);
3807 if selection.start == range.start && range.start >= region.pair.start.len() {
3808 range.start -= region.pair.start.len();
3809 if buffer.contains_str_at(range.start, ®ion.pair.start)
3810 && buffer.contains_str_at(range.end, ®ion.pair.end)
3811 {
3812 range.end += region.pair.end.len();
3813 selection.start = range.start;
3814 selection.end = range.end;
3815
3816 return selection;
3817 }
3818 }
3819 }
3820
3821 let always_treat_brackets_as_autoclosed = buffer
3822 .settings_at(selection.start, cx)
3823 .always_treat_brackets_as_autoclosed;
3824
3825 if !always_treat_brackets_as_autoclosed {
3826 return selection;
3827 }
3828
3829 if let Some(scope) = buffer.language_scope_at(selection.start) {
3830 for (pair, enabled) in scope.brackets() {
3831 if !enabled || !pair.close {
3832 continue;
3833 }
3834
3835 if buffer.contains_str_at(selection.start, &pair.end) {
3836 let pair_start_len = pair.start.len();
3837 if buffer.contains_str_at(selection.start - pair_start_len, &pair.start)
3838 {
3839 selection.start -= pair_start_len;
3840 selection.end += pair.end.len();
3841
3842 return selection;
3843 }
3844 }
3845 }
3846 }
3847
3848 selection
3849 })
3850 .collect();
3851
3852 drop(buffer);
3853 self.change_selections(None, cx, |selections| selections.select(new_selections));
3854 }
3855
3856 /// Iterate the given selections, and for each one, find the smallest surrounding
3857 /// autoclose region. This uses the ordering of the selections and the autoclose
3858 /// regions to avoid repeated comparisons.
3859 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3860 &'a self,
3861 selections: impl IntoIterator<Item = Selection<D>>,
3862 buffer: &'a MultiBufferSnapshot,
3863 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3864 let mut i = 0;
3865 let mut regions = self.autoclose_regions.as_slice();
3866 selections.into_iter().map(move |selection| {
3867 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3868
3869 let mut enclosing = None;
3870 while let Some(pair_state) = regions.get(i) {
3871 if pair_state.range.end.to_offset(buffer) < range.start {
3872 regions = ®ions[i + 1..];
3873 i = 0;
3874 } else if pair_state.range.start.to_offset(buffer) > range.end {
3875 break;
3876 } else {
3877 if pair_state.selection_id == selection.id {
3878 enclosing = Some(pair_state);
3879 }
3880 i += 1;
3881 }
3882 }
3883
3884 (selection.clone(), enclosing)
3885 })
3886 }
3887
3888 /// Remove any autoclose regions that no longer contain their selection.
3889 fn invalidate_autoclose_regions(
3890 &mut self,
3891 mut selections: &[Selection<Anchor>],
3892 buffer: &MultiBufferSnapshot,
3893 ) {
3894 self.autoclose_regions.retain(|state| {
3895 let mut i = 0;
3896 while let Some(selection) = selections.get(i) {
3897 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3898 selections = &selections[1..];
3899 continue;
3900 }
3901 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3902 break;
3903 }
3904 if selection.id == state.selection_id {
3905 return true;
3906 } else {
3907 i += 1;
3908 }
3909 }
3910 false
3911 });
3912 }
3913
3914 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3915 let offset = position.to_offset(buffer);
3916 let (word_range, kind) = buffer.surrounding_word(offset);
3917 if offset > word_range.start && kind == Some(CharKind::Word) {
3918 Some(
3919 buffer
3920 .text_for_range(word_range.start..offset)
3921 .collect::<String>(),
3922 )
3923 } else {
3924 None
3925 }
3926 }
3927
3928 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
3929 self.refresh_inlay_hints(
3930 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
3931 cx,
3932 );
3933 }
3934
3935 pub fn inlay_hints_enabled(&self) -> bool {
3936 self.inlay_hint_cache.enabled
3937 }
3938
3939 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
3940 if self.project.is_none() || self.mode != EditorMode::Full {
3941 return;
3942 }
3943
3944 let reason_description = reason.description();
3945 let ignore_debounce = matches!(
3946 reason,
3947 InlayHintRefreshReason::SettingsChange(_)
3948 | InlayHintRefreshReason::Toggle(_)
3949 | InlayHintRefreshReason::ExcerptsRemoved(_)
3950 );
3951 let (invalidate_cache, required_languages) = match reason {
3952 InlayHintRefreshReason::Toggle(enabled) => {
3953 self.inlay_hint_cache.enabled = enabled;
3954 if enabled {
3955 (InvalidationStrategy::RefreshRequested, None)
3956 } else {
3957 self.inlay_hint_cache.clear();
3958 self.splice_inlays(
3959 self.visible_inlay_hints(cx)
3960 .iter()
3961 .map(|inlay| inlay.id)
3962 .collect(),
3963 Vec::new(),
3964 cx,
3965 );
3966 return;
3967 }
3968 }
3969 InlayHintRefreshReason::SettingsChange(new_settings) => {
3970 match self.inlay_hint_cache.update_settings(
3971 &self.buffer,
3972 new_settings,
3973 self.visible_inlay_hints(cx),
3974 cx,
3975 ) {
3976 ControlFlow::Break(Some(InlaySplice {
3977 to_remove,
3978 to_insert,
3979 })) => {
3980 self.splice_inlays(to_remove, to_insert, cx);
3981 return;
3982 }
3983 ControlFlow::Break(None) => return,
3984 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3985 }
3986 }
3987 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3988 if let Some(InlaySplice {
3989 to_remove,
3990 to_insert,
3991 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3992 {
3993 self.splice_inlays(to_remove, to_insert, cx);
3994 }
3995 return;
3996 }
3997 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3998 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3999 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
4000 }
4001 InlayHintRefreshReason::RefreshRequested => {
4002 (InvalidationStrategy::RefreshRequested, None)
4003 }
4004 };
4005
4006 if let Some(InlaySplice {
4007 to_remove,
4008 to_insert,
4009 }) = self.inlay_hint_cache.spawn_hint_refresh(
4010 reason_description,
4011 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
4012 invalidate_cache,
4013 ignore_debounce,
4014 cx,
4015 ) {
4016 self.splice_inlays(to_remove, to_insert, cx);
4017 }
4018 }
4019
4020 fn visible_inlay_hints(&self, cx: &ViewContext<'_, Editor>) -> Vec<Inlay> {
4021 self.display_map
4022 .read(cx)
4023 .current_inlays()
4024 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
4025 .cloned()
4026 .collect()
4027 }
4028
4029 pub fn excerpts_for_inlay_hints_query(
4030 &self,
4031 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
4032 cx: &mut ViewContext<Editor>,
4033 ) -> HashMap<ExcerptId, (Model<Buffer>, clock::Global, Range<usize>)> {
4034 let Some(project) = self.project.as_ref() else {
4035 return HashMap::default();
4036 };
4037 let project = project.read(cx);
4038 let multi_buffer = self.buffer().read(cx);
4039 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
4040 let multi_buffer_visible_start = self
4041 .scroll_manager
4042 .anchor()
4043 .anchor
4044 .to_point(&multi_buffer_snapshot);
4045 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
4046 multi_buffer_visible_start
4047 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
4048 Bias::Left,
4049 );
4050 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
4051 multi_buffer
4052 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
4053 .into_iter()
4054 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
4055 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
4056 let buffer = buffer_handle.read(cx);
4057 let buffer_file = project::File::from_dyn(buffer.file())?;
4058 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
4059 let worktree_entry = buffer_worktree
4060 .read(cx)
4061 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
4062 if worktree_entry.is_ignored {
4063 return None;
4064 }
4065
4066 let language = buffer.language()?;
4067 if let Some(restrict_to_languages) = restrict_to_languages {
4068 if !restrict_to_languages.contains(language) {
4069 return None;
4070 }
4071 }
4072 Some((
4073 excerpt_id,
4074 (
4075 buffer_handle,
4076 buffer.version().clone(),
4077 excerpt_visible_range,
4078 ),
4079 ))
4080 })
4081 .collect()
4082 }
4083
4084 pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
4085 TextLayoutDetails {
4086 text_system: cx.text_system().clone(),
4087 editor_style: self.style.clone().unwrap(),
4088 rem_size: cx.rem_size(),
4089 scroll_anchor: self.scroll_manager.anchor(),
4090 visible_rows: self.visible_line_count(),
4091 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
4092 }
4093 }
4094
4095 fn splice_inlays(
4096 &self,
4097 to_remove: Vec<InlayId>,
4098 to_insert: Vec<Inlay>,
4099 cx: &mut ViewContext<Self>,
4100 ) {
4101 self.display_map.update(cx, |display_map, cx| {
4102 display_map.splice_inlays(to_remove, to_insert, cx);
4103 });
4104 cx.notify();
4105 }
4106
4107 fn trigger_on_type_formatting(
4108 &self,
4109 input: String,
4110 cx: &mut ViewContext<Self>,
4111 ) -> Option<Task<Result<()>>> {
4112 if input.len() != 1 {
4113 return None;
4114 }
4115
4116 let project = self.project.as_ref()?;
4117 let position = self.selections.newest_anchor().head();
4118 let (buffer, buffer_position) = self
4119 .buffer
4120 .read(cx)
4121 .text_anchor_for_position(position, cx)?;
4122
4123 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
4124 // hence we do LSP request & edit on host side only — add formats to host's history.
4125 let push_to_lsp_host_history = true;
4126 // If this is not the host, append its history with new edits.
4127 let push_to_client_history = project.read(cx).is_via_collab();
4128
4129 let on_type_formatting = project.update(cx, |project, cx| {
4130 project.on_type_format(
4131 buffer.clone(),
4132 buffer_position,
4133 input,
4134 push_to_lsp_host_history,
4135 cx,
4136 )
4137 });
4138 Some(cx.spawn(|editor, mut cx| async move {
4139 if let Some(transaction) = on_type_formatting.await? {
4140 if push_to_client_history {
4141 buffer
4142 .update(&mut cx, |buffer, _| {
4143 buffer.push_transaction(transaction, Instant::now());
4144 })
4145 .ok();
4146 }
4147 editor.update(&mut cx, |editor, cx| {
4148 editor.refresh_document_highlights(cx);
4149 })?;
4150 }
4151 Ok(())
4152 }))
4153 }
4154
4155 pub fn show_completions(&mut self, options: &ShowCompletions, cx: &mut ViewContext<Self>) {
4156 if self.pending_rename.is_some() {
4157 return;
4158 }
4159
4160 let Some(provider) = self.completion_provider.as_ref() else {
4161 return;
4162 };
4163
4164 let position = self.selections.newest_anchor().head();
4165 let (buffer, buffer_position) =
4166 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
4167 output
4168 } else {
4169 return;
4170 };
4171
4172 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
4173 let is_followup_invoke = {
4174 let context_menu_state = self.context_menu.read();
4175 matches!(
4176 context_menu_state.deref(),
4177 Some(ContextMenu::Completions(_))
4178 )
4179 };
4180 let trigger_kind = match (&options.trigger, is_followup_invoke) {
4181 (_, true) => CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS,
4182 (Some(trigger), _) if buffer.read(cx).completion_triggers().contains(&trigger) => {
4183 CompletionTriggerKind::TRIGGER_CHARACTER
4184 }
4185
4186 _ => CompletionTriggerKind::INVOKED,
4187 };
4188 let completion_context = CompletionContext {
4189 trigger_character: options.trigger.as_ref().and_then(|trigger| {
4190 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
4191 Some(String::from(trigger))
4192 } else {
4193 None
4194 }
4195 }),
4196 trigger_kind,
4197 };
4198 let completions = provider.completions(&buffer, buffer_position, completion_context, cx);
4199 let sort_completions = provider.sort_completions();
4200
4201 let id = post_inc(&mut self.next_completion_id);
4202 let task = cx.spawn(|this, mut cx| {
4203 async move {
4204 this.update(&mut cx, |this, _| {
4205 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
4206 })?;
4207 let completions = completions.await.log_err();
4208 let menu = if let Some(completions) = completions {
4209 let mut menu = CompletionsMenu {
4210 id,
4211 sort_completions,
4212 initial_position: position,
4213 match_candidates: completions
4214 .iter()
4215 .enumerate()
4216 .map(|(id, completion)| {
4217 StringMatchCandidate::new(
4218 id,
4219 completion.label.text[completion.label.filter_range.clone()]
4220 .into(),
4221 )
4222 })
4223 .collect(),
4224 buffer: buffer.clone(),
4225 completions: Arc::new(RwLock::new(completions.into())),
4226 matches: Vec::new().into(),
4227 selected_item: 0,
4228 scroll_handle: UniformListScrollHandle::new(),
4229 selected_completion_documentation_resolve_debounce: Arc::new(Mutex::new(
4230 DebouncedDelay::new(),
4231 )),
4232 };
4233 menu.filter(query.as_deref(), cx.background_executor().clone())
4234 .await;
4235
4236 if menu.matches.is_empty() {
4237 None
4238 } else {
4239 this.update(&mut cx, |editor, cx| {
4240 let completions = menu.completions.clone();
4241 let matches = menu.matches.clone();
4242
4243 let delay_ms = EditorSettings::get_global(cx)
4244 .completion_documentation_secondary_query_debounce;
4245 let delay = Duration::from_millis(delay_ms);
4246 editor
4247 .completion_documentation_pre_resolve_debounce
4248 .fire_new(delay, cx, |editor, cx| {
4249 CompletionsMenu::pre_resolve_completion_documentation(
4250 buffer,
4251 completions,
4252 matches,
4253 editor,
4254 cx,
4255 )
4256 });
4257 })
4258 .ok();
4259 Some(menu)
4260 }
4261 } else {
4262 None
4263 };
4264
4265 this.update(&mut cx, |this, cx| {
4266 let mut context_menu = this.context_menu.write();
4267 match context_menu.as_ref() {
4268 None => {}
4269
4270 Some(ContextMenu::Completions(prev_menu)) => {
4271 if prev_menu.id > id {
4272 return;
4273 }
4274 }
4275
4276 _ => return,
4277 }
4278
4279 if this.focus_handle.is_focused(cx) && menu.is_some() {
4280 let menu = menu.unwrap();
4281 *context_menu = Some(ContextMenu::Completions(menu));
4282 drop(context_menu);
4283 this.discard_inline_completion(false, cx);
4284 cx.notify();
4285 } else if this.completion_tasks.len() <= 1 {
4286 // If there are no more completion tasks and the last menu was
4287 // empty, we should hide it. If it was already hidden, we should
4288 // also show the copilot completion when available.
4289 drop(context_menu);
4290 if this.hide_context_menu(cx).is_none() {
4291 this.update_visible_inline_completion(cx);
4292 }
4293 }
4294 })?;
4295
4296 Ok::<_, anyhow::Error>(())
4297 }
4298 .log_err()
4299 });
4300
4301 self.completion_tasks.push((id, task));
4302 }
4303
4304 pub fn confirm_completion(
4305 &mut self,
4306 action: &ConfirmCompletion,
4307 cx: &mut ViewContext<Self>,
4308 ) -> Option<Task<Result<()>>> {
4309 self.do_completion(action.item_ix, CompletionIntent::Complete, cx)
4310 }
4311
4312 pub fn compose_completion(
4313 &mut self,
4314 action: &ComposeCompletion,
4315 cx: &mut ViewContext<Self>,
4316 ) -> Option<Task<Result<()>>> {
4317 self.do_completion(action.item_ix, CompletionIntent::Compose, cx)
4318 }
4319
4320 fn do_completion(
4321 &mut self,
4322 item_ix: Option<usize>,
4323 intent: CompletionIntent,
4324 cx: &mut ViewContext<Editor>,
4325 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
4326 use language::ToOffset as _;
4327
4328 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
4329 menu
4330 } else {
4331 return None;
4332 };
4333
4334 let mat = completions_menu
4335 .matches
4336 .get(item_ix.unwrap_or(completions_menu.selected_item))?;
4337 let buffer_handle = completions_menu.buffer;
4338 let completions = completions_menu.completions.read();
4339 let completion = completions.get(mat.candidate_id)?;
4340 cx.stop_propagation();
4341
4342 let snippet;
4343 let text;
4344
4345 if completion.is_snippet() {
4346 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4347 text = snippet.as_ref().unwrap().text.clone();
4348 } else {
4349 snippet = None;
4350 text = completion.new_text.clone();
4351 };
4352 let selections = self.selections.all::<usize>(cx);
4353 let buffer = buffer_handle.read(cx);
4354 let old_range = completion.old_range.to_offset(buffer);
4355 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4356
4357 let newest_selection = self.selections.newest_anchor();
4358 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4359 return None;
4360 }
4361
4362 let lookbehind = newest_selection
4363 .start
4364 .text_anchor
4365 .to_offset(buffer)
4366 .saturating_sub(old_range.start);
4367 let lookahead = old_range
4368 .end
4369 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4370 let mut common_prefix_len = old_text
4371 .bytes()
4372 .zip(text.bytes())
4373 .take_while(|(a, b)| a == b)
4374 .count();
4375
4376 let snapshot = self.buffer.read(cx).snapshot(cx);
4377 let mut range_to_replace: Option<Range<isize>> = None;
4378 let mut ranges = Vec::new();
4379 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4380 for selection in &selections {
4381 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4382 let start = selection.start.saturating_sub(lookbehind);
4383 let end = selection.end + lookahead;
4384 if selection.id == newest_selection.id {
4385 range_to_replace = Some(
4386 ((start + common_prefix_len) as isize - selection.start as isize)
4387 ..(end as isize - selection.start as isize),
4388 );
4389 }
4390 ranges.push(start + common_prefix_len..end);
4391 } else {
4392 common_prefix_len = 0;
4393 ranges.clear();
4394 ranges.extend(selections.iter().map(|s| {
4395 if s.id == newest_selection.id {
4396 range_to_replace = Some(
4397 old_range.start.to_offset_utf16(&snapshot).0 as isize
4398 - selection.start as isize
4399 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4400 - selection.start as isize,
4401 );
4402 old_range.clone()
4403 } else {
4404 s.start..s.end
4405 }
4406 }));
4407 break;
4408 }
4409 if !self.linked_edit_ranges.is_empty() {
4410 let start_anchor = snapshot.anchor_before(selection.head());
4411 let end_anchor = snapshot.anchor_after(selection.tail());
4412 if let Some(ranges) = self
4413 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4414 {
4415 for (buffer, edits) in ranges {
4416 linked_edits.entry(buffer.clone()).or_default().extend(
4417 edits
4418 .into_iter()
4419 .map(|range| (range, text[common_prefix_len..].to_owned())),
4420 );
4421 }
4422 }
4423 }
4424 }
4425 let text = &text[common_prefix_len..];
4426
4427 cx.emit(EditorEvent::InputHandled {
4428 utf16_range_to_replace: range_to_replace,
4429 text: text.into(),
4430 });
4431
4432 self.transact(cx, |this, cx| {
4433 if let Some(mut snippet) = snippet {
4434 snippet.text = text.to_string();
4435 for tabstop in snippet.tabstops.iter_mut().flatten() {
4436 tabstop.start -= common_prefix_len as isize;
4437 tabstop.end -= common_prefix_len as isize;
4438 }
4439
4440 this.insert_snippet(&ranges, snippet, cx).log_err();
4441 } else {
4442 this.buffer.update(cx, |buffer, cx| {
4443 buffer.edit(
4444 ranges.iter().map(|range| (range.clone(), text)),
4445 this.autoindent_mode.clone(),
4446 cx,
4447 );
4448 });
4449 }
4450 for (buffer, edits) in linked_edits {
4451 buffer.update(cx, |buffer, cx| {
4452 let snapshot = buffer.snapshot();
4453 let edits = edits
4454 .into_iter()
4455 .map(|(range, text)| {
4456 use text::ToPoint as TP;
4457 let end_point = TP::to_point(&range.end, &snapshot);
4458 let start_point = TP::to_point(&range.start, &snapshot);
4459 (start_point..end_point, text)
4460 })
4461 .sorted_by_key(|(range, _)| range.start)
4462 .collect::<Vec<_>>();
4463 buffer.edit(edits, None, cx);
4464 })
4465 }
4466
4467 this.refresh_inline_completion(true, false, cx);
4468 });
4469
4470 let show_new_completions_on_confirm = completion
4471 .confirm
4472 .as_ref()
4473 .map_or(false, |confirm| confirm(intent, cx));
4474 if show_new_completions_on_confirm {
4475 self.show_completions(&ShowCompletions { trigger: None }, cx);
4476 }
4477
4478 let provider = self.completion_provider.as_ref()?;
4479 let apply_edits = provider.apply_additional_edits_for_completion(
4480 buffer_handle,
4481 completion.clone(),
4482 true,
4483 cx,
4484 );
4485
4486 let editor_settings = EditorSettings::get_global(cx);
4487 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4488 // After the code completion is finished, users often want to know what signatures are needed.
4489 // so we should automatically call signature_help
4490 self.show_signature_help(&ShowSignatureHelp, cx);
4491 }
4492
4493 Some(cx.foreground_executor().spawn(async move {
4494 apply_edits.await?;
4495 Ok(())
4496 }))
4497 }
4498
4499 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
4500 let mut context_menu = self.context_menu.write();
4501 if let Some(ContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4502 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4503 // Toggle if we're selecting the same one
4504 *context_menu = None;
4505 cx.notify();
4506 return;
4507 } else {
4508 // Otherwise, clear it and start a new one
4509 *context_menu = None;
4510 cx.notify();
4511 }
4512 }
4513 drop(context_menu);
4514 let snapshot = self.snapshot(cx);
4515 let deployed_from_indicator = action.deployed_from_indicator;
4516 let mut task = self.code_actions_task.take();
4517 let action = action.clone();
4518 cx.spawn(|editor, mut cx| async move {
4519 while let Some(prev_task) = task {
4520 prev_task.await;
4521 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4522 }
4523
4524 let spawned_test_task = editor.update(&mut cx, |editor, cx| {
4525 if editor.focus_handle.is_focused(cx) {
4526 let multibuffer_point = action
4527 .deployed_from_indicator
4528 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4529 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4530 let (buffer, buffer_row) = snapshot
4531 .buffer_snapshot
4532 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4533 .and_then(|(buffer_snapshot, range)| {
4534 editor
4535 .buffer
4536 .read(cx)
4537 .buffer(buffer_snapshot.remote_id())
4538 .map(|buffer| (buffer, range.start.row))
4539 })?;
4540 let (_, code_actions) = editor
4541 .available_code_actions
4542 .clone()
4543 .and_then(|(location, code_actions)| {
4544 let snapshot = location.buffer.read(cx).snapshot();
4545 let point_range = location.range.to_point(&snapshot);
4546 let point_range = point_range.start.row..=point_range.end.row;
4547 if point_range.contains(&buffer_row) {
4548 Some((location, code_actions))
4549 } else {
4550 None
4551 }
4552 })
4553 .unzip();
4554 let buffer_id = buffer.read(cx).remote_id();
4555 let tasks = editor
4556 .tasks
4557 .get(&(buffer_id, buffer_row))
4558 .map(|t| Arc::new(t.to_owned()));
4559 if tasks.is_none() && code_actions.is_none() {
4560 return None;
4561 }
4562
4563 editor.completion_tasks.clear();
4564 editor.discard_inline_completion(false, cx);
4565 let task_context =
4566 tasks
4567 .as_ref()
4568 .zip(editor.project.clone())
4569 .map(|(tasks, project)| {
4570 let position = Point::new(buffer_row, tasks.column);
4571 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
4572 let location = Location {
4573 buffer: buffer.clone(),
4574 range: range_start..range_start,
4575 };
4576 // Fill in the environmental variables from the tree-sitter captures
4577 let mut captured_task_variables = TaskVariables::default();
4578 for (capture_name, value) in tasks.extra_variables.clone() {
4579 captured_task_variables.insert(
4580 task::VariableName::Custom(capture_name.into()),
4581 value.clone(),
4582 );
4583 }
4584 project.update(cx, |project, cx| {
4585 project.task_context_for_location(
4586 captured_task_variables,
4587 location,
4588 cx,
4589 )
4590 })
4591 });
4592
4593 Some(cx.spawn(|editor, mut cx| async move {
4594 let task_context = match task_context {
4595 Some(task_context) => task_context.await,
4596 None => None,
4597 };
4598 let resolved_tasks =
4599 tasks.zip(task_context).map(|(tasks, task_context)| {
4600 Arc::new(ResolvedTasks {
4601 templates: tasks
4602 .templates
4603 .iter()
4604 .filter_map(|(kind, template)| {
4605 template
4606 .resolve_task(&kind.to_id_base(), &task_context)
4607 .map(|task| (kind.clone(), task))
4608 })
4609 .collect(),
4610 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4611 multibuffer_point.row,
4612 tasks.column,
4613 )),
4614 })
4615 });
4616 let spawn_straight_away = resolved_tasks
4617 .as_ref()
4618 .map_or(false, |tasks| tasks.templates.len() == 1)
4619 && code_actions
4620 .as_ref()
4621 .map_or(true, |actions| actions.is_empty());
4622 if let Some(task) = editor
4623 .update(&mut cx, |editor, cx| {
4624 *editor.context_menu.write() =
4625 Some(ContextMenu::CodeActions(CodeActionsMenu {
4626 buffer,
4627 actions: CodeActionContents {
4628 tasks: resolved_tasks,
4629 actions: code_actions,
4630 },
4631 selected_item: Default::default(),
4632 scroll_handle: UniformListScrollHandle::default(),
4633 deployed_from_indicator,
4634 }));
4635 if spawn_straight_away {
4636 if let Some(task) = editor.confirm_code_action(
4637 &ConfirmCodeAction { item_ix: Some(0) },
4638 cx,
4639 ) {
4640 cx.notify();
4641 return task;
4642 }
4643 }
4644 cx.notify();
4645 Task::ready(Ok(()))
4646 })
4647 .ok()
4648 {
4649 task.await
4650 } else {
4651 Ok(())
4652 }
4653 }))
4654 } else {
4655 Some(Task::ready(Ok(())))
4656 }
4657 })?;
4658 if let Some(task) = spawned_test_task {
4659 task.await?;
4660 }
4661
4662 Ok::<_, anyhow::Error>(())
4663 })
4664 .detach_and_log_err(cx);
4665 }
4666
4667 pub fn confirm_code_action(
4668 &mut self,
4669 action: &ConfirmCodeAction,
4670 cx: &mut ViewContext<Self>,
4671 ) -> Option<Task<Result<()>>> {
4672 let actions_menu = if let ContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
4673 menu
4674 } else {
4675 return None;
4676 };
4677 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4678 let action = actions_menu.actions.get(action_ix)?;
4679 let title = action.label();
4680 let buffer = actions_menu.buffer;
4681 let workspace = self.workspace()?;
4682
4683 match action {
4684 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4685 workspace.update(cx, |workspace, cx| {
4686 workspace::tasks::schedule_resolved_task(
4687 workspace,
4688 task_source_kind,
4689 resolved_task,
4690 false,
4691 cx,
4692 );
4693
4694 Some(Task::ready(Ok(())))
4695 })
4696 }
4697 CodeActionsItem::CodeAction(action) => {
4698 let apply_code_actions = workspace
4699 .read(cx)
4700 .project()
4701 .clone()
4702 .update(cx, |project, cx| {
4703 project.apply_code_action(buffer, action, true, cx)
4704 });
4705 let workspace = workspace.downgrade();
4706 Some(cx.spawn(|editor, cx| async move {
4707 let project_transaction = apply_code_actions.await?;
4708 Self::open_project_transaction(
4709 &editor,
4710 workspace,
4711 project_transaction,
4712 title,
4713 cx,
4714 )
4715 .await
4716 }))
4717 }
4718 }
4719 }
4720
4721 pub async fn open_project_transaction(
4722 this: &WeakView<Editor>,
4723 workspace: WeakView<Workspace>,
4724 transaction: ProjectTransaction,
4725 title: String,
4726 mut cx: AsyncWindowContext,
4727 ) -> Result<()> {
4728 let replica_id = this.update(&mut cx, |this, cx| this.replica_id(cx))?;
4729
4730 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4731 cx.update(|cx| {
4732 entries.sort_unstable_by_key(|(buffer, _)| {
4733 buffer.read(cx).file().map(|f| f.path().clone())
4734 });
4735 })?;
4736
4737 // If the project transaction's edits are all contained within this editor, then
4738 // avoid opening a new editor to display them.
4739
4740 if let Some((buffer, transaction)) = entries.first() {
4741 if entries.len() == 1 {
4742 let excerpt = this.update(&mut cx, |editor, cx| {
4743 editor
4744 .buffer()
4745 .read(cx)
4746 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4747 })?;
4748 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4749 if excerpted_buffer == *buffer {
4750 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4751 let excerpt_range = excerpt_range.to_offset(buffer);
4752 buffer
4753 .edited_ranges_for_transaction::<usize>(transaction)
4754 .all(|range| {
4755 excerpt_range.start <= range.start
4756 && excerpt_range.end >= range.end
4757 })
4758 })?;
4759
4760 if all_edits_within_excerpt {
4761 return Ok(());
4762 }
4763 }
4764 }
4765 }
4766 } else {
4767 return Ok(());
4768 }
4769
4770 let mut ranges_to_highlight = Vec::new();
4771 let excerpt_buffer = cx.new_model(|cx| {
4772 let mut multibuffer =
4773 MultiBuffer::new(replica_id, Capability::ReadWrite).with_title(title);
4774 for (buffer_handle, transaction) in &entries {
4775 let buffer = buffer_handle.read(cx);
4776 ranges_to_highlight.extend(
4777 multibuffer.push_excerpts_with_context_lines(
4778 buffer_handle.clone(),
4779 buffer
4780 .edited_ranges_for_transaction::<usize>(transaction)
4781 .collect(),
4782 DEFAULT_MULTIBUFFER_CONTEXT,
4783 cx,
4784 ),
4785 );
4786 }
4787 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4788 multibuffer
4789 })?;
4790
4791 workspace.update(&mut cx, |workspace, cx| {
4792 let project = workspace.project().clone();
4793 let editor =
4794 cx.new_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, cx));
4795 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
4796 editor.update(cx, |editor, cx| {
4797 editor.highlight_background::<Self>(
4798 &ranges_to_highlight,
4799 |theme| theme.editor_highlighted_line_background,
4800 cx,
4801 );
4802 });
4803 })?;
4804
4805 Ok(())
4806 }
4807
4808 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4809 let project = self.project.clone()?;
4810 let buffer = self.buffer.read(cx);
4811 let newest_selection = self.selections.newest_anchor().clone();
4812 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4813 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4814 if start_buffer != end_buffer {
4815 return None;
4816 }
4817
4818 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
4819 cx.background_executor()
4820 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4821 .await;
4822
4823 let actions = if let Ok(code_actions) = project.update(&mut cx, |project, cx| {
4824 project.code_actions(&start_buffer, start..end, cx)
4825 }) {
4826 code_actions.await
4827 } else {
4828 Vec::new()
4829 };
4830
4831 this.update(&mut cx, |this, cx| {
4832 this.available_code_actions = if actions.is_empty() {
4833 None
4834 } else {
4835 Some((
4836 Location {
4837 buffer: start_buffer,
4838 range: start..end,
4839 },
4840 actions.into(),
4841 ))
4842 };
4843 cx.notify();
4844 })
4845 .log_err();
4846 }));
4847 None
4848 }
4849
4850 fn start_inline_blame_timer(&mut self, cx: &mut ViewContext<Self>) {
4851 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4852 self.show_git_blame_inline = false;
4853
4854 self.show_git_blame_inline_delay_task = Some(cx.spawn(|this, mut cx| async move {
4855 cx.background_executor().timer(delay).await;
4856
4857 this.update(&mut cx, |this, cx| {
4858 this.show_git_blame_inline = true;
4859 cx.notify();
4860 })
4861 .log_err();
4862 }));
4863 }
4864 }
4865
4866 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4867 if self.pending_rename.is_some() {
4868 return None;
4869 }
4870
4871 let project = self.project.clone()?;
4872 let buffer = self.buffer.read(cx);
4873 let newest_selection = self.selections.newest_anchor().clone();
4874 let cursor_position = newest_selection.head();
4875 let (cursor_buffer, cursor_buffer_position) =
4876 buffer.text_anchor_for_position(cursor_position, cx)?;
4877 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4878 if cursor_buffer != tail_buffer {
4879 return None;
4880 }
4881
4882 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4883 cx.background_executor()
4884 .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT)
4885 .await;
4886
4887 let highlights = if let Some(highlights) = project
4888 .update(&mut cx, |project, cx| {
4889 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4890 })
4891 .log_err()
4892 {
4893 highlights.await.log_err()
4894 } else {
4895 None
4896 };
4897
4898 if let Some(highlights) = highlights {
4899 this.update(&mut cx, |this, cx| {
4900 if this.pending_rename.is_some() {
4901 return;
4902 }
4903
4904 let buffer_id = cursor_position.buffer_id;
4905 let buffer = this.buffer.read(cx);
4906 if !buffer
4907 .text_anchor_for_position(cursor_position, cx)
4908 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4909 {
4910 return;
4911 }
4912
4913 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4914 let mut write_ranges = Vec::new();
4915 let mut read_ranges = Vec::new();
4916 for highlight in highlights {
4917 for (excerpt_id, excerpt_range) in
4918 buffer.excerpts_for_buffer(&cursor_buffer, cx)
4919 {
4920 let start = highlight
4921 .range
4922 .start
4923 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4924 let end = highlight
4925 .range
4926 .end
4927 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4928 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4929 continue;
4930 }
4931
4932 let range = Anchor {
4933 buffer_id,
4934 excerpt_id,
4935 text_anchor: start,
4936 }..Anchor {
4937 buffer_id,
4938 excerpt_id,
4939 text_anchor: end,
4940 };
4941 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4942 write_ranges.push(range);
4943 } else {
4944 read_ranges.push(range);
4945 }
4946 }
4947 }
4948
4949 this.highlight_background::<DocumentHighlightRead>(
4950 &read_ranges,
4951 |theme| theme.editor_document_highlight_read_background,
4952 cx,
4953 );
4954 this.highlight_background::<DocumentHighlightWrite>(
4955 &write_ranges,
4956 |theme| theme.editor_document_highlight_write_background,
4957 cx,
4958 );
4959 cx.notify();
4960 })
4961 .log_err();
4962 }
4963 }));
4964 None
4965 }
4966
4967 pub fn refresh_inline_completion(
4968 &mut self,
4969 debounce: bool,
4970 user_requested: bool,
4971 cx: &mut ViewContext<Self>,
4972 ) -> Option<()> {
4973 let provider = self.inline_completion_provider()?;
4974 let cursor = self.selections.newest_anchor().head();
4975 let (buffer, cursor_buffer_position) =
4976 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4977 if !user_requested
4978 && !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
4979 {
4980 self.discard_inline_completion(false, cx);
4981 return None;
4982 }
4983
4984 self.update_visible_inline_completion(cx);
4985 provider.refresh(buffer, cursor_buffer_position, debounce, cx);
4986 Some(())
4987 }
4988
4989 fn cycle_inline_completion(
4990 &mut self,
4991 direction: Direction,
4992 cx: &mut ViewContext<Self>,
4993 ) -> Option<()> {
4994 let provider = self.inline_completion_provider()?;
4995 let cursor = self.selections.newest_anchor().head();
4996 let (buffer, cursor_buffer_position) =
4997 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4998 if !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx) {
4999 return None;
5000 }
5001
5002 provider.cycle(buffer, cursor_buffer_position, direction, cx);
5003 self.update_visible_inline_completion(cx);
5004
5005 Some(())
5006 }
5007
5008 pub fn show_inline_completion(&mut self, _: &ShowInlineCompletion, cx: &mut ViewContext<Self>) {
5009 if !self.has_active_inline_completion(cx) {
5010 self.refresh_inline_completion(false, true, cx);
5011 return;
5012 }
5013
5014 self.update_visible_inline_completion(cx);
5015 }
5016
5017 pub fn display_cursor_names(&mut self, _: &DisplayCursorNames, cx: &mut ViewContext<Self>) {
5018 self.show_cursor_names(cx);
5019 }
5020
5021 fn show_cursor_names(&mut self, cx: &mut ViewContext<Self>) {
5022 self.show_cursor_names = true;
5023 cx.notify();
5024 cx.spawn(|this, mut cx| async move {
5025 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5026 this.update(&mut cx, |this, cx| {
5027 this.show_cursor_names = false;
5028 cx.notify()
5029 })
5030 .ok()
5031 })
5032 .detach();
5033 }
5034
5035 pub fn next_inline_completion(&mut self, _: &NextInlineCompletion, cx: &mut ViewContext<Self>) {
5036 if self.has_active_inline_completion(cx) {
5037 self.cycle_inline_completion(Direction::Next, cx);
5038 } else {
5039 let is_copilot_disabled = self.refresh_inline_completion(false, true, cx).is_none();
5040 if is_copilot_disabled {
5041 cx.propagate();
5042 }
5043 }
5044 }
5045
5046 pub fn previous_inline_completion(
5047 &mut self,
5048 _: &PreviousInlineCompletion,
5049 cx: &mut ViewContext<Self>,
5050 ) {
5051 if self.has_active_inline_completion(cx) {
5052 self.cycle_inline_completion(Direction::Prev, cx);
5053 } else {
5054 let is_copilot_disabled = self.refresh_inline_completion(false, true, cx).is_none();
5055 if is_copilot_disabled {
5056 cx.propagate();
5057 }
5058 }
5059 }
5060
5061 pub fn accept_inline_completion(
5062 &mut self,
5063 _: &AcceptInlineCompletion,
5064 cx: &mut ViewContext<Self>,
5065 ) {
5066 let Some((completion, delete_range)) = self.take_active_inline_completion(cx) else {
5067 return;
5068 };
5069 if let Some(provider) = self.inline_completion_provider() {
5070 provider.accept(cx);
5071 }
5072
5073 cx.emit(EditorEvent::InputHandled {
5074 utf16_range_to_replace: None,
5075 text: completion.text.to_string().into(),
5076 });
5077
5078 if let Some(range) = delete_range {
5079 self.change_selections(None, cx, |s| s.select_ranges([range]))
5080 }
5081 self.insert_with_autoindent_mode(&completion.text.to_string(), None, cx);
5082 self.refresh_inline_completion(true, true, cx);
5083 cx.notify();
5084 }
5085
5086 pub fn accept_partial_inline_completion(
5087 &mut self,
5088 _: &AcceptPartialInlineCompletion,
5089 cx: &mut ViewContext<Self>,
5090 ) {
5091 if self.selections.count() == 1 && self.has_active_inline_completion(cx) {
5092 if let Some((completion, delete_range)) = self.take_active_inline_completion(cx) {
5093 let mut partial_completion = completion
5094 .text
5095 .chars()
5096 .by_ref()
5097 .take_while(|c| c.is_alphabetic())
5098 .collect::<String>();
5099 if partial_completion.is_empty() {
5100 partial_completion = completion
5101 .text
5102 .chars()
5103 .by_ref()
5104 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5105 .collect::<String>();
5106 }
5107
5108 cx.emit(EditorEvent::InputHandled {
5109 utf16_range_to_replace: None,
5110 text: partial_completion.clone().into(),
5111 });
5112
5113 if let Some(range) = delete_range {
5114 self.change_selections(None, cx, |s| s.select_ranges([range]))
5115 }
5116 self.insert_with_autoindent_mode(&partial_completion, None, cx);
5117
5118 self.refresh_inline_completion(true, true, cx);
5119 cx.notify();
5120 }
5121 }
5122 }
5123
5124 fn discard_inline_completion(
5125 &mut self,
5126 should_report_inline_completion_event: bool,
5127 cx: &mut ViewContext<Self>,
5128 ) -> bool {
5129 if let Some(provider) = self.inline_completion_provider() {
5130 provider.discard(should_report_inline_completion_event, cx);
5131 }
5132
5133 self.take_active_inline_completion(cx).is_some()
5134 }
5135
5136 pub fn has_active_inline_completion(&self, cx: &AppContext) -> bool {
5137 if let Some(completion) = self.active_inline_completion.as_ref() {
5138 let buffer = self.buffer.read(cx).read(cx);
5139 completion.0.position.is_valid(&buffer)
5140 } else {
5141 false
5142 }
5143 }
5144
5145 fn take_active_inline_completion(
5146 &mut self,
5147 cx: &mut ViewContext<Self>,
5148 ) -> Option<(Inlay, Option<Range<Anchor>>)> {
5149 let completion = self.active_inline_completion.take()?;
5150 self.display_map.update(cx, |map, cx| {
5151 map.splice_inlays(vec![completion.0.id], Default::default(), cx);
5152 });
5153 let buffer = self.buffer.read(cx).read(cx);
5154
5155 if completion.0.position.is_valid(&buffer) {
5156 Some(completion)
5157 } else {
5158 None
5159 }
5160 }
5161
5162 fn update_visible_inline_completion(&mut self, cx: &mut ViewContext<Self>) {
5163 let selection = self.selections.newest_anchor();
5164 let cursor = selection.head();
5165
5166 let excerpt_id = cursor.excerpt_id;
5167
5168 if self.context_menu.read().is_none()
5169 && self.completion_tasks.is_empty()
5170 && selection.start == selection.end
5171 {
5172 if let Some(provider) = self.inline_completion_provider() {
5173 if let Some((buffer, cursor_buffer_position)) =
5174 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5175 {
5176 if let Some((text, text_anchor_range)) =
5177 provider.active_completion_text(&buffer, cursor_buffer_position, cx)
5178 {
5179 let text = Rope::from(text);
5180 let mut to_remove = Vec::new();
5181 if let Some(completion) = self.active_inline_completion.take() {
5182 to_remove.push(completion.0.id);
5183 }
5184
5185 let completion_inlay =
5186 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
5187
5188 let multibuffer_anchor_range = text_anchor_range.and_then(|range| {
5189 let snapshot = self.buffer.read(cx).snapshot(cx);
5190 Some(
5191 snapshot.anchor_in_excerpt(excerpt_id, range.start)?
5192 ..snapshot.anchor_in_excerpt(excerpt_id, range.end)?,
5193 )
5194 });
5195 self.active_inline_completion =
5196 Some((completion_inlay.clone(), multibuffer_anchor_range));
5197
5198 self.display_map.update(cx, move |map, cx| {
5199 map.splice_inlays(to_remove, vec![completion_inlay], cx)
5200 });
5201 cx.notify();
5202 return;
5203 }
5204 }
5205 }
5206 }
5207
5208 self.discard_inline_completion(false, cx);
5209 }
5210
5211 fn inline_completion_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5212 Some(self.inline_completion_provider.as_ref()?.provider.clone())
5213 }
5214
5215 fn render_code_actions_indicator(
5216 &self,
5217 _style: &EditorStyle,
5218 row: DisplayRow,
5219 is_active: bool,
5220 cx: &mut ViewContext<Self>,
5221 ) -> Option<IconButton> {
5222 if self.available_code_actions.is_some() {
5223 Some(
5224 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5225 .shape(ui::IconButtonShape::Square)
5226 .icon_size(IconSize::XSmall)
5227 .icon_color(Color::Muted)
5228 .selected(is_active)
5229 .on_click(cx.listener(move |editor, _e, cx| {
5230 editor.focus(cx);
5231 editor.toggle_code_actions(
5232 &ToggleCodeActions {
5233 deployed_from_indicator: Some(row),
5234 },
5235 cx,
5236 );
5237 })),
5238 )
5239 } else {
5240 None
5241 }
5242 }
5243
5244 fn clear_tasks(&mut self) {
5245 self.tasks.clear()
5246 }
5247
5248 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5249 if let Some(_) = self.tasks.insert(key, value) {
5250 // This case should hopefully be rare, but just in case...
5251 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5252 }
5253 }
5254
5255 fn render_run_indicator(
5256 &self,
5257 _style: &EditorStyle,
5258 is_active: bool,
5259 row: DisplayRow,
5260 cx: &mut ViewContext<Self>,
5261 ) -> IconButton {
5262 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5263 .shape(ui::IconButtonShape::Square)
5264 .icon_size(IconSize::XSmall)
5265 .icon_color(Color::Muted)
5266 .selected(is_active)
5267 .on_click(cx.listener(move |editor, _e, cx| {
5268 editor.focus(cx);
5269 editor.toggle_code_actions(
5270 &ToggleCodeActions {
5271 deployed_from_indicator: Some(row),
5272 },
5273 cx,
5274 );
5275 }))
5276 }
5277
5278 fn close_hunk_diff_button(
5279 &self,
5280 hunk: HoveredHunk,
5281 row: DisplayRow,
5282 cx: &mut ViewContext<Self>,
5283 ) -> IconButton {
5284 IconButton::new(
5285 ("close_hunk_diff_indicator", row.0 as usize),
5286 ui::IconName::Close,
5287 )
5288 .shape(ui::IconButtonShape::Square)
5289 .icon_size(IconSize::XSmall)
5290 .icon_color(Color::Muted)
5291 .tooltip(|cx| Tooltip::for_action("Close hunk diff", &ToggleHunkDiff, cx))
5292 .on_click(cx.listener(move |editor, _e, cx| editor.toggle_hovered_hunk(&hunk, cx)))
5293 }
5294
5295 pub fn context_menu_visible(&self) -> bool {
5296 self.context_menu
5297 .read()
5298 .as_ref()
5299 .map_or(false, |menu| menu.visible())
5300 }
5301
5302 fn render_context_menu(
5303 &self,
5304 cursor_position: DisplayPoint,
5305 style: &EditorStyle,
5306 max_height: Pixels,
5307 cx: &mut ViewContext<Editor>,
5308 ) -> Option<(ContextMenuOrigin, AnyElement)> {
5309 self.context_menu.read().as_ref().map(|menu| {
5310 menu.render(
5311 cursor_position,
5312 style,
5313 max_height,
5314 self.workspace.as_ref().map(|(w, _)| w.clone()),
5315 cx,
5316 )
5317 })
5318 }
5319
5320 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
5321 cx.notify();
5322 self.completion_tasks.clear();
5323 let context_menu = self.context_menu.write().take();
5324 if context_menu.is_some() {
5325 self.update_visible_inline_completion(cx);
5326 }
5327 context_menu
5328 }
5329
5330 pub fn insert_snippet(
5331 &mut self,
5332 insertion_ranges: &[Range<usize>],
5333 snippet: Snippet,
5334 cx: &mut ViewContext<Self>,
5335 ) -> Result<()> {
5336 struct Tabstop<T> {
5337 is_end_tabstop: bool,
5338 ranges: Vec<Range<T>>,
5339 }
5340
5341 let tabstops = self.buffer.update(cx, |buffer, cx| {
5342 let snippet_text: Arc<str> = snippet.text.clone().into();
5343 buffer.edit(
5344 insertion_ranges
5345 .iter()
5346 .cloned()
5347 .map(|range| (range, snippet_text.clone())),
5348 Some(AutoindentMode::EachLine),
5349 cx,
5350 );
5351
5352 let snapshot = &*buffer.read(cx);
5353 let snippet = &snippet;
5354 snippet
5355 .tabstops
5356 .iter()
5357 .map(|tabstop| {
5358 let is_end_tabstop = tabstop.first().map_or(false, |tabstop| {
5359 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
5360 });
5361 let mut tabstop_ranges = tabstop
5362 .iter()
5363 .flat_map(|tabstop_range| {
5364 let mut delta = 0_isize;
5365 insertion_ranges.iter().map(move |insertion_range| {
5366 let insertion_start = insertion_range.start as isize + delta;
5367 delta +=
5368 snippet.text.len() as isize - insertion_range.len() as isize;
5369
5370 let start = ((insertion_start + tabstop_range.start) as usize)
5371 .min(snapshot.len());
5372 let end = ((insertion_start + tabstop_range.end) as usize)
5373 .min(snapshot.len());
5374 snapshot.anchor_before(start)..snapshot.anchor_after(end)
5375 })
5376 })
5377 .collect::<Vec<_>>();
5378 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
5379
5380 Tabstop {
5381 is_end_tabstop,
5382 ranges: tabstop_ranges,
5383 }
5384 })
5385 .collect::<Vec<_>>()
5386 });
5387 if let Some(tabstop) = tabstops.first() {
5388 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5389 s.select_ranges(tabstop.ranges.iter().cloned());
5390 });
5391
5392 // If we're already at the last tabstop and it's at the end of the snippet,
5393 // we're done, we don't need to keep the state around.
5394 if !tabstop.is_end_tabstop {
5395 let ranges = tabstops
5396 .into_iter()
5397 .map(|tabstop| tabstop.ranges)
5398 .collect::<Vec<_>>();
5399 self.snippet_stack.push(SnippetState {
5400 active_index: 0,
5401 ranges,
5402 });
5403 }
5404
5405 // Check whether the just-entered snippet ends with an auto-closable bracket.
5406 if self.autoclose_regions.is_empty() {
5407 let snapshot = self.buffer.read(cx).snapshot(cx);
5408 for selection in &mut self.selections.all::<Point>(cx) {
5409 let selection_head = selection.head();
5410 let Some(scope) = snapshot.language_scope_at(selection_head) else {
5411 continue;
5412 };
5413
5414 let mut bracket_pair = None;
5415 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
5416 let prev_chars = snapshot
5417 .reversed_chars_at(selection_head)
5418 .collect::<String>();
5419 for (pair, enabled) in scope.brackets() {
5420 if enabled
5421 && pair.close
5422 && prev_chars.starts_with(pair.start.as_str())
5423 && next_chars.starts_with(pair.end.as_str())
5424 {
5425 bracket_pair = Some(pair.clone());
5426 break;
5427 }
5428 }
5429 if let Some(pair) = bracket_pair {
5430 let start = snapshot.anchor_after(selection_head);
5431 let end = snapshot.anchor_after(selection_head);
5432 self.autoclose_regions.push(AutocloseRegion {
5433 selection_id: selection.id,
5434 range: start..end,
5435 pair,
5436 });
5437 }
5438 }
5439 }
5440 }
5441 Ok(())
5442 }
5443
5444 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5445 self.move_to_snippet_tabstop(Bias::Right, cx)
5446 }
5447
5448 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5449 self.move_to_snippet_tabstop(Bias::Left, cx)
5450 }
5451
5452 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
5453 if let Some(mut snippet) = self.snippet_stack.pop() {
5454 match bias {
5455 Bias::Left => {
5456 if snippet.active_index > 0 {
5457 snippet.active_index -= 1;
5458 } else {
5459 self.snippet_stack.push(snippet);
5460 return false;
5461 }
5462 }
5463 Bias::Right => {
5464 if snippet.active_index + 1 < snippet.ranges.len() {
5465 snippet.active_index += 1;
5466 } else {
5467 self.snippet_stack.push(snippet);
5468 return false;
5469 }
5470 }
5471 }
5472 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
5473 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5474 s.select_anchor_ranges(current_ranges.iter().cloned())
5475 });
5476 // If snippet state is not at the last tabstop, push it back on the stack
5477 if snippet.active_index + 1 < snippet.ranges.len() {
5478 self.snippet_stack.push(snippet);
5479 }
5480 return true;
5481 }
5482 }
5483
5484 false
5485 }
5486
5487 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
5488 self.transact(cx, |this, cx| {
5489 this.select_all(&SelectAll, cx);
5490 this.insert("", cx);
5491 });
5492 }
5493
5494 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
5495 self.transact(cx, |this, cx| {
5496 this.select_autoclose_pair(cx);
5497 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
5498 if !this.linked_edit_ranges.is_empty() {
5499 let selections = this.selections.all::<MultiBufferPoint>(cx);
5500 let snapshot = this.buffer.read(cx).snapshot(cx);
5501
5502 for selection in selections.iter() {
5503 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
5504 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
5505 if selection_start.buffer_id != selection_end.buffer_id {
5506 continue;
5507 }
5508 if let Some(ranges) =
5509 this.linked_editing_ranges_for(selection_start..selection_end, cx)
5510 {
5511 for (buffer, entries) in ranges {
5512 linked_ranges.entry(buffer).or_default().extend(entries);
5513 }
5514 }
5515 }
5516 }
5517
5518 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
5519 if !this.selections.line_mode {
5520 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
5521 for selection in &mut selections {
5522 if selection.is_empty() {
5523 let old_head = selection.head();
5524 let mut new_head =
5525 movement::left(&display_map, old_head.to_display_point(&display_map))
5526 .to_point(&display_map);
5527 if let Some((buffer, line_buffer_range)) = display_map
5528 .buffer_snapshot
5529 .buffer_line_for_row(MultiBufferRow(old_head.row))
5530 {
5531 let indent_size =
5532 buffer.indent_size_for_line(line_buffer_range.start.row);
5533 let indent_len = match indent_size.kind {
5534 IndentKind::Space => {
5535 buffer.settings_at(line_buffer_range.start, cx).tab_size
5536 }
5537 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
5538 };
5539 if old_head.column <= indent_size.len && old_head.column > 0 {
5540 let indent_len = indent_len.get();
5541 new_head = cmp::min(
5542 new_head,
5543 MultiBufferPoint::new(
5544 old_head.row,
5545 ((old_head.column - 1) / indent_len) * indent_len,
5546 ),
5547 );
5548 }
5549 }
5550
5551 selection.set_head(new_head, SelectionGoal::None);
5552 }
5553 }
5554 }
5555
5556 this.signature_help_state.set_backspace_pressed(true);
5557 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5558 this.insert("", cx);
5559 let empty_str: Arc<str> = Arc::from("");
5560 for (buffer, edits) in linked_ranges {
5561 let snapshot = buffer.read(cx).snapshot();
5562 use text::ToPoint as TP;
5563
5564 let edits = edits
5565 .into_iter()
5566 .map(|range| {
5567 let end_point = TP::to_point(&range.end, &snapshot);
5568 let mut start_point = TP::to_point(&range.start, &snapshot);
5569
5570 if end_point == start_point {
5571 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
5572 .saturating_sub(1);
5573 start_point = TP::to_point(&offset, &snapshot);
5574 };
5575
5576 (start_point..end_point, empty_str.clone())
5577 })
5578 .sorted_by_key(|(range, _)| range.start)
5579 .collect::<Vec<_>>();
5580 buffer.update(cx, |this, cx| {
5581 this.edit(edits, None, cx);
5582 })
5583 }
5584 this.refresh_inline_completion(true, false, cx);
5585 linked_editing_ranges::refresh_linked_ranges(this, cx);
5586 });
5587 }
5588
5589 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
5590 self.transact(cx, |this, cx| {
5591 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5592 let line_mode = s.line_mode;
5593 s.move_with(|map, selection| {
5594 if selection.is_empty() && !line_mode {
5595 let cursor = movement::right(map, selection.head());
5596 selection.end = cursor;
5597 selection.reversed = true;
5598 selection.goal = SelectionGoal::None;
5599 }
5600 })
5601 });
5602 this.insert("", cx);
5603 this.refresh_inline_completion(true, false, cx);
5604 });
5605 }
5606
5607 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
5608 if self.move_to_prev_snippet_tabstop(cx) {
5609 return;
5610 }
5611
5612 self.outdent(&Outdent, cx);
5613 }
5614
5615 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
5616 if self.move_to_next_snippet_tabstop(cx) || self.read_only(cx) {
5617 return;
5618 }
5619
5620 let mut selections = self.selections.all_adjusted(cx);
5621 let buffer = self.buffer.read(cx);
5622 let snapshot = buffer.snapshot(cx);
5623 let rows_iter = selections.iter().map(|s| s.head().row);
5624 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
5625
5626 let mut edits = Vec::new();
5627 let mut prev_edited_row = 0;
5628 let mut row_delta = 0;
5629 for selection in &mut selections {
5630 if selection.start.row != prev_edited_row {
5631 row_delta = 0;
5632 }
5633 prev_edited_row = selection.end.row;
5634
5635 // If the selection is non-empty, then increase the indentation of the selected lines.
5636 if !selection.is_empty() {
5637 row_delta =
5638 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5639 continue;
5640 }
5641
5642 // If the selection is empty and the cursor is in the leading whitespace before the
5643 // suggested indentation, then auto-indent the line.
5644 let cursor = selection.head();
5645 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
5646 if let Some(suggested_indent) =
5647 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
5648 {
5649 if cursor.column < suggested_indent.len
5650 && cursor.column <= current_indent.len
5651 && current_indent.len <= suggested_indent.len
5652 {
5653 selection.start = Point::new(cursor.row, suggested_indent.len);
5654 selection.end = selection.start;
5655 if row_delta == 0 {
5656 edits.extend(Buffer::edit_for_indent_size_adjustment(
5657 cursor.row,
5658 current_indent,
5659 suggested_indent,
5660 ));
5661 row_delta = suggested_indent.len - current_indent.len;
5662 }
5663 continue;
5664 }
5665 }
5666
5667 // Otherwise, insert a hard or soft tab.
5668 let settings = buffer.settings_at(cursor, cx);
5669 let tab_size = if settings.hard_tabs {
5670 IndentSize::tab()
5671 } else {
5672 let tab_size = settings.tab_size.get();
5673 let char_column = snapshot
5674 .text_for_range(Point::new(cursor.row, 0)..cursor)
5675 .flat_map(str::chars)
5676 .count()
5677 + row_delta as usize;
5678 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
5679 IndentSize::spaces(chars_to_next_tab_stop)
5680 };
5681 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
5682 selection.end = selection.start;
5683 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
5684 row_delta += tab_size.len;
5685 }
5686
5687 self.transact(cx, |this, cx| {
5688 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5689 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5690 this.refresh_inline_completion(true, false, cx);
5691 });
5692 }
5693
5694 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
5695 if self.read_only(cx) {
5696 return;
5697 }
5698 let mut selections = self.selections.all::<Point>(cx);
5699 let mut prev_edited_row = 0;
5700 let mut row_delta = 0;
5701 let mut edits = Vec::new();
5702 let buffer = self.buffer.read(cx);
5703 let snapshot = buffer.snapshot(cx);
5704 for selection in &mut selections {
5705 if selection.start.row != prev_edited_row {
5706 row_delta = 0;
5707 }
5708 prev_edited_row = selection.end.row;
5709
5710 row_delta =
5711 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5712 }
5713
5714 self.transact(cx, |this, cx| {
5715 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5716 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5717 });
5718 }
5719
5720 fn indent_selection(
5721 buffer: &MultiBuffer,
5722 snapshot: &MultiBufferSnapshot,
5723 selection: &mut Selection<Point>,
5724 edits: &mut Vec<(Range<Point>, String)>,
5725 delta_for_start_row: u32,
5726 cx: &AppContext,
5727 ) -> u32 {
5728 let settings = buffer.settings_at(selection.start, cx);
5729 let tab_size = settings.tab_size.get();
5730 let indent_kind = if settings.hard_tabs {
5731 IndentKind::Tab
5732 } else {
5733 IndentKind::Space
5734 };
5735 let mut start_row = selection.start.row;
5736 let mut end_row = selection.end.row + 1;
5737
5738 // If a selection ends at the beginning of a line, don't indent
5739 // that last line.
5740 if selection.end.column == 0 && selection.end.row > selection.start.row {
5741 end_row -= 1;
5742 }
5743
5744 // Avoid re-indenting a row that has already been indented by a
5745 // previous selection, but still update this selection's column
5746 // to reflect that indentation.
5747 if delta_for_start_row > 0 {
5748 start_row += 1;
5749 selection.start.column += delta_for_start_row;
5750 if selection.end.row == selection.start.row {
5751 selection.end.column += delta_for_start_row;
5752 }
5753 }
5754
5755 let mut delta_for_end_row = 0;
5756 let has_multiple_rows = start_row + 1 != end_row;
5757 for row in start_row..end_row {
5758 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
5759 let indent_delta = match (current_indent.kind, indent_kind) {
5760 (IndentKind::Space, IndentKind::Space) => {
5761 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
5762 IndentSize::spaces(columns_to_next_tab_stop)
5763 }
5764 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
5765 (_, IndentKind::Tab) => IndentSize::tab(),
5766 };
5767
5768 let start = if has_multiple_rows || current_indent.len < selection.start.column {
5769 0
5770 } else {
5771 selection.start.column
5772 };
5773 let row_start = Point::new(row, start);
5774 edits.push((
5775 row_start..row_start,
5776 indent_delta.chars().collect::<String>(),
5777 ));
5778
5779 // Update this selection's endpoints to reflect the indentation.
5780 if row == selection.start.row {
5781 selection.start.column += indent_delta.len;
5782 }
5783 if row == selection.end.row {
5784 selection.end.column += indent_delta.len;
5785 delta_for_end_row = indent_delta.len;
5786 }
5787 }
5788
5789 if selection.start.row == selection.end.row {
5790 delta_for_start_row + delta_for_end_row
5791 } else {
5792 delta_for_end_row
5793 }
5794 }
5795
5796 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
5797 if self.read_only(cx) {
5798 return;
5799 }
5800 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5801 let selections = self.selections.all::<Point>(cx);
5802 let mut deletion_ranges = Vec::new();
5803 let mut last_outdent = None;
5804 {
5805 let buffer = self.buffer.read(cx);
5806 let snapshot = buffer.snapshot(cx);
5807 for selection in &selections {
5808 let settings = buffer.settings_at(selection.start, cx);
5809 let tab_size = settings.tab_size.get();
5810 let mut rows = selection.spanned_rows(false, &display_map);
5811
5812 // Avoid re-outdenting a row that has already been outdented by a
5813 // previous selection.
5814 if let Some(last_row) = last_outdent {
5815 if last_row == rows.start {
5816 rows.start = rows.start.next_row();
5817 }
5818 }
5819 let has_multiple_rows = rows.len() > 1;
5820 for row in rows.iter_rows() {
5821 let indent_size = snapshot.indent_size_for_line(row);
5822 if indent_size.len > 0 {
5823 let deletion_len = match indent_size.kind {
5824 IndentKind::Space => {
5825 let columns_to_prev_tab_stop = indent_size.len % tab_size;
5826 if columns_to_prev_tab_stop == 0 {
5827 tab_size
5828 } else {
5829 columns_to_prev_tab_stop
5830 }
5831 }
5832 IndentKind::Tab => 1,
5833 };
5834 let start = if has_multiple_rows
5835 || deletion_len > selection.start.column
5836 || indent_size.len < selection.start.column
5837 {
5838 0
5839 } else {
5840 selection.start.column - deletion_len
5841 };
5842 deletion_ranges.push(
5843 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
5844 );
5845 last_outdent = Some(row);
5846 }
5847 }
5848 }
5849 }
5850
5851 self.transact(cx, |this, cx| {
5852 this.buffer.update(cx, |buffer, cx| {
5853 let empty_str: Arc<str> = Arc::default();
5854 buffer.edit(
5855 deletion_ranges
5856 .into_iter()
5857 .map(|range| (range, empty_str.clone())),
5858 None,
5859 cx,
5860 );
5861 });
5862 let selections = this.selections.all::<usize>(cx);
5863 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5864 });
5865 }
5866
5867 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
5868 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5869 let selections = self.selections.all::<Point>(cx);
5870
5871 let mut new_cursors = Vec::new();
5872 let mut edit_ranges = Vec::new();
5873 let mut selections = selections.iter().peekable();
5874 while let Some(selection) = selections.next() {
5875 let mut rows = selection.spanned_rows(false, &display_map);
5876 let goal_display_column = selection.head().to_display_point(&display_map).column();
5877
5878 // Accumulate contiguous regions of rows that we want to delete.
5879 while let Some(next_selection) = selections.peek() {
5880 let next_rows = next_selection.spanned_rows(false, &display_map);
5881 if next_rows.start <= rows.end {
5882 rows.end = next_rows.end;
5883 selections.next().unwrap();
5884 } else {
5885 break;
5886 }
5887 }
5888
5889 let buffer = &display_map.buffer_snapshot;
5890 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
5891 let edit_end;
5892 let cursor_buffer_row;
5893 if buffer.max_point().row >= rows.end.0 {
5894 // If there's a line after the range, delete the \n from the end of the row range
5895 // and position the cursor on the next line.
5896 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
5897 cursor_buffer_row = rows.end;
5898 } else {
5899 // If there isn't a line after the range, delete the \n from the line before the
5900 // start of the row range and position the cursor there.
5901 edit_start = edit_start.saturating_sub(1);
5902 edit_end = buffer.len();
5903 cursor_buffer_row = rows.start.previous_row();
5904 }
5905
5906 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
5907 *cursor.column_mut() =
5908 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
5909
5910 new_cursors.push((
5911 selection.id,
5912 buffer.anchor_after(cursor.to_point(&display_map)),
5913 ));
5914 edit_ranges.push(edit_start..edit_end);
5915 }
5916
5917 self.transact(cx, |this, cx| {
5918 let buffer = this.buffer.update(cx, |buffer, cx| {
5919 let empty_str: Arc<str> = Arc::default();
5920 buffer.edit(
5921 edit_ranges
5922 .into_iter()
5923 .map(|range| (range, empty_str.clone())),
5924 None,
5925 cx,
5926 );
5927 buffer.snapshot(cx)
5928 });
5929 let new_selections = new_cursors
5930 .into_iter()
5931 .map(|(id, cursor)| {
5932 let cursor = cursor.to_point(&buffer);
5933 Selection {
5934 id,
5935 start: cursor,
5936 end: cursor,
5937 reversed: false,
5938 goal: SelectionGoal::None,
5939 }
5940 })
5941 .collect();
5942
5943 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5944 s.select(new_selections);
5945 });
5946 });
5947 }
5948
5949 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
5950 if self.read_only(cx) {
5951 return;
5952 }
5953 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
5954 for selection in self.selections.all::<Point>(cx) {
5955 let start = MultiBufferRow(selection.start.row);
5956 let end = if selection.start.row == selection.end.row {
5957 MultiBufferRow(selection.start.row + 1)
5958 } else {
5959 MultiBufferRow(selection.end.row)
5960 };
5961
5962 if let Some(last_row_range) = row_ranges.last_mut() {
5963 if start <= last_row_range.end {
5964 last_row_range.end = end;
5965 continue;
5966 }
5967 }
5968 row_ranges.push(start..end);
5969 }
5970
5971 let snapshot = self.buffer.read(cx).snapshot(cx);
5972 let mut cursor_positions = Vec::new();
5973 for row_range in &row_ranges {
5974 let anchor = snapshot.anchor_before(Point::new(
5975 row_range.end.previous_row().0,
5976 snapshot.line_len(row_range.end.previous_row()),
5977 ));
5978 cursor_positions.push(anchor..anchor);
5979 }
5980
5981 self.transact(cx, |this, cx| {
5982 for row_range in row_ranges.into_iter().rev() {
5983 for row in row_range.iter_rows().rev() {
5984 let end_of_line = Point::new(row.0, snapshot.line_len(row));
5985 let next_line_row = row.next_row();
5986 let indent = snapshot.indent_size_for_line(next_line_row);
5987 let start_of_next_line = Point::new(next_line_row.0, indent.len);
5988
5989 let replace = if snapshot.line_len(next_line_row) > indent.len {
5990 " "
5991 } else {
5992 ""
5993 };
5994
5995 this.buffer.update(cx, |buffer, cx| {
5996 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
5997 });
5998 }
5999 }
6000
6001 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6002 s.select_anchor_ranges(cursor_positions)
6003 });
6004 });
6005 }
6006
6007 pub fn sort_lines_case_sensitive(
6008 &mut self,
6009 _: &SortLinesCaseSensitive,
6010 cx: &mut ViewContext<Self>,
6011 ) {
6012 self.manipulate_lines(cx, |lines| lines.sort())
6013 }
6014
6015 pub fn sort_lines_case_insensitive(
6016 &mut self,
6017 _: &SortLinesCaseInsensitive,
6018 cx: &mut ViewContext<Self>,
6019 ) {
6020 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
6021 }
6022
6023 pub fn unique_lines_case_insensitive(
6024 &mut self,
6025 _: &UniqueLinesCaseInsensitive,
6026 cx: &mut ViewContext<Self>,
6027 ) {
6028 self.manipulate_lines(cx, |lines| {
6029 let mut seen = HashSet::default();
6030 lines.retain(|line| seen.insert(line.to_lowercase()));
6031 })
6032 }
6033
6034 pub fn unique_lines_case_sensitive(
6035 &mut self,
6036 _: &UniqueLinesCaseSensitive,
6037 cx: &mut ViewContext<Self>,
6038 ) {
6039 self.manipulate_lines(cx, |lines| {
6040 let mut seen = HashSet::default();
6041 lines.retain(|line| seen.insert(*line));
6042 })
6043 }
6044
6045 pub fn revert_file(&mut self, _: &RevertFile, cx: &mut ViewContext<Self>) {
6046 let mut revert_changes = HashMap::default();
6047 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
6048 for hunk in hunks_for_rows(
6049 Some(MultiBufferRow(0)..multi_buffer_snapshot.max_buffer_row()).into_iter(),
6050 &multi_buffer_snapshot,
6051 ) {
6052 Self::prepare_revert_change(&mut revert_changes, &self.buffer(), &hunk, cx);
6053 }
6054 if !revert_changes.is_empty() {
6055 self.transact(cx, |editor, cx| {
6056 editor.revert(revert_changes, cx);
6057 });
6058 }
6059 }
6060
6061 pub fn revert_selected_hunks(&mut self, _: &RevertSelectedHunks, cx: &mut ViewContext<Self>) {
6062 let revert_changes = self.gather_revert_changes(&self.selections.disjoint_anchors(), cx);
6063 if !revert_changes.is_empty() {
6064 self.transact(cx, |editor, cx| {
6065 editor.revert(revert_changes, cx);
6066 });
6067 }
6068 }
6069
6070 pub fn open_active_item_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
6071 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
6072 let project_path = buffer.read(cx).project_path(cx)?;
6073 let project = self.project.as_ref()?.read(cx);
6074 let entry = project.entry_for_path(&project_path, cx)?;
6075 let abs_path = project.absolute_path(&project_path, cx)?;
6076 let parent = if entry.is_symlink {
6077 abs_path.canonicalize().ok()?
6078 } else {
6079 abs_path
6080 }
6081 .parent()?
6082 .to_path_buf();
6083 Some(parent)
6084 }) {
6085 cx.dispatch_action(OpenTerminal { working_directory }.boxed_clone());
6086 }
6087 }
6088
6089 fn gather_revert_changes(
6090 &mut self,
6091 selections: &[Selection<Anchor>],
6092 cx: &mut ViewContext<'_, Editor>,
6093 ) -> HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>> {
6094 let mut revert_changes = HashMap::default();
6095 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
6096 for hunk in hunks_for_selections(&multi_buffer_snapshot, selections) {
6097 Self::prepare_revert_change(&mut revert_changes, self.buffer(), &hunk, cx);
6098 }
6099 revert_changes
6100 }
6101
6102 pub fn prepare_revert_change(
6103 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
6104 multi_buffer: &Model<MultiBuffer>,
6105 hunk: &DiffHunk<MultiBufferRow>,
6106 cx: &AppContext,
6107 ) -> Option<()> {
6108 let buffer = multi_buffer.read(cx).buffer(hunk.buffer_id)?;
6109 let buffer = buffer.read(cx);
6110 let original_text = buffer.diff_base()?.slice(hunk.diff_base_byte_range.clone());
6111 let buffer_snapshot = buffer.snapshot();
6112 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
6113 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
6114 probe
6115 .0
6116 .start
6117 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
6118 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
6119 }) {
6120 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
6121 Some(())
6122 } else {
6123 None
6124 }
6125 }
6126
6127 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
6128 self.manipulate_lines(cx, |lines| lines.reverse())
6129 }
6130
6131 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
6132 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
6133 }
6134
6135 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6136 where
6137 Fn: FnMut(&mut Vec<&str>),
6138 {
6139 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6140 let buffer = self.buffer.read(cx).snapshot(cx);
6141
6142 let mut edits = Vec::new();
6143
6144 let selections = self.selections.all::<Point>(cx);
6145 let mut selections = selections.iter().peekable();
6146 let mut contiguous_row_selections = Vec::new();
6147 let mut new_selections = Vec::new();
6148 let mut added_lines = 0;
6149 let mut removed_lines = 0;
6150
6151 while let Some(selection) = selections.next() {
6152 let (start_row, end_row) = consume_contiguous_rows(
6153 &mut contiguous_row_selections,
6154 selection,
6155 &display_map,
6156 &mut selections,
6157 );
6158
6159 let start_point = Point::new(start_row.0, 0);
6160 let end_point = Point::new(
6161 end_row.previous_row().0,
6162 buffer.line_len(end_row.previous_row()),
6163 );
6164 let text = buffer
6165 .text_for_range(start_point..end_point)
6166 .collect::<String>();
6167
6168 let mut lines = text.split('\n').collect_vec();
6169
6170 let lines_before = lines.len();
6171 callback(&mut lines);
6172 let lines_after = lines.len();
6173
6174 edits.push((start_point..end_point, lines.join("\n")));
6175
6176 // Selections must change based on added and removed line count
6177 let start_row =
6178 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
6179 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
6180 new_selections.push(Selection {
6181 id: selection.id,
6182 start: start_row,
6183 end: end_row,
6184 goal: SelectionGoal::None,
6185 reversed: selection.reversed,
6186 });
6187
6188 if lines_after > lines_before {
6189 added_lines += lines_after - lines_before;
6190 } else if lines_before > lines_after {
6191 removed_lines += lines_before - lines_after;
6192 }
6193 }
6194
6195 self.transact(cx, |this, cx| {
6196 let buffer = this.buffer.update(cx, |buffer, cx| {
6197 buffer.edit(edits, None, cx);
6198 buffer.snapshot(cx)
6199 });
6200
6201 // Recalculate offsets on newly edited buffer
6202 let new_selections = new_selections
6203 .iter()
6204 .map(|s| {
6205 let start_point = Point::new(s.start.0, 0);
6206 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
6207 Selection {
6208 id: s.id,
6209 start: buffer.point_to_offset(start_point),
6210 end: buffer.point_to_offset(end_point),
6211 goal: s.goal,
6212 reversed: s.reversed,
6213 }
6214 })
6215 .collect();
6216
6217 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6218 s.select(new_selections);
6219 });
6220
6221 this.request_autoscroll(Autoscroll::fit(), cx);
6222 });
6223 }
6224
6225 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
6226 self.manipulate_text(cx, |text| text.to_uppercase())
6227 }
6228
6229 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
6230 self.manipulate_text(cx, |text| text.to_lowercase())
6231 }
6232
6233 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
6234 self.manipulate_text(cx, |text| {
6235 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6236 // https://github.com/rutrum/convert-case/issues/16
6237 text.split('\n')
6238 .map(|line| line.to_case(Case::Title))
6239 .join("\n")
6240 })
6241 }
6242
6243 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
6244 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
6245 }
6246
6247 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
6248 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
6249 }
6250
6251 pub fn convert_to_upper_camel_case(
6252 &mut self,
6253 _: &ConvertToUpperCamelCase,
6254 cx: &mut ViewContext<Self>,
6255 ) {
6256 self.manipulate_text(cx, |text| {
6257 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6258 // https://github.com/rutrum/convert-case/issues/16
6259 text.split('\n')
6260 .map(|line| line.to_case(Case::UpperCamel))
6261 .join("\n")
6262 })
6263 }
6264
6265 pub fn convert_to_lower_camel_case(
6266 &mut self,
6267 _: &ConvertToLowerCamelCase,
6268 cx: &mut ViewContext<Self>,
6269 ) {
6270 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
6271 }
6272
6273 pub fn convert_to_opposite_case(
6274 &mut self,
6275 _: &ConvertToOppositeCase,
6276 cx: &mut ViewContext<Self>,
6277 ) {
6278 self.manipulate_text(cx, |text| {
6279 text.chars()
6280 .fold(String::with_capacity(text.len()), |mut t, c| {
6281 if c.is_uppercase() {
6282 t.extend(c.to_lowercase());
6283 } else {
6284 t.extend(c.to_uppercase());
6285 }
6286 t
6287 })
6288 })
6289 }
6290
6291 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6292 where
6293 Fn: FnMut(&str) -> String,
6294 {
6295 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6296 let buffer = self.buffer.read(cx).snapshot(cx);
6297
6298 let mut new_selections = Vec::new();
6299 let mut edits = Vec::new();
6300 let mut selection_adjustment = 0i32;
6301
6302 for selection in self.selections.all::<usize>(cx) {
6303 let selection_is_empty = selection.is_empty();
6304
6305 let (start, end) = if selection_is_empty {
6306 let word_range = movement::surrounding_word(
6307 &display_map,
6308 selection.start.to_display_point(&display_map),
6309 );
6310 let start = word_range.start.to_offset(&display_map, Bias::Left);
6311 let end = word_range.end.to_offset(&display_map, Bias::Left);
6312 (start, end)
6313 } else {
6314 (selection.start, selection.end)
6315 };
6316
6317 let text = buffer.text_for_range(start..end).collect::<String>();
6318 let old_length = text.len() as i32;
6319 let text = callback(&text);
6320
6321 new_selections.push(Selection {
6322 start: (start as i32 - selection_adjustment) as usize,
6323 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
6324 goal: SelectionGoal::None,
6325 ..selection
6326 });
6327
6328 selection_adjustment += old_length - text.len() as i32;
6329
6330 edits.push((start..end, text));
6331 }
6332
6333 self.transact(cx, |this, cx| {
6334 this.buffer.update(cx, |buffer, cx| {
6335 buffer.edit(edits, None, cx);
6336 });
6337
6338 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6339 s.select(new_selections);
6340 });
6341
6342 this.request_autoscroll(Autoscroll::fit(), cx);
6343 });
6344 }
6345
6346 pub fn duplicate_line(&mut self, upwards: bool, cx: &mut ViewContext<Self>) {
6347 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6348 let buffer = &display_map.buffer_snapshot;
6349 let selections = self.selections.all::<Point>(cx);
6350
6351 let mut edits = Vec::new();
6352 let mut selections_iter = selections.iter().peekable();
6353 while let Some(selection) = selections_iter.next() {
6354 // Avoid duplicating the same lines twice.
6355 let mut rows = selection.spanned_rows(false, &display_map);
6356
6357 while let Some(next_selection) = selections_iter.peek() {
6358 let next_rows = next_selection.spanned_rows(false, &display_map);
6359 if next_rows.start < rows.end {
6360 rows.end = next_rows.end;
6361 selections_iter.next().unwrap();
6362 } else {
6363 break;
6364 }
6365 }
6366
6367 // Copy the text from the selected row region and splice it either at the start
6368 // or end of the region.
6369 let start = Point::new(rows.start.0, 0);
6370 let end = Point::new(
6371 rows.end.previous_row().0,
6372 buffer.line_len(rows.end.previous_row()),
6373 );
6374 let text = buffer
6375 .text_for_range(start..end)
6376 .chain(Some("\n"))
6377 .collect::<String>();
6378 let insert_location = if upwards {
6379 Point::new(rows.end.0, 0)
6380 } else {
6381 start
6382 };
6383 edits.push((insert_location..insert_location, text));
6384 }
6385
6386 self.transact(cx, |this, cx| {
6387 this.buffer.update(cx, |buffer, cx| {
6388 buffer.edit(edits, None, cx);
6389 });
6390
6391 this.request_autoscroll(Autoscroll::fit(), cx);
6392 });
6393 }
6394
6395 pub fn duplicate_line_up(&mut self, _: &DuplicateLineUp, cx: &mut ViewContext<Self>) {
6396 self.duplicate_line(true, cx);
6397 }
6398
6399 pub fn duplicate_line_down(&mut self, _: &DuplicateLineDown, cx: &mut ViewContext<Self>) {
6400 self.duplicate_line(false, cx);
6401 }
6402
6403 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
6404 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6405 let buffer = self.buffer.read(cx).snapshot(cx);
6406
6407 let mut edits = Vec::new();
6408 let mut unfold_ranges = Vec::new();
6409 let mut refold_ranges = Vec::new();
6410
6411 let selections = self.selections.all::<Point>(cx);
6412 let mut selections = selections.iter().peekable();
6413 let mut contiguous_row_selections = Vec::new();
6414 let mut new_selections = Vec::new();
6415
6416 while let Some(selection) = selections.next() {
6417 // Find all the selections that span a contiguous row range
6418 let (start_row, end_row) = consume_contiguous_rows(
6419 &mut contiguous_row_selections,
6420 selection,
6421 &display_map,
6422 &mut selections,
6423 );
6424
6425 // Move the text spanned by the row range to be before the line preceding the row range
6426 if start_row.0 > 0 {
6427 let range_to_move = Point::new(
6428 start_row.previous_row().0,
6429 buffer.line_len(start_row.previous_row()),
6430 )
6431 ..Point::new(
6432 end_row.previous_row().0,
6433 buffer.line_len(end_row.previous_row()),
6434 );
6435 let insertion_point = display_map
6436 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
6437 .0;
6438
6439 // Don't move lines across excerpts
6440 if buffer
6441 .excerpt_boundaries_in_range((
6442 Bound::Excluded(insertion_point),
6443 Bound::Included(range_to_move.end),
6444 ))
6445 .next()
6446 .is_none()
6447 {
6448 let text = buffer
6449 .text_for_range(range_to_move.clone())
6450 .flat_map(|s| s.chars())
6451 .skip(1)
6452 .chain(['\n'])
6453 .collect::<String>();
6454
6455 edits.push((
6456 buffer.anchor_after(range_to_move.start)
6457 ..buffer.anchor_before(range_to_move.end),
6458 String::new(),
6459 ));
6460 let insertion_anchor = buffer.anchor_after(insertion_point);
6461 edits.push((insertion_anchor..insertion_anchor, text));
6462
6463 let row_delta = range_to_move.start.row - insertion_point.row + 1;
6464
6465 // Move selections up
6466 new_selections.extend(contiguous_row_selections.drain(..).map(
6467 |mut selection| {
6468 selection.start.row -= row_delta;
6469 selection.end.row -= row_delta;
6470 selection
6471 },
6472 ));
6473
6474 // Move folds up
6475 unfold_ranges.push(range_to_move.clone());
6476 for fold in display_map.folds_in_range(
6477 buffer.anchor_before(range_to_move.start)
6478 ..buffer.anchor_after(range_to_move.end),
6479 ) {
6480 let mut start = fold.range.start.to_point(&buffer);
6481 let mut end = fold.range.end.to_point(&buffer);
6482 start.row -= row_delta;
6483 end.row -= row_delta;
6484 refold_ranges.push((start..end, fold.placeholder.clone()));
6485 }
6486 }
6487 }
6488
6489 // If we didn't move line(s), preserve the existing selections
6490 new_selections.append(&mut contiguous_row_selections);
6491 }
6492
6493 self.transact(cx, |this, cx| {
6494 this.unfold_ranges(unfold_ranges, true, true, cx);
6495 this.buffer.update(cx, |buffer, cx| {
6496 for (range, text) in edits {
6497 buffer.edit([(range, text)], None, cx);
6498 }
6499 });
6500 this.fold_ranges(refold_ranges, true, cx);
6501 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6502 s.select(new_selections);
6503 })
6504 });
6505 }
6506
6507 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
6508 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6509 let buffer = self.buffer.read(cx).snapshot(cx);
6510
6511 let mut edits = Vec::new();
6512 let mut unfold_ranges = Vec::new();
6513 let mut refold_ranges = Vec::new();
6514
6515 let selections = self.selections.all::<Point>(cx);
6516 let mut selections = selections.iter().peekable();
6517 let mut contiguous_row_selections = Vec::new();
6518 let mut new_selections = Vec::new();
6519
6520 while let Some(selection) = selections.next() {
6521 // Find all the selections that span a contiguous row range
6522 let (start_row, end_row) = consume_contiguous_rows(
6523 &mut contiguous_row_selections,
6524 selection,
6525 &display_map,
6526 &mut selections,
6527 );
6528
6529 // Move the text spanned by the row range to be after the last line of the row range
6530 if end_row.0 <= buffer.max_point().row {
6531 let range_to_move =
6532 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
6533 let insertion_point = display_map
6534 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
6535 .0;
6536
6537 // Don't move lines across excerpt boundaries
6538 if buffer
6539 .excerpt_boundaries_in_range((
6540 Bound::Excluded(range_to_move.start),
6541 Bound::Included(insertion_point),
6542 ))
6543 .next()
6544 .is_none()
6545 {
6546 let mut text = String::from("\n");
6547 text.extend(buffer.text_for_range(range_to_move.clone()));
6548 text.pop(); // Drop trailing newline
6549 edits.push((
6550 buffer.anchor_after(range_to_move.start)
6551 ..buffer.anchor_before(range_to_move.end),
6552 String::new(),
6553 ));
6554 let insertion_anchor = buffer.anchor_after(insertion_point);
6555 edits.push((insertion_anchor..insertion_anchor, text));
6556
6557 let row_delta = insertion_point.row - range_to_move.end.row + 1;
6558
6559 // Move selections down
6560 new_selections.extend(contiguous_row_selections.drain(..).map(
6561 |mut selection| {
6562 selection.start.row += row_delta;
6563 selection.end.row += row_delta;
6564 selection
6565 },
6566 ));
6567
6568 // Move folds down
6569 unfold_ranges.push(range_to_move.clone());
6570 for fold in display_map.folds_in_range(
6571 buffer.anchor_before(range_to_move.start)
6572 ..buffer.anchor_after(range_to_move.end),
6573 ) {
6574 let mut start = fold.range.start.to_point(&buffer);
6575 let mut end = fold.range.end.to_point(&buffer);
6576 start.row += row_delta;
6577 end.row += row_delta;
6578 refold_ranges.push((start..end, fold.placeholder.clone()));
6579 }
6580 }
6581 }
6582
6583 // If we didn't move line(s), preserve the existing selections
6584 new_selections.append(&mut contiguous_row_selections);
6585 }
6586
6587 self.transact(cx, |this, cx| {
6588 this.unfold_ranges(unfold_ranges, true, true, cx);
6589 this.buffer.update(cx, |buffer, cx| {
6590 for (range, text) in edits {
6591 buffer.edit([(range, text)], None, cx);
6592 }
6593 });
6594 this.fold_ranges(refold_ranges, true, cx);
6595 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
6596 });
6597 }
6598
6599 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
6600 let text_layout_details = &self.text_layout_details(cx);
6601 self.transact(cx, |this, cx| {
6602 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6603 let mut edits: Vec<(Range<usize>, String)> = Default::default();
6604 let line_mode = s.line_mode;
6605 s.move_with(|display_map, selection| {
6606 if !selection.is_empty() || line_mode {
6607 return;
6608 }
6609
6610 let mut head = selection.head();
6611 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
6612 if head.column() == display_map.line_len(head.row()) {
6613 transpose_offset = display_map
6614 .buffer_snapshot
6615 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6616 }
6617
6618 if transpose_offset == 0 {
6619 return;
6620 }
6621
6622 *head.column_mut() += 1;
6623 head = display_map.clip_point(head, Bias::Right);
6624 let goal = SelectionGoal::HorizontalPosition(
6625 display_map
6626 .x_for_display_point(head, &text_layout_details)
6627 .into(),
6628 );
6629 selection.collapse_to(head, goal);
6630
6631 let transpose_start = display_map
6632 .buffer_snapshot
6633 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6634 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
6635 let transpose_end = display_map
6636 .buffer_snapshot
6637 .clip_offset(transpose_offset + 1, Bias::Right);
6638 if let Some(ch) =
6639 display_map.buffer_snapshot.chars_at(transpose_start).next()
6640 {
6641 edits.push((transpose_start..transpose_offset, String::new()));
6642 edits.push((transpose_end..transpose_end, ch.to_string()));
6643 }
6644 }
6645 });
6646 edits
6647 });
6648 this.buffer
6649 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
6650 let selections = this.selections.all::<usize>(cx);
6651 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6652 s.select(selections);
6653 });
6654 });
6655 }
6656
6657 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
6658 let mut text = String::new();
6659 let buffer = self.buffer.read(cx).snapshot(cx);
6660 let mut selections = self.selections.all::<Point>(cx);
6661 let mut clipboard_selections = Vec::with_capacity(selections.len());
6662 {
6663 let max_point = buffer.max_point();
6664 let mut is_first = true;
6665 for selection in &mut selections {
6666 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6667 if is_entire_line {
6668 selection.start = Point::new(selection.start.row, 0);
6669 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
6670 selection.goal = SelectionGoal::None;
6671 }
6672 if is_first {
6673 is_first = false;
6674 } else {
6675 text += "\n";
6676 }
6677 let mut len = 0;
6678 for chunk in buffer.text_for_range(selection.start..selection.end) {
6679 text.push_str(chunk);
6680 len += chunk.len();
6681 }
6682 clipboard_selections.push(ClipboardSelection {
6683 len,
6684 is_entire_line,
6685 first_line_indent: buffer
6686 .indent_size_for_line(MultiBufferRow(selection.start.row))
6687 .len,
6688 });
6689 }
6690 }
6691
6692 self.transact(cx, |this, cx| {
6693 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6694 s.select(selections);
6695 });
6696 this.insert("", cx);
6697 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
6698 text,
6699 clipboard_selections,
6700 ));
6701 });
6702 }
6703
6704 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
6705 let selections = self.selections.all::<Point>(cx);
6706 let buffer = self.buffer.read(cx).read(cx);
6707 let mut text = String::new();
6708
6709 let mut clipboard_selections = Vec::with_capacity(selections.len());
6710 {
6711 let max_point = buffer.max_point();
6712 let mut is_first = true;
6713 for selection in selections.iter() {
6714 let mut start = selection.start;
6715 let mut end = selection.end;
6716 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6717 if is_entire_line {
6718 start = Point::new(start.row, 0);
6719 end = cmp::min(max_point, Point::new(end.row + 1, 0));
6720 }
6721 if is_first {
6722 is_first = false;
6723 } else {
6724 text += "\n";
6725 }
6726 let mut len = 0;
6727 for chunk in buffer.text_for_range(start..end) {
6728 text.push_str(chunk);
6729 len += chunk.len();
6730 }
6731 clipboard_selections.push(ClipboardSelection {
6732 len,
6733 is_entire_line,
6734 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
6735 });
6736 }
6737 }
6738
6739 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
6740 text,
6741 clipboard_selections,
6742 ));
6743 }
6744
6745 pub fn do_paste(
6746 &mut self,
6747 text: &String,
6748 clipboard_selections: Option<Vec<ClipboardSelection>>,
6749 handle_entire_lines: bool,
6750 cx: &mut ViewContext<Self>,
6751 ) {
6752 if self.read_only(cx) {
6753 return;
6754 }
6755
6756 let clipboard_text = Cow::Borrowed(text);
6757
6758 self.transact(cx, |this, cx| {
6759 if let Some(mut clipboard_selections) = clipboard_selections {
6760 let old_selections = this.selections.all::<usize>(cx);
6761 let all_selections_were_entire_line =
6762 clipboard_selections.iter().all(|s| s.is_entire_line);
6763 let first_selection_indent_column =
6764 clipboard_selections.first().map(|s| s.first_line_indent);
6765 if clipboard_selections.len() != old_selections.len() {
6766 clipboard_selections.drain(..);
6767 }
6768
6769 this.buffer.update(cx, |buffer, cx| {
6770 let snapshot = buffer.read(cx);
6771 let mut start_offset = 0;
6772 let mut edits = Vec::new();
6773 let mut original_indent_columns = Vec::new();
6774 for (ix, selection) in old_selections.iter().enumerate() {
6775 let to_insert;
6776 let entire_line;
6777 let original_indent_column;
6778 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
6779 let end_offset = start_offset + clipboard_selection.len;
6780 to_insert = &clipboard_text[start_offset..end_offset];
6781 entire_line = clipboard_selection.is_entire_line;
6782 start_offset = end_offset + 1;
6783 original_indent_column = Some(clipboard_selection.first_line_indent);
6784 } else {
6785 to_insert = clipboard_text.as_str();
6786 entire_line = all_selections_were_entire_line;
6787 original_indent_column = first_selection_indent_column
6788 }
6789
6790 // If the corresponding selection was empty when this slice of the
6791 // clipboard text was written, then the entire line containing the
6792 // selection was copied. If this selection is also currently empty,
6793 // then paste the line before the current line of the buffer.
6794 let range = if selection.is_empty() && handle_entire_lines && entire_line {
6795 let column = selection.start.to_point(&snapshot).column as usize;
6796 let line_start = selection.start - column;
6797 line_start..line_start
6798 } else {
6799 selection.range()
6800 };
6801
6802 edits.push((range, to_insert));
6803 original_indent_columns.extend(original_indent_column);
6804 }
6805 drop(snapshot);
6806
6807 buffer.edit(
6808 edits,
6809 Some(AutoindentMode::Block {
6810 original_indent_columns,
6811 }),
6812 cx,
6813 );
6814 });
6815
6816 let selections = this.selections.all::<usize>(cx);
6817 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6818 } else {
6819 this.insert(&clipboard_text, cx);
6820 }
6821 });
6822 }
6823
6824 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
6825 if let Some(item) = cx.read_from_clipboard() {
6826 let entries = item.entries();
6827
6828 match entries.first() {
6829 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
6830 // of all the pasted entries.
6831 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
6832 .do_paste(
6833 clipboard_string.text(),
6834 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
6835 true,
6836 cx,
6837 ),
6838 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, cx),
6839 }
6840 }
6841 }
6842
6843 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
6844 if self.read_only(cx) {
6845 return;
6846 }
6847
6848 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
6849 if let Some((selections, _)) =
6850 self.selection_history.transaction(transaction_id).cloned()
6851 {
6852 self.change_selections(None, cx, |s| {
6853 s.select_anchors(selections.to_vec());
6854 });
6855 }
6856 self.request_autoscroll(Autoscroll::fit(), cx);
6857 self.unmark_text(cx);
6858 self.refresh_inline_completion(true, false, cx);
6859 cx.emit(EditorEvent::Edited { transaction_id });
6860 cx.emit(EditorEvent::TransactionUndone { transaction_id });
6861 }
6862 }
6863
6864 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
6865 if self.read_only(cx) {
6866 return;
6867 }
6868
6869 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
6870 if let Some((_, Some(selections))) =
6871 self.selection_history.transaction(transaction_id).cloned()
6872 {
6873 self.change_selections(None, cx, |s| {
6874 s.select_anchors(selections.to_vec());
6875 });
6876 }
6877 self.request_autoscroll(Autoscroll::fit(), cx);
6878 self.unmark_text(cx);
6879 self.refresh_inline_completion(true, false, cx);
6880 cx.emit(EditorEvent::Edited { transaction_id });
6881 }
6882 }
6883
6884 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
6885 self.buffer
6886 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
6887 }
6888
6889 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut ViewContext<Self>) {
6890 self.buffer
6891 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
6892 }
6893
6894 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
6895 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6896 let line_mode = s.line_mode;
6897 s.move_with(|map, selection| {
6898 let cursor = if selection.is_empty() && !line_mode {
6899 movement::left(map, selection.start)
6900 } else {
6901 selection.start
6902 };
6903 selection.collapse_to(cursor, SelectionGoal::None);
6904 });
6905 })
6906 }
6907
6908 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
6909 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6910 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
6911 })
6912 }
6913
6914 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
6915 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6916 let line_mode = s.line_mode;
6917 s.move_with(|map, selection| {
6918 let cursor = if selection.is_empty() && !line_mode {
6919 movement::right(map, selection.end)
6920 } else {
6921 selection.end
6922 };
6923 selection.collapse_to(cursor, SelectionGoal::None)
6924 });
6925 })
6926 }
6927
6928 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
6929 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6930 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
6931 })
6932 }
6933
6934 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
6935 if self.take_rename(true, cx).is_some() {
6936 return;
6937 }
6938
6939 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6940 cx.propagate();
6941 return;
6942 }
6943
6944 let text_layout_details = &self.text_layout_details(cx);
6945 let selection_count = self.selections.count();
6946 let first_selection = self.selections.first_anchor();
6947
6948 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6949 let line_mode = s.line_mode;
6950 s.move_with(|map, selection| {
6951 if !selection.is_empty() && !line_mode {
6952 selection.goal = SelectionGoal::None;
6953 }
6954 let (cursor, goal) = movement::up(
6955 map,
6956 selection.start,
6957 selection.goal,
6958 false,
6959 &text_layout_details,
6960 );
6961 selection.collapse_to(cursor, goal);
6962 });
6963 });
6964
6965 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
6966 {
6967 cx.propagate();
6968 }
6969 }
6970
6971 pub fn move_up_by_lines(&mut self, action: &MoveUpByLines, cx: &mut ViewContext<Self>) {
6972 if self.take_rename(true, cx).is_some() {
6973 return;
6974 }
6975
6976 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6977 cx.propagate();
6978 return;
6979 }
6980
6981 let text_layout_details = &self.text_layout_details(cx);
6982
6983 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6984 let line_mode = s.line_mode;
6985 s.move_with(|map, selection| {
6986 if !selection.is_empty() && !line_mode {
6987 selection.goal = SelectionGoal::None;
6988 }
6989 let (cursor, goal) = movement::up_by_rows(
6990 map,
6991 selection.start,
6992 action.lines,
6993 selection.goal,
6994 false,
6995 &text_layout_details,
6996 );
6997 selection.collapse_to(cursor, goal);
6998 });
6999 })
7000 }
7001
7002 pub fn move_down_by_lines(&mut self, action: &MoveDownByLines, cx: &mut ViewContext<Self>) {
7003 if self.take_rename(true, cx).is_some() {
7004 return;
7005 }
7006
7007 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7008 cx.propagate();
7009 return;
7010 }
7011
7012 let text_layout_details = &self.text_layout_details(cx);
7013
7014 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7015 let line_mode = s.line_mode;
7016 s.move_with(|map, selection| {
7017 if !selection.is_empty() && !line_mode {
7018 selection.goal = SelectionGoal::None;
7019 }
7020 let (cursor, goal) = movement::down_by_rows(
7021 map,
7022 selection.start,
7023 action.lines,
7024 selection.goal,
7025 false,
7026 &text_layout_details,
7027 );
7028 selection.collapse_to(cursor, goal);
7029 });
7030 })
7031 }
7032
7033 pub fn select_down_by_lines(&mut self, action: &SelectDownByLines, cx: &mut ViewContext<Self>) {
7034 let text_layout_details = &self.text_layout_details(cx);
7035 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7036 s.move_heads_with(|map, head, goal| {
7037 movement::down_by_rows(map, head, action.lines, goal, false, &text_layout_details)
7038 })
7039 })
7040 }
7041
7042 pub fn select_up_by_lines(&mut self, action: &SelectUpByLines, cx: &mut ViewContext<Self>) {
7043 let text_layout_details = &self.text_layout_details(cx);
7044 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7045 s.move_heads_with(|map, head, goal| {
7046 movement::up_by_rows(map, head, action.lines, goal, false, &text_layout_details)
7047 })
7048 })
7049 }
7050
7051 pub fn select_page_up(&mut self, _: &SelectPageUp, cx: &mut ViewContext<Self>) {
7052 let Some(row_count) = self.visible_row_count() else {
7053 return;
7054 };
7055
7056 let text_layout_details = &self.text_layout_details(cx);
7057
7058 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7059 s.move_heads_with(|map, head, goal| {
7060 movement::up_by_rows(map, head, row_count, goal, false, &text_layout_details)
7061 })
7062 })
7063 }
7064
7065 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
7066 if self.take_rename(true, cx).is_some() {
7067 return;
7068 }
7069
7070 if self
7071 .context_menu
7072 .write()
7073 .as_mut()
7074 .map(|menu| menu.select_first(self.project.as_ref(), cx))
7075 .unwrap_or(false)
7076 {
7077 return;
7078 }
7079
7080 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7081 cx.propagate();
7082 return;
7083 }
7084
7085 let Some(row_count) = self.visible_row_count() else {
7086 return;
7087 };
7088
7089 let autoscroll = if action.center_cursor {
7090 Autoscroll::center()
7091 } else {
7092 Autoscroll::fit()
7093 };
7094
7095 let text_layout_details = &self.text_layout_details(cx);
7096
7097 self.change_selections(Some(autoscroll), cx, |s| {
7098 let line_mode = s.line_mode;
7099 s.move_with(|map, selection| {
7100 if !selection.is_empty() && !line_mode {
7101 selection.goal = SelectionGoal::None;
7102 }
7103 let (cursor, goal) = movement::up_by_rows(
7104 map,
7105 selection.end,
7106 row_count,
7107 selection.goal,
7108 false,
7109 &text_layout_details,
7110 );
7111 selection.collapse_to(cursor, goal);
7112 });
7113 });
7114 }
7115
7116 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
7117 let text_layout_details = &self.text_layout_details(cx);
7118 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7119 s.move_heads_with(|map, head, goal| {
7120 movement::up(map, head, goal, false, &text_layout_details)
7121 })
7122 })
7123 }
7124
7125 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
7126 self.take_rename(true, cx);
7127
7128 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7129 cx.propagate();
7130 return;
7131 }
7132
7133 let text_layout_details = &self.text_layout_details(cx);
7134 let selection_count = self.selections.count();
7135 let first_selection = self.selections.first_anchor();
7136
7137 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7138 let line_mode = s.line_mode;
7139 s.move_with(|map, selection| {
7140 if !selection.is_empty() && !line_mode {
7141 selection.goal = SelectionGoal::None;
7142 }
7143 let (cursor, goal) = movement::down(
7144 map,
7145 selection.end,
7146 selection.goal,
7147 false,
7148 &text_layout_details,
7149 );
7150 selection.collapse_to(cursor, goal);
7151 });
7152 });
7153
7154 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7155 {
7156 cx.propagate();
7157 }
7158 }
7159
7160 pub fn select_page_down(&mut self, _: &SelectPageDown, cx: &mut ViewContext<Self>) {
7161 let Some(row_count) = self.visible_row_count() else {
7162 return;
7163 };
7164
7165 let text_layout_details = &self.text_layout_details(cx);
7166
7167 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7168 s.move_heads_with(|map, head, goal| {
7169 movement::down_by_rows(map, head, row_count, goal, false, &text_layout_details)
7170 })
7171 })
7172 }
7173
7174 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
7175 if self.take_rename(true, cx).is_some() {
7176 return;
7177 }
7178
7179 if self
7180 .context_menu
7181 .write()
7182 .as_mut()
7183 .map(|menu| menu.select_last(self.project.as_ref(), cx))
7184 .unwrap_or(false)
7185 {
7186 return;
7187 }
7188
7189 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7190 cx.propagate();
7191 return;
7192 }
7193
7194 let Some(row_count) = self.visible_row_count() else {
7195 return;
7196 };
7197
7198 let autoscroll = if action.center_cursor {
7199 Autoscroll::center()
7200 } else {
7201 Autoscroll::fit()
7202 };
7203
7204 let text_layout_details = &self.text_layout_details(cx);
7205 self.change_selections(Some(autoscroll), cx, |s| {
7206 let line_mode = s.line_mode;
7207 s.move_with(|map, selection| {
7208 if !selection.is_empty() && !line_mode {
7209 selection.goal = SelectionGoal::None;
7210 }
7211 let (cursor, goal) = movement::down_by_rows(
7212 map,
7213 selection.end,
7214 row_count,
7215 selection.goal,
7216 false,
7217 &text_layout_details,
7218 );
7219 selection.collapse_to(cursor, goal);
7220 });
7221 });
7222 }
7223
7224 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
7225 let text_layout_details = &self.text_layout_details(cx);
7226 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7227 s.move_heads_with(|map, head, goal| {
7228 movement::down(map, head, goal, false, &text_layout_details)
7229 })
7230 });
7231 }
7232
7233 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
7234 if let Some(context_menu) = self.context_menu.write().as_mut() {
7235 context_menu.select_first(self.project.as_ref(), cx);
7236 }
7237 }
7238
7239 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
7240 if let Some(context_menu) = self.context_menu.write().as_mut() {
7241 context_menu.select_prev(self.project.as_ref(), cx);
7242 }
7243 }
7244
7245 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
7246 if let Some(context_menu) = self.context_menu.write().as_mut() {
7247 context_menu.select_next(self.project.as_ref(), cx);
7248 }
7249 }
7250
7251 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
7252 if let Some(context_menu) = self.context_menu.write().as_mut() {
7253 context_menu.select_last(self.project.as_ref(), cx);
7254 }
7255 }
7256
7257 pub fn move_to_previous_word_start(
7258 &mut self,
7259 _: &MoveToPreviousWordStart,
7260 cx: &mut ViewContext<Self>,
7261 ) {
7262 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7263 s.move_cursors_with(|map, head, _| {
7264 (
7265 movement::previous_word_start(map, head),
7266 SelectionGoal::None,
7267 )
7268 });
7269 })
7270 }
7271
7272 pub fn move_to_previous_subword_start(
7273 &mut self,
7274 _: &MoveToPreviousSubwordStart,
7275 cx: &mut ViewContext<Self>,
7276 ) {
7277 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7278 s.move_cursors_with(|map, head, _| {
7279 (
7280 movement::previous_subword_start(map, head),
7281 SelectionGoal::None,
7282 )
7283 });
7284 })
7285 }
7286
7287 pub fn select_to_previous_word_start(
7288 &mut self,
7289 _: &SelectToPreviousWordStart,
7290 cx: &mut ViewContext<Self>,
7291 ) {
7292 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7293 s.move_heads_with(|map, head, _| {
7294 (
7295 movement::previous_word_start(map, head),
7296 SelectionGoal::None,
7297 )
7298 });
7299 })
7300 }
7301
7302 pub fn select_to_previous_subword_start(
7303 &mut self,
7304 _: &SelectToPreviousSubwordStart,
7305 cx: &mut ViewContext<Self>,
7306 ) {
7307 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7308 s.move_heads_with(|map, head, _| {
7309 (
7310 movement::previous_subword_start(map, head),
7311 SelectionGoal::None,
7312 )
7313 });
7314 })
7315 }
7316
7317 pub fn delete_to_previous_word_start(
7318 &mut self,
7319 _: &DeleteToPreviousWordStart,
7320 cx: &mut ViewContext<Self>,
7321 ) {
7322 self.transact(cx, |this, cx| {
7323 this.select_autoclose_pair(cx);
7324 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7325 let line_mode = s.line_mode;
7326 s.move_with(|map, selection| {
7327 if selection.is_empty() && !line_mode {
7328 let cursor = movement::previous_word_start(map, selection.head());
7329 selection.set_head(cursor, SelectionGoal::None);
7330 }
7331 });
7332 });
7333 this.insert("", cx);
7334 });
7335 }
7336
7337 pub fn delete_to_previous_subword_start(
7338 &mut self,
7339 _: &DeleteToPreviousSubwordStart,
7340 cx: &mut ViewContext<Self>,
7341 ) {
7342 self.transact(cx, |this, cx| {
7343 this.select_autoclose_pair(cx);
7344 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7345 let line_mode = s.line_mode;
7346 s.move_with(|map, selection| {
7347 if selection.is_empty() && !line_mode {
7348 let cursor = movement::previous_subword_start(map, selection.head());
7349 selection.set_head(cursor, SelectionGoal::None);
7350 }
7351 });
7352 });
7353 this.insert("", cx);
7354 });
7355 }
7356
7357 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
7358 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7359 s.move_cursors_with(|map, head, _| {
7360 (movement::next_word_end(map, head), SelectionGoal::None)
7361 });
7362 })
7363 }
7364
7365 pub fn move_to_next_subword_end(
7366 &mut self,
7367 _: &MoveToNextSubwordEnd,
7368 cx: &mut ViewContext<Self>,
7369 ) {
7370 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7371 s.move_cursors_with(|map, head, _| {
7372 (movement::next_subword_end(map, head), SelectionGoal::None)
7373 });
7374 })
7375 }
7376
7377 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
7378 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7379 s.move_heads_with(|map, head, _| {
7380 (movement::next_word_end(map, head), SelectionGoal::None)
7381 });
7382 })
7383 }
7384
7385 pub fn select_to_next_subword_end(
7386 &mut self,
7387 _: &SelectToNextSubwordEnd,
7388 cx: &mut ViewContext<Self>,
7389 ) {
7390 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7391 s.move_heads_with(|map, head, _| {
7392 (movement::next_subword_end(map, head), SelectionGoal::None)
7393 });
7394 })
7395 }
7396
7397 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
7398 self.transact(cx, |this, cx| {
7399 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7400 let line_mode = s.line_mode;
7401 s.move_with(|map, selection| {
7402 if selection.is_empty() && !line_mode {
7403 let cursor = movement::next_word_end(map, selection.head());
7404 selection.set_head(cursor, SelectionGoal::None);
7405 }
7406 });
7407 });
7408 this.insert("", cx);
7409 });
7410 }
7411
7412 pub fn delete_to_next_subword_end(
7413 &mut self,
7414 _: &DeleteToNextSubwordEnd,
7415 cx: &mut ViewContext<Self>,
7416 ) {
7417 self.transact(cx, |this, cx| {
7418 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7419 s.move_with(|map, selection| {
7420 if selection.is_empty() {
7421 let cursor = movement::next_subword_end(map, selection.head());
7422 selection.set_head(cursor, SelectionGoal::None);
7423 }
7424 });
7425 });
7426 this.insert("", cx);
7427 });
7428 }
7429
7430 pub fn move_to_beginning_of_line(
7431 &mut self,
7432 action: &MoveToBeginningOfLine,
7433 cx: &mut ViewContext<Self>,
7434 ) {
7435 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7436 s.move_cursors_with(|map, head, _| {
7437 (
7438 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7439 SelectionGoal::None,
7440 )
7441 });
7442 })
7443 }
7444
7445 pub fn select_to_beginning_of_line(
7446 &mut self,
7447 action: &SelectToBeginningOfLine,
7448 cx: &mut ViewContext<Self>,
7449 ) {
7450 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7451 s.move_heads_with(|map, head, _| {
7452 (
7453 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7454 SelectionGoal::None,
7455 )
7456 });
7457 });
7458 }
7459
7460 pub fn delete_to_beginning_of_line(
7461 &mut self,
7462 _: &DeleteToBeginningOfLine,
7463 cx: &mut ViewContext<Self>,
7464 ) {
7465 self.transact(cx, |this, cx| {
7466 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7467 s.move_with(|_, selection| {
7468 selection.reversed = true;
7469 });
7470 });
7471
7472 this.select_to_beginning_of_line(
7473 &SelectToBeginningOfLine {
7474 stop_at_soft_wraps: false,
7475 },
7476 cx,
7477 );
7478 this.backspace(&Backspace, cx);
7479 });
7480 }
7481
7482 pub fn move_to_end_of_line(&mut self, action: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
7483 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7484 s.move_cursors_with(|map, head, _| {
7485 (
7486 movement::line_end(map, head, action.stop_at_soft_wraps),
7487 SelectionGoal::None,
7488 )
7489 });
7490 })
7491 }
7492
7493 pub fn select_to_end_of_line(
7494 &mut self,
7495 action: &SelectToEndOfLine,
7496 cx: &mut ViewContext<Self>,
7497 ) {
7498 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7499 s.move_heads_with(|map, head, _| {
7500 (
7501 movement::line_end(map, head, action.stop_at_soft_wraps),
7502 SelectionGoal::None,
7503 )
7504 });
7505 })
7506 }
7507
7508 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
7509 self.transact(cx, |this, cx| {
7510 this.select_to_end_of_line(
7511 &SelectToEndOfLine {
7512 stop_at_soft_wraps: false,
7513 },
7514 cx,
7515 );
7516 this.delete(&Delete, cx);
7517 });
7518 }
7519
7520 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
7521 self.transact(cx, |this, cx| {
7522 this.select_to_end_of_line(
7523 &SelectToEndOfLine {
7524 stop_at_soft_wraps: false,
7525 },
7526 cx,
7527 );
7528 this.cut(&Cut, cx);
7529 });
7530 }
7531
7532 pub fn move_to_start_of_paragraph(
7533 &mut self,
7534 _: &MoveToStartOfParagraph,
7535 cx: &mut ViewContext<Self>,
7536 ) {
7537 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7538 cx.propagate();
7539 return;
7540 }
7541
7542 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7543 s.move_with(|map, selection| {
7544 selection.collapse_to(
7545 movement::start_of_paragraph(map, selection.head(), 1),
7546 SelectionGoal::None,
7547 )
7548 });
7549 })
7550 }
7551
7552 pub fn move_to_end_of_paragraph(
7553 &mut self,
7554 _: &MoveToEndOfParagraph,
7555 cx: &mut ViewContext<Self>,
7556 ) {
7557 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7558 cx.propagate();
7559 return;
7560 }
7561
7562 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7563 s.move_with(|map, selection| {
7564 selection.collapse_to(
7565 movement::end_of_paragraph(map, selection.head(), 1),
7566 SelectionGoal::None,
7567 )
7568 });
7569 })
7570 }
7571
7572 pub fn select_to_start_of_paragraph(
7573 &mut self,
7574 _: &SelectToStartOfParagraph,
7575 cx: &mut ViewContext<Self>,
7576 ) {
7577 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7578 cx.propagate();
7579 return;
7580 }
7581
7582 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7583 s.move_heads_with(|map, head, _| {
7584 (
7585 movement::start_of_paragraph(map, head, 1),
7586 SelectionGoal::None,
7587 )
7588 });
7589 })
7590 }
7591
7592 pub fn select_to_end_of_paragraph(
7593 &mut self,
7594 _: &SelectToEndOfParagraph,
7595 cx: &mut ViewContext<Self>,
7596 ) {
7597 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7598 cx.propagate();
7599 return;
7600 }
7601
7602 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7603 s.move_heads_with(|map, head, _| {
7604 (
7605 movement::end_of_paragraph(map, head, 1),
7606 SelectionGoal::None,
7607 )
7608 });
7609 })
7610 }
7611
7612 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
7613 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7614 cx.propagate();
7615 return;
7616 }
7617
7618 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7619 s.select_ranges(vec![0..0]);
7620 });
7621 }
7622
7623 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
7624 let mut selection = self.selections.last::<Point>(cx);
7625 selection.set_head(Point::zero(), SelectionGoal::None);
7626
7627 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7628 s.select(vec![selection]);
7629 });
7630 }
7631
7632 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
7633 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7634 cx.propagate();
7635 return;
7636 }
7637
7638 let cursor = self.buffer.read(cx).read(cx).len();
7639 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7640 s.select_ranges(vec![cursor..cursor])
7641 });
7642 }
7643
7644 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
7645 self.nav_history = nav_history;
7646 }
7647
7648 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
7649 self.nav_history.as_ref()
7650 }
7651
7652 fn push_to_nav_history(
7653 &mut self,
7654 cursor_anchor: Anchor,
7655 new_position: Option<Point>,
7656 cx: &mut ViewContext<Self>,
7657 ) {
7658 if let Some(nav_history) = self.nav_history.as_mut() {
7659 let buffer = self.buffer.read(cx).read(cx);
7660 let cursor_position = cursor_anchor.to_point(&buffer);
7661 let scroll_state = self.scroll_manager.anchor();
7662 let scroll_top_row = scroll_state.top_row(&buffer);
7663 drop(buffer);
7664
7665 if let Some(new_position) = new_position {
7666 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
7667 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
7668 return;
7669 }
7670 }
7671
7672 nav_history.push(
7673 Some(NavigationData {
7674 cursor_anchor,
7675 cursor_position,
7676 scroll_anchor: scroll_state,
7677 scroll_top_row,
7678 }),
7679 cx,
7680 );
7681 }
7682 }
7683
7684 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
7685 let buffer = self.buffer.read(cx).snapshot(cx);
7686 let mut selection = self.selections.first::<usize>(cx);
7687 selection.set_head(buffer.len(), SelectionGoal::None);
7688 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7689 s.select(vec![selection]);
7690 });
7691 }
7692
7693 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
7694 let end = self.buffer.read(cx).read(cx).len();
7695 self.change_selections(None, cx, |s| {
7696 s.select_ranges(vec![0..end]);
7697 });
7698 }
7699
7700 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
7701 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7702 let mut selections = self.selections.all::<Point>(cx);
7703 let max_point = display_map.buffer_snapshot.max_point();
7704 for selection in &mut selections {
7705 let rows = selection.spanned_rows(true, &display_map);
7706 selection.start = Point::new(rows.start.0, 0);
7707 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
7708 selection.reversed = false;
7709 }
7710 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7711 s.select(selections);
7712 });
7713 }
7714
7715 pub fn split_selection_into_lines(
7716 &mut self,
7717 _: &SplitSelectionIntoLines,
7718 cx: &mut ViewContext<Self>,
7719 ) {
7720 let mut to_unfold = Vec::new();
7721 let mut new_selection_ranges = Vec::new();
7722 {
7723 let selections = self.selections.all::<Point>(cx);
7724 let buffer = self.buffer.read(cx).read(cx);
7725 for selection in selections {
7726 for row in selection.start.row..selection.end.row {
7727 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
7728 new_selection_ranges.push(cursor..cursor);
7729 }
7730 new_selection_ranges.push(selection.end..selection.end);
7731 to_unfold.push(selection.start..selection.end);
7732 }
7733 }
7734 self.unfold_ranges(to_unfold, true, true, cx);
7735 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7736 s.select_ranges(new_selection_ranges);
7737 });
7738 }
7739
7740 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
7741 self.add_selection(true, cx);
7742 }
7743
7744 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
7745 self.add_selection(false, cx);
7746 }
7747
7748 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
7749 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7750 let mut selections = self.selections.all::<Point>(cx);
7751 let text_layout_details = self.text_layout_details(cx);
7752 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
7753 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
7754 let range = oldest_selection.display_range(&display_map).sorted();
7755
7756 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
7757 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
7758 let positions = start_x.min(end_x)..start_x.max(end_x);
7759
7760 selections.clear();
7761 let mut stack = Vec::new();
7762 for row in range.start.row().0..=range.end.row().0 {
7763 if let Some(selection) = self.selections.build_columnar_selection(
7764 &display_map,
7765 DisplayRow(row),
7766 &positions,
7767 oldest_selection.reversed,
7768 &text_layout_details,
7769 ) {
7770 stack.push(selection.id);
7771 selections.push(selection);
7772 }
7773 }
7774
7775 if above {
7776 stack.reverse();
7777 }
7778
7779 AddSelectionsState { above, stack }
7780 });
7781
7782 let last_added_selection = *state.stack.last().unwrap();
7783 let mut new_selections = Vec::new();
7784 if above == state.above {
7785 let end_row = if above {
7786 DisplayRow(0)
7787 } else {
7788 display_map.max_point().row()
7789 };
7790
7791 'outer: for selection in selections {
7792 if selection.id == last_added_selection {
7793 let range = selection.display_range(&display_map).sorted();
7794 debug_assert_eq!(range.start.row(), range.end.row());
7795 let mut row = range.start.row();
7796 let positions =
7797 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
7798 px(start)..px(end)
7799 } else {
7800 let start_x =
7801 display_map.x_for_display_point(range.start, &text_layout_details);
7802 let end_x =
7803 display_map.x_for_display_point(range.end, &text_layout_details);
7804 start_x.min(end_x)..start_x.max(end_x)
7805 };
7806
7807 while row != end_row {
7808 if above {
7809 row.0 -= 1;
7810 } else {
7811 row.0 += 1;
7812 }
7813
7814 if let Some(new_selection) = self.selections.build_columnar_selection(
7815 &display_map,
7816 row,
7817 &positions,
7818 selection.reversed,
7819 &text_layout_details,
7820 ) {
7821 state.stack.push(new_selection.id);
7822 if above {
7823 new_selections.push(new_selection);
7824 new_selections.push(selection);
7825 } else {
7826 new_selections.push(selection);
7827 new_selections.push(new_selection);
7828 }
7829
7830 continue 'outer;
7831 }
7832 }
7833 }
7834
7835 new_selections.push(selection);
7836 }
7837 } else {
7838 new_selections = selections;
7839 new_selections.retain(|s| s.id != last_added_selection);
7840 state.stack.pop();
7841 }
7842
7843 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7844 s.select(new_selections);
7845 });
7846 if state.stack.len() > 1 {
7847 self.add_selections_state = Some(state);
7848 }
7849 }
7850
7851 pub fn select_next_match_internal(
7852 &mut self,
7853 display_map: &DisplaySnapshot,
7854 replace_newest: bool,
7855 autoscroll: Option<Autoscroll>,
7856 cx: &mut ViewContext<Self>,
7857 ) -> Result<()> {
7858 fn select_next_match_ranges(
7859 this: &mut Editor,
7860 range: Range<usize>,
7861 replace_newest: bool,
7862 auto_scroll: Option<Autoscroll>,
7863 cx: &mut ViewContext<Editor>,
7864 ) {
7865 this.unfold_ranges([range.clone()], false, true, cx);
7866 this.change_selections(auto_scroll, cx, |s| {
7867 if replace_newest {
7868 s.delete(s.newest_anchor().id);
7869 }
7870 s.insert_range(range.clone());
7871 });
7872 }
7873
7874 let buffer = &display_map.buffer_snapshot;
7875 let mut selections = self.selections.all::<usize>(cx);
7876 if let Some(mut select_next_state) = self.select_next_state.take() {
7877 let query = &select_next_state.query;
7878 if !select_next_state.done {
7879 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
7880 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
7881 let mut next_selected_range = None;
7882
7883 let bytes_after_last_selection =
7884 buffer.bytes_in_range(last_selection.end..buffer.len());
7885 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
7886 let query_matches = query
7887 .stream_find_iter(bytes_after_last_selection)
7888 .map(|result| (last_selection.end, result))
7889 .chain(
7890 query
7891 .stream_find_iter(bytes_before_first_selection)
7892 .map(|result| (0, result)),
7893 );
7894
7895 for (start_offset, query_match) in query_matches {
7896 let query_match = query_match.unwrap(); // can only fail due to I/O
7897 let offset_range =
7898 start_offset + query_match.start()..start_offset + query_match.end();
7899 let display_range = offset_range.start.to_display_point(&display_map)
7900 ..offset_range.end.to_display_point(&display_map);
7901
7902 if !select_next_state.wordwise
7903 || (!movement::is_inside_word(&display_map, display_range.start)
7904 && !movement::is_inside_word(&display_map, display_range.end))
7905 {
7906 // TODO: This is n^2, because we might check all the selections
7907 if !selections
7908 .iter()
7909 .any(|selection| selection.range().overlaps(&offset_range))
7910 {
7911 next_selected_range = Some(offset_range);
7912 break;
7913 }
7914 }
7915 }
7916
7917 if let Some(next_selected_range) = next_selected_range {
7918 select_next_match_ranges(
7919 self,
7920 next_selected_range,
7921 replace_newest,
7922 autoscroll,
7923 cx,
7924 );
7925 } else {
7926 select_next_state.done = true;
7927 }
7928 }
7929
7930 self.select_next_state = Some(select_next_state);
7931 } else {
7932 let mut only_carets = true;
7933 let mut same_text_selected = true;
7934 let mut selected_text = None;
7935
7936 let mut selections_iter = selections.iter().peekable();
7937 while let Some(selection) = selections_iter.next() {
7938 if selection.start != selection.end {
7939 only_carets = false;
7940 }
7941
7942 if same_text_selected {
7943 if selected_text.is_none() {
7944 selected_text =
7945 Some(buffer.text_for_range(selection.range()).collect::<String>());
7946 }
7947
7948 if let Some(next_selection) = selections_iter.peek() {
7949 if next_selection.range().len() == selection.range().len() {
7950 let next_selected_text = buffer
7951 .text_for_range(next_selection.range())
7952 .collect::<String>();
7953 if Some(next_selected_text) != selected_text {
7954 same_text_selected = false;
7955 selected_text = None;
7956 }
7957 } else {
7958 same_text_selected = false;
7959 selected_text = None;
7960 }
7961 }
7962 }
7963 }
7964
7965 if only_carets {
7966 for selection in &mut selections {
7967 let word_range = movement::surrounding_word(
7968 &display_map,
7969 selection.start.to_display_point(&display_map),
7970 );
7971 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
7972 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
7973 selection.goal = SelectionGoal::None;
7974 selection.reversed = false;
7975 select_next_match_ranges(
7976 self,
7977 selection.start..selection.end,
7978 replace_newest,
7979 autoscroll,
7980 cx,
7981 );
7982 }
7983
7984 if selections.len() == 1 {
7985 let selection = selections
7986 .last()
7987 .expect("ensured that there's only one selection");
7988 let query = buffer
7989 .text_for_range(selection.start..selection.end)
7990 .collect::<String>();
7991 let is_empty = query.is_empty();
7992 let select_state = SelectNextState {
7993 query: AhoCorasick::new(&[query])?,
7994 wordwise: true,
7995 done: is_empty,
7996 };
7997 self.select_next_state = Some(select_state);
7998 } else {
7999 self.select_next_state = None;
8000 }
8001 } else if let Some(selected_text) = selected_text {
8002 self.select_next_state = Some(SelectNextState {
8003 query: AhoCorasick::new(&[selected_text])?,
8004 wordwise: false,
8005 done: false,
8006 });
8007 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
8008 }
8009 }
8010 Ok(())
8011 }
8012
8013 pub fn select_all_matches(
8014 &mut self,
8015 _action: &SelectAllMatches,
8016 cx: &mut ViewContext<Self>,
8017 ) -> Result<()> {
8018 self.push_to_selection_history();
8019 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8020
8021 self.select_next_match_internal(&display_map, false, None, cx)?;
8022 let Some(select_next_state) = self.select_next_state.as_mut() else {
8023 return Ok(());
8024 };
8025 if select_next_state.done {
8026 return Ok(());
8027 }
8028
8029 let mut new_selections = self.selections.all::<usize>(cx);
8030
8031 let buffer = &display_map.buffer_snapshot;
8032 let query_matches = select_next_state
8033 .query
8034 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
8035
8036 for query_match in query_matches {
8037 let query_match = query_match.unwrap(); // can only fail due to I/O
8038 let offset_range = query_match.start()..query_match.end();
8039 let display_range = offset_range.start.to_display_point(&display_map)
8040 ..offset_range.end.to_display_point(&display_map);
8041
8042 if !select_next_state.wordwise
8043 || (!movement::is_inside_word(&display_map, display_range.start)
8044 && !movement::is_inside_word(&display_map, display_range.end))
8045 {
8046 self.selections.change_with(cx, |selections| {
8047 new_selections.push(Selection {
8048 id: selections.new_selection_id(),
8049 start: offset_range.start,
8050 end: offset_range.end,
8051 reversed: false,
8052 goal: SelectionGoal::None,
8053 });
8054 });
8055 }
8056 }
8057
8058 new_selections.sort_by_key(|selection| selection.start);
8059 let mut ix = 0;
8060 while ix + 1 < new_selections.len() {
8061 let current_selection = &new_selections[ix];
8062 let next_selection = &new_selections[ix + 1];
8063 if current_selection.range().overlaps(&next_selection.range()) {
8064 if current_selection.id < next_selection.id {
8065 new_selections.remove(ix + 1);
8066 } else {
8067 new_selections.remove(ix);
8068 }
8069 } else {
8070 ix += 1;
8071 }
8072 }
8073
8074 select_next_state.done = true;
8075 self.unfold_ranges(
8076 new_selections.iter().map(|selection| selection.range()),
8077 false,
8078 false,
8079 cx,
8080 );
8081 self.change_selections(Some(Autoscroll::fit()), cx, |selections| {
8082 selections.select(new_selections)
8083 });
8084
8085 Ok(())
8086 }
8087
8088 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
8089 self.push_to_selection_history();
8090 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8091 self.select_next_match_internal(
8092 &display_map,
8093 action.replace_newest,
8094 Some(Autoscroll::newest()),
8095 cx,
8096 )?;
8097 Ok(())
8098 }
8099
8100 pub fn select_previous(
8101 &mut self,
8102 action: &SelectPrevious,
8103 cx: &mut ViewContext<Self>,
8104 ) -> Result<()> {
8105 self.push_to_selection_history();
8106 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8107 let buffer = &display_map.buffer_snapshot;
8108 let mut selections = self.selections.all::<usize>(cx);
8109 if let Some(mut select_prev_state) = self.select_prev_state.take() {
8110 let query = &select_prev_state.query;
8111 if !select_prev_state.done {
8112 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
8113 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
8114 let mut next_selected_range = None;
8115 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
8116 let bytes_before_last_selection =
8117 buffer.reversed_bytes_in_range(0..last_selection.start);
8118 let bytes_after_first_selection =
8119 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
8120 let query_matches = query
8121 .stream_find_iter(bytes_before_last_selection)
8122 .map(|result| (last_selection.start, result))
8123 .chain(
8124 query
8125 .stream_find_iter(bytes_after_first_selection)
8126 .map(|result| (buffer.len(), result)),
8127 );
8128 for (end_offset, query_match) in query_matches {
8129 let query_match = query_match.unwrap(); // can only fail due to I/O
8130 let offset_range =
8131 end_offset - query_match.end()..end_offset - query_match.start();
8132 let display_range = offset_range.start.to_display_point(&display_map)
8133 ..offset_range.end.to_display_point(&display_map);
8134
8135 if !select_prev_state.wordwise
8136 || (!movement::is_inside_word(&display_map, display_range.start)
8137 && !movement::is_inside_word(&display_map, display_range.end))
8138 {
8139 next_selected_range = Some(offset_range);
8140 break;
8141 }
8142 }
8143
8144 if let Some(next_selected_range) = next_selected_range {
8145 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
8146 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8147 if action.replace_newest {
8148 s.delete(s.newest_anchor().id);
8149 }
8150 s.insert_range(next_selected_range);
8151 });
8152 } else {
8153 select_prev_state.done = true;
8154 }
8155 }
8156
8157 self.select_prev_state = Some(select_prev_state);
8158 } else {
8159 let mut only_carets = true;
8160 let mut same_text_selected = true;
8161 let mut selected_text = None;
8162
8163 let mut selections_iter = selections.iter().peekable();
8164 while let Some(selection) = selections_iter.next() {
8165 if selection.start != selection.end {
8166 only_carets = false;
8167 }
8168
8169 if same_text_selected {
8170 if selected_text.is_none() {
8171 selected_text =
8172 Some(buffer.text_for_range(selection.range()).collect::<String>());
8173 }
8174
8175 if let Some(next_selection) = selections_iter.peek() {
8176 if next_selection.range().len() == selection.range().len() {
8177 let next_selected_text = buffer
8178 .text_for_range(next_selection.range())
8179 .collect::<String>();
8180 if Some(next_selected_text) != selected_text {
8181 same_text_selected = false;
8182 selected_text = None;
8183 }
8184 } else {
8185 same_text_selected = false;
8186 selected_text = None;
8187 }
8188 }
8189 }
8190 }
8191
8192 if only_carets {
8193 for selection in &mut selections {
8194 let word_range = movement::surrounding_word(
8195 &display_map,
8196 selection.start.to_display_point(&display_map),
8197 );
8198 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
8199 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
8200 selection.goal = SelectionGoal::None;
8201 selection.reversed = false;
8202 }
8203 if selections.len() == 1 {
8204 let selection = selections
8205 .last()
8206 .expect("ensured that there's only one selection");
8207 let query = buffer
8208 .text_for_range(selection.start..selection.end)
8209 .collect::<String>();
8210 let is_empty = query.is_empty();
8211 let select_state = SelectNextState {
8212 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
8213 wordwise: true,
8214 done: is_empty,
8215 };
8216 self.select_prev_state = Some(select_state);
8217 } else {
8218 self.select_prev_state = None;
8219 }
8220
8221 self.unfold_ranges(
8222 selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
8223 false,
8224 true,
8225 cx,
8226 );
8227 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8228 s.select(selections);
8229 });
8230 } else if let Some(selected_text) = selected_text {
8231 self.select_prev_state = Some(SelectNextState {
8232 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
8233 wordwise: false,
8234 done: false,
8235 });
8236 self.select_previous(action, cx)?;
8237 }
8238 }
8239 Ok(())
8240 }
8241
8242 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
8243 let text_layout_details = &self.text_layout_details(cx);
8244 self.transact(cx, |this, cx| {
8245 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
8246 let mut edits = Vec::new();
8247 let mut selection_edit_ranges = Vec::new();
8248 let mut last_toggled_row = None;
8249 let snapshot = this.buffer.read(cx).read(cx);
8250 let empty_str: Arc<str> = Arc::default();
8251 let mut suffixes_inserted = Vec::new();
8252
8253 fn comment_prefix_range(
8254 snapshot: &MultiBufferSnapshot,
8255 row: MultiBufferRow,
8256 comment_prefix: &str,
8257 comment_prefix_whitespace: &str,
8258 ) -> Range<Point> {
8259 let start = Point::new(row.0, snapshot.indent_size_for_line(row).len);
8260
8261 let mut line_bytes = snapshot
8262 .bytes_in_range(start..snapshot.max_point())
8263 .flatten()
8264 .copied();
8265
8266 // If this line currently begins with the line comment prefix, then record
8267 // the range containing the prefix.
8268 if line_bytes
8269 .by_ref()
8270 .take(comment_prefix.len())
8271 .eq(comment_prefix.bytes())
8272 {
8273 // Include any whitespace that matches the comment prefix.
8274 let matching_whitespace_len = line_bytes
8275 .zip(comment_prefix_whitespace.bytes())
8276 .take_while(|(a, b)| a == b)
8277 .count() as u32;
8278 let end = Point::new(
8279 start.row,
8280 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
8281 );
8282 start..end
8283 } else {
8284 start..start
8285 }
8286 }
8287
8288 fn comment_suffix_range(
8289 snapshot: &MultiBufferSnapshot,
8290 row: MultiBufferRow,
8291 comment_suffix: &str,
8292 comment_suffix_has_leading_space: bool,
8293 ) -> Range<Point> {
8294 let end = Point::new(row.0, snapshot.line_len(row));
8295 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
8296
8297 let mut line_end_bytes = snapshot
8298 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
8299 .flatten()
8300 .copied();
8301
8302 let leading_space_len = if suffix_start_column > 0
8303 && line_end_bytes.next() == Some(b' ')
8304 && comment_suffix_has_leading_space
8305 {
8306 1
8307 } else {
8308 0
8309 };
8310
8311 // If this line currently begins with the line comment prefix, then record
8312 // the range containing the prefix.
8313 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
8314 let start = Point::new(end.row, suffix_start_column - leading_space_len);
8315 start..end
8316 } else {
8317 end..end
8318 }
8319 }
8320
8321 // TODO: Handle selections that cross excerpts
8322 for selection in &mut selections {
8323 let start_column = snapshot
8324 .indent_size_for_line(MultiBufferRow(selection.start.row))
8325 .len;
8326 let language = if let Some(language) =
8327 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
8328 {
8329 language
8330 } else {
8331 continue;
8332 };
8333
8334 selection_edit_ranges.clear();
8335
8336 // If multiple selections contain a given row, avoid processing that
8337 // row more than once.
8338 let mut start_row = MultiBufferRow(selection.start.row);
8339 if last_toggled_row == Some(start_row) {
8340 start_row = start_row.next_row();
8341 }
8342 let end_row =
8343 if selection.end.row > selection.start.row && selection.end.column == 0 {
8344 MultiBufferRow(selection.end.row - 1)
8345 } else {
8346 MultiBufferRow(selection.end.row)
8347 };
8348 last_toggled_row = Some(end_row);
8349
8350 if start_row > end_row {
8351 continue;
8352 }
8353
8354 // If the language has line comments, toggle those.
8355 let full_comment_prefixes = language.line_comment_prefixes();
8356 if !full_comment_prefixes.is_empty() {
8357 let first_prefix = full_comment_prefixes
8358 .first()
8359 .expect("prefixes is non-empty");
8360 let prefix_trimmed_lengths = full_comment_prefixes
8361 .iter()
8362 .map(|p| p.trim_end_matches(' ').len())
8363 .collect::<SmallVec<[usize; 4]>>();
8364
8365 let mut all_selection_lines_are_comments = true;
8366
8367 for row in start_row.0..=end_row.0 {
8368 let row = MultiBufferRow(row);
8369 if start_row < end_row && snapshot.is_line_blank(row) {
8370 continue;
8371 }
8372
8373 let prefix_range = full_comment_prefixes
8374 .iter()
8375 .zip(prefix_trimmed_lengths.iter().copied())
8376 .map(|(prefix, trimmed_prefix_len)| {
8377 comment_prefix_range(
8378 snapshot.deref(),
8379 row,
8380 &prefix[..trimmed_prefix_len],
8381 &prefix[trimmed_prefix_len..],
8382 )
8383 })
8384 .max_by_key(|range| range.end.column - range.start.column)
8385 .expect("prefixes is non-empty");
8386
8387 if prefix_range.is_empty() {
8388 all_selection_lines_are_comments = false;
8389 }
8390
8391 selection_edit_ranges.push(prefix_range);
8392 }
8393
8394 if all_selection_lines_are_comments {
8395 edits.extend(
8396 selection_edit_ranges
8397 .iter()
8398 .cloned()
8399 .map(|range| (range, empty_str.clone())),
8400 );
8401 } else {
8402 let min_column = selection_edit_ranges
8403 .iter()
8404 .map(|range| range.start.column)
8405 .min()
8406 .unwrap_or(0);
8407 edits.extend(selection_edit_ranges.iter().map(|range| {
8408 let position = Point::new(range.start.row, min_column);
8409 (position..position, first_prefix.clone())
8410 }));
8411 }
8412 } else if let Some((full_comment_prefix, comment_suffix)) =
8413 language.block_comment_delimiters()
8414 {
8415 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
8416 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
8417 let prefix_range = comment_prefix_range(
8418 snapshot.deref(),
8419 start_row,
8420 comment_prefix,
8421 comment_prefix_whitespace,
8422 );
8423 let suffix_range = comment_suffix_range(
8424 snapshot.deref(),
8425 end_row,
8426 comment_suffix.trim_start_matches(' '),
8427 comment_suffix.starts_with(' '),
8428 );
8429
8430 if prefix_range.is_empty() || suffix_range.is_empty() {
8431 edits.push((
8432 prefix_range.start..prefix_range.start,
8433 full_comment_prefix.clone(),
8434 ));
8435 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
8436 suffixes_inserted.push((end_row, comment_suffix.len()));
8437 } else {
8438 edits.push((prefix_range, empty_str.clone()));
8439 edits.push((suffix_range, empty_str.clone()));
8440 }
8441 } else {
8442 continue;
8443 }
8444 }
8445
8446 drop(snapshot);
8447 this.buffer.update(cx, |buffer, cx| {
8448 buffer.edit(edits, None, cx);
8449 });
8450
8451 // Adjust selections so that they end before any comment suffixes that
8452 // were inserted.
8453 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
8454 let mut selections = this.selections.all::<Point>(cx);
8455 let snapshot = this.buffer.read(cx).read(cx);
8456 for selection in &mut selections {
8457 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
8458 match row.cmp(&MultiBufferRow(selection.end.row)) {
8459 Ordering::Less => {
8460 suffixes_inserted.next();
8461 continue;
8462 }
8463 Ordering::Greater => break,
8464 Ordering::Equal => {
8465 if selection.end.column == snapshot.line_len(row) {
8466 if selection.is_empty() {
8467 selection.start.column -= suffix_len as u32;
8468 }
8469 selection.end.column -= suffix_len as u32;
8470 }
8471 break;
8472 }
8473 }
8474 }
8475 }
8476
8477 drop(snapshot);
8478 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
8479
8480 let selections = this.selections.all::<Point>(cx);
8481 let selections_on_single_row = selections.windows(2).all(|selections| {
8482 selections[0].start.row == selections[1].start.row
8483 && selections[0].end.row == selections[1].end.row
8484 && selections[0].start.row == selections[0].end.row
8485 });
8486 let selections_selecting = selections
8487 .iter()
8488 .any(|selection| selection.start != selection.end);
8489 let advance_downwards = action.advance_downwards
8490 && selections_on_single_row
8491 && !selections_selecting
8492 && !matches!(this.mode, EditorMode::SingleLine { .. });
8493
8494 if advance_downwards {
8495 let snapshot = this.buffer.read(cx).snapshot(cx);
8496
8497 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8498 s.move_cursors_with(|display_snapshot, display_point, _| {
8499 let mut point = display_point.to_point(display_snapshot);
8500 point.row += 1;
8501 point = snapshot.clip_point(point, Bias::Left);
8502 let display_point = point.to_display_point(display_snapshot);
8503 let goal = SelectionGoal::HorizontalPosition(
8504 display_snapshot
8505 .x_for_display_point(display_point, &text_layout_details)
8506 .into(),
8507 );
8508 (display_point, goal)
8509 })
8510 });
8511 }
8512 });
8513 }
8514
8515 pub fn select_enclosing_symbol(
8516 &mut self,
8517 _: &SelectEnclosingSymbol,
8518 cx: &mut ViewContext<Self>,
8519 ) {
8520 let buffer = self.buffer.read(cx).snapshot(cx);
8521 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8522
8523 fn update_selection(
8524 selection: &Selection<usize>,
8525 buffer_snap: &MultiBufferSnapshot,
8526 ) -> Option<Selection<usize>> {
8527 let cursor = selection.head();
8528 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
8529 for symbol in symbols.iter().rev() {
8530 let start = symbol.range.start.to_offset(&buffer_snap);
8531 let end = symbol.range.end.to_offset(&buffer_snap);
8532 let new_range = start..end;
8533 if start < selection.start || end > selection.end {
8534 return Some(Selection {
8535 id: selection.id,
8536 start: new_range.start,
8537 end: new_range.end,
8538 goal: SelectionGoal::None,
8539 reversed: selection.reversed,
8540 });
8541 }
8542 }
8543 None
8544 }
8545
8546 let mut selected_larger_symbol = false;
8547 let new_selections = old_selections
8548 .iter()
8549 .map(|selection| match update_selection(selection, &buffer) {
8550 Some(new_selection) => {
8551 if new_selection.range() != selection.range() {
8552 selected_larger_symbol = true;
8553 }
8554 new_selection
8555 }
8556 None => selection.clone(),
8557 })
8558 .collect::<Vec<_>>();
8559
8560 if selected_larger_symbol {
8561 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8562 s.select(new_selections);
8563 });
8564 }
8565 }
8566
8567 pub fn select_larger_syntax_node(
8568 &mut self,
8569 _: &SelectLargerSyntaxNode,
8570 cx: &mut ViewContext<Self>,
8571 ) {
8572 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8573 let buffer = self.buffer.read(cx).snapshot(cx);
8574 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8575
8576 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8577 let mut selected_larger_node = false;
8578 let new_selections = old_selections
8579 .iter()
8580 .map(|selection| {
8581 let old_range = selection.start..selection.end;
8582 let mut new_range = old_range.clone();
8583 while let Some(containing_range) =
8584 buffer.range_for_syntax_ancestor(new_range.clone())
8585 {
8586 new_range = containing_range;
8587 if !display_map.intersects_fold(new_range.start)
8588 && !display_map.intersects_fold(new_range.end)
8589 {
8590 break;
8591 }
8592 }
8593
8594 selected_larger_node |= new_range != old_range;
8595 Selection {
8596 id: selection.id,
8597 start: new_range.start,
8598 end: new_range.end,
8599 goal: SelectionGoal::None,
8600 reversed: selection.reversed,
8601 }
8602 })
8603 .collect::<Vec<_>>();
8604
8605 if selected_larger_node {
8606 stack.push(old_selections);
8607 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8608 s.select(new_selections);
8609 });
8610 }
8611 self.select_larger_syntax_node_stack = stack;
8612 }
8613
8614 pub fn select_smaller_syntax_node(
8615 &mut self,
8616 _: &SelectSmallerSyntaxNode,
8617 cx: &mut ViewContext<Self>,
8618 ) {
8619 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8620 if let Some(selections) = stack.pop() {
8621 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8622 s.select(selections.to_vec());
8623 });
8624 }
8625 self.select_larger_syntax_node_stack = stack;
8626 }
8627
8628 fn refresh_runnables(&mut self, cx: &mut ViewContext<Self>) -> Task<()> {
8629 if !EditorSettings::get_global(cx).gutter.runnables {
8630 self.clear_tasks();
8631 return Task::ready(());
8632 }
8633 let project = self.project.clone();
8634 cx.spawn(|this, mut cx| async move {
8635 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
8636 this.display_map.update(cx, |map, cx| map.snapshot(cx))
8637 }) else {
8638 return;
8639 };
8640
8641 let Some(project) = project else {
8642 return;
8643 };
8644
8645 let hide_runnables = project
8646 .update(&mut cx, |project, cx| {
8647 // Do not display any test indicators in non-dev server remote projects.
8648 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
8649 })
8650 .unwrap_or(true);
8651 if hide_runnables {
8652 return;
8653 }
8654 let new_rows =
8655 cx.background_executor()
8656 .spawn({
8657 let snapshot = display_snapshot.clone();
8658 async move {
8659 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
8660 }
8661 })
8662 .await;
8663 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
8664
8665 this.update(&mut cx, |this, _| {
8666 this.clear_tasks();
8667 for (key, value) in rows {
8668 this.insert_tasks(key, value);
8669 }
8670 })
8671 .ok();
8672 })
8673 }
8674 fn fetch_runnable_ranges(
8675 snapshot: &DisplaySnapshot,
8676 range: Range<Anchor>,
8677 ) -> Vec<language::RunnableRange> {
8678 snapshot.buffer_snapshot.runnable_ranges(range).collect()
8679 }
8680
8681 fn runnable_rows(
8682 project: Model<Project>,
8683 snapshot: DisplaySnapshot,
8684 runnable_ranges: Vec<RunnableRange>,
8685 mut cx: AsyncWindowContext,
8686 ) -> Vec<((BufferId, u32), RunnableTasks)> {
8687 runnable_ranges
8688 .into_iter()
8689 .filter_map(|mut runnable| {
8690 let tasks = cx
8691 .update(|cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
8692 .ok()?;
8693 if tasks.is_empty() {
8694 return None;
8695 }
8696
8697 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
8698
8699 let row = snapshot
8700 .buffer_snapshot
8701 .buffer_line_for_row(MultiBufferRow(point.row))?
8702 .1
8703 .start
8704 .row;
8705
8706 let context_range =
8707 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
8708 Some((
8709 (runnable.buffer_id, row),
8710 RunnableTasks {
8711 templates: tasks,
8712 offset: MultiBufferOffset(runnable.run_range.start),
8713 context_range,
8714 column: point.column,
8715 extra_variables: runnable.extra_captures,
8716 },
8717 ))
8718 })
8719 .collect()
8720 }
8721
8722 fn templates_with_tags(
8723 project: &Model<Project>,
8724 runnable: &mut Runnable,
8725 cx: &WindowContext<'_>,
8726 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
8727 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
8728 let (worktree_id, file) = project
8729 .buffer_for_id(runnable.buffer, cx)
8730 .and_then(|buffer| buffer.read(cx).file())
8731 .map(|file| (WorktreeId::from_usize(file.worktree_id()), file.clone()))
8732 .unzip();
8733
8734 (project.task_inventory().clone(), worktree_id, file)
8735 });
8736
8737 let inventory = inventory.read(cx);
8738 let tags = mem::take(&mut runnable.tags);
8739 let mut tags: Vec<_> = tags
8740 .into_iter()
8741 .flat_map(|tag| {
8742 let tag = tag.0.clone();
8743 inventory
8744 .list_tasks(
8745 file.clone(),
8746 Some(runnable.language.clone()),
8747 worktree_id,
8748 cx,
8749 )
8750 .into_iter()
8751 .filter(move |(_, template)| {
8752 template.tags.iter().any(|source_tag| source_tag == &tag)
8753 })
8754 })
8755 .sorted_by_key(|(kind, _)| kind.to_owned())
8756 .collect();
8757 if let Some((leading_tag_source, _)) = tags.first() {
8758 // Strongest source wins; if we have worktree tag binding, prefer that to
8759 // global and language bindings;
8760 // if we have a global binding, prefer that to language binding.
8761 let first_mismatch = tags
8762 .iter()
8763 .position(|(tag_source, _)| tag_source != leading_tag_source);
8764 if let Some(index) = first_mismatch {
8765 tags.truncate(index);
8766 }
8767 }
8768
8769 tags
8770 }
8771
8772 pub fn move_to_enclosing_bracket(
8773 &mut self,
8774 _: &MoveToEnclosingBracket,
8775 cx: &mut ViewContext<Self>,
8776 ) {
8777 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8778 s.move_offsets_with(|snapshot, selection| {
8779 let Some(enclosing_bracket_ranges) =
8780 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
8781 else {
8782 return;
8783 };
8784
8785 let mut best_length = usize::MAX;
8786 let mut best_inside = false;
8787 let mut best_in_bracket_range = false;
8788 let mut best_destination = None;
8789 for (open, close) in enclosing_bracket_ranges {
8790 let close = close.to_inclusive();
8791 let length = close.end() - open.start;
8792 let inside = selection.start >= open.end && selection.end <= *close.start();
8793 let in_bracket_range = open.to_inclusive().contains(&selection.head())
8794 || close.contains(&selection.head());
8795
8796 // If best is next to a bracket and current isn't, skip
8797 if !in_bracket_range && best_in_bracket_range {
8798 continue;
8799 }
8800
8801 // Prefer smaller lengths unless best is inside and current isn't
8802 if length > best_length && (best_inside || !inside) {
8803 continue;
8804 }
8805
8806 best_length = length;
8807 best_inside = inside;
8808 best_in_bracket_range = in_bracket_range;
8809 best_destination = Some(
8810 if close.contains(&selection.start) && close.contains(&selection.end) {
8811 if inside {
8812 open.end
8813 } else {
8814 open.start
8815 }
8816 } else {
8817 if inside {
8818 *close.start()
8819 } else {
8820 *close.end()
8821 }
8822 },
8823 );
8824 }
8825
8826 if let Some(destination) = best_destination {
8827 selection.collapse_to(destination, SelectionGoal::None);
8828 }
8829 })
8830 });
8831 }
8832
8833 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
8834 self.end_selection(cx);
8835 self.selection_history.mode = SelectionHistoryMode::Undoing;
8836 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
8837 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8838 self.select_next_state = entry.select_next_state;
8839 self.select_prev_state = entry.select_prev_state;
8840 self.add_selections_state = entry.add_selections_state;
8841 self.request_autoscroll(Autoscroll::newest(), cx);
8842 }
8843 self.selection_history.mode = SelectionHistoryMode::Normal;
8844 }
8845
8846 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
8847 self.end_selection(cx);
8848 self.selection_history.mode = SelectionHistoryMode::Redoing;
8849 if let Some(entry) = self.selection_history.redo_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 expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
8860 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
8861 }
8862
8863 pub fn expand_excerpts_down(
8864 &mut self,
8865 action: &ExpandExcerptsDown,
8866 cx: &mut ViewContext<Self>,
8867 ) {
8868 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
8869 }
8870
8871 pub fn expand_excerpts_up(&mut self, action: &ExpandExcerptsUp, cx: &mut ViewContext<Self>) {
8872 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
8873 }
8874
8875 pub fn expand_excerpts_for_direction(
8876 &mut self,
8877 lines: u32,
8878 direction: ExpandExcerptDirection,
8879 cx: &mut ViewContext<Self>,
8880 ) {
8881 let selections = self.selections.disjoint_anchors();
8882
8883 let lines = if lines == 0 {
8884 EditorSettings::get_global(cx).expand_excerpt_lines
8885 } else {
8886 lines
8887 };
8888
8889 self.buffer.update(cx, |buffer, cx| {
8890 buffer.expand_excerpts(
8891 selections
8892 .into_iter()
8893 .map(|selection| selection.head().excerpt_id)
8894 .dedup(),
8895 lines,
8896 direction,
8897 cx,
8898 )
8899 })
8900 }
8901
8902 pub fn expand_excerpt(
8903 &mut self,
8904 excerpt: ExcerptId,
8905 direction: ExpandExcerptDirection,
8906 cx: &mut ViewContext<Self>,
8907 ) {
8908 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
8909 self.buffer.update(cx, |buffer, cx| {
8910 buffer.expand_excerpts([excerpt], lines, direction, cx)
8911 })
8912 }
8913
8914 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
8915 self.go_to_diagnostic_impl(Direction::Next, cx)
8916 }
8917
8918 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
8919 self.go_to_diagnostic_impl(Direction::Prev, cx)
8920 }
8921
8922 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
8923 let buffer = self.buffer.read(cx).snapshot(cx);
8924 let selection = self.selections.newest::<usize>(cx);
8925
8926 // If there is an active Diagnostic Popover jump to its diagnostic instead.
8927 if direction == Direction::Next {
8928 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
8929 let (group_id, jump_to) = popover.activation_info();
8930 if self.activate_diagnostics(group_id, cx) {
8931 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8932 let mut new_selection = s.newest_anchor().clone();
8933 new_selection.collapse_to(jump_to, SelectionGoal::None);
8934 s.select_anchors(vec![new_selection.clone()]);
8935 });
8936 }
8937 return;
8938 }
8939 }
8940
8941 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
8942 active_diagnostics
8943 .primary_range
8944 .to_offset(&buffer)
8945 .to_inclusive()
8946 });
8947 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
8948 if active_primary_range.contains(&selection.head()) {
8949 *active_primary_range.start()
8950 } else {
8951 selection.head()
8952 }
8953 } else {
8954 selection.head()
8955 };
8956 let snapshot = self.snapshot(cx);
8957 loop {
8958 let diagnostics = if direction == Direction::Prev {
8959 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
8960 } else {
8961 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
8962 }
8963 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start));
8964 let group = diagnostics
8965 // relies on diagnostics_in_range to return diagnostics with the same starting range to
8966 // be sorted in a stable way
8967 // skip until we are at current active diagnostic, if it exists
8968 .skip_while(|entry| {
8969 (match direction {
8970 Direction::Prev => entry.range.start >= search_start,
8971 Direction::Next => entry.range.start <= search_start,
8972 }) && self
8973 .active_diagnostics
8974 .as_ref()
8975 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
8976 })
8977 .find_map(|entry| {
8978 if entry.diagnostic.is_primary
8979 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
8980 && !entry.range.is_empty()
8981 // if we match with the active diagnostic, skip it
8982 && Some(entry.diagnostic.group_id)
8983 != self.active_diagnostics.as_ref().map(|d| d.group_id)
8984 {
8985 Some((entry.range, entry.diagnostic.group_id))
8986 } else {
8987 None
8988 }
8989 });
8990
8991 if let Some((primary_range, group_id)) = group {
8992 if self.activate_diagnostics(group_id, cx) {
8993 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8994 s.select(vec![Selection {
8995 id: selection.id,
8996 start: primary_range.start,
8997 end: primary_range.start,
8998 reversed: false,
8999 goal: SelectionGoal::None,
9000 }]);
9001 });
9002 }
9003 break;
9004 } else {
9005 // Cycle around to the start of the buffer, potentially moving back to the start of
9006 // the currently active diagnostic.
9007 active_primary_range.take();
9008 if direction == Direction::Prev {
9009 if search_start == buffer.len() {
9010 break;
9011 } else {
9012 search_start = buffer.len();
9013 }
9014 } else if search_start == 0 {
9015 break;
9016 } else {
9017 search_start = 0;
9018 }
9019 }
9020 }
9021 }
9022
9023 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
9024 let snapshot = self
9025 .display_map
9026 .update(cx, |display_map, cx| display_map.snapshot(cx));
9027 let selection = self.selections.newest::<Point>(cx);
9028
9029 if !self.seek_in_direction(
9030 &snapshot,
9031 selection.head(),
9032 false,
9033 snapshot.buffer_snapshot.git_diff_hunks_in_range(
9034 MultiBufferRow(selection.head().row + 1)..MultiBufferRow::MAX,
9035 ),
9036 cx,
9037 ) {
9038 let wrapped_point = Point::zero();
9039 self.seek_in_direction(
9040 &snapshot,
9041 wrapped_point,
9042 true,
9043 snapshot.buffer_snapshot.git_diff_hunks_in_range(
9044 MultiBufferRow(wrapped_point.row + 1)..MultiBufferRow::MAX,
9045 ),
9046 cx,
9047 );
9048 }
9049 }
9050
9051 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
9052 let snapshot = self
9053 .display_map
9054 .update(cx, |display_map, cx| display_map.snapshot(cx));
9055 let selection = self.selections.newest::<Point>(cx);
9056
9057 if !self.seek_in_direction(
9058 &snapshot,
9059 selection.head(),
9060 false,
9061 snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
9062 MultiBufferRow(0)..MultiBufferRow(selection.head().row),
9063 ),
9064 cx,
9065 ) {
9066 let wrapped_point = snapshot.buffer_snapshot.max_point();
9067 self.seek_in_direction(
9068 &snapshot,
9069 wrapped_point,
9070 true,
9071 snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
9072 MultiBufferRow(0)..MultiBufferRow(wrapped_point.row),
9073 ),
9074 cx,
9075 );
9076 }
9077 }
9078
9079 fn seek_in_direction(
9080 &mut self,
9081 snapshot: &DisplaySnapshot,
9082 initial_point: Point,
9083 is_wrapped: bool,
9084 hunks: impl Iterator<Item = DiffHunk<MultiBufferRow>>,
9085 cx: &mut ViewContext<Editor>,
9086 ) -> bool {
9087 let display_point = initial_point.to_display_point(snapshot);
9088 let mut hunks = hunks
9089 .map(|hunk| diff_hunk_to_display(&hunk, &snapshot))
9090 .filter(|hunk| is_wrapped || !hunk.contains_display_row(display_point.row()))
9091 .dedup();
9092
9093 if let Some(hunk) = hunks.next() {
9094 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9095 let row = hunk.start_display_row();
9096 let point = DisplayPoint::new(row, 0);
9097 s.select_display_ranges([point..point]);
9098 });
9099
9100 true
9101 } else {
9102 false
9103 }
9104 }
9105
9106 pub fn go_to_definition(
9107 &mut self,
9108 _: &GoToDefinition,
9109 cx: &mut ViewContext<Self>,
9110 ) -> Task<Result<Navigated>> {
9111 let definition = self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
9112 let references = self.find_all_references(&FindAllReferences, cx);
9113 cx.background_executor().spawn(async move {
9114 if definition.await? == Navigated::Yes {
9115 return Ok(Navigated::Yes);
9116 }
9117 if let Some(references) = references {
9118 if references.await? == Navigated::Yes {
9119 return Ok(Navigated::Yes);
9120 }
9121 }
9122
9123 Ok(Navigated::No)
9124 })
9125 }
9126
9127 pub fn go_to_declaration(
9128 &mut self,
9129 _: &GoToDeclaration,
9130 cx: &mut ViewContext<Self>,
9131 ) -> Task<Result<Navigated>> {
9132 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, cx)
9133 }
9134
9135 pub fn go_to_declaration_split(
9136 &mut self,
9137 _: &GoToDeclaration,
9138 cx: &mut ViewContext<Self>,
9139 ) -> Task<Result<Navigated>> {
9140 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, cx)
9141 }
9142
9143 pub fn go_to_implementation(
9144 &mut self,
9145 _: &GoToImplementation,
9146 cx: &mut ViewContext<Self>,
9147 ) -> Task<Result<Navigated>> {
9148 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx)
9149 }
9150
9151 pub fn go_to_implementation_split(
9152 &mut self,
9153 _: &GoToImplementationSplit,
9154 cx: &mut ViewContext<Self>,
9155 ) -> Task<Result<Navigated>> {
9156 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx)
9157 }
9158
9159 pub fn go_to_type_definition(
9160 &mut self,
9161 _: &GoToTypeDefinition,
9162 cx: &mut ViewContext<Self>,
9163 ) -> Task<Result<Navigated>> {
9164 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx)
9165 }
9166
9167 pub fn go_to_definition_split(
9168 &mut self,
9169 _: &GoToDefinitionSplit,
9170 cx: &mut ViewContext<Self>,
9171 ) -> Task<Result<Navigated>> {
9172 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx)
9173 }
9174
9175 pub fn go_to_type_definition_split(
9176 &mut self,
9177 _: &GoToTypeDefinitionSplit,
9178 cx: &mut ViewContext<Self>,
9179 ) -> Task<Result<Navigated>> {
9180 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx)
9181 }
9182
9183 fn go_to_definition_of_kind(
9184 &mut self,
9185 kind: GotoDefinitionKind,
9186 split: bool,
9187 cx: &mut ViewContext<Self>,
9188 ) -> Task<Result<Navigated>> {
9189 let Some(workspace) = self.workspace() else {
9190 return Task::ready(Ok(Navigated::No));
9191 };
9192 let buffer = self.buffer.read(cx);
9193 let head = self.selections.newest::<usize>(cx).head();
9194 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
9195 text_anchor
9196 } else {
9197 return Task::ready(Ok(Navigated::No));
9198 };
9199
9200 let project = workspace.read(cx).project().clone();
9201 let definitions = project.update(cx, |project, cx| match kind {
9202 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
9203 GotoDefinitionKind::Declaration => project.declaration(&buffer, head, cx),
9204 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
9205 GotoDefinitionKind::Implementation => project.implementation(&buffer, head, cx),
9206 });
9207
9208 cx.spawn(|editor, mut cx| async move {
9209 let definitions = definitions.await?;
9210 let navigated = editor
9211 .update(&mut cx, |editor, cx| {
9212 editor.navigate_to_hover_links(
9213 Some(kind),
9214 definitions
9215 .into_iter()
9216 .filter(|location| {
9217 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
9218 })
9219 .map(HoverLink::Text)
9220 .collect::<Vec<_>>(),
9221 split,
9222 cx,
9223 )
9224 })?
9225 .await?;
9226 anyhow::Ok(navigated)
9227 })
9228 }
9229
9230 pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext<Self>) {
9231 let position = self.selections.newest_anchor().head();
9232 let Some((buffer, buffer_position)) =
9233 self.buffer.read(cx).text_anchor_for_position(position, cx)
9234 else {
9235 return;
9236 };
9237
9238 cx.spawn(|editor, mut cx| async move {
9239 if let Some((_, url)) = find_url(&buffer, buffer_position, cx.clone()) {
9240 editor.update(&mut cx, |_, cx| {
9241 cx.open_url(&url);
9242 })
9243 } else {
9244 Ok(())
9245 }
9246 })
9247 .detach();
9248 }
9249
9250 pub fn open_file(&mut self, _: &OpenFile, cx: &mut ViewContext<Self>) {
9251 let Some(workspace) = self.workspace() else {
9252 return;
9253 };
9254
9255 let position = self.selections.newest_anchor().head();
9256
9257 let Some((buffer, buffer_position)) =
9258 self.buffer.read(cx).text_anchor_for_position(position, cx)
9259 else {
9260 return;
9261 };
9262
9263 let Some(project) = self.project.clone() else {
9264 return;
9265 };
9266
9267 cx.spawn(|_, mut cx| async move {
9268 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
9269
9270 if let Some((_, path)) = result {
9271 workspace
9272 .update(&mut cx, |workspace, cx| {
9273 workspace.open_resolved_path(path, cx)
9274 })?
9275 .await?;
9276 }
9277 anyhow::Ok(())
9278 })
9279 .detach();
9280 }
9281
9282 pub(crate) fn navigate_to_hover_links(
9283 &mut self,
9284 kind: Option<GotoDefinitionKind>,
9285 mut definitions: Vec<HoverLink>,
9286 split: bool,
9287 cx: &mut ViewContext<Editor>,
9288 ) -> Task<Result<Navigated>> {
9289 // If there is one definition, just open it directly
9290 if definitions.len() == 1 {
9291 let definition = definitions.pop().unwrap();
9292
9293 enum TargetTaskResult {
9294 Location(Option<Location>),
9295 AlreadyNavigated,
9296 }
9297
9298 let target_task = match definition {
9299 HoverLink::Text(link) => {
9300 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
9301 }
9302 HoverLink::InlayHint(lsp_location, server_id) => {
9303 let computation = self.compute_target_location(lsp_location, server_id, cx);
9304 cx.background_executor().spawn(async move {
9305 let location = computation.await?;
9306 Ok(TargetTaskResult::Location(location))
9307 })
9308 }
9309 HoverLink::Url(url) => {
9310 cx.open_url(&url);
9311 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
9312 }
9313 HoverLink::File(path) => {
9314 if let Some(workspace) = self.workspace() {
9315 cx.spawn(|_, mut cx| async move {
9316 workspace
9317 .update(&mut cx, |workspace, cx| {
9318 workspace.open_resolved_path(path, cx)
9319 })?
9320 .await
9321 .map(|_| TargetTaskResult::AlreadyNavigated)
9322 })
9323 } else {
9324 Task::ready(Ok(TargetTaskResult::Location(None)))
9325 }
9326 }
9327 };
9328 cx.spawn(|editor, mut cx| async move {
9329 let target = match target_task.await.context("target resolution task")? {
9330 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
9331 TargetTaskResult::Location(None) => return Ok(Navigated::No),
9332 TargetTaskResult::Location(Some(target)) => target,
9333 };
9334
9335 editor.update(&mut cx, |editor, cx| {
9336 let Some(workspace) = editor.workspace() else {
9337 return Navigated::No;
9338 };
9339 let pane = workspace.read(cx).active_pane().clone();
9340
9341 let range = target.range.to_offset(target.buffer.read(cx));
9342 let range = editor.range_for_match(&range);
9343
9344 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
9345 let buffer = target.buffer.read(cx);
9346 let range = check_multiline_range(buffer, range);
9347 editor.change_selections(Some(Autoscroll::focused()), cx, |s| {
9348 s.select_ranges([range]);
9349 });
9350 } else {
9351 cx.window_context().defer(move |cx| {
9352 let target_editor: View<Self> =
9353 workspace.update(cx, |workspace, cx| {
9354 let pane = if split {
9355 workspace.adjacent_pane(cx)
9356 } else {
9357 workspace.active_pane().clone()
9358 };
9359
9360 workspace.open_project_item(
9361 pane,
9362 target.buffer.clone(),
9363 true,
9364 true,
9365 cx,
9366 )
9367 });
9368 target_editor.update(cx, |target_editor, cx| {
9369 // When selecting a definition in a different buffer, disable the nav history
9370 // to avoid creating a history entry at the previous cursor location.
9371 pane.update(cx, |pane, _| pane.disable_history());
9372 let buffer = target.buffer.read(cx);
9373 let range = check_multiline_range(buffer, range);
9374 target_editor.change_selections(
9375 Some(Autoscroll::focused()),
9376 cx,
9377 |s| {
9378 s.select_ranges([range]);
9379 },
9380 );
9381 pane.update(cx, |pane, _| pane.enable_history());
9382 });
9383 });
9384 }
9385 Navigated::Yes
9386 })
9387 })
9388 } else if !definitions.is_empty() {
9389 let replica_id = self.replica_id(cx);
9390 cx.spawn(|editor, mut cx| async move {
9391 let (title, location_tasks, workspace) = editor
9392 .update(&mut cx, |editor, cx| {
9393 let tab_kind = match kind {
9394 Some(GotoDefinitionKind::Implementation) => "Implementations",
9395 _ => "Definitions",
9396 };
9397 let title = definitions
9398 .iter()
9399 .find_map(|definition| match definition {
9400 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
9401 let buffer = origin.buffer.read(cx);
9402 format!(
9403 "{} for {}",
9404 tab_kind,
9405 buffer
9406 .text_for_range(origin.range.clone())
9407 .collect::<String>()
9408 )
9409 }),
9410 HoverLink::InlayHint(_, _) => None,
9411 HoverLink::Url(_) => None,
9412 HoverLink::File(_) => None,
9413 })
9414 .unwrap_or(tab_kind.to_string());
9415 let location_tasks = definitions
9416 .into_iter()
9417 .map(|definition| match definition {
9418 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
9419 HoverLink::InlayHint(lsp_location, server_id) => {
9420 editor.compute_target_location(lsp_location, server_id, cx)
9421 }
9422 HoverLink::Url(_) => Task::ready(Ok(None)),
9423 HoverLink::File(_) => Task::ready(Ok(None)),
9424 })
9425 .collect::<Vec<_>>();
9426 (title, location_tasks, editor.workspace().clone())
9427 })
9428 .context("location tasks preparation")?;
9429
9430 let locations = futures::future::join_all(location_tasks)
9431 .await
9432 .into_iter()
9433 .filter_map(|location| location.transpose())
9434 .collect::<Result<_>>()
9435 .context("location tasks")?;
9436
9437 let Some(workspace) = workspace else {
9438 return Ok(Navigated::No);
9439 };
9440 let opened = workspace
9441 .update(&mut cx, |workspace, cx| {
9442 Self::open_locations_in_multibuffer(
9443 workspace, locations, replica_id, title, split, cx,
9444 )
9445 })
9446 .ok();
9447
9448 anyhow::Ok(Navigated::from_bool(opened.is_some()))
9449 })
9450 } else {
9451 Task::ready(Ok(Navigated::No))
9452 }
9453 }
9454
9455 fn compute_target_location(
9456 &self,
9457 lsp_location: lsp::Location,
9458 server_id: LanguageServerId,
9459 cx: &mut ViewContext<Editor>,
9460 ) -> Task<anyhow::Result<Option<Location>>> {
9461 let Some(project) = self.project.clone() else {
9462 return Task::Ready(Some(Ok(None)));
9463 };
9464
9465 cx.spawn(move |editor, mut cx| async move {
9466 let location_task = editor.update(&mut cx, |editor, cx| {
9467 project.update(cx, |project, cx| {
9468 let language_server_name =
9469 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
9470 project
9471 .language_server_for_buffer(buffer.read(cx), server_id, cx)
9472 .map(|(lsp_adapter, _)| lsp_adapter.name.clone())
9473 });
9474 language_server_name.map(|language_server_name| {
9475 project.open_local_buffer_via_lsp(
9476 lsp_location.uri.clone(),
9477 server_id,
9478 language_server_name,
9479 cx,
9480 )
9481 })
9482 })
9483 })?;
9484 let location = match location_task {
9485 Some(task) => Some({
9486 let target_buffer_handle = task.await.context("open local buffer")?;
9487 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
9488 let target_start = target_buffer
9489 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
9490 let target_end = target_buffer
9491 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
9492 target_buffer.anchor_after(target_start)
9493 ..target_buffer.anchor_before(target_end)
9494 })?;
9495 Location {
9496 buffer: target_buffer_handle,
9497 range,
9498 }
9499 }),
9500 None => None,
9501 };
9502 Ok(location)
9503 })
9504 }
9505
9506 pub fn find_all_references(
9507 &mut self,
9508 _: &FindAllReferences,
9509 cx: &mut ViewContext<Self>,
9510 ) -> Option<Task<Result<Navigated>>> {
9511 let multi_buffer = self.buffer.read(cx);
9512 let selection = self.selections.newest::<usize>(cx);
9513 let head = selection.head();
9514
9515 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
9516 let head_anchor = multi_buffer_snapshot.anchor_at(
9517 head,
9518 if head < selection.tail() {
9519 Bias::Right
9520 } else {
9521 Bias::Left
9522 },
9523 );
9524
9525 match self
9526 .find_all_references_task_sources
9527 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
9528 {
9529 Ok(_) => {
9530 log::info!(
9531 "Ignoring repeated FindAllReferences invocation with the position of already running task"
9532 );
9533 return None;
9534 }
9535 Err(i) => {
9536 self.find_all_references_task_sources.insert(i, head_anchor);
9537 }
9538 }
9539
9540 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
9541 let replica_id = self.replica_id(cx);
9542 let workspace = self.workspace()?;
9543 let project = workspace.read(cx).project().clone();
9544 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
9545 Some(cx.spawn(|editor, mut cx| async move {
9546 let _cleanup = defer({
9547 let mut cx = cx.clone();
9548 move || {
9549 let _ = editor.update(&mut cx, |editor, _| {
9550 if let Ok(i) =
9551 editor
9552 .find_all_references_task_sources
9553 .binary_search_by(|anchor| {
9554 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
9555 })
9556 {
9557 editor.find_all_references_task_sources.remove(i);
9558 }
9559 });
9560 }
9561 });
9562
9563 let locations = references.await?;
9564 if locations.is_empty() {
9565 return anyhow::Ok(Navigated::No);
9566 }
9567
9568 workspace.update(&mut cx, |workspace, cx| {
9569 let title = locations
9570 .first()
9571 .as_ref()
9572 .map(|location| {
9573 let buffer = location.buffer.read(cx);
9574 format!(
9575 "References to `{}`",
9576 buffer
9577 .text_for_range(location.range.clone())
9578 .collect::<String>()
9579 )
9580 })
9581 .unwrap();
9582 Self::open_locations_in_multibuffer(
9583 workspace, locations, replica_id, title, false, cx,
9584 );
9585 Navigated::Yes
9586 })
9587 }))
9588 }
9589
9590 /// Opens a multibuffer with the given project locations in it
9591 pub fn open_locations_in_multibuffer(
9592 workspace: &mut Workspace,
9593 mut locations: Vec<Location>,
9594 replica_id: ReplicaId,
9595 title: String,
9596 split: bool,
9597 cx: &mut ViewContext<Workspace>,
9598 ) {
9599 // If there are multiple definitions, open them in a multibuffer
9600 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
9601 let mut locations = locations.into_iter().peekable();
9602 let mut ranges_to_highlight = Vec::new();
9603 let capability = workspace.project().read(cx).capability();
9604
9605 let excerpt_buffer = cx.new_model(|cx| {
9606 let mut multibuffer = MultiBuffer::new(replica_id, capability);
9607 while let Some(location) = locations.next() {
9608 let buffer = location.buffer.read(cx);
9609 let mut ranges_for_buffer = Vec::new();
9610 let range = location.range.to_offset(buffer);
9611 ranges_for_buffer.push(range.clone());
9612
9613 while let Some(next_location) = locations.peek() {
9614 if next_location.buffer == location.buffer {
9615 ranges_for_buffer.push(next_location.range.to_offset(buffer));
9616 locations.next();
9617 } else {
9618 break;
9619 }
9620 }
9621
9622 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
9623 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
9624 location.buffer.clone(),
9625 ranges_for_buffer,
9626 DEFAULT_MULTIBUFFER_CONTEXT,
9627 cx,
9628 ))
9629 }
9630
9631 multibuffer.with_title(title)
9632 });
9633
9634 let editor = cx.new_view(|cx| {
9635 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), true, cx)
9636 });
9637 editor.update(cx, |editor, cx| {
9638 if let Some(first_range) = ranges_to_highlight.first() {
9639 editor.change_selections(None, cx, |selections| {
9640 selections.clear_disjoint();
9641 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
9642 });
9643 }
9644 editor.highlight_background::<Self>(
9645 &ranges_to_highlight,
9646 |theme| theme.editor_highlighted_line_background,
9647 cx,
9648 );
9649 });
9650
9651 let item = Box::new(editor);
9652 let item_id = item.item_id();
9653
9654 if split {
9655 workspace.split_item(SplitDirection::Right, item.clone(), cx);
9656 } else {
9657 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
9658 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
9659 pane.close_current_preview_item(cx)
9660 } else {
9661 None
9662 }
9663 });
9664 workspace.add_item_to_active_pane(item.clone(), destination_index, true, cx);
9665 }
9666 workspace.active_pane().update(cx, |pane, cx| {
9667 pane.set_preview_item_id(Some(item_id), cx);
9668 });
9669 }
9670
9671 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9672 use language::ToOffset as _;
9673
9674 let project = self.project.clone()?;
9675 let selection = self.selections.newest_anchor().clone();
9676 let (cursor_buffer, cursor_buffer_position) = self
9677 .buffer
9678 .read(cx)
9679 .text_anchor_for_position(selection.head(), cx)?;
9680 let (tail_buffer, cursor_buffer_position_end) = self
9681 .buffer
9682 .read(cx)
9683 .text_anchor_for_position(selection.tail(), cx)?;
9684 if tail_buffer != cursor_buffer {
9685 return None;
9686 }
9687
9688 let snapshot = cursor_buffer.read(cx).snapshot();
9689 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
9690 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
9691 let prepare_rename = project.update(cx, |project, cx| {
9692 project.prepare_rename(cursor_buffer.clone(), cursor_buffer_offset, cx)
9693 });
9694 drop(snapshot);
9695
9696 Some(cx.spawn(|this, mut cx| async move {
9697 let rename_range = if let Some(range) = prepare_rename.await? {
9698 Some(range)
9699 } else {
9700 this.update(&mut cx, |this, cx| {
9701 let buffer = this.buffer.read(cx).snapshot(cx);
9702 let mut buffer_highlights = this
9703 .document_highlights_for_position(selection.head(), &buffer)
9704 .filter(|highlight| {
9705 highlight.start.excerpt_id == selection.head().excerpt_id
9706 && highlight.end.excerpt_id == selection.head().excerpt_id
9707 });
9708 buffer_highlights
9709 .next()
9710 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
9711 })?
9712 };
9713 if let Some(rename_range) = rename_range {
9714 this.update(&mut cx, |this, cx| {
9715 let snapshot = cursor_buffer.read(cx).snapshot();
9716 let rename_buffer_range = rename_range.to_offset(&snapshot);
9717 let cursor_offset_in_rename_range =
9718 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
9719 let cursor_offset_in_rename_range_end =
9720 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
9721
9722 this.take_rename(false, cx);
9723 let buffer = this.buffer.read(cx).read(cx);
9724 let cursor_offset = selection.head().to_offset(&buffer);
9725 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
9726 let rename_end = rename_start + rename_buffer_range.len();
9727 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
9728 let mut old_highlight_id = None;
9729 let old_name: Arc<str> = buffer
9730 .chunks(rename_start..rename_end, true)
9731 .map(|chunk| {
9732 if old_highlight_id.is_none() {
9733 old_highlight_id = chunk.syntax_highlight_id;
9734 }
9735 chunk.text
9736 })
9737 .collect::<String>()
9738 .into();
9739
9740 drop(buffer);
9741
9742 // Position the selection in the rename editor so that it matches the current selection.
9743 this.show_local_selections = false;
9744 let rename_editor = cx.new_view(|cx| {
9745 let mut editor = Editor::single_line(cx);
9746 editor.buffer.update(cx, |buffer, cx| {
9747 buffer.edit([(0..0, old_name.clone())], None, cx)
9748 });
9749 let rename_selection_range = match cursor_offset_in_rename_range
9750 .cmp(&cursor_offset_in_rename_range_end)
9751 {
9752 Ordering::Equal => {
9753 editor.select_all(&SelectAll, cx);
9754 return editor;
9755 }
9756 Ordering::Less => {
9757 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
9758 }
9759 Ordering::Greater => {
9760 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
9761 }
9762 };
9763 if rename_selection_range.end > old_name.len() {
9764 editor.select_all(&SelectAll, cx);
9765 } else {
9766 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
9767 s.select_ranges([rename_selection_range]);
9768 });
9769 }
9770 editor
9771 });
9772 cx.subscribe(&rename_editor, |_, _, e, cx| match e {
9773 EditorEvent::Focused => cx.emit(EditorEvent::FocusedIn),
9774 _ => {}
9775 })
9776 .detach();
9777
9778 let write_highlights =
9779 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
9780 let read_highlights =
9781 this.clear_background_highlights::<DocumentHighlightRead>(cx);
9782 let ranges = write_highlights
9783 .iter()
9784 .flat_map(|(_, ranges)| ranges.iter())
9785 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
9786 .cloned()
9787 .collect();
9788
9789 this.highlight_text::<Rename>(
9790 ranges,
9791 HighlightStyle {
9792 fade_out: Some(0.6),
9793 ..Default::default()
9794 },
9795 cx,
9796 );
9797 let rename_focus_handle = rename_editor.focus_handle(cx);
9798 cx.focus(&rename_focus_handle);
9799 let block_id = this.insert_blocks(
9800 [BlockProperties {
9801 style: BlockStyle::Flex,
9802 position: range.start,
9803 height: 1,
9804 render: Box::new({
9805 let rename_editor = rename_editor.clone();
9806 move |cx: &mut BlockContext| {
9807 let mut text_style = cx.editor_style.text.clone();
9808 if let Some(highlight_style) = old_highlight_id
9809 .and_then(|h| h.style(&cx.editor_style.syntax))
9810 {
9811 text_style = text_style.highlight(highlight_style);
9812 }
9813 div()
9814 .pl(cx.anchor_x)
9815 .child(EditorElement::new(
9816 &rename_editor,
9817 EditorStyle {
9818 background: cx.theme().system().transparent,
9819 local_player: cx.editor_style.local_player,
9820 text: text_style,
9821 scrollbar_width: cx.editor_style.scrollbar_width,
9822 syntax: cx.editor_style.syntax.clone(),
9823 status: cx.editor_style.status.clone(),
9824 inlay_hints_style: HighlightStyle {
9825 color: Some(cx.theme().status().hint),
9826 font_weight: Some(FontWeight::BOLD),
9827 ..HighlightStyle::default()
9828 },
9829 suggestions_style: HighlightStyle {
9830 color: Some(cx.theme().status().predictive),
9831 ..HighlightStyle::default()
9832 },
9833 ..EditorStyle::default()
9834 },
9835 ))
9836 .into_any_element()
9837 }
9838 }),
9839 disposition: BlockDisposition::Below,
9840 priority: 0,
9841 }],
9842 Some(Autoscroll::fit()),
9843 cx,
9844 )[0];
9845 this.pending_rename = Some(RenameState {
9846 range,
9847 old_name,
9848 editor: rename_editor,
9849 block_id,
9850 });
9851 })?;
9852 }
9853
9854 Ok(())
9855 }))
9856 }
9857
9858 pub fn confirm_rename(
9859 &mut self,
9860 _: &ConfirmRename,
9861 cx: &mut ViewContext<Self>,
9862 ) -> Option<Task<Result<()>>> {
9863 let rename = self.take_rename(false, cx)?;
9864 let workspace = self.workspace()?;
9865 let (start_buffer, start) = self
9866 .buffer
9867 .read(cx)
9868 .text_anchor_for_position(rename.range.start, cx)?;
9869 let (end_buffer, end) = self
9870 .buffer
9871 .read(cx)
9872 .text_anchor_for_position(rename.range.end, cx)?;
9873 if start_buffer != end_buffer {
9874 return None;
9875 }
9876
9877 let buffer = start_buffer;
9878 let range = start..end;
9879 let old_name = rename.old_name;
9880 let new_name = rename.editor.read(cx).text(cx);
9881
9882 let rename = workspace
9883 .read(cx)
9884 .project()
9885 .clone()
9886 .update(cx, |project, cx| {
9887 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
9888 });
9889 let workspace = workspace.downgrade();
9890
9891 Some(cx.spawn(|editor, mut cx| async move {
9892 let project_transaction = rename.await?;
9893 Self::open_project_transaction(
9894 &editor,
9895 workspace,
9896 project_transaction,
9897 format!("Rename: {} → {}", old_name, new_name),
9898 cx.clone(),
9899 )
9900 .await?;
9901
9902 editor.update(&mut cx, |editor, cx| {
9903 editor.refresh_document_highlights(cx);
9904 })?;
9905 Ok(())
9906 }))
9907 }
9908
9909 fn take_rename(
9910 &mut self,
9911 moving_cursor: bool,
9912 cx: &mut ViewContext<Self>,
9913 ) -> Option<RenameState> {
9914 let rename = self.pending_rename.take()?;
9915 if rename.editor.focus_handle(cx).is_focused(cx) {
9916 cx.focus(&self.focus_handle);
9917 }
9918
9919 self.remove_blocks(
9920 [rename.block_id].into_iter().collect(),
9921 Some(Autoscroll::fit()),
9922 cx,
9923 );
9924 self.clear_highlights::<Rename>(cx);
9925 self.show_local_selections = true;
9926
9927 if moving_cursor {
9928 let rename_editor = rename.editor.read(cx);
9929 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
9930
9931 // Update the selection to match the position of the selection inside
9932 // the rename editor.
9933 let snapshot = self.buffer.read(cx).read(cx);
9934 let rename_range = rename.range.to_offset(&snapshot);
9935 let cursor_in_editor = snapshot
9936 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
9937 .min(rename_range.end);
9938 drop(snapshot);
9939
9940 self.change_selections(None, cx, |s| {
9941 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
9942 });
9943 } else {
9944 self.refresh_document_highlights(cx);
9945 }
9946
9947 Some(rename)
9948 }
9949
9950 pub fn pending_rename(&self) -> Option<&RenameState> {
9951 self.pending_rename.as_ref()
9952 }
9953
9954 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9955 let project = match &self.project {
9956 Some(project) => project.clone(),
9957 None => return None,
9958 };
9959
9960 Some(self.perform_format(project, FormatTrigger::Manual, cx))
9961 }
9962
9963 fn perform_format(
9964 &mut self,
9965 project: Model<Project>,
9966 trigger: FormatTrigger,
9967 cx: &mut ViewContext<Self>,
9968 ) -> Task<Result<()>> {
9969 let buffer = self.buffer().clone();
9970 let mut buffers = buffer.read(cx).all_buffers();
9971 if trigger == FormatTrigger::Save {
9972 buffers.retain(|buffer| buffer.read(cx).is_dirty());
9973 }
9974
9975 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
9976 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
9977
9978 cx.spawn(|_, mut cx| async move {
9979 let transaction = futures::select_biased! {
9980 () = timeout => {
9981 log::warn!("timed out waiting for formatting");
9982 None
9983 }
9984 transaction = format.log_err().fuse() => transaction,
9985 };
9986
9987 buffer
9988 .update(&mut cx, |buffer, cx| {
9989 if let Some(transaction) = transaction {
9990 if !buffer.is_singleton() {
9991 buffer.push_transaction(&transaction.0, cx);
9992 }
9993 }
9994
9995 cx.notify();
9996 })
9997 .ok();
9998
9999 Ok(())
10000 })
10001 }
10002
10003 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
10004 if let Some(project) = self.project.clone() {
10005 self.buffer.update(cx, |multi_buffer, cx| {
10006 project.update(cx, |project, cx| {
10007 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
10008 });
10009 })
10010 }
10011 }
10012
10013 fn cancel_language_server_work(
10014 &mut self,
10015 _: &CancelLanguageServerWork,
10016 cx: &mut ViewContext<Self>,
10017 ) {
10018 if let Some(project) = self.project.clone() {
10019 self.buffer.update(cx, |multi_buffer, cx| {
10020 project.update(cx, |project, cx| {
10021 project.cancel_language_server_work_for_buffers(multi_buffer.all_buffers(), cx);
10022 });
10023 })
10024 }
10025 }
10026
10027 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
10028 cx.show_character_palette();
10029 }
10030
10031 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
10032 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
10033 let buffer = self.buffer.read(cx).snapshot(cx);
10034 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
10035 let is_valid = buffer
10036 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
10037 .any(|entry| {
10038 entry.diagnostic.is_primary
10039 && !entry.range.is_empty()
10040 && entry.range.start == primary_range_start
10041 && entry.diagnostic.message == active_diagnostics.primary_message
10042 });
10043
10044 if is_valid != active_diagnostics.is_valid {
10045 active_diagnostics.is_valid = is_valid;
10046 let mut new_styles = HashMap::default();
10047 for (block_id, diagnostic) in &active_diagnostics.blocks {
10048 new_styles.insert(
10049 *block_id,
10050 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
10051 );
10052 }
10053 self.display_map.update(cx, |display_map, _cx| {
10054 display_map.replace_blocks(new_styles)
10055 });
10056 }
10057 }
10058 }
10059
10060 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
10061 self.dismiss_diagnostics(cx);
10062 let snapshot = self.snapshot(cx);
10063 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
10064 let buffer = self.buffer.read(cx).snapshot(cx);
10065
10066 let mut primary_range = None;
10067 let mut primary_message = None;
10068 let mut group_end = Point::zero();
10069 let diagnostic_group = buffer
10070 .diagnostic_group::<MultiBufferPoint>(group_id)
10071 .filter_map(|entry| {
10072 if snapshot.is_line_folded(MultiBufferRow(entry.range.start.row))
10073 && (entry.range.start.row == entry.range.end.row
10074 || snapshot.is_line_folded(MultiBufferRow(entry.range.end.row)))
10075 {
10076 return None;
10077 }
10078 if entry.range.end > group_end {
10079 group_end = entry.range.end;
10080 }
10081 if entry.diagnostic.is_primary {
10082 primary_range = Some(entry.range.clone());
10083 primary_message = Some(entry.diagnostic.message.clone());
10084 }
10085 Some(entry)
10086 })
10087 .collect::<Vec<_>>();
10088 let primary_range = primary_range?;
10089 let primary_message = primary_message?;
10090 let primary_range =
10091 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
10092
10093 let blocks = display_map
10094 .insert_blocks(
10095 diagnostic_group.iter().map(|entry| {
10096 let diagnostic = entry.diagnostic.clone();
10097 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
10098 BlockProperties {
10099 style: BlockStyle::Fixed,
10100 position: buffer.anchor_after(entry.range.start),
10101 height: message_height,
10102 render: diagnostic_block_renderer(diagnostic, None, true, true),
10103 disposition: BlockDisposition::Below,
10104 priority: 0,
10105 }
10106 }),
10107 cx,
10108 )
10109 .into_iter()
10110 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
10111 .collect();
10112
10113 Some(ActiveDiagnosticGroup {
10114 primary_range,
10115 primary_message,
10116 group_id,
10117 blocks,
10118 is_valid: true,
10119 })
10120 });
10121 self.active_diagnostics.is_some()
10122 }
10123
10124 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
10125 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
10126 self.display_map.update(cx, |display_map, cx| {
10127 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
10128 });
10129 cx.notify();
10130 }
10131 }
10132
10133 pub fn set_selections_from_remote(
10134 &mut self,
10135 selections: Vec<Selection<Anchor>>,
10136 pending_selection: Option<Selection<Anchor>>,
10137 cx: &mut ViewContext<Self>,
10138 ) {
10139 let old_cursor_position = self.selections.newest_anchor().head();
10140 self.selections.change_with(cx, |s| {
10141 s.select_anchors(selections);
10142 if let Some(pending_selection) = pending_selection {
10143 s.set_pending(pending_selection, SelectMode::Character);
10144 } else {
10145 s.clear_pending();
10146 }
10147 });
10148 self.selections_did_change(false, &old_cursor_position, true, cx);
10149 }
10150
10151 fn push_to_selection_history(&mut self) {
10152 self.selection_history.push(SelectionHistoryEntry {
10153 selections: self.selections.disjoint_anchors(),
10154 select_next_state: self.select_next_state.clone(),
10155 select_prev_state: self.select_prev_state.clone(),
10156 add_selections_state: self.add_selections_state.clone(),
10157 });
10158 }
10159
10160 pub fn transact(
10161 &mut self,
10162 cx: &mut ViewContext<Self>,
10163 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
10164 ) -> Option<TransactionId> {
10165 self.start_transaction_at(Instant::now(), cx);
10166 update(self, cx);
10167 self.end_transaction_at(Instant::now(), cx)
10168 }
10169
10170 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
10171 self.end_selection(cx);
10172 if let Some(tx_id) = self
10173 .buffer
10174 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
10175 {
10176 self.selection_history
10177 .insert_transaction(tx_id, self.selections.disjoint_anchors());
10178 cx.emit(EditorEvent::TransactionBegun {
10179 transaction_id: tx_id,
10180 })
10181 }
10182 }
10183
10184 fn end_transaction_at(
10185 &mut self,
10186 now: Instant,
10187 cx: &mut ViewContext<Self>,
10188 ) -> Option<TransactionId> {
10189 if let Some(transaction_id) = self
10190 .buffer
10191 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
10192 {
10193 if let Some((_, end_selections)) =
10194 self.selection_history.transaction_mut(transaction_id)
10195 {
10196 *end_selections = Some(self.selections.disjoint_anchors());
10197 } else {
10198 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
10199 }
10200
10201 cx.emit(EditorEvent::Edited { transaction_id });
10202 Some(transaction_id)
10203 } else {
10204 None
10205 }
10206 }
10207
10208 pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
10209 let mut fold_ranges = Vec::new();
10210
10211 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10212
10213 let selections = self.selections.all_adjusted(cx);
10214 for selection in selections {
10215 let range = selection.range().sorted();
10216 let buffer_start_row = range.start.row;
10217
10218 for row in (0..=range.end.row).rev() {
10219 if let Some((foldable_range, fold_text)) =
10220 display_map.foldable_range(MultiBufferRow(row))
10221 {
10222 if foldable_range.end.row >= buffer_start_row {
10223 fold_ranges.push((foldable_range, fold_text));
10224 if row <= range.start.row {
10225 break;
10226 }
10227 }
10228 }
10229 }
10230 }
10231
10232 self.fold_ranges(fold_ranges, true, cx);
10233 }
10234
10235 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
10236 let buffer_row = fold_at.buffer_row;
10237 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10238
10239 if let Some((fold_range, placeholder)) = display_map.foldable_range(buffer_row) {
10240 let autoscroll = self
10241 .selections
10242 .all::<Point>(cx)
10243 .iter()
10244 .any(|selection| fold_range.overlaps(&selection.range()));
10245
10246 self.fold_ranges([(fold_range, placeholder)], autoscroll, cx);
10247 }
10248 }
10249
10250 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
10251 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10252 let buffer = &display_map.buffer_snapshot;
10253 let selections = self.selections.all::<Point>(cx);
10254 let ranges = selections
10255 .iter()
10256 .map(|s| {
10257 let range = s.display_range(&display_map).sorted();
10258 let mut start = range.start.to_point(&display_map);
10259 let mut end = range.end.to_point(&display_map);
10260 start.column = 0;
10261 end.column = buffer.line_len(MultiBufferRow(end.row));
10262 start..end
10263 })
10264 .collect::<Vec<_>>();
10265
10266 self.unfold_ranges(ranges, true, true, cx);
10267 }
10268
10269 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
10270 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10271
10272 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
10273 ..Point::new(
10274 unfold_at.buffer_row.0,
10275 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
10276 );
10277
10278 let autoscroll = self
10279 .selections
10280 .all::<Point>(cx)
10281 .iter()
10282 .any(|selection| selection.range().overlaps(&intersection_range));
10283
10284 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
10285 }
10286
10287 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
10288 let selections = self.selections.all::<Point>(cx);
10289 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10290 let line_mode = self.selections.line_mode;
10291 let ranges = selections.into_iter().map(|s| {
10292 if line_mode {
10293 let start = Point::new(s.start.row, 0);
10294 let end = Point::new(
10295 s.end.row,
10296 display_map
10297 .buffer_snapshot
10298 .line_len(MultiBufferRow(s.end.row)),
10299 );
10300 (start..end, display_map.fold_placeholder.clone())
10301 } else {
10302 (s.start..s.end, display_map.fold_placeholder.clone())
10303 }
10304 });
10305 self.fold_ranges(ranges, true, cx);
10306 }
10307
10308 pub fn fold_ranges<T: ToOffset + Clone>(
10309 &mut self,
10310 ranges: impl IntoIterator<Item = (Range<T>, FoldPlaceholder)>,
10311 auto_scroll: bool,
10312 cx: &mut ViewContext<Self>,
10313 ) {
10314 let mut fold_ranges = Vec::new();
10315 let mut buffers_affected = HashMap::default();
10316 let multi_buffer = self.buffer().read(cx);
10317 for (fold_range, fold_text) in ranges {
10318 if let Some((_, buffer, _)) =
10319 multi_buffer.excerpt_containing(fold_range.start.clone(), cx)
10320 {
10321 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
10322 };
10323 fold_ranges.push((fold_range, fold_text));
10324 }
10325
10326 let mut ranges = fold_ranges.into_iter().peekable();
10327 if ranges.peek().is_some() {
10328 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
10329
10330 if auto_scroll {
10331 self.request_autoscroll(Autoscroll::fit(), cx);
10332 }
10333
10334 for buffer in buffers_affected.into_values() {
10335 self.sync_expanded_diff_hunks(buffer, cx);
10336 }
10337
10338 cx.notify();
10339
10340 if let Some(active_diagnostics) = self.active_diagnostics.take() {
10341 // Clear diagnostics block when folding a range that contains it.
10342 let snapshot = self.snapshot(cx);
10343 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
10344 drop(snapshot);
10345 self.active_diagnostics = Some(active_diagnostics);
10346 self.dismiss_diagnostics(cx);
10347 } else {
10348 self.active_diagnostics = Some(active_diagnostics);
10349 }
10350 }
10351
10352 self.scrollbar_marker_state.dirty = true;
10353 }
10354 }
10355
10356 pub fn unfold_ranges<T: ToOffset + Clone>(
10357 &mut self,
10358 ranges: impl IntoIterator<Item = Range<T>>,
10359 inclusive: bool,
10360 auto_scroll: bool,
10361 cx: &mut ViewContext<Self>,
10362 ) {
10363 let mut unfold_ranges = Vec::new();
10364 let mut buffers_affected = HashMap::default();
10365 let multi_buffer = self.buffer().read(cx);
10366 for range in ranges {
10367 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
10368 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
10369 };
10370 unfold_ranges.push(range);
10371 }
10372
10373 let mut ranges = unfold_ranges.into_iter().peekable();
10374 if ranges.peek().is_some() {
10375 self.display_map
10376 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
10377 if auto_scroll {
10378 self.request_autoscroll(Autoscroll::fit(), cx);
10379 }
10380
10381 for buffer in buffers_affected.into_values() {
10382 self.sync_expanded_diff_hunks(buffer, cx);
10383 }
10384
10385 cx.notify();
10386 self.scrollbar_marker_state.dirty = true;
10387 self.active_indent_guides_state.dirty = true;
10388 }
10389 }
10390
10391 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
10392 if hovered != self.gutter_hovered {
10393 self.gutter_hovered = hovered;
10394 cx.notify();
10395 }
10396 }
10397
10398 pub fn insert_blocks(
10399 &mut self,
10400 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
10401 autoscroll: Option<Autoscroll>,
10402 cx: &mut ViewContext<Self>,
10403 ) -> Vec<CustomBlockId> {
10404 let blocks = self
10405 .display_map
10406 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
10407 if let Some(autoscroll) = autoscroll {
10408 self.request_autoscroll(autoscroll, cx);
10409 }
10410 cx.notify();
10411 blocks
10412 }
10413
10414 pub fn resize_blocks(
10415 &mut self,
10416 heights: HashMap<CustomBlockId, u32>,
10417 autoscroll: Option<Autoscroll>,
10418 cx: &mut ViewContext<Self>,
10419 ) {
10420 self.display_map
10421 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
10422 if let Some(autoscroll) = autoscroll {
10423 self.request_autoscroll(autoscroll, cx);
10424 }
10425 cx.notify();
10426 }
10427
10428 pub fn replace_blocks(
10429 &mut self,
10430 renderers: HashMap<CustomBlockId, RenderBlock>,
10431 autoscroll: Option<Autoscroll>,
10432 cx: &mut ViewContext<Self>,
10433 ) {
10434 self.display_map
10435 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
10436 if let Some(autoscroll) = autoscroll {
10437 self.request_autoscroll(autoscroll, cx);
10438 }
10439 cx.notify();
10440 }
10441
10442 pub fn remove_blocks(
10443 &mut self,
10444 block_ids: HashSet<CustomBlockId>,
10445 autoscroll: Option<Autoscroll>,
10446 cx: &mut ViewContext<Self>,
10447 ) {
10448 self.display_map.update(cx, |display_map, cx| {
10449 display_map.remove_blocks(block_ids, cx)
10450 });
10451 if let Some(autoscroll) = autoscroll {
10452 self.request_autoscroll(autoscroll, cx);
10453 }
10454 cx.notify();
10455 }
10456
10457 pub fn row_for_block(
10458 &self,
10459 block_id: CustomBlockId,
10460 cx: &mut ViewContext<Self>,
10461 ) -> Option<DisplayRow> {
10462 self.display_map
10463 .update(cx, |map, cx| map.row_for_block(block_id, cx))
10464 }
10465
10466 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
10467 self.focused_block = Some(focused_block);
10468 }
10469
10470 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
10471 self.focused_block.take()
10472 }
10473
10474 pub fn insert_creases(
10475 &mut self,
10476 creases: impl IntoIterator<Item = Crease>,
10477 cx: &mut ViewContext<Self>,
10478 ) -> Vec<CreaseId> {
10479 self.display_map
10480 .update(cx, |map, cx| map.insert_creases(creases, cx))
10481 }
10482
10483 pub fn remove_creases(
10484 &mut self,
10485 ids: impl IntoIterator<Item = CreaseId>,
10486 cx: &mut ViewContext<Self>,
10487 ) {
10488 self.display_map
10489 .update(cx, |map, cx| map.remove_creases(ids, cx));
10490 }
10491
10492 pub fn longest_row(&self, cx: &mut AppContext) -> DisplayRow {
10493 self.display_map
10494 .update(cx, |map, cx| map.snapshot(cx))
10495 .longest_row()
10496 }
10497
10498 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
10499 self.display_map
10500 .update(cx, |map, cx| map.snapshot(cx))
10501 .max_point()
10502 }
10503
10504 pub fn text(&self, cx: &AppContext) -> String {
10505 self.buffer.read(cx).read(cx).text()
10506 }
10507
10508 pub fn text_option(&self, cx: &AppContext) -> Option<String> {
10509 let text = self.text(cx);
10510 let text = text.trim();
10511
10512 if text.is_empty() {
10513 return None;
10514 }
10515
10516 Some(text.to_string())
10517 }
10518
10519 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
10520 self.transact(cx, |this, cx| {
10521 this.buffer
10522 .read(cx)
10523 .as_singleton()
10524 .expect("you can only call set_text on editors for singleton buffers")
10525 .update(cx, |buffer, cx| buffer.set_text(text, cx));
10526 });
10527 }
10528
10529 pub fn display_text(&self, cx: &mut AppContext) -> String {
10530 self.display_map
10531 .update(cx, |map, cx| map.snapshot(cx))
10532 .text()
10533 }
10534
10535 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
10536 let mut wrap_guides = smallvec::smallvec![];
10537
10538 if self.show_wrap_guides == Some(false) {
10539 return wrap_guides;
10540 }
10541
10542 let settings = self.buffer.read(cx).settings_at(0, cx);
10543 if settings.show_wrap_guides {
10544 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
10545 wrap_guides.push((soft_wrap as usize, true));
10546 } else if let SoftWrap::Bounded(soft_wrap) = self.soft_wrap_mode(cx) {
10547 wrap_guides.push((soft_wrap as usize, true));
10548 }
10549 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
10550 }
10551
10552 wrap_guides
10553 }
10554
10555 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
10556 let settings = self.buffer.read(cx).settings_at(0, cx);
10557 let mode = self
10558 .soft_wrap_mode_override
10559 .unwrap_or_else(|| settings.soft_wrap);
10560 match mode {
10561 language_settings::SoftWrap::None => SoftWrap::None,
10562 language_settings::SoftWrap::PreferLine => SoftWrap::PreferLine,
10563 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
10564 language_settings::SoftWrap::PreferredLineLength => {
10565 SoftWrap::Column(settings.preferred_line_length)
10566 }
10567 language_settings::SoftWrap::Bounded => {
10568 SoftWrap::Bounded(settings.preferred_line_length)
10569 }
10570 }
10571 }
10572
10573 pub fn set_soft_wrap_mode(
10574 &mut self,
10575 mode: language_settings::SoftWrap,
10576 cx: &mut ViewContext<Self>,
10577 ) {
10578 self.soft_wrap_mode_override = Some(mode);
10579 cx.notify();
10580 }
10581
10582 pub fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
10583 let rem_size = cx.rem_size();
10584 self.display_map.update(cx, |map, cx| {
10585 map.set_font(
10586 style.text.font(),
10587 style.text.font_size.to_pixels(rem_size),
10588 cx,
10589 )
10590 });
10591 self.style = Some(style);
10592 }
10593
10594 pub fn style(&self) -> Option<&EditorStyle> {
10595 self.style.as_ref()
10596 }
10597
10598 // Called by the element. This method is not designed to be called outside of the editor
10599 // element's layout code because it does not notify when rewrapping is computed synchronously.
10600 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
10601 self.display_map
10602 .update(cx, |map, cx| map.set_wrap_width(width, cx))
10603 }
10604
10605 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
10606 if self.soft_wrap_mode_override.is_some() {
10607 self.soft_wrap_mode_override.take();
10608 } else {
10609 let soft_wrap = match self.soft_wrap_mode(cx) {
10610 SoftWrap::None | SoftWrap::PreferLine => language_settings::SoftWrap::EditorWidth,
10611 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
10612 language_settings::SoftWrap::PreferLine
10613 }
10614 };
10615 self.soft_wrap_mode_override = Some(soft_wrap);
10616 }
10617 cx.notify();
10618 }
10619
10620 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, cx: &mut ViewContext<Self>) {
10621 let Some(workspace) = self.workspace() else {
10622 return;
10623 };
10624 let fs = workspace.read(cx).app_state().fs.clone();
10625 let current_show = TabBarSettings::get_global(cx).show;
10626 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
10627 setting.show = Some(!current_show);
10628 });
10629 }
10630
10631 pub fn toggle_indent_guides(&mut self, _: &ToggleIndentGuides, cx: &mut ViewContext<Self>) {
10632 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
10633 self.buffer
10634 .read(cx)
10635 .settings_at(0, cx)
10636 .indent_guides
10637 .enabled
10638 });
10639 self.show_indent_guides = Some(!currently_enabled);
10640 cx.notify();
10641 }
10642
10643 fn should_show_indent_guides(&self) -> Option<bool> {
10644 self.show_indent_guides
10645 }
10646
10647 pub fn toggle_line_numbers(&mut self, _: &ToggleLineNumbers, cx: &mut ViewContext<Self>) {
10648 let mut editor_settings = EditorSettings::get_global(cx).clone();
10649 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
10650 EditorSettings::override_global(editor_settings, cx);
10651 }
10652
10653 pub fn should_use_relative_line_numbers(&self, cx: &WindowContext) -> bool {
10654 self.use_relative_line_numbers
10655 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
10656 }
10657
10658 pub fn toggle_relative_line_numbers(
10659 &mut self,
10660 _: &ToggleRelativeLineNumbers,
10661 cx: &mut ViewContext<Self>,
10662 ) {
10663 let is_relative = self.should_use_relative_line_numbers(cx);
10664 self.set_relative_line_number(Some(!is_relative), cx)
10665 }
10666
10667 pub fn set_relative_line_number(
10668 &mut self,
10669 is_relative: Option<bool>,
10670 cx: &mut ViewContext<Self>,
10671 ) {
10672 self.use_relative_line_numbers = is_relative;
10673 cx.notify();
10674 }
10675
10676 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
10677 self.show_gutter = show_gutter;
10678 cx.notify();
10679 }
10680
10681 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut ViewContext<Self>) {
10682 self.show_line_numbers = Some(show_line_numbers);
10683 cx.notify();
10684 }
10685
10686 pub fn set_show_git_diff_gutter(
10687 &mut self,
10688 show_git_diff_gutter: bool,
10689 cx: &mut ViewContext<Self>,
10690 ) {
10691 self.show_git_diff_gutter = Some(show_git_diff_gutter);
10692 cx.notify();
10693 }
10694
10695 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut ViewContext<Self>) {
10696 self.show_code_actions = Some(show_code_actions);
10697 cx.notify();
10698 }
10699
10700 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut ViewContext<Self>) {
10701 self.show_runnables = Some(show_runnables);
10702 cx.notify();
10703 }
10704
10705 pub fn set_masked(&mut self, masked: bool, cx: &mut ViewContext<Self>) {
10706 if self.display_map.read(cx).masked != masked {
10707 self.display_map.update(cx, |map, _| map.masked = masked);
10708 }
10709 cx.notify()
10710 }
10711
10712 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut ViewContext<Self>) {
10713 self.show_wrap_guides = Some(show_wrap_guides);
10714 cx.notify();
10715 }
10716
10717 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut ViewContext<Self>) {
10718 self.show_indent_guides = Some(show_indent_guides);
10719 cx.notify();
10720 }
10721
10722 pub fn working_directory(&self, cx: &WindowContext) -> Option<PathBuf> {
10723 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10724 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10725 if let Some(dir) = file.abs_path(cx).parent() {
10726 return Some(dir.to_owned());
10727 }
10728 }
10729
10730 if let Some(project_path) = buffer.read(cx).project_path(cx) {
10731 return Some(project_path.path.to_path_buf());
10732 }
10733 }
10734
10735 None
10736 }
10737
10738 pub fn reveal_in_finder(&mut self, _: &RevealInFileManager, cx: &mut ViewContext<Self>) {
10739 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10740 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10741 cx.reveal_path(&file.abs_path(cx));
10742 }
10743 }
10744 }
10745
10746 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
10747 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10748 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10749 if let Some(path) = file.abs_path(cx).to_str() {
10750 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
10751 }
10752 }
10753 }
10754 }
10755
10756 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
10757 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10758 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10759 if let Some(path) = file.path().to_str() {
10760 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
10761 }
10762 }
10763 }
10764 }
10765
10766 pub fn toggle_git_blame(&mut self, _: &ToggleGitBlame, cx: &mut ViewContext<Self>) {
10767 self.show_git_blame_gutter = !self.show_git_blame_gutter;
10768
10769 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
10770 self.start_git_blame(true, cx);
10771 }
10772
10773 cx.notify();
10774 }
10775
10776 pub fn toggle_git_blame_inline(
10777 &mut self,
10778 _: &ToggleGitBlameInline,
10779 cx: &mut ViewContext<Self>,
10780 ) {
10781 self.toggle_git_blame_inline_internal(true, cx);
10782 cx.notify();
10783 }
10784
10785 pub fn git_blame_inline_enabled(&self) -> bool {
10786 self.git_blame_inline_enabled
10787 }
10788
10789 pub fn toggle_selection_menu(&mut self, _: &ToggleSelectionMenu, cx: &mut ViewContext<Self>) {
10790 self.show_selection_menu = self
10791 .show_selection_menu
10792 .map(|show_selections_menu| !show_selections_menu)
10793 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
10794
10795 cx.notify();
10796 }
10797
10798 pub fn selection_menu_enabled(&self, cx: &AppContext) -> bool {
10799 self.show_selection_menu
10800 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
10801 }
10802
10803 fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
10804 if let Some(project) = self.project.as_ref() {
10805 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
10806 return;
10807 };
10808
10809 if buffer.read(cx).file().is_none() {
10810 return;
10811 }
10812
10813 let focused = self.focus_handle(cx).contains_focused(cx);
10814
10815 let project = project.clone();
10816 let blame =
10817 cx.new_model(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
10818 self.blame_subscription = Some(cx.observe(&blame, |_, _, cx| cx.notify()));
10819 self.blame = Some(blame);
10820 }
10821 }
10822
10823 fn toggle_git_blame_inline_internal(
10824 &mut self,
10825 user_triggered: bool,
10826 cx: &mut ViewContext<Self>,
10827 ) {
10828 if self.git_blame_inline_enabled {
10829 self.git_blame_inline_enabled = false;
10830 self.show_git_blame_inline = false;
10831 self.show_git_blame_inline_delay_task.take();
10832 } else {
10833 self.git_blame_inline_enabled = true;
10834 self.start_git_blame_inline(user_triggered, cx);
10835 }
10836
10837 cx.notify();
10838 }
10839
10840 fn start_git_blame_inline(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
10841 self.start_git_blame(user_triggered, cx);
10842
10843 if ProjectSettings::get_global(cx)
10844 .git
10845 .inline_blame_delay()
10846 .is_some()
10847 {
10848 self.start_inline_blame_timer(cx);
10849 } else {
10850 self.show_git_blame_inline = true
10851 }
10852 }
10853
10854 pub fn blame(&self) -> Option<&Model<GitBlame>> {
10855 self.blame.as_ref()
10856 }
10857
10858 pub fn render_git_blame_gutter(&mut self, cx: &mut WindowContext) -> bool {
10859 self.show_git_blame_gutter && self.has_blame_entries(cx)
10860 }
10861
10862 pub fn render_git_blame_inline(&mut self, cx: &mut WindowContext) -> bool {
10863 self.show_git_blame_inline
10864 && self.focus_handle.is_focused(cx)
10865 && !self.newest_selection_head_on_empty_line(cx)
10866 && self.has_blame_entries(cx)
10867 }
10868
10869 fn has_blame_entries(&self, cx: &mut WindowContext) -> bool {
10870 self.blame()
10871 .map_or(false, |blame| blame.read(cx).has_generated_entries())
10872 }
10873
10874 fn newest_selection_head_on_empty_line(&mut self, cx: &mut WindowContext) -> bool {
10875 let cursor_anchor = self.selections.newest_anchor().head();
10876
10877 let snapshot = self.buffer.read(cx).snapshot(cx);
10878 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
10879
10880 snapshot.line_len(buffer_row) == 0
10881 }
10882
10883 fn get_permalink_to_line(&mut self, cx: &mut ViewContext<Self>) -> Result<url::Url> {
10884 let (path, selection, repo) = maybe!({
10885 let project_handle = self.project.as_ref()?.clone();
10886 let project = project_handle.read(cx);
10887
10888 let selection = self.selections.newest::<Point>(cx);
10889 let selection_range = selection.range();
10890
10891 let (buffer, selection) = if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10892 (buffer, selection_range.start.row..selection_range.end.row)
10893 } else {
10894 let buffer_ranges = self
10895 .buffer()
10896 .read(cx)
10897 .range_to_buffer_ranges(selection_range, cx);
10898
10899 let (buffer, range, _) = if selection.reversed {
10900 buffer_ranges.first()
10901 } else {
10902 buffer_ranges.last()
10903 }?;
10904
10905 let snapshot = buffer.read(cx).snapshot();
10906 let selection = text::ToPoint::to_point(&range.start, &snapshot).row
10907 ..text::ToPoint::to_point(&range.end, &snapshot).row;
10908 (buffer.clone(), selection)
10909 };
10910
10911 let path = buffer
10912 .read(cx)
10913 .file()?
10914 .as_local()?
10915 .path()
10916 .to_str()?
10917 .to_string();
10918 let repo = project.get_repo(&buffer.read(cx).project_path(cx)?, cx)?;
10919 Some((path, selection, repo))
10920 })
10921 .ok_or_else(|| anyhow!("unable to open git repository"))?;
10922
10923 const REMOTE_NAME: &str = "origin";
10924 let origin_url = repo
10925 .remote_url(REMOTE_NAME)
10926 .ok_or_else(|| anyhow!("remote \"{REMOTE_NAME}\" not found"))?;
10927 let sha = repo
10928 .head_sha()
10929 .ok_or_else(|| anyhow!("failed to read HEAD SHA"))?;
10930
10931 let (provider, remote) =
10932 parse_git_remote_url(GitHostingProviderRegistry::default_global(cx), &origin_url)
10933 .ok_or_else(|| anyhow!("failed to parse Git remote URL"))?;
10934
10935 Ok(provider.build_permalink(
10936 remote,
10937 BuildPermalinkParams {
10938 sha: &sha,
10939 path: &path,
10940 selection: Some(selection),
10941 },
10942 ))
10943 }
10944
10945 pub fn copy_permalink_to_line(&mut self, _: &CopyPermalinkToLine, cx: &mut ViewContext<Self>) {
10946 let permalink = self.get_permalink_to_line(cx);
10947
10948 match permalink {
10949 Ok(permalink) => {
10950 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
10951 }
10952 Err(err) => {
10953 let message = format!("Failed to copy permalink: {err}");
10954
10955 Err::<(), anyhow::Error>(err).log_err();
10956
10957 if let Some(workspace) = self.workspace() {
10958 workspace.update(cx, |workspace, cx| {
10959 struct CopyPermalinkToLine;
10960
10961 workspace.show_toast(
10962 Toast::new(NotificationId::unique::<CopyPermalinkToLine>(), message),
10963 cx,
10964 )
10965 })
10966 }
10967 }
10968 }
10969 }
10970
10971 pub fn copy_file_location(&mut self, _: &CopyFileLocation, cx: &mut ViewContext<Self>) {
10972 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10973 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10974 if let Some(path) = file.path().to_str() {
10975 let selection = self.selections.newest::<Point>(cx).start.row + 1;
10976 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
10977 }
10978 }
10979 }
10980 }
10981
10982 pub fn open_permalink_to_line(&mut self, _: &OpenPermalinkToLine, cx: &mut ViewContext<Self>) {
10983 let permalink = self.get_permalink_to_line(cx);
10984
10985 match permalink {
10986 Ok(permalink) => {
10987 cx.open_url(permalink.as_ref());
10988 }
10989 Err(err) => {
10990 let message = format!("Failed to open permalink: {err}");
10991
10992 Err::<(), anyhow::Error>(err).log_err();
10993
10994 if let Some(workspace) = self.workspace() {
10995 workspace.update(cx, |workspace, cx| {
10996 struct OpenPermalinkToLine;
10997
10998 workspace.show_toast(
10999 Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
11000 cx,
11001 )
11002 })
11003 }
11004 }
11005 }
11006 }
11007
11008 /// Adds or removes (on `None` color) a highlight for the rows corresponding to the anchor range given.
11009 /// On matching anchor range, replaces the old highlight; does not clear the other existing highlights.
11010 /// If multiple anchor ranges will produce highlights for the same row, the last range added will be used.
11011 pub fn highlight_rows<T: 'static>(
11012 &mut self,
11013 rows: RangeInclusive<Anchor>,
11014 color: Option<Hsla>,
11015 should_autoscroll: bool,
11016 cx: &mut ViewContext<Self>,
11017 ) {
11018 let snapshot = self.buffer().read(cx).snapshot(cx);
11019 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
11020 let existing_highlight_index = row_highlights.binary_search_by(|highlight| {
11021 highlight
11022 .range
11023 .start()
11024 .cmp(&rows.start(), &snapshot)
11025 .then(highlight.range.end().cmp(&rows.end(), &snapshot))
11026 });
11027 match (color, existing_highlight_index) {
11028 (Some(_), Ok(ix)) | (_, Err(ix)) => row_highlights.insert(
11029 ix,
11030 RowHighlight {
11031 index: post_inc(&mut self.highlight_order),
11032 range: rows,
11033 should_autoscroll,
11034 color,
11035 },
11036 ),
11037 (None, Ok(i)) => {
11038 row_highlights.remove(i);
11039 }
11040 }
11041 }
11042
11043 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
11044 pub fn clear_row_highlights<T: 'static>(&mut self) {
11045 self.highlighted_rows.remove(&TypeId::of::<T>());
11046 }
11047
11048 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
11049 pub fn highlighted_rows<T: 'static>(
11050 &self,
11051 ) -> Option<impl Iterator<Item = (&RangeInclusive<Anchor>, Option<&Hsla>)>> {
11052 Some(
11053 self.highlighted_rows
11054 .get(&TypeId::of::<T>())?
11055 .iter()
11056 .map(|highlight| (&highlight.range, highlight.color.as_ref())),
11057 )
11058 }
11059
11060 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
11061 /// Rerturns a map of display rows that are highlighted and their corresponding highlight color.
11062 /// Allows to ignore certain kinds of highlights.
11063 pub fn highlighted_display_rows(
11064 &mut self,
11065 cx: &mut WindowContext,
11066 ) -> BTreeMap<DisplayRow, Hsla> {
11067 let snapshot = self.snapshot(cx);
11068 let mut used_highlight_orders = HashMap::default();
11069 self.highlighted_rows
11070 .iter()
11071 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
11072 .fold(
11073 BTreeMap::<DisplayRow, Hsla>::new(),
11074 |mut unique_rows, highlight| {
11075 let start_row = highlight.range.start().to_display_point(&snapshot).row();
11076 let end_row = highlight.range.end().to_display_point(&snapshot).row();
11077 for row in start_row.0..=end_row.0 {
11078 let used_index =
11079 used_highlight_orders.entry(row).or_insert(highlight.index);
11080 if highlight.index >= *used_index {
11081 *used_index = highlight.index;
11082 match highlight.color {
11083 Some(hsla) => unique_rows.insert(DisplayRow(row), hsla),
11084 None => unique_rows.remove(&DisplayRow(row)),
11085 };
11086 }
11087 }
11088 unique_rows
11089 },
11090 )
11091 }
11092
11093 pub fn highlighted_display_row_for_autoscroll(
11094 &self,
11095 snapshot: &DisplaySnapshot,
11096 ) -> Option<DisplayRow> {
11097 self.highlighted_rows
11098 .values()
11099 .flat_map(|highlighted_rows| highlighted_rows.iter())
11100 .filter_map(|highlight| {
11101 if highlight.color.is_none() || !highlight.should_autoscroll {
11102 return None;
11103 }
11104 Some(highlight.range.start().to_display_point(&snapshot).row())
11105 })
11106 .min()
11107 }
11108
11109 pub fn set_search_within_ranges(
11110 &mut self,
11111 ranges: &[Range<Anchor>],
11112 cx: &mut ViewContext<Self>,
11113 ) {
11114 self.highlight_background::<SearchWithinRange>(
11115 ranges,
11116 |colors| colors.editor_document_highlight_read_background,
11117 cx,
11118 )
11119 }
11120
11121 pub fn set_breadcrumb_header(&mut self, new_header: String) {
11122 self.breadcrumb_header = Some(new_header);
11123 }
11124
11125 pub fn clear_search_within_ranges(&mut self, cx: &mut ViewContext<Self>) {
11126 self.clear_background_highlights::<SearchWithinRange>(cx);
11127 }
11128
11129 pub fn highlight_background<T: 'static>(
11130 &mut self,
11131 ranges: &[Range<Anchor>],
11132 color_fetcher: fn(&ThemeColors) -> Hsla,
11133 cx: &mut ViewContext<Self>,
11134 ) {
11135 self.background_highlights
11136 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
11137 self.scrollbar_marker_state.dirty = true;
11138 cx.notify();
11139 }
11140
11141 pub fn clear_background_highlights<T: 'static>(
11142 &mut self,
11143 cx: &mut ViewContext<Self>,
11144 ) -> Option<BackgroundHighlight> {
11145 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
11146 if !text_highlights.1.is_empty() {
11147 self.scrollbar_marker_state.dirty = true;
11148 cx.notify();
11149 }
11150 Some(text_highlights)
11151 }
11152
11153 pub fn highlight_gutter<T: 'static>(
11154 &mut self,
11155 ranges: &[Range<Anchor>],
11156 color_fetcher: fn(&AppContext) -> Hsla,
11157 cx: &mut ViewContext<Self>,
11158 ) {
11159 self.gutter_highlights
11160 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
11161 cx.notify();
11162 }
11163
11164 pub fn clear_gutter_highlights<T: 'static>(
11165 &mut self,
11166 cx: &mut ViewContext<Self>,
11167 ) -> Option<GutterHighlight> {
11168 cx.notify();
11169 self.gutter_highlights.remove(&TypeId::of::<T>())
11170 }
11171
11172 #[cfg(feature = "test-support")]
11173 pub fn all_text_background_highlights(
11174 &mut self,
11175 cx: &mut ViewContext<Self>,
11176 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11177 let snapshot = self.snapshot(cx);
11178 let buffer = &snapshot.buffer_snapshot;
11179 let start = buffer.anchor_before(0);
11180 let end = buffer.anchor_after(buffer.len());
11181 let theme = cx.theme().colors();
11182 self.background_highlights_in_range(start..end, &snapshot, theme)
11183 }
11184
11185 #[cfg(feature = "test-support")]
11186 pub fn search_background_highlights(
11187 &mut self,
11188 cx: &mut ViewContext<Self>,
11189 ) -> Vec<Range<Point>> {
11190 let snapshot = self.buffer().read(cx).snapshot(cx);
11191
11192 let highlights = self
11193 .background_highlights
11194 .get(&TypeId::of::<items::BufferSearchHighlights>());
11195
11196 if let Some((_color, ranges)) = highlights {
11197 ranges
11198 .iter()
11199 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
11200 .collect_vec()
11201 } else {
11202 vec![]
11203 }
11204 }
11205
11206 fn document_highlights_for_position<'a>(
11207 &'a self,
11208 position: Anchor,
11209 buffer: &'a MultiBufferSnapshot,
11210 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
11211 let read_highlights = self
11212 .background_highlights
11213 .get(&TypeId::of::<DocumentHighlightRead>())
11214 .map(|h| &h.1);
11215 let write_highlights = self
11216 .background_highlights
11217 .get(&TypeId::of::<DocumentHighlightWrite>())
11218 .map(|h| &h.1);
11219 let left_position = position.bias_left(buffer);
11220 let right_position = position.bias_right(buffer);
11221 read_highlights
11222 .into_iter()
11223 .chain(write_highlights)
11224 .flat_map(move |ranges| {
11225 let start_ix = match ranges.binary_search_by(|probe| {
11226 let cmp = probe.end.cmp(&left_position, buffer);
11227 if cmp.is_ge() {
11228 Ordering::Greater
11229 } else {
11230 Ordering::Less
11231 }
11232 }) {
11233 Ok(i) | Err(i) => i,
11234 };
11235
11236 ranges[start_ix..]
11237 .iter()
11238 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
11239 })
11240 }
11241
11242 pub fn has_background_highlights<T: 'static>(&self) -> bool {
11243 self.background_highlights
11244 .get(&TypeId::of::<T>())
11245 .map_or(false, |(_, highlights)| !highlights.is_empty())
11246 }
11247
11248 pub fn background_highlights_in_range(
11249 &self,
11250 search_range: Range<Anchor>,
11251 display_snapshot: &DisplaySnapshot,
11252 theme: &ThemeColors,
11253 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11254 let mut results = Vec::new();
11255 for (color_fetcher, ranges) in self.background_highlights.values() {
11256 let color = color_fetcher(theme);
11257 let start_ix = match ranges.binary_search_by(|probe| {
11258 let cmp = probe
11259 .end
11260 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11261 if cmp.is_gt() {
11262 Ordering::Greater
11263 } else {
11264 Ordering::Less
11265 }
11266 }) {
11267 Ok(i) | Err(i) => i,
11268 };
11269 for range in &ranges[start_ix..] {
11270 if range
11271 .start
11272 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11273 .is_ge()
11274 {
11275 break;
11276 }
11277
11278 let start = range.start.to_display_point(&display_snapshot);
11279 let end = range.end.to_display_point(&display_snapshot);
11280 results.push((start..end, color))
11281 }
11282 }
11283 results
11284 }
11285
11286 pub fn background_highlight_row_ranges<T: 'static>(
11287 &self,
11288 search_range: Range<Anchor>,
11289 display_snapshot: &DisplaySnapshot,
11290 count: usize,
11291 ) -> Vec<RangeInclusive<DisplayPoint>> {
11292 let mut results = Vec::new();
11293 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
11294 return vec![];
11295 };
11296
11297 let start_ix = match ranges.binary_search_by(|probe| {
11298 let cmp = probe
11299 .end
11300 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11301 if cmp.is_gt() {
11302 Ordering::Greater
11303 } else {
11304 Ordering::Less
11305 }
11306 }) {
11307 Ok(i) | Err(i) => i,
11308 };
11309 let mut push_region = |start: Option<Point>, end: Option<Point>| {
11310 if let (Some(start_display), Some(end_display)) = (start, end) {
11311 results.push(
11312 start_display.to_display_point(display_snapshot)
11313 ..=end_display.to_display_point(display_snapshot),
11314 );
11315 }
11316 };
11317 let mut start_row: Option<Point> = None;
11318 let mut end_row: Option<Point> = None;
11319 if ranges.len() > count {
11320 return Vec::new();
11321 }
11322 for range in &ranges[start_ix..] {
11323 if range
11324 .start
11325 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11326 .is_ge()
11327 {
11328 break;
11329 }
11330 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
11331 if let Some(current_row) = &end_row {
11332 if end.row == current_row.row {
11333 continue;
11334 }
11335 }
11336 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
11337 if start_row.is_none() {
11338 assert_eq!(end_row, None);
11339 start_row = Some(start);
11340 end_row = Some(end);
11341 continue;
11342 }
11343 if let Some(current_end) = end_row.as_mut() {
11344 if start.row > current_end.row + 1 {
11345 push_region(start_row, end_row);
11346 start_row = Some(start);
11347 end_row = Some(end);
11348 } else {
11349 // Merge two hunks.
11350 *current_end = end;
11351 }
11352 } else {
11353 unreachable!();
11354 }
11355 }
11356 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
11357 push_region(start_row, end_row);
11358 results
11359 }
11360
11361 pub fn gutter_highlights_in_range(
11362 &self,
11363 search_range: Range<Anchor>,
11364 display_snapshot: &DisplaySnapshot,
11365 cx: &AppContext,
11366 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11367 let mut results = Vec::new();
11368 for (color_fetcher, ranges) in self.gutter_highlights.values() {
11369 let color = color_fetcher(cx);
11370 let start_ix = match ranges.binary_search_by(|probe| {
11371 let cmp = probe
11372 .end
11373 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11374 if cmp.is_gt() {
11375 Ordering::Greater
11376 } else {
11377 Ordering::Less
11378 }
11379 }) {
11380 Ok(i) | Err(i) => i,
11381 };
11382 for range in &ranges[start_ix..] {
11383 if range
11384 .start
11385 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11386 .is_ge()
11387 {
11388 break;
11389 }
11390
11391 let start = range.start.to_display_point(&display_snapshot);
11392 let end = range.end.to_display_point(&display_snapshot);
11393 results.push((start..end, color))
11394 }
11395 }
11396 results
11397 }
11398
11399 /// Get the text ranges corresponding to the redaction query
11400 pub fn redacted_ranges(
11401 &self,
11402 search_range: Range<Anchor>,
11403 display_snapshot: &DisplaySnapshot,
11404 cx: &WindowContext,
11405 ) -> Vec<Range<DisplayPoint>> {
11406 display_snapshot
11407 .buffer_snapshot
11408 .redacted_ranges(search_range, |file| {
11409 if let Some(file) = file {
11410 file.is_private()
11411 && EditorSettings::get(Some(file.as_ref().into()), cx).redact_private_values
11412 } else {
11413 false
11414 }
11415 })
11416 .map(|range| {
11417 range.start.to_display_point(display_snapshot)
11418 ..range.end.to_display_point(display_snapshot)
11419 })
11420 .collect()
11421 }
11422
11423 pub fn highlight_text<T: 'static>(
11424 &mut self,
11425 ranges: Vec<Range<Anchor>>,
11426 style: HighlightStyle,
11427 cx: &mut ViewContext<Self>,
11428 ) {
11429 self.display_map.update(cx, |map, _| {
11430 map.highlight_text(TypeId::of::<T>(), ranges, style)
11431 });
11432 cx.notify();
11433 }
11434
11435 pub(crate) fn highlight_inlays<T: 'static>(
11436 &mut self,
11437 highlights: Vec<InlayHighlight>,
11438 style: HighlightStyle,
11439 cx: &mut ViewContext<Self>,
11440 ) {
11441 self.display_map.update(cx, |map, _| {
11442 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
11443 });
11444 cx.notify();
11445 }
11446
11447 pub fn text_highlights<'a, T: 'static>(
11448 &'a self,
11449 cx: &'a AppContext,
11450 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
11451 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
11452 }
11453
11454 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
11455 let cleared = self
11456 .display_map
11457 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
11458 if cleared {
11459 cx.notify();
11460 }
11461 }
11462
11463 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
11464 (self.read_only(cx) || self.blink_manager.read(cx).visible())
11465 && self.focus_handle.is_focused(cx)
11466 }
11467
11468 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut ViewContext<Self>) {
11469 self.show_cursor_when_unfocused = is_enabled;
11470 cx.notify();
11471 }
11472
11473 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
11474 cx.notify();
11475 }
11476
11477 fn on_buffer_event(
11478 &mut self,
11479 multibuffer: Model<MultiBuffer>,
11480 event: &multi_buffer::Event,
11481 cx: &mut ViewContext<Self>,
11482 ) {
11483 match event {
11484 multi_buffer::Event::Edited {
11485 singleton_buffer_edited,
11486 } => {
11487 self.scrollbar_marker_state.dirty = true;
11488 self.active_indent_guides_state.dirty = true;
11489 self.refresh_active_diagnostics(cx);
11490 self.refresh_code_actions(cx);
11491 if self.has_active_inline_completion(cx) {
11492 self.update_visible_inline_completion(cx);
11493 }
11494 cx.emit(EditorEvent::BufferEdited);
11495 cx.emit(SearchEvent::MatchesInvalidated);
11496 if *singleton_buffer_edited {
11497 if let Some(project) = &self.project {
11498 let project = project.read(cx);
11499 #[allow(clippy::mutable_key_type)]
11500 let languages_affected = multibuffer
11501 .read(cx)
11502 .all_buffers()
11503 .into_iter()
11504 .filter_map(|buffer| {
11505 let buffer = buffer.read(cx);
11506 let language = buffer.language()?;
11507 if project.is_local_or_ssh()
11508 && project.language_servers_for_buffer(buffer, cx).count() == 0
11509 {
11510 None
11511 } else {
11512 Some(language)
11513 }
11514 })
11515 .cloned()
11516 .collect::<HashSet<_>>();
11517 if !languages_affected.is_empty() {
11518 self.refresh_inlay_hints(
11519 InlayHintRefreshReason::BufferEdited(languages_affected),
11520 cx,
11521 );
11522 }
11523 }
11524 }
11525
11526 let Some(project) = &self.project else { return };
11527 let telemetry = project.read(cx).client().telemetry().clone();
11528 refresh_linked_ranges(self, cx);
11529 telemetry.log_edit_event("editor");
11530 }
11531 multi_buffer::Event::ExcerptsAdded {
11532 buffer,
11533 predecessor,
11534 excerpts,
11535 } => {
11536 self.tasks_update_task = Some(self.refresh_runnables(cx));
11537 cx.emit(EditorEvent::ExcerptsAdded {
11538 buffer: buffer.clone(),
11539 predecessor: *predecessor,
11540 excerpts: excerpts.clone(),
11541 });
11542 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
11543 }
11544 multi_buffer::Event::ExcerptsRemoved { ids } => {
11545 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
11546 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
11547 }
11548 multi_buffer::Event::ExcerptsEdited { ids } => {
11549 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
11550 }
11551 multi_buffer::Event::ExcerptsExpanded { ids } => {
11552 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
11553 }
11554 multi_buffer::Event::Reparsed(buffer_id) => {
11555 self.tasks_update_task = Some(self.refresh_runnables(cx));
11556
11557 cx.emit(EditorEvent::Reparsed(*buffer_id));
11558 }
11559 multi_buffer::Event::LanguageChanged(buffer_id) => {
11560 linked_editing_ranges::refresh_linked_ranges(self, cx);
11561 cx.emit(EditorEvent::Reparsed(*buffer_id));
11562 cx.notify();
11563 }
11564 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
11565 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
11566 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
11567 cx.emit(EditorEvent::TitleChanged)
11568 }
11569 multi_buffer::Event::DiffBaseChanged => {
11570 self.scrollbar_marker_state.dirty = true;
11571 cx.emit(EditorEvent::DiffBaseChanged);
11572 cx.notify();
11573 }
11574 multi_buffer::Event::DiffUpdated { buffer } => {
11575 self.sync_expanded_diff_hunks(buffer.clone(), cx);
11576 cx.notify();
11577 }
11578 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
11579 multi_buffer::Event::DiagnosticsUpdated => {
11580 self.refresh_active_diagnostics(cx);
11581 self.scrollbar_marker_state.dirty = true;
11582 cx.notify();
11583 }
11584 _ => {}
11585 };
11586 }
11587
11588 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
11589 cx.notify();
11590 }
11591
11592 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
11593 self.tasks_update_task = Some(self.refresh_runnables(cx));
11594 self.refresh_inline_completion(true, false, cx);
11595 self.refresh_inlay_hints(
11596 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
11597 self.selections.newest_anchor().head(),
11598 &self.buffer.read(cx).snapshot(cx),
11599 cx,
11600 )),
11601 cx,
11602 );
11603 let editor_settings = EditorSettings::get_global(cx);
11604 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
11605 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
11606
11607 let project_settings = ProjectSettings::get_global(cx);
11608 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
11609
11610 if self.mode == EditorMode::Full {
11611 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
11612 if self.git_blame_inline_enabled != inline_blame_enabled {
11613 self.toggle_git_blame_inline_internal(false, cx);
11614 }
11615 }
11616
11617 cx.notify();
11618 }
11619
11620 pub fn set_searchable(&mut self, searchable: bool) {
11621 self.searchable = searchable;
11622 }
11623
11624 pub fn searchable(&self) -> bool {
11625 self.searchable
11626 }
11627
11628 fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext<Self>) {
11629 self.open_excerpts_common(true, cx)
11630 }
11631
11632 fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
11633 self.open_excerpts_common(false, cx)
11634 }
11635
11636 fn open_excerpts_common(&mut self, split: bool, cx: &mut ViewContext<Self>) {
11637 let buffer = self.buffer.read(cx);
11638 if buffer.is_singleton() {
11639 cx.propagate();
11640 return;
11641 }
11642
11643 let Some(workspace) = self.workspace() else {
11644 cx.propagate();
11645 return;
11646 };
11647
11648 let mut new_selections_by_buffer = HashMap::default();
11649 for selection in self.selections.all::<usize>(cx) {
11650 for (buffer, mut range, _) in
11651 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
11652 {
11653 if selection.reversed {
11654 mem::swap(&mut range.start, &mut range.end);
11655 }
11656 new_selections_by_buffer
11657 .entry(buffer)
11658 .or_insert(Vec::new())
11659 .push(range)
11660 }
11661 }
11662
11663 // We defer the pane interaction because we ourselves are a workspace item
11664 // and activating a new item causes the pane to call a method on us reentrantly,
11665 // which panics if we're on the stack.
11666 cx.window_context().defer(move |cx| {
11667 workspace.update(cx, |workspace, cx| {
11668 let pane = if split {
11669 workspace.adjacent_pane(cx)
11670 } else {
11671 workspace.active_pane().clone()
11672 };
11673
11674 for (buffer, ranges) in new_selections_by_buffer {
11675 let editor =
11676 workspace.open_project_item::<Self>(pane.clone(), buffer, true, true, cx);
11677 editor.update(cx, |editor, cx| {
11678 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
11679 s.select_ranges(ranges);
11680 });
11681 });
11682 }
11683 })
11684 });
11685 }
11686
11687 fn jump(
11688 &mut self,
11689 path: ProjectPath,
11690 position: Point,
11691 anchor: language::Anchor,
11692 offset_from_top: u32,
11693 cx: &mut ViewContext<Self>,
11694 ) {
11695 let workspace = self.workspace();
11696 cx.spawn(|_, mut cx| async move {
11697 let workspace = workspace.ok_or_else(|| anyhow!("cannot jump without workspace"))?;
11698 let editor = workspace.update(&mut cx, |workspace, cx| {
11699 // Reset the preview item id before opening the new item
11700 workspace.active_pane().update(cx, |pane, cx| {
11701 pane.set_preview_item_id(None, cx);
11702 });
11703 workspace.open_path_preview(path, None, true, true, cx)
11704 })?;
11705 let editor = editor
11706 .await?
11707 .downcast::<Editor>()
11708 .ok_or_else(|| anyhow!("opened item was not an editor"))?
11709 .downgrade();
11710 editor.update(&mut cx, |editor, cx| {
11711 let buffer = editor
11712 .buffer()
11713 .read(cx)
11714 .as_singleton()
11715 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
11716 let buffer = buffer.read(cx);
11717 let cursor = if buffer.can_resolve(&anchor) {
11718 language::ToPoint::to_point(&anchor, buffer)
11719 } else {
11720 buffer.clip_point(position, Bias::Left)
11721 };
11722
11723 let nav_history = editor.nav_history.take();
11724 editor.change_selections(
11725 Some(Autoscroll::top_relative(offset_from_top as usize)),
11726 cx,
11727 |s| {
11728 s.select_ranges([cursor..cursor]);
11729 },
11730 );
11731 editor.nav_history = nav_history;
11732
11733 anyhow::Ok(())
11734 })??;
11735
11736 anyhow::Ok(())
11737 })
11738 .detach_and_log_err(cx);
11739 }
11740
11741 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
11742 let snapshot = self.buffer.read(cx).read(cx);
11743 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
11744 Some(
11745 ranges
11746 .iter()
11747 .map(move |range| {
11748 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
11749 })
11750 .collect(),
11751 )
11752 }
11753
11754 fn selection_replacement_ranges(
11755 &self,
11756 range: Range<OffsetUtf16>,
11757 cx: &AppContext,
11758 ) -> Vec<Range<OffsetUtf16>> {
11759 let selections = self.selections.all::<OffsetUtf16>(cx);
11760 let newest_selection = selections
11761 .iter()
11762 .max_by_key(|selection| selection.id)
11763 .unwrap();
11764 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
11765 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
11766 let snapshot = self.buffer.read(cx).read(cx);
11767 selections
11768 .into_iter()
11769 .map(|mut selection| {
11770 selection.start.0 =
11771 (selection.start.0 as isize).saturating_add(start_delta) as usize;
11772 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
11773 snapshot.clip_offset_utf16(selection.start, Bias::Left)
11774 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
11775 })
11776 .collect()
11777 }
11778
11779 fn report_editor_event(
11780 &self,
11781 operation: &'static str,
11782 file_extension: Option<String>,
11783 cx: &AppContext,
11784 ) {
11785 if cfg!(any(test, feature = "test-support")) {
11786 return;
11787 }
11788
11789 let Some(project) = &self.project else { return };
11790
11791 // If None, we are in a file without an extension
11792 let file = self
11793 .buffer
11794 .read(cx)
11795 .as_singleton()
11796 .and_then(|b| b.read(cx).file());
11797 let file_extension = file_extension.or(file
11798 .as_ref()
11799 .and_then(|file| Path::new(file.file_name(cx)).extension())
11800 .and_then(|e| e.to_str())
11801 .map(|a| a.to_string()));
11802
11803 let vim_mode = cx
11804 .global::<SettingsStore>()
11805 .raw_user_settings()
11806 .get("vim_mode")
11807 == Some(&serde_json::Value::Bool(true));
11808
11809 let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
11810 == language::language_settings::InlineCompletionProvider::Copilot;
11811 let copilot_enabled_for_language = self
11812 .buffer
11813 .read(cx)
11814 .settings_at(0, cx)
11815 .show_inline_completions;
11816
11817 let telemetry = project.read(cx).client().telemetry().clone();
11818 telemetry.report_editor_event(
11819 file_extension,
11820 vim_mode,
11821 operation,
11822 copilot_enabled,
11823 copilot_enabled_for_language,
11824 )
11825 }
11826
11827 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
11828 /// with each line being an array of {text, highlight} objects.
11829 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
11830 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
11831 return;
11832 };
11833
11834 #[derive(Serialize)]
11835 struct Chunk<'a> {
11836 text: String,
11837 highlight: Option<&'a str>,
11838 }
11839
11840 let snapshot = buffer.read(cx).snapshot();
11841 let range = self
11842 .selected_text_range(cx)
11843 .and_then(|selected_range| {
11844 if selected_range.is_empty() {
11845 None
11846 } else {
11847 Some(selected_range)
11848 }
11849 })
11850 .unwrap_or_else(|| 0..snapshot.len());
11851
11852 let chunks = snapshot.chunks(range, true);
11853 let mut lines = Vec::new();
11854 let mut line: VecDeque<Chunk> = VecDeque::new();
11855
11856 let Some(style) = self.style.as_ref() else {
11857 return;
11858 };
11859
11860 for chunk in chunks {
11861 let highlight = chunk
11862 .syntax_highlight_id
11863 .and_then(|id| id.name(&style.syntax));
11864 let mut chunk_lines = chunk.text.split('\n').peekable();
11865 while let Some(text) = chunk_lines.next() {
11866 let mut merged_with_last_token = false;
11867 if let Some(last_token) = line.back_mut() {
11868 if last_token.highlight == highlight {
11869 last_token.text.push_str(text);
11870 merged_with_last_token = true;
11871 }
11872 }
11873
11874 if !merged_with_last_token {
11875 line.push_back(Chunk {
11876 text: text.into(),
11877 highlight,
11878 });
11879 }
11880
11881 if chunk_lines.peek().is_some() {
11882 if line.len() > 1 && line.front().unwrap().text.is_empty() {
11883 line.pop_front();
11884 }
11885 if line.len() > 1 && line.back().unwrap().text.is_empty() {
11886 line.pop_back();
11887 }
11888
11889 lines.push(mem::take(&mut line));
11890 }
11891 }
11892 }
11893
11894 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
11895 return;
11896 };
11897 cx.write_to_clipboard(ClipboardItem::new_string(lines));
11898 }
11899
11900 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
11901 &self.inlay_hint_cache
11902 }
11903
11904 pub fn replay_insert_event(
11905 &mut self,
11906 text: &str,
11907 relative_utf16_range: Option<Range<isize>>,
11908 cx: &mut ViewContext<Self>,
11909 ) {
11910 if !self.input_enabled {
11911 cx.emit(EditorEvent::InputIgnored { text: text.into() });
11912 return;
11913 }
11914 if let Some(relative_utf16_range) = relative_utf16_range {
11915 let selections = self.selections.all::<OffsetUtf16>(cx);
11916 self.change_selections(None, cx, |s| {
11917 let new_ranges = selections.into_iter().map(|range| {
11918 let start = OffsetUtf16(
11919 range
11920 .head()
11921 .0
11922 .saturating_add_signed(relative_utf16_range.start),
11923 );
11924 let end = OffsetUtf16(
11925 range
11926 .head()
11927 .0
11928 .saturating_add_signed(relative_utf16_range.end),
11929 );
11930 start..end
11931 });
11932 s.select_ranges(new_ranges);
11933 });
11934 }
11935
11936 self.handle_input(text, cx);
11937 }
11938
11939 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
11940 let Some(project) = self.project.as_ref() else {
11941 return false;
11942 };
11943 let project = project.read(cx);
11944
11945 let mut supports = false;
11946 self.buffer().read(cx).for_each_buffer(|buffer| {
11947 if !supports {
11948 supports = project
11949 .language_servers_for_buffer(buffer.read(cx), cx)
11950 .any(
11951 |(_, server)| match server.capabilities().inlay_hint_provider {
11952 Some(lsp::OneOf::Left(enabled)) => enabled,
11953 Some(lsp::OneOf::Right(_)) => true,
11954 None => false,
11955 },
11956 )
11957 }
11958 });
11959 supports
11960 }
11961
11962 pub fn focus(&self, cx: &mut WindowContext) {
11963 cx.focus(&self.focus_handle)
11964 }
11965
11966 pub fn is_focused(&self, cx: &WindowContext) -> bool {
11967 self.focus_handle.is_focused(cx)
11968 }
11969
11970 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
11971 cx.emit(EditorEvent::Focused);
11972
11973 if let Some(descendant) = self
11974 .last_focused_descendant
11975 .take()
11976 .and_then(|descendant| descendant.upgrade())
11977 {
11978 cx.focus(&descendant);
11979 } else {
11980 if let Some(blame) = self.blame.as_ref() {
11981 blame.update(cx, GitBlame::focus)
11982 }
11983
11984 self.blink_manager.update(cx, BlinkManager::enable);
11985 self.show_cursor_names(cx);
11986 self.buffer.update(cx, |buffer, cx| {
11987 buffer.finalize_last_transaction(cx);
11988 if self.leader_peer_id.is_none() {
11989 buffer.set_active_selections(
11990 &self.selections.disjoint_anchors(),
11991 self.selections.line_mode,
11992 self.cursor_shape,
11993 cx,
11994 );
11995 }
11996 });
11997 }
11998 }
11999
12000 fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
12001 cx.emit(EditorEvent::FocusedIn)
12002 }
12003
12004 fn handle_focus_out(&mut self, event: FocusOutEvent, _cx: &mut ViewContext<Self>) {
12005 if event.blurred != self.focus_handle {
12006 self.last_focused_descendant = Some(event.blurred);
12007 }
12008 }
12009
12010 pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
12011 self.blink_manager.update(cx, BlinkManager::disable);
12012 self.buffer
12013 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
12014
12015 if let Some(blame) = self.blame.as_ref() {
12016 blame.update(cx, GitBlame::blur)
12017 }
12018 if !self.hover_state.focused(cx) {
12019 hide_hover(self, cx);
12020 }
12021
12022 self.hide_context_menu(cx);
12023 cx.emit(EditorEvent::Blurred);
12024 cx.notify();
12025 }
12026
12027 pub fn register_action<A: Action>(
12028 &mut self,
12029 listener: impl Fn(&A, &mut WindowContext) + 'static,
12030 ) -> Subscription {
12031 let id = self.next_editor_action_id.post_inc();
12032 let listener = Arc::new(listener);
12033 self.editor_actions.borrow_mut().insert(
12034 id,
12035 Box::new(move |cx| {
12036 let cx = cx.window_context();
12037 let listener = listener.clone();
12038 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
12039 let action = action.downcast_ref().unwrap();
12040 if phase == DispatchPhase::Bubble {
12041 listener(action, cx)
12042 }
12043 })
12044 }),
12045 );
12046
12047 let editor_actions = self.editor_actions.clone();
12048 Subscription::new(move || {
12049 editor_actions.borrow_mut().remove(&id);
12050 })
12051 }
12052
12053 pub fn file_header_size(&self) -> u32 {
12054 self.file_header_size
12055 }
12056
12057 pub fn revert(
12058 &mut self,
12059 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
12060 cx: &mut ViewContext<Self>,
12061 ) {
12062 self.buffer().update(cx, |multi_buffer, cx| {
12063 for (buffer_id, changes) in revert_changes {
12064 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
12065 buffer.update(cx, |buffer, cx| {
12066 buffer.edit(
12067 changes.into_iter().map(|(range, text)| {
12068 (range, text.to_string().map(Arc::<str>::from))
12069 }),
12070 None,
12071 cx,
12072 );
12073 });
12074 }
12075 }
12076 });
12077 self.change_selections(None, cx, |selections| selections.refresh());
12078 }
12079
12080 pub fn to_pixel_point(
12081 &mut self,
12082 source: multi_buffer::Anchor,
12083 editor_snapshot: &EditorSnapshot,
12084 cx: &mut ViewContext<Self>,
12085 ) -> Option<gpui::Point<Pixels>> {
12086 let source_point = source.to_display_point(editor_snapshot);
12087 self.display_to_pixel_point(source_point, editor_snapshot, cx)
12088 }
12089
12090 pub fn display_to_pixel_point(
12091 &mut self,
12092 source: DisplayPoint,
12093 editor_snapshot: &EditorSnapshot,
12094 cx: &mut ViewContext<Self>,
12095 ) -> Option<gpui::Point<Pixels>> {
12096 let line_height = self.style()?.text.line_height_in_pixels(cx.rem_size());
12097 let text_layout_details = self.text_layout_details(cx);
12098 let scroll_top = text_layout_details
12099 .scroll_anchor
12100 .scroll_position(editor_snapshot)
12101 .y;
12102
12103 if source.row().as_f32() < scroll_top.floor() {
12104 return None;
12105 }
12106 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
12107 let source_y = line_height * (source.row().as_f32() - scroll_top);
12108 Some(gpui::Point::new(source_x, source_y))
12109 }
12110
12111 fn gutter_bounds(&self) -> Option<Bounds<Pixels>> {
12112 let bounds = self.last_bounds?;
12113 Some(element::gutter_bounds(bounds, self.gutter_dimensions))
12114 }
12115
12116 pub fn has_active_completions_menu(&self) -> bool {
12117 self.context_menu.read().as_ref().map_or(false, |menu| {
12118 menu.visible() && matches!(menu, ContextMenu::Completions(_))
12119 })
12120 }
12121
12122 pub fn register_addon<T: Addon>(&mut self, instance: T) {
12123 self.addons
12124 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
12125 }
12126
12127 pub fn unregister_addon<T: Addon>(&mut self) {
12128 self.addons.remove(&std::any::TypeId::of::<T>());
12129 }
12130
12131 pub fn addon<T: Addon>(&self) -> Option<&T> {
12132 let type_id = std::any::TypeId::of::<T>();
12133 self.addons
12134 .get(&type_id)
12135 .and_then(|item| item.to_any().downcast_ref::<T>())
12136 }
12137}
12138
12139fn hunks_for_selections(
12140 multi_buffer_snapshot: &MultiBufferSnapshot,
12141 selections: &[Selection<Anchor>],
12142) -> Vec<DiffHunk<MultiBufferRow>> {
12143 let buffer_rows_for_selections = selections.iter().map(|selection| {
12144 let head = selection.head();
12145 let tail = selection.tail();
12146 let start = MultiBufferRow(tail.to_point(&multi_buffer_snapshot).row);
12147 let end = MultiBufferRow(head.to_point(&multi_buffer_snapshot).row);
12148 if start > end {
12149 end..start
12150 } else {
12151 start..end
12152 }
12153 });
12154
12155 hunks_for_rows(buffer_rows_for_selections, multi_buffer_snapshot)
12156}
12157
12158pub fn hunks_for_rows(
12159 rows: impl Iterator<Item = Range<MultiBufferRow>>,
12160 multi_buffer_snapshot: &MultiBufferSnapshot,
12161) -> Vec<DiffHunk<MultiBufferRow>> {
12162 let mut hunks = Vec::new();
12163 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
12164 HashMap::default();
12165 for selected_multi_buffer_rows in rows {
12166 let query_rows =
12167 selected_multi_buffer_rows.start..selected_multi_buffer_rows.end.next_row();
12168 for hunk in multi_buffer_snapshot.git_diff_hunks_in_range(query_rows.clone()) {
12169 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
12170 // when the caret is just above or just below the deleted hunk.
12171 let allow_adjacent = hunk_status(&hunk) == DiffHunkStatus::Removed;
12172 let related_to_selection = if allow_adjacent {
12173 hunk.associated_range.overlaps(&query_rows)
12174 || hunk.associated_range.start == query_rows.end
12175 || hunk.associated_range.end == query_rows.start
12176 } else {
12177 // `selected_multi_buffer_rows` are inclusive (e.g. [2..2] means 2nd row is selected)
12178 // `hunk.associated_range` is exclusive (e.g. [2..3] means 2nd row is selected)
12179 hunk.associated_range.overlaps(&selected_multi_buffer_rows)
12180 || selected_multi_buffer_rows.end == hunk.associated_range.start
12181 };
12182 if related_to_selection {
12183 if !processed_buffer_rows
12184 .entry(hunk.buffer_id)
12185 .or_default()
12186 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
12187 {
12188 continue;
12189 }
12190 hunks.push(hunk);
12191 }
12192 }
12193 }
12194
12195 hunks
12196}
12197
12198pub trait CollaborationHub {
12199 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
12200 fn user_participant_indices<'a>(
12201 &self,
12202 cx: &'a AppContext,
12203 ) -> &'a HashMap<u64, ParticipantIndex>;
12204 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
12205}
12206
12207impl CollaborationHub for Model<Project> {
12208 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
12209 self.read(cx).collaborators()
12210 }
12211
12212 fn user_participant_indices<'a>(
12213 &self,
12214 cx: &'a AppContext,
12215 ) -> &'a HashMap<u64, ParticipantIndex> {
12216 self.read(cx).user_store().read(cx).participant_indices()
12217 }
12218
12219 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
12220 let this = self.read(cx);
12221 let user_ids = this.collaborators().values().map(|c| c.user_id);
12222 this.user_store().read_with(cx, |user_store, cx| {
12223 user_store.participant_names(user_ids, cx)
12224 })
12225 }
12226}
12227
12228pub trait CompletionProvider {
12229 fn completions(
12230 &self,
12231 buffer: &Model<Buffer>,
12232 buffer_position: text::Anchor,
12233 trigger: CompletionContext,
12234 cx: &mut ViewContext<Editor>,
12235 ) -> Task<Result<Vec<Completion>>>;
12236
12237 fn resolve_completions(
12238 &self,
12239 buffer: Model<Buffer>,
12240 completion_indices: Vec<usize>,
12241 completions: Arc<RwLock<Box<[Completion]>>>,
12242 cx: &mut ViewContext<Editor>,
12243 ) -> Task<Result<bool>>;
12244
12245 fn apply_additional_edits_for_completion(
12246 &self,
12247 buffer: Model<Buffer>,
12248 completion: Completion,
12249 push_to_history: bool,
12250 cx: &mut ViewContext<Editor>,
12251 ) -> Task<Result<Option<language::Transaction>>>;
12252
12253 fn is_completion_trigger(
12254 &self,
12255 buffer: &Model<Buffer>,
12256 position: language::Anchor,
12257 text: &str,
12258 trigger_in_words: bool,
12259 cx: &mut ViewContext<Editor>,
12260 ) -> bool;
12261
12262 fn sort_completions(&self) -> bool {
12263 true
12264 }
12265}
12266
12267fn snippet_completions(
12268 project: &Project,
12269 buffer: &Model<Buffer>,
12270 buffer_position: text::Anchor,
12271 cx: &mut AppContext,
12272) -> Vec<Completion> {
12273 let language = buffer.read(cx).language_at(buffer_position);
12274 let language_name = language.as_ref().map(|language| language.lsp_id());
12275 let snippet_store = project.snippets().read(cx);
12276 let snippets = snippet_store.snippets_for(language_name, cx);
12277
12278 if snippets.is_empty() {
12279 return vec![];
12280 }
12281 let snapshot = buffer.read(cx).text_snapshot();
12282 let chunks = snapshot.reversed_chunks_in_range(text::Anchor::MIN..buffer_position);
12283
12284 let mut lines = chunks.lines();
12285 let Some(line_at) = lines.next().filter(|line| !line.is_empty()) else {
12286 return vec![];
12287 };
12288
12289 let scope = language.map(|language| language.default_scope());
12290 let mut last_word = line_at
12291 .chars()
12292 .rev()
12293 .take_while(|c| char_kind(&scope, *c) == CharKind::Word)
12294 .collect::<String>();
12295 last_word = last_word.chars().rev().collect();
12296 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
12297 let to_lsp = |point: &text::Anchor| {
12298 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
12299 point_to_lsp(end)
12300 };
12301 let lsp_end = to_lsp(&buffer_position);
12302 snippets
12303 .into_iter()
12304 .filter_map(|snippet| {
12305 let matching_prefix = snippet
12306 .prefix
12307 .iter()
12308 .find(|prefix| prefix.starts_with(&last_word))?;
12309 let start = as_offset - last_word.len();
12310 let start = snapshot.anchor_before(start);
12311 let range = start..buffer_position;
12312 let lsp_start = to_lsp(&start);
12313 let lsp_range = lsp::Range {
12314 start: lsp_start,
12315 end: lsp_end,
12316 };
12317 Some(Completion {
12318 old_range: range,
12319 new_text: snippet.body.clone(),
12320 label: CodeLabel {
12321 text: matching_prefix.clone(),
12322 runs: vec![],
12323 filter_range: 0..matching_prefix.len(),
12324 },
12325 server_id: LanguageServerId(usize::MAX),
12326 documentation: snippet
12327 .description
12328 .clone()
12329 .map(|description| Documentation::SingleLine(description)),
12330 lsp_completion: lsp::CompletionItem {
12331 label: snippet.prefix.first().unwrap().clone(),
12332 kind: Some(CompletionItemKind::SNIPPET),
12333 label_details: snippet.description.as_ref().map(|description| {
12334 lsp::CompletionItemLabelDetails {
12335 detail: Some(description.clone()),
12336 description: None,
12337 }
12338 }),
12339 insert_text_format: Some(InsertTextFormat::SNIPPET),
12340 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
12341 lsp::InsertReplaceEdit {
12342 new_text: snippet.body.clone(),
12343 insert: lsp_range,
12344 replace: lsp_range,
12345 },
12346 )),
12347 filter_text: Some(snippet.body.clone()),
12348 sort_text: Some(char::MAX.to_string()),
12349 ..Default::default()
12350 },
12351 confirm: None,
12352 })
12353 })
12354 .collect()
12355}
12356
12357impl CompletionProvider for Model<Project> {
12358 fn completions(
12359 &self,
12360 buffer: &Model<Buffer>,
12361 buffer_position: text::Anchor,
12362 options: CompletionContext,
12363 cx: &mut ViewContext<Editor>,
12364 ) -> Task<Result<Vec<Completion>>> {
12365 self.update(cx, |project, cx| {
12366 let snippets = snippet_completions(project, buffer, buffer_position, cx);
12367 let project_completions = project.completions(&buffer, buffer_position, options, cx);
12368 cx.background_executor().spawn(async move {
12369 let mut completions = project_completions.await?;
12370 //let snippets = snippets.into_iter().;
12371 completions.extend(snippets);
12372 Ok(completions)
12373 })
12374 })
12375 }
12376
12377 fn resolve_completions(
12378 &self,
12379 buffer: Model<Buffer>,
12380 completion_indices: Vec<usize>,
12381 completions: Arc<RwLock<Box<[Completion]>>>,
12382 cx: &mut ViewContext<Editor>,
12383 ) -> Task<Result<bool>> {
12384 self.update(cx, |project, cx| {
12385 project.resolve_completions(buffer, completion_indices, completions, cx)
12386 })
12387 }
12388
12389 fn apply_additional_edits_for_completion(
12390 &self,
12391 buffer: Model<Buffer>,
12392 completion: Completion,
12393 push_to_history: bool,
12394 cx: &mut ViewContext<Editor>,
12395 ) -> Task<Result<Option<language::Transaction>>> {
12396 self.update(cx, |project, cx| {
12397 project.apply_additional_edits_for_completion(buffer, completion, push_to_history, cx)
12398 })
12399 }
12400
12401 fn is_completion_trigger(
12402 &self,
12403 buffer: &Model<Buffer>,
12404 position: language::Anchor,
12405 text: &str,
12406 trigger_in_words: bool,
12407 cx: &mut ViewContext<Editor>,
12408 ) -> bool {
12409 if !EditorSettings::get_global(cx).show_completions_on_input {
12410 return false;
12411 }
12412
12413 let mut chars = text.chars();
12414 let char = if let Some(char) = chars.next() {
12415 char
12416 } else {
12417 return false;
12418 };
12419 if chars.next().is_some() {
12420 return false;
12421 }
12422
12423 let buffer = buffer.read(cx);
12424 let scope = buffer.snapshot().language_scope_at(position);
12425 if trigger_in_words && char_kind(&scope, char) == CharKind::Word {
12426 return true;
12427 }
12428
12429 buffer
12430 .completion_triggers()
12431 .iter()
12432 .any(|string| string == text)
12433 }
12434}
12435
12436fn inlay_hint_settings(
12437 location: Anchor,
12438 snapshot: &MultiBufferSnapshot,
12439 cx: &mut ViewContext<'_, Editor>,
12440) -> InlayHintSettings {
12441 let file = snapshot.file_at(location);
12442 let language = snapshot.language_at(location);
12443 let settings = all_language_settings(file, cx);
12444 settings
12445 .language(language.map(|l| l.name()).as_deref())
12446 .inlay_hints
12447}
12448
12449fn consume_contiguous_rows(
12450 contiguous_row_selections: &mut Vec<Selection<Point>>,
12451 selection: &Selection<Point>,
12452 display_map: &DisplaySnapshot,
12453 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
12454) -> (MultiBufferRow, MultiBufferRow) {
12455 contiguous_row_selections.push(selection.clone());
12456 let start_row = MultiBufferRow(selection.start.row);
12457 let mut end_row = ending_row(selection, display_map);
12458
12459 while let Some(next_selection) = selections.peek() {
12460 if next_selection.start.row <= end_row.0 {
12461 end_row = ending_row(next_selection, display_map);
12462 contiguous_row_selections.push(selections.next().unwrap().clone());
12463 } else {
12464 break;
12465 }
12466 }
12467 (start_row, end_row)
12468}
12469
12470fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
12471 if next_selection.end.column > 0 || next_selection.is_empty() {
12472 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
12473 } else {
12474 MultiBufferRow(next_selection.end.row)
12475 }
12476}
12477
12478impl EditorSnapshot {
12479 pub fn remote_selections_in_range<'a>(
12480 &'a self,
12481 range: &'a Range<Anchor>,
12482 collaboration_hub: &dyn CollaborationHub,
12483 cx: &'a AppContext,
12484 ) -> impl 'a + Iterator<Item = RemoteSelection> {
12485 let participant_names = collaboration_hub.user_names(cx);
12486 let participant_indices = collaboration_hub.user_participant_indices(cx);
12487 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
12488 let collaborators_by_replica_id = collaborators_by_peer_id
12489 .iter()
12490 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
12491 .collect::<HashMap<_, _>>();
12492 self.buffer_snapshot
12493 .selections_in_range(range, false)
12494 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
12495 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
12496 let participant_index = participant_indices.get(&collaborator.user_id).copied();
12497 let user_name = participant_names.get(&collaborator.user_id).cloned();
12498 Some(RemoteSelection {
12499 replica_id,
12500 selection,
12501 cursor_shape,
12502 line_mode,
12503 participant_index,
12504 peer_id: collaborator.peer_id,
12505 user_name,
12506 })
12507 })
12508 }
12509
12510 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
12511 self.display_snapshot.buffer_snapshot.language_at(position)
12512 }
12513
12514 pub fn is_focused(&self) -> bool {
12515 self.is_focused
12516 }
12517
12518 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
12519 self.placeholder_text.as_ref()
12520 }
12521
12522 pub fn scroll_position(&self) -> gpui::Point<f32> {
12523 self.scroll_anchor.scroll_position(&self.display_snapshot)
12524 }
12525
12526 fn gutter_dimensions(
12527 &self,
12528 font_id: FontId,
12529 font_size: Pixels,
12530 em_width: Pixels,
12531 max_line_number_width: Pixels,
12532 cx: &AppContext,
12533 ) -> GutterDimensions {
12534 if !self.show_gutter {
12535 return GutterDimensions::default();
12536 }
12537 let descent = cx.text_system().descent(font_id, font_size);
12538
12539 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
12540 matches!(
12541 ProjectSettings::get_global(cx).git.git_gutter,
12542 Some(GitGutterSetting::TrackedFiles)
12543 )
12544 });
12545 let gutter_settings = EditorSettings::get_global(cx).gutter;
12546 let show_line_numbers = self
12547 .show_line_numbers
12548 .unwrap_or(gutter_settings.line_numbers);
12549 let line_gutter_width = if show_line_numbers {
12550 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
12551 let min_width_for_number_on_gutter = em_width * 4.0;
12552 max_line_number_width.max(min_width_for_number_on_gutter)
12553 } else {
12554 0.0.into()
12555 };
12556
12557 let show_code_actions = self
12558 .show_code_actions
12559 .unwrap_or(gutter_settings.code_actions);
12560
12561 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
12562
12563 let git_blame_entries_width = self
12564 .render_git_blame_gutter
12565 .then_some(em_width * GIT_BLAME_GUTTER_WIDTH_CHARS);
12566
12567 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
12568 left_padding += if show_code_actions || show_runnables {
12569 em_width * 3.0
12570 } else if show_git_gutter && show_line_numbers {
12571 em_width * 2.0
12572 } else if show_git_gutter || show_line_numbers {
12573 em_width
12574 } else {
12575 px(0.)
12576 };
12577
12578 let right_padding = if gutter_settings.folds && show_line_numbers {
12579 em_width * 4.0
12580 } else if gutter_settings.folds {
12581 em_width * 3.0
12582 } else if show_line_numbers {
12583 em_width
12584 } else {
12585 px(0.)
12586 };
12587
12588 GutterDimensions {
12589 left_padding,
12590 right_padding,
12591 width: line_gutter_width + left_padding + right_padding,
12592 margin: -descent,
12593 git_blame_entries_width,
12594 }
12595 }
12596
12597 pub fn render_fold_toggle(
12598 &self,
12599 buffer_row: MultiBufferRow,
12600 row_contains_cursor: bool,
12601 editor: View<Editor>,
12602 cx: &mut WindowContext,
12603 ) -> Option<AnyElement> {
12604 let folded = self.is_line_folded(buffer_row);
12605
12606 if let Some(crease) = self
12607 .crease_snapshot
12608 .query_row(buffer_row, &self.buffer_snapshot)
12609 {
12610 let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
12611 if folded {
12612 editor.update(cx, |editor, cx| {
12613 editor.fold_at(&crate::FoldAt { buffer_row }, cx)
12614 });
12615 } else {
12616 editor.update(cx, |editor, cx| {
12617 editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
12618 });
12619 }
12620 });
12621
12622 Some((crease.render_toggle)(
12623 buffer_row,
12624 folded,
12625 toggle_callback,
12626 cx,
12627 ))
12628 } else if folded
12629 || (self.starts_indent(buffer_row) && (row_contains_cursor || self.gutter_hovered))
12630 {
12631 Some(
12632 Disclosure::new(("indent-fold-indicator", buffer_row.0), !folded)
12633 .selected(folded)
12634 .on_click(cx.listener_for(&editor, move |this, _e, cx| {
12635 if folded {
12636 this.unfold_at(&UnfoldAt { buffer_row }, cx);
12637 } else {
12638 this.fold_at(&FoldAt { buffer_row }, cx);
12639 }
12640 }))
12641 .into_any_element(),
12642 )
12643 } else {
12644 None
12645 }
12646 }
12647
12648 pub fn render_crease_trailer(
12649 &self,
12650 buffer_row: MultiBufferRow,
12651 cx: &mut WindowContext,
12652 ) -> Option<AnyElement> {
12653 let folded = self.is_line_folded(buffer_row);
12654 let crease = self
12655 .crease_snapshot
12656 .query_row(buffer_row, &self.buffer_snapshot)?;
12657 Some((crease.render_trailer)(buffer_row, folded, cx))
12658 }
12659}
12660
12661impl Deref for EditorSnapshot {
12662 type Target = DisplaySnapshot;
12663
12664 fn deref(&self) -> &Self::Target {
12665 &self.display_snapshot
12666 }
12667}
12668
12669#[derive(Clone, Debug, PartialEq, Eq)]
12670pub enum EditorEvent {
12671 InputIgnored {
12672 text: Arc<str>,
12673 },
12674 InputHandled {
12675 utf16_range_to_replace: Option<Range<isize>>,
12676 text: Arc<str>,
12677 },
12678 ExcerptsAdded {
12679 buffer: Model<Buffer>,
12680 predecessor: ExcerptId,
12681 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
12682 },
12683 ExcerptsRemoved {
12684 ids: Vec<ExcerptId>,
12685 },
12686 ExcerptsEdited {
12687 ids: Vec<ExcerptId>,
12688 },
12689 ExcerptsExpanded {
12690 ids: Vec<ExcerptId>,
12691 },
12692 BufferEdited,
12693 Edited {
12694 transaction_id: clock::Lamport,
12695 },
12696 Reparsed(BufferId),
12697 Focused,
12698 FocusedIn,
12699 Blurred,
12700 DirtyChanged,
12701 Saved,
12702 TitleChanged,
12703 DiffBaseChanged,
12704 SelectionsChanged {
12705 local: bool,
12706 },
12707 ScrollPositionChanged {
12708 local: bool,
12709 autoscroll: bool,
12710 },
12711 Closed,
12712 TransactionUndone {
12713 transaction_id: clock::Lamport,
12714 },
12715 TransactionBegun {
12716 transaction_id: clock::Lamport,
12717 },
12718}
12719
12720impl EventEmitter<EditorEvent> for Editor {}
12721
12722impl FocusableView for Editor {
12723 fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
12724 self.focus_handle.clone()
12725 }
12726}
12727
12728impl Render for Editor {
12729 fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
12730 let settings = ThemeSettings::get_global(cx);
12731
12732 let text_style = match self.mode {
12733 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
12734 color: cx.theme().colors().editor_foreground,
12735 font_family: settings.ui_font.family.clone(),
12736 font_features: settings.ui_font.features.clone(),
12737 font_fallbacks: settings.ui_font.fallbacks.clone(),
12738 font_size: rems(0.875).into(),
12739 font_weight: settings.ui_font.weight,
12740 line_height: relative(settings.buffer_line_height.value()),
12741 ..Default::default()
12742 },
12743 EditorMode::Full => TextStyle {
12744 color: cx.theme().colors().editor_foreground,
12745 font_family: settings.buffer_font.family.clone(),
12746 font_features: settings.buffer_font.features.clone(),
12747 font_fallbacks: settings.buffer_font.fallbacks.clone(),
12748 font_size: settings.buffer_font_size(cx).into(),
12749 font_weight: settings.buffer_font.weight,
12750 line_height: relative(settings.buffer_line_height.value()),
12751 ..Default::default()
12752 },
12753 };
12754
12755 let background = match self.mode {
12756 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
12757 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
12758 EditorMode::Full => cx.theme().colors().editor_background,
12759 };
12760
12761 EditorElement::new(
12762 cx.view(),
12763 EditorStyle {
12764 background,
12765 local_player: cx.theme().players().local(),
12766 text: text_style,
12767 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
12768 syntax: cx.theme().syntax().clone(),
12769 status: cx.theme().status().clone(),
12770 inlay_hints_style: HighlightStyle {
12771 color: Some(cx.theme().status().hint),
12772 ..HighlightStyle::default()
12773 },
12774 suggestions_style: HighlightStyle {
12775 color: Some(cx.theme().status().predictive),
12776 ..HighlightStyle::default()
12777 },
12778 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
12779 },
12780 )
12781 }
12782}
12783
12784impl ViewInputHandler for Editor {
12785 fn text_for_range(
12786 &mut self,
12787 range_utf16: Range<usize>,
12788 cx: &mut ViewContext<Self>,
12789 ) -> Option<String> {
12790 Some(
12791 self.buffer
12792 .read(cx)
12793 .read(cx)
12794 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
12795 .collect(),
12796 )
12797 }
12798
12799 fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
12800 // Prevent the IME menu from appearing when holding down an alphabetic key
12801 // while input is disabled.
12802 if !self.input_enabled {
12803 return None;
12804 }
12805
12806 let range = self.selections.newest::<OffsetUtf16>(cx).range();
12807 Some(range.start.0..range.end.0)
12808 }
12809
12810 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
12811 let snapshot = self.buffer.read(cx).read(cx);
12812 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
12813 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
12814 }
12815
12816 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
12817 self.clear_highlights::<InputComposition>(cx);
12818 self.ime_transaction.take();
12819 }
12820
12821 fn replace_text_in_range(
12822 &mut self,
12823 range_utf16: Option<Range<usize>>,
12824 text: &str,
12825 cx: &mut ViewContext<Self>,
12826 ) {
12827 if !self.input_enabled {
12828 cx.emit(EditorEvent::InputIgnored { text: text.into() });
12829 return;
12830 }
12831
12832 self.transact(cx, |this, cx| {
12833 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
12834 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
12835 Some(this.selection_replacement_ranges(range_utf16, cx))
12836 } else {
12837 this.marked_text_ranges(cx)
12838 };
12839
12840 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
12841 let newest_selection_id = this.selections.newest_anchor().id;
12842 this.selections
12843 .all::<OffsetUtf16>(cx)
12844 .iter()
12845 .zip(ranges_to_replace.iter())
12846 .find_map(|(selection, range)| {
12847 if selection.id == newest_selection_id {
12848 Some(
12849 (range.start.0 as isize - selection.head().0 as isize)
12850 ..(range.end.0 as isize - selection.head().0 as isize),
12851 )
12852 } else {
12853 None
12854 }
12855 })
12856 });
12857
12858 cx.emit(EditorEvent::InputHandled {
12859 utf16_range_to_replace: range_to_replace,
12860 text: text.into(),
12861 });
12862
12863 if let Some(new_selected_ranges) = new_selected_ranges {
12864 this.change_selections(None, cx, |selections| {
12865 selections.select_ranges(new_selected_ranges)
12866 });
12867 this.backspace(&Default::default(), cx);
12868 }
12869
12870 this.handle_input(text, cx);
12871 });
12872
12873 if let Some(transaction) = self.ime_transaction {
12874 self.buffer.update(cx, |buffer, cx| {
12875 buffer.group_until_transaction(transaction, cx);
12876 });
12877 }
12878
12879 self.unmark_text(cx);
12880 }
12881
12882 fn replace_and_mark_text_in_range(
12883 &mut self,
12884 range_utf16: Option<Range<usize>>,
12885 text: &str,
12886 new_selected_range_utf16: Option<Range<usize>>,
12887 cx: &mut ViewContext<Self>,
12888 ) {
12889 if !self.input_enabled {
12890 return;
12891 }
12892
12893 let transaction = self.transact(cx, |this, cx| {
12894 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
12895 let snapshot = this.buffer.read(cx).read(cx);
12896 if let Some(relative_range_utf16) = range_utf16.as_ref() {
12897 for marked_range in &mut marked_ranges {
12898 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
12899 marked_range.start.0 += relative_range_utf16.start;
12900 marked_range.start =
12901 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
12902 marked_range.end =
12903 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
12904 }
12905 }
12906 Some(marked_ranges)
12907 } else if let Some(range_utf16) = range_utf16 {
12908 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
12909 Some(this.selection_replacement_ranges(range_utf16, cx))
12910 } else {
12911 None
12912 };
12913
12914 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
12915 let newest_selection_id = this.selections.newest_anchor().id;
12916 this.selections
12917 .all::<OffsetUtf16>(cx)
12918 .iter()
12919 .zip(ranges_to_replace.iter())
12920 .find_map(|(selection, range)| {
12921 if selection.id == newest_selection_id {
12922 Some(
12923 (range.start.0 as isize - selection.head().0 as isize)
12924 ..(range.end.0 as isize - selection.head().0 as isize),
12925 )
12926 } else {
12927 None
12928 }
12929 })
12930 });
12931
12932 cx.emit(EditorEvent::InputHandled {
12933 utf16_range_to_replace: range_to_replace,
12934 text: text.into(),
12935 });
12936
12937 if let Some(ranges) = ranges_to_replace {
12938 this.change_selections(None, cx, |s| s.select_ranges(ranges));
12939 }
12940
12941 let marked_ranges = {
12942 let snapshot = this.buffer.read(cx).read(cx);
12943 this.selections
12944 .disjoint_anchors()
12945 .iter()
12946 .map(|selection| {
12947 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
12948 })
12949 .collect::<Vec<_>>()
12950 };
12951
12952 if text.is_empty() {
12953 this.unmark_text(cx);
12954 } else {
12955 this.highlight_text::<InputComposition>(
12956 marked_ranges.clone(),
12957 HighlightStyle {
12958 underline: Some(UnderlineStyle {
12959 thickness: px(1.),
12960 color: None,
12961 wavy: false,
12962 }),
12963 ..Default::default()
12964 },
12965 cx,
12966 );
12967 }
12968
12969 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
12970 let use_autoclose = this.use_autoclose;
12971 let use_auto_surround = this.use_auto_surround;
12972 this.set_use_autoclose(false);
12973 this.set_use_auto_surround(false);
12974 this.handle_input(text, cx);
12975 this.set_use_autoclose(use_autoclose);
12976 this.set_use_auto_surround(use_auto_surround);
12977
12978 if let Some(new_selected_range) = new_selected_range_utf16 {
12979 let snapshot = this.buffer.read(cx).read(cx);
12980 let new_selected_ranges = marked_ranges
12981 .into_iter()
12982 .map(|marked_range| {
12983 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
12984 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
12985 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
12986 snapshot.clip_offset_utf16(new_start, Bias::Left)
12987 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
12988 })
12989 .collect::<Vec<_>>();
12990
12991 drop(snapshot);
12992 this.change_selections(None, cx, |selections| {
12993 selections.select_ranges(new_selected_ranges)
12994 });
12995 }
12996 });
12997
12998 self.ime_transaction = self.ime_transaction.or(transaction);
12999 if let Some(transaction) = self.ime_transaction {
13000 self.buffer.update(cx, |buffer, cx| {
13001 buffer.group_until_transaction(transaction, cx);
13002 });
13003 }
13004
13005 if self.text_highlights::<InputComposition>(cx).is_none() {
13006 self.ime_transaction.take();
13007 }
13008 }
13009
13010 fn bounds_for_range(
13011 &mut self,
13012 range_utf16: Range<usize>,
13013 element_bounds: gpui::Bounds<Pixels>,
13014 cx: &mut ViewContext<Self>,
13015 ) -> Option<gpui::Bounds<Pixels>> {
13016 let text_layout_details = self.text_layout_details(cx);
13017 let style = &text_layout_details.editor_style;
13018 let font_id = cx.text_system().resolve_font(&style.text.font());
13019 let font_size = style.text.font_size.to_pixels(cx.rem_size());
13020 let line_height = style.text.line_height_in_pixels(cx.rem_size());
13021
13022 let em_width = cx
13023 .text_system()
13024 .typographic_bounds(font_id, font_size, 'm')
13025 .unwrap()
13026 .size
13027 .width;
13028
13029 let snapshot = self.snapshot(cx);
13030 let scroll_position = snapshot.scroll_position();
13031 let scroll_left = scroll_position.x * em_width;
13032
13033 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
13034 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
13035 + self.gutter_dimensions.width;
13036 let y = line_height * (start.row().as_f32() - scroll_position.y);
13037
13038 Some(Bounds {
13039 origin: element_bounds.origin + point(x, y),
13040 size: size(em_width, line_height),
13041 })
13042 }
13043}
13044
13045trait SelectionExt {
13046 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
13047 fn spanned_rows(
13048 &self,
13049 include_end_if_at_line_start: bool,
13050 map: &DisplaySnapshot,
13051 ) -> Range<MultiBufferRow>;
13052}
13053
13054impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
13055 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
13056 let start = self
13057 .start
13058 .to_point(&map.buffer_snapshot)
13059 .to_display_point(map);
13060 let end = self
13061 .end
13062 .to_point(&map.buffer_snapshot)
13063 .to_display_point(map);
13064 if self.reversed {
13065 end..start
13066 } else {
13067 start..end
13068 }
13069 }
13070
13071 fn spanned_rows(
13072 &self,
13073 include_end_if_at_line_start: bool,
13074 map: &DisplaySnapshot,
13075 ) -> Range<MultiBufferRow> {
13076 let start = self.start.to_point(&map.buffer_snapshot);
13077 let mut end = self.end.to_point(&map.buffer_snapshot);
13078 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
13079 end.row -= 1;
13080 }
13081
13082 let buffer_start = map.prev_line_boundary(start).0;
13083 let buffer_end = map.next_line_boundary(end).0;
13084 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
13085 }
13086}
13087
13088impl<T: InvalidationRegion> InvalidationStack<T> {
13089 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
13090 where
13091 S: Clone + ToOffset,
13092 {
13093 while let Some(region) = self.last() {
13094 let all_selections_inside_invalidation_ranges =
13095 if selections.len() == region.ranges().len() {
13096 selections
13097 .iter()
13098 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
13099 .all(|(selection, invalidation_range)| {
13100 let head = selection.head().to_offset(buffer);
13101 invalidation_range.start <= head && invalidation_range.end >= head
13102 })
13103 } else {
13104 false
13105 };
13106
13107 if all_selections_inside_invalidation_ranges {
13108 break;
13109 } else {
13110 self.pop();
13111 }
13112 }
13113 }
13114}
13115
13116impl<T> Default for InvalidationStack<T> {
13117 fn default() -> Self {
13118 Self(Default::default())
13119 }
13120}
13121
13122impl<T> Deref for InvalidationStack<T> {
13123 type Target = Vec<T>;
13124
13125 fn deref(&self) -> &Self::Target {
13126 &self.0
13127 }
13128}
13129
13130impl<T> DerefMut for InvalidationStack<T> {
13131 fn deref_mut(&mut self) -> &mut Self::Target {
13132 &mut self.0
13133 }
13134}
13135
13136impl InvalidationRegion for SnippetState {
13137 fn ranges(&self) -> &[Range<Anchor>] {
13138 &self.ranges[self.active_index]
13139 }
13140}
13141
13142pub fn diagnostic_block_renderer(
13143 diagnostic: Diagnostic,
13144 max_message_rows: Option<u8>,
13145 allow_closing: bool,
13146 _is_valid: bool,
13147) -> RenderBlock {
13148 let (text_without_backticks, code_ranges) =
13149 highlight_diagnostic_message(&diagnostic, max_message_rows);
13150
13151 Box::new(move |cx: &mut BlockContext| {
13152 let group_id: SharedString = cx.block_id.to_string().into();
13153
13154 let mut text_style = cx.text_style().clone();
13155 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
13156 let theme_settings = ThemeSettings::get_global(cx);
13157 text_style.font_family = theme_settings.buffer_font.family.clone();
13158 text_style.font_style = theme_settings.buffer_font.style;
13159 text_style.font_features = theme_settings.buffer_font.features.clone();
13160 text_style.font_weight = theme_settings.buffer_font.weight;
13161
13162 let multi_line_diagnostic = diagnostic.message.contains('\n');
13163
13164 let buttons = |diagnostic: &Diagnostic, block_id: BlockId| {
13165 if multi_line_diagnostic {
13166 v_flex()
13167 } else {
13168 h_flex()
13169 }
13170 .when(allow_closing, |div| {
13171 div.children(diagnostic.is_primary.then(|| {
13172 IconButton::new(("close-block", EntityId::from(block_id)), IconName::XCircle)
13173 .icon_color(Color::Muted)
13174 .size(ButtonSize::Compact)
13175 .style(ButtonStyle::Transparent)
13176 .visible_on_hover(group_id.clone())
13177 .on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
13178 .tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
13179 }))
13180 })
13181 .child(
13182 IconButton::new(("copy-block", EntityId::from(block_id)), IconName::Copy)
13183 .icon_color(Color::Muted)
13184 .size(ButtonSize::Compact)
13185 .style(ButtonStyle::Transparent)
13186 .visible_on_hover(group_id.clone())
13187 .on_click({
13188 let message = diagnostic.message.clone();
13189 move |_click, cx| {
13190 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
13191 }
13192 })
13193 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
13194 )
13195 };
13196
13197 let icon_size = buttons(&diagnostic, cx.block_id)
13198 .into_any_element()
13199 .layout_as_root(AvailableSpace::min_size(), cx);
13200
13201 h_flex()
13202 .id(cx.block_id)
13203 .group(group_id.clone())
13204 .relative()
13205 .size_full()
13206 .pl(cx.gutter_dimensions.width)
13207 .w(cx.max_width + cx.gutter_dimensions.width)
13208 .child(
13209 div()
13210 .flex()
13211 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
13212 .flex_shrink(),
13213 )
13214 .child(buttons(&diagnostic, cx.block_id))
13215 .child(div().flex().flex_shrink_0().child(
13216 StyledText::new(text_without_backticks.clone()).with_highlights(
13217 &text_style,
13218 code_ranges.iter().map(|range| {
13219 (
13220 range.clone(),
13221 HighlightStyle {
13222 font_weight: Some(FontWeight::BOLD),
13223 ..Default::default()
13224 },
13225 )
13226 }),
13227 ),
13228 ))
13229 .into_any_element()
13230 })
13231}
13232
13233pub fn highlight_diagnostic_message(
13234 diagnostic: &Diagnostic,
13235 mut max_message_rows: Option<u8>,
13236) -> (SharedString, Vec<Range<usize>>) {
13237 let mut text_without_backticks = String::new();
13238 let mut code_ranges = Vec::new();
13239
13240 if let Some(source) = &diagnostic.source {
13241 text_without_backticks.push_str(&source);
13242 code_ranges.push(0..source.len());
13243 text_without_backticks.push_str(": ");
13244 }
13245
13246 let mut prev_offset = 0;
13247 let mut in_code_block = false;
13248 let has_row_limit = max_message_rows.is_some();
13249 let mut newline_indices = diagnostic
13250 .message
13251 .match_indices('\n')
13252 .filter(|_| has_row_limit)
13253 .map(|(ix, _)| ix)
13254 .fuse()
13255 .peekable();
13256
13257 for (quote_ix, _) in diagnostic
13258 .message
13259 .match_indices('`')
13260 .chain([(diagnostic.message.len(), "")])
13261 {
13262 let mut first_newline_ix = None;
13263 let mut last_newline_ix = None;
13264 while let Some(newline_ix) = newline_indices.peek() {
13265 if *newline_ix < quote_ix {
13266 if first_newline_ix.is_none() {
13267 first_newline_ix = Some(*newline_ix);
13268 }
13269 last_newline_ix = Some(*newline_ix);
13270
13271 if let Some(rows_left) = &mut max_message_rows {
13272 if *rows_left == 0 {
13273 break;
13274 } else {
13275 *rows_left -= 1;
13276 }
13277 }
13278 let _ = newline_indices.next();
13279 } else {
13280 break;
13281 }
13282 }
13283 let prev_len = text_without_backticks.len();
13284 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
13285 text_without_backticks.push_str(new_text);
13286 if in_code_block {
13287 code_ranges.push(prev_len..text_without_backticks.len());
13288 }
13289 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
13290 in_code_block = !in_code_block;
13291 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
13292 text_without_backticks.push_str("...");
13293 break;
13294 }
13295 }
13296
13297 (text_without_backticks.into(), code_ranges)
13298}
13299
13300fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
13301 match severity {
13302 DiagnosticSeverity::ERROR => colors.error,
13303 DiagnosticSeverity::WARNING => colors.warning,
13304 DiagnosticSeverity::INFORMATION => colors.info,
13305 DiagnosticSeverity::HINT => colors.info,
13306 _ => colors.ignored,
13307 }
13308}
13309
13310pub fn styled_runs_for_code_label<'a>(
13311 label: &'a CodeLabel,
13312 syntax_theme: &'a theme::SyntaxTheme,
13313) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
13314 let fade_out = HighlightStyle {
13315 fade_out: Some(0.35),
13316 ..Default::default()
13317 };
13318
13319 let mut prev_end = label.filter_range.end;
13320 label
13321 .runs
13322 .iter()
13323 .enumerate()
13324 .flat_map(move |(ix, (range, highlight_id))| {
13325 let style = if let Some(style) = highlight_id.style(syntax_theme) {
13326 style
13327 } else {
13328 return Default::default();
13329 };
13330 let mut muted_style = style;
13331 muted_style.highlight(fade_out);
13332
13333 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
13334 if range.start >= label.filter_range.end {
13335 if range.start > prev_end {
13336 runs.push((prev_end..range.start, fade_out));
13337 }
13338 runs.push((range.clone(), muted_style));
13339 } else if range.end <= label.filter_range.end {
13340 runs.push((range.clone(), style));
13341 } else {
13342 runs.push((range.start..label.filter_range.end, style));
13343 runs.push((label.filter_range.end..range.end, muted_style));
13344 }
13345 prev_end = cmp::max(prev_end, range.end);
13346
13347 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
13348 runs.push((prev_end..label.text.len(), fade_out));
13349 }
13350
13351 runs
13352 })
13353}
13354
13355pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
13356 let mut prev_index = 0;
13357 let mut prev_codepoint: Option<char> = None;
13358 text.char_indices()
13359 .chain([(text.len(), '\0')])
13360 .filter_map(move |(index, codepoint)| {
13361 let prev_codepoint = prev_codepoint.replace(codepoint)?;
13362 let is_boundary = index == text.len()
13363 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
13364 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
13365 if is_boundary {
13366 let chunk = &text[prev_index..index];
13367 prev_index = index;
13368 Some(chunk)
13369 } else {
13370 None
13371 }
13372 })
13373}
13374
13375pub trait RangeToAnchorExt: Sized {
13376 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
13377
13378 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
13379 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
13380 anchor_range.start.to_display_point(&snapshot)..anchor_range.end.to_display_point(&snapshot)
13381 }
13382}
13383
13384impl<T: ToOffset> RangeToAnchorExt for Range<T> {
13385 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
13386 let start_offset = self.start.to_offset(snapshot);
13387 let end_offset = self.end.to_offset(snapshot);
13388 if start_offset == end_offset {
13389 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
13390 } else {
13391 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
13392 }
13393 }
13394}
13395
13396pub trait RowExt {
13397 fn as_f32(&self) -> f32;
13398
13399 fn next_row(&self) -> Self;
13400
13401 fn previous_row(&self) -> Self;
13402
13403 fn minus(&self, other: Self) -> u32;
13404}
13405
13406impl RowExt for DisplayRow {
13407 fn as_f32(&self) -> f32 {
13408 self.0 as f32
13409 }
13410
13411 fn next_row(&self) -> Self {
13412 Self(self.0 + 1)
13413 }
13414
13415 fn previous_row(&self) -> Self {
13416 Self(self.0.saturating_sub(1))
13417 }
13418
13419 fn minus(&self, other: Self) -> u32 {
13420 self.0 - other.0
13421 }
13422}
13423
13424impl RowExt for MultiBufferRow {
13425 fn as_f32(&self) -> f32 {
13426 self.0 as f32
13427 }
13428
13429 fn next_row(&self) -> Self {
13430 Self(self.0 + 1)
13431 }
13432
13433 fn previous_row(&self) -> Self {
13434 Self(self.0.saturating_sub(1))
13435 }
13436
13437 fn minus(&self, other: Self) -> u32 {
13438 self.0 - other.0
13439 }
13440}
13441
13442trait RowRangeExt {
13443 type Row;
13444
13445 fn len(&self) -> usize;
13446
13447 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
13448}
13449
13450impl RowRangeExt for Range<MultiBufferRow> {
13451 type Row = MultiBufferRow;
13452
13453 fn len(&self) -> usize {
13454 (self.end.0 - self.start.0) as usize
13455 }
13456
13457 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
13458 (self.start.0..self.end.0).map(MultiBufferRow)
13459 }
13460}
13461
13462impl RowRangeExt for Range<DisplayRow> {
13463 type Row = DisplayRow;
13464
13465 fn len(&self) -> usize {
13466 (self.end.0 - self.start.0) as usize
13467 }
13468
13469 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
13470 (self.start.0..self.end.0).map(DisplayRow)
13471 }
13472}
13473
13474fn hunk_status(hunk: &DiffHunk<MultiBufferRow>) -> DiffHunkStatus {
13475 if hunk.diff_base_byte_range.is_empty() {
13476 DiffHunkStatus::Added
13477 } else if hunk.associated_range.is_empty() {
13478 DiffHunkStatus::Removed
13479 } else {
13480 DiffHunkStatus::Modified
13481 }
13482}
13483
13484/// If select range has more than one line, we
13485/// just point the cursor to range.start.
13486fn check_multiline_range(buffer: &Buffer, range: Range<usize>) -> Range<usize> {
13487 if buffer.offset_to_point(range.start).row == buffer.offset_to_point(range.end).row {
13488 range
13489 } else {
13490 range.start..range.start
13491 }
13492}