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 debounced_delay;
19pub mod display_map;
20mod editor_settings;
21mod editor_settings_controls;
22mod element;
23mod git;
24mod highlight_matching_bracket;
25mod hover_links;
26mod hover_popover;
27mod hunk_diff;
28mod indent_guides;
29mod inlay_hint_cache;
30mod inline_completion_provider;
31pub mod items;
32mod linked_editing_ranges;
33mod mouse_context_menu;
34pub mod movement;
35mod persistence;
36mod rust_analyzer_ext;
37pub mod scroll;
38mod selections_collection;
39pub mod tasks;
40
41#[cfg(test)]
42mod editor_tests;
43mod signature_help;
44#[cfg(any(test, feature = "test-support"))]
45pub mod test;
46
47use ::git::diff::{DiffHunk, DiffHunkStatus};
48use ::git::{parse_git_remote_url, BuildPermalinkParams, GitHostingProviderRegistry};
49pub(crate) use actions::*;
50use aho_corasick::AhoCorasick;
51use anyhow::{anyhow, Context as _, Result};
52use blink_manager::BlinkManager;
53use client::{Collaborator, ParticipantIndex};
54use clock::ReplicaId;
55use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
56use convert_case::{Case, Casing};
57use debounced_delay::DebouncedDelay;
58use display_map::*;
59pub use display_map::{DisplayPoint, FoldPlaceholder};
60pub use editor_settings::{CurrentLineHighlight, EditorSettings};
61pub use editor_settings_controls::*;
62use element::LineWithInvisibles;
63pub use element::{
64 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
65};
66use futures::FutureExt;
67use fuzzy::{StringMatch, StringMatchCandidate};
68use git::blame::GitBlame;
69use git::diff_hunk_to_display;
70use gpui::{
71 div, impl_actions, point, prelude::*, px, relative, size, uniform_list, Action, AnyElement,
72 AppContext, AsyncWindowContext, AvailableSpace, BackgroundExecutor, Bounds, ClipboardEntry,
73 ClipboardItem, Context, DispatchPhase, ElementId, EntityId, EventEmitter, FocusHandle,
74 FocusOutEvent, FocusableView, FontId, FontWeight, HighlightStyle, Hsla, InteractiveText,
75 KeyContext, ListSizingBehavior, Model, MouseButton, PaintQuad, ParentElement, Pixels, Render,
76 SharedString, Size, StrikethroughStyle, Styled, StyledText, Subscription, Task, TextStyle,
77 UnderlineStyle, UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext,
78 WeakFocusHandle, WeakView, WindowContext,
79};
80use highlight_matching_bracket::refresh_matching_bracket_highlights;
81use hover_popover::{hide_hover, HoverState};
82use hunk_diff::ExpandedHunks;
83pub(crate) use hunk_diff::HoveredHunk;
84use indent_guides::ActiveIndentGuidesState;
85use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
86pub use inline_completion_provider::*;
87pub use items::MAX_TAB_TITLE_LEN;
88use itertools::Itertools;
89use language::{
90 char_kind,
91 language_settings::{self, all_language_settings, InlayHintSettings},
92 markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
93 CursorShape, Diagnostic, Documentation, IndentKind, IndentSize, Language, OffsetRangeExt,
94 Point, Selection, SelectionGoal, TransactionId,
95};
96use language::{point_to_lsp, BufferRow, Runnable, RunnableRange};
97use linked_editing_ranges::refresh_linked_ranges;
98use task::{ResolvedTask, TaskTemplate, TaskVariables};
99
100use hover_links::{HoverLink, HoveredLinkState, InlayHighlight};
101pub use lsp::CompletionContext;
102use lsp::{
103 CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity, InsertTextFormat,
104 LanguageServerId,
105};
106use mouse_context_menu::MouseContextMenu;
107use movement::TextLayoutDetails;
108pub use multi_buffer::{
109 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
110 ToPoint,
111};
112use multi_buffer::{ExpandExcerptDirection, MultiBufferPoint, MultiBufferRow, ToOffsetUtf16};
113use ordered_float::OrderedFloat;
114use parking_lot::{Mutex, RwLock};
115use project::project_settings::{GitGutterSetting, ProjectSettings};
116use project::{
117 CodeAction, Completion, CompletionIntent, FormatTrigger, Item, Location, Project, ProjectPath,
118 ProjectTransaction, TaskSourceKind, WorktreeId,
119};
120use rand::prelude::*;
121use rpc::{proto::*, ErrorExt};
122use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
123use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
124use serde::{Deserialize, Serialize};
125use settings::{update_settings_file, Settings, SettingsStore};
126use smallvec::SmallVec;
127use snippet::Snippet;
128use std::{
129 any::TypeId,
130 borrow::Cow,
131 cell::RefCell,
132 cmp::{self, Ordering, Reverse},
133 mem,
134 num::NonZeroU32,
135 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
136 path::{Path, PathBuf},
137 rc::Rc,
138 sync::Arc,
139 time::{Duration, Instant},
140};
141pub use sum_tree::Bias;
142use sum_tree::TreeMap;
143use text::{BufferId, OffsetUtf16, Rope};
144use theme::{
145 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
146 ThemeColors, ThemeSettings,
147};
148use ui::{
149 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize,
150 ListItem, Popover, Tooltip,
151};
152use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
153use workspace::item::{ItemHandle, PreviewTabsSettings};
154use workspace::notifications::{DetachAndPromptErr, NotificationId};
155use workspace::{
156 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
157};
158use workspace::{OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
159
160use crate::hover_links::find_url;
161use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
162
163pub const FILE_HEADER_HEIGHT: u32 = 1;
164pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
165pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u32 = 1;
166pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
167const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
168const MAX_LINE_LEN: usize = 1024;
169const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
170const MAX_SELECTION_HISTORY_LEN: usize = 1024;
171pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
172#[doc(hidden)]
173pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
174#[doc(hidden)]
175pub const DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
176
177pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
178pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
179
180pub fn render_parsed_markdown(
181 element_id: impl Into<ElementId>,
182 parsed: &language::ParsedMarkdown,
183 editor_style: &EditorStyle,
184 workspace: Option<WeakView<Workspace>>,
185 cx: &mut WindowContext,
186) -> InteractiveText {
187 let code_span_background_color = cx
188 .theme()
189 .colors()
190 .editor_document_highlight_read_background;
191
192 let highlights = gpui::combine_highlights(
193 parsed.highlights.iter().filter_map(|(range, highlight)| {
194 let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
195 Some((range.clone(), highlight))
196 }),
197 parsed
198 .regions
199 .iter()
200 .zip(&parsed.region_ranges)
201 .filter_map(|(region, range)| {
202 if region.code {
203 Some((
204 range.clone(),
205 HighlightStyle {
206 background_color: Some(code_span_background_color),
207 ..Default::default()
208 },
209 ))
210 } else {
211 None
212 }
213 }),
214 );
215
216 let mut links = Vec::new();
217 let mut link_ranges = Vec::new();
218 for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
219 if let Some(link) = region.link.clone() {
220 links.push(link);
221 link_ranges.push(range.clone());
222 }
223 }
224
225 InteractiveText::new(
226 element_id,
227 StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
228 )
229 .on_click(link_ranges, move |clicked_range_ix, cx| {
230 match &links[clicked_range_ix] {
231 markdown::Link::Web { url } => cx.open_url(url),
232 markdown::Link::Path { path } => {
233 if let Some(workspace) = &workspace {
234 _ = workspace.update(cx, |workspace, cx| {
235 workspace.open_abs_path(path.clone(), false, cx).detach();
236 });
237 }
238 }
239 }
240 })
241}
242
243#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
244pub(crate) enum InlayId {
245 Suggestion(usize),
246 Hint(usize),
247}
248
249impl InlayId {
250 fn id(&self) -> usize {
251 match self {
252 Self::Suggestion(id) => *id,
253 Self::Hint(id) => *id,
254 }
255 }
256}
257
258enum DiffRowHighlight {}
259enum DocumentHighlightRead {}
260enum DocumentHighlightWrite {}
261enum InputComposition {}
262
263#[derive(Copy, Clone, PartialEq, Eq)]
264pub enum Direction {
265 Prev,
266 Next,
267}
268
269pub fn init_settings(cx: &mut AppContext) {
270 EditorSettings::register(cx);
271}
272
273pub fn init(cx: &mut AppContext) {
274 init_settings(cx);
275
276 workspace::register_project_item::<Editor>(cx);
277 workspace::FollowableViewRegistry::register::<Editor>(cx);
278 workspace::register_serializable_item::<Editor>(cx);
279
280 cx.observe_new_views(
281 |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
282 workspace.register_action(Editor::new_file);
283 workspace.register_action(Editor::new_file_in_direction);
284 },
285 )
286 .detach();
287
288 cx.on_action(move |_: &workspace::NewFile, cx| {
289 let app_state = workspace::AppState::global(cx);
290 if let Some(app_state) = app_state.upgrade() {
291 workspace::open_new(app_state, cx, |workspace, cx| {
292 Editor::new_file(workspace, &Default::default(), cx)
293 })
294 .detach();
295 }
296 });
297 cx.on_action(move |_: &workspace::NewWindow, cx| {
298 let app_state = workspace::AppState::global(cx);
299 if let Some(app_state) = app_state.upgrade() {
300 workspace::open_new(app_state, cx, |workspace, cx| {
301 Editor::new_file(workspace, &Default::default(), cx)
302 })
303 .detach();
304 }
305 });
306}
307
308pub struct SearchWithinRange;
309
310trait InvalidationRegion {
311 fn ranges(&self) -> &[Range<Anchor>];
312}
313
314#[derive(Clone, Debug, PartialEq)]
315pub enum SelectPhase {
316 Begin {
317 position: DisplayPoint,
318 add: bool,
319 click_count: usize,
320 },
321 BeginColumnar {
322 position: DisplayPoint,
323 reset: bool,
324 goal_column: u32,
325 },
326 Extend {
327 position: DisplayPoint,
328 click_count: usize,
329 },
330 Update {
331 position: DisplayPoint,
332 goal_column: u32,
333 scroll_delta: gpui::Point<f32>,
334 },
335 End,
336}
337
338#[derive(Clone, Debug)]
339pub enum SelectMode {
340 Character,
341 Word(Range<Anchor>),
342 Line(Range<Anchor>),
343 All,
344}
345
346#[derive(Copy, Clone, PartialEq, Eq, Debug)]
347pub enum EditorMode {
348 SingleLine { auto_width: bool },
349 AutoHeight { max_lines: usize },
350 Full,
351}
352
353#[derive(Clone, Debug)]
354pub enum SoftWrap {
355 None,
356 PreferLine,
357 EditorWidth,
358 Column(u32),
359}
360
361#[derive(Clone)]
362pub struct EditorStyle {
363 pub background: Hsla,
364 pub local_player: PlayerColor,
365 pub text: TextStyle,
366 pub scrollbar_width: Pixels,
367 pub syntax: Arc<SyntaxTheme>,
368 pub status: StatusColors,
369 pub inlay_hints_style: HighlightStyle,
370 pub suggestions_style: HighlightStyle,
371}
372
373impl Default for EditorStyle {
374 fn default() -> Self {
375 Self {
376 background: Hsla::default(),
377 local_player: PlayerColor::default(),
378 text: TextStyle::default(),
379 scrollbar_width: Pixels::default(),
380 syntax: Default::default(),
381 // HACK: Status colors don't have a real default.
382 // We should look into removing the status colors from the editor
383 // style and retrieve them directly from the theme.
384 status: StatusColors::dark(),
385 inlay_hints_style: HighlightStyle::default(),
386 suggestions_style: HighlightStyle::default(),
387 }
388 }
389}
390
391type CompletionId = usize;
392
393#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
394struct EditorActionId(usize);
395
396impl EditorActionId {
397 pub fn post_inc(&mut self) -> Self {
398 let answer = self.0;
399
400 *self = Self(answer + 1);
401
402 Self(answer)
403 }
404}
405
406// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
407// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
408
409type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
410type GutterHighlight = (fn(&AppContext) -> Hsla, Arc<[Range<Anchor>]>);
411
412#[derive(Default)]
413struct ScrollbarMarkerState {
414 scrollbar_size: Size<Pixels>,
415 dirty: bool,
416 markers: Arc<[PaintQuad]>,
417 pending_refresh: Option<Task<Result<()>>>,
418}
419
420impl ScrollbarMarkerState {
421 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
422 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
423 }
424}
425
426#[derive(Clone, Debug)]
427struct RunnableTasks {
428 templates: Vec<(TaskSourceKind, TaskTemplate)>,
429 offset: MultiBufferOffset,
430 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
431 column: u32,
432 // Values of all named captures, including those starting with '_'
433 extra_variables: HashMap<String, String>,
434 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
435 context_range: Range<BufferOffset>,
436}
437
438#[derive(Clone)]
439struct ResolvedTasks {
440 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
441 position: Anchor,
442}
443#[derive(Copy, Clone, Debug)]
444struct MultiBufferOffset(usize);
445#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
446struct BufferOffset(usize);
447/// Zed's primary text input `View`, allowing users to edit a [`MultiBuffer`]
448///
449/// See the [module level documentation](self) for more information.
450pub struct Editor {
451 focus_handle: FocusHandle,
452 last_focused_descendant: Option<WeakFocusHandle>,
453 /// The text buffer being edited
454 buffer: Model<MultiBuffer>,
455 /// Map of how text in the buffer should be displayed.
456 /// Handles soft wraps, folds, fake inlay text insertions, etc.
457 pub display_map: Model<DisplayMap>,
458 pub selections: SelectionsCollection,
459 pub scroll_manager: ScrollManager,
460 /// When inline assist editors are linked, they all render cursors because
461 /// typing enters text into each of them, even the ones that aren't focused.
462 pub(crate) show_cursor_when_unfocused: bool,
463 columnar_selection_tail: Option<Anchor>,
464 add_selections_state: Option<AddSelectionsState>,
465 select_next_state: Option<SelectNextState>,
466 select_prev_state: Option<SelectNextState>,
467 selection_history: SelectionHistory,
468 autoclose_regions: Vec<AutocloseRegion>,
469 snippet_stack: InvalidationStack<SnippetState>,
470 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
471 ime_transaction: Option<TransactionId>,
472 active_diagnostics: Option<ActiveDiagnosticGroup>,
473 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
474 project: Option<Model<Project>>,
475 completion_provider: Option<Box<dyn CompletionProvider>>,
476 collaboration_hub: Option<Box<dyn CollaborationHub>>,
477 blink_manager: Model<BlinkManager>,
478 show_cursor_names: bool,
479 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
480 pub show_local_selections: bool,
481 mode: EditorMode,
482 show_breadcrumbs: bool,
483 show_gutter: bool,
484 show_line_numbers: Option<bool>,
485 show_git_diff_gutter: Option<bool>,
486 show_code_actions: Option<bool>,
487 show_runnables: Option<bool>,
488 show_wrap_guides: Option<bool>,
489 show_indent_guides: Option<bool>,
490 placeholder_text: Option<Arc<str>>,
491 highlight_order: usize,
492 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
493 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
494 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
495 scrollbar_marker_state: ScrollbarMarkerState,
496 active_indent_guides_state: ActiveIndentGuidesState,
497 nav_history: Option<ItemNavHistory>,
498 context_menu: RwLock<Option<ContextMenu>>,
499 mouse_context_menu: Option<MouseContextMenu>,
500 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
501 signature_help_state: SignatureHelpState,
502 auto_signature_help: Option<bool>,
503 find_all_references_task_sources: Vec<Anchor>,
504 next_completion_id: CompletionId,
505 completion_documentation_pre_resolve_debounce: DebouncedDelay,
506 available_code_actions: Option<(Location, Arc<[CodeAction]>)>,
507 code_actions_task: Option<Task<()>>,
508 document_highlights_task: Option<Task<()>>,
509 linked_editing_range_task: Option<Task<Option<()>>>,
510 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
511 pending_rename: Option<RenameState>,
512 searchable: bool,
513 cursor_shape: CursorShape,
514 current_line_highlight: Option<CurrentLineHighlight>,
515 collapse_matches: bool,
516 autoindent_mode: Option<AutoindentMode>,
517 workspace: Option<(WeakView<Workspace>, Option<WorkspaceId>)>,
518 keymap_context_layers: BTreeMap<TypeId, KeyContext>,
519 input_enabled: bool,
520 use_modal_editing: bool,
521 read_only: bool,
522 leader_peer_id: Option<PeerId>,
523 remote_id: Option<ViewId>,
524 hover_state: HoverState,
525 gutter_hovered: bool,
526 hovered_link_state: Option<HoveredLinkState>,
527 inline_completion_provider: Option<RegisteredInlineCompletionProvider>,
528 active_inline_completion: Option<(Inlay, Option<Range<Anchor>>)>,
529 show_inline_completions: bool,
530 inlay_hint_cache: InlayHintCache,
531 expanded_hunks: ExpandedHunks,
532 next_inlay_id: usize,
533 _subscriptions: Vec<Subscription>,
534 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
535 gutter_dimensions: GutterDimensions,
536 pub vim_replace_map: HashMap<Range<usize>, String>,
537 style: Option<EditorStyle>,
538 next_editor_action_id: EditorActionId,
539 editor_actions: Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut ViewContext<Self>)>>>>,
540 use_autoclose: bool,
541 use_auto_surround: bool,
542 auto_replace_emoji_shortcode: bool,
543 show_git_blame_gutter: bool,
544 show_git_blame_inline: bool,
545 show_git_blame_inline_delay_task: Option<Task<()>>,
546 git_blame_inline_enabled: bool,
547 serialize_dirty_buffers: bool,
548 show_selection_menu: Option<bool>,
549 blame: Option<Model<GitBlame>>,
550 blame_subscription: Option<Subscription>,
551 custom_context_menu: Option<
552 Box<
553 dyn 'static
554 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
555 >,
556 >,
557 last_bounds: Option<Bounds<Pixels>>,
558 expect_bounds_change: Option<Bounds<Pixels>>,
559 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
560 tasks_update_task: Option<Task<()>>,
561 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
562 file_header_size: u32,
563 breadcrumb_header: Option<String>,
564 focused_block: Option<FocusedBlock>,
565 next_scroll_position: NextScrollCursorCenterTopBottom,
566 _scroll_cursor_center_top_bottom_task: Task<()>,
567}
568
569#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
570enum NextScrollCursorCenterTopBottom {
571 #[default]
572 Center,
573 Top,
574 Bottom,
575}
576
577impl NextScrollCursorCenterTopBottom {
578 fn next(&self) -> Self {
579 match self {
580 Self::Center => Self::Top,
581 Self::Top => Self::Bottom,
582 Self::Bottom => Self::Center,
583 }
584 }
585}
586
587#[derive(Clone)]
588pub struct EditorSnapshot {
589 pub mode: EditorMode,
590 show_gutter: bool,
591 show_line_numbers: Option<bool>,
592 show_git_diff_gutter: Option<bool>,
593 show_code_actions: Option<bool>,
594 show_runnables: Option<bool>,
595 render_git_blame_gutter: bool,
596 pub display_snapshot: DisplaySnapshot,
597 pub placeholder_text: Option<Arc<str>>,
598 is_focused: bool,
599 scroll_anchor: ScrollAnchor,
600 ongoing_scroll: OngoingScroll,
601 current_line_highlight: CurrentLineHighlight,
602 gutter_hovered: bool,
603}
604
605const GIT_BLAME_GUTTER_WIDTH_CHARS: f32 = 53.;
606
607#[derive(Default, Debug, Clone, Copy)]
608pub struct GutterDimensions {
609 pub left_padding: Pixels,
610 pub right_padding: Pixels,
611 pub width: Pixels,
612 pub margin: Pixels,
613 pub git_blame_entries_width: Option<Pixels>,
614}
615
616impl GutterDimensions {
617 /// The full width of the space taken up by the gutter.
618 pub fn full_width(&self) -> Pixels {
619 self.margin + self.width
620 }
621
622 /// The width of the space reserved for the fold indicators,
623 /// use alongside 'justify_end' and `gutter_width` to
624 /// right align content with the line numbers
625 pub fn fold_area_width(&self) -> Pixels {
626 self.margin + self.right_padding
627 }
628}
629
630#[derive(Debug)]
631pub struct RemoteSelection {
632 pub replica_id: ReplicaId,
633 pub selection: Selection<Anchor>,
634 pub cursor_shape: CursorShape,
635 pub peer_id: PeerId,
636 pub line_mode: bool,
637 pub participant_index: Option<ParticipantIndex>,
638 pub user_name: Option<SharedString>,
639}
640
641#[derive(Clone, Debug)]
642struct SelectionHistoryEntry {
643 selections: Arc<[Selection<Anchor>]>,
644 select_next_state: Option<SelectNextState>,
645 select_prev_state: Option<SelectNextState>,
646 add_selections_state: Option<AddSelectionsState>,
647}
648
649enum SelectionHistoryMode {
650 Normal,
651 Undoing,
652 Redoing,
653}
654
655#[derive(Clone, PartialEq, Eq, Hash)]
656struct HoveredCursor {
657 replica_id: u16,
658 selection_id: usize,
659}
660
661impl Default for SelectionHistoryMode {
662 fn default() -> Self {
663 Self::Normal
664 }
665}
666
667#[derive(Default)]
668struct SelectionHistory {
669 #[allow(clippy::type_complexity)]
670 selections_by_transaction:
671 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
672 mode: SelectionHistoryMode,
673 undo_stack: VecDeque<SelectionHistoryEntry>,
674 redo_stack: VecDeque<SelectionHistoryEntry>,
675}
676
677impl SelectionHistory {
678 fn insert_transaction(
679 &mut self,
680 transaction_id: TransactionId,
681 selections: Arc<[Selection<Anchor>]>,
682 ) {
683 self.selections_by_transaction
684 .insert(transaction_id, (selections, None));
685 }
686
687 #[allow(clippy::type_complexity)]
688 fn transaction(
689 &self,
690 transaction_id: TransactionId,
691 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
692 self.selections_by_transaction.get(&transaction_id)
693 }
694
695 #[allow(clippy::type_complexity)]
696 fn transaction_mut(
697 &mut self,
698 transaction_id: TransactionId,
699 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
700 self.selections_by_transaction.get_mut(&transaction_id)
701 }
702
703 fn push(&mut self, entry: SelectionHistoryEntry) {
704 if !entry.selections.is_empty() {
705 match self.mode {
706 SelectionHistoryMode::Normal => {
707 self.push_undo(entry);
708 self.redo_stack.clear();
709 }
710 SelectionHistoryMode::Undoing => self.push_redo(entry),
711 SelectionHistoryMode::Redoing => self.push_undo(entry),
712 }
713 }
714 }
715
716 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
717 if self
718 .undo_stack
719 .back()
720 .map_or(true, |e| e.selections != entry.selections)
721 {
722 self.undo_stack.push_back(entry);
723 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
724 self.undo_stack.pop_front();
725 }
726 }
727 }
728
729 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
730 if self
731 .redo_stack
732 .back()
733 .map_or(true, |e| e.selections != entry.selections)
734 {
735 self.redo_stack.push_back(entry);
736 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
737 self.redo_stack.pop_front();
738 }
739 }
740 }
741}
742
743struct RowHighlight {
744 index: usize,
745 range: RangeInclusive<Anchor>,
746 color: Option<Hsla>,
747 should_autoscroll: bool,
748}
749
750#[derive(Clone, Debug)]
751struct AddSelectionsState {
752 above: bool,
753 stack: Vec<usize>,
754}
755
756#[derive(Clone)]
757struct SelectNextState {
758 query: AhoCorasick,
759 wordwise: bool,
760 done: bool,
761}
762
763impl std::fmt::Debug for SelectNextState {
764 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
765 f.debug_struct(std::any::type_name::<Self>())
766 .field("wordwise", &self.wordwise)
767 .field("done", &self.done)
768 .finish()
769 }
770}
771
772#[derive(Debug)]
773struct AutocloseRegion {
774 selection_id: usize,
775 range: Range<Anchor>,
776 pair: BracketPair,
777}
778
779#[derive(Debug)]
780struct SnippetState {
781 ranges: Vec<Vec<Range<Anchor>>>,
782 active_index: usize,
783}
784
785#[doc(hidden)]
786pub struct RenameState {
787 pub range: Range<Anchor>,
788 pub old_name: Arc<str>,
789 pub editor: View<Editor>,
790 block_id: CustomBlockId,
791}
792
793struct InvalidationStack<T>(Vec<T>);
794
795struct RegisteredInlineCompletionProvider {
796 provider: Arc<dyn InlineCompletionProviderHandle>,
797 _subscription: Subscription,
798}
799
800enum ContextMenu {
801 Completions(CompletionsMenu),
802 CodeActions(CodeActionsMenu),
803}
804
805impl ContextMenu {
806 fn select_first(
807 &mut self,
808 project: Option<&Model<Project>>,
809 cx: &mut ViewContext<Editor>,
810 ) -> bool {
811 if self.visible() {
812 match self {
813 ContextMenu::Completions(menu) => menu.select_first(project, cx),
814 ContextMenu::CodeActions(menu) => menu.select_first(cx),
815 }
816 true
817 } else {
818 false
819 }
820 }
821
822 fn select_prev(
823 &mut self,
824 project: Option<&Model<Project>>,
825 cx: &mut ViewContext<Editor>,
826 ) -> bool {
827 if self.visible() {
828 match self {
829 ContextMenu::Completions(menu) => menu.select_prev(project, cx),
830 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
831 }
832 true
833 } else {
834 false
835 }
836 }
837
838 fn select_next(
839 &mut self,
840 project: Option<&Model<Project>>,
841 cx: &mut ViewContext<Editor>,
842 ) -> bool {
843 if self.visible() {
844 match self {
845 ContextMenu::Completions(menu) => menu.select_next(project, cx),
846 ContextMenu::CodeActions(menu) => menu.select_next(cx),
847 }
848 true
849 } else {
850 false
851 }
852 }
853
854 fn select_last(
855 &mut self,
856 project: Option<&Model<Project>>,
857 cx: &mut ViewContext<Editor>,
858 ) -> bool {
859 if self.visible() {
860 match self {
861 ContextMenu::Completions(menu) => menu.select_last(project, cx),
862 ContextMenu::CodeActions(menu) => menu.select_last(cx),
863 }
864 true
865 } else {
866 false
867 }
868 }
869
870 fn visible(&self) -> bool {
871 match self {
872 ContextMenu::Completions(menu) => menu.visible(),
873 ContextMenu::CodeActions(menu) => menu.visible(),
874 }
875 }
876
877 fn render(
878 &self,
879 cursor_position: DisplayPoint,
880 style: &EditorStyle,
881 max_height: Pixels,
882 workspace: Option<WeakView<Workspace>>,
883 cx: &mut ViewContext<Editor>,
884 ) -> (ContextMenuOrigin, AnyElement) {
885 match self {
886 ContextMenu::Completions(menu) => (
887 ContextMenuOrigin::EditorPoint(cursor_position),
888 menu.render(style, max_height, workspace, cx),
889 ),
890 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, max_height, cx),
891 }
892 }
893}
894
895enum ContextMenuOrigin {
896 EditorPoint(DisplayPoint),
897 GutterIndicator(DisplayRow),
898}
899
900#[derive(Clone)]
901struct CompletionsMenu {
902 id: CompletionId,
903 sort_completions: bool,
904 initial_position: Anchor,
905 buffer: Model<Buffer>,
906 completions: Arc<RwLock<Box<[Completion]>>>,
907 match_candidates: Arc<[StringMatchCandidate]>,
908 matches: Arc<[StringMatch]>,
909 selected_item: usize,
910 scroll_handle: UniformListScrollHandle,
911 selected_completion_documentation_resolve_debounce: Arc<Mutex<DebouncedDelay>>,
912}
913
914impl CompletionsMenu {
915 fn select_first(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
916 self.selected_item = 0;
917 self.scroll_handle.scroll_to_item(self.selected_item);
918 self.attempt_resolve_selected_completion_documentation(project, cx);
919 cx.notify();
920 }
921
922 fn select_prev(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
923 if self.selected_item > 0 {
924 self.selected_item -= 1;
925 } else {
926 self.selected_item = self.matches.len() - 1;
927 }
928 self.scroll_handle.scroll_to_item(self.selected_item);
929 self.attempt_resolve_selected_completion_documentation(project, cx);
930 cx.notify();
931 }
932
933 fn select_next(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
934 if self.selected_item + 1 < self.matches.len() {
935 self.selected_item += 1;
936 } else {
937 self.selected_item = 0;
938 }
939 self.scroll_handle.scroll_to_item(self.selected_item);
940 self.attempt_resolve_selected_completion_documentation(project, cx);
941 cx.notify();
942 }
943
944 fn select_last(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
945 self.selected_item = self.matches.len() - 1;
946 self.scroll_handle.scroll_to_item(self.selected_item);
947 self.attempt_resolve_selected_completion_documentation(project, cx);
948 cx.notify();
949 }
950
951 fn pre_resolve_completion_documentation(
952 buffer: Model<Buffer>,
953 completions: Arc<RwLock<Box<[Completion]>>>,
954 matches: Arc<[StringMatch]>,
955 editor: &Editor,
956 cx: &mut ViewContext<Editor>,
957 ) -> Task<()> {
958 let settings = EditorSettings::get_global(cx);
959 if !settings.show_completion_documentation {
960 return Task::ready(());
961 }
962
963 let Some(provider) = editor.completion_provider.as_ref() else {
964 return Task::ready(());
965 };
966
967 let resolve_task = provider.resolve_completions(
968 buffer,
969 matches.iter().map(|m| m.candidate_id).collect(),
970 completions.clone(),
971 cx,
972 );
973
974 return cx.spawn(move |this, mut cx| async move {
975 if let Some(true) = resolve_task.await.log_err() {
976 this.update(&mut cx, |_, cx| cx.notify()).ok();
977 }
978 });
979 }
980
981 fn attempt_resolve_selected_completion_documentation(
982 &mut self,
983 project: Option<&Model<Project>>,
984 cx: &mut ViewContext<Editor>,
985 ) {
986 let settings = EditorSettings::get_global(cx);
987 if !settings.show_completion_documentation {
988 return;
989 }
990
991 let completion_index = self.matches[self.selected_item].candidate_id;
992 let Some(project) = project else {
993 return;
994 };
995
996 let resolve_task = project.update(cx, |project, cx| {
997 project.resolve_completions(
998 self.buffer.clone(),
999 vec![completion_index],
1000 self.completions.clone(),
1001 cx,
1002 )
1003 });
1004
1005 let delay_ms =
1006 EditorSettings::get_global(cx).completion_documentation_secondary_query_debounce;
1007 let delay = Duration::from_millis(delay_ms);
1008
1009 self.selected_completion_documentation_resolve_debounce
1010 .lock()
1011 .fire_new(delay, cx, |_, cx| {
1012 cx.spawn(move |this, mut cx| async move {
1013 if let Some(true) = resolve_task.await.log_err() {
1014 this.update(&mut cx, |_, cx| cx.notify()).ok();
1015 }
1016 })
1017 });
1018 }
1019
1020 fn visible(&self) -> bool {
1021 !self.matches.is_empty()
1022 }
1023
1024 fn render(
1025 &self,
1026 style: &EditorStyle,
1027 max_height: Pixels,
1028 workspace: Option<WeakView<Workspace>>,
1029 cx: &mut ViewContext<Editor>,
1030 ) -> AnyElement {
1031 let settings = EditorSettings::get_global(cx);
1032 let show_completion_documentation = settings.show_completion_documentation;
1033
1034 let widest_completion_ix = self
1035 .matches
1036 .iter()
1037 .enumerate()
1038 .max_by_key(|(_, mat)| {
1039 let completions = self.completions.read();
1040 let completion = &completions[mat.candidate_id];
1041 let documentation = &completion.documentation;
1042
1043 let mut len = completion.label.text.chars().count();
1044 if let Some(Documentation::SingleLine(text)) = documentation {
1045 if show_completion_documentation {
1046 len += text.chars().count();
1047 }
1048 }
1049
1050 len
1051 })
1052 .map(|(ix, _)| ix);
1053
1054 let completions = self.completions.clone();
1055 let matches = self.matches.clone();
1056 let selected_item = self.selected_item;
1057 let style = style.clone();
1058
1059 let multiline_docs = if show_completion_documentation {
1060 let mat = &self.matches[selected_item];
1061 let multiline_docs = match &self.completions.read()[mat.candidate_id].documentation {
1062 Some(Documentation::MultiLinePlainText(text)) => {
1063 Some(div().child(SharedString::from(text.clone())))
1064 }
1065 Some(Documentation::MultiLineMarkdown(parsed)) if !parsed.text.is_empty() => {
1066 Some(div().child(render_parsed_markdown(
1067 "completions_markdown",
1068 parsed,
1069 &style,
1070 workspace,
1071 cx,
1072 )))
1073 }
1074 _ => None,
1075 };
1076 multiline_docs.map(|div| {
1077 div.id("multiline_docs")
1078 .max_h(max_height)
1079 .flex_1()
1080 .px_1p5()
1081 .py_1()
1082 .min_w(px(260.))
1083 .max_w(px(640.))
1084 .w(px(500.))
1085 .overflow_y_scroll()
1086 .occlude()
1087 })
1088 } else {
1089 None
1090 };
1091
1092 let list = uniform_list(
1093 cx.view().clone(),
1094 "completions",
1095 matches.len(),
1096 move |_editor, range, cx| {
1097 let start_ix = range.start;
1098 let completions_guard = completions.read();
1099
1100 matches[range]
1101 .iter()
1102 .enumerate()
1103 .map(|(ix, mat)| {
1104 let item_ix = start_ix + ix;
1105 let candidate_id = mat.candidate_id;
1106 let completion = &completions_guard[candidate_id];
1107
1108 let documentation = if show_completion_documentation {
1109 &completion.documentation
1110 } else {
1111 &None
1112 };
1113
1114 let highlights = gpui::combine_highlights(
1115 mat.ranges().map(|range| (range, FontWeight::BOLD.into())),
1116 styled_runs_for_code_label(&completion.label, &style.syntax).map(
1117 |(range, mut highlight)| {
1118 // Ignore font weight for syntax highlighting, as we'll use it
1119 // for fuzzy matches.
1120 highlight.font_weight = None;
1121
1122 if completion.lsp_completion.deprecated.unwrap_or(false) {
1123 highlight.strikethrough = Some(StrikethroughStyle {
1124 thickness: 1.0.into(),
1125 ..Default::default()
1126 });
1127 highlight.color = Some(cx.theme().colors().text_muted);
1128 }
1129
1130 (range, highlight)
1131 },
1132 ),
1133 );
1134 let completion_label = StyledText::new(completion.label.text.clone())
1135 .with_highlights(&style.text, highlights);
1136 let documentation_label =
1137 if let Some(Documentation::SingleLine(text)) = documentation {
1138 if text.trim().is_empty() {
1139 None
1140 } else {
1141 Some(
1142 Label::new(text.clone())
1143 .ml_4()
1144 .size(LabelSize::Small)
1145 .color(Color::Muted),
1146 )
1147 }
1148 } else {
1149 None
1150 };
1151
1152 div().min_w(px(220.)).max_w(px(540.)).child(
1153 ListItem::new(mat.candidate_id)
1154 .inset(true)
1155 .selected(item_ix == selected_item)
1156 .on_click(cx.listener(move |editor, _event, cx| {
1157 cx.stop_propagation();
1158 if let Some(task) = editor.confirm_completion(
1159 &ConfirmCompletion {
1160 item_ix: Some(item_ix),
1161 },
1162 cx,
1163 ) {
1164 task.detach_and_log_err(cx)
1165 }
1166 }))
1167 .child(h_flex().overflow_hidden().child(completion_label))
1168 .end_slot::<Label>(documentation_label),
1169 )
1170 })
1171 .collect()
1172 },
1173 )
1174 .occlude()
1175 .max_h(max_height)
1176 .track_scroll(self.scroll_handle.clone())
1177 .with_width_from_item(widest_completion_ix)
1178 .with_sizing_behavior(ListSizingBehavior::Infer);
1179
1180 Popover::new()
1181 .child(list)
1182 .when_some(multiline_docs, |popover, multiline_docs| {
1183 popover.aside(multiline_docs)
1184 })
1185 .into_any_element()
1186 }
1187
1188 pub async fn filter(&mut self, query: Option<&str>, executor: BackgroundExecutor) {
1189 let mut matches = if let Some(query) = query {
1190 fuzzy::match_strings(
1191 &self.match_candidates,
1192 query,
1193 query.chars().any(|c| c.is_uppercase()),
1194 100,
1195 &Default::default(),
1196 executor,
1197 )
1198 .await
1199 } else {
1200 self.match_candidates
1201 .iter()
1202 .enumerate()
1203 .map(|(candidate_id, candidate)| StringMatch {
1204 candidate_id,
1205 score: Default::default(),
1206 positions: Default::default(),
1207 string: candidate.string.clone(),
1208 })
1209 .collect()
1210 };
1211
1212 // Remove all candidates where the query's start does not match the start of any word in the candidate
1213 if let Some(query) = query {
1214 if let Some(query_start) = query.chars().next() {
1215 matches.retain(|string_match| {
1216 split_words(&string_match.string).any(|word| {
1217 // Check that the first codepoint of the word as lowercase matches the first
1218 // codepoint of the query as lowercase
1219 word.chars()
1220 .flat_map(|codepoint| codepoint.to_lowercase())
1221 .zip(query_start.to_lowercase())
1222 .all(|(word_cp, query_cp)| word_cp == query_cp)
1223 })
1224 });
1225 }
1226 }
1227
1228 let completions = self.completions.read();
1229 if self.sort_completions {
1230 matches.sort_unstable_by_key(|mat| {
1231 // We do want to strike a balance here between what the language server tells us
1232 // to sort by (the sort_text) and what are "obvious" good matches (i.e. when you type
1233 // `Creat` and there is a local variable called `CreateComponent`).
1234 // So what we do is: we bucket all matches into two buckets
1235 // - Strong matches
1236 // - Weak matches
1237 // Strong matches are the ones with a high fuzzy-matcher score (the "obvious" matches)
1238 // and the Weak matches are the rest.
1239 //
1240 // For the strong matches, we sort by the language-servers score first and for the weak
1241 // matches, we prefer our fuzzy finder first.
1242 //
1243 // The thinking behind that: it's useless to take the sort_text the language-server gives
1244 // us into account when it's obviously a bad match.
1245
1246 #[derive(PartialEq, Eq, PartialOrd, Ord)]
1247 enum MatchScore<'a> {
1248 Strong {
1249 sort_text: Option<&'a str>,
1250 score: Reverse<OrderedFloat<f64>>,
1251 sort_key: (usize, &'a str),
1252 },
1253 Weak {
1254 score: Reverse<OrderedFloat<f64>>,
1255 sort_text: Option<&'a str>,
1256 sort_key: (usize, &'a str),
1257 },
1258 }
1259
1260 let completion = &completions[mat.candidate_id];
1261 let sort_key = completion.sort_key();
1262 let sort_text = completion.lsp_completion.sort_text.as_deref();
1263 let score = Reverse(OrderedFloat(mat.score));
1264
1265 if mat.score >= 0.2 {
1266 MatchScore::Strong {
1267 sort_text,
1268 score,
1269 sort_key,
1270 }
1271 } else {
1272 MatchScore::Weak {
1273 score,
1274 sort_text,
1275 sort_key,
1276 }
1277 }
1278 });
1279 }
1280
1281 for mat in &mut matches {
1282 let completion = &completions[mat.candidate_id];
1283 mat.string.clone_from(&completion.label.text);
1284 for position in &mut mat.positions {
1285 *position += completion.label.filter_range.start;
1286 }
1287 }
1288 drop(completions);
1289
1290 self.matches = matches.into();
1291 self.selected_item = 0;
1292 }
1293}
1294
1295#[derive(Clone)]
1296struct CodeActionContents {
1297 tasks: Option<Arc<ResolvedTasks>>,
1298 actions: Option<Arc<[CodeAction]>>,
1299}
1300
1301impl CodeActionContents {
1302 fn len(&self) -> usize {
1303 match (&self.tasks, &self.actions) {
1304 (Some(tasks), Some(actions)) => actions.len() + tasks.templates.len(),
1305 (Some(tasks), None) => tasks.templates.len(),
1306 (None, Some(actions)) => actions.len(),
1307 (None, None) => 0,
1308 }
1309 }
1310
1311 fn is_empty(&self) -> bool {
1312 match (&self.tasks, &self.actions) {
1313 (Some(tasks), Some(actions)) => actions.is_empty() && tasks.templates.is_empty(),
1314 (Some(tasks), None) => tasks.templates.is_empty(),
1315 (None, Some(actions)) => actions.is_empty(),
1316 (None, None) => true,
1317 }
1318 }
1319
1320 fn iter(&self) -> impl Iterator<Item = CodeActionsItem> + '_ {
1321 self.tasks
1322 .iter()
1323 .flat_map(|tasks| {
1324 tasks
1325 .templates
1326 .iter()
1327 .map(|(kind, task)| CodeActionsItem::Task(kind.clone(), task.clone()))
1328 })
1329 .chain(self.actions.iter().flat_map(|actions| {
1330 actions
1331 .iter()
1332 .map(|action| CodeActionsItem::CodeAction(action.clone()))
1333 }))
1334 }
1335 fn get(&self, index: usize) -> Option<CodeActionsItem> {
1336 match (&self.tasks, &self.actions) {
1337 (Some(tasks), Some(actions)) => {
1338 if index < tasks.templates.len() {
1339 tasks
1340 .templates
1341 .get(index)
1342 .cloned()
1343 .map(|(kind, task)| CodeActionsItem::Task(kind, task))
1344 } else {
1345 actions
1346 .get(index - tasks.templates.len())
1347 .cloned()
1348 .map(CodeActionsItem::CodeAction)
1349 }
1350 }
1351 (Some(tasks), None) => tasks
1352 .templates
1353 .get(index)
1354 .cloned()
1355 .map(|(kind, task)| CodeActionsItem::Task(kind, task)),
1356 (None, Some(actions)) => actions.get(index).cloned().map(CodeActionsItem::CodeAction),
1357 (None, None) => None,
1358 }
1359 }
1360}
1361
1362#[allow(clippy::large_enum_variant)]
1363#[derive(Clone)]
1364enum CodeActionsItem {
1365 Task(TaskSourceKind, ResolvedTask),
1366 CodeAction(CodeAction),
1367}
1368
1369impl CodeActionsItem {
1370 fn as_task(&self) -> Option<&ResolvedTask> {
1371 let Self::Task(_, task) = self else {
1372 return None;
1373 };
1374 Some(task)
1375 }
1376 fn as_code_action(&self) -> Option<&CodeAction> {
1377 let Self::CodeAction(action) = self else {
1378 return None;
1379 };
1380 Some(action)
1381 }
1382 fn label(&self) -> String {
1383 match self {
1384 Self::CodeAction(action) => action.lsp_action.title.clone(),
1385 Self::Task(_, task) => task.resolved_label.clone(),
1386 }
1387 }
1388}
1389
1390struct CodeActionsMenu {
1391 actions: CodeActionContents,
1392 buffer: Model<Buffer>,
1393 selected_item: usize,
1394 scroll_handle: UniformListScrollHandle,
1395 deployed_from_indicator: Option<DisplayRow>,
1396}
1397
1398impl CodeActionsMenu {
1399 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
1400 self.selected_item = 0;
1401 self.scroll_handle.scroll_to_item(self.selected_item);
1402 cx.notify()
1403 }
1404
1405 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
1406 if self.selected_item > 0 {
1407 self.selected_item -= 1;
1408 } else {
1409 self.selected_item = self.actions.len() - 1;
1410 }
1411 self.scroll_handle.scroll_to_item(self.selected_item);
1412 cx.notify();
1413 }
1414
1415 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
1416 if self.selected_item + 1 < self.actions.len() {
1417 self.selected_item += 1;
1418 } else {
1419 self.selected_item = 0;
1420 }
1421 self.scroll_handle.scroll_to_item(self.selected_item);
1422 cx.notify();
1423 }
1424
1425 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
1426 self.selected_item = self.actions.len() - 1;
1427 self.scroll_handle.scroll_to_item(self.selected_item);
1428 cx.notify()
1429 }
1430
1431 fn visible(&self) -> bool {
1432 !self.actions.is_empty()
1433 }
1434
1435 fn render(
1436 &self,
1437 cursor_position: DisplayPoint,
1438 _style: &EditorStyle,
1439 max_height: Pixels,
1440 cx: &mut ViewContext<Editor>,
1441 ) -> (ContextMenuOrigin, AnyElement) {
1442 let actions = self.actions.clone();
1443 let selected_item = self.selected_item;
1444 let element = uniform_list(
1445 cx.view().clone(),
1446 "code_actions_menu",
1447 self.actions.len(),
1448 move |_this, range, cx| {
1449 actions
1450 .iter()
1451 .skip(range.start)
1452 .take(range.end - range.start)
1453 .enumerate()
1454 .map(|(ix, action)| {
1455 let item_ix = range.start + ix;
1456 let selected = selected_item == item_ix;
1457 let colors = cx.theme().colors();
1458 div()
1459 .px_2()
1460 .text_color(colors.text)
1461 .when(selected, |style| {
1462 style
1463 .bg(colors.element_active)
1464 .text_color(colors.text_accent)
1465 })
1466 .hover(|style| {
1467 style
1468 .bg(colors.element_hover)
1469 .text_color(colors.text_accent)
1470 })
1471 .whitespace_nowrap()
1472 .when_some(action.as_code_action(), |this, action| {
1473 this.on_mouse_down(
1474 MouseButton::Left,
1475 cx.listener(move |editor, _, cx| {
1476 cx.stop_propagation();
1477 if let Some(task) = editor.confirm_code_action(
1478 &ConfirmCodeAction {
1479 item_ix: Some(item_ix),
1480 },
1481 cx,
1482 ) {
1483 task.detach_and_log_err(cx)
1484 }
1485 }),
1486 )
1487 // TASK: It would be good to make lsp_action.title a SharedString to avoid allocating here.
1488 .child(SharedString::from(action.lsp_action.title.clone()))
1489 })
1490 .when_some(action.as_task(), |this, task| {
1491 this.on_mouse_down(
1492 MouseButton::Left,
1493 cx.listener(move |editor, _, cx| {
1494 cx.stop_propagation();
1495 if let Some(task) = editor.confirm_code_action(
1496 &ConfirmCodeAction {
1497 item_ix: Some(item_ix),
1498 },
1499 cx,
1500 ) {
1501 task.detach_and_log_err(cx)
1502 }
1503 }),
1504 )
1505 .child(SharedString::from(task.resolved_label.clone()))
1506 })
1507 })
1508 .collect()
1509 },
1510 )
1511 .elevation_1(cx)
1512 .px_2()
1513 .py_1()
1514 .max_h(max_height)
1515 .occlude()
1516 .track_scroll(self.scroll_handle.clone())
1517 .with_width_from_item(
1518 self.actions
1519 .iter()
1520 .enumerate()
1521 .max_by_key(|(_, action)| match action {
1522 CodeActionsItem::Task(_, task) => task.resolved_label.chars().count(),
1523 CodeActionsItem::CodeAction(action) => action.lsp_action.title.chars().count(),
1524 })
1525 .map(|(ix, _)| ix),
1526 )
1527 .with_sizing_behavior(ListSizingBehavior::Infer)
1528 .into_any_element();
1529
1530 let cursor_position = if let Some(row) = self.deployed_from_indicator {
1531 ContextMenuOrigin::GutterIndicator(row)
1532 } else {
1533 ContextMenuOrigin::EditorPoint(cursor_position)
1534 };
1535
1536 (cursor_position, element)
1537 }
1538}
1539
1540#[derive(Debug)]
1541struct ActiveDiagnosticGroup {
1542 primary_range: Range<Anchor>,
1543 primary_message: String,
1544 group_id: usize,
1545 blocks: HashMap<CustomBlockId, Diagnostic>,
1546 is_valid: bool,
1547}
1548
1549#[derive(Serialize, Deserialize, Clone, Debug)]
1550pub struct ClipboardSelection {
1551 pub len: usize,
1552 pub is_entire_line: bool,
1553 pub first_line_indent: u32,
1554}
1555
1556#[derive(Debug)]
1557pub(crate) struct NavigationData {
1558 cursor_anchor: Anchor,
1559 cursor_position: Point,
1560 scroll_anchor: ScrollAnchor,
1561 scroll_top_row: u32,
1562}
1563
1564enum GotoDefinitionKind {
1565 Symbol,
1566 Declaration,
1567 Type,
1568 Implementation,
1569}
1570
1571#[derive(Debug, Clone)]
1572enum InlayHintRefreshReason {
1573 Toggle(bool),
1574 SettingsChange(InlayHintSettings),
1575 NewLinesShown,
1576 BufferEdited(HashSet<Arc<Language>>),
1577 RefreshRequested,
1578 ExcerptsRemoved(Vec<ExcerptId>),
1579}
1580
1581impl InlayHintRefreshReason {
1582 fn description(&self) -> &'static str {
1583 match self {
1584 Self::Toggle(_) => "toggle",
1585 Self::SettingsChange(_) => "settings change",
1586 Self::NewLinesShown => "new lines shown",
1587 Self::BufferEdited(_) => "buffer edited",
1588 Self::RefreshRequested => "refresh requested",
1589 Self::ExcerptsRemoved(_) => "excerpts removed",
1590 }
1591 }
1592}
1593
1594pub(crate) struct FocusedBlock {
1595 id: BlockId,
1596 focus_handle: WeakFocusHandle,
1597}
1598
1599impl Editor {
1600 pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
1601 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1602 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1603 Self::new(
1604 EditorMode::SingleLine { auto_width: false },
1605 buffer,
1606 None,
1607 false,
1608 cx,
1609 )
1610 }
1611
1612 pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
1613 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1614 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1615 Self::new(EditorMode::Full, buffer, None, false, cx)
1616 }
1617
1618 pub fn auto_width(cx: &mut ViewContext<Self>) -> Self {
1619 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1620 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1621 Self::new(
1622 EditorMode::SingleLine { auto_width: true },
1623 buffer,
1624 None,
1625 false,
1626 cx,
1627 )
1628 }
1629
1630 pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
1631 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1632 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1633 Self::new(
1634 EditorMode::AutoHeight { max_lines },
1635 buffer,
1636 None,
1637 false,
1638 cx,
1639 )
1640 }
1641
1642 pub fn for_buffer(
1643 buffer: Model<Buffer>,
1644 project: Option<Model<Project>>,
1645 cx: &mut ViewContext<Self>,
1646 ) -> Self {
1647 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1648 Self::new(EditorMode::Full, buffer, project, false, cx)
1649 }
1650
1651 pub fn for_multibuffer(
1652 buffer: Model<MultiBuffer>,
1653 project: Option<Model<Project>>,
1654 show_excerpt_controls: bool,
1655 cx: &mut ViewContext<Self>,
1656 ) -> Self {
1657 Self::new(EditorMode::Full, buffer, project, show_excerpt_controls, cx)
1658 }
1659
1660 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1661 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1662 let mut clone = Self::new(
1663 self.mode,
1664 self.buffer.clone(),
1665 self.project.clone(),
1666 show_excerpt_controls,
1667 cx,
1668 );
1669 self.display_map.update(cx, |display_map, cx| {
1670 let snapshot = display_map.snapshot(cx);
1671 clone.display_map.update(cx, |display_map, cx| {
1672 display_map.set_state(&snapshot, cx);
1673 });
1674 });
1675 clone.selections.clone_state(&self.selections);
1676 clone.scroll_manager.clone_state(&self.scroll_manager);
1677 clone.searchable = self.searchable;
1678 clone
1679 }
1680
1681 pub fn new(
1682 mode: EditorMode,
1683 buffer: Model<MultiBuffer>,
1684 project: Option<Model<Project>>,
1685 show_excerpt_controls: bool,
1686 cx: &mut ViewContext<Self>,
1687 ) -> Self {
1688 let style = cx.text_style();
1689 let font_size = style.font_size.to_pixels(cx.rem_size());
1690 let editor = cx.view().downgrade();
1691 let fold_placeholder = FoldPlaceholder {
1692 constrain_width: true,
1693 render: Arc::new(move |fold_id, fold_range, cx| {
1694 let editor = editor.clone();
1695 div()
1696 .id(fold_id)
1697 .bg(cx.theme().colors().ghost_element_background)
1698 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1699 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1700 .rounded_sm()
1701 .size_full()
1702 .cursor_pointer()
1703 .child("⋯")
1704 .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
1705 .on_click(move |_, cx| {
1706 editor
1707 .update(cx, |editor, cx| {
1708 editor.unfold_ranges(
1709 [fold_range.start..fold_range.end],
1710 true,
1711 false,
1712 cx,
1713 );
1714 cx.stop_propagation();
1715 })
1716 .ok();
1717 })
1718 .into_any()
1719 }),
1720 merge_adjacent: true,
1721 };
1722 let file_header_size = if show_excerpt_controls { 3 } else { 2 };
1723 let display_map = cx.new_model(|cx| {
1724 DisplayMap::new(
1725 buffer.clone(),
1726 style.font(),
1727 font_size,
1728 None,
1729 show_excerpt_controls,
1730 file_header_size,
1731 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1732 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1733 fold_placeholder,
1734 cx,
1735 )
1736 });
1737
1738 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1739
1740 let blink_manager = cx.new_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1741
1742 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1743 .then(|| language_settings::SoftWrap::PreferLine);
1744
1745 let mut project_subscriptions = Vec::new();
1746 if mode == EditorMode::Full {
1747 if let Some(project) = project.as_ref() {
1748 if buffer.read(cx).is_singleton() {
1749 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1750 cx.emit(EditorEvent::TitleChanged);
1751 }));
1752 }
1753 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1754 if let project::Event::RefreshInlayHints = event {
1755 editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1756 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1757 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1758 let focus_handle = editor.focus_handle(cx);
1759 if focus_handle.is_focused(cx) {
1760 let snapshot = buffer.read(cx).snapshot();
1761 for (range, snippet) in snippet_edits {
1762 let editor_range =
1763 language::range_from_lsp(*range).to_offset(&snapshot);
1764 editor
1765 .insert_snippet(&[editor_range], snippet.clone(), cx)
1766 .ok();
1767 }
1768 }
1769 }
1770 }
1771 }));
1772 let task_inventory = project.read(cx).task_inventory().clone();
1773 project_subscriptions.push(cx.observe(&task_inventory, |editor, _, cx| {
1774 editor.tasks_update_task = Some(editor.refresh_runnables(cx));
1775 }));
1776 }
1777 }
1778
1779 let inlay_hint_settings = inlay_hint_settings(
1780 selections.newest_anchor().head(),
1781 &buffer.read(cx).snapshot(cx),
1782 cx,
1783 );
1784 let focus_handle = cx.focus_handle();
1785 cx.on_focus(&focus_handle, Self::handle_focus).detach();
1786 cx.on_focus_in(&focus_handle, Self::handle_focus_in)
1787 .detach();
1788 cx.on_focus_out(&focus_handle, Self::handle_focus_out)
1789 .detach();
1790 cx.on_blur(&focus_handle, Self::handle_blur).detach();
1791
1792 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1793 Some(false)
1794 } else {
1795 None
1796 };
1797
1798 let mut this = Self {
1799 focus_handle,
1800 show_cursor_when_unfocused: false,
1801 last_focused_descendant: None,
1802 buffer: buffer.clone(),
1803 display_map: display_map.clone(),
1804 selections,
1805 scroll_manager: ScrollManager::new(cx),
1806 columnar_selection_tail: None,
1807 add_selections_state: None,
1808 select_next_state: None,
1809 select_prev_state: None,
1810 selection_history: Default::default(),
1811 autoclose_regions: Default::default(),
1812 snippet_stack: Default::default(),
1813 select_larger_syntax_node_stack: Vec::new(),
1814 ime_transaction: Default::default(),
1815 active_diagnostics: None,
1816 soft_wrap_mode_override,
1817 completion_provider: project.clone().map(|project| Box::new(project) as _),
1818 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1819 project,
1820 blink_manager: blink_manager.clone(),
1821 show_local_selections: true,
1822 mode,
1823 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1824 show_gutter: mode == EditorMode::Full,
1825 show_line_numbers: None,
1826 show_git_diff_gutter: None,
1827 show_code_actions: None,
1828 show_runnables: None,
1829 show_wrap_guides: None,
1830 show_indent_guides,
1831 placeholder_text: None,
1832 highlight_order: 0,
1833 highlighted_rows: HashMap::default(),
1834 background_highlights: Default::default(),
1835 gutter_highlights: TreeMap::default(),
1836 scrollbar_marker_state: ScrollbarMarkerState::default(),
1837 active_indent_guides_state: ActiveIndentGuidesState::default(),
1838 nav_history: None,
1839 context_menu: RwLock::new(None),
1840 mouse_context_menu: None,
1841 completion_tasks: Default::default(),
1842 signature_help_state: SignatureHelpState::default(),
1843 auto_signature_help: None,
1844 find_all_references_task_sources: Vec::new(),
1845 next_completion_id: 0,
1846 completion_documentation_pre_resolve_debounce: DebouncedDelay::new(),
1847 next_inlay_id: 0,
1848 available_code_actions: Default::default(),
1849 code_actions_task: Default::default(),
1850 document_highlights_task: Default::default(),
1851 linked_editing_range_task: Default::default(),
1852 pending_rename: Default::default(),
1853 searchable: true,
1854 cursor_shape: Default::default(),
1855 current_line_highlight: None,
1856 autoindent_mode: Some(AutoindentMode::EachLine),
1857 collapse_matches: false,
1858 workspace: None,
1859 keymap_context_layers: Default::default(),
1860 input_enabled: true,
1861 use_modal_editing: mode == EditorMode::Full,
1862 read_only: false,
1863 use_autoclose: true,
1864 use_auto_surround: true,
1865 auto_replace_emoji_shortcode: false,
1866 leader_peer_id: None,
1867 remote_id: None,
1868 hover_state: Default::default(),
1869 hovered_link_state: Default::default(),
1870 inline_completion_provider: None,
1871 active_inline_completion: None,
1872 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1873 expanded_hunks: ExpandedHunks::default(),
1874 gutter_hovered: false,
1875 pixel_position_of_newest_cursor: None,
1876 last_bounds: None,
1877 expect_bounds_change: None,
1878 gutter_dimensions: GutterDimensions::default(),
1879 style: None,
1880 show_cursor_names: false,
1881 hovered_cursors: Default::default(),
1882 next_editor_action_id: EditorActionId::default(),
1883 editor_actions: Rc::default(),
1884 vim_replace_map: Default::default(),
1885 show_inline_completions: mode == EditorMode::Full,
1886 custom_context_menu: None,
1887 show_git_blame_gutter: false,
1888 show_git_blame_inline: false,
1889 show_selection_menu: None,
1890 show_git_blame_inline_delay_task: None,
1891 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1892 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1893 .session
1894 .restore_unsaved_buffers,
1895 blame: None,
1896 blame_subscription: None,
1897 file_header_size,
1898 tasks: Default::default(),
1899 _subscriptions: vec![
1900 cx.observe(&buffer, Self::on_buffer_changed),
1901 cx.subscribe(&buffer, Self::on_buffer_event),
1902 cx.observe(&display_map, Self::on_display_map_changed),
1903 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1904 cx.observe_global::<SettingsStore>(Self::settings_changed),
1905 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1906 cx.observe_window_activation(|editor, cx| {
1907 let active = cx.is_window_active();
1908 editor.blink_manager.update(cx, |blink_manager, cx| {
1909 if active {
1910 blink_manager.enable(cx);
1911 } else {
1912 blink_manager.disable(cx);
1913 }
1914 });
1915 }),
1916 ],
1917 tasks_update_task: None,
1918 linked_edit_ranges: Default::default(),
1919 previous_search_ranges: None,
1920 breadcrumb_header: None,
1921 focused_block: None,
1922 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1923 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1924 };
1925 this.tasks_update_task = Some(this.refresh_runnables(cx));
1926 this._subscriptions.extend(project_subscriptions);
1927
1928 this.end_selection(cx);
1929 this.scroll_manager.show_scrollbar(cx);
1930
1931 if mode == EditorMode::Full {
1932 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1933 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1934
1935 if this.git_blame_inline_enabled {
1936 this.git_blame_inline_enabled = true;
1937 this.start_git_blame_inline(false, cx);
1938 }
1939 }
1940
1941 this.report_editor_event("open", None, cx);
1942 this
1943 }
1944
1945 pub fn mouse_menu_is_focused(&self, cx: &mut WindowContext) -> bool {
1946 self.mouse_context_menu
1947 .as_ref()
1948 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(cx))
1949 }
1950
1951 fn key_context(&self, cx: &AppContext) -> KeyContext {
1952 let mut key_context = KeyContext::new_with_defaults();
1953 key_context.add("Editor");
1954 let mode = match self.mode {
1955 EditorMode::SingleLine { .. } => "single_line",
1956 EditorMode::AutoHeight { .. } => "auto_height",
1957 EditorMode::Full => "full",
1958 };
1959
1960 if EditorSettings::jupyter_enabled(cx) {
1961 key_context.add("jupyter");
1962 }
1963
1964 key_context.set("mode", mode);
1965 if self.pending_rename.is_some() {
1966 key_context.add("renaming");
1967 }
1968 if self.context_menu_visible() {
1969 match self.context_menu.read().as_ref() {
1970 Some(ContextMenu::Completions(_)) => {
1971 key_context.add("menu");
1972 key_context.add("showing_completions")
1973 }
1974 Some(ContextMenu::CodeActions(_)) => {
1975 key_context.add("menu");
1976 key_context.add("showing_code_actions")
1977 }
1978 None => {}
1979 }
1980 }
1981
1982 for layer in self.keymap_context_layers.values() {
1983 key_context.extend(layer);
1984 }
1985
1986 if let Some(extension) = self
1987 .buffer
1988 .read(cx)
1989 .as_singleton()
1990 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
1991 {
1992 key_context.set("extension", extension.to_string());
1993 }
1994
1995 if self.has_active_inline_completion(cx) {
1996 key_context.add("copilot_suggestion");
1997 key_context.add("inline_completion");
1998 }
1999
2000 key_context
2001 }
2002
2003 pub fn new_file(
2004 workspace: &mut Workspace,
2005 _: &workspace::NewFile,
2006 cx: &mut ViewContext<Workspace>,
2007 ) {
2008 Self::new_in_workspace(workspace, cx).detach_and_prompt_err(
2009 "Failed to create buffer",
2010 cx,
2011 |e, _| match e.error_code() {
2012 ErrorCode::RemoteUpgradeRequired => Some(format!(
2013 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2014 e.error_tag("required").unwrap_or("the latest version")
2015 )),
2016 _ => None,
2017 },
2018 );
2019 }
2020
2021 pub fn new_in_workspace(
2022 workspace: &mut Workspace,
2023 cx: &mut ViewContext<Workspace>,
2024 ) -> Task<Result<View<Editor>>> {
2025 let project = workspace.project().clone();
2026 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2027
2028 cx.spawn(|workspace, mut cx| async move {
2029 let buffer = create.await?;
2030 workspace.update(&mut cx, |workspace, cx| {
2031 let editor =
2032 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx));
2033 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
2034 editor
2035 })
2036 })
2037 }
2038
2039 pub fn new_file_in_direction(
2040 workspace: &mut Workspace,
2041 action: &workspace::NewFileInDirection,
2042 cx: &mut ViewContext<Workspace>,
2043 ) {
2044 let project = workspace.project().clone();
2045 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2046 let direction = action.0;
2047
2048 cx.spawn(|workspace, mut cx| async move {
2049 let buffer = create.await?;
2050 workspace.update(&mut cx, move |workspace, cx| {
2051 workspace.split_item(
2052 direction,
2053 Box::new(
2054 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)),
2055 ),
2056 cx,
2057 )
2058 })?;
2059 anyhow::Ok(())
2060 })
2061 .detach_and_prompt_err("Failed to create buffer", cx, |e, _| match e.error_code() {
2062 ErrorCode::RemoteUpgradeRequired => Some(format!(
2063 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2064 e.error_tag("required").unwrap_or("the latest version")
2065 )),
2066 _ => None,
2067 });
2068 }
2069
2070 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
2071 self.buffer.read(cx).replica_id()
2072 }
2073
2074 pub fn leader_peer_id(&self) -> Option<PeerId> {
2075 self.leader_peer_id
2076 }
2077
2078 pub fn buffer(&self) -> &Model<MultiBuffer> {
2079 &self.buffer
2080 }
2081
2082 pub fn workspace(&self) -> Option<View<Workspace>> {
2083 self.workspace.as_ref()?.0.upgrade()
2084 }
2085
2086 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
2087 self.buffer().read(cx).title(cx)
2088 }
2089
2090 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
2091 EditorSnapshot {
2092 mode: self.mode,
2093 show_gutter: self.show_gutter,
2094 show_line_numbers: self.show_line_numbers,
2095 show_git_diff_gutter: self.show_git_diff_gutter,
2096 show_code_actions: self.show_code_actions,
2097 show_runnables: self.show_runnables,
2098 render_git_blame_gutter: self.render_git_blame_gutter(cx),
2099 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
2100 scroll_anchor: self.scroll_manager.anchor(),
2101 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
2102 placeholder_text: self.placeholder_text.clone(),
2103 is_focused: self.focus_handle.is_focused(cx),
2104 current_line_highlight: self
2105 .current_line_highlight
2106 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
2107 gutter_hovered: self.gutter_hovered,
2108 }
2109 }
2110
2111 pub fn language_at<T: ToOffset>(&self, point: T, cx: &AppContext) -> Option<Arc<Language>> {
2112 self.buffer.read(cx).language_at(point, cx)
2113 }
2114
2115 pub fn file_at<T: ToOffset>(
2116 &self,
2117 point: T,
2118 cx: &AppContext,
2119 ) -> Option<Arc<dyn language::File>> {
2120 self.buffer.read(cx).read(cx).file_at(point).cloned()
2121 }
2122
2123 pub fn active_excerpt(
2124 &self,
2125 cx: &AppContext,
2126 ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
2127 self.buffer
2128 .read(cx)
2129 .excerpt_containing(self.selections.newest_anchor().head(), cx)
2130 }
2131
2132 pub fn mode(&self) -> EditorMode {
2133 self.mode
2134 }
2135
2136 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
2137 self.collaboration_hub.as_deref()
2138 }
2139
2140 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
2141 self.collaboration_hub = Some(hub);
2142 }
2143
2144 pub fn set_custom_context_menu(
2145 &mut self,
2146 f: impl 'static
2147 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
2148 ) {
2149 self.custom_context_menu = Some(Box::new(f))
2150 }
2151
2152 pub fn set_completion_provider(&mut self, provider: Box<dyn CompletionProvider>) {
2153 self.completion_provider = Some(provider);
2154 }
2155
2156 pub fn set_inline_completion_provider<T>(
2157 &mut self,
2158 provider: Option<Model<T>>,
2159 cx: &mut ViewContext<Self>,
2160 ) where
2161 T: InlineCompletionProvider,
2162 {
2163 self.inline_completion_provider =
2164 provider.map(|provider| RegisteredInlineCompletionProvider {
2165 _subscription: cx.observe(&provider, |this, _, cx| {
2166 if this.focus_handle.is_focused(cx) {
2167 this.update_visible_inline_completion(cx);
2168 }
2169 }),
2170 provider: Arc::new(provider),
2171 });
2172 self.refresh_inline_completion(false, cx);
2173 }
2174
2175 pub fn placeholder_text(&self, _cx: &WindowContext) -> Option<&str> {
2176 self.placeholder_text.as_deref()
2177 }
2178
2179 pub fn set_placeholder_text(
2180 &mut self,
2181 placeholder_text: impl Into<Arc<str>>,
2182 cx: &mut ViewContext<Self>,
2183 ) {
2184 let placeholder_text = Some(placeholder_text.into());
2185 if self.placeholder_text != placeholder_text {
2186 self.placeholder_text = placeholder_text;
2187 cx.notify();
2188 }
2189 }
2190
2191 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
2192 self.cursor_shape = cursor_shape;
2193
2194 // Disrupt blink for immediate user feedback that the cursor shape has changed
2195 self.blink_manager.update(cx, BlinkManager::show_cursor);
2196
2197 cx.notify();
2198 }
2199
2200 pub fn set_current_line_highlight(
2201 &mut self,
2202 current_line_highlight: Option<CurrentLineHighlight>,
2203 ) {
2204 self.current_line_highlight = current_line_highlight;
2205 }
2206
2207 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
2208 self.collapse_matches = collapse_matches;
2209 }
2210
2211 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
2212 if self.collapse_matches {
2213 return range.start..range.start;
2214 }
2215 range.clone()
2216 }
2217
2218 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
2219 if self.display_map.read(cx).clip_at_line_ends != clip {
2220 self.display_map
2221 .update(cx, |map, _| map.clip_at_line_ends = clip);
2222 }
2223 }
2224
2225 pub fn set_keymap_context_layer<Tag: 'static>(
2226 &mut self,
2227 context: KeyContext,
2228 cx: &mut ViewContext<Self>,
2229 ) {
2230 self.keymap_context_layers
2231 .insert(TypeId::of::<Tag>(), context);
2232 cx.notify();
2233 }
2234
2235 pub fn remove_keymap_context_layer<Tag: 'static>(&mut self, cx: &mut ViewContext<Self>) {
2236 self.keymap_context_layers.remove(&TypeId::of::<Tag>());
2237 cx.notify();
2238 }
2239
2240 pub fn set_input_enabled(&mut self, input_enabled: bool) {
2241 self.input_enabled = input_enabled;
2242 }
2243
2244 pub fn set_autoindent(&mut self, autoindent: bool) {
2245 if autoindent {
2246 self.autoindent_mode = Some(AutoindentMode::EachLine);
2247 } else {
2248 self.autoindent_mode = None;
2249 }
2250 }
2251
2252 pub fn read_only(&self, cx: &AppContext) -> bool {
2253 self.read_only || self.buffer.read(cx).read_only()
2254 }
2255
2256 pub fn set_read_only(&mut self, read_only: bool) {
2257 self.read_only = read_only;
2258 }
2259
2260 pub fn set_use_autoclose(&mut self, autoclose: bool) {
2261 self.use_autoclose = autoclose;
2262 }
2263
2264 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
2265 self.use_auto_surround = auto_surround;
2266 }
2267
2268 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
2269 self.auto_replace_emoji_shortcode = auto_replace;
2270 }
2271
2272 pub fn set_show_inline_completions(&mut self, show_inline_completions: bool) {
2273 self.show_inline_completions = show_inline_completions;
2274 }
2275
2276 pub fn set_use_modal_editing(&mut self, to: bool) {
2277 self.use_modal_editing = to;
2278 }
2279
2280 pub fn use_modal_editing(&self) -> bool {
2281 self.use_modal_editing
2282 }
2283
2284 fn selections_did_change(
2285 &mut self,
2286 local: bool,
2287 old_cursor_position: &Anchor,
2288 show_completions: bool,
2289 cx: &mut ViewContext<Self>,
2290 ) {
2291 // Copy selections to primary selection buffer
2292 #[cfg(target_os = "linux")]
2293 if local {
2294 let selections = self.selections.all::<usize>(cx);
2295 let buffer_handle = self.buffer.read(cx).read(cx);
2296
2297 let mut text = String::new();
2298 for (index, selection) in selections.iter().enumerate() {
2299 let text_for_selection = buffer_handle
2300 .text_for_range(selection.start..selection.end)
2301 .collect::<String>();
2302
2303 text.push_str(&text_for_selection);
2304 if index != selections.len() - 1 {
2305 text.push('\n');
2306 }
2307 }
2308
2309 if !text.is_empty() {
2310 cx.write_to_primary(ClipboardItem::new_string(text));
2311 }
2312 }
2313
2314 if self.focus_handle.is_focused(cx) && self.leader_peer_id.is_none() {
2315 self.buffer.update(cx, |buffer, cx| {
2316 buffer.set_active_selections(
2317 &self.selections.disjoint_anchors(),
2318 self.selections.line_mode,
2319 self.cursor_shape,
2320 cx,
2321 )
2322 });
2323 }
2324 let display_map = self
2325 .display_map
2326 .update(cx, |display_map, cx| display_map.snapshot(cx));
2327 let buffer = &display_map.buffer_snapshot;
2328 self.add_selections_state = None;
2329 self.select_next_state = None;
2330 self.select_prev_state = None;
2331 self.select_larger_syntax_node_stack.clear();
2332 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2333 self.snippet_stack
2334 .invalidate(&self.selections.disjoint_anchors(), buffer);
2335 self.take_rename(false, cx);
2336
2337 let new_cursor_position = self.selections.newest_anchor().head();
2338
2339 self.push_to_nav_history(
2340 *old_cursor_position,
2341 Some(new_cursor_position.to_point(buffer)),
2342 cx,
2343 );
2344
2345 if local {
2346 let new_cursor_position = self.selections.newest_anchor().head();
2347 let mut context_menu = self.context_menu.write();
2348 let completion_menu = match context_menu.as_ref() {
2349 Some(ContextMenu::Completions(menu)) => Some(menu),
2350
2351 _ => {
2352 *context_menu = None;
2353 None
2354 }
2355 };
2356
2357 if let Some(completion_menu) = completion_menu {
2358 let cursor_position = new_cursor_position.to_offset(buffer);
2359 let (word_range, kind) = buffer.surrounding_word(completion_menu.initial_position);
2360 if kind == Some(CharKind::Word)
2361 && word_range.to_inclusive().contains(&cursor_position)
2362 {
2363 let mut completion_menu = completion_menu.clone();
2364 drop(context_menu);
2365
2366 let query = Self::completion_query(buffer, cursor_position);
2367 cx.spawn(move |this, mut cx| async move {
2368 completion_menu
2369 .filter(query.as_deref(), cx.background_executor().clone())
2370 .await;
2371
2372 this.update(&mut cx, |this, cx| {
2373 let mut context_menu = this.context_menu.write();
2374 let Some(ContextMenu::Completions(menu)) = context_menu.as_ref() else {
2375 return;
2376 };
2377
2378 if menu.id > completion_menu.id {
2379 return;
2380 }
2381
2382 *context_menu = Some(ContextMenu::Completions(completion_menu));
2383 drop(context_menu);
2384 cx.notify();
2385 })
2386 })
2387 .detach();
2388
2389 if show_completions {
2390 self.show_completions(&ShowCompletions { trigger: None }, cx);
2391 }
2392 } else {
2393 drop(context_menu);
2394 self.hide_context_menu(cx);
2395 }
2396 } else {
2397 drop(context_menu);
2398 }
2399
2400 hide_hover(self, cx);
2401
2402 if old_cursor_position.to_display_point(&display_map).row()
2403 != new_cursor_position.to_display_point(&display_map).row()
2404 {
2405 self.available_code_actions.take();
2406 }
2407 self.refresh_code_actions(cx);
2408 self.refresh_document_highlights(cx);
2409 refresh_matching_bracket_highlights(self, cx);
2410 self.discard_inline_completion(false, cx);
2411 linked_editing_ranges::refresh_linked_ranges(self, cx);
2412 if self.git_blame_inline_enabled {
2413 self.start_inline_blame_timer(cx);
2414 }
2415 }
2416
2417 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2418 cx.emit(EditorEvent::SelectionsChanged { local });
2419
2420 if self.selections.disjoint_anchors().len() == 1 {
2421 cx.emit(SearchEvent::ActiveMatchChanged)
2422 }
2423 cx.notify();
2424 }
2425
2426 pub fn change_selections<R>(
2427 &mut self,
2428 autoscroll: Option<Autoscroll>,
2429 cx: &mut ViewContext<Self>,
2430 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2431 ) -> R {
2432 self.change_selections_inner(autoscroll, true, cx, change)
2433 }
2434
2435 pub fn change_selections_inner<R>(
2436 &mut self,
2437 autoscroll: Option<Autoscroll>,
2438 request_completions: bool,
2439 cx: &mut ViewContext<Self>,
2440 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2441 ) -> R {
2442 let old_cursor_position = self.selections.newest_anchor().head();
2443 self.push_to_selection_history();
2444
2445 let (changed, result) = self.selections.change_with(cx, change);
2446
2447 if changed {
2448 if let Some(autoscroll) = autoscroll {
2449 self.request_autoscroll(autoscroll, cx);
2450 }
2451 self.selections_did_change(true, &old_cursor_position, request_completions, cx);
2452
2453 if self.should_open_signature_help_automatically(
2454 &old_cursor_position,
2455 self.signature_help_state.backspace_pressed(),
2456 cx,
2457 ) {
2458 self.show_signature_help(&ShowSignatureHelp, cx);
2459 }
2460 self.signature_help_state.set_backspace_pressed(false);
2461 }
2462
2463 result
2464 }
2465
2466 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2467 where
2468 I: IntoIterator<Item = (Range<S>, T)>,
2469 S: ToOffset,
2470 T: Into<Arc<str>>,
2471 {
2472 if self.read_only(cx) {
2473 return;
2474 }
2475
2476 self.buffer
2477 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2478 }
2479
2480 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2481 where
2482 I: IntoIterator<Item = (Range<S>, T)>,
2483 S: ToOffset,
2484 T: Into<Arc<str>>,
2485 {
2486 if self.read_only(cx) {
2487 return;
2488 }
2489
2490 self.buffer.update(cx, |buffer, cx| {
2491 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2492 });
2493 }
2494
2495 pub fn edit_with_block_indent<I, S, T>(
2496 &mut self,
2497 edits: I,
2498 original_indent_columns: Vec<u32>,
2499 cx: &mut ViewContext<Self>,
2500 ) where
2501 I: IntoIterator<Item = (Range<S>, T)>,
2502 S: ToOffset,
2503 T: Into<Arc<str>>,
2504 {
2505 if self.read_only(cx) {
2506 return;
2507 }
2508
2509 self.buffer.update(cx, |buffer, cx| {
2510 buffer.edit(
2511 edits,
2512 Some(AutoindentMode::Block {
2513 original_indent_columns,
2514 }),
2515 cx,
2516 )
2517 });
2518 }
2519
2520 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
2521 self.hide_context_menu(cx);
2522
2523 match phase {
2524 SelectPhase::Begin {
2525 position,
2526 add,
2527 click_count,
2528 } => self.begin_selection(position, add, click_count, cx),
2529 SelectPhase::BeginColumnar {
2530 position,
2531 goal_column,
2532 reset,
2533 } => self.begin_columnar_selection(position, goal_column, reset, cx),
2534 SelectPhase::Extend {
2535 position,
2536 click_count,
2537 } => self.extend_selection(position, click_count, cx),
2538 SelectPhase::Update {
2539 position,
2540 goal_column,
2541 scroll_delta,
2542 } => self.update_selection(position, goal_column, scroll_delta, cx),
2543 SelectPhase::End => self.end_selection(cx),
2544 }
2545 }
2546
2547 fn extend_selection(
2548 &mut self,
2549 position: DisplayPoint,
2550 click_count: usize,
2551 cx: &mut ViewContext<Self>,
2552 ) {
2553 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2554 let tail = self.selections.newest::<usize>(cx).tail();
2555 self.begin_selection(position, false, click_count, cx);
2556
2557 let position = position.to_offset(&display_map, Bias::Left);
2558 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2559
2560 let mut pending_selection = self
2561 .selections
2562 .pending_anchor()
2563 .expect("extend_selection not called with pending selection");
2564 if position >= tail {
2565 pending_selection.start = tail_anchor;
2566 } else {
2567 pending_selection.end = tail_anchor;
2568 pending_selection.reversed = true;
2569 }
2570
2571 let mut pending_mode = self.selections.pending_mode().unwrap();
2572 match &mut pending_mode {
2573 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2574 _ => {}
2575 }
2576
2577 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
2578 s.set_pending(pending_selection, pending_mode)
2579 });
2580 }
2581
2582 fn begin_selection(
2583 &mut self,
2584 position: DisplayPoint,
2585 add: bool,
2586 click_count: usize,
2587 cx: &mut ViewContext<Self>,
2588 ) {
2589 if !self.focus_handle.is_focused(cx) {
2590 self.last_focused_descendant = None;
2591 cx.focus(&self.focus_handle);
2592 }
2593
2594 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2595 let buffer = &display_map.buffer_snapshot;
2596 let newest_selection = self.selections.newest_anchor().clone();
2597 let position = display_map.clip_point(position, Bias::Left);
2598
2599 let start;
2600 let end;
2601 let mode;
2602 let auto_scroll;
2603 match click_count {
2604 1 => {
2605 start = buffer.anchor_before(position.to_point(&display_map));
2606 end = start;
2607 mode = SelectMode::Character;
2608 auto_scroll = true;
2609 }
2610 2 => {
2611 let range = movement::surrounding_word(&display_map, position);
2612 start = buffer.anchor_before(range.start.to_point(&display_map));
2613 end = buffer.anchor_before(range.end.to_point(&display_map));
2614 mode = SelectMode::Word(start..end);
2615 auto_scroll = true;
2616 }
2617 3 => {
2618 let position = display_map
2619 .clip_point(position, Bias::Left)
2620 .to_point(&display_map);
2621 let line_start = display_map.prev_line_boundary(position).0;
2622 let next_line_start = buffer.clip_point(
2623 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2624 Bias::Left,
2625 );
2626 start = buffer.anchor_before(line_start);
2627 end = buffer.anchor_before(next_line_start);
2628 mode = SelectMode::Line(start..end);
2629 auto_scroll = true;
2630 }
2631 _ => {
2632 start = buffer.anchor_before(0);
2633 end = buffer.anchor_before(buffer.len());
2634 mode = SelectMode::All;
2635 auto_scroll = false;
2636 }
2637 }
2638
2639 let point_to_delete: Option<usize> = {
2640 let selected_points: Vec<Selection<Point>> =
2641 self.selections.disjoint_in_range(start..end, cx);
2642
2643 if !add || click_count > 1 {
2644 None
2645 } else if selected_points.len() > 0 {
2646 Some(selected_points[0].id)
2647 } else {
2648 let clicked_point_already_selected =
2649 self.selections.disjoint.iter().find(|selection| {
2650 selection.start.to_point(buffer) == start.to_point(buffer)
2651 || selection.end.to_point(buffer) == end.to_point(buffer)
2652 });
2653
2654 if let Some(selection) = clicked_point_already_selected {
2655 Some(selection.id)
2656 } else {
2657 None
2658 }
2659 }
2660 };
2661
2662 let selections_count = self.selections.count();
2663
2664 self.change_selections(auto_scroll.then(|| Autoscroll::newest()), cx, |s| {
2665 if let Some(point_to_delete) = point_to_delete {
2666 s.delete(point_to_delete);
2667
2668 if selections_count == 1 {
2669 s.set_pending_anchor_range(start..end, mode);
2670 }
2671 } else {
2672 if !add {
2673 s.clear_disjoint();
2674 } else if click_count > 1 {
2675 s.delete(newest_selection.id)
2676 }
2677
2678 s.set_pending_anchor_range(start..end, mode);
2679 }
2680 });
2681 }
2682
2683 fn begin_columnar_selection(
2684 &mut self,
2685 position: DisplayPoint,
2686 goal_column: u32,
2687 reset: bool,
2688 cx: &mut ViewContext<Self>,
2689 ) {
2690 if !self.focus_handle.is_focused(cx) {
2691 self.last_focused_descendant = None;
2692 cx.focus(&self.focus_handle);
2693 }
2694
2695 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2696
2697 if reset {
2698 let pointer_position = display_map
2699 .buffer_snapshot
2700 .anchor_before(position.to_point(&display_map));
2701
2702 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
2703 s.clear_disjoint();
2704 s.set_pending_anchor_range(
2705 pointer_position..pointer_position,
2706 SelectMode::Character,
2707 );
2708 });
2709 }
2710
2711 let tail = self.selections.newest::<Point>(cx).tail();
2712 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2713
2714 if !reset {
2715 self.select_columns(
2716 tail.to_display_point(&display_map),
2717 position,
2718 goal_column,
2719 &display_map,
2720 cx,
2721 );
2722 }
2723 }
2724
2725 fn update_selection(
2726 &mut self,
2727 position: DisplayPoint,
2728 goal_column: u32,
2729 scroll_delta: gpui::Point<f32>,
2730 cx: &mut ViewContext<Self>,
2731 ) {
2732 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2733
2734 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2735 let tail = tail.to_display_point(&display_map);
2736 self.select_columns(tail, position, goal_column, &display_map, cx);
2737 } else if let Some(mut pending) = self.selections.pending_anchor() {
2738 let buffer = self.buffer.read(cx).snapshot(cx);
2739 let head;
2740 let tail;
2741 let mode = self.selections.pending_mode().unwrap();
2742 match &mode {
2743 SelectMode::Character => {
2744 head = position.to_point(&display_map);
2745 tail = pending.tail().to_point(&buffer);
2746 }
2747 SelectMode::Word(original_range) => {
2748 let original_display_range = original_range.start.to_display_point(&display_map)
2749 ..original_range.end.to_display_point(&display_map);
2750 let original_buffer_range = original_display_range.start.to_point(&display_map)
2751 ..original_display_range.end.to_point(&display_map);
2752 if movement::is_inside_word(&display_map, position)
2753 || original_display_range.contains(&position)
2754 {
2755 let word_range = movement::surrounding_word(&display_map, position);
2756 if word_range.start < original_display_range.start {
2757 head = word_range.start.to_point(&display_map);
2758 } else {
2759 head = word_range.end.to_point(&display_map);
2760 }
2761 } else {
2762 head = position.to_point(&display_map);
2763 }
2764
2765 if head <= original_buffer_range.start {
2766 tail = original_buffer_range.end;
2767 } else {
2768 tail = original_buffer_range.start;
2769 }
2770 }
2771 SelectMode::Line(original_range) => {
2772 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2773
2774 let position = display_map
2775 .clip_point(position, Bias::Left)
2776 .to_point(&display_map);
2777 let line_start = display_map.prev_line_boundary(position).0;
2778 let next_line_start = buffer.clip_point(
2779 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2780 Bias::Left,
2781 );
2782
2783 if line_start < original_range.start {
2784 head = line_start
2785 } else {
2786 head = next_line_start
2787 }
2788
2789 if head <= original_range.start {
2790 tail = original_range.end;
2791 } else {
2792 tail = original_range.start;
2793 }
2794 }
2795 SelectMode::All => {
2796 return;
2797 }
2798 };
2799
2800 if head < tail {
2801 pending.start = buffer.anchor_before(head);
2802 pending.end = buffer.anchor_before(tail);
2803 pending.reversed = true;
2804 } else {
2805 pending.start = buffer.anchor_before(tail);
2806 pending.end = buffer.anchor_before(head);
2807 pending.reversed = false;
2808 }
2809
2810 self.change_selections(None, cx, |s| {
2811 s.set_pending(pending, mode);
2812 });
2813 } else {
2814 log::error!("update_selection dispatched with no pending selection");
2815 return;
2816 }
2817
2818 self.apply_scroll_delta(scroll_delta, cx);
2819 cx.notify();
2820 }
2821
2822 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
2823 self.columnar_selection_tail.take();
2824 if self.selections.pending_anchor().is_some() {
2825 let selections = self.selections.all::<usize>(cx);
2826 self.change_selections(None, cx, |s| {
2827 s.select(selections);
2828 s.clear_pending();
2829 });
2830 }
2831 }
2832
2833 fn select_columns(
2834 &mut self,
2835 tail: DisplayPoint,
2836 head: DisplayPoint,
2837 goal_column: u32,
2838 display_map: &DisplaySnapshot,
2839 cx: &mut ViewContext<Self>,
2840 ) {
2841 let start_row = cmp::min(tail.row(), head.row());
2842 let end_row = cmp::max(tail.row(), head.row());
2843 let start_column = cmp::min(tail.column(), goal_column);
2844 let end_column = cmp::max(tail.column(), goal_column);
2845 let reversed = start_column < tail.column();
2846
2847 let selection_ranges = (start_row.0..=end_row.0)
2848 .map(DisplayRow)
2849 .filter_map(|row| {
2850 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2851 let start = display_map
2852 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2853 .to_point(display_map);
2854 let end = display_map
2855 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2856 .to_point(display_map);
2857 if reversed {
2858 Some(end..start)
2859 } else {
2860 Some(start..end)
2861 }
2862 } else {
2863 None
2864 }
2865 })
2866 .collect::<Vec<_>>();
2867
2868 self.change_selections(None, cx, |s| {
2869 s.select_ranges(selection_ranges);
2870 });
2871 cx.notify();
2872 }
2873
2874 pub fn has_pending_nonempty_selection(&self) -> bool {
2875 let pending_nonempty_selection = match self.selections.pending_anchor() {
2876 Some(Selection { start, end, .. }) => start != end,
2877 None => false,
2878 };
2879
2880 pending_nonempty_selection
2881 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2882 }
2883
2884 pub fn has_pending_selection(&self) -> bool {
2885 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2886 }
2887
2888 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
2889 if self.clear_clicked_diff_hunks(cx) {
2890 cx.notify();
2891 return;
2892 }
2893 if self.dismiss_menus_and_popups(true, cx) {
2894 return;
2895 }
2896
2897 if self.mode == EditorMode::Full {
2898 if self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel()) {
2899 return;
2900 }
2901 }
2902
2903 cx.propagate();
2904 }
2905
2906 pub fn dismiss_menus_and_popups(
2907 &mut self,
2908 should_report_inline_completion_event: bool,
2909 cx: &mut ViewContext<Self>,
2910 ) -> bool {
2911 if self.take_rename(false, cx).is_some() {
2912 return true;
2913 }
2914
2915 if hide_hover(self, cx) {
2916 return true;
2917 }
2918
2919 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2920 return true;
2921 }
2922
2923 if self.hide_context_menu(cx).is_some() {
2924 return true;
2925 }
2926
2927 if self.mouse_context_menu.take().is_some() {
2928 return true;
2929 }
2930
2931 if self.discard_inline_completion(should_report_inline_completion_event, cx) {
2932 return true;
2933 }
2934
2935 if self.snippet_stack.pop().is_some() {
2936 return true;
2937 }
2938
2939 if self.mode == EditorMode::Full {
2940 if self.active_diagnostics.is_some() {
2941 self.dismiss_diagnostics(cx);
2942 return true;
2943 }
2944 }
2945
2946 false
2947 }
2948
2949 fn linked_editing_ranges_for(
2950 &self,
2951 selection: Range<text::Anchor>,
2952 cx: &AppContext,
2953 ) -> Option<HashMap<Model<Buffer>, Vec<Range<text::Anchor>>>> {
2954 if self.linked_edit_ranges.is_empty() {
2955 return None;
2956 }
2957 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2958 selection.end.buffer_id.and_then(|end_buffer_id| {
2959 if selection.start.buffer_id != Some(end_buffer_id) {
2960 return None;
2961 }
2962 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2963 let snapshot = buffer.read(cx).snapshot();
2964 self.linked_edit_ranges
2965 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2966 .map(|ranges| (ranges, snapshot, buffer))
2967 })?;
2968 use text::ToOffset as TO;
2969 // find offset from the start of current range to current cursor position
2970 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2971
2972 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2973 let start_difference = start_offset - start_byte_offset;
2974 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2975 let end_difference = end_offset - start_byte_offset;
2976 // Current range has associated linked ranges.
2977 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2978 for range in linked_ranges.iter() {
2979 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2980 let end_offset = start_offset + end_difference;
2981 let start_offset = start_offset + start_difference;
2982 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2983 continue;
2984 }
2985 let start = buffer_snapshot.anchor_after(start_offset);
2986 let end = buffer_snapshot.anchor_after(end_offset);
2987 linked_edits
2988 .entry(buffer.clone())
2989 .or_default()
2990 .push(start..end);
2991 }
2992 Some(linked_edits)
2993 }
2994
2995 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2996 let text: Arc<str> = text.into();
2997
2998 if self.read_only(cx) {
2999 return;
3000 }
3001
3002 let selections = self.selections.all_adjusted(cx);
3003 let mut bracket_inserted = false;
3004 let mut edits = Vec::new();
3005 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3006 let mut new_selections = Vec::with_capacity(selections.len());
3007 let mut new_autoclose_regions = Vec::new();
3008 let snapshot = self.buffer.read(cx).read(cx);
3009
3010 for (selection, autoclose_region) in
3011 self.selections_with_autoclose_regions(selections, &snapshot)
3012 {
3013 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
3014 // Determine if the inserted text matches the opening or closing
3015 // bracket of any of this language's bracket pairs.
3016 let mut bracket_pair = None;
3017 let mut is_bracket_pair_start = false;
3018 let mut is_bracket_pair_end = false;
3019 if !text.is_empty() {
3020 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
3021 // and they are removing the character that triggered IME popup.
3022 for (pair, enabled) in scope.brackets() {
3023 if !pair.close && !pair.surround {
3024 continue;
3025 }
3026
3027 if enabled && pair.start.ends_with(text.as_ref()) {
3028 bracket_pair = Some(pair.clone());
3029 is_bracket_pair_start = true;
3030 break;
3031 }
3032 if pair.end.as_str() == text.as_ref() {
3033 bracket_pair = Some(pair.clone());
3034 is_bracket_pair_end = true;
3035 break;
3036 }
3037 }
3038 }
3039
3040 if let Some(bracket_pair) = bracket_pair {
3041 let snapshot_settings = snapshot.settings_at(selection.start, cx);
3042 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
3043 let auto_surround =
3044 self.use_auto_surround && snapshot_settings.use_auto_surround;
3045 if selection.is_empty() {
3046 if is_bracket_pair_start {
3047 let prefix_len = bracket_pair.start.len() - text.len();
3048
3049 // If the inserted text is a suffix of an opening bracket and the
3050 // selection is preceded by the rest of the opening bracket, then
3051 // insert the closing bracket.
3052 let following_text_allows_autoclose = snapshot
3053 .chars_at(selection.start)
3054 .next()
3055 .map_or(true, |c| scope.should_autoclose_before(c));
3056 let preceding_text_matches_prefix = prefix_len == 0
3057 || (selection.start.column >= (prefix_len as u32)
3058 && snapshot.contains_str_at(
3059 Point::new(
3060 selection.start.row,
3061 selection.start.column - (prefix_len as u32),
3062 ),
3063 &bracket_pair.start[..prefix_len],
3064 ));
3065
3066 if autoclose
3067 && bracket_pair.close
3068 && following_text_allows_autoclose
3069 && preceding_text_matches_prefix
3070 {
3071 let anchor = snapshot.anchor_before(selection.end);
3072 new_selections.push((selection.map(|_| anchor), text.len()));
3073 new_autoclose_regions.push((
3074 anchor,
3075 text.len(),
3076 selection.id,
3077 bracket_pair.clone(),
3078 ));
3079 edits.push((
3080 selection.range(),
3081 format!("{}{}", text, bracket_pair.end).into(),
3082 ));
3083 bracket_inserted = true;
3084 continue;
3085 }
3086 }
3087
3088 if let Some(region) = autoclose_region {
3089 // If the selection is followed by an auto-inserted closing bracket,
3090 // then don't insert that closing bracket again; just move the selection
3091 // past the closing bracket.
3092 let should_skip = selection.end == region.range.end.to_point(&snapshot)
3093 && text.as_ref() == region.pair.end.as_str();
3094 if should_skip {
3095 let anchor = snapshot.anchor_after(selection.end);
3096 new_selections
3097 .push((selection.map(|_| anchor), region.pair.end.len()));
3098 continue;
3099 }
3100 }
3101
3102 let always_treat_brackets_as_autoclosed = snapshot
3103 .settings_at(selection.start, cx)
3104 .always_treat_brackets_as_autoclosed;
3105 if always_treat_brackets_as_autoclosed
3106 && is_bracket_pair_end
3107 && snapshot.contains_str_at(selection.end, text.as_ref())
3108 {
3109 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
3110 // and the inserted text is a closing bracket and the selection is followed
3111 // by the closing bracket then move the selection past the closing bracket.
3112 let anchor = snapshot.anchor_after(selection.end);
3113 new_selections.push((selection.map(|_| anchor), text.len()));
3114 continue;
3115 }
3116 }
3117 // If an opening bracket is 1 character long and is typed while
3118 // text is selected, then surround that text with the bracket pair.
3119 else if auto_surround
3120 && bracket_pair.surround
3121 && is_bracket_pair_start
3122 && bracket_pair.start.chars().count() == 1
3123 {
3124 edits.push((selection.start..selection.start, text.clone()));
3125 edits.push((
3126 selection.end..selection.end,
3127 bracket_pair.end.as_str().into(),
3128 ));
3129 bracket_inserted = true;
3130 new_selections.push((
3131 Selection {
3132 id: selection.id,
3133 start: snapshot.anchor_after(selection.start),
3134 end: snapshot.anchor_before(selection.end),
3135 reversed: selection.reversed,
3136 goal: selection.goal,
3137 },
3138 0,
3139 ));
3140 continue;
3141 }
3142 }
3143 }
3144
3145 if self.auto_replace_emoji_shortcode
3146 && selection.is_empty()
3147 && text.as_ref().ends_with(':')
3148 {
3149 if let Some(possible_emoji_short_code) =
3150 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
3151 {
3152 if !possible_emoji_short_code.is_empty() {
3153 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
3154 let emoji_shortcode_start = Point::new(
3155 selection.start.row,
3156 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
3157 );
3158
3159 // Remove shortcode from buffer
3160 edits.push((
3161 emoji_shortcode_start..selection.start,
3162 "".to_string().into(),
3163 ));
3164 new_selections.push((
3165 Selection {
3166 id: selection.id,
3167 start: snapshot.anchor_after(emoji_shortcode_start),
3168 end: snapshot.anchor_before(selection.start),
3169 reversed: selection.reversed,
3170 goal: selection.goal,
3171 },
3172 0,
3173 ));
3174
3175 // Insert emoji
3176 let selection_start_anchor = snapshot.anchor_after(selection.start);
3177 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3178 edits.push((selection.start..selection.end, emoji.to_string().into()));
3179
3180 continue;
3181 }
3182 }
3183 }
3184 }
3185
3186 // If not handling any auto-close operation, then just replace the selected
3187 // text with the given input and move the selection to the end of the
3188 // newly inserted text.
3189 let anchor = snapshot.anchor_after(selection.end);
3190 if !self.linked_edit_ranges.is_empty() {
3191 let start_anchor = snapshot.anchor_before(selection.start);
3192
3193 let is_word_char = text.chars().next().map_or(true, |char| {
3194 let scope = snapshot.language_scope_at(start_anchor.to_offset(&snapshot));
3195 let kind = char_kind(&scope, char);
3196
3197 kind == CharKind::Word
3198 });
3199
3200 if is_word_char {
3201 if let Some(ranges) = self
3202 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3203 {
3204 for (buffer, edits) in ranges {
3205 linked_edits
3206 .entry(buffer.clone())
3207 .or_default()
3208 .extend(edits.into_iter().map(|range| (range, text.clone())));
3209 }
3210 }
3211 }
3212 }
3213
3214 new_selections.push((selection.map(|_| anchor), 0));
3215 edits.push((selection.start..selection.end, text.clone()));
3216 }
3217
3218 drop(snapshot);
3219
3220 self.transact(cx, |this, cx| {
3221 this.buffer.update(cx, |buffer, cx| {
3222 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3223 });
3224 for (buffer, edits) in linked_edits {
3225 buffer.update(cx, |buffer, cx| {
3226 let snapshot = buffer.snapshot();
3227 let edits = edits
3228 .into_iter()
3229 .map(|(range, text)| {
3230 use text::ToPoint as TP;
3231 let end_point = TP::to_point(&range.end, &snapshot);
3232 let start_point = TP::to_point(&range.start, &snapshot);
3233 (start_point..end_point, text)
3234 })
3235 .sorted_by_key(|(range, _)| range.start)
3236 .collect::<Vec<_>>();
3237 buffer.edit(edits, None, cx);
3238 })
3239 }
3240 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3241 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3242 let snapshot = this.buffer.read(cx).read(cx);
3243 let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
3244 .zip(new_selection_deltas)
3245 .map(|(selection, delta)| Selection {
3246 id: selection.id,
3247 start: selection.start + delta,
3248 end: selection.end + delta,
3249 reversed: selection.reversed,
3250 goal: SelectionGoal::None,
3251 })
3252 .collect::<Vec<_>>();
3253
3254 let mut i = 0;
3255 for (position, delta, selection_id, pair) in new_autoclose_regions {
3256 let position = position.to_offset(&snapshot) + delta;
3257 let start = snapshot.anchor_before(position);
3258 let end = snapshot.anchor_after(position);
3259 while let Some(existing_state) = this.autoclose_regions.get(i) {
3260 match existing_state.range.start.cmp(&start, &snapshot) {
3261 Ordering::Less => i += 1,
3262 Ordering::Greater => break,
3263 Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
3264 Ordering::Less => i += 1,
3265 Ordering::Equal => break,
3266 Ordering::Greater => break,
3267 },
3268 }
3269 }
3270 this.autoclose_regions.insert(
3271 i,
3272 AutocloseRegion {
3273 selection_id,
3274 range: start..end,
3275 pair,
3276 },
3277 );
3278 }
3279
3280 drop(snapshot);
3281 let had_active_inline_completion = this.has_active_inline_completion(cx);
3282 this.change_selections_inner(Some(Autoscroll::fit()), false, cx, |s| {
3283 s.select(new_selections)
3284 });
3285
3286 if !bracket_inserted && EditorSettings::get_global(cx).use_on_type_format {
3287 if let Some(on_type_format_task) =
3288 this.trigger_on_type_formatting(text.to_string(), cx)
3289 {
3290 on_type_format_task.detach_and_log_err(cx);
3291 }
3292 }
3293
3294 let editor_settings = EditorSettings::get_global(cx);
3295 if bracket_inserted
3296 && (editor_settings.auto_signature_help
3297 || editor_settings.show_signature_help_after_edits)
3298 {
3299 this.show_signature_help(&ShowSignatureHelp, cx);
3300 }
3301
3302 let trigger_in_words = !had_active_inline_completion;
3303 this.trigger_completion_on_input(&text, trigger_in_words, cx);
3304 linked_editing_ranges::refresh_linked_ranges(this, cx);
3305 this.refresh_inline_completion(true, cx);
3306 });
3307 }
3308
3309 fn find_possible_emoji_shortcode_at_position(
3310 snapshot: &MultiBufferSnapshot,
3311 position: Point,
3312 ) -> Option<String> {
3313 let mut chars = Vec::new();
3314 let mut found_colon = false;
3315 for char in snapshot.reversed_chars_at(position).take(100) {
3316 // Found a possible emoji shortcode in the middle of the buffer
3317 if found_colon {
3318 if char.is_whitespace() {
3319 chars.reverse();
3320 return Some(chars.iter().collect());
3321 }
3322 // If the previous character is not a whitespace, we are in the middle of a word
3323 // and we only want to complete the shortcode if the word is made up of other emojis
3324 let mut containing_word = String::new();
3325 for ch in snapshot
3326 .reversed_chars_at(position)
3327 .skip(chars.len() + 1)
3328 .take(100)
3329 {
3330 if ch.is_whitespace() {
3331 break;
3332 }
3333 containing_word.push(ch);
3334 }
3335 let containing_word = containing_word.chars().rev().collect::<String>();
3336 if util::word_consists_of_emojis(containing_word.as_str()) {
3337 chars.reverse();
3338 return Some(chars.iter().collect());
3339 }
3340 }
3341
3342 if char.is_whitespace() || !char.is_ascii() {
3343 return None;
3344 }
3345 if char == ':' {
3346 found_colon = true;
3347 } else {
3348 chars.push(char);
3349 }
3350 }
3351 // Found a possible emoji shortcode at the beginning of the buffer
3352 chars.reverse();
3353 Some(chars.iter().collect())
3354 }
3355
3356 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
3357 self.transact(cx, |this, cx| {
3358 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3359 let selections = this.selections.all::<usize>(cx);
3360 let multi_buffer = this.buffer.read(cx);
3361 let buffer = multi_buffer.snapshot(cx);
3362 selections
3363 .iter()
3364 .map(|selection| {
3365 let start_point = selection.start.to_point(&buffer);
3366 let mut indent =
3367 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3368 indent.len = cmp::min(indent.len, start_point.column);
3369 let start = selection.start;
3370 let end = selection.end;
3371 let selection_is_empty = start == end;
3372 let language_scope = buffer.language_scope_at(start);
3373 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3374 &language_scope
3375 {
3376 let leading_whitespace_len = buffer
3377 .reversed_chars_at(start)
3378 .take_while(|c| c.is_whitespace() && *c != '\n')
3379 .map(|c| c.len_utf8())
3380 .sum::<usize>();
3381
3382 let trailing_whitespace_len = buffer
3383 .chars_at(end)
3384 .take_while(|c| c.is_whitespace() && *c != '\n')
3385 .map(|c| c.len_utf8())
3386 .sum::<usize>();
3387
3388 let insert_extra_newline =
3389 language.brackets().any(|(pair, enabled)| {
3390 let pair_start = pair.start.trim_end();
3391 let pair_end = pair.end.trim_start();
3392
3393 enabled
3394 && pair.newline
3395 && buffer.contains_str_at(
3396 end + trailing_whitespace_len,
3397 pair_end,
3398 )
3399 && buffer.contains_str_at(
3400 (start - leading_whitespace_len)
3401 .saturating_sub(pair_start.len()),
3402 pair_start,
3403 )
3404 });
3405
3406 // Comment extension on newline is allowed only for cursor selections
3407 let comment_delimiter = maybe!({
3408 if !selection_is_empty {
3409 return None;
3410 }
3411
3412 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
3413 return None;
3414 }
3415
3416 let delimiters = language.line_comment_prefixes();
3417 let max_len_of_delimiter =
3418 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3419 let (snapshot, range) =
3420 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3421
3422 let mut index_of_first_non_whitespace = 0;
3423 let comment_candidate = snapshot
3424 .chars_for_range(range)
3425 .skip_while(|c| {
3426 let should_skip = c.is_whitespace();
3427 if should_skip {
3428 index_of_first_non_whitespace += 1;
3429 }
3430 should_skip
3431 })
3432 .take(max_len_of_delimiter)
3433 .collect::<String>();
3434 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3435 comment_candidate.starts_with(comment_prefix.as_ref())
3436 })?;
3437 let cursor_is_placed_after_comment_marker =
3438 index_of_first_non_whitespace + comment_prefix.len()
3439 <= start_point.column as usize;
3440 if cursor_is_placed_after_comment_marker {
3441 Some(comment_prefix.clone())
3442 } else {
3443 None
3444 }
3445 });
3446 (comment_delimiter, insert_extra_newline)
3447 } else {
3448 (None, false)
3449 };
3450
3451 let capacity_for_delimiter = comment_delimiter
3452 .as_deref()
3453 .map(str::len)
3454 .unwrap_or_default();
3455 let mut new_text =
3456 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3457 new_text.push_str("\n");
3458 new_text.extend(indent.chars());
3459 if let Some(delimiter) = &comment_delimiter {
3460 new_text.push_str(&delimiter);
3461 }
3462 if insert_extra_newline {
3463 new_text = new_text.repeat(2);
3464 }
3465
3466 let anchor = buffer.anchor_after(end);
3467 let new_selection = selection.map(|_| anchor);
3468 (
3469 (start..end, new_text),
3470 (insert_extra_newline, new_selection),
3471 )
3472 })
3473 .unzip()
3474 };
3475
3476 this.edit_with_autoindent(edits, cx);
3477 let buffer = this.buffer.read(cx).snapshot(cx);
3478 let new_selections = selection_fixup_info
3479 .into_iter()
3480 .map(|(extra_newline_inserted, new_selection)| {
3481 let mut cursor = new_selection.end.to_point(&buffer);
3482 if extra_newline_inserted {
3483 cursor.row -= 1;
3484 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3485 }
3486 new_selection.map(|_| cursor)
3487 })
3488 .collect();
3489
3490 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
3491 this.refresh_inline_completion(true, cx);
3492 });
3493 }
3494
3495 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
3496 let buffer = self.buffer.read(cx);
3497 let snapshot = buffer.snapshot(cx);
3498
3499 let mut edits = Vec::new();
3500 let mut rows = Vec::new();
3501
3502 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3503 let cursor = selection.head();
3504 let row = cursor.row;
3505
3506 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3507
3508 let newline = "\n".to_string();
3509 edits.push((start_of_line..start_of_line, newline));
3510
3511 rows.push(row + rows_inserted as u32);
3512 }
3513
3514 self.transact(cx, |editor, cx| {
3515 editor.edit(edits, cx);
3516
3517 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3518 let mut index = 0;
3519 s.move_cursors_with(|map, _, _| {
3520 let row = rows[index];
3521 index += 1;
3522
3523 let point = Point::new(row, 0);
3524 let boundary = map.next_line_boundary(point).1;
3525 let clipped = map.clip_point(boundary, Bias::Left);
3526
3527 (clipped, SelectionGoal::None)
3528 });
3529 });
3530
3531 let mut indent_edits = Vec::new();
3532 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3533 for row in rows {
3534 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3535 for (row, indent) in indents {
3536 if indent.len == 0 {
3537 continue;
3538 }
3539
3540 let text = match indent.kind {
3541 IndentKind::Space => " ".repeat(indent.len as usize),
3542 IndentKind::Tab => "\t".repeat(indent.len as usize),
3543 };
3544 let point = Point::new(row.0, 0);
3545 indent_edits.push((point..point, text));
3546 }
3547 }
3548 editor.edit(indent_edits, cx);
3549 });
3550 }
3551
3552 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
3553 let buffer = self.buffer.read(cx);
3554 let snapshot = buffer.snapshot(cx);
3555
3556 let mut edits = Vec::new();
3557 let mut rows = Vec::new();
3558 let mut rows_inserted = 0;
3559
3560 for selection in self.selections.all_adjusted(cx) {
3561 let cursor = selection.head();
3562 let row = cursor.row;
3563
3564 let point = Point::new(row + 1, 0);
3565 let start_of_line = snapshot.clip_point(point, Bias::Left);
3566
3567 let newline = "\n".to_string();
3568 edits.push((start_of_line..start_of_line, newline));
3569
3570 rows_inserted += 1;
3571 rows.push(row + rows_inserted);
3572 }
3573
3574 self.transact(cx, |editor, cx| {
3575 editor.edit(edits, cx);
3576
3577 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3578 let mut index = 0;
3579 s.move_cursors_with(|map, _, _| {
3580 let row = rows[index];
3581 index += 1;
3582
3583 let point = Point::new(row, 0);
3584 let boundary = map.next_line_boundary(point).1;
3585 let clipped = map.clip_point(boundary, Bias::Left);
3586
3587 (clipped, SelectionGoal::None)
3588 });
3589 });
3590
3591 let mut indent_edits = Vec::new();
3592 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3593 for row in rows {
3594 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3595 for (row, indent) in indents {
3596 if indent.len == 0 {
3597 continue;
3598 }
3599
3600 let text = match indent.kind {
3601 IndentKind::Space => " ".repeat(indent.len as usize),
3602 IndentKind::Tab => "\t".repeat(indent.len as usize),
3603 };
3604 let point = Point::new(row.0, 0);
3605 indent_edits.push((point..point, text));
3606 }
3607 }
3608 editor.edit(indent_edits, cx);
3609 });
3610 }
3611
3612 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3613 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3614 original_indent_columns: Vec::new(),
3615 });
3616 self.insert_with_autoindent_mode(text, autoindent, cx);
3617 }
3618
3619 fn insert_with_autoindent_mode(
3620 &mut self,
3621 text: &str,
3622 autoindent_mode: Option<AutoindentMode>,
3623 cx: &mut ViewContext<Self>,
3624 ) {
3625 if self.read_only(cx) {
3626 return;
3627 }
3628
3629 let text: Arc<str> = text.into();
3630 self.transact(cx, |this, cx| {
3631 let old_selections = this.selections.all_adjusted(cx);
3632 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3633 let anchors = {
3634 let snapshot = buffer.read(cx);
3635 old_selections
3636 .iter()
3637 .map(|s| {
3638 let anchor = snapshot.anchor_after(s.head());
3639 s.map(|_| anchor)
3640 })
3641 .collect::<Vec<_>>()
3642 };
3643 buffer.edit(
3644 old_selections
3645 .iter()
3646 .map(|s| (s.start..s.end, text.clone())),
3647 autoindent_mode,
3648 cx,
3649 );
3650 anchors
3651 });
3652
3653 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3654 s.select_anchors(selection_anchors);
3655 })
3656 });
3657 }
3658
3659 fn trigger_completion_on_input(
3660 &mut self,
3661 text: &str,
3662 trigger_in_words: bool,
3663 cx: &mut ViewContext<Self>,
3664 ) {
3665 if self.is_completion_trigger(text, trigger_in_words, cx) {
3666 self.show_completions(
3667 &ShowCompletions {
3668 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3669 },
3670 cx,
3671 );
3672 } else {
3673 self.hide_context_menu(cx);
3674 }
3675 }
3676
3677 fn is_completion_trigger(
3678 &self,
3679 text: &str,
3680 trigger_in_words: bool,
3681 cx: &mut ViewContext<Self>,
3682 ) -> bool {
3683 let position = self.selections.newest_anchor().head();
3684 let multibuffer = self.buffer.read(cx);
3685 let Some(buffer) = position
3686 .buffer_id
3687 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3688 else {
3689 return false;
3690 };
3691
3692 if let Some(completion_provider) = &self.completion_provider {
3693 completion_provider.is_completion_trigger(
3694 &buffer,
3695 position.text_anchor,
3696 text,
3697 trigger_in_words,
3698 cx,
3699 )
3700 } else {
3701 false
3702 }
3703 }
3704
3705 /// If any empty selections is touching the start of its innermost containing autoclose
3706 /// region, expand it to select the brackets.
3707 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
3708 let selections = self.selections.all::<usize>(cx);
3709 let buffer = self.buffer.read(cx).read(cx);
3710 let new_selections = self
3711 .selections_with_autoclose_regions(selections, &buffer)
3712 .map(|(mut selection, region)| {
3713 if !selection.is_empty() {
3714 return selection;
3715 }
3716
3717 if let Some(region) = region {
3718 let mut range = region.range.to_offset(&buffer);
3719 if selection.start == range.start && range.start >= region.pair.start.len() {
3720 range.start -= region.pair.start.len();
3721 if buffer.contains_str_at(range.start, ®ion.pair.start)
3722 && buffer.contains_str_at(range.end, ®ion.pair.end)
3723 {
3724 range.end += region.pair.end.len();
3725 selection.start = range.start;
3726 selection.end = range.end;
3727
3728 return selection;
3729 }
3730 }
3731 }
3732
3733 let always_treat_brackets_as_autoclosed = buffer
3734 .settings_at(selection.start, cx)
3735 .always_treat_brackets_as_autoclosed;
3736
3737 if !always_treat_brackets_as_autoclosed {
3738 return selection;
3739 }
3740
3741 if let Some(scope) = buffer.language_scope_at(selection.start) {
3742 for (pair, enabled) in scope.brackets() {
3743 if !enabled || !pair.close {
3744 continue;
3745 }
3746
3747 if buffer.contains_str_at(selection.start, &pair.end) {
3748 let pair_start_len = pair.start.len();
3749 if buffer.contains_str_at(selection.start - pair_start_len, &pair.start)
3750 {
3751 selection.start -= pair_start_len;
3752 selection.end += pair.end.len();
3753
3754 return selection;
3755 }
3756 }
3757 }
3758 }
3759
3760 selection
3761 })
3762 .collect();
3763
3764 drop(buffer);
3765 self.change_selections(None, cx, |selections| selections.select(new_selections));
3766 }
3767
3768 /// Iterate the given selections, and for each one, find the smallest surrounding
3769 /// autoclose region. This uses the ordering of the selections and the autoclose
3770 /// regions to avoid repeated comparisons.
3771 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3772 &'a self,
3773 selections: impl IntoIterator<Item = Selection<D>>,
3774 buffer: &'a MultiBufferSnapshot,
3775 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3776 let mut i = 0;
3777 let mut regions = self.autoclose_regions.as_slice();
3778 selections.into_iter().map(move |selection| {
3779 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3780
3781 let mut enclosing = None;
3782 while let Some(pair_state) = regions.get(i) {
3783 if pair_state.range.end.to_offset(buffer) < range.start {
3784 regions = ®ions[i + 1..];
3785 i = 0;
3786 } else if pair_state.range.start.to_offset(buffer) > range.end {
3787 break;
3788 } else {
3789 if pair_state.selection_id == selection.id {
3790 enclosing = Some(pair_state);
3791 }
3792 i += 1;
3793 }
3794 }
3795
3796 (selection.clone(), enclosing)
3797 })
3798 }
3799
3800 /// Remove any autoclose regions that no longer contain their selection.
3801 fn invalidate_autoclose_regions(
3802 &mut self,
3803 mut selections: &[Selection<Anchor>],
3804 buffer: &MultiBufferSnapshot,
3805 ) {
3806 self.autoclose_regions.retain(|state| {
3807 let mut i = 0;
3808 while let Some(selection) = selections.get(i) {
3809 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3810 selections = &selections[1..];
3811 continue;
3812 }
3813 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3814 break;
3815 }
3816 if selection.id == state.selection_id {
3817 return true;
3818 } else {
3819 i += 1;
3820 }
3821 }
3822 false
3823 });
3824 }
3825
3826 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3827 let offset = position.to_offset(buffer);
3828 let (word_range, kind) = buffer.surrounding_word(offset);
3829 if offset > word_range.start && kind == Some(CharKind::Word) {
3830 Some(
3831 buffer
3832 .text_for_range(word_range.start..offset)
3833 .collect::<String>(),
3834 )
3835 } else {
3836 None
3837 }
3838 }
3839
3840 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
3841 self.refresh_inlay_hints(
3842 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
3843 cx,
3844 );
3845 }
3846
3847 pub fn inlay_hints_enabled(&self) -> bool {
3848 self.inlay_hint_cache.enabled
3849 }
3850
3851 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
3852 if self.project.is_none() || self.mode != EditorMode::Full {
3853 return;
3854 }
3855
3856 let reason_description = reason.description();
3857 let ignore_debounce = matches!(
3858 reason,
3859 InlayHintRefreshReason::SettingsChange(_)
3860 | InlayHintRefreshReason::Toggle(_)
3861 | InlayHintRefreshReason::ExcerptsRemoved(_)
3862 );
3863 let (invalidate_cache, required_languages) = match reason {
3864 InlayHintRefreshReason::Toggle(enabled) => {
3865 self.inlay_hint_cache.enabled = enabled;
3866 if enabled {
3867 (InvalidationStrategy::RefreshRequested, None)
3868 } else {
3869 self.inlay_hint_cache.clear();
3870 self.splice_inlays(
3871 self.visible_inlay_hints(cx)
3872 .iter()
3873 .map(|inlay| inlay.id)
3874 .collect(),
3875 Vec::new(),
3876 cx,
3877 );
3878 return;
3879 }
3880 }
3881 InlayHintRefreshReason::SettingsChange(new_settings) => {
3882 match self.inlay_hint_cache.update_settings(
3883 &self.buffer,
3884 new_settings,
3885 self.visible_inlay_hints(cx),
3886 cx,
3887 ) {
3888 ControlFlow::Break(Some(InlaySplice {
3889 to_remove,
3890 to_insert,
3891 })) => {
3892 self.splice_inlays(to_remove, to_insert, cx);
3893 return;
3894 }
3895 ControlFlow::Break(None) => return,
3896 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3897 }
3898 }
3899 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3900 if let Some(InlaySplice {
3901 to_remove,
3902 to_insert,
3903 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3904 {
3905 self.splice_inlays(to_remove, to_insert, cx);
3906 }
3907 return;
3908 }
3909 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3910 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3911 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3912 }
3913 InlayHintRefreshReason::RefreshRequested => {
3914 (InvalidationStrategy::RefreshRequested, None)
3915 }
3916 };
3917
3918 if let Some(InlaySplice {
3919 to_remove,
3920 to_insert,
3921 }) = self.inlay_hint_cache.spawn_hint_refresh(
3922 reason_description,
3923 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3924 invalidate_cache,
3925 ignore_debounce,
3926 cx,
3927 ) {
3928 self.splice_inlays(to_remove, to_insert, cx);
3929 }
3930 }
3931
3932 fn visible_inlay_hints(&self, cx: &ViewContext<'_, Editor>) -> Vec<Inlay> {
3933 self.display_map
3934 .read(cx)
3935 .current_inlays()
3936 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3937 .cloned()
3938 .collect()
3939 }
3940
3941 pub fn excerpts_for_inlay_hints_query(
3942 &self,
3943 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3944 cx: &mut ViewContext<Editor>,
3945 ) -> HashMap<ExcerptId, (Model<Buffer>, clock::Global, Range<usize>)> {
3946 let Some(project) = self.project.as_ref() else {
3947 return HashMap::default();
3948 };
3949 let project = project.read(cx);
3950 let multi_buffer = self.buffer().read(cx);
3951 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3952 let multi_buffer_visible_start = self
3953 .scroll_manager
3954 .anchor()
3955 .anchor
3956 .to_point(&multi_buffer_snapshot);
3957 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3958 multi_buffer_visible_start
3959 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3960 Bias::Left,
3961 );
3962 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3963 multi_buffer
3964 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
3965 .into_iter()
3966 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3967 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
3968 let buffer = buffer_handle.read(cx);
3969 let buffer_file = project::File::from_dyn(buffer.file())?;
3970 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3971 let worktree_entry = buffer_worktree
3972 .read(cx)
3973 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3974 if worktree_entry.is_ignored {
3975 return None;
3976 }
3977
3978 let language = buffer.language()?;
3979 if let Some(restrict_to_languages) = restrict_to_languages {
3980 if !restrict_to_languages.contains(language) {
3981 return None;
3982 }
3983 }
3984 Some((
3985 excerpt_id,
3986 (
3987 buffer_handle,
3988 buffer.version().clone(),
3989 excerpt_visible_range,
3990 ),
3991 ))
3992 })
3993 .collect()
3994 }
3995
3996 pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
3997 TextLayoutDetails {
3998 text_system: cx.text_system().clone(),
3999 editor_style: self.style.clone().unwrap(),
4000 rem_size: cx.rem_size(),
4001 scroll_anchor: self.scroll_manager.anchor(),
4002 visible_rows: self.visible_line_count(),
4003 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
4004 }
4005 }
4006
4007 fn splice_inlays(
4008 &self,
4009 to_remove: Vec<InlayId>,
4010 to_insert: Vec<Inlay>,
4011 cx: &mut ViewContext<Self>,
4012 ) {
4013 self.display_map.update(cx, |display_map, cx| {
4014 display_map.splice_inlays(to_remove, to_insert, cx);
4015 });
4016 cx.notify();
4017 }
4018
4019 fn trigger_on_type_formatting(
4020 &self,
4021 input: String,
4022 cx: &mut ViewContext<Self>,
4023 ) -> Option<Task<Result<()>>> {
4024 if input.len() != 1 {
4025 return None;
4026 }
4027
4028 let project = self.project.as_ref()?;
4029 let position = self.selections.newest_anchor().head();
4030 let (buffer, buffer_position) = self
4031 .buffer
4032 .read(cx)
4033 .text_anchor_for_position(position, cx)?;
4034
4035 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
4036 // hence we do LSP request & edit on host side only — add formats to host's history.
4037 let push_to_lsp_host_history = true;
4038 // If this is not the host, append its history with new edits.
4039 let push_to_client_history = project.read(cx).is_remote();
4040
4041 let on_type_formatting = project.update(cx, |project, cx| {
4042 project.on_type_format(
4043 buffer.clone(),
4044 buffer_position,
4045 input,
4046 push_to_lsp_host_history,
4047 cx,
4048 )
4049 });
4050 Some(cx.spawn(|editor, mut cx| async move {
4051 if let Some(transaction) = on_type_formatting.await? {
4052 if push_to_client_history {
4053 buffer
4054 .update(&mut cx, |buffer, _| {
4055 buffer.push_transaction(transaction, Instant::now());
4056 })
4057 .ok();
4058 }
4059 editor.update(&mut cx, |editor, cx| {
4060 editor.refresh_document_highlights(cx);
4061 })?;
4062 }
4063 Ok(())
4064 }))
4065 }
4066
4067 pub fn show_completions(&mut self, options: &ShowCompletions, cx: &mut ViewContext<Self>) {
4068 if self.pending_rename.is_some() {
4069 return;
4070 }
4071
4072 let Some(provider) = self.completion_provider.as_ref() else {
4073 return;
4074 };
4075
4076 let position = self.selections.newest_anchor().head();
4077 let (buffer, buffer_position) =
4078 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
4079 output
4080 } else {
4081 return;
4082 };
4083
4084 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
4085 let is_followup_invoke = {
4086 let context_menu_state = self.context_menu.read();
4087 matches!(
4088 context_menu_state.deref(),
4089 Some(ContextMenu::Completions(_))
4090 )
4091 };
4092 let trigger_kind = match (&options.trigger, is_followup_invoke) {
4093 (_, true) => CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS,
4094 (Some(trigger), _) if buffer.read(cx).completion_triggers().contains(&trigger) => {
4095 CompletionTriggerKind::TRIGGER_CHARACTER
4096 }
4097
4098 _ => CompletionTriggerKind::INVOKED,
4099 };
4100 let completion_context = CompletionContext {
4101 trigger_character: options.trigger.as_ref().and_then(|trigger| {
4102 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
4103 Some(String::from(trigger))
4104 } else {
4105 None
4106 }
4107 }),
4108 trigger_kind,
4109 };
4110 let completions = provider.completions(&buffer, buffer_position, completion_context, cx);
4111 let sort_completions = provider.sort_completions();
4112
4113 let id = post_inc(&mut self.next_completion_id);
4114 let task = cx.spawn(|this, mut cx| {
4115 async move {
4116 this.update(&mut cx, |this, _| {
4117 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
4118 })?;
4119 let completions = completions.await.log_err();
4120 let menu = if let Some(completions) = completions {
4121 let mut menu = CompletionsMenu {
4122 id,
4123 sort_completions,
4124 initial_position: position,
4125 match_candidates: completions
4126 .iter()
4127 .enumerate()
4128 .map(|(id, completion)| {
4129 StringMatchCandidate::new(
4130 id,
4131 completion.label.text[completion.label.filter_range.clone()]
4132 .into(),
4133 )
4134 })
4135 .collect(),
4136 buffer: buffer.clone(),
4137 completions: Arc::new(RwLock::new(completions.into())),
4138 matches: Vec::new().into(),
4139 selected_item: 0,
4140 scroll_handle: UniformListScrollHandle::new(),
4141 selected_completion_documentation_resolve_debounce: Arc::new(Mutex::new(
4142 DebouncedDelay::new(),
4143 )),
4144 };
4145 menu.filter(query.as_deref(), cx.background_executor().clone())
4146 .await;
4147
4148 if menu.matches.is_empty() {
4149 None
4150 } else {
4151 this.update(&mut cx, |editor, cx| {
4152 let completions = menu.completions.clone();
4153 let matches = menu.matches.clone();
4154
4155 let delay_ms = EditorSettings::get_global(cx)
4156 .completion_documentation_secondary_query_debounce;
4157 let delay = Duration::from_millis(delay_ms);
4158 editor
4159 .completion_documentation_pre_resolve_debounce
4160 .fire_new(delay, cx, |editor, cx| {
4161 CompletionsMenu::pre_resolve_completion_documentation(
4162 buffer,
4163 completions,
4164 matches,
4165 editor,
4166 cx,
4167 )
4168 });
4169 })
4170 .ok();
4171 Some(menu)
4172 }
4173 } else {
4174 None
4175 };
4176
4177 this.update(&mut cx, |this, cx| {
4178 let mut context_menu = this.context_menu.write();
4179 match context_menu.as_ref() {
4180 None => {}
4181
4182 Some(ContextMenu::Completions(prev_menu)) => {
4183 if prev_menu.id > id {
4184 return;
4185 }
4186 }
4187
4188 _ => return,
4189 }
4190
4191 if this.focus_handle.is_focused(cx) && menu.is_some() {
4192 let menu = menu.unwrap();
4193 *context_menu = Some(ContextMenu::Completions(menu));
4194 drop(context_menu);
4195 this.discard_inline_completion(false, cx);
4196 cx.notify();
4197 } else if this.completion_tasks.len() <= 1 {
4198 // If there are no more completion tasks and the last menu was
4199 // empty, we should hide it. If it was already hidden, we should
4200 // also show the copilot completion when available.
4201 drop(context_menu);
4202 if this.hide_context_menu(cx).is_none() {
4203 this.update_visible_inline_completion(cx);
4204 }
4205 }
4206 })?;
4207
4208 Ok::<_, anyhow::Error>(())
4209 }
4210 .log_err()
4211 });
4212
4213 self.completion_tasks.push((id, task));
4214 }
4215
4216 pub fn confirm_completion(
4217 &mut self,
4218 action: &ConfirmCompletion,
4219 cx: &mut ViewContext<Self>,
4220 ) -> Option<Task<Result<()>>> {
4221 self.do_completion(action.item_ix, CompletionIntent::Complete, cx)
4222 }
4223
4224 pub fn compose_completion(
4225 &mut self,
4226 action: &ComposeCompletion,
4227 cx: &mut ViewContext<Self>,
4228 ) -> Option<Task<Result<()>>> {
4229 self.do_completion(action.item_ix, CompletionIntent::Compose, cx)
4230 }
4231
4232 fn do_completion(
4233 &mut self,
4234 item_ix: Option<usize>,
4235 intent: CompletionIntent,
4236 cx: &mut ViewContext<Editor>,
4237 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
4238 use language::ToOffset as _;
4239
4240 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
4241 menu
4242 } else {
4243 return None;
4244 };
4245
4246 let mat = completions_menu
4247 .matches
4248 .get(item_ix.unwrap_or(completions_menu.selected_item))?;
4249 let buffer_handle = completions_menu.buffer;
4250 let completions = completions_menu.completions.read();
4251 let completion = completions.get(mat.candidate_id)?;
4252 cx.stop_propagation();
4253
4254 let snippet;
4255 let text;
4256
4257 if completion.is_snippet() {
4258 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4259 text = snippet.as_ref().unwrap().text.clone();
4260 } else {
4261 snippet = None;
4262 text = completion.new_text.clone();
4263 };
4264 let selections = self.selections.all::<usize>(cx);
4265 let buffer = buffer_handle.read(cx);
4266 let old_range = completion.old_range.to_offset(buffer);
4267 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4268
4269 let newest_selection = self.selections.newest_anchor();
4270 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4271 return None;
4272 }
4273
4274 let lookbehind = newest_selection
4275 .start
4276 .text_anchor
4277 .to_offset(buffer)
4278 .saturating_sub(old_range.start);
4279 let lookahead = old_range
4280 .end
4281 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4282 let mut common_prefix_len = old_text
4283 .bytes()
4284 .zip(text.bytes())
4285 .take_while(|(a, b)| a == b)
4286 .count();
4287
4288 let snapshot = self.buffer.read(cx).snapshot(cx);
4289 let mut range_to_replace: Option<Range<isize>> = None;
4290 let mut ranges = Vec::new();
4291 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4292 for selection in &selections {
4293 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4294 let start = selection.start.saturating_sub(lookbehind);
4295 let end = selection.end + lookahead;
4296 if selection.id == newest_selection.id {
4297 range_to_replace = Some(
4298 ((start + common_prefix_len) as isize - selection.start as isize)
4299 ..(end as isize - selection.start as isize),
4300 );
4301 }
4302 ranges.push(start + common_prefix_len..end);
4303 } else {
4304 common_prefix_len = 0;
4305 ranges.clear();
4306 ranges.extend(selections.iter().map(|s| {
4307 if s.id == newest_selection.id {
4308 range_to_replace = Some(
4309 old_range.start.to_offset_utf16(&snapshot).0 as isize
4310 - selection.start as isize
4311 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4312 - selection.start as isize,
4313 );
4314 old_range.clone()
4315 } else {
4316 s.start..s.end
4317 }
4318 }));
4319 break;
4320 }
4321 if !self.linked_edit_ranges.is_empty() {
4322 let start_anchor = snapshot.anchor_before(selection.head());
4323 let end_anchor = snapshot.anchor_after(selection.tail());
4324 if let Some(ranges) = self
4325 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4326 {
4327 for (buffer, edits) in ranges {
4328 linked_edits.entry(buffer.clone()).or_default().extend(
4329 edits
4330 .into_iter()
4331 .map(|range| (range, text[common_prefix_len..].to_owned())),
4332 );
4333 }
4334 }
4335 }
4336 }
4337 let text = &text[common_prefix_len..];
4338
4339 cx.emit(EditorEvent::InputHandled {
4340 utf16_range_to_replace: range_to_replace,
4341 text: text.into(),
4342 });
4343
4344 self.transact(cx, |this, cx| {
4345 if let Some(mut snippet) = snippet {
4346 snippet.text = text.to_string();
4347 for tabstop in snippet.tabstops.iter_mut().flatten() {
4348 tabstop.start -= common_prefix_len as isize;
4349 tabstop.end -= common_prefix_len as isize;
4350 }
4351
4352 this.insert_snippet(&ranges, snippet, cx).log_err();
4353 } else {
4354 this.buffer.update(cx, |buffer, cx| {
4355 buffer.edit(
4356 ranges.iter().map(|range| (range.clone(), text)),
4357 this.autoindent_mode.clone(),
4358 cx,
4359 );
4360 });
4361 }
4362 for (buffer, edits) in linked_edits {
4363 buffer.update(cx, |buffer, cx| {
4364 let snapshot = buffer.snapshot();
4365 let edits = edits
4366 .into_iter()
4367 .map(|(range, text)| {
4368 use text::ToPoint as TP;
4369 let end_point = TP::to_point(&range.end, &snapshot);
4370 let start_point = TP::to_point(&range.start, &snapshot);
4371 (start_point..end_point, text)
4372 })
4373 .sorted_by_key(|(range, _)| range.start)
4374 .collect::<Vec<_>>();
4375 buffer.edit(edits, None, cx);
4376 })
4377 }
4378
4379 this.refresh_inline_completion(true, cx);
4380 });
4381
4382 let show_new_completions_on_confirm = completion
4383 .confirm
4384 .as_ref()
4385 .map_or(false, |confirm| confirm(intent, cx));
4386 if show_new_completions_on_confirm {
4387 self.show_completions(&ShowCompletions { trigger: None }, cx);
4388 }
4389
4390 let provider = self.completion_provider.as_ref()?;
4391 let apply_edits = provider.apply_additional_edits_for_completion(
4392 buffer_handle,
4393 completion.clone(),
4394 true,
4395 cx,
4396 );
4397
4398 let editor_settings = EditorSettings::get_global(cx);
4399 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4400 // After the code completion is finished, users often want to know what signatures are needed.
4401 // so we should automatically call signature_help
4402 self.show_signature_help(&ShowSignatureHelp, cx);
4403 }
4404
4405 Some(cx.foreground_executor().spawn(async move {
4406 apply_edits.await?;
4407 Ok(())
4408 }))
4409 }
4410
4411 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
4412 let mut context_menu = self.context_menu.write();
4413 if let Some(ContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4414 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4415 // Toggle if we're selecting the same one
4416 *context_menu = None;
4417 cx.notify();
4418 return;
4419 } else {
4420 // Otherwise, clear it and start a new one
4421 *context_menu = None;
4422 cx.notify();
4423 }
4424 }
4425 drop(context_menu);
4426 let snapshot = self.snapshot(cx);
4427 let deployed_from_indicator = action.deployed_from_indicator;
4428 let mut task = self.code_actions_task.take();
4429 let action = action.clone();
4430 cx.spawn(|editor, mut cx| async move {
4431 while let Some(prev_task) = task {
4432 prev_task.await;
4433 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4434 }
4435
4436 let spawned_test_task = editor.update(&mut cx, |editor, cx| {
4437 if editor.focus_handle.is_focused(cx) {
4438 let multibuffer_point = action
4439 .deployed_from_indicator
4440 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4441 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4442 let (buffer, buffer_row) = snapshot
4443 .buffer_snapshot
4444 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4445 .and_then(|(buffer_snapshot, range)| {
4446 editor
4447 .buffer
4448 .read(cx)
4449 .buffer(buffer_snapshot.remote_id())
4450 .map(|buffer| (buffer, range.start.row))
4451 })?;
4452 let (_, code_actions) = editor
4453 .available_code_actions
4454 .clone()
4455 .and_then(|(location, code_actions)| {
4456 let snapshot = location.buffer.read(cx).snapshot();
4457 let point_range = location.range.to_point(&snapshot);
4458 let point_range = point_range.start.row..=point_range.end.row;
4459 if point_range.contains(&buffer_row) {
4460 Some((location, code_actions))
4461 } else {
4462 None
4463 }
4464 })
4465 .unzip();
4466 let buffer_id = buffer.read(cx).remote_id();
4467 let tasks = editor
4468 .tasks
4469 .get(&(buffer_id, buffer_row))
4470 .map(|t| Arc::new(t.to_owned()));
4471 if tasks.is_none() && code_actions.is_none() {
4472 return None;
4473 }
4474
4475 editor.completion_tasks.clear();
4476 editor.discard_inline_completion(false, cx);
4477 let task_context =
4478 tasks
4479 .as_ref()
4480 .zip(editor.project.clone())
4481 .map(|(tasks, project)| {
4482 let position = Point::new(buffer_row, tasks.column);
4483 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
4484 let location = Location {
4485 buffer: buffer.clone(),
4486 range: range_start..range_start,
4487 };
4488 // Fill in the environmental variables from the tree-sitter captures
4489 let mut captured_task_variables = TaskVariables::default();
4490 for (capture_name, value) in tasks.extra_variables.clone() {
4491 captured_task_variables.insert(
4492 task::VariableName::Custom(capture_name.into()),
4493 value.clone(),
4494 );
4495 }
4496 project.update(cx, |project, cx| {
4497 project.task_context_for_location(
4498 captured_task_variables,
4499 location,
4500 cx,
4501 )
4502 })
4503 });
4504
4505 Some(cx.spawn(|editor, mut cx| async move {
4506 let task_context = match task_context {
4507 Some(task_context) => task_context.await,
4508 None => None,
4509 };
4510 let resolved_tasks =
4511 tasks.zip(task_context).map(|(tasks, task_context)| {
4512 Arc::new(ResolvedTasks {
4513 templates: tasks
4514 .templates
4515 .iter()
4516 .filter_map(|(kind, template)| {
4517 template
4518 .resolve_task(&kind.to_id_base(), &task_context)
4519 .map(|task| (kind.clone(), task))
4520 })
4521 .collect(),
4522 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4523 multibuffer_point.row,
4524 tasks.column,
4525 )),
4526 })
4527 });
4528 let spawn_straight_away = resolved_tasks
4529 .as_ref()
4530 .map_or(false, |tasks| tasks.templates.len() == 1)
4531 && code_actions
4532 .as_ref()
4533 .map_or(true, |actions| actions.is_empty());
4534 if let Some(task) = editor
4535 .update(&mut cx, |editor, cx| {
4536 *editor.context_menu.write() =
4537 Some(ContextMenu::CodeActions(CodeActionsMenu {
4538 buffer,
4539 actions: CodeActionContents {
4540 tasks: resolved_tasks,
4541 actions: code_actions,
4542 },
4543 selected_item: Default::default(),
4544 scroll_handle: UniformListScrollHandle::default(),
4545 deployed_from_indicator,
4546 }));
4547 if spawn_straight_away {
4548 if let Some(task) = editor.confirm_code_action(
4549 &ConfirmCodeAction { item_ix: Some(0) },
4550 cx,
4551 ) {
4552 cx.notify();
4553 return task;
4554 }
4555 }
4556 cx.notify();
4557 Task::ready(Ok(()))
4558 })
4559 .ok()
4560 {
4561 task.await
4562 } else {
4563 Ok(())
4564 }
4565 }))
4566 } else {
4567 Some(Task::ready(Ok(())))
4568 }
4569 })?;
4570 if let Some(task) = spawned_test_task {
4571 task.await?;
4572 }
4573
4574 Ok::<_, anyhow::Error>(())
4575 })
4576 .detach_and_log_err(cx);
4577 }
4578
4579 pub fn confirm_code_action(
4580 &mut self,
4581 action: &ConfirmCodeAction,
4582 cx: &mut ViewContext<Self>,
4583 ) -> Option<Task<Result<()>>> {
4584 let actions_menu = if let ContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
4585 menu
4586 } else {
4587 return None;
4588 };
4589 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4590 let action = actions_menu.actions.get(action_ix)?;
4591 let title = action.label();
4592 let buffer = actions_menu.buffer;
4593 let workspace = self.workspace()?;
4594
4595 match action {
4596 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4597 workspace.update(cx, |workspace, cx| {
4598 workspace::tasks::schedule_resolved_task(
4599 workspace,
4600 task_source_kind,
4601 resolved_task,
4602 false,
4603 cx,
4604 );
4605
4606 Some(Task::ready(Ok(())))
4607 })
4608 }
4609 CodeActionsItem::CodeAction(action) => {
4610 let apply_code_actions = workspace
4611 .read(cx)
4612 .project()
4613 .clone()
4614 .update(cx, |project, cx| {
4615 project.apply_code_action(buffer, action, true, cx)
4616 });
4617 let workspace = workspace.downgrade();
4618 Some(cx.spawn(|editor, cx| async move {
4619 let project_transaction = apply_code_actions.await?;
4620 Self::open_project_transaction(
4621 &editor,
4622 workspace,
4623 project_transaction,
4624 title,
4625 cx,
4626 )
4627 .await
4628 }))
4629 }
4630 }
4631 }
4632
4633 pub async fn open_project_transaction(
4634 this: &WeakView<Editor>,
4635 workspace: WeakView<Workspace>,
4636 transaction: ProjectTransaction,
4637 title: String,
4638 mut cx: AsyncWindowContext,
4639 ) -> Result<()> {
4640 let replica_id = this.update(&mut cx, |this, cx| this.replica_id(cx))?;
4641
4642 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4643 cx.update(|cx| {
4644 entries.sort_unstable_by_key(|(buffer, _)| {
4645 buffer.read(cx).file().map(|f| f.path().clone())
4646 });
4647 })?;
4648
4649 // If the project transaction's edits are all contained within this editor, then
4650 // avoid opening a new editor to display them.
4651
4652 if let Some((buffer, transaction)) = entries.first() {
4653 if entries.len() == 1 {
4654 let excerpt = this.update(&mut cx, |editor, cx| {
4655 editor
4656 .buffer()
4657 .read(cx)
4658 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4659 })?;
4660 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4661 if excerpted_buffer == *buffer {
4662 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4663 let excerpt_range = excerpt_range.to_offset(buffer);
4664 buffer
4665 .edited_ranges_for_transaction::<usize>(transaction)
4666 .all(|range| {
4667 excerpt_range.start <= range.start
4668 && excerpt_range.end >= range.end
4669 })
4670 })?;
4671
4672 if all_edits_within_excerpt {
4673 return Ok(());
4674 }
4675 }
4676 }
4677 }
4678 } else {
4679 return Ok(());
4680 }
4681
4682 let mut ranges_to_highlight = Vec::new();
4683 let excerpt_buffer = cx.new_model(|cx| {
4684 let mut multibuffer =
4685 MultiBuffer::new(replica_id, Capability::ReadWrite).with_title(title);
4686 for (buffer_handle, transaction) in &entries {
4687 let buffer = buffer_handle.read(cx);
4688 ranges_to_highlight.extend(
4689 multibuffer.push_excerpts_with_context_lines(
4690 buffer_handle.clone(),
4691 buffer
4692 .edited_ranges_for_transaction::<usize>(transaction)
4693 .collect(),
4694 DEFAULT_MULTIBUFFER_CONTEXT,
4695 cx,
4696 ),
4697 );
4698 }
4699 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4700 multibuffer
4701 })?;
4702
4703 workspace.update(&mut cx, |workspace, cx| {
4704 let project = workspace.project().clone();
4705 let editor =
4706 cx.new_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, cx));
4707 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
4708 editor.update(cx, |editor, cx| {
4709 editor.highlight_background::<Self>(
4710 &ranges_to_highlight,
4711 |theme| theme.editor_highlighted_line_background,
4712 cx,
4713 );
4714 });
4715 })?;
4716
4717 Ok(())
4718 }
4719
4720 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4721 let project = self.project.clone()?;
4722 let buffer = self.buffer.read(cx);
4723 let newest_selection = self.selections.newest_anchor().clone();
4724 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4725 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4726 if start_buffer != end_buffer {
4727 return None;
4728 }
4729
4730 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
4731 cx.background_executor()
4732 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4733 .await;
4734
4735 let actions = if let Ok(code_actions) = project.update(&mut cx, |project, cx| {
4736 project.code_actions(&start_buffer, start..end, cx)
4737 }) {
4738 code_actions.await
4739 } else {
4740 Vec::new()
4741 };
4742
4743 this.update(&mut cx, |this, cx| {
4744 this.available_code_actions = if actions.is_empty() {
4745 None
4746 } else {
4747 Some((
4748 Location {
4749 buffer: start_buffer,
4750 range: start..end,
4751 },
4752 actions.into(),
4753 ))
4754 };
4755 cx.notify();
4756 })
4757 .log_err();
4758 }));
4759 None
4760 }
4761
4762 fn start_inline_blame_timer(&mut self, cx: &mut ViewContext<Self>) {
4763 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4764 self.show_git_blame_inline = false;
4765
4766 self.show_git_blame_inline_delay_task = Some(cx.spawn(|this, mut cx| async move {
4767 cx.background_executor().timer(delay).await;
4768
4769 this.update(&mut cx, |this, cx| {
4770 this.show_git_blame_inline = true;
4771 cx.notify();
4772 })
4773 .log_err();
4774 }));
4775 }
4776 }
4777
4778 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4779 if self.pending_rename.is_some() {
4780 return None;
4781 }
4782
4783 let project = self.project.clone()?;
4784 let buffer = self.buffer.read(cx);
4785 let newest_selection = self.selections.newest_anchor().clone();
4786 let cursor_position = newest_selection.head();
4787 let (cursor_buffer, cursor_buffer_position) =
4788 buffer.text_anchor_for_position(cursor_position, cx)?;
4789 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4790 if cursor_buffer != tail_buffer {
4791 return None;
4792 }
4793
4794 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4795 cx.background_executor()
4796 .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT)
4797 .await;
4798
4799 let highlights = if let Some(highlights) = project
4800 .update(&mut cx, |project, cx| {
4801 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4802 })
4803 .log_err()
4804 {
4805 highlights.await.log_err()
4806 } else {
4807 None
4808 };
4809
4810 if let Some(highlights) = highlights {
4811 this.update(&mut cx, |this, cx| {
4812 if this.pending_rename.is_some() {
4813 return;
4814 }
4815
4816 let buffer_id = cursor_position.buffer_id;
4817 let buffer = this.buffer.read(cx);
4818 if !buffer
4819 .text_anchor_for_position(cursor_position, cx)
4820 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4821 {
4822 return;
4823 }
4824
4825 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4826 let mut write_ranges = Vec::new();
4827 let mut read_ranges = Vec::new();
4828 for highlight in highlights {
4829 for (excerpt_id, excerpt_range) in
4830 buffer.excerpts_for_buffer(&cursor_buffer, cx)
4831 {
4832 let start = highlight
4833 .range
4834 .start
4835 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4836 let end = highlight
4837 .range
4838 .end
4839 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4840 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4841 continue;
4842 }
4843
4844 let range = Anchor {
4845 buffer_id,
4846 excerpt_id: excerpt_id,
4847 text_anchor: start,
4848 }..Anchor {
4849 buffer_id,
4850 excerpt_id,
4851 text_anchor: end,
4852 };
4853 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4854 write_ranges.push(range);
4855 } else {
4856 read_ranges.push(range);
4857 }
4858 }
4859 }
4860
4861 this.highlight_background::<DocumentHighlightRead>(
4862 &read_ranges,
4863 |theme| theme.editor_document_highlight_read_background,
4864 cx,
4865 );
4866 this.highlight_background::<DocumentHighlightWrite>(
4867 &write_ranges,
4868 |theme| theme.editor_document_highlight_write_background,
4869 cx,
4870 );
4871 cx.notify();
4872 })
4873 .log_err();
4874 }
4875 }));
4876 None
4877 }
4878
4879 fn refresh_inline_completion(
4880 &mut self,
4881 debounce: bool,
4882 cx: &mut ViewContext<Self>,
4883 ) -> Option<()> {
4884 let provider = self.inline_completion_provider()?;
4885 let cursor = self.selections.newest_anchor().head();
4886 let (buffer, cursor_buffer_position) =
4887 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4888 if !self.show_inline_completions
4889 || !provider.is_enabled(&buffer, cursor_buffer_position, cx)
4890 {
4891 self.discard_inline_completion(false, cx);
4892 return None;
4893 }
4894
4895 self.update_visible_inline_completion(cx);
4896 provider.refresh(buffer, cursor_buffer_position, debounce, cx);
4897 Some(())
4898 }
4899
4900 fn cycle_inline_completion(
4901 &mut self,
4902 direction: Direction,
4903 cx: &mut ViewContext<Self>,
4904 ) -> Option<()> {
4905 let provider = self.inline_completion_provider()?;
4906 let cursor = self.selections.newest_anchor().head();
4907 let (buffer, cursor_buffer_position) =
4908 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4909 if !self.show_inline_completions
4910 || !provider.is_enabled(&buffer, cursor_buffer_position, cx)
4911 {
4912 return None;
4913 }
4914
4915 provider.cycle(buffer, cursor_buffer_position, direction, cx);
4916 self.update_visible_inline_completion(cx);
4917
4918 Some(())
4919 }
4920
4921 pub fn show_inline_completion(&mut self, _: &ShowInlineCompletion, cx: &mut ViewContext<Self>) {
4922 if !self.has_active_inline_completion(cx) {
4923 self.refresh_inline_completion(false, cx);
4924 return;
4925 }
4926
4927 self.update_visible_inline_completion(cx);
4928 }
4929
4930 pub fn display_cursor_names(&mut self, _: &DisplayCursorNames, cx: &mut ViewContext<Self>) {
4931 self.show_cursor_names(cx);
4932 }
4933
4934 fn show_cursor_names(&mut self, cx: &mut ViewContext<Self>) {
4935 self.show_cursor_names = true;
4936 cx.notify();
4937 cx.spawn(|this, mut cx| async move {
4938 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
4939 this.update(&mut cx, |this, cx| {
4940 this.show_cursor_names = false;
4941 cx.notify()
4942 })
4943 .ok()
4944 })
4945 .detach();
4946 }
4947
4948 pub fn next_inline_completion(&mut self, _: &NextInlineCompletion, cx: &mut ViewContext<Self>) {
4949 if self.has_active_inline_completion(cx) {
4950 self.cycle_inline_completion(Direction::Next, cx);
4951 } else {
4952 let is_copilot_disabled = self.refresh_inline_completion(false, cx).is_none();
4953 if is_copilot_disabled {
4954 cx.propagate();
4955 }
4956 }
4957 }
4958
4959 pub fn previous_inline_completion(
4960 &mut self,
4961 _: &PreviousInlineCompletion,
4962 cx: &mut ViewContext<Self>,
4963 ) {
4964 if self.has_active_inline_completion(cx) {
4965 self.cycle_inline_completion(Direction::Prev, cx);
4966 } else {
4967 let is_copilot_disabled = self.refresh_inline_completion(false, cx).is_none();
4968 if is_copilot_disabled {
4969 cx.propagate();
4970 }
4971 }
4972 }
4973
4974 pub fn accept_inline_completion(
4975 &mut self,
4976 _: &AcceptInlineCompletion,
4977 cx: &mut ViewContext<Self>,
4978 ) {
4979 let Some((completion, delete_range)) = self.take_active_inline_completion(cx) else {
4980 return;
4981 };
4982 if let Some(provider) = self.inline_completion_provider() {
4983 provider.accept(cx);
4984 }
4985
4986 cx.emit(EditorEvent::InputHandled {
4987 utf16_range_to_replace: None,
4988 text: completion.text.to_string().into(),
4989 });
4990
4991 if let Some(range) = delete_range {
4992 self.change_selections(None, cx, |s| s.select_ranges([range]))
4993 }
4994 self.insert_with_autoindent_mode(&completion.text.to_string(), None, cx);
4995 self.refresh_inline_completion(true, cx);
4996 cx.notify();
4997 }
4998
4999 pub fn accept_partial_inline_completion(
5000 &mut self,
5001 _: &AcceptPartialInlineCompletion,
5002 cx: &mut ViewContext<Self>,
5003 ) {
5004 if self.selections.count() == 1 && self.has_active_inline_completion(cx) {
5005 if let Some((completion, delete_range)) = self.take_active_inline_completion(cx) {
5006 let mut partial_completion = completion
5007 .text
5008 .chars()
5009 .by_ref()
5010 .take_while(|c| c.is_alphabetic())
5011 .collect::<String>();
5012 if partial_completion.is_empty() {
5013 partial_completion = completion
5014 .text
5015 .chars()
5016 .by_ref()
5017 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5018 .collect::<String>();
5019 }
5020
5021 cx.emit(EditorEvent::InputHandled {
5022 utf16_range_to_replace: None,
5023 text: partial_completion.clone().into(),
5024 });
5025
5026 if let Some(range) = delete_range {
5027 self.change_selections(None, cx, |s| s.select_ranges([range]))
5028 }
5029 self.insert_with_autoindent_mode(&partial_completion, None, cx);
5030
5031 self.refresh_inline_completion(true, cx);
5032 cx.notify();
5033 }
5034 }
5035 }
5036
5037 fn discard_inline_completion(
5038 &mut self,
5039 should_report_inline_completion_event: bool,
5040 cx: &mut ViewContext<Self>,
5041 ) -> bool {
5042 if let Some(provider) = self.inline_completion_provider() {
5043 provider.discard(should_report_inline_completion_event, cx);
5044 }
5045
5046 self.take_active_inline_completion(cx).is_some()
5047 }
5048
5049 pub fn has_active_inline_completion(&self, cx: &AppContext) -> bool {
5050 if let Some(completion) = self.active_inline_completion.as_ref() {
5051 let buffer = self.buffer.read(cx).read(cx);
5052 completion.0.position.is_valid(&buffer)
5053 } else {
5054 false
5055 }
5056 }
5057
5058 fn take_active_inline_completion(
5059 &mut self,
5060 cx: &mut ViewContext<Self>,
5061 ) -> Option<(Inlay, Option<Range<Anchor>>)> {
5062 let completion = self.active_inline_completion.take()?;
5063 self.display_map.update(cx, |map, cx| {
5064 map.splice_inlays(vec![completion.0.id], Default::default(), cx);
5065 });
5066 let buffer = self.buffer.read(cx).read(cx);
5067
5068 if completion.0.position.is_valid(&buffer) {
5069 Some(completion)
5070 } else {
5071 None
5072 }
5073 }
5074
5075 fn update_visible_inline_completion(&mut self, cx: &mut ViewContext<Self>) {
5076 let selection = self.selections.newest_anchor();
5077 let cursor = selection.head();
5078
5079 let excerpt_id = cursor.excerpt_id;
5080
5081 if self.context_menu.read().is_none()
5082 && self.completion_tasks.is_empty()
5083 && selection.start == selection.end
5084 {
5085 if let Some(provider) = self.inline_completion_provider() {
5086 if let Some((buffer, cursor_buffer_position)) =
5087 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5088 {
5089 if let Some((text, text_anchor_range)) =
5090 provider.active_completion_text(&buffer, cursor_buffer_position, cx)
5091 {
5092 let text = Rope::from(text);
5093 let mut to_remove = Vec::new();
5094 if let Some(completion) = self.active_inline_completion.take() {
5095 to_remove.push(completion.0.id);
5096 }
5097
5098 let completion_inlay =
5099 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
5100
5101 let multibuffer_anchor_range = text_anchor_range.and_then(|range| {
5102 let snapshot = self.buffer.read(cx).snapshot(cx);
5103 Some(
5104 snapshot.anchor_in_excerpt(excerpt_id, range.start)?
5105 ..snapshot.anchor_in_excerpt(excerpt_id, range.end)?,
5106 )
5107 });
5108 self.active_inline_completion =
5109 Some((completion_inlay.clone(), multibuffer_anchor_range));
5110
5111 self.display_map.update(cx, move |map, cx| {
5112 map.splice_inlays(to_remove, vec![completion_inlay], cx)
5113 });
5114 cx.notify();
5115 return;
5116 }
5117 }
5118 }
5119 }
5120
5121 self.discard_inline_completion(false, cx);
5122 }
5123
5124 fn inline_completion_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5125 Some(self.inline_completion_provider.as_ref()?.provider.clone())
5126 }
5127
5128 fn render_code_actions_indicator(
5129 &self,
5130 _style: &EditorStyle,
5131 row: DisplayRow,
5132 is_active: bool,
5133 cx: &mut ViewContext<Self>,
5134 ) -> Option<IconButton> {
5135 if self.available_code_actions.is_some() {
5136 Some(
5137 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5138 .shape(ui::IconButtonShape::Square)
5139 .icon_size(IconSize::XSmall)
5140 .icon_color(Color::Muted)
5141 .selected(is_active)
5142 .on_click(cx.listener(move |editor, _e, cx| {
5143 editor.focus(cx);
5144 editor.toggle_code_actions(
5145 &ToggleCodeActions {
5146 deployed_from_indicator: Some(row),
5147 },
5148 cx,
5149 );
5150 })),
5151 )
5152 } else {
5153 None
5154 }
5155 }
5156
5157 fn clear_tasks(&mut self) {
5158 self.tasks.clear()
5159 }
5160
5161 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5162 if let Some(_) = self.tasks.insert(key, value) {
5163 // This case should hopefully be rare, but just in case...
5164 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5165 }
5166 }
5167
5168 fn render_run_indicator(
5169 &self,
5170 _style: &EditorStyle,
5171 is_active: bool,
5172 row: DisplayRow,
5173 cx: &mut ViewContext<Self>,
5174 ) -> IconButton {
5175 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5176 .shape(ui::IconButtonShape::Square)
5177 .icon_size(IconSize::XSmall)
5178 .icon_color(Color::Muted)
5179 .selected(is_active)
5180 .on_click(cx.listener(move |editor, _e, cx| {
5181 editor.focus(cx);
5182 editor.toggle_code_actions(
5183 &ToggleCodeActions {
5184 deployed_from_indicator: Some(row),
5185 },
5186 cx,
5187 );
5188 }))
5189 }
5190
5191 fn close_hunk_diff_button(
5192 &self,
5193 hunk: HoveredHunk,
5194 row: DisplayRow,
5195 cx: &mut ViewContext<Self>,
5196 ) -> IconButton {
5197 IconButton::new(
5198 ("close_hunk_diff_indicator", row.0 as usize),
5199 ui::IconName::Close,
5200 )
5201 .shape(ui::IconButtonShape::Square)
5202 .icon_size(IconSize::XSmall)
5203 .icon_color(Color::Muted)
5204 .tooltip(|cx| Tooltip::for_action("Close hunk diff", &ToggleHunkDiff, cx))
5205 .on_click(cx.listener(move |editor, _e, cx| editor.toggle_hovered_hunk(&hunk, cx)))
5206 }
5207
5208 pub fn context_menu_visible(&self) -> bool {
5209 self.context_menu
5210 .read()
5211 .as_ref()
5212 .map_or(false, |menu| menu.visible())
5213 }
5214
5215 fn render_context_menu(
5216 &self,
5217 cursor_position: DisplayPoint,
5218 style: &EditorStyle,
5219 max_height: Pixels,
5220 cx: &mut ViewContext<Editor>,
5221 ) -> Option<(ContextMenuOrigin, AnyElement)> {
5222 self.context_menu.read().as_ref().map(|menu| {
5223 menu.render(
5224 cursor_position,
5225 style,
5226 max_height,
5227 self.workspace.as_ref().map(|(w, _)| w.clone()),
5228 cx,
5229 )
5230 })
5231 }
5232
5233 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
5234 cx.notify();
5235 self.completion_tasks.clear();
5236 let context_menu = self.context_menu.write().take();
5237 if context_menu.is_some() {
5238 self.update_visible_inline_completion(cx);
5239 }
5240 context_menu
5241 }
5242
5243 pub fn insert_snippet(
5244 &mut self,
5245 insertion_ranges: &[Range<usize>],
5246 snippet: Snippet,
5247 cx: &mut ViewContext<Self>,
5248 ) -> Result<()> {
5249 struct Tabstop<T> {
5250 is_end_tabstop: bool,
5251 ranges: Vec<Range<T>>,
5252 }
5253
5254 let tabstops = self.buffer.update(cx, |buffer, cx| {
5255 let snippet_text: Arc<str> = snippet.text.clone().into();
5256 buffer.edit(
5257 insertion_ranges
5258 .iter()
5259 .cloned()
5260 .map(|range| (range, snippet_text.clone())),
5261 Some(AutoindentMode::EachLine),
5262 cx,
5263 );
5264
5265 let snapshot = &*buffer.read(cx);
5266 let snippet = &snippet;
5267 snippet
5268 .tabstops
5269 .iter()
5270 .map(|tabstop| {
5271 let is_end_tabstop = tabstop.first().map_or(false, |tabstop| {
5272 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
5273 });
5274 let mut tabstop_ranges = tabstop
5275 .iter()
5276 .flat_map(|tabstop_range| {
5277 let mut delta = 0_isize;
5278 insertion_ranges.iter().map(move |insertion_range| {
5279 let insertion_start = insertion_range.start as isize + delta;
5280 delta +=
5281 snippet.text.len() as isize - insertion_range.len() as isize;
5282
5283 let start = ((insertion_start + tabstop_range.start) as usize)
5284 .min(snapshot.len());
5285 let end = ((insertion_start + tabstop_range.end) as usize)
5286 .min(snapshot.len());
5287 snapshot.anchor_before(start)..snapshot.anchor_after(end)
5288 })
5289 })
5290 .collect::<Vec<_>>();
5291 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
5292
5293 Tabstop {
5294 is_end_tabstop,
5295 ranges: tabstop_ranges,
5296 }
5297 })
5298 .collect::<Vec<_>>()
5299 });
5300 if let Some(tabstop) = tabstops.first() {
5301 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5302 s.select_ranges(tabstop.ranges.iter().cloned());
5303 });
5304
5305 // If we're already at the last tabstop and it's at the end of the snippet,
5306 // we're done, we don't need to keep the state around.
5307 if !tabstop.is_end_tabstop {
5308 let ranges = tabstops
5309 .into_iter()
5310 .map(|tabstop| tabstop.ranges)
5311 .collect::<Vec<_>>();
5312 self.snippet_stack.push(SnippetState {
5313 active_index: 0,
5314 ranges,
5315 });
5316 }
5317
5318 // Check whether the just-entered snippet ends with an auto-closable bracket.
5319 if self.autoclose_regions.is_empty() {
5320 let snapshot = self.buffer.read(cx).snapshot(cx);
5321 for selection in &mut self.selections.all::<Point>(cx) {
5322 let selection_head = selection.head();
5323 let Some(scope) = snapshot.language_scope_at(selection_head) else {
5324 continue;
5325 };
5326
5327 let mut bracket_pair = None;
5328 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
5329 let prev_chars = snapshot
5330 .reversed_chars_at(selection_head)
5331 .collect::<String>();
5332 for (pair, enabled) in scope.brackets() {
5333 if enabled
5334 && pair.close
5335 && prev_chars.starts_with(pair.start.as_str())
5336 && next_chars.starts_with(pair.end.as_str())
5337 {
5338 bracket_pair = Some(pair.clone());
5339 break;
5340 }
5341 }
5342 if let Some(pair) = bracket_pair {
5343 let start = snapshot.anchor_after(selection_head);
5344 let end = snapshot.anchor_after(selection_head);
5345 self.autoclose_regions.push(AutocloseRegion {
5346 selection_id: selection.id,
5347 range: start..end,
5348 pair,
5349 });
5350 }
5351 }
5352 }
5353 }
5354 Ok(())
5355 }
5356
5357 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5358 self.move_to_snippet_tabstop(Bias::Right, cx)
5359 }
5360
5361 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5362 self.move_to_snippet_tabstop(Bias::Left, cx)
5363 }
5364
5365 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
5366 if let Some(mut snippet) = self.snippet_stack.pop() {
5367 match bias {
5368 Bias::Left => {
5369 if snippet.active_index > 0 {
5370 snippet.active_index -= 1;
5371 } else {
5372 self.snippet_stack.push(snippet);
5373 return false;
5374 }
5375 }
5376 Bias::Right => {
5377 if snippet.active_index + 1 < snippet.ranges.len() {
5378 snippet.active_index += 1;
5379 } else {
5380 self.snippet_stack.push(snippet);
5381 return false;
5382 }
5383 }
5384 }
5385 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
5386 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5387 s.select_anchor_ranges(current_ranges.iter().cloned())
5388 });
5389 // If snippet state is not at the last tabstop, push it back on the stack
5390 if snippet.active_index + 1 < snippet.ranges.len() {
5391 self.snippet_stack.push(snippet);
5392 }
5393 return true;
5394 }
5395 }
5396
5397 false
5398 }
5399
5400 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
5401 self.transact(cx, |this, cx| {
5402 this.select_all(&SelectAll, cx);
5403 this.insert("", cx);
5404 });
5405 }
5406
5407 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
5408 self.transact(cx, |this, cx| {
5409 this.select_autoclose_pair(cx);
5410 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
5411 if !this.linked_edit_ranges.is_empty() {
5412 let selections = this.selections.all::<MultiBufferPoint>(cx);
5413 let snapshot = this.buffer.read(cx).snapshot(cx);
5414
5415 for selection in selections.iter() {
5416 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
5417 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
5418 if selection_start.buffer_id != selection_end.buffer_id {
5419 continue;
5420 }
5421 if let Some(ranges) =
5422 this.linked_editing_ranges_for(selection_start..selection_end, cx)
5423 {
5424 for (buffer, entries) in ranges {
5425 linked_ranges.entry(buffer).or_default().extend(entries);
5426 }
5427 }
5428 }
5429 }
5430
5431 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
5432 if !this.selections.line_mode {
5433 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
5434 for selection in &mut selections {
5435 if selection.is_empty() {
5436 let old_head = selection.head();
5437 let mut new_head =
5438 movement::left(&display_map, old_head.to_display_point(&display_map))
5439 .to_point(&display_map);
5440 if let Some((buffer, line_buffer_range)) = display_map
5441 .buffer_snapshot
5442 .buffer_line_for_row(MultiBufferRow(old_head.row))
5443 {
5444 let indent_size =
5445 buffer.indent_size_for_line(line_buffer_range.start.row);
5446 let indent_len = match indent_size.kind {
5447 IndentKind::Space => {
5448 buffer.settings_at(line_buffer_range.start, cx).tab_size
5449 }
5450 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
5451 };
5452 if old_head.column <= indent_size.len && old_head.column > 0 {
5453 let indent_len = indent_len.get();
5454 new_head = cmp::min(
5455 new_head,
5456 MultiBufferPoint::new(
5457 old_head.row,
5458 ((old_head.column - 1) / indent_len) * indent_len,
5459 ),
5460 );
5461 }
5462 }
5463
5464 selection.set_head(new_head, SelectionGoal::None);
5465 }
5466 }
5467 }
5468
5469 this.signature_help_state.set_backspace_pressed(true);
5470 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5471 this.insert("", cx);
5472 let empty_str: Arc<str> = Arc::from("");
5473 for (buffer, edits) in linked_ranges {
5474 let snapshot = buffer.read(cx).snapshot();
5475 use text::ToPoint as TP;
5476
5477 let edits = edits
5478 .into_iter()
5479 .map(|range| {
5480 let end_point = TP::to_point(&range.end, &snapshot);
5481 let mut start_point = TP::to_point(&range.start, &snapshot);
5482
5483 if end_point == start_point {
5484 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
5485 .saturating_sub(1);
5486 start_point = TP::to_point(&offset, &snapshot);
5487 };
5488
5489 (start_point..end_point, empty_str.clone())
5490 })
5491 .sorted_by_key(|(range, _)| range.start)
5492 .collect::<Vec<_>>();
5493 buffer.update(cx, |this, cx| {
5494 this.edit(edits, None, cx);
5495 })
5496 }
5497 this.refresh_inline_completion(true, cx);
5498 linked_editing_ranges::refresh_linked_ranges(this, cx);
5499 });
5500 }
5501
5502 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
5503 self.transact(cx, |this, cx| {
5504 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5505 let line_mode = s.line_mode;
5506 s.move_with(|map, selection| {
5507 if selection.is_empty() && !line_mode {
5508 let cursor = movement::right(map, selection.head());
5509 selection.end = cursor;
5510 selection.reversed = true;
5511 selection.goal = SelectionGoal::None;
5512 }
5513 })
5514 });
5515 this.insert("", cx);
5516 this.refresh_inline_completion(true, cx);
5517 });
5518 }
5519
5520 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
5521 if self.move_to_prev_snippet_tabstop(cx) {
5522 return;
5523 }
5524
5525 self.outdent(&Outdent, cx);
5526 }
5527
5528 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
5529 if self.move_to_next_snippet_tabstop(cx) || self.read_only(cx) {
5530 return;
5531 }
5532
5533 let mut selections = self.selections.all_adjusted(cx);
5534 let buffer = self.buffer.read(cx);
5535 let snapshot = buffer.snapshot(cx);
5536 let rows_iter = selections.iter().map(|s| s.head().row);
5537 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
5538
5539 let mut edits = Vec::new();
5540 let mut prev_edited_row = 0;
5541 let mut row_delta = 0;
5542 for selection in &mut selections {
5543 if selection.start.row != prev_edited_row {
5544 row_delta = 0;
5545 }
5546 prev_edited_row = selection.end.row;
5547
5548 // If the selection is non-empty, then increase the indentation of the selected lines.
5549 if !selection.is_empty() {
5550 row_delta =
5551 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5552 continue;
5553 }
5554
5555 // If the selection is empty and the cursor is in the leading whitespace before the
5556 // suggested indentation, then auto-indent the line.
5557 let cursor = selection.head();
5558 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
5559 if let Some(suggested_indent) =
5560 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
5561 {
5562 if cursor.column < suggested_indent.len
5563 && cursor.column <= current_indent.len
5564 && current_indent.len <= suggested_indent.len
5565 {
5566 selection.start = Point::new(cursor.row, suggested_indent.len);
5567 selection.end = selection.start;
5568 if row_delta == 0 {
5569 edits.extend(Buffer::edit_for_indent_size_adjustment(
5570 cursor.row,
5571 current_indent,
5572 suggested_indent,
5573 ));
5574 row_delta = suggested_indent.len - current_indent.len;
5575 }
5576 continue;
5577 }
5578 }
5579
5580 // Otherwise, insert a hard or soft tab.
5581 let settings = buffer.settings_at(cursor, cx);
5582 let tab_size = if settings.hard_tabs {
5583 IndentSize::tab()
5584 } else {
5585 let tab_size = settings.tab_size.get();
5586 let char_column = snapshot
5587 .text_for_range(Point::new(cursor.row, 0)..cursor)
5588 .flat_map(str::chars)
5589 .count()
5590 + row_delta as usize;
5591 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
5592 IndentSize::spaces(chars_to_next_tab_stop)
5593 };
5594 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
5595 selection.end = selection.start;
5596 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
5597 row_delta += tab_size.len;
5598 }
5599
5600 self.transact(cx, |this, cx| {
5601 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5602 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5603 this.refresh_inline_completion(true, cx);
5604 });
5605 }
5606
5607 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
5608 if self.read_only(cx) {
5609 return;
5610 }
5611 let mut selections = self.selections.all::<Point>(cx);
5612 let mut prev_edited_row = 0;
5613 let mut row_delta = 0;
5614 let mut edits = Vec::new();
5615 let buffer = self.buffer.read(cx);
5616 let snapshot = buffer.snapshot(cx);
5617 for selection in &mut selections {
5618 if selection.start.row != prev_edited_row {
5619 row_delta = 0;
5620 }
5621 prev_edited_row = selection.end.row;
5622
5623 row_delta =
5624 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5625 }
5626
5627 self.transact(cx, |this, cx| {
5628 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5629 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5630 });
5631 }
5632
5633 fn indent_selection(
5634 buffer: &MultiBuffer,
5635 snapshot: &MultiBufferSnapshot,
5636 selection: &mut Selection<Point>,
5637 edits: &mut Vec<(Range<Point>, String)>,
5638 delta_for_start_row: u32,
5639 cx: &AppContext,
5640 ) -> u32 {
5641 let settings = buffer.settings_at(selection.start, cx);
5642 let tab_size = settings.tab_size.get();
5643 let indent_kind = if settings.hard_tabs {
5644 IndentKind::Tab
5645 } else {
5646 IndentKind::Space
5647 };
5648 let mut start_row = selection.start.row;
5649 let mut end_row = selection.end.row + 1;
5650
5651 // If a selection ends at the beginning of a line, don't indent
5652 // that last line.
5653 if selection.end.column == 0 && selection.end.row > selection.start.row {
5654 end_row -= 1;
5655 }
5656
5657 // Avoid re-indenting a row that has already been indented by a
5658 // previous selection, but still update this selection's column
5659 // to reflect that indentation.
5660 if delta_for_start_row > 0 {
5661 start_row += 1;
5662 selection.start.column += delta_for_start_row;
5663 if selection.end.row == selection.start.row {
5664 selection.end.column += delta_for_start_row;
5665 }
5666 }
5667
5668 let mut delta_for_end_row = 0;
5669 let has_multiple_rows = start_row + 1 != end_row;
5670 for row in start_row..end_row {
5671 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
5672 let indent_delta = match (current_indent.kind, indent_kind) {
5673 (IndentKind::Space, IndentKind::Space) => {
5674 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
5675 IndentSize::spaces(columns_to_next_tab_stop)
5676 }
5677 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
5678 (_, IndentKind::Tab) => IndentSize::tab(),
5679 };
5680
5681 let start = if has_multiple_rows || current_indent.len < selection.start.column {
5682 0
5683 } else {
5684 selection.start.column
5685 };
5686 let row_start = Point::new(row, start);
5687 edits.push((
5688 row_start..row_start,
5689 indent_delta.chars().collect::<String>(),
5690 ));
5691
5692 // Update this selection's endpoints to reflect the indentation.
5693 if row == selection.start.row {
5694 selection.start.column += indent_delta.len;
5695 }
5696 if row == selection.end.row {
5697 selection.end.column += indent_delta.len;
5698 delta_for_end_row = indent_delta.len;
5699 }
5700 }
5701
5702 if selection.start.row == selection.end.row {
5703 delta_for_start_row + delta_for_end_row
5704 } else {
5705 delta_for_end_row
5706 }
5707 }
5708
5709 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
5710 if self.read_only(cx) {
5711 return;
5712 }
5713 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5714 let selections = self.selections.all::<Point>(cx);
5715 let mut deletion_ranges = Vec::new();
5716 let mut last_outdent = None;
5717 {
5718 let buffer = self.buffer.read(cx);
5719 let snapshot = buffer.snapshot(cx);
5720 for selection in &selections {
5721 let settings = buffer.settings_at(selection.start, cx);
5722 let tab_size = settings.tab_size.get();
5723 let mut rows = selection.spanned_rows(false, &display_map);
5724
5725 // Avoid re-outdenting a row that has already been outdented by a
5726 // previous selection.
5727 if let Some(last_row) = last_outdent {
5728 if last_row == rows.start {
5729 rows.start = rows.start.next_row();
5730 }
5731 }
5732 let has_multiple_rows = rows.len() > 1;
5733 for row in rows.iter_rows() {
5734 let indent_size = snapshot.indent_size_for_line(row);
5735 if indent_size.len > 0 {
5736 let deletion_len = match indent_size.kind {
5737 IndentKind::Space => {
5738 let columns_to_prev_tab_stop = indent_size.len % tab_size;
5739 if columns_to_prev_tab_stop == 0 {
5740 tab_size
5741 } else {
5742 columns_to_prev_tab_stop
5743 }
5744 }
5745 IndentKind::Tab => 1,
5746 };
5747 let start = if has_multiple_rows
5748 || deletion_len > selection.start.column
5749 || indent_size.len < selection.start.column
5750 {
5751 0
5752 } else {
5753 selection.start.column - deletion_len
5754 };
5755 deletion_ranges.push(
5756 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
5757 );
5758 last_outdent = Some(row);
5759 }
5760 }
5761 }
5762 }
5763
5764 self.transact(cx, |this, cx| {
5765 this.buffer.update(cx, |buffer, cx| {
5766 let empty_str: Arc<str> = Arc::default();
5767 buffer.edit(
5768 deletion_ranges
5769 .into_iter()
5770 .map(|range| (range, empty_str.clone())),
5771 None,
5772 cx,
5773 );
5774 });
5775 let selections = this.selections.all::<usize>(cx);
5776 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5777 });
5778 }
5779
5780 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
5781 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5782 let selections = self.selections.all::<Point>(cx);
5783
5784 let mut new_cursors = Vec::new();
5785 let mut edit_ranges = Vec::new();
5786 let mut selections = selections.iter().peekable();
5787 while let Some(selection) = selections.next() {
5788 let mut rows = selection.spanned_rows(false, &display_map);
5789 let goal_display_column = selection.head().to_display_point(&display_map).column();
5790
5791 // Accumulate contiguous regions of rows that we want to delete.
5792 while let Some(next_selection) = selections.peek() {
5793 let next_rows = next_selection.spanned_rows(false, &display_map);
5794 if next_rows.start <= rows.end {
5795 rows.end = next_rows.end;
5796 selections.next().unwrap();
5797 } else {
5798 break;
5799 }
5800 }
5801
5802 let buffer = &display_map.buffer_snapshot;
5803 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
5804 let edit_end;
5805 let cursor_buffer_row;
5806 if buffer.max_point().row >= rows.end.0 {
5807 // If there's a line after the range, delete the \n from the end of the row range
5808 // and position the cursor on the next line.
5809 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
5810 cursor_buffer_row = rows.end;
5811 } else {
5812 // If there isn't a line after the range, delete the \n from the line before the
5813 // start of the row range and position the cursor there.
5814 edit_start = edit_start.saturating_sub(1);
5815 edit_end = buffer.len();
5816 cursor_buffer_row = rows.start.previous_row();
5817 }
5818
5819 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
5820 *cursor.column_mut() =
5821 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
5822
5823 new_cursors.push((
5824 selection.id,
5825 buffer.anchor_after(cursor.to_point(&display_map)),
5826 ));
5827 edit_ranges.push(edit_start..edit_end);
5828 }
5829
5830 self.transact(cx, |this, cx| {
5831 let buffer = this.buffer.update(cx, |buffer, cx| {
5832 let empty_str: Arc<str> = Arc::default();
5833 buffer.edit(
5834 edit_ranges
5835 .into_iter()
5836 .map(|range| (range, empty_str.clone())),
5837 None,
5838 cx,
5839 );
5840 buffer.snapshot(cx)
5841 });
5842 let new_selections = new_cursors
5843 .into_iter()
5844 .map(|(id, cursor)| {
5845 let cursor = cursor.to_point(&buffer);
5846 Selection {
5847 id,
5848 start: cursor,
5849 end: cursor,
5850 reversed: false,
5851 goal: SelectionGoal::None,
5852 }
5853 })
5854 .collect();
5855
5856 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5857 s.select(new_selections);
5858 });
5859 });
5860 }
5861
5862 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
5863 if self.read_only(cx) {
5864 return;
5865 }
5866 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
5867 for selection in self.selections.all::<Point>(cx) {
5868 let start = MultiBufferRow(selection.start.row);
5869 let end = if selection.start.row == selection.end.row {
5870 MultiBufferRow(selection.start.row + 1)
5871 } else {
5872 MultiBufferRow(selection.end.row)
5873 };
5874
5875 if let Some(last_row_range) = row_ranges.last_mut() {
5876 if start <= last_row_range.end {
5877 last_row_range.end = end;
5878 continue;
5879 }
5880 }
5881 row_ranges.push(start..end);
5882 }
5883
5884 let snapshot = self.buffer.read(cx).snapshot(cx);
5885 let mut cursor_positions = Vec::new();
5886 for row_range in &row_ranges {
5887 let anchor = snapshot.anchor_before(Point::new(
5888 row_range.end.previous_row().0,
5889 snapshot.line_len(row_range.end.previous_row()),
5890 ));
5891 cursor_positions.push(anchor..anchor);
5892 }
5893
5894 self.transact(cx, |this, cx| {
5895 for row_range in row_ranges.into_iter().rev() {
5896 for row in row_range.iter_rows().rev() {
5897 let end_of_line = Point::new(row.0, snapshot.line_len(row));
5898 let next_line_row = row.next_row();
5899 let indent = snapshot.indent_size_for_line(next_line_row);
5900 let start_of_next_line = Point::new(next_line_row.0, indent.len);
5901
5902 let replace = if snapshot.line_len(next_line_row) > indent.len {
5903 " "
5904 } else {
5905 ""
5906 };
5907
5908 this.buffer.update(cx, |buffer, cx| {
5909 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
5910 });
5911 }
5912 }
5913
5914 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5915 s.select_anchor_ranges(cursor_positions)
5916 });
5917 });
5918 }
5919
5920 pub fn sort_lines_case_sensitive(
5921 &mut self,
5922 _: &SortLinesCaseSensitive,
5923 cx: &mut ViewContext<Self>,
5924 ) {
5925 self.manipulate_lines(cx, |lines| lines.sort())
5926 }
5927
5928 pub fn sort_lines_case_insensitive(
5929 &mut self,
5930 _: &SortLinesCaseInsensitive,
5931 cx: &mut ViewContext<Self>,
5932 ) {
5933 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
5934 }
5935
5936 pub fn unique_lines_case_insensitive(
5937 &mut self,
5938 _: &UniqueLinesCaseInsensitive,
5939 cx: &mut ViewContext<Self>,
5940 ) {
5941 self.manipulate_lines(cx, |lines| {
5942 let mut seen = HashSet::default();
5943 lines.retain(|line| seen.insert(line.to_lowercase()));
5944 })
5945 }
5946
5947 pub fn unique_lines_case_sensitive(
5948 &mut self,
5949 _: &UniqueLinesCaseSensitive,
5950 cx: &mut ViewContext<Self>,
5951 ) {
5952 self.manipulate_lines(cx, |lines| {
5953 let mut seen = HashSet::default();
5954 lines.retain(|line| seen.insert(*line));
5955 })
5956 }
5957
5958 pub fn revert_file(&mut self, _: &RevertFile, cx: &mut ViewContext<Self>) {
5959 let mut revert_changes = HashMap::default();
5960 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
5961 for hunk in hunks_for_rows(
5962 Some(MultiBufferRow(0)..multi_buffer_snapshot.max_buffer_row()).into_iter(),
5963 &multi_buffer_snapshot,
5964 ) {
5965 Self::prepare_revert_change(&mut revert_changes, &self.buffer(), &hunk, cx);
5966 }
5967 if !revert_changes.is_empty() {
5968 self.transact(cx, |editor, cx| {
5969 editor.revert(revert_changes, cx);
5970 });
5971 }
5972 }
5973
5974 pub fn revert_selected_hunks(&mut self, _: &RevertSelectedHunks, cx: &mut ViewContext<Self>) {
5975 let revert_changes = self.gather_revert_changes(&self.selections.disjoint_anchors(), cx);
5976 if !revert_changes.is_empty() {
5977 self.transact(cx, |editor, cx| {
5978 editor.revert(revert_changes, cx);
5979 });
5980 }
5981 }
5982
5983 pub fn open_active_item_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
5984 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
5985 let project_path = buffer.read(cx).project_path(cx)?;
5986 let project = self.project.as_ref()?.read(cx);
5987 let entry = project.entry_for_path(&project_path, cx)?;
5988 let abs_path = project.absolute_path(&project_path, cx)?;
5989 let parent = if entry.is_symlink {
5990 abs_path.canonicalize().ok()?
5991 } else {
5992 abs_path
5993 }
5994 .parent()?
5995 .to_path_buf();
5996 Some(parent)
5997 }) {
5998 cx.dispatch_action(OpenTerminal { working_directory }.boxed_clone());
5999 }
6000 }
6001
6002 fn gather_revert_changes(
6003 &mut self,
6004 selections: &[Selection<Anchor>],
6005 cx: &mut ViewContext<'_, Editor>,
6006 ) -> HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>> {
6007 let mut revert_changes = HashMap::default();
6008 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
6009 for hunk in hunks_for_selections(&multi_buffer_snapshot, selections) {
6010 Self::prepare_revert_change(&mut revert_changes, self.buffer(), &hunk, cx);
6011 }
6012 revert_changes
6013 }
6014
6015 pub fn prepare_revert_change(
6016 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
6017 multi_buffer: &Model<MultiBuffer>,
6018 hunk: &DiffHunk<MultiBufferRow>,
6019 cx: &AppContext,
6020 ) -> Option<()> {
6021 let buffer = multi_buffer.read(cx).buffer(hunk.buffer_id)?;
6022 let buffer = buffer.read(cx);
6023 let original_text = buffer.diff_base()?.slice(hunk.diff_base_byte_range.clone());
6024 let buffer_snapshot = buffer.snapshot();
6025 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
6026 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
6027 probe
6028 .0
6029 .start
6030 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
6031 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
6032 }) {
6033 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
6034 Some(())
6035 } else {
6036 None
6037 }
6038 }
6039
6040 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
6041 self.manipulate_lines(cx, |lines| lines.reverse())
6042 }
6043
6044 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
6045 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
6046 }
6047
6048 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6049 where
6050 Fn: FnMut(&mut Vec<&str>),
6051 {
6052 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6053 let buffer = self.buffer.read(cx).snapshot(cx);
6054
6055 let mut edits = Vec::new();
6056
6057 let selections = self.selections.all::<Point>(cx);
6058 let mut selections = selections.iter().peekable();
6059 let mut contiguous_row_selections = Vec::new();
6060 let mut new_selections = Vec::new();
6061 let mut added_lines = 0;
6062 let mut removed_lines = 0;
6063
6064 while let Some(selection) = selections.next() {
6065 let (start_row, end_row) = consume_contiguous_rows(
6066 &mut contiguous_row_selections,
6067 selection,
6068 &display_map,
6069 &mut selections,
6070 );
6071
6072 let start_point = Point::new(start_row.0, 0);
6073 let end_point = Point::new(
6074 end_row.previous_row().0,
6075 buffer.line_len(end_row.previous_row()),
6076 );
6077 let text = buffer
6078 .text_for_range(start_point..end_point)
6079 .collect::<String>();
6080
6081 let mut lines = text.split('\n').collect_vec();
6082
6083 let lines_before = lines.len();
6084 callback(&mut lines);
6085 let lines_after = lines.len();
6086
6087 edits.push((start_point..end_point, lines.join("\n")));
6088
6089 // Selections must change based on added and removed line count
6090 let start_row =
6091 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
6092 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
6093 new_selections.push(Selection {
6094 id: selection.id,
6095 start: start_row,
6096 end: end_row,
6097 goal: SelectionGoal::None,
6098 reversed: selection.reversed,
6099 });
6100
6101 if lines_after > lines_before {
6102 added_lines += lines_after - lines_before;
6103 } else if lines_before > lines_after {
6104 removed_lines += lines_before - lines_after;
6105 }
6106 }
6107
6108 self.transact(cx, |this, cx| {
6109 let buffer = this.buffer.update(cx, |buffer, cx| {
6110 buffer.edit(edits, None, cx);
6111 buffer.snapshot(cx)
6112 });
6113
6114 // Recalculate offsets on newly edited buffer
6115 let new_selections = new_selections
6116 .iter()
6117 .map(|s| {
6118 let start_point = Point::new(s.start.0, 0);
6119 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
6120 Selection {
6121 id: s.id,
6122 start: buffer.point_to_offset(start_point),
6123 end: buffer.point_to_offset(end_point),
6124 goal: s.goal,
6125 reversed: s.reversed,
6126 }
6127 })
6128 .collect();
6129
6130 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6131 s.select(new_selections);
6132 });
6133
6134 this.request_autoscroll(Autoscroll::fit(), cx);
6135 });
6136 }
6137
6138 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
6139 self.manipulate_text(cx, |text| text.to_uppercase())
6140 }
6141
6142 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
6143 self.manipulate_text(cx, |text| text.to_lowercase())
6144 }
6145
6146 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
6147 self.manipulate_text(cx, |text| {
6148 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6149 // https://github.com/rutrum/convert-case/issues/16
6150 text.split('\n')
6151 .map(|line| line.to_case(Case::Title))
6152 .join("\n")
6153 })
6154 }
6155
6156 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
6157 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
6158 }
6159
6160 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
6161 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
6162 }
6163
6164 pub fn convert_to_upper_camel_case(
6165 &mut self,
6166 _: &ConvertToUpperCamelCase,
6167 cx: &mut ViewContext<Self>,
6168 ) {
6169 self.manipulate_text(cx, |text| {
6170 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6171 // https://github.com/rutrum/convert-case/issues/16
6172 text.split('\n')
6173 .map(|line| line.to_case(Case::UpperCamel))
6174 .join("\n")
6175 })
6176 }
6177
6178 pub fn convert_to_lower_camel_case(
6179 &mut self,
6180 _: &ConvertToLowerCamelCase,
6181 cx: &mut ViewContext<Self>,
6182 ) {
6183 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
6184 }
6185
6186 pub fn convert_to_opposite_case(
6187 &mut self,
6188 _: &ConvertToOppositeCase,
6189 cx: &mut ViewContext<Self>,
6190 ) {
6191 self.manipulate_text(cx, |text| {
6192 text.chars()
6193 .fold(String::with_capacity(text.len()), |mut t, c| {
6194 if c.is_uppercase() {
6195 t.extend(c.to_lowercase());
6196 } else {
6197 t.extend(c.to_uppercase());
6198 }
6199 t
6200 })
6201 })
6202 }
6203
6204 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6205 where
6206 Fn: FnMut(&str) -> String,
6207 {
6208 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6209 let buffer = self.buffer.read(cx).snapshot(cx);
6210
6211 let mut new_selections = Vec::new();
6212 let mut edits = Vec::new();
6213 let mut selection_adjustment = 0i32;
6214
6215 for selection in self.selections.all::<usize>(cx) {
6216 let selection_is_empty = selection.is_empty();
6217
6218 let (start, end) = if selection_is_empty {
6219 let word_range = movement::surrounding_word(
6220 &display_map,
6221 selection.start.to_display_point(&display_map),
6222 );
6223 let start = word_range.start.to_offset(&display_map, Bias::Left);
6224 let end = word_range.end.to_offset(&display_map, Bias::Left);
6225 (start, end)
6226 } else {
6227 (selection.start, selection.end)
6228 };
6229
6230 let text = buffer.text_for_range(start..end).collect::<String>();
6231 let old_length = text.len() as i32;
6232 let text = callback(&text);
6233
6234 new_selections.push(Selection {
6235 start: (start as i32 - selection_adjustment) as usize,
6236 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
6237 goal: SelectionGoal::None,
6238 ..selection
6239 });
6240
6241 selection_adjustment += old_length - text.len() as i32;
6242
6243 edits.push((start..end, text));
6244 }
6245
6246 self.transact(cx, |this, cx| {
6247 this.buffer.update(cx, |buffer, cx| {
6248 buffer.edit(edits, None, cx);
6249 });
6250
6251 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6252 s.select(new_selections);
6253 });
6254
6255 this.request_autoscroll(Autoscroll::fit(), cx);
6256 });
6257 }
6258
6259 pub fn duplicate_line(&mut self, upwards: bool, cx: &mut ViewContext<Self>) {
6260 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6261 let buffer = &display_map.buffer_snapshot;
6262 let selections = self.selections.all::<Point>(cx);
6263
6264 let mut edits = Vec::new();
6265 let mut selections_iter = selections.iter().peekable();
6266 while let Some(selection) = selections_iter.next() {
6267 // Avoid duplicating the same lines twice.
6268 let mut rows = selection.spanned_rows(false, &display_map);
6269
6270 while let Some(next_selection) = selections_iter.peek() {
6271 let next_rows = next_selection.spanned_rows(false, &display_map);
6272 if next_rows.start < rows.end {
6273 rows.end = next_rows.end;
6274 selections_iter.next().unwrap();
6275 } else {
6276 break;
6277 }
6278 }
6279
6280 // Copy the text from the selected row region and splice it either at the start
6281 // or end of the region.
6282 let start = Point::new(rows.start.0, 0);
6283 let end = Point::new(
6284 rows.end.previous_row().0,
6285 buffer.line_len(rows.end.previous_row()),
6286 );
6287 let text = buffer
6288 .text_for_range(start..end)
6289 .chain(Some("\n"))
6290 .collect::<String>();
6291 let insert_location = if upwards {
6292 Point::new(rows.end.0, 0)
6293 } else {
6294 start
6295 };
6296 edits.push((insert_location..insert_location, text));
6297 }
6298
6299 self.transact(cx, |this, cx| {
6300 this.buffer.update(cx, |buffer, cx| {
6301 buffer.edit(edits, None, cx);
6302 });
6303
6304 this.request_autoscroll(Autoscroll::fit(), cx);
6305 });
6306 }
6307
6308 pub fn duplicate_line_up(&mut self, _: &DuplicateLineUp, cx: &mut ViewContext<Self>) {
6309 self.duplicate_line(true, cx);
6310 }
6311
6312 pub fn duplicate_line_down(&mut self, _: &DuplicateLineDown, cx: &mut ViewContext<Self>) {
6313 self.duplicate_line(false, cx);
6314 }
6315
6316 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
6317 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6318 let buffer = self.buffer.read(cx).snapshot(cx);
6319
6320 let mut edits = Vec::new();
6321 let mut unfold_ranges = Vec::new();
6322 let mut refold_ranges = Vec::new();
6323
6324 let selections = self.selections.all::<Point>(cx);
6325 let mut selections = selections.iter().peekable();
6326 let mut contiguous_row_selections = Vec::new();
6327 let mut new_selections = Vec::new();
6328
6329 while let Some(selection) = selections.next() {
6330 // Find all the selections that span a contiguous row range
6331 let (start_row, end_row) = consume_contiguous_rows(
6332 &mut contiguous_row_selections,
6333 selection,
6334 &display_map,
6335 &mut selections,
6336 );
6337
6338 // Move the text spanned by the row range to be before the line preceding the row range
6339 if start_row.0 > 0 {
6340 let range_to_move = Point::new(
6341 start_row.previous_row().0,
6342 buffer.line_len(start_row.previous_row()),
6343 )
6344 ..Point::new(
6345 end_row.previous_row().0,
6346 buffer.line_len(end_row.previous_row()),
6347 );
6348 let insertion_point = display_map
6349 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
6350 .0;
6351
6352 // Don't move lines across excerpts
6353 if buffer
6354 .excerpt_boundaries_in_range((
6355 Bound::Excluded(insertion_point),
6356 Bound::Included(range_to_move.end),
6357 ))
6358 .next()
6359 .is_none()
6360 {
6361 let text = buffer
6362 .text_for_range(range_to_move.clone())
6363 .flat_map(|s| s.chars())
6364 .skip(1)
6365 .chain(['\n'])
6366 .collect::<String>();
6367
6368 edits.push((
6369 buffer.anchor_after(range_to_move.start)
6370 ..buffer.anchor_before(range_to_move.end),
6371 String::new(),
6372 ));
6373 let insertion_anchor = buffer.anchor_after(insertion_point);
6374 edits.push((insertion_anchor..insertion_anchor, text));
6375
6376 let row_delta = range_to_move.start.row - insertion_point.row + 1;
6377
6378 // Move selections up
6379 new_selections.extend(contiguous_row_selections.drain(..).map(
6380 |mut selection| {
6381 selection.start.row -= row_delta;
6382 selection.end.row -= row_delta;
6383 selection
6384 },
6385 ));
6386
6387 // Move folds up
6388 unfold_ranges.push(range_to_move.clone());
6389 for fold in display_map.folds_in_range(
6390 buffer.anchor_before(range_to_move.start)
6391 ..buffer.anchor_after(range_to_move.end),
6392 ) {
6393 let mut start = fold.range.start.to_point(&buffer);
6394 let mut end = fold.range.end.to_point(&buffer);
6395 start.row -= row_delta;
6396 end.row -= row_delta;
6397 refold_ranges.push((start..end, fold.placeholder.clone()));
6398 }
6399 }
6400 }
6401
6402 // If we didn't move line(s), preserve the existing selections
6403 new_selections.append(&mut contiguous_row_selections);
6404 }
6405
6406 self.transact(cx, |this, cx| {
6407 this.unfold_ranges(unfold_ranges, true, true, cx);
6408 this.buffer.update(cx, |buffer, cx| {
6409 for (range, text) in edits {
6410 buffer.edit([(range, text)], None, cx);
6411 }
6412 });
6413 this.fold_ranges(refold_ranges, true, cx);
6414 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6415 s.select(new_selections);
6416 })
6417 });
6418 }
6419
6420 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
6421 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6422 let buffer = self.buffer.read(cx).snapshot(cx);
6423
6424 let mut edits = Vec::new();
6425 let mut unfold_ranges = Vec::new();
6426 let mut refold_ranges = Vec::new();
6427
6428 let selections = self.selections.all::<Point>(cx);
6429 let mut selections = selections.iter().peekable();
6430 let mut contiguous_row_selections = Vec::new();
6431 let mut new_selections = Vec::new();
6432
6433 while let Some(selection) = selections.next() {
6434 // Find all the selections that span a contiguous row range
6435 let (start_row, end_row) = consume_contiguous_rows(
6436 &mut contiguous_row_selections,
6437 selection,
6438 &display_map,
6439 &mut selections,
6440 );
6441
6442 // Move the text spanned by the row range to be after the last line of the row range
6443 if end_row.0 <= buffer.max_point().row {
6444 let range_to_move =
6445 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
6446 let insertion_point = display_map
6447 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
6448 .0;
6449
6450 // Don't move lines across excerpt boundaries
6451 if buffer
6452 .excerpt_boundaries_in_range((
6453 Bound::Excluded(range_to_move.start),
6454 Bound::Included(insertion_point),
6455 ))
6456 .next()
6457 .is_none()
6458 {
6459 let mut text = String::from("\n");
6460 text.extend(buffer.text_for_range(range_to_move.clone()));
6461 text.pop(); // Drop trailing newline
6462 edits.push((
6463 buffer.anchor_after(range_to_move.start)
6464 ..buffer.anchor_before(range_to_move.end),
6465 String::new(),
6466 ));
6467 let insertion_anchor = buffer.anchor_after(insertion_point);
6468 edits.push((insertion_anchor..insertion_anchor, text));
6469
6470 let row_delta = insertion_point.row - range_to_move.end.row + 1;
6471
6472 // Move selections down
6473 new_selections.extend(contiguous_row_selections.drain(..).map(
6474 |mut selection| {
6475 selection.start.row += row_delta;
6476 selection.end.row += row_delta;
6477 selection
6478 },
6479 ));
6480
6481 // Move folds down
6482 unfold_ranges.push(range_to_move.clone());
6483 for fold in display_map.folds_in_range(
6484 buffer.anchor_before(range_to_move.start)
6485 ..buffer.anchor_after(range_to_move.end),
6486 ) {
6487 let mut start = fold.range.start.to_point(&buffer);
6488 let mut end = fold.range.end.to_point(&buffer);
6489 start.row += row_delta;
6490 end.row += row_delta;
6491 refold_ranges.push((start..end, fold.placeholder.clone()));
6492 }
6493 }
6494 }
6495
6496 // If we didn't move line(s), preserve the existing selections
6497 new_selections.append(&mut contiguous_row_selections);
6498 }
6499
6500 self.transact(cx, |this, cx| {
6501 this.unfold_ranges(unfold_ranges, true, true, cx);
6502 this.buffer.update(cx, |buffer, cx| {
6503 for (range, text) in edits {
6504 buffer.edit([(range, text)], None, cx);
6505 }
6506 });
6507 this.fold_ranges(refold_ranges, true, cx);
6508 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
6509 });
6510 }
6511
6512 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
6513 let text_layout_details = &self.text_layout_details(cx);
6514 self.transact(cx, |this, cx| {
6515 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6516 let mut edits: Vec<(Range<usize>, String)> = Default::default();
6517 let line_mode = s.line_mode;
6518 s.move_with(|display_map, selection| {
6519 if !selection.is_empty() || line_mode {
6520 return;
6521 }
6522
6523 let mut head = selection.head();
6524 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
6525 if head.column() == display_map.line_len(head.row()) {
6526 transpose_offset = display_map
6527 .buffer_snapshot
6528 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6529 }
6530
6531 if transpose_offset == 0 {
6532 return;
6533 }
6534
6535 *head.column_mut() += 1;
6536 head = display_map.clip_point(head, Bias::Right);
6537 let goal = SelectionGoal::HorizontalPosition(
6538 display_map
6539 .x_for_display_point(head, &text_layout_details)
6540 .into(),
6541 );
6542 selection.collapse_to(head, goal);
6543
6544 let transpose_start = display_map
6545 .buffer_snapshot
6546 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6547 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
6548 let transpose_end = display_map
6549 .buffer_snapshot
6550 .clip_offset(transpose_offset + 1, Bias::Right);
6551 if let Some(ch) =
6552 display_map.buffer_snapshot.chars_at(transpose_start).next()
6553 {
6554 edits.push((transpose_start..transpose_offset, String::new()));
6555 edits.push((transpose_end..transpose_end, ch.to_string()));
6556 }
6557 }
6558 });
6559 edits
6560 });
6561 this.buffer
6562 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
6563 let selections = this.selections.all::<usize>(cx);
6564 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6565 s.select(selections);
6566 });
6567 });
6568 }
6569
6570 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
6571 let mut text = String::new();
6572 let buffer = self.buffer.read(cx).snapshot(cx);
6573 let mut selections = self.selections.all::<Point>(cx);
6574 let mut clipboard_selections = Vec::with_capacity(selections.len());
6575 {
6576 let max_point = buffer.max_point();
6577 let mut is_first = true;
6578 for selection in &mut selections {
6579 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6580 if is_entire_line {
6581 selection.start = Point::new(selection.start.row, 0);
6582 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
6583 selection.goal = SelectionGoal::None;
6584 }
6585 if is_first {
6586 is_first = false;
6587 } else {
6588 text += "\n";
6589 }
6590 let mut len = 0;
6591 for chunk in buffer.text_for_range(selection.start..selection.end) {
6592 text.push_str(chunk);
6593 len += chunk.len();
6594 }
6595 clipboard_selections.push(ClipboardSelection {
6596 len,
6597 is_entire_line,
6598 first_line_indent: buffer
6599 .indent_size_for_line(MultiBufferRow(selection.start.row))
6600 .len,
6601 });
6602 }
6603 }
6604
6605 self.transact(cx, |this, cx| {
6606 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6607 s.select(selections);
6608 });
6609 this.insert("", cx);
6610 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
6611 text,
6612 clipboard_selections,
6613 ));
6614 });
6615 }
6616
6617 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
6618 let selections = self.selections.all::<Point>(cx);
6619 let buffer = self.buffer.read(cx).read(cx);
6620 let mut text = String::new();
6621
6622 let mut clipboard_selections = Vec::with_capacity(selections.len());
6623 {
6624 let max_point = buffer.max_point();
6625 let mut is_first = true;
6626 for selection in selections.iter() {
6627 let mut start = selection.start;
6628 let mut end = selection.end;
6629 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6630 if is_entire_line {
6631 start = Point::new(start.row, 0);
6632 end = cmp::min(max_point, Point::new(end.row + 1, 0));
6633 }
6634 if is_first {
6635 is_first = false;
6636 } else {
6637 text += "\n";
6638 }
6639 let mut len = 0;
6640 for chunk in buffer.text_for_range(start..end) {
6641 text.push_str(chunk);
6642 len += chunk.len();
6643 }
6644 clipboard_selections.push(ClipboardSelection {
6645 len,
6646 is_entire_line,
6647 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
6648 });
6649 }
6650 }
6651
6652 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
6653 text,
6654 clipboard_selections,
6655 ));
6656 }
6657
6658 pub fn do_paste(
6659 &mut self,
6660 text: &String,
6661 clipboard_selections: Option<Vec<ClipboardSelection>>,
6662 handle_entire_lines: bool,
6663 cx: &mut ViewContext<Self>,
6664 ) {
6665 if self.read_only(cx) {
6666 return;
6667 }
6668
6669 let clipboard_text = Cow::Borrowed(text);
6670
6671 self.transact(cx, |this, cx| {
6672 if let Some(mut clipboard_selections) = clipboard_selections {
6673 let old_selections = this.selections.all::<usize>(cx);
6674 let all_selections_were_entire_line =
6675 clipboard_selections.iter().all(|s| s.is_entire_line);
6676 let first_selection_indent_column =
6677 clipboard_selections.first().map(|s| s.first_line_indent);
6678 if clipboard_selections.len() != old_selections.len() {
6679 clipboard_selections.drain(..);
6680 }
6681
6682 this.buffer.update(cx, |buffer, cx| {
6683 let snapshot = buffer.read(cx);
6684 let mut start_offset = 0;
6685 let mut edits = Vec::new();
6686 let mut original_indent_columns = Vec::new();
6687 for (ix, selection) in old_selections.iter().enumerate() {
6688 let to_insert;
6689 let entire_line;
6690 let original_indent_column;
6691 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
6692 let end_offset = start_offset + clipboard_selection.len;
6693 to_insert = &clipboard_text[start_offset..end_offset];
6694 entire_line = clipboard_selection.is_entire_line;
6695 start_offset = end_offset + 1;
6696 original_indent_column = Some(clipboard_selection.first_line_indent);
6697 } else {
6698 to_insert = clipboard_text.as_str();
6699 entire_line = all_selections_were_entire_line;
6700 original_indent_column = first_selection_indent_column
6701 }
6702
6703 // If the corresponding selection was empty when this slice of the
6704 // clipboard text was written, then the entire line containing the
6705 // selection was copied. If this selection is also currently empty,
6706 // then paste the line before the current line of the buffer.
6707 let range = if selection.is_empty() && handle_entire_lines && entire_line {
6708 let column = selection.start.to_point(&snapshot).column as usize;
6709 let line_start = selection.start - column;
6710 line_start..line_start
6711 } else {
6712 selection.range()
6713 };
6714
6715 edits.push((range, to_insert));
6716 original_indent_columns.extend(original_indent_column);
6717 }
6718 drop(snapshot);
6719
6720 buffer.edit(
6721 edits,
6722 Some(AutoindentMode::Block {
6723 original_indent_columns,
6724 }),
6725 cx,
6726 );
6727 });
6728
6729 let selections = this.selections.all::<usize>(cx);
6730 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6731 } else {
6732 this.insert(&clipboard_text, cx);
6733 }
6734 });
6735 }
6736
6737 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
6738 if let Some(item) = cx.read_from_clipboard() {
6739 let entries = item.entries();
6740
6741 match entries.first() {
6742 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
6743 // of all the pasted entries.
6744 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
6745 .do_paste(
6746 clipboard_string.text(),
6747 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
6748 true,
6749 cx,
6750 ),
6751 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, cx),
6752 }
6753 }
6754 }
6755
6756 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
6757 if self.read_only(cx) {
6758 return;
6759 }
6760
6761 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
6762 if let Some((selections, _)) =
6763 self.selection_history.transaction(transaction_id).cloned()
6764 {
6765 self.change_selections(None, cx, |s| {
6766 s.select_anchors(selections.to_vec());
6767 });
6768 }
6769 self.request_autoscroll(Autoscroll::fit(), cx);
6770 self.unmark_text(cx);
6771 self.refresh_inline_completion(true, cx);
6772 cx.emit(EditorEvent::Edited { transaction_id });
6773 cx.emit(EditorEvent::TransactionUndone { transaction_id });
6774 }
6775 }
6776
6777 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
6778 if self.read_only(cx) {
6779 return;
6780 }
6781
6782 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
6783 if let Some((_, Some(selections))) =
6784 self.selection_history.transaction(transaction_id).cloned()
6785 {
6786 self.change_selections(None, cx, |s| {
6787 s.select_anchors(selections.to_vec());
6788 });
6789 }
6790 self.request_autoscroll(Autoscroll::fit(), cx);
6791 self.unmark_text(cx);
6792 self.refresh_inline_completion(true, cx);
6793 cx.emit(EditorEvent::Edited { transaction_id });
6794 }
6795 }
6796
6797 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
6798 self.buffer
6799 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
6800 }
6801
6802 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut ViewContext<Self>) {
6803 self.buffer
6804 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
6805 }
6806
6807 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
6808 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6809 let line_mode = s.line_mode;
6810 s.move_with(|map, selection| {
6811 let cursor = if selection.is_empty() && !line_mode {
6812 movement::left(map, selection.start)
6813 } else {
6814 selection.start
6815 };
6816 selection.collapse_to(cursor, SelectionGoal::None);
6817 });
6818 })
6819 }
6820
6821 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
6822 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6823 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
6824 })
6825 }
6826
6827 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
6828 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6829 let line_mode = s.line_mode;
6830 s.move_with(|map, selection| {
6831 let cursor = if selection.is_empty() && !line_mode {
6832 movement::right(map, selection.end)
6833 } else {
6834 selection.end
6835 };
6836 selection.collapse_to(cursor, SelectionGoal::None)
6837 });
6838 })
6839 }
6840
6841 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
6842 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6843 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
6844 })
6845 }
6846
6847 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
6848 if self.take_rename(true, cx).is_some() {
6849 return;
6850 }
6851
6852 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6853 cx.propagate();
6854 return;
6855 }
6856
6857 let text_layout_details = &self.text_layout_details(cx);
6858 let selection_count = self.selections.count();
6859 let first_selection = self.selections.first_anchor();
6860
6861 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6862 let line_mode = s.line_mode;
6863 s.move_with(|map, selection| {
6864 if !selection.is_empty() && !line_mode {
6865 selection.goal = SelectionGoal::None;
6866 }
6867 let (cursor, goal) = movement::up(
6868 map,
6869 selection.start,
6870 selection.goal,
6871 false,
6872 &text_layout_details,
6873 );
6874 selection.collapse_to(cursor, goal);
6875 });
6876 });
6877
6878 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
6879 {
6880 cx.propagate();
6881 }
6882 }
6883
6884 pub fn move_up_by_lines(&mut self, action: &MoveUpByLines, cx: &mut ViewContext<Self>) {
6885 if self.take_rename(true, cx).is_some() {
6886 return;
6887 }
6888
6889 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6890 cx.propagate();
6891 return;
6892 }
6893
6894 let text_layout_details = &self.text_layout_details(cx);
6895
6896 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6897 let line_mode = s.line_mode;
6898 s.move_with(|map, selection| {
6899 if !selection.is_empty() && !line_mode {
6900 selection.goal = SelectionGoal::None;
6901 }
6902 let (cursor, goal) = movement::up_by_rows(
6903 map,
6904 selection.start,
6905 action.lines,
6906 selection.goal,
6907 false,
6908 &text_layout_details,
6909 );
6910 selection.collapse_to(cursor, goal);
6911 });
6912 })
6913 }
6914
6915 pub fn move_down_by_lines(&mut self, action: &MoveDownByLines, cx: &mut ViewContext<Self>) {
6916 if self.take_rename(true, cx).is_some() {
6917 return;
6918 }
6919
6920 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6921 cx.propagate();
6922 return;
6923 }
6924
6925 let text_layout_details = &self.text_layout_details(cx);
6926
6927 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6928 let line_mode = s.line_mode;
6929 s.move_with(|map, selection| {
6930 if !selection.is_empty() && !line_mode {
6931 selection.goal = SelectionGoal::None;
6932 }
6933 let (cursor, goal) = movement::down_by_rows(
6934 map,
6935 selection.start,
6936 action.lines,
6937 selection.goal,
6938 false,
6939 &text_layout_details,
6940 );
6941 selection.collapse_to(cursor, goal);
6942 });
6943 })
6944 }
6945
6946 pub fn select_down_by_lines(&mut self, action: &SelectDownByLines, cx: &mut ViewContext<Self>) {
6947 let text_layout_details = &self.text_layout_details(cx);
6948 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6949 s.move_heads_with(|map, head, goal| {
6950 movement::down_by_rows(map, head, action.lines, goal, false, &text_layout_details)
6951 })
6952 })
6953 }
6954
6955 pub fn select_up_by_lines(&mut self, action: &SelectUpByLines, cx: &mut ViewContext<Self>) {
6956 let text_layout_details = &self.text_layout_details(cx);
6957 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6958 s.move_heads_with(|map, head, goal| {
6959 movement::up_by_rows(map, head, action.lines, goal, false, &text_layout_details)
6960 })
6961 })
6962 }
6963
6964 pub fn select_page_up(&mut self, _: &SelectPageUp, cx: &mut ViewContext<Self>) {
6965 let Some(row_count) = self.visible_row_count() else {
6966 return;
6967 };
6968
6969 let text_layout_details = &self.text_layout_details(cx);
6970
6971 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6972 s.move_heads_with(|map, head, goal| {
6973 movement::up_by_rows(map, head, row_count, goal, false, &text_layout_details)
6974 })
6975 })
6976 }
6977
6978 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
6979 if self.take_rename(true, cx).is_some() {
6980 return;
6981 }
6982
6983 if self
6984 .context_menu
6985 .write()
6986 .as_mut()
6987 .map(|menu| menu.select_first(self.project.as_ref(), cx))
6988 .unwrap_or(false)
6989 {
6990 return;
6991 }
6992
6993 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6994 cx.propagate();
6995 return;
6996 }
6997
6998 let Some(row_count) = self.visible_row_count() else {
6999 return;
7000 };
7001
7002 let autoscroll = if action.center_cursor {
7003 Autoscroll::center()
7004 } else {
7005 Autoscroll::fit()
7006 };
7007
7008 let text_layout_details = &self.text_layout_details(cx);
7009
7010 self.change_selections(Some(autoscroll), cx, |s| {
7011 let line_mode = s.line_mode;
7012 s.move_with(|map, selection| {
7013 if !selection.is_empty() && !line_mode {
7014 selection.goal = SelectionGoal::None;
7015 }
7016 let (cursor, goal) = movement::up_by_rows(
7017 map,
7018 selection.end,
7019 row_count,
7020 selection.goal,
7021 false,
7022 &text_layout_details,
7023 );
7024 selection.collapse_to(cursor, goal);
7025 });
7026 });
7027 }
7028
7029 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
7030 let text_layout_details = &self.text_layout_details(cx);
7031 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7032 s.move_heads_with(|map, head, goal| {
7033 movement::up(map, head, goal, false, &text_layout_details)
7034 })
7035 })
7036 }
7037
7038 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
7039 self.take_rename(true, cx);
7040
7041 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7042 cx.propagate();
7043 return;
7044 }
7045
7046 let text_layout_details = &self.text_layout_details(cx);
7047 let selection_count = self.selections.count();
7048 let first_selection = self.selections.first_anchor();
7049
7050 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7051 let line_mode = s.line_mode;
7052 s.move_with(|map, selection| {
7053 if !selection.is_empty() && !line_mode {
7054 selection.goal = SelectionGoal::None;
7055 }
7056 let (cursor, goal) = movement::down(
7057 map,
7058 selection.end,
7059 selection.goal,
7060 false,
7061 &text_layout_details,
7062 );
7063 selection.collapse_to(cursor, goal);
7064 });
7065 });
7066
7067 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7068 {
7069 cx.propagate();
7070 }
7071 }
7072
7073 pub fn select_page_down(&mut self, _: &SelectPageDown, cx: &mut ViewContext<Self>) {
7074 let Some(row_count) = self.visible_row_count() else {
7075 return;
7076 };
7077
7078 let text_layout_details = &self.text_layout_details(cx);
7079
7080 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7081 s.move_heads_with(|map, head, goal| {
7082 movement::down_by_rows(map, head, row_count, goal, false, &text_layout_details)
7083 })
7084 })
7085 }
7086
7087 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
7088 if self.take_rename(true, cx).is_some() {
7089 return;
7090 }
7091
7092 if self
7093 .context_menu
7094 .write()
7095 .as_mut()
7096 .map(|menu| menu.select_last(self.project.as_ref(), cx))
7097 .unwrap_or(false)
7098 {
7099 return;
7100 }
7101
7102 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7103 cx.propagate();
7104 return;
7105 }
7106
7107 let Some(row_count) = self.visible_row_count() else {
7108 return;
7109 };
7110
7111 let autoscroll = if action.center_cursor {
7112 Autoscroll::center()
7113 } else {
7114 Autoscroll::fit()
7115 };
7116
7117 let text_layout_details = &self.text_layout_details(cx);
7118 self.change_selections(Some(autoscroll), cx, |s| {
7119 let line_mode = s.line_mode;
7120 s.move_with(|map, selection| {
7121 if !selection.is_empty() && !line_mode {
7122 selection.goal = SelectionGoal::None;
7123 }
7124 let (cursor, goal) = movement::down_by_rows(
7125 map,
7126 selection.end,
7127 row_count,
7128 selection.goal,
7129 false,
7130 &text_layout_details,
7131 );
7132 selection.collapse_to(cursor, goal);
7133 });
7134 });
7135 }
7136
7137 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
7138 let text_layout_details = &self.text_layout_details(cx);
7139 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7140 s.move_heads_with(|map, head, goal| {
7141 movement::down(map, head, goal, false, &text_layout_details)
7142 })
7143 });
7144 }
7145
7146 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
7147 if let Some(context_menu) = self.context_menu.write().as_mut() {
7148 context_menu.select_first(self.project.as_ref(), cx);
7149 }
7150 }
7151
7152 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
7153 if let Some(context_menu) = self.context_menu.write().as_mut() {
7154 context_menu.select_prev(self.project.as_ref(), cx);
7155 }
7156 }
7157
7158 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
7159 if let Some(context_menu) = self.context_menu.write().as_mut() {
7160 context_menu.select_next(self.project.as_ref(), cx);
7161 }
7162 }
7163
7164 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
7165 if let Some(context_menu) = self.context_menu.write().as_mut() {
7166 context_menu.select_last(self.project.as_ref(), cx);
7167 }
7168 }
7169
7170 pub fn move_to_previous_word_start(
7171 &mut self,
7172 _: &MoveToPreviousWordStart,
7173 cx: &mut ViewContext<Self>,
7174 ) {
7175 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7176 s.move_cursors_with(|map, head, _| {
7177 (
7178 movement::previous_word_start(map, head),
7179 SelectionGoal::None,
7180 )
7181 });
7182 })
7183 }
7184
7185 pub fn move_to_previous_subword_start(
7186 &mut self,
7187 _: &MoveToPreviousSubwordStart,
7188 cx: &mut ViewContext<Self>,
7189 ) {
7190 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7191 s.move_cursors_with(|map, head, _| {
7192 (
7193 movement::previous_subword_start(map, head),
7194 SelectionGoal::None,
7195 )
7196 });
7197 })
7198 }
7199
7200 pub fn select_to_previous_word_start(
7201 &mut self,
7202 _: &SelectToPreviousWordStart,
7203 cx: &mut ViewContext<Self>,
7204 ) {
7205 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7206 s.move_heads_with(|map, head, _| {
7207 (
7208 movement::previous_word_start(map, head),
7209 SelectionGoal::None,
7210 )
7211 });
7212 })
7213 }
7214
7215 pub fn select_to_previous_subword_start(
7216 &mut self,
7217 _: &SelectToPreviousSubwordStart,
7218 cx: &mut ViewContext<Self>,
7219 ) {
7220 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7221 s.move_heads_with(|map, head, _| {
7222 (
7223 movement::previous_subword_start(map, head),
7224 SelectionGoal::None,
7225 )
7226 });
7227 })
7228 }
7229
7230 pub fn delete_to_previous_word_start(
7231 &mut self,
7232 _: &DeleteToPreviousWordStart,
7233 cx: &mut ViewContext<Self>,
7234 ) {
7235 self.transact(cx, |this, cx| {
7236 this.select_autoclose_pair(cx);
7237 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7238 let line_mode = s.line_mode;
7239 s.move_with(|map, selection| {
7240 if selection.is_empty() && !line_mode {
7241 let cursor = movement::previous_word_start(map, selection.head());
7242 selection.set_head(cursor, SelectionGoal::None);
7243 }
7244 });
7245 });
7246 this.insert("", cx);
7247 });
7248 }
7249
7250 pub fn delete_to_previous_subword_start(
7251 &mut self,
7252 _: &DeleteToPreviousSubwordStart,
7253 cx: &mut ViewContext<Self>,
7254 ) {
7255 self.transact(cx, |this, cx| {
7256 this.select_autoclose_pair(cx);
7257 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7258 let line_mode = s.line_mode;
7259 s.move_with(|map, selection| {
7260 if selection.is_empty() && !line_mode {
7261 let cursor = movement::previous_subword_start(map, selection.head());
7262 selection.set_head(cursor, SelectionGoal::None);
7263 }
7264 });
7265 });
7266 this.insert("", cx);
7267 });
7268 }
7269
7270 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
7271 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7272 s.move_cursors_with(|map, head, _| {
7273 (movement::next_word_end(map, head), SelectionGoal::None)
7274 });
7275 })
7276 }
7277
7278 pub fn move_to_next_subword_end(
7279 &mut self,
7280 _: &MoveToNextSubwordEnd,
7281 cx: &mut ViewContext<Self>,
7282 ) {
7283 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7284 s.move_cursors_with(|map, head, _| {
7285 (movement::next_subword_end(map, head), SelectionGoal::None)
7286 });
7287 })
7288 }
7289
7290 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
7291 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7292 s.move_heads_with(|map, head, _| {
7293 (movement::next_word_end(map, head), SelectionGoal::None)
7294 });
7295 })
7296 }
7297
7298 pub fn select_to_next_subword_end(
7299 &mut self,
7300 _: &SelectToNextSubwordEnd,
7301 cx: &mut ViewContext<Self>,
7302 ) {
7303 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7304 s.move_heads_with(|map, head, _| {
7305 (movement::next_subword_end(map, head), SelectionGoal::None)
7306 });
7307 })
7308 }
7309
7310 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
7311 self.transact(cx, |this, cx| {
7312 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7313 let line_mode = s.line_mode;
7314 s.move_with(|map, selection| {
7315 if selection.is_empty() && !line_mode {
7316 let cursor = movement::next_word_end(map, selection.head());
7317 selection.set_head(cursor, SelectionGoal::None);
7318 }
7319 });
7320 });
7321 this.insert("", cx);
7322 });
7323 }
7324
7325 pub fn delete_to_next_subword_end(
7326 &mut self,
7327 _: &DeleteToNextSubwordEnd,
7328 cx: &mut ViewContext<Self>,
7329 ) {
7330 self.transact(cx, |this, cx| {
7331 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7332 s.move_with(|map, selection| {
7333 if selection.is_empty() {
7334 let cursor = movement::next_subword_end(map, selection.head());
7335 selection.set_head(cursor, SelectionGoal::None);
7336 }
7337 });
7338 });
7339 this.insert("", cx);
7340 });
7341 }
7342
7343 pub fn move_to_beginning_of_line(
7344 &mut self,
7345 action: &MoveToBeginningOfLine,
7346 cx: &mut ViewContext<Self>,
7347 ) {
7348 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7349 s.move_cursors_with(|map, head, _| {
7350 (
7351 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7352 SelectionGoal::None,
7353 )
7354 });
7355 })
7356 }
7357
7358 pub fn select_to_beginning_of_line(
7359 &mut self,
7360 action: &SelectToBeginningOfLine,
7361 cx: &mut ViewContext<Self>,
7362 ) {
7363 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7364 s.move_heads_with(|map, head, _| {
7365 (
7366 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7367 SelectionGoal::None,
7368 )
7369 });
7370 });
7371 }
7372
7373 pub fn delete_to_beginning_of_line(
7374 &mut self,
7375 _: &DeleteToBeginningOfLine,
7376 cx: &mut ViewContext<Self>,
7377 ) {
7378 self.transact(cx, |this, cx| {
7379 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7380 s.move_with(|_, selection| {
7381 selection.reversed = true;
7382 });
7383 });
7384
7385 this.select_to_beginning_of_line(
7386 &SelectToBeginningOfLine {
7387 stop_at_soft_wraps: false,
7388 },
7389 cx,
7390 );
7391 this.backspace(&Backspace, cx);
7392 });
7393 }
7394
7395 pub fn move_to_end_of_line(&mut self, action: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
7396 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7397 s.move_cursors_with(|map, head, _| {
7398 (
7399 movement::line_end(map, head, action.stop_at_soft_wraps),
7400 SelectionGoal::None,
7401 )
7402 });
7403 })
7404 }
7405
7406 pub fn select_to_end_of_line(
7407 &mut self,
7408 action: &SelectToEndOfLine,
7409 cx: &mut ViewContext<Self>,
7410 ) {
7411 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7412 s.move_heads_with(|map, head, _| {
7413 (
7414 movement::line_end(map, head, action.stop_at_soft_wraps),
7415 SelectionGoal::None,
7416 )
7417 });
7418 })
7419 }
7420
7421 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
7422 self.transact(cx, |this, cx| {
7423 this.select_to_end_of_line(
7424 &SelectToEndOfLine {
7425 stop_at_soft_wraps: false,
7426 },
7427 cx,
7428 );
7429 this.delete(&Delete, cx);
7430 });
7431 }
7432
7433 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
7434 self.transact(cx, |this, cx| {
7435 this.select_to_end_of_line(
7436 &SelectToEndOfLine {
7437 stop_at_soft_wraps: false,
7438 },
7439 cx,
7440 );
7441 this.cut(&Cut, cx);
7442 });
7443 }
7444
7445 pub fn move_to_start_of_paragraph(
7446 &mut self,
7447 _: &MoveToStartOfParagraph,
7448 cx: &mut ViewContext<Self>,
7449 ) {
7450 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7451 cx.propagate();
7452 return;
7453 }
7454
7455 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7456 s.move_with(|map, selection| {
7457 selection.collapse_to(
7458 movement::start_of_paragraph(map, selection.head(), 1),
7459 SelectionGoal::None,
7460 )
7461 });
7462 })
7463 }
7464
7465 pub fn move_to_end_of_paragraph(
7466 &mut self,
7467 _: &MoveToEndOfParagraph,
7468 cx: &mut ViewContext<Self>,
7469 ) {
7470 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7471 cx.propagate();
7472 return;
7473 }
7474
7475 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7476 s.move_with(|map, selection| {
7477 selection.collapse_to(
7478 movement::end_of_paragraph(map, selection.head(), 1),
7479 SelectionGoal::None,
7480 )
7481 });
7482 })
7483 }
7484
7485 pub fn select_to_start_of_paragraph(
7486 &mut self,
7487 _: &SelectToStartOfParagraph,
7488 cx: &mut ViewContext<Self>,
7489 ) {
7490 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7491 cx.propagate();
7492 return;
7493 }
7494
7495 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7496 s.move_heads_with(|map, head, _| {
7497 (
7498 movement::start_of_paragraph(map, head, 1),
7499 SelectionGoal::None,
7500 )
7501 });
7502 })
7503 }
7504
7505 pub fn select_to_end_of_paragraph(
7506 &mut self,
7507 _: &SelectToEndOfParagraph,
7508 cx: &mut ViewContext<Self>,
7509 ) {
7510 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7511 cx.propagate();
7512 return;
7513 }
7514
7515 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7516 s.move_heads_with(|map, head, _| {
7517 (
7518 movement::end_of_paragraph(map, head, 1),
7519 SelectionGoal::None,
7520 )
7521 });
7522 })
7523 }
7524
7525 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
7526 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7527 cx.propagate();
7528 return;
7529 }
7530
7531 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7532 s.select_ranges(vec![0..0]);
7533 });
7534 }
7535
7536 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
7537 let mut selection = self.selections.last::<Point>(cx);
7538 selection.set_head(Point::zero(), SelectionGoal::None);
7539
7540 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7541 s.select(vec![selection]);
7542 });
7543 }
7544
7545 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
7546 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7547 cx.propagate();
7548 return;
7549 }
7550
7551 let cursor = self.buffer.read(cx).read(cx).len();
7552 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7553 s.select_ranges(vec![cursor..cursor])
7554 });
7555 }
7556
7557 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
7558 self.nav_history = nav_history;
7559 }
7560
7561 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
7562 self.nav_history.as_ref()
7563 }
7564
7565 fn push_to_nav_history(
7566 &mut self,
7567 cursor_anchor: Anchor,
7568 new_position: Option<Point>,
7569 cx: &mut ViewContext<Self>,
7570 ) {
7571 if let Some(nav_history) = self.nav_history.as_mut() {
7572 let buffer = self.buffer.read(cx).read(cx);
7573 let cursor_position = cursor_anchor.to_point(&buffer);
7574 let scroll_state = self.scroll_manager.anchor();
7575 let scroll_top_row = scroll_state.top_row(&buffer);
7576 drop(buffer);
7577
7578 if let Some(new_position) = new_position {
7579 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
7580 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
7581 return;
7582 }
7583 }
7584
7585 nav_history.push(
7586 Some(NavigationData {
7587 cursor_anchor,
7588 cursor_position,
7589 scroll_anchor: scroll_state,
7590 scroll_top_row,
7591 }),
7592 cx,
7593 );
7594 }
7595 }
7596
7597 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
7598 let buffer = self.buffer.read(cx).snapshot(cx);
7599 let mut selection = self.selections.first::<usize>(cx);
7600 selection.set_head(buffer.len(), SelectionGoal::None);
7601 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7602 s.select(vec![selection]);
7603 });
7604 }
7605
7606 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
7607 let end = self.buffer.read(cx).read(cx).len();
7608 self.change_selections(None, cx, |s| {
7609 s.select_ranges(vec![0..end]);
7610 });
7611 }
7612
7613 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
7614 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7615 let mut selections = self.selections.all::<Point>(cx);
7616 let max_point = display_map.buffer_snapshot.max_point();
7617 for selection in &mut selections {
7618 let rows = selection.spanned_rows(true, &display_map);
7619 selection.start = Point::new(rows.start.0, 0);
7620 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
7621 selection.reversed = false;
7622 }
7623 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7624 s.select(selections);
7625 });
7626 }
7627
7628 pub fn split_selection_into_lines(
7629 &mut self,
7630 _: &SplitSelectionIntoLines,
7631 cx: &mut ViewContext<Self>,
7632 ) {
7633 let mut to_unfold = Vec::new();
7634 let mut new_selection_ranges = Vec::new();
7635 {
7636 let selections = self.selections.all::<Point>(cx);
7637 let buffer = self.buffer.read(cx).read(cx);
7638 for selection in selections {
7639 for row in selection.start.row..selection.end.row {
7640 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
7641 new_selection_ranges.push(cursor..cursor);
7642 }
7643 new_selection_ranges.push(selection.end..selection.end);
7644 to_unfold.push(selection.start..selection.end);
7645 }
7646 }
7647 self.unfold_ranges(to_unfold, true, true, cx);
7648 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7649 s.select_ranges(new_selection_ranges);
7650 });
7651 }
7652
7653 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
7654 self.add_selection(true, cx);
7655 }
7656
7657 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
7658 self.add_selection(false, cx);
7659 }
7660
7661 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
7662 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7663 let mut selections = self.selections.all::<Point>(cx);
7664 let text_layout_details = self.text_layout_details(cx);
7665 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
7666 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
7667 let range = oldest_selection.display_range(&display_map).sorted();
7668
7669 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
7670 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
7671 let positions = start_x.min(end_x)..start_x.max(end_x);
7672
7673 selections.clear();
7674 let mut stack = Vec::new();
7675 for row in range.start.row().0..=range.end.row().0 {
7676 if let Some(selection) = self.selections.build_columnar_selection(
7677 &display_map,
7678 DisplayRow(row),
7679 &positions,
7680 oldest_selection.reversed,
7681 &text_layout_details,
7682 ) {
7683 stack.push(selection.id);
7684 selections.push(selection);
7685 }
7686 }
7687
7688 if above {
7689 stack.reverse();
7690 }
7691
7692 AddSelectionsState { above, stack }
7693 });
7694
7695 let last_added_selection = *state.stack.last().unwrap();
7696 let mut new_selections = Vec::new();
7697 if above == state.above {
7698 let end_row = if above {
7699 DisplayRow(0)
7700 } else {
7701 display_map.max_point().row()
7702 };
7703
7704 'outer: for selection in selections {
7705 if selection.id == last_added_selection {
7706 let range = selection.display_range(&display_map).sorted();
7707 debug_assert_eq!(range.start.row(), range.end.row());
7708 let mut row = range.start.row();
7709 let positions =
7710 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
7711 px(start)..px(end)
7712 } else {
7713 let start_x =
7714 display_map.x_for_display_point(range.start, &text_layout_details);
7715 let end_x =
7716 display_map.x_for_display_point(range.end, &text_layout_details);
7717 start_x.min(end_x)..start_x.max(end_x)
7718 };
7719
7720 while row != end_row {
7721 if above {
7722 row.0 -= 1;
7723 } else {
7724 row.0 += 1;
7725 }
7726
7727 if let Some(new_selection) = self.selections.build_columnar_selection(
7728 &display_map,
7729 row,
7730 &positions,
7731 selection.reversed,
7732 &text_layout_details,
7733 ) {
7734 state.stack.push(new_selection.id);
7735 if above {
7736 new_selections.push(new_selection);
7737 new_selections.push(selection);
7738 } else {
7739 new_selections.push(selection);
7740 new_selections.push(new_selection);
7741 }
7742
7743 continue 'outer;
7744 }
7745 }
7746 }
7747
7748 new_selections.push(selection);
7749 }
7750 } else {
7751 new_selections = selections;
7752 new_selections.retain(|s| s.id != last_added_selection);
7753 state.stack.pop();
7754 }
7755
7756 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7757 s.select(new_selections);
7758 });
7759 if state.stack.len() > 1 {
7760 self.add_selections_state = Some(state);
7761 }
7762 }
7763
7764 pub fn select_next_match_internal(
7765 &mut self,
7766 display_map: &DisplaySnapshot,
7767 replace_newest: bool,
7768 autoscroll: Option<Autoscroll>,
7769 cx: &mut ViewContext<Self>,
7770 ) -> Result<()> {
7771 fn select_next_match_ranges(
7772 this: &mut Editor,
7773 range: Range<usize>,
7774 replace_newest: bool,
7775 auto_scroll: Option<Autoscroll>,
7776 cx: &mut ViewContext<Editor>,
7777 ) {
7778 this.unfold_ranges([range.clone()], false, true, cx);
7779 this.change_selections(auto_scroll, cx, |s| {
7780 if replace_newest {
7781 s.delete(s.newest_anchor().id);
7782 }
7783 s.insert_range(range.clone());
7784 });
7785 }
7786
7787 let buffer = &display_map.buffer_snapshot;
7788 let mut selections = self.selections.all::<usize>(cx);
7789 if let Some(mut select_next_state) = self.select_next_state.take() {
7790 let query = &select_next_state.query;
7791 if !select_next_state.done {
7792 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
7793 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
7794 let mut next_selected_range = None;
7795
7796 let bytes_after_last_selection =
7797 buffer.bytes_in_range(last_selection.end..buffer.len());
7798 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
7799 let query_matches = query
7800 .stream_find_iter(bytes_after_last_selection)
7801 .map(|result| (last_selection.end, result))
7802 .chain(
7803 query
7804 .stream_find_iter(bytes_before_first_selection)
7805 .map(|result| (0, result)),
7806 );
7807
7808 for (start_offset, query_match) in query_matches {
7809 let query_match = query_match.unwrap(); // can only fail due to I/O
7810 let offset_range =
7811 start_offset + query_match.start()..start_offset + query_match.end();
7812 let display_range = offset_range.start.to_display_point(&display_map)
7813 ..offset_range.end.to_display_point(&display_map);
7814
7815 if !select_next_state.wordwise
7816 || (!movement::is_inside_word(&display_map, display_range.start)
7817 && !movement::is_inside_word(&display_map, display_range.end))
7818 {
7819 // TODO: This is n^2, because we might check all the selections
7820 if !selections
7821 .iter()
7822 .any(|selection| selection.range().overlaps(&offset_range))
7823 {
7824 next_selected_range = Some(offset_range);
7825 break;
7826 }
7827 }
7828 }
7829
7830 if let Some(next_selected_range) = next_selected_range {
7831 select_next_match_ranges(
7832 self,
7833 next_selected_range,
7834 replace_newest,
7835 autoscroll,
7836 cx,
7837 );
7838 } else {
7839 select_next_state.done = true;
7840 }
7841 }
7842
7843 self.select_next_state = Some(select_next_state);
7844 } else {
7845 let mut only_carets = true;
7846 let mut same_text_selected = true;
7847 let mut selected_text = None;
7848
7849 let mut selections_iter = selections.iter().peekable();
7850 while let Some(selection) = selections_iter.next() {
7851 if selection.start != selection.end {
7852 only_carets = false;
7853 }
7854
7855 if same_text_selected {
7856 if selected_text.is_none() {
7857 selected_text =
7858 Some(buffer.text_for_range(selection.range()).collect::<String>());
7859 }
7860
7861 if let Some(next_selection) = selections_iter.peek() {
7862 if next_selection.range().len() == selection.range().len() {
7863 let next_selected_text = buffer
7864 .text_for_range(next_selection.range())
7865 .collect::<String>();
7866 if Some(next_selected_text) != selected_text {
7867 same_text_selected = false;
7868 selected_text = None;
7869 }
7870 } else {
7871 same_text_selected = false;
7872 selected_text = None;
7873 }
7874 }
7875 }
7876 }
7877
7878 if only_carets {
7879 for selection in &mut selections {
7880 let word_range = movement::surrounding_word(
7881 &display_map,
7882 selection.start.to_display_point(&display_map),
7883 );
7884 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
7885 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
7886 selection.goal = SelectionGoal::None;
7887 selection.reversed = false;
7888 select_next_match_ranges(
7889 self,
7890 selection.start..selection.end,
7891 replace_newest,
7892 autoscroll,
7893 cx,
7894 );
7895 }
7896
7897 if selections.len() == 1 {
7898 let selection = selections
7899 .last()
7900 .expect("ensured that there's only one selection");
7901 let query = buffer
7902 .text_for_range(selection.start..selection.end)
7903 .collect::<String>();
7904 let is_empty = query.is_empty();
7905 let select_state = SelectNextState {
7906 query: AhoCorasick::new(&[query])?,
7907 wordwise: true,
7908 done: is_empty,
7909 };
7910 self.select_next_state = Some(select_state);
7911 } else {
7912 self.select_next_state = None;
7913 }
7914 } else if let Some(selected_text) = selected_text {
7915 self.select_next_state = Some(SelectNextState {
7916 query: AhoCorasick::new(&[selected_text])?,
7917 wordwise: false,
7918 done: false,
7919 });
7920 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
7921 }
7922 }
7923 Ok(())
7924 }
7925
7926 pub fn select_all_matches(
7927 &mut self,
7928 _action: &SelectAllMatches,
7929 cx: &mut ViewContext<Self>,
7930 ) -> Result<()> {
7931 self.push_to_selection_history();
7932 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7933
7934 self.select_next_match_internal(&display_map, false, None, cx)?;
7935 let Some(select_next_state) = self.select_next_state.as_mut() else {
7936 return Ok(());
7937 };
7938 if select_next_state.done {
7939 return Ok(());
7940 }
7941
7942 let mut new_selections = self.selections.all::<usize>(cx);
7943
7944 let buffer = &display_map.buffer_snapshot;
7945 let query_matches = select_next_state
7946 .query
7947 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
7948
7949 for query_match in query_matches {
7950 let query_match = query_match.unwrap(); // can only fail due to I/O
7951 let offset_range = query_match.start()..query_match.end();
7952 let display_range = offset_range.start.to_display_point(&display_map)
7953 ..offset_range.end.to_display_point(&display_map);
7954
7955 if !select_next_state.wordwise
7956 || (!movement::is_inside_word(&display_map, display_range.start)
7957 && !movement::is_inside_word(&display_map, display_range.end))
7958 {
7959 self.selections.change_with(cx, |selections| {
7960 new_selections.push(Selection {
7961 id: selections.new_selection_id(),
7962 start: offset_range.start,
7963 end: offset_range.end,
7964 reversed: false,
7965 goal: SelectionGoal::None,
7966 });
7967 });
7968 }
7969 }
7970
7971 new_selections.sort_by_key(|selection| selection.start);
7972 let mut ix = 0;
7973 while ix + 1 < new_selections.len() {
7974 let current_selection = &new_selections[ix];
7975 let next_selection = &new_selections[ix + 1];
7976 if current_selection.range().overlaps(&next_selection.range()) {
7977 if current_selection.id < next_selection.id {
7978 new_selections.remove(ix + 1);
7979 } else {
7980 new_selections.remove(ix);
7981 }
7982 } else {
7983 ix += 1;
7984 }
7985 }
7986
7987 select_next_state.done = true;
7988 self.unfold_ranges(
7989 new_selections.iter().map(|selection| selection.range()),
7990 false,
7991 false,
7992 cx,
7993 );
7994 self.change_selections(Some(Autoscroll::fit()), cx, |selections| {
7995 selections.select(new_selections)
7996 });
7997
7998 Ok(())
7999 }
8000
8001 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
8002 self.push_to_selection_history();
8003 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8004 self.select_next_match_internal(
8005 &display_map,
8006 action.replace_newest,
8007 Some(Autoscroll::newest()),
8008 cx,
8009 )?;
8010 Ok(())
8011 }
8012
8013 pub fn select_previous(
8014 &mut self,
8015 action: &SelectPrevious,
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 let buffer = &display_map.buffer_snapshot;
8021 let mut selections = self.selections.all::<usize>(cx);
8022 if let Some(mut select_prev_state) = self.select_prev_state.take() {
8023 let query = &select_prev_state.query;
8024 if !select_prev_state.done {
8025 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
8026 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
8027 let mut next_selected_range = None;
8028 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
8029 let bytes_before_last_selection =
8030 buffer.reversed_bytes_in_range(0..last_selection.start);
8031 let bytes_after_first_selection =
8032 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
8033 let query_matches = query
8034 .stream_find_iter(bytes_before_last_selection)
8035 .map(|result| (last_selection.start, result))
8036 .chain(
8037 query
8038 .stream_find_iter(bytes_after_first_selection)
8039 .map(|result| (buffer.len(), result)),
8040 );
8041 for (end_offset, query_match) in query_matches {
8042 let query_match = query_match.unwrap(); // can only fail due to I/O
8043 let offset_range =
8044 end_offset - query_match.end()..end_offset - query_match.start();
8045 let display_range = offset_range.start.to_display_point(&display_map)
8046 ..offset_range.end.to_display_point(&display_map);
8047
8048 if !select_prev_state.wordwise
8049 || (!movement::is_inside_word(&display_map, display_range.start)
8050 && !movement::is_inside_word(&display_map, display_range.end))
8051 {
8052 next_selected_range = Some(offset_range);
8053 break;
8054 }
8055 }
8056
8057 if let Some(next_selected_range) = next_selected_range {
8058 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
8059 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8060 if action.replace_newest {
8061 s.delete(s.newest_anchor().id);
8062 }
8063 s.insert_range(next_selected_range);
8064 });
8065 } else {
8066 select_prev_state.done = true;
8067 }
8068 }
8069
8070 self.select_prev_state = Some(select_prev_state);
8071 } else {
8072 let mut only_carets = true;
8073 let mut same_text_selected = true;
8074 let mut selected_text = None;
8075
8076 let mut selections_iter = selections.iter().peekable();
8077 while let Some(selection) = selections_iter.next() {
8078 if selection.start != selection.end {
8079 only_carets = false;
8080 }
8081
8082 if same_text_selected {
8083 if selected_text.is_none() {
8084 selected_text =
8085 Some(buffer.text_for_range(selection.range()).collect::<String>());
8086 }
8087
8088 if let Some(next_selection) = selections_iter.peek() {
8089 if next_selection.range().len() == selection.range().len() {
8090 let next_selected_text = buffer
8091 .text_for_range(next_selection.range())
8092 .collect::<String>();
8093 if Some(next_selected_text) != selected_text {
8094 same_text_selected = false;
8095 selected_text = None;
8096 }
8097 } else {
8098 same_text_selected = false;
8099 selected_text = None;
8100 }
8101 }
8102 }
8103 }
8104
8105 if only_carets {
8106 for selection in &mut selections {
8107 let word_range = movement::surrounding_word(
8108 &display_map,
8109 selection.start.to_display_point(&display_map),
8110 );
8111 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
8112 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
8113 selection.goal = SelectionGoal::None;
8114 selection.reversed = false;
8115 }
8116 if selections.len() == 1 {
8117 let selection = selections
8118 .last()
8119 .expect("ensured that there's only one selection");
8120 let query = buffer
8121 .text_for_range(selection.start..selection.end)
8122 .collect::<String>();
8123 let is_empty = query.is_empty();
8124 let select_state = SelectNextState {
8125 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
8126 wordwise: true,
8127 done: is_empty,
8128 };
8129 self.select_prev_state = Some(select_state);
8130 } else {
8131 self.select_prev_state = None;
8132 }
8133
8134 self.unfold_ranges(
8135 selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
8136 false,
8137 true,
8138 cx,
8139 );
8140 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8141 s.select(selections);
8142 });
8143 } else if let Some(selected_text) = selected_text {
8144 self.select_prev_state = Some(SelectNextState {
8145 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
8146 wordwise: false,
8147 done: false,
8148 });
8149 self.select_previous(action, cx)?;
8150 }
8151 }
8152 Ok(())
8153 }
8154
8155 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
8156 let text_layout_details = &self.text_layout_details(cx);
8157 self.transact(cx, |this, cx| {
8158 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
8159 let mut edits = Vec::new();
8160 let mut selection_edit_ranges = Vec::new();
8161 let mut last_toggled_row = None;
8162 let snapshot = this.buffer.read(cx).read(cx);
8163 let empty_str: Arc<str> = Arc::default();
8164 let mut suffixes_inserted = Vec::new();
8165
8166 fn comment_prefix_range(
8167 snapshot: &MultiBufferSnapshot,
8168 row: MultiBufferRow,
8169 comment_prefix: &str,
8170 comment_prefix_whitespace: &str,
8171 ) -> Range<Point> {
8172 let start = Point::new(row.0, snapshot.indent_size_for_line(row).len);
8173
8174 let mut line_bytes = snapshot
8175 .bytes_in_range(start..snapshot.max_point())
8176 .flatten()
8177 .copied();
8178
8179 // If this line currently begins with the line comment prefix, then record
8180 // the range containing the prefix.
8181 if line_bytes
8182 .by_ref()
8183 .take(comment_prefix.len())
8184 .eq(comment_prefix.bytes())
8185 {
8186 // Include any whitespace that matches the comment prefix.
8187 let matching_whitespace_len = line_bytes
8188 .zip(comment_prefix_whitespace.bytes())
8189 .take_while(|(a, b)| a == b)
8190 .count() as u32;
8191 let end = Point::new(
8192 start.row,
8193 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
8194 );
8195 start..end
8196 } else {
8197 start..start
8198 }
8199 }
8200
8201 fn comment_suffix_range(
8202 snapshot: &MultiBufferSnapshot,
8203 row: MultiBufferRow,
8204 comment_suffix: &str,
8205 comment_suffix_has_leading_space: bool,
8206 ) -> Range<Point> {
8207 let end = Point::new(row.0, snapshot.line_len(row));
8208 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
8209
8210 let mut line_end_bytes = snapshot
8211 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
8212 .flatten()
8213 .copied();
8214
8215 let leading_space_len = if suffix_start_column > 0
8216 && line_end_bytes.next() == Some(b' ')
8217 && comment_suffix_has_leading_space
8218 {
8219 1
8220 } else {
8221 0
8222 };
8223
8224 // If this line currently begins with the line comment prefix, then record
8225 // the range containing the prefix.
8226 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
8227 let start = Point::new(end.row, suffix_start_column - leading_space_len);
8228 start..end
8229 } else {
8230 end..end
8231 }
8232 }
8233
8234 // TODO: Handle selections that cross excerpts
8235 for selection in &mut selections {
8236 let start_column = snapshot
8237 .indent_size_for_line(MultiBufferRow(selection.start.row))
8238 .len;
8239 let language = if let Some(language) =
8240 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
8241 {
8242 language
8243 } else {
8244 continue;
8245 };
8246
8247 selection_edit_ranges.clear();
8248
8249 // If multiple selections contain a given row, avoid processing that
8250 // row more than once.
8251 let mut start_row = MultiBufferRow(selection.start.row);
8252 if last_toggled_row == Some(start_row) {
8253 start_row = start_row.next_row();
8254 }
8255 let end_row =
8256 if selection.end.row > selection.start.row && selection.end.column == 0 {
8257 MultiBufferRow(selection.end.row - 1)
8258 } else {
8259 MultiBufferRow(selection.end.row)
8260 };
8261 last_toggled_row = Some(end_row);
8262
8263 if start_row > end_row {
8264 continue;
8265 }
8266
8267 // If the language has line comments, toggle those.
8268 let full_comment_prefixes = language.line_comment_prefixes();
8269 if !full_comment_prefixes.is_empty() {
8270 let first_prefix = full_comment_prefixes
8271 .first()
8272 .expect("prefixes is non-empty");
8273 let prefix_trimmed_lengths = full_comment_prefixes
8274 .iter()
8275 .map(|p| p.trim_end_matches(' ').len())
8276 .collect::<SmallVec<[usize; 4]>>();
8277
8278 let mut all_selection_lines_are_comments = true;
8279
8280 for row in start_row.0..=end_row.0 {
8281 let row = MultiBufferRow(row);
8282 if start_row < end_row && snapshot.is_line_blank(row) {
8283 continue;
8284 }
8285
8286 let prefix_range = full_comment_prefixes
8287 .iter()
8288 .zip(prefix_trimmed_lengths.iter().copied())
8289 .map(|(prefix, trimmed_prefix_len)| {
8290 comment_prefix_range(
8291 snapshot.deref(),
8292 row,
8293 &prefix[..trimmed_prefix_len],
8294 &prefix[trimmed_prefix_len..],
8295 )
8296 })
8297 .max_by_key(|range| range.end.column - range.start.column)
8298 .expect("prefixes is non-empty");
8299
8300 if prefix_range.is_empty() {
8301 all_selection_lines_are_comments = false;
8302 }
8303
8304 selection_edit_ranges.push(prefix_range);
8305 }
8306
8307 if all_selection_lines_are_comments {
8308 edits.extend(
8309 selection_edit_ranges
8310 .iter()
8311 .cloned()
8312 .map(|range| (range, empty_str.clone())),
8313 );
8314 } else {
8315 let min_column = selection_edit_ranges
8316 .iter()
8317 .map(|range| range.start.column)
8318 .min()
8319 .unwrap_or(0);
8320 edits.extend(selection_edit_ranges.iter().map(|range| {
8321 let position = Point::new(range.start.row, min_column);
8322 (position..position, first_prefix.clone())
8323 }));
8324 }
8325 } else if let Some((full_comment_prefix, comment_suffix)) =
8326 language.block_comment_delimiters()
8327 {
8328 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
8329 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
8330 let prefix_range = comment_prefix_range(
8331 snapshot.deref(),
8332 start_row,
8333 comment_prefix,
8334 comment_prefix_whitespace,
8335 );
8336 let suffix_range = comment_suffix_range(
8337 snapshot.deref(),
8338 end_row,
8339 comment_suffix.trim_start_matches(' '),
8340 comment_suffix.starts_with(' '),
8341 );
8342
8343 if prefix_range.is_empty() || suffix_range.is_empty() {
8344 edits.push((
8345 prefix_range.start..prefix_range.start,
8346 full_comment_prefix.clone(),
8347 ));
8348 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
8349 suffixes_inserted.push((end_row, comment_suffix.len()));
8350 } else {
8351 edits.push((prefix_range, empty_str.clone()));
8352 edits.push((suffix_range, empty_str.clone()));
8353 }
8354 } else {
8355 continue;
8356 }
8357 }
8358
8359 drop(snapshot);
8360 this.buffer.update(cx, |buffer, cx| {
8361 buffer.edit(edits, None, cx);
8362 });
8363
8364 // Adjust selections so that they end before any comment suffixes that
8365 // were inserted.
8366 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
8367 let mut selections = this.selections.all::<Point>(cx);
8368 let snapshot = this.buffer.read(cx).read(cx);
8369 for selection in &mut selections {
8370 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
8371 match row.cmp(&MultiBufferRow(selection.end.row)) {
8372 Ordering::Less => {
8373 suffixes_inserted.next();
8374 continue;
8375 }
8376 Ordering::Greater => break,
8377 Ordering::Equal => {
8378 if selection.end.column == snapshot.line_len(row) {
8379 if selection.is_empty() {
8380 selection.start.column -= suffix_len as u32;
8381 }
8382 selection.end.column -= suffix_len as u32;
8383 }
8384 break;
8385 }
8386 }
8387 }
8388 }
8389
8390 drop(snapshot);
8391 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
8392
8393 let selections = this.selections.all::<Point>(cx);
8394 let selections_on_single_row = selections.windows(2).all(|selections| {
8395 selections[0].start.row == selections[1].start.row
8396 && selections[0].end.row == selections[1].end.row
8397 && selections[0].start.row == selections[0].end.row
8398 });
8399 let selections_selecting = selections
8400 .iter()
8401 .any(|selection| selection.start != selection.end);
8402 let advance_downwards = action.advance_downwards
8403 && selections_on_single_row
8404 && !selections_selecting
8405 && !matches!(this.mode, EditorMode::SingleLine { .. });
8406
8407 if advance_downwards {
8408 let snapshot = this.buffer.read(cx).snapshot(cx);
8409
8410 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8411 s.move_cursors_with(|display_snapshot, display_point, _| {
8412 let mut point = display_point.to_point(display_snapshot);
8413 point.row += 1;
8414 point = snapshot.clip_point(point, Bias::Left);
8415 let display_point = point.to_display_point(display_snapshot);
8416 let goal = SelectionGoal::HorizontalPosition(
8417 display_snapshot
8418 .x_for_display_point(display_point, &text_layout_details)
8419 .into(),
8420 );
8421 (display_point, goal)
8422 })
8423 });
8424 }
8425 });
8426 }
8427
8428 pub fn select_enclosing_symbol(
8429 &mut self,
8430 _: &SelectEnclosingSymbol,
8431 cx: &mut ViewContext<Self>,
8432 ) {
8433 let buffer = self.buffer.read(cx).snapshot(cx);
8434 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8435
8436 fn update_selection(
8437 selection: &Selection<usize>,
8438 buffer_snap: &MultiBufferSnapshot,
8439 ) -> Option<Selection<usize>> {
8440 let cursor = selection.head();
8441 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
8442 for symbol in symbols.iter().rev() {
8443 let start = symbol.range.start.to_offset(&buffer_snap);
8444 let end = symbol.range.end.to_offset(&buffer_snap);
8445 let new_range = start..end;
8446 if start < selection.start || end > selection.end {
8447 return Some(Selection {
8448 id: selection.id,
8449 start: new_range.start,
8450 end: new_range.end,
8451 goal: SelectionGoal::None,
8452 reversed: selection.reversed,
8453 });
8454 }
8455 }
8456 None
8457 }
8458
8459 let mut selected_larger_symbol = false;
8460 let new_selections = old_selections
8461 .iter()
8462 .map(|selection| match update_selection(selection, &buffer) {
8463 Some(new_selection) => {
8464 if new_selection.range() != selection.range() {
8465 selected_larger_symbol = true;
8466 }
8467 new_selection
8468 }
8469 None => selection.clone(),
8470 })
8471 .collect::<Vec<_>>();
8472
8473 if selected_larger_symbol {
8474 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8475 s.select(new_selections);
8476 });
8477 }
8478 }
8479
8480 pub fn select_larger_syntax_node(
8481 &mut self,
8482 _: &SelectLargerSyntaxNode,
8483 cx: &mut ViewContext<Self>,
8484 ) {
8485 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8486 let buffer = self.buffer.read(cx).snapshot(cx);
8487 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8488
8489 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8490 let mut selected_larger_node = false;
8491 let new_selections = old_selections
8492 .iter()
8493 .map(|selection| {
8494 let old_range = selection.start..selection.end;
8495 let mut new_range = old_range.clone();
8496 while let Some(containing_range) =
8497 buffer.range_for_syntax_ancestor(new_range.clone())
8498 {
8499 new_range = containing_range;
8500 if !display_map.intersects_fold(new_range.start)
8501 && !display_map.intersects_fold(new_range.end)
8502 {
8503 break;
8504 }
8505 }
8506
8507 selected_larger_node |= new_range != old_range;
8508 Selection {
8509 id: selection.id,
8510 start: new_range.start,
8511 end: new_range.end,
8512 goal: SelectionGoal::None,
8513 reversed: selection.reversed,
8514 }
8515 })
8516 .collect::<Vec<_>>();
8517
8518 if selected_larger_node {
8519 stack.push(old_selections);
8520 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8521 s.select(new_selections);
8522 });
8523 }
8524 self.select_larger_syntax_node_stack = stack;
8525 }
8526
8527 pub fn select_smaller_syntax_node(
8528 &mut self,
8529 _: &SelectSmallerSyntaxNode,
8530 cx: &mut ViewContext<Self>,
8531 ) {
8532 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8533 if let Some(selections) = stack.pop() {
8534 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8535 s.select(selections.to_vec());
8536 });
8537 }
8538 self.select_larger_syntax_node_stack = stack;
8539 }
8540
8541 fn refresh_runnables(&mut self, cx: &mut ViewContext<Self>) -> Task<()> {
8542 if !EditorSettings::get_global(cx).gutter.runnables {
8543 self.clear_tasks();
8544 return Task::ready(());
8545 }
8546 let project = self.project.clone();
8547 cx.spawn(|this, mut cx| async move {
8548 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
8549 this.display_map.update(cx, |map, cx| map.snapshot(cx))
8550 }) else {
8551 return;
8552 };
8553
8554 let Some(project) = project else {
8555 return;
8556 };
8557
8558 let hide_runnables = project
8559 .update(&mut cx, |project, cx| {
8560 // Do not display any test indicators in non-dev server remote projects.
8561 project.is_remote() && project.ssh_connection_string(cx).is_none()
8562 })
8563 .unwrap_or(true);
8564 if hide_runnables {
8565 return;
8566 }
8567 let new_rows =
8568 cx.background_executor()
8569 .spawn({
8570 let snapshot = display_snapshot.clone();
8571 async move {
8572 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
8573 }
8574 })
8575 .await;
8576 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
8577
8578 this.update(&mut cx, |this, _| {
8579 this.clear_tasks();
8580 for (key, value) in rows {
8581 this.insert_tasks(key, value);
8582 }
8583 })
8584 .ok();
8585 })
8586 }
8587 fn fetch_runnable_ranges(
8588 snapshot: &DisplaySnapshot,
8589 range: Range<Anchor>,
8590 ) -> Vec<language::RunnableRange> {
8591 snapshot.buffer_snapshot.runnable_ranges(range).collect()
8592 }
8593
8594 fn runnable_rows(
8595 project: Model<Project>,
8596 snapshot: DisplaySnapshot,
8597 runnable_ranges: Vec<RunnableRange>,
8598 mut cx: AsyncWindowContext,
8599 ) -> Vec<((BufferId, u32), RunnableTasks)> {
8600 runnable_ranges
8601 .into_iter()
8602 .filter_map(|mut runnable| {
8603 let tasks = cx
8604 .update(|cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
8605 .ok()?;
8606 if tasks.is_empty() {
8607 return None;
8608 }
8609
8610 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
8611
8612 let row = snapshot
8613 .buffer_snapshot
8614 .buffer_line_for_row(MultiBufferRow(point.row))?
8615 .1
8616 .start
8617 .row;
8618
8619 let context_range =
8620 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
8621 Some((
8622 (runnable.buffer_id, row),
8623 RunnableTasks {
8624 templates: tasks,
8625 offset: MultiBufferOffset(runnable.run_range.start),
8626 context_range,
8627 column: point.column,
8628 extra_variables: runnable.extra_captures,
8629 },
8630 ))
8631 })
8632 .collect()
8633 }
8634
8635 fn templates_with_tags(
8636 project: &Model<Project>,
8637 runnable: &mut Runnable,
8638 cx: &WindowContext<'_>,
8639 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
8640 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
8641 let (worktree_id, file) = project
8642 .buffer_for_id(runnable.buffer, cx)
8643 .and_then(|buffer| buffer.read(cx).file())
8644 .map(|file| (WorktreeId::from_usize(file.worktree_id()), file.clone()))
8645 .unzip();
8646
8647 (project.task_inventory().clone(), worktree_id, file)
8648 });
8649
8650 let inventory = inventory.read(cx);
8651 let tags = mem::take(&mut runnable.tags);
8652 let mut tags: Vec<_> = tags
8653 .into_iter()
8654 .flat_map(|tag| {
8655 let tag = tag.0.clone();
8656 inventory
8657 .list_tasks(
8658 file.clone(),
8659 Some(runnable.language.clone()),
8660 worktree_id,
8661 cx,
8662 )
8663 .into_iter()
8664 .filter(move |(_, template)| {
8665 template.tags.iter().any(|source_tag| source_tag == &tag)
8666 })
8667 })
8668 .sorted_by_key(|(kind, _)| kind.to_owned())
8669 .collect();
8670 if let Some((leading_tag_source, _)) = tags.first() {
8671 // Strongest source wins; if we have worktree tag binding, prefer that to
8672 // global and language bindings;
8673 // if we have a global binding, prefer that to language binding.
8674 let first_mismatch = tags
8675 .iter()
8676 .position(|(tag_source, _)| tag_source != leading_tag_source);
8677 if let Some(index) = first_mismatch {
8678 tags.truncate(index);
8679 }
8680 }
8681
8682 tags
8683 }
8684
8685 pub fn move_to_enclosing_bracket(
8686 &mut self,
8687 _: &MoveToEnclosingBracket,
8688 cx: &mut ViewContext<Self>,
8689 ) {
8690 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8691 s.move_offsets_with(|snapshot, selection| {
8692 let Some(enclosing_bracket_ranges) =
8693 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
8694 else {
8695 return;
8696 };
8697
8698 let mut best_length = usize::MAX;
8699 let mut best_inside = false;
8700 let mut best_in_bracket_range = false;
8701 let mut best_destination = None;
8702 for (open, close) in enclosing_bracket_ranges {
8703 let close = close.to_inclusive();
8704 let length = close.end() - open.start;
8705 let inside = selection.start >= open.end && selection.end <= *close.start();
8706 let in_bracket_range = open.to_inclusive().contains(&selection.head())
8707 || close.contains(&selection.head());
8708
8709 // If best is next to a bracket and current isn't, skip
8710 if !in_bracket_range && best_in_bracket_range {
8711 continue;
8712 }
8713
8714 // Prefer smaller lengths unless best is inside and current isn't
8715 if length > best_length && (best_inside || !inside) {
8716 continue;
8717 }
8718
8719 best_length = length;
8720 best_inside = inside;
8721 best_in_bracket_range = in_bracket_range;
8722 best_destination = Some(
8723 if close.contains(&selection.start) && close.contains(&selection.end) {
8724 if inside {
8725 open.end
8726 } else {
8727 open.start
8728 }
8729 } else {
8730 if inside {
8731 *close.start()
8732 } else {
8733 *close.end()
8734 }
8735 },
8736 );
8737 }
8738
8739 if let Some(destination) = best_destination {
8740 selection.collapse_to(destination, SelectionGoal::None);
8741 }
8742 })
8743 });
8744 }
8745
8746 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
8747 self.end_selection(cx);
8748 self.selection_history.mode = SelectionHistoryMode::Undoing;
8749 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
8750 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8751 self.select_next_state = entry.select_next_state;
8752 self.select_prev_state = entry.select_prev_state;
8753 self.add_selections_state = entry.add_selections_state;
8754 self.request_autoscroll(Autoscroll::newest(), cx);
8755 }
8756 self.selection_history.mode = SelectionHistoryMode::Normal;
8757 }
8758
8759 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
8760 self.end_selection(cx);
8761 self.selection_history.mode = SelectionHistoryMode::Redoing;
8762 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
8763 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8764 self.select_next_state = entry.select_next_state;
8765 self.select_prev_state = entry.select_prev_state;
8766 self.add_selections_state = entry.add_selections_state;
8767 self.request_autoscroll(Autoscroll::newest(), cx);
8768 }
8769 self.selection_history.mode = SelectionHistoryMode::Normal;
8770 }
8771
8772 pub fn expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
8773 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
8774 }
8775
8776 pub fn expand_excerpts_down(
8777 &mut self,
8778 action: &ExpandExcerptsDown,
8779 cx: &mut ViewContext<Self>,
8780 ) {
8781 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
8782 }
8783
8784 pub fn expand_excerpts_up(&mut self, action: &ExpandExcerptsUp, cx: &mut ViewContext<Self>) {
8785 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
8786 }
8787
8788 pub fn expand_excerpts_for_direction(
8789 &mut self,
8790 lines: u32,
8791 direction: ExpandExcerptDirection,
8792 cx: &mut ViewContext<Self>,
8793 ) {
8794 let selections = self.selections.disjoint_anchors();
8795
8796 let lines = if lines == 0 {
8797 EditorSettings::get_global(cx).expand_excerpt_lines
8798 } else {
8799 lines
8800 };
8801
8802 self.buffer.update(cx, |buffer, cx| {
8803 buffer.expand_excerpts(
8804 selections
8805 .into_iter()
8806 .map(|selection| selection.head().excerpt_id)
8807 .dedup(),
8808 lines,
8809 direction,
8810 cx,
8811 )
8812 })
8813 }
8814
8815 pub fn expand_excerpt(
8816 &mut self,
8817 excerpt: ExcerptId,
8818 direction: ExpandExcerptDirection,
8819 cx: &mut ViewContext<Self>,
8820 ) {
8821 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
8822 self.buffer.update(cx, |buffer, cx| {
8823 buffer.expand_excerpts([excerpt], lines, direction, cx)
8824 })
8825 }
8826
8827 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
8828 self.go_to_diagnostic_impl(Direction::Next, cx)
8829 }
8830
8831 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
8832 self.go_to_diagnostic_impl(Direction::Prev, cx)
8833 }
8834
8835 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
8836 let buffer = self.buffer.read(cx).snapshot(cx);
8837 let selection = self.selections.newest::<usize>(cx);
8838
8839 // If there is an active Diagnostic Popover jump to its diagnostic instead.
8840 if direction == Direction::Next {
8841 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
8842 let (group_id, jump_to) = popover.activation_info();
8843 if self.activate_diagnostics(group_id, cx) {
8844 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8845 let mut new_selection = s.newest_anchor().clone();
8846 new_selection.collapse_to(jump_to, SelectionGoal::None);
8847 s.select_anchors(vec![new_selection.clone()]);
8848 });
8849 }
8850 return;
8851 }
8852 }
8853
8854 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
8855 active_diagnostics
8856 .primary_range
8857 .to_offset(&buffer)
8858 .to_inclusive()
8859 });
8860 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
8861 if active_primary_range.contains(&selection.head()) {
8862 *active_primary_range.start()
8863 } else {
8864 selection.head()
8865 }
8866 } else {
8867 selection.head()
8868 };
8869 let snapshot = self.snapshot(cx);
8870 loop {
8871 let diagnostics = if direction == Direction::Prev {
8872 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
8873 } else {
8874 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
8875 }
8876 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start));
8877 let group = diagnostics
8878 // relies on diagnostics_in_range to return diagnostics with the same starting range to
8879 // be sorted in a stable way
8880 // skip until we are at current active diagnostic, if it exists
8881 .skip_while(|entry| {
8882 (match direction {
8883 Direction::Prev => entry.range.start >= search_start,
8884 Direction::Next => entry.range.start <= search_start,
8885 }) && self
8886 .active_diagnostics
8887 .as_ref()
8888 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
8889 })
8890 .find_map(|entry| {
8891 if entry.diagnostic.is_primary
8892 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
8893 && !entry.range.is_empty()
8894 // if we match with the active diagnostic, skip it
8895 && Some(entry.diagnostic.group_id)
8896 != self.active_diagnostics.as_ref().map(|d| d.group_id)
8897 {
8898 Some((entry.range, entry.diagnostic.group_id))
8899 } else {
8900 None
8901 }
8902 });
8903
8904 if let Some((primary_range, group_id)) = group {
8905 if self.activate_diagnostics(group_id, cx) {
8906 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8907 s.select(vec![Selection {
8908 id: selection.id,
8909 start: primary_range.start,
8910 end: primary_range.start,
8911 reversed: false,
8912 goal: SelectionGoal::None,
8913 }]);
8914 });
8915 }
8916 break;
8917 } else {
8918 // Cycle around to the start of the buffer, potentially moving back to the start of
8919 // the currently active diagnostic.
8920 active_primary_range.take();
8921 if direction == Direction::Prev {
8922 if search_start == buffer.len() {
8923 break;
8924 } else {
8925 search_start = buffer.len();
8926 }
8927 } else if search_start == 0 {
8928 break;
8929 } else {
8930 search_start = 0;
8931 }
8932 }
8933 }
8934 }
8935
8936 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
8937 let snapshot = self
8938 .display_map
8939 .update(cx, |display_map, cx| display_map.snapshot(cx));
8940 let selection = self.selections.newest::<Point>(cx);
8941
8942 if !self.seek_in_direction(
8943 &snapshot,
8944 selection.head(),
8945 false,
8946 snapshot.buffer_snapshot.git_diff_hunks_in_range(
8947 MultiBufferRow(selection.head().row + 1)..MultiBufferRow::MAX,
8948 ),
8949 cx,
8950 ) {
8951 let wrapped_point = Point::zero();
8952 self.seek_in_direction(
8953 &snapshot,
8954 wrapped_point,
8955 true,
8956 snapshot.buffer_snapshot.git_diff_hunks_in_range(
8957 MultiBufferRow(wrapped_point.row + 1)..MultiBufferRow::MAX,
8958 ),
8959 cx,
8960 );
8961 }
8962 }
8963
8964 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
8965 let snapshot = self
8966 .display_map
8967 .update(cx, |display_map, cx| display_map.snapshot(cx));
8968 let selection = self.selections.newest::<Point>(cx);
8969
8970 if !self.seek_in_direction(
8971 &snapshot,
8972 selection.head(),
8973 false,
8974 snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
8975 MultiBufferRow(0)..MultiBufferRow(selection.head().row),
8976 ),
8977 cx,
8978 ) {
8979 let wrapped_point = snapshot.buffer_snapshot.max_point();
8980 self.seek_in_direction(
8981 &snapshot,
8982 wrapped_point,
8983 true,
8984 snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
8985 MultiBufferRow(0)..MultiBufferRow(wrapped_point.row),
8986 ),
8987 cx,
8988 );
8989 }
8990 }
8991
8992 fn seek_in_direction(
8993 &mut self,
8994 snapshot: &DisplaySnapshot,
8995 initial_point: Point,
8996 is_wrapped: bool,
8997 hunks: impl Iterator<Item = DiffHunk<MultiBufferRow>>,
8998 cx: &mut ViewContext<Editor>,
8999 ) -> bool {
9000 let display_point = initial_point.to_display_point(snapshot);
9001 let mut hunks = hunks
9002 .map(|hunk| diff_hunk_to_display(&hunk, &snapshot))
9003 .filter(|hunk| is_wrapped || !hunk.contains_display_row(display_point.row()))
9004 .dedup();
9005
9006 if let Some(hunk) = hunks.next() {
9007 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9008 let row = hunk.start_display_row();
9009 let point = DisplayPoint::new(row, 0);
9010 s.select_display_ranges([point..point]);
9011 });
9012
9013 true
9014 } else {
9015 false
9016 }
9017 }
9018
9019 pub fn go_to_definition(
9020 &mut self,
9021 _: &GoToDefinition,
9022 cx: &mut ViewContext<Self>,
9023 ) -> Task<Result<bool>> {
9024 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx)
9025 }
9026
9027 pub fn go_to_declaration(
9028 &mut self,
9029 _: &GoToDeclaration,
9030 cx: &mut ViewContext<Self>,
9031 ) -> Task<Result<bool>> {
9032 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, cx)
9033 }
9034
9035 pub fn go_to_declaration_split(
9036 &mut self,
9037 _: &GoToDeclaration,
9038 cx: &mut ViewContext<Self>,
9039 ) -> Task<Result<bool>> {
9040 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, cx)
9041 }
9042
9043 pub fn go_to_implementation(
9044 &mut self,
9045 _: &GoToImplementation,
9046 cx: &mut ViewContext<Self>,
9047 ) -> Task<Result<bool>> {
9048 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx)
9049 }
9050
9051 pub fn go_to_implementation_split(
9052 &mut self,
9053 _: &GoToImplementationSplit,
9054 cx: &mut ViewContext<Self>,
9055 ) -> Task<Result<bool>> {
9056 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx)
9057 }
9058
9059 pub fn go_to_type_definition(
9060 &mut self,
9061 _: &GoToTypeDefinition,
9062 cx: &mut ViewContext<Self>,
9063 ) -> Task<Result<bool>> {
9064 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx)
9065 }
9066
9067 pub fn go_to_definition_split(
9068 &mut self,
9069 _: &GoToDefinitionSplit,
9070 cx: &mut ViewContext<Self>,
9071 ) -> Task<Result<bool>> {
9072 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx)
9073 }
9074
9075 pub fn go_to_type_definition_split(
9076 &mut self,
9077 _: &GoToTypeDefinitionSplit,
9078 cx: &mut ViewContext<Self>,
9079 ) -> Task<Result<bool>> {
9080 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx)
9081 }
9082
9083 fn go_to_definition_of_kind(
9084 &mut self,
9085 kind: GotoDefinitionKind,
9086 split: bool,
9087 cx: &mut ViewContext<Self>,
9088 ) -> Task<Result<bool>> {
9089 let Some(workspace) = self.workspace() else {
9090 return Task::ready(Ok(false));
9091 };
9092 let buffer = self.buffer.read(cx);
9093 let head = self.selections.newest::<usize>(cx).head();
9094 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
9095 text_anchor
9096 } else {
9097 return Task::ready(Ok(false));
9098 };
9099
9100 let project = workspace.read(cx).project().clone();
9101 let definitions = project.update(cx, |project, cx| match kind {
9102 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
9103 GotoDefinitionKind::Declaration => project.declaration(&buffer, head, cx),
9104 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
9105 GotoDefinitionKind::Implementation => project.implementation(&buffer, head, cx),
9106 });
9107
9108 cx.spawn(|editor, mut cx| async move {
9109 let definitions = definitions.await?;
9110 let navigated = editor
9111 .update(&mut cx, |editor, cx| {
9112 editor.navigate_to_hover_links(
9113 Some(kind),
9114 definitions
9115 .into_iter()
9116 .filter(|location| {
9117 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
9118 })
9119 .map(HoverLink::Text)
9120 .collect::<Vec<_>>(),
9121 split,
9122 cx,
9123 )
9124 })?
9125 .await?;
9126 anyhow::Ok(navigated)
9127 })
9128 }
9129
9130 pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext<Self>) {
9131 let position = self.selections.newest_anchor().head();
9132 let Some((buffer, buffer_position)) =
9133 self.buffer.read(cx).text_anchor_for_position(position, cx)
9134 else {
9135 return;
9136 };
9137
9138 cx.spawn(|editor, mut cx| async move {
9139 if let Some((_, url)) = find_url(&buffer, buffer_position, cx.clone()) {
9140 editor.update(&mut cx, |_, cx| {
9141 cx.open_url(&url);
9142 })
9143 } else {
9144 Ok(())
9145 }
9146 })
9147 .detach();
9148 }
9149
9150 pub(crate) fn navigate_to_hover_links(
9151 &mut self,
9152 kind: Option<GotoDefinitionKind>,
9153 mut definitions: Vec<HoverLink>,
9154 split: bool,
9155 cx: &mut ViewContext<Editor>,
9156 ) -> Task<Result<bool>> {
9157 // If there is one definition, just open it directly
9158 if definitions.len() == 1 {
9159 let definition = definitions.pop().unwrap();
9160 let target_task = match definition {
9161 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
9162 HoverLink::InlayHint(lsp_location, server_id) => {
9163 self.compute_target_location(lsp_location, server_id, cx)
9164 }
9165 HoverLink::Url(url) => {
9166 cx.open_url(&url);
9167 Task::ready(Ok(None))
9168 }
9169 };
9170 cx.spawn(|editor, mut cx| async move {
9171 let target = target_task.await.context("target resolution task")?;
9172 if let Some(target) = target {
9173 editor.update(&mut cx, |editor, cx| {
9174 let Some(workspace) = editor.workspace() else {
9175 return false;
9176 };
9177 let pane = workspace.read(cx).active_pane().clone();
9178
9179 let range = target.range.to_offset(target.buffer.read(cx));
9180 let range = editor.range_for_match(&range);
9181
9182 /// If select range has more than one line, we
9183 /// just point the cursor to range.start.
9184 fn check_multiline_range(
9185 buffer: &Buffer,
9186 range: Range<usize>,
9187 ) -> Range<usize> {
9188 if buffer.offset_to_point(range.start).row
9189 == buffer.offset_to_point(range.end).row
9190 {
9191 range
9192 } else {
9193 range.start..range.start
9194 }
9195 }
9196
9197 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
9198 let buffer = target.buffer.read(cx);
9199 let range = check_multiline_range(buffer, range);
9200 editor.change_selections(Some(Autoscroll::focused()), cx, |s| {
9201 s.select_ranges([range]);
9202 });
9203 } else {
9204 cx.window_context().defer(move |cx| {
9205 let target_editor: View<Self> =
9206 workspace.update(cx, |workspace, cx| {
9207 let pane = if split {
9208 workspace.adjacent_pane(cx)
9209 } else {
9210 workspace.active_pane().clone()
9211 };
9212
9213 workspace.open_project_item(
9214 pane,
9215 target.buffer.clone(),
9216 true,
9217 true,
9218 cx,
9219 )
9220 });
9221 target_editor.update(cx, |target_editor, cx| {
9222 // When selecting a definition in a different buffer, disable the nav history
9223 // to avoid creating a history entry at the previous cursor location.
9224 pane.update(cx, |pane, _| pane.disable_history());
9225 let buffer = target.buffer.read(cx);
9226 let range = check_multiline_range(buffer, range);
9227 target_editor.change_selections(
9228 Some(Autoscroll::focused()),
9229 cx,
9230 |s| {
9231 s.select_ranges([range]);
9232 },
9233 );
9234 pane.update(cx, |pane, _| pane.enable_history());
9235 });
9236 });
9237 }
9238 true
9239 })
9240 } else {
9241 Ok(false)
9242 }
9243 })
9244 } else if !definitions.is_empty() {
9245 let replica_id = self.replica_id(cx);
9246 cx.spawn(|editor, mut cx| async move {
9247 let (title, location_tasks, workspace) = editor
9248 .update(&mut cx, |editor, cx| {
9249 let tab_kind = match kind {
9250 Some(GotoDefinitionKind::Implementation) => "Implementations",
9251 _ => "Definitions",
9252 };
9253 let title = definitions
9254 .iter()
9255 .find_map(|definition| match definition {
9256 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
9257 let buffer = origin.buffer.read(cx);
9258 format!(
9259 "{} for {}",
9260 tab_kind,
9261 buffer
9262 .text_for_range(origin.range.clone())
9263 .collect::<String>()
9264 )
9265 }),
9266 HoverLink::InlayHint(_, _) => None,
9267 HoverLink::Url(_) => None,
9268 })
9269 .unwrap_or(tab_kind.to_string());
9270 let location_tasks = definitions
9271 .into_iter()
9272 .map(|definition| match definition {
9273 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
9274 HoverLink::InlayHint(lsp_location, server_id) => {
9275 editor.compute_target_location(lsp_location, server_id, cx)
9276 }
9277 HoverLink::Url(_) => Task::ready(Ok(None)),
9278 })
9279 .collect::<Vec<_>>();
9280 (title, location_tasks, editor.workspace().clone())
9281 })
9282 .context("location tasks preparation")?;
9283
9284 let locations = futures::future::join_all(location_tasks)
9285 .await
9286 .into_iter()
9287 .filter_map(|location| location.transpose())
9288 .collect::<Result<_>>()
9289 .context("location tasks")?;
9290
9291 let Some(workspace) = workspace else {
9292 return Ok(false);
9293 };
9294 let opened = workspace
9295 .update(&mut cx, |workspace, cx| {
9296 Self::open_locations_in_multibuffer(
9297 workspace, locations, replica_id, title, split, cx,
9298 )
9299 })
9300 .ok();
9301
9302 anyhow::Ok(opened.is_some())
9303 })
9304 } else {
9305 Task::ready(Ok(false))
9306 }
9307 }
9308
9309 fn compute_target_location(
9310 &self,
9311 lsp_location: lsp::Location,
9312 server_id: LanguageServerId,
9313 cx: &mut ViewContext<Editor>,
9314 ) -> Task<anyhow::Result<Option<Location>>> {
9315 let Some(project) = self.project.clone() else {
9316 return Task::Ready(Some(Ok(None)));
9317 };
9318
9319 cx.spawn(move |editor, mut cx| async move {
9320 let location_task = editor.update(&mut cx, |editor, cx| {
9321 project.update(cx, |project, cx| {
9322 let language_server_name =
9323 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
9324 project
9325 .language_server_for_buffer(buffer.read(cx), server_id, cx)
9326 .map(|(lsp_adapter, _)| lsp_adapter.name.clone())
9327 });
9328 language_server_name.map(|language_server_name| {
9329 project.open_local_buffer_via_lsp(
9330 lsp_location.uri.clone(),
9331 server_id,
9332 language_server_name,
9333 cx,
9334 )
9335 })
9336 })
9337 })?;
9338 let location = match location_task {
9339 Some(task) => Some({
9340 let target_buffer_handle = task.await.context("open local buffer")?;
9341 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
9342 let target_start = target_buffer
9343 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
9344 let target_end = target_buffer
9345 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
9346 target_buffer.anchor_after(target_start)
9347 ..target_buffer.anchor_before(target_end)
9348 })?;
9349 Location {
9350 buffer: target_buffer_handle,
9351 range,
9352 }
9353 }),
9354 None => None,
9355 };
9356 Ok(location)
9357 })
9358 }
9359
9360 pub fn find_all_references(
9361 &mut self,
9362 _: &FindAllReferences,
9363 cx: &mut ViewContext<Self>,
9364 ) -> Option<Task<Result<()>>> {
9365 let multi_buffer = self.buffer.read(cx);
9366 let selection = self.selections.newest::<usize>(cx);
9367 let head = selection.head();
9368
9369 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
9370 let head_anchor = multi_buffer_snapshot.anchor_at(
9371 head,
9372 if head < selection.tail() {
9373 Bias::Right
9374 } else {
9375 Bias::Left
9376 },
9377 );
9378
9379 match self
9380 .find_all_references_task_sources
9381 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
9382 {
9383 Ok(_) => {
9384 log::info!(
9385 "Ignoring repeated FindAllReferences invocation with the position of already running task"
9386 );
9387 return None;
9388 }
9389 Err(i) => {
9390 self.find_all_references_task_sources.insert(i, head_anchor);
9391 }
9392 }
9393
9394 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
9395 let replica_id = self.replica_id(cx);
9396 let workspace = self.workspace()?;
9397 let project = workspace.read(cx).project().clone();
9398 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
9399 Some(cx.spawn(|editor, mut cx| async move {
9400 let _cleanup = defer({
9401 let mut cx = cx.clone();
9402 move || {
9403 let _ = editor.update(&mut cx, |editor, _| {
9404 if let Ok(i) =
9405 editor
9406 .find_all_references_task_sources
9407 .binary_search_by(|anchor| {
9408 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
9409 })
9410 {
9411 editor.find_all_references_task_sources.remove(i);
9412 }
9413 });
9414 }
9415 });
9416
9417 let locations = references.await?;
9418 if locations.is_empty() {
9419 return anyhow::Ok(());
9420 }
9421
9422 workspace.update(&mut cx, |workspace, cx| {
9423 let title = locations
9424 .first()
9425 .as_ref()
9426 .map(|location| {
9427 let buffer = location.buffer.read(cx);
9428 format!(
9429 "References to `{}`",
9430 buffer
9431 .text_for_range(location.range.clone())
9432 .collect::<String>()
9433 )
9434 })
9435 .unwrap();
9436 Self::open_locations_in_multibuffer(
9437 workspace, locations, replica_id, title, false, cx,
9438 );
9439 })
9440 }))
9441 }
9442
9443 /// Opens a multibuffer with the given project locations in it
9444 pub fn open_locations_in_multibuffer(
9445 workspace: &mut Workspace,
9446 mut locations: Vec<Location>,
9447 replica_id: ReplicaId,
9448 title: String,
9449 split: bool,
9450 cx: &mut ViewContext<Workspace>,
9451 ) {
9452 // If there are multiple definitions, open them in a multibuffer
9453 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
9454 let mut locations = locations.into_iter().peekable();
9455 let mut ranges_to_highlight = Vec::new();
9456 let capability = workspace.project().read(cx).capability();
9457
9458 let excerpt_buffer = cx.new_model(|cx| {
9459 let mut multibuffer = MultiBuffer::new(replica_id, capability);
9460 while let Some(location) = locations.next() {
9461 let buffer = location.buffer.read(cx);
9462 let mut ranges_for_buffer = Vec::new();
9463 let range = location.range.to_offset(buffer);
9464 ranges_for_buffer.push(range.clone());
9465
9466 while let Some(next_location) = locations.peek() {
9467 if next_location.buffer == location.buffer {
9468 ranges_for_buffer.push(next_location.range.to_offset(buffer));
9469 locations.next();
9470 } else {
9471 break;
9472 }
9473 }
9474
9475 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
9476 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
9477 location.buffer.clone(),
9478 ranges_for_buffer,
9479 DEFAULT_MULTIBUFFER_CONTEXT,
9480 cx,
9481 ))
9482 }
9483
9484 multibuffer.with_title(title)
9485 });
9486
9487 let editor = cx.new_view(|cx| {
9488 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), true, cx)
9489 });
9490 editor.update(cx, |editor, cx| {
9491 if let Some(first_range) = ranges_to_highlight.first() {
9492 editor.change_selections(None, cx, |selections| {
9493 selections.clear_disjoint();
9494 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
9495 });
9496 }
9497 editor.highlight_background::<Self>(
9498 &ranges_to_highlight,
9499 |theme| theme.editor_highlighted_line_background,
9500 cx,
9501 );
9502 });
9503
9504 let item = Box::new(editor);
9505 let item_id = item.item_id();
9506
9507 if split {
9508 workspace.split_item(SplitDirection::Right, item.clone(), cx);
9509 } else {
9510 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
9511 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
9512 pane.close_current_preview_item(cx)
9513 } else {
9514 None
9515 }
9516 });
9517 workspace.add_item_to_active_pane(item.clone(), destination_index, true, cx);
9518 }
9519 workspace.active_pane().update(cx, |pane, cx| {
9520 pane.set_preview_item_id(Some(item_id), cx);
9521 });
9522 }
9523
9524 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9525 use language::ToOffset as _;
9526
9527 let project = self.project.clone()?;
9528 let selection = self.selections.newest_anchor().clone();
9529 let (cursor_buffer, cursor_buffer_position) = self
9530 .buffer
9531 .read(cx)
9532 .text_anchor_for_position(selection.head(), cx)?;
9533 let (tail_buffer, cursor_buffer_position_end) = self
9534 .buffer
9535 .read(cx)
9536 .text_anchor_for_position(selection.tail(), cx)?;
9537 if tail_buffer != cursor_buffer {
9538 return None;
9539 }
9540
9541 let snapshot = cursor_buffer.read(cx).snapshot();
9542 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
9543 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
9544 let prepare_rename = project.update(cx, |project, cx| {
9545 project.prepare_rename(cursor_buffer.clone(), cursor_buffer_offset, cx)
9546 });
9547 drop(snapshot);
9548
9549 Some(cx.spawn(|this, mut cx| async move {
9550 let rename_range = if let Some(range) = prepare_rename.await? {
9551 Some(range)
9552 } else {
9553 this.update(&mut cx, |this, cx| {
9554 let buffer = this.buffer.read(cx).snapshot(cx);
9555 let mut buffer_highlights = this
9556 .document_highlights_for_position(selection.head(), &buffer)
9557 .filter(|highlight| {
9558 highlight.start.excerpt_id == selection.head().excerpt_id
9559 && highlight.end.excerpt_id == selection.head().excerpt_id
9560 });
9561 buffer_highlights
9562 .next()
9563 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
9564 })?
9565 };
9566 if let Some(rename_range) = rename_range {
9567 this.update(&mut cx, |this, cx| {
9568 let snapshot = cursor_buffer.read(cx).snapshot();
9569 let rename_buffer_range = rename_range.to_offset(&snapshot);
9570 let cursor_offset_in_rename_range =
9571 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
9572 let cursor_offset_in_rename_range_end =
9573 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
9574
9575 this.take_rename(false, cx);
9576 let buffer = this.buffer.read(cx).read(cx);
9577 let cursor_offset = selection.head().to_offset(&buffer);
9578 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
9579 let rename_end = rename_start + rename_buffer_range.len();
9580 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
9581 let mut old_highlight_id = None;
9582 let old_name: Arc<str> = buffer
9583 .chunks(rename_start..rename_end, true)
9584 .map(|chunk| {
9585 if old_highlight_id.is_none() {
9586 old_highlight_id = chunk.syntax_highlight_id;
9587 }
9588 chunk.text
9589 })
9590 .collect::<String>()
9591 .into();
9592
9593 drop(buffer);
9594
9595 // Position the selection in the rename editor so that it matches the current selection.
9596 this.show_local_selections = false;
9597 let rename_editor = cx.new_view(|cx| {
9598 let mut editor = Editor::single_line(cx);
9599 editor.buffer.update(cx, |buffer, cx| {
9600 buffer.edit([(0..0, old_name.clone())], None, cx)
9601 });
9602 let rename_selection_range = match cursor_offset_in_rename_range
9603 .cmp(&cursor_offset_in_rename_range_end)
9604 {
9605 Ordering::Equal => {
9606 editor.select_all(&SelectAll, cx);
9607 return editor;
9608 }
9609 Ordering::Less => {
9610 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
9611 }
9612 Ordering::Greater => {
9613 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
9614 }
9615 };
9616 if rename_selection_range.end > old_name.len() {
9617 editor.select_all(&SelectAll, cx);
9618 } else {
9619 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
9620 s.select_ranges([rename_selection_range]);
9621 });
9622 }
9623 editor
9624 });
9625 cx.subscribe(&rename_editor, |_, _, e, cx| match e {
9626 EditorEvent::Focused => cx.emit(EditorEvent::FocusedIn),
9627 _ => {}
9628 })
9629 .detach();
9630
9631 let write_highlights =
9632 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
9633 let read_highlights =
9634 this.clear_background_highlights::<DocumentHighlightRead>(cx);
9635 let ranges = write_highlights
9636 .iter()
9637 .flat_map(|(_, ranges)| ranges.iter())
9638 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
9639 .cloned()
9640 .collect();
9641
9642 this.highlight_text::<Rename>(
9643 ranges,
9644 HighlightStyle {
9645 fade_out: Some(0.6),
9646 ..Default::default()
9647 },
9648 cx,
9649 );
9650 let rename_focus_handle = rename_editor.focus_handle(cx);
9651 cx.focus(&rename_focus_handle);
9652 let block_id = this.insert_blocks(
9653 [BlockProperties {
9654 style: BlockStyle::Flex,
9655 position: range.start,
9656 height: 1,
9657 render: Box::new({
9658 let rename_editor = rename_editor.clone();
9659 move |cx: &mut BlockContext| {
9660 let mut text_style = cx.editor_style.text.clone();
9661 if let Some(highlight_style) = old_highlight_id
9662 .and_then(|h| h.style(&cx.editor_style.syntax))
9663 {
9664 text_style = text_style.highlight(highlight_style);
9665 }
9666 div()
9667 .pl(cx.anchor_x)
9668 .child(EditorElement::new(
9669 &rename_editor,
9670 EditorStyle {
9671 background: cx.theme().system().transparent,
9672 local_player: cx.editor_style.local_player,
9673 text: text_style,
9674 scrollbar_width: cx.editor_style.scrollbar_width,
9675 syntax: cx.editor_style.syntax.clone(),
9676 status: cx.editor_style.status.clone(),
9677 inlay_hints_style: HighlightStyle {
9678 color: Some(cx.theme().status().hint),
9679 font_weight: Some(FontWeight::BOLD),
9680 ..HighlightStyle::default()
9681 },
9682 suggestions_style: HighlightStyle {
9683 color: Some(cx.theme().status().predictive),
9684 ..HighlightStyle::default()
9685 },
9686 },
9687 ))
9688 .into_any_element()
9689 }
9690 }),
9691 disposition: BlockDisposition::Below,
9692 priority: 0,
9693 }],
9694 Some(Autoscroll::fit()),
9695 cx,
9696 )[0];
9697 this.pending_rename = Some(RenameState {
9698 range,
9699 old_name,
9700 editor: rename_editor,
9701 block_id,
9702 });
9703 })?;
9704 }
9705
9706 Ok(())
9707 }))
9708 }
9709
9710 pub fn confirm_rename(
9711 &mut self,
9712 _: &ConfirmRename,
9713 cx: &mut ViewContext<Self>,
9714 ) -> Option<Task<Result<()>>> {
9715 let rename = self.take_rename(false, cx)?;
9716 let workspace = self.workspace()?;
9717 let (start_buffer, start) = self
9718 .buffer
9719 .read(cx)
9720 .text_anchor_for_position(rename.range.start, cx)?;
9721 let (end_buffer, end) = self
9722 .buffer
9723 .read(cx)
9724 .text_anchor_for_position(rename.range.end, cx)?;
9725 if start_buffer != end_buffer {
9726 return None;
9727 }
9728
9729 let buffer = start_buffer;
9730 let range = start..end;
9731 let old_name = rename.old_name;
9732 let new_name = rename.editor.read(cx).text(cx);
9733
9734 let rename = workspace
9735 .read(cx)
9736 .project()
9737 .clone()
9738 .update(cx, |project, cx| {
9739 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
9740 });
9741 let workspace = workspace.downgrade();
9742
9743 Some(cx.spawn(|editor, mut cx| async move {
9744 let project_transaction = rename.await?;
9745 Self::open_project_transaction(
9746 &editor,
9747 workspace,
9748 project_transaction,
9749 format!("Rename: {} → {}", old_name, new_name),
9750 cx.clone(),
9751 )
9752 .await?;
9753
9754 editor.update(&mut cx, |editor, cx| {
9755 editor.refresh_document_highlights(cx);
9756 })?;
9757 Ok(())
9758 }))
9759 }
9760
9761 fn take_rename(
9762 &mut self,
9763 moving_cursor: bool,
9764 cx: &mut ViewContext<Self>,
9765 ) -> Option<RenameState> {
9766 let rename = self.pending_rename.take()?;
9767 if rename.editor.focus_handle(cx).is_focused(cx) {
9768 cx.focus(&self.focus_handle);
9769 }
9770
9771 self.remove_blocks(
9772 [rename.block_id].into_iter().collect(),
9773 Some(Autoscroll::fit()),
9774 cx,
9775 );
9776 self.clear_highlights::<Rename>(cx);
9777 self.show_local_selections = true;
9778
9779 if moving_cursor {
9780 let rename_editor = rename.editor.read(cx);
9781 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
9782
9783 // Update the selection to match the position of the selection inside
9784 // the rename editor.
9785 let snapshot = self.buffer.read(cx).read(cx);
9786 let rename_range = rename.range.to_offset(&snapshot);
9787 let cursor_in_editor = snapshot
9788 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
9789 .min(rename_range.end);
9790 drop(snapshot);
9791
9792 self.change_selections(None, cx, |s| {
9793 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
9794 });
9795 } else {
9796 self.refresh_document_highlights(cx);
9797 }
9798
9799 Some(rename)
9800 }
9801
9802 pub fn pending_rename(&self) -> Option<&RenameState> {
9803 self.pending_rename.as_ref()
9804 }
9805
9806 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9807 let project = match &self.project {
9808 Some(project) => project.clone(),
9809 None => return None,
9810 };
9811
9812 Some(self.perform_format(project, FormatTrigger::Manual, cx))
9813 }
9814
9815 fn perform_format(
9816 &mut self,
9817 project: Model<Project>,
9818 trigger: FormatTrigger,
9819 cx: &mut ViewContext<Self>,
9820 ) -> Task<Result<()>> {
9821 let buffer = self.buffer().clone();
9822 let mut buffers = buffer.read(cx).all_buffers();
9823 if trigger == FormatTrigger::Save {
9824 buffers.retain(|buffer| buffer.read(cx).is_dirty());
9825 }
9826
9827 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
9828 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
9829
9830 cx.spawn(|_, mut cx| async move {
9831 let transaction = futures::select_biased! {
9832 () = timeout => {
9833 log::warn!("timed out waiting for formatting");
9834 None
9835 }
9836 transaction = format.log_err().fuse() => transaction,
9837 };
9838
9839 buffer
9840 .update(&mut cx, |buffer, cx| {
9841 if let Some(transaction) = transaction {
9842 if !buffer.is_singleton() {
9843 buffer.push_transaction(&transaction.0, cx);
9844 }
9845 }
9846
9847 cx.notify();
9848 })
9849 .ok();
9850
9851 Ok(())
9852 })
9853 }
9854
9855 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
9856 if let Some(project) = self.project.clone() {
9857 self.buffer.update(cx, |multi_buffer, cx| {
9858 project.update(cx, |project, cx| {
9859 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
9860 });
9861 })
9862 }
9863 }
9864
9865 fn cancel_language_server_work(
9866 &mut self,
9867 _: &CancelLanguageServerWork,
9868 cx: &mut ViewContext<Self>,
9869 ) {
9870 if let Some(project) = self.project.clone() {
9871 self.buffer.update(cx, |multi_buffer, cx| {
9872 project.update(cx, |project, cx| {
9873 project.cancel_language_server_work_for_buffers(multi_buffer.all_buffers(), cx);
9874 });
9875 })
9876 }
9877 }
9878
9879 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
9880 cx.show_character_palette();
9881 }
9882
9883 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
9884 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
9885 let buffer = self.buffer.read(cx).snapshot(cx);
9886 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
9887 let is_valid = buffer
9888 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
9889 .any(|entry| {
9890 entry.diagnostic.is_primary
9891 && !entry.range.is_empty()
9892 && entry.range.start == primary_range_start
9893 && entry.diagnostic.message == active_diagnostics.primary_message
9894 });
9895
9896 if is_valid != active_diagnostics.is_valid {
9897 active_diagnostics.is_valid = is_valid;
9898 let mut new_styles = HashMap::default();
9899 for (block_id, diagnostic) in &active_diagnostics.blocks {
9900 new_styles.insert(
9901 *block_id,
9902 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
9903 );
9904 }
9905 self.display_map.update(cx, |display_map, _cx| {
9906 display_map.replace_blocks(new_styles)
9907 });
9908 }
9909 }
9910 }
9911
9912 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
9913 self.dismiss_diagnostics(cx);
9914 let snapshot = self.snapshot(cx);
9915 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
9916 let buffer = self.buffer.read(cx).snapshot(cx);
9917
9918 let mut primary_range = None;
9919 let mut primary_message = None;
9920 let mut group_end = Point::zero();
9921 let diagnostic_group = buffer
9922 .diagnostic_group::<MultiBufferPoint>(group_id)
9923 .filter_map(|entry| {
9924 if snapshot.is_line_folded(MultiBufferRow(entry.range.start.row))
9925 && (entry.range.start.row == entry.range.end.row
9926 || snapshot.is_line_folded(MultiBufferRow(entry.range.end.row)))
9927 {
9928 return None;
9929 }
9930 if entry.range.end > group_end {
9931 group_end = entry.range.end;
9932 }
9933 if entry.diagnostic.is_primary {
9934 primary_range = Some(entry.range.clone());
9935 primary_message = Some(entry.diagnostic.message.clone());
9936 }
9937 Some(entry)
9938 })
9939 .collect::<Vec<_>>();
9940 let primary_range = primary_range?;
9941 let primary_message = primary_message?;
9942 let primary_range =
9943 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
9944
9945 let blocks = display_map
9946 .insert_blocks(
9947 diagnostic_group.iter().map(|entry| {
9948 let diagnostic = entry.diagnostic.clone();
9949 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
9950 BlockProperties {
9951 style: BlockStyle::Fixed,
9952 position: buffer.anchor_after(entry.range.start),
9953 height: message_height,
9954 render: diagnostic_block_renderer(diagnostic, None, true, true),
9955 disposition: BlockDisposition::Below,
9956 priority: 0,
9957 }
9958 }),
9959 cx,
9960 )
9961 .into_iter()
9962 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
9963 .collect();
9964
9965 Some(ActiveDiagnosticGroup {
9966 primary_range,
9967 primary_message,
9968 group_id,
9969 blocks,
9970 is_valid: true,
9971 })
9972 });
9973 self.active_diagnostics.is_some()
9974 }
9975
9976 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
9977 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
9978 self.display_map.update(cx, |display_map, cx| {
9979 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
9980 });
9981 cx.notify();
9982 }
9983 }
9984
9985 pub fn set_selections_from_remote(
9986 &mut self,
9987 selections: Vec<Selection<Anchor>>,
9988 pending_selection: Option<Selection<Anchor>>,
9989 cx: &mut ViewContext<Self>,
9990 ) {
9991 let old_cursor_position = self.selections.newest_anchor().head();
9992 self.selections.change_with(cx, |s| {
9993 s.select_anchors(selections);
9994 if let Some(pending_selection) = pending_selection {
9995 s.set_pending(pending_selection, SelectMode::Character);
9996 } else {
9997 s.clear_pending();
9998 }
9999 });
10000 self.selections_did_change(false, &old_cursor_position, true, cx);
10001 }
10002
10003 fn push_to_selection_history(&mut self) {
10004 self.selection_history.push(SelectionHistoryEntry {
10005 selections: self.selections.disjoint_anchors(),
10006 select_next_state: self.select_next_state.clone(),
10007 select_prev_state: self.select_prev_state.clone(),
10008 add_selections_state: self.add_selections_state.clone(),
10009 });
10010 }
10011
10012 pub fn transact(
10013 &mut self,
10014 cx: &mut ViewContext<Self>,
10015 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
10016 ) -> Option<TransactionId> {
10017 self.start_transaction_at(Instant::now(), cx);
10018 update(self, cx);
10019 self.end_transaction_at(Instant::now(), cx)
10020 }
10021
10022 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
10023 self.end_selection(cx);
10024 if let Some(tx_id) = self
10025 .buffer
10026 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
10027 {
10028 self.selection_history
10029 .insert_transaction(tx_id, self.selections.disjoint_anchors());
10030 cx.emit(EditorEvent::TransactionBegun {
10031 transaction_id: tx_id,
10032 })
10033 }
10034 }
10035
10036 fn end_transaction_at(
10037 &mut self,
10038 now: Instant,
10039 cx: &mut ViewContext<Self>,
10040 ) -> Option<TransactionId> {
10041 if let Some(transaction_id) = self
10042 .buffer
10043 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
10044 {
10045 if let Some((_, end_selections)) =
10046 self.selection_history.transaction_mut(transaction_id)
10047 {
10048 *end_selections = Some(self.selections.disjoint_anchors());
10049 } else {
10050 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
10051 }
10052
10053 cx.emit(EditorEvent::Edited { transaction_id });
10054 Some(transaction_id)
10055 } else {
10056 None
10057 }
10058 }
10059
10060 pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
10061 let mut fold_ranges = Vec::new();
10062
10063 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10064
10065 let selections = self.selections.all_adjusted(cx);
10066 for selection in selections {
10067 let range = selection.range().sorted();
10068 let buffer_start_row = range.start.row;
10069
10070 for row in (0..=range.end.row).rev() {
10071 if let Some((foldable_range, fold_text)) =
10072 display_map.foldable_range(MultiBufferRow(row))
10073 {
10074 if foldable_range.end.row >= buffer_start_row {
10075 fold_ranges.push((foldable_range, fold_text));
10076 if row <= range.start.row {
10077 break;
10078 }
10079 }
10080 }
10081 }
10082 }
10083
10084 self.fold_ranges(fold_ranges, true, cx);
10085 }
10086
10087 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
10088 let buffer_row = fold_at.buffer_row;
10089 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10090
10091 if let Some((fold_range, placeholder)) = display_map.foldable_range(buffer_row) {
10092 let autoscroll = self
10093 .selections
10094 .all::<Point>(cx)
10095 .iter()
10096 .any(|selection| fold_range.overlaps(&selection.range()));
10097
10098 self.fold_ranges([(fold_range, placeholder)], autoscroll, cx);
10099 }
10100 }
10101
10102 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
10103 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10104 let buffer = &display_map.buffer_snapshot;
10105 let selections = self.selections.all::<Point>(cx);
10106 let ranges = selections
10107 .iter()
10108 .map(|s| {
10109 let range = s.display_range(&display_map).sorted();
10110 let mut start = range.start.to_point(&display_map);
10111 let mut end = range.end.to_point(&display_map);
10112 start.column = 0;
10113 end.column = buffer.line_len(MultiBufferRow(end.row));
10114 start..end
10115 })
10116 .collect::<Vec<_>>();
10117
10118 self.unfold_ranges(ranges, true, true, cx);
10119 }
10120
10121 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
10122 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10123
10124 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
10125 ..Point::new(
10126 unfold_at.buffer_row.0,
10127 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
10128 );
10129
10130 let autoscroll = self
10131 .selections
10132 .all::<Point>(cx)
10133 .iter()
10134 .any(|selection| selection.range().overlaps(&intersection_range));
10135
10136 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
10137 }
10138
10139 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
10140 let selections = self.selections.all::<Point>(cx);
10141 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10142 let line_mode = self.selections.line_mode;
10143 let ranges = selections.into_iter().map(|s| {
10144 if line_mode {
10145 let start = Point::new(s.start.row, 0);
10146 let end = Point::new(
10147 s.end.row,
10148 display_map
10149 .buffer_snapshot
10150 .line_len(MultiBufferRow(s.end.row)),
10151 );
10152 (start..end, display_map.fold_placeholder.clone())
10153 } else {
10154 (s.start..s.end, display_map.fold_placeholder.clone())
10155 }
10156 });
10157 self.fold_ranges(ranges, true, cx);
10158 }
10159
10160 pub fn fold_ranges<T: ToOffset + Clone>(
10161 &mut self,
10162 ranges: impl IntoIterator<Item = (Range<T>, FoldPlaceholder)>,
10163 auto_scroll: bool,
10164 cx: &mut ViewContext<Self>,
10165 ) {
10166 let mut fold_ranges = Vec::new();
10167 let mut buffers_affected = HashMap::default();
10168 let multi_buffer = self.buffer().read(cx);
10169 for (fold_range, fold_text) in ranges {
10170 if let Some((_, buffer, _)) =
10171 multi_buffer.excerpt_containing(fold_range.start.clone(), cx)
10172 {
10173 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
10174 };
10175 fold_ranges.push((fold_range, fold_text));
10176 }
10177
10178 let mut ranges = fold_ranges.into_iter().peekable();
10179 if ranges.peek().is_some() {
10180 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
10181
10182 if auto_scroll {
10183 self.request_autoscroll(Autoscroll::fit(), cx);
10184 }
10185
10186 for buffer in buffers_affected.into_values() {
10187 self.sync_expanded_diff_hunks(buffer, cx);
10188 }
10189
10190 cx.notify();
10191
10192 if let Some(active_diagnostics) = self.active_diagnostics.take() {
10193 // Clear diagnostics block when folding a range that contains it.
10194 let snapshot = self.snapshot(cx);
10195 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
10196 drop(snapshot);
10197 self.active_diagnostics = Some(active_diagnostics);
10198 self.dismiss_diagnostics(cx);
10199 } else {
10200 self.active_diagnostics = Some(active_diagnostics);
10201 }
10202 }
10203
10204 self.scrollbar_marker_state.dirty = true;
10205 }
10206 }
10207
10208 pub fn unfold_ranges<T: ToOffset + Clone>(
10209 &mut self,
10210 ranges: impl IntoIterator<Item = Range<T>>,
10211 inclusive: bool,
10212 auto_scroll: bool,
10213 cx: &mut ViewContext<Self>,
10214 ) {
10215 let mut unfold_ranges = Vec::new();
10216 let mut buffers_affected = HashMap::default();
10217 let multi_buffer = self.buffer().read(cx);
10218 for range in ranges {
10219 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
10220 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
10221 };
10222 unfold_ranges.push(range);
10223 }
10224
10225 let mut ranges = unfold_ranges.into_iter().peekable();
10226 if ranges.peek().is_some() {
10227 self.display_map
10228 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
10229 if auto_scroll {
10230 self.request_autoscroll(Autoscroll::fit(), cx);
10231 }
10232
10233 for buffer in buffers_affected.into_values() {
10234 self.sync_expanded_diff_hunks(buffer, cx);
10235 }
10236
10237 cx.notify();
10238 self.scrollbar_marker_state.dirty = true;
10239 self.active_indent_guides_state.dirty = true;
10240 }
10241 }
10242
10243 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
10244 if hovered != self.gutter_hovered {
10245 self.gutter_hovered = hovered;
10246 cx.notify();
10247 }
10248 }
10249
10250 pub fn insert_blocks(
10251 &mut self,
10252 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
10253 autoscroll: Option<Autoscroll>,
10254 cx: &mut ViewContext<Self>,
10255 ) -> Vec<CustomBlockId> {
10256 let blocks = self
10257 .display_map
10258 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
10259 if let Some(autoscroll) = autoscroll {
10260 self.request_autoscroll(autoscroll, cx);
10261 }
10262 cx.notify();
10263 blocks
10264 }
10265
10266 pub fn resize_blocks(
10267 &mut self,
10268 heights: HashMap<CustomBlockId, u32>,
10269 autoscroll: Option<Autoscroll>,
10270 cx: &mut ViewContext<Self>,
10271 ) {
10272 self.display_map
10273 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
10274 if let Some(autoscroll) = autoscroll {
10275 self.request_autoscroll(autoscroll, cx);
10276 }
10277 cx.notify();
10278 }
10279
10280 pub fn replace_blocks(
10281 &mut self,
10282 renderers: HashMap<CustomBlockId, RenderBlock>,
10283 autoscroll: Option<Autoscroll>,
10284 cx: &mut ViewContext<Self>,
10285 ) {
10286 self.display_map
10287 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
10288 if let Some(autoscroll) = autoscroll {
10289 self.request_autoscroll(autoscroll, cx);
10290 }
10291 cx.notify();
10292 }
10293
10294 pub fn remove_blocks(
10295 &mut self,
10296 block_ids: HashSet<CustomBlockId>,
10297 autoscroll: Option<Autoscroll>,
10298 cx: &mut ViewContext<Self>,
10299 ) {
10300 self.display_map.update(cx, |display_map, cx| {
10301 display_map.remove_blocks(block_ids, cx)
10302 });
10303 if let Some(autoscroll) = autoscroll {
10304 self.request_autoscroll(autoscroll, cx);
10305 }
10306 cx.notify();
10307 }
10308
10309 pub fn row_for_block(
10310 &self,
10311 block_id: CustomBlockId,
10312 cx: &mut ViewContext<Self>,
10313 ) -> Option<DisplayRow> {
10314 self.display_map
10315 .update(cx, |map, cx| map.row_for_block(block_id, cx))
10316 }
10317
10318 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
10319 self.focused_block = Some(focused_block);
10320 }
10321
10322 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
10323 self.focused_block.take()
10324 }
10325
10326 pub fn insert_creases(
10327 &mut self,
10328 creases: impl IntoIterator<Item = Crease>,
10329 cx: &mut ViewContext<Self>,
10330 ) -> Vec<CreaseId> {
10331 self.display_map
10332 .update(cx, |map, cx| map.insert_creases(creases, cx))
10333 }
10334
10335 pub fn remove_creases(
10336 &mut self,
10337 ids: impl IntoIterator<Item = CreaseId>,
10338 cx: &mut ViewContext<Self>,
10339 ) {
10340 self.display_map
10341 .update(cx, |map, cx| map.remove_creases(ids, cx));
10342 }
10343
10344 pub fn longest_row(&self, cx: &mut AppContext) -> DisplayRow {
10345 self.display_map
10346 .update(cx, |map, cx| map.snapshot(cx))
10347 .longest_row()
10348 }
10349
10350 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
10351 self.display_map
10352 .update(cx, |map, cx| map.snapshot(cx))
10353 .max_point()
10354 }
10355
10356 pub fn text(&self, cx: &AppContext) -> String {
10357 self.buffer.read(cx).read(cx).text()
10358 }
10359
10360 pub fn text_option(&self, cx: &AppContext) -> Option<String> {
10361 let text = self.text(cx);
10362 let text = text.trim();
10363
10364 if text.is_empty() {
10365 return None;
10366 }
10367
10368 Some(text.to_string())
10369 }
10370
10371 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
10372 self.transact(cx, |this, cx| {
10373 this.buffer
10374 .read(cx)
10375 .as_singleton()
10376 .expect("you can only call set_text on editors for singleton buffers")
10377 .update(cx, |buffer, cx| buffer.set_text(text, cx));
10378 });
10379 }
10380
10381 pub fn display_text(&self, cx: &mut AppContext) -> String {
10382 self.display_map
10383 .update(cx, |map, cx| map.snapshot(cx))
10384 .text()
10385 }
10386
10387 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
10388 let mut wrap_guides = smallvec::smallvec![];
10389
10390 if self.show_wrap_guides == Some(false) {
10391 return wrap_guides;
10392 }
10393
10394 let settings = self.buffer.read(cx).settings_at(0, cx);
10395 if settings.show_wrap_guides {
10396 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
10397 wrap_guides.push((soft_wrap as usize, true));
10398 }
10399 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
10400 }
10401
10402 wrap_guides
10403 }
10404
10405 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
10406 let settings = self.buffer.read(cx).settings_at(0, cx);
10407 let mode = self
10408 .soft_wrap_mode_override
10409 .unwrap_or_else(|| settings.soft_wrap);
10410 match mode {
10411 language_settings::SoftWrap::None => SoftWrap::None,
10412 language_settings::SoftWrap::PreferLine => SoftWrap::PreferLine,
10413 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
10414 language_settings::SoftWrap::PreferredLineLength => {
10415 SoftWrap::Column(settings.preferred_line_length)
10416 }
10417 }
10418 }
10419
10420 pub fn set_soft_wrap_mode(
10421 &mut self,
10422 mode: language_settings::SoftWrap,
10423 cx: &mut ViewContext<Self>,
10424 ) {
10425 self.soft_wrap_mode_override = Some(mode);
10426 cx.notify();
10427 }
10428
10429 pub fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
10430 let rem_size = cx.rem_size();
10431 self.display_map.update(cx, |map, cx| {
10432 map.set_font(
10433 style.text.font(),
10434 style.text.font_size.to_pixels(rem_size),
10435 cx,
10436 )
10437 });
10438 self.style = Some(style);
10439 }
10440
10441 pub fn style(&self) -> Option<&EditorStyle> {
10442 self.style.as_ref()
10443 }
10444
10445 // Called by the element. This method is not designed to be called outside of the editor
10446 // element's layout code because it does not notify when rewrapping is computed synchronously.
10447 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
10448 self.display_map
10449 .update(cx, |map, cx| map.set_wrap_width(width, cx))
10450 }
10451
10452 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
10453 if self.soft_wrap_mode_override.is_some() {
10454 self.soft_wrap_mode_override.take();
10455 } else {
10456 let soft_wrap = match self.soft_wrap_mode(cx) {
10457 SoftWrap::None | SoftWrap::PreferLine => language_settings::SoftWrap::EditorWidth,
10458 SoftWrap::EditorWidth | SoftWrap::Column(_) => {
10459 language_settings::SoftWrap::PreferLine
10460 }
10461 };
10462 self.soft_wrap_mode_override = Some(soft_wrap);
10463 }
10464 cx.notify();
10465 }
10466
10467 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, cx: &mut ViewContext<Self>) {
10468 let Some(workspace) = self.workspace() else {
10469 return;
10470 };
10471 let fs = workspace.read(cx).app_state().fs.clone();
10472 let current_show = TabBarSettings::get_global(cx).show;
10473 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
10474 setting.show = Some(!current_show);
10475 });
10476 }
10477
10478 pub fn toggle_indent_guides(&mut self, _: &ToggleIndentGuides, cx: &mut ViewContext<Self>) {
10479 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
10480 self.buffer
10481 .read(cx)
10482 .settings_at(0, cx)
10483 .indent_guides
10484 .enabled
10485 });
10486 self.show_indent_guides = Some(!currently_enabled);
10487 cx.notify();
10488 }
10489
10490 fn should_show_indent_guides(&self) -> Option<bool> {
10491 self.show_indent_guides
10492 }
10493
10494 pub fn toggle_line_numbers(&mut self, _: &ToggleLineNumbers, cx: &mut ViewContext<Self>) {
10495 let mut editor_settings = EditorSettings::get_global(cx).clone();
10496 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
10497 EditorSettings::override_global(editor_settings, cx);
10498 }
10499
10500 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
10501 self.show_gutter = show_gutter;
10502 cx.notify();
10503 }
10504
10505 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut ViewContext<Self>) {
10506 self.show_line_numbers = Some(show_line_numbers);
10507 cx.notify();
10508 }
10509
10510 pub fn set_show_git_diff_gutter(
10511 &mut self,
10512 show_git_diff_gutter: bool,
10513 cx: &mut ViewContext<Self>,
10514 ) {
10515 self.show_git_diff_gutter = Some(show_git_diff_gutter);
10516 cx.notify();
10517 }
10518
10519 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut ViewContext<Self>) {
10520 self.show_code_actions = Some(show_code_actions);
10521 cx.notify();
10522 }
10523
10524 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut ViewContext<Self>) {
10525 self.show_runnables = Some(show_runnables);
10526 cx.notify();
10527 }
10528
10529 pub fn set_masked(&mut self, masked: bool, cx: &mut ViewContext<Self>) {
10530 if self.display_map.read(cx).masked != masked {
10531 self.display_map.update(cx, |map, _| map.masked = masked);
10532 }
10533 cx.notify()
10534 }
10535
10536 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut ViewContext<Self>) {
10537 self.show_wrap_guides = Some(show_wrap_guides);
10538 cx.notify();
10539 }
10540
10541 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut ViewContext<Self>) {
10542 self.show_indent_guides = Some(show_indent_guides);
10543 cx.notify();
10544 }
10545
10546 pub fn working_directory(&self, cx: &WindowContext) -> Option<PathBuf> {
10547 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10548 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10549 if let Some(dir) = file.abs_path(cx).parent() {
10550 return Some(dir.to_owned());
10551 }
10552 }
10553
10554 if let Some(project_path) = buffer.read(cx).project_path(cx) {
10555 return Some(project_path.path.to_path_buf());
10556 }
10557 }
10558
10559 None
10560 }
10561
10562 pub fn reveal_in_finder(&mut self, _: &RevealInFileManager, cx: &mut ViewContext<Self>) {
10563 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10564 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10565 cx.reveal_path(&file.abs_path(cx));
10566 }
10567 }
10568 }
10569
10570 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
10571 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10572 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10573 if let Some(path) = file.abs_path(cx).to_str() {
10574 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
10575 }
10576 }
10577 }
10578 }
10579
10580 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
10581 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10582 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10583 if let Some(path) = file.path().to_str() {
10584 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
10585 }
10586 }
10587 }
10588 }
10589
10590 pub fn toggle_git_blame(&mut self, _: &ToggleGitBlame, cx: &mut ViewContext<Self>) {
10591 self.show_git_blame_gutter = !self.show_git_blame_gutter;
10592
10593 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
10594 self.start_git_blame(true, cx);
10595 }
10596
10597 cx.notify();
10598 }
10599
10600 pub fn toggle_git_blame_inline(
10601 &mut self,
10602 _: &ToggleGitBlameInline,
10603 cx: &mut ViewContext<Self>,
10604 ) {
10605 self.toggle_git_blame_inline_internal(true, cx);
10606 cx.notify();
10607 }
10608
10609 pub fn git_blame_inline_enabled(&self) -> bool {
10610 self.git_blame_inline_enabled
10611 }
10612
10613 pub fn toggle_selection_menu(&mut self, _: &ToggleSelectionMenu, cx: &mut ViewContext<Self>) {
10614 self.show_selection_menu = self
10615 .show_selection_menu
10616 .map(|show_selections_menu| !show_selections_menu)
10617 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
10618
10619 cx.notify();
10620 }
10621
10622 pub fn selection_menu_enabled(&self, cx: &AppContext) -> bool {
10623 self.show_selection_menu
10624 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
10625 }
10626
10627 fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
10628 if let Some(project) = self.project.as_ref() {
10629 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
10630 return;
10631 };
10632
10633 if buffer.read(cx).file().is_none() {
10634 return;
10635 }
10636
10637 let focused = self.focus_handle(cx).contains_focused(cx);
10638
10639 let project = project.clone();
10640 let blame =
10641 cx.new_model(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
10642 self.blame_subscription = Some(cx.observe(&blame, |_, _, cx| cx.notify()));
10643 self.blame = Some(blame);
10644 }
10645 }
10646
10647 fn toggle_git_blame_inline_internal(
10648 &mut self,
10649 user_triggered: bool,
10650 cx: &mut ViewContext<Self>,
10651 ) {
10652 if self.git_blame_inline_enabled {
10653 self.git_blame_inline_enabled = false;
10654 self.show_git_blame_inline = false;
10655 self.show_git_blame_inline_delay_task.take();
10656 } else {
10657 self.git_blame_inline_enabled = true;
10658 self.start_git_blame_inline(user_triggered, cx);
10659 }
10660
10661 cx.notify();
10662 }
10663
10664 fn start_git_blame_inline(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
10665 self.start_git_blame(user_triggered, cx);
10666
10667 if ProjectSettings::get_global(cx)
10668 .git
10669 .inline_blame_delay()
10670 .is_some()
10671 {
10672 self.start_inline_blame_timer(cx);
10673 } else {
10674 self.show_git_blame_inline = true
10675 }
10676 }
10677
10678 pub fn blame(&self) -> Option<&Model<GitBlame>> {
10679 self.blame.as_ref()
10680 }
10681
10682 pub fn render_git_blame_gutter(&mut self, cx: &mut WindowContext) -> bool {
10683 self.show_git_blame_gutter && self.has_blame_entries(cx)
10684 }
10685
10686 pub fn render_git_blame_inline(&mut self, cx: &mut WindowContext) -> bool {
10687 self.show_git_blame_inline
10688 && self.focus_handle.is_focused(cx)
10689 && !self.newest_selection_head_on_empty_line(cx)
10690 && self.has_blame_entries(cx)
10691 }
10692
10693 fn has_blame_entries(&self, cx: &mut WindowContext) -> bool {
10694 self.blame()
10695 .map_or(false, |blame| blame.read(cx).has_generated_entries())
10696 }
10697
10698 fn newest_selection_head_on_empty_line(&mut self, cx: &mut WindowContext) -> bool {
10699 let cursor_anchor = self.selections.newest_anchor().head();
10700
10701 let snapshot = self.buffer.read(cx).snapshot(cx);
10702 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
10703
10704 snapshot.line_len(buffer_row) == 0
10705 }
10706
10707 fn get_permalink_to_line(&mut self, cx: &mut ViewContext<Self>) -> Result<url::Url> {
10708 let (path, selection, repo) = maybe!({
10709 let project_handle = self.project.as_ref()?.clone();
10710 let project = project_handle.read(cx);
10711
10712 let selection = self.selections.newest::<Point>(cx);
10713 let selection_range = selection.range();
10714
10715 let (buffer, selection) = if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10716 (buffer, selection_range.start.row..selection_range.end.row)
10717 } else {
10718 let buffer_ranges = self
10719 .buffer()
10720 .read(cx)
10721 .range_to_buffer_ranges(selection_range, cx);
10722
10723 let (buffer, range, _) = if selection.reversed {
10724 buffer_ranges.first()
10725 } else {
10726 buffer_ranges.last()
10727 }?;
10728
10729 let snapshot = buffer.read(cx).snapshot();
10730 let selection = text::ToPoint::to_point(&range.start, &snapshot).row
10731 ..text::ToPoint::to_point(&range.end, &snapshot).row;
10732 (buffer.clone(), selection)
10733 };
10734
10735 let path = buffer
10736 .read(cx)
10737 .file()?
10738 .as_local()?
10739 .path()
10740 .to_str()?
10741 .to_string();
10742 let repo = project.get_repo(&buffer.read(cx).project_path(cx)?, cx)?;
10743 Some((path, selection, repo))
10744 })
10745 .ok_or_else(|| anyhow!("unable to open git repository"))?;
10746
10747 const REMOTE_NAME: &str = "origin";
10748 let origin_url = repo
10749 .remote_url(REMOTE_NAME)
10750 .ok_or_else(|| anyhow!("remote \"{REMOTE_NAME}\" not found"))?;
10751 let sha = repo
10752 .head_sha()
10753 .ok_or_else(|| anyhow!("failed to read HEAD SHA"))?;
10754
10755 let (provider, remote) =
10756 parse_git_remote_url(GitHostingProviderRegistry::default_global(cx), &origin_url)
10757 .ok_or_else(|| anyhow!("failed to parse Git remote URL"))?;
10758
10759 Ok(provider.build_permalink(
10760 remote,
10761 BuildPermalinkParams {
10762 sha: &sha,
10763 path: &path,
10764 selection: Some(selection),
10765 },
10766 ))
10767 }
10768
10769 pub fn copy_permalink_to_line(&mut self, _: &CopyPermalinkToLine, cx: &mut ViewContext<Self>) {
10770 let permalink = self.get_permalink_to_line(cx);
10771
10772 match permalink {
10773 Ok(permalink) => {
10774 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
10775 }
10776 Err(err) => {
10777 let message = format!("Failed to copy permalink: {err}");
10778
10779 Err::<(), anyhow::Error>(err).log_err();
10780
10781 if let Some(workspace) = self.workspace() {
10782 workspace.update(cx, |workspace, cx| {
10783 struct CopyPermalinkToLine;
10784
10785 workspace.show_toast(
10786 Toast::new(NotificationId::unique::<CopyPermalinkToLine>(), message),
10787 cx,
10788 )
10789 })
10790 }
10791 }
10792 }
10793 }
10794
10795 pub fn open_permalink_to_line(&mut self, _: &OpenPermalinkToLine, cx: &mut ViewContext<Self>) {
10796 let permalink = self.get_permalink_to_line(cx);
10797
10798 match permalink {
10799 Ok(permalink) => {
10800 cx.open_url(permalink.as_ref());
10801 }
10802 Err(err) => {
10803 let message = format!("Failed to open permalink: {err}");
10804
10805 Err::<(), anyhow::Error>(err).log_err();
10806
10807 if let Some(workspace) = self.workspace() {
10808 workspace.update(cx, |workspace, cx| {
10809 struct OpenPermalinkToLine;
10810
10811 workspace.show_toast(
10812 Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
10813 cx,
10814 )
10815 })
10816 }
10817 }
10818 }
10819 }
10820
10821 /// Adds or removes (on `None` color) a highlight for the rows corresponding to the anchor range given.
10822 /// On matching anchor range, replaces the old highlight; does not clear the other existing highlights.
10823 /// If multiple anchor ranges will produce highlights for the same row, the last range added will be used.
10824 pub fn highlight_rows<T: 'static>(
10825 &mut self,
10826 rows: RangeInclusive<Anchor>,
10827 color: Option<Hsla>,
10828 should_autoscroll: bool,
10829 cx: &mut ViewContext<Self>,
10830 ) {
10831 let snapshot = self.buffer().read(cx).snapshot(cx);
10832 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
10833 let existing_highlight_index = row_highlights.binary_search_by(|highlight| {
10834 highlight
10835 .range
10836 .start()
10837 .cmp(&rows.start(), &snapshot)
10838 .then(highlight.range.end().cmp(&rows.end(), &snapshot))
10839 });
10840 match (color, existing_highlight_index) {
10841 (Some(_), Ok(ix)) | (_, Err(ix)) => row_highlights.insert(
10842 ix,
10843 RowHighlight {
10844 index: post_inc(&mut self.highlight_order),
10845 range: rows,
10846 should_autoscroll,
10847 color,
10848 },
10849 ),
10850 (None, Ok(i)) => {
10851 row_highlights.remove(i);
10852 }
10853 }
10854 }
10855
10856 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
10857 pub fn clear_row_highlights<T: 'static>(&mut self) {
10858 self.highlighted_rows.remove(&TypeId::of::<T>());
10859 }
10860
10861 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
10862 pub fn highlighted_rows<T: 'static>(
10863 &self,
10864 ) -> Option<impl Iterator<Item = (&RangeInclusive<Anchor>, Option<&Hsla>)>> {
10865 Some(
10866 self.highlighted_rows
10867 .get(&TypeId::of::<T>())?
10868 .iter()
10869 .map(|highlight| (&highlight.range, highlight.color.as_ref())),
10870 )
10871 }
10872
10873 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
10874 /// Rerturns a map of display rows that are highlighted and their corresponding highlight color.
10875 /// Allows to ignore certain kinds of highlights.
10876 pub fn highlighted_display_rows(
10877 &mut self,
10878 cx: &mut WindowContext,
10879 ) -> BTreeMap<DisplayRow, Hsla> {
10880 let snapshot = self.snapshot(cx);
10881 let mut used_highlight_orders = HashMap::default();
10882 self.highlighted_rows
10883 .iter()
10884 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
10885 .fold(
10886 BTreeMap::<DisplayRow, Hsla>::new(),
10887 |mut unique_rows, highlight| {
10888 let start_row = highlight.range.start().to_display_point(&snapshot).row();
10889 let end_row = highlight.range.end().to_display_point(&snapshot).row();
10890 for row in start_row.0..=end_row.0 {
10891 let used_index =
10892 used_highlight_orders.entry(row).or_insert(highlight.index);
10893 if highlight.index >= *used_index {
10894 *used_index = highlight.index;
10895 match highlight.color {
10896 Some(hsla) => unique_rows.insert(DisplayRow(row), hsla),
10897 None => unique_rows.remove(&DisplayRow(row)),
10898 };
10899 }
10900 }
10901 unique_rows
10902 },
10903 )
10904 }
10905
10906 pub fn highlighted_display_row_for_autoscroll(
10907 &self,
10908 snapshot: &DisplaySnapshot,
10909 ) -> Option<DisplayRow> {
10910 self.highlighted_rows
10911 .values()
10912 .flat_map(|highlighted_rows| highlighted_rows.iter())
10913 .filter_map(|highlight| {
10914 if highlight.color.is_none() || !highlight.should_autoscroll {
10915 return None;
10916 }
10917 Some(highlight.range.start().to_display_point(&snapshot).row())
10918 })
10919 .min()
10920 }
10921
10922 pub fn set_search_within_ranges(
10923 &mut self,
10924 ranges: &[Range<Anchor>],
10925 cx: &mut ViewContext<Self>,
10926 ) {
10927 self.highlight_background::<SearchWithinRange>(
10928 ranges,
10929 |colors| colors.editor_document_highlight_read_background,
10930 cx,
10931 )
10932 }
10933
10934 pub fn set_breadcrumb_header(&mut self, new_header: String) {
10935 self.breadcrumb_header = Some(new_header);
10936 }
10937
10938 pub fn clear_search_within_ranges(&mut self, cx: &mut ViewContext<Self>) {
10939 self.clear_background_highlights::<SearchWithinRange>(cx);
10940 }
10941
10942 pub fn highlight_background<T: 'static>(
10943 &mut self,
10944 ranges: &[Range<Anchor>],
10945 color_fetcher: fn(&ThemeColors) -> Hsla,
10946 cx: &mut ViewContext<Self>,
10947 ) {
10948 self.background_highlights
10949 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
10950 self.scrollbar_marker_state.dirty = true;
10951 cx.notify();
10952 }
10953
10954 pub fn clear_background_highlights<T: 'static>(
10955 &mut self,
10956 cx: &mut ViewContext<Self>,
10957 ) -> Option<BackgroundHighlight> {
10958 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
10959 if !text_highlights.1.is_empty() {
10960 self.scrollbar_marker_state.dirty = true;
10961 cx.notify();
10962 }
10963 Some(text_highlights)
10964 }
10965
10966 pub fn highlight_gutter<T: 'static>(
10967 &mut self,
10968 ranges: &[Range<Anchor>],
10969 color_fetcher: fn(&AppContext) -> Hsla,
10970 cx: &mut ViewContext<Self>,
10971 ) {
10972 self.gutter_highlights
10973 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
10974 cx.notify();
10975 }
10976
10977 pub fn clear_gutter_highlights<T: 'static>(
10978 &mut self,
10979 cx: &mut ViewContext<Self>,
10980 ) -> Option<GutterHighlight> {
10981 cx.notify();
10982 self.gutter_highlights.remove(&TypeId::of::<T>())
10983 }
10984
10985 #[cfg(feature = "test-support")]
10986 pub fn all_text_background_highlights(
10987 &mut self,
10988 cx: &mut ViewContext<Self>,
10989 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
10990 let snapshot = self.snapshot(cx);
10991 let buffer = &snapshot.buffer_snapshot;
10992 let start = buffer.anchor_before(0);
10993 let end = buffer.anchor_after(buffer.len());
10994 let theme = cx.theme().colors();
10995 self.background_highlights_in_range(start..end, &snapshot, theme)
10996 }
10997
10998 #[cfg(feature = "test-support")]
10999 pub fn search_background_highlights(
11000 &mut self,
11001 cx: &mut ViewContext<Self>,
11002 ) -> Vec<Range<Point>> {
11003 let snapshot = self.buffer().read(cx).snapshot(cx);
11004
11005 let highlights = self
11006 .background_highlights
11007 .get(&TypeId::of::<items::BufferSearchHighlights>());
11008
11009 if let Some((_color, ranges)) = highlights {
11010 ranges
11011 .iter()
11012 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
11013 .collect_vec()
11014 } else {
11015 vec![]
11016 }
11017 }
11018
11019 fn document_highlights_for_position<'a>(
11020 &'a self,
11021 position: Anchor,
11022 buffer: &'a MultiBufferSnapshot,
11023 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
11024 let read_highlights = self
11025 .background_highlights
11026 .get(&TypeId::of::<DocumentHighlightRead>())
11027 .map(|h| &h.1);
11028 let write_highlights = self
11029 .background_highlights
11030 .get(&TypeId::of::<DocumentHighlightWrite>())
11031 .map(|h| &h.1);
11032 let left_position = position.bias_left(buffer);
11033 let right_position = position.bias_right(buffer);
11034 read_highlights
11035 .into_iter()
11036 .chain(write_highlights)
11037 .flat_map(move |ranges| {
11038 let start_ix = match ranges.binary_search_by(|probe| {
11039 let cmp = probe.end.cmp(&left_position, buffer);
11040 if cmp.is_ge() {
11041 Ordering::Greater
11042 } else {
11043 Ordering::Less
11044 }
11045 }) {
11046 Ok(i) | Err(i) => i,
11047 };
11048
11049 ranges[start_ix..]
11050 .iter()
11051 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
11052 })
11053 }
11054
11055 pub fn has_background_highlights<T: 'static>(&self) -> bool {
11056 self.background_highlights
11057 .get(&TypeId::of::<T>())
11058 .map_or(false, |(_, highlights)| !highlights.is_empty())
11059 }
11060
11061 pub fn background_highlights_in_range(
11062 &self,
11063 search_range: Range<Anchor>,
11064 display_snapshot: &DisplaySnapshot,
11065 theme: &ThemeColors,
11066 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11067 let mut results = Vec::new();
11068 for (color_fetcher, ranges) in self.background_highlights.values() {
11069 let color = color_fetcher(theme);
11070 let start_ix = match ranges.binary_search_by(|probe| {
11071 let cmp = probe
11072 .end
11073 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11074 if cmp.is_gt() {
11075 Ordering::Greater
11076 } else {
11077 Ordering::Less
11078 }
11079 }) {
11080 Ok(i) | Err(i) => i,
11081 };
11082 for range in &ranges[start_ix..] {
11083 if range
11084 .start
11085 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11086 .is_ge()
11087 {
11088 break;
11089 }
11090
11091 let start = range.start.to_display_point(&display_snapshot);
11092 let end = range.end.to_display_point(&display_snapshot);
11093 results.push((start..end, color))
11094 }
11095 }
11096 results
11097 }
11098
11099 pub fn background_highlight_row_ranges<T: 'static>(
11100 &self,
11101 search_range: Range<Anchor>,
11102 display_snapshot: &DisplaySnapshot,
11103 count: usize,
11104 ) -> Vec<RangeInclusive<DisplayPoint>> {
11105 let mut results = Vec::new();
11106 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
11107 return vec![];
11108 };
11109
11110 let start_ix = match ranges.binary_search_by(|probe| {
11111 let cmp = probe
11112 .end
11113 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11114 if cmp.is_gt() {
11115 Ordering::Greater
11116 } else {
11117 Ordering::Less
11118 }
11119 }) {
11120 Ok(i) | Err(i) => i,
11121 };
11122 let mut push_region = |start: Option<Point>, end: Option<Point>| {
11123 if let (Some(start_display), Some(end_display)) = (start, end) {
11124 results.push(
11125 start_display.to_display_point(display_snapshot)
11126 ..=end_display.to_display_point(display_snapshot),
11127 );
11128 }
11129 };
11130 let mut start_row: Option<Point> = None;
11131 let mut end_row: Option<Point> = None;
11132 if ranges.len() > count {
11133 return Vec::new();
11134 }
11135 for range in &ranges[start_ix..] {
11136 if range
11137 .start
11138 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11139 .is_ge()
11140 {
11141 break;
11142 }
11143 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
11144 if let Some(current_row) = &end_row {
11145 if end.row == current_row.row {
11146 continue;
11147 }
11148 }
11149 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
11150 if start_row.is_none() {
11151 assert_eq!(end_row, None);
11152 start_row = Some(start);
11153 end_row = Some(end);
11154 continue;
11155 }
11156 if let Some(current_end) = end_row.as_mut() {
11157 if start.row > current_end.row + 1 {
11158 push_region(start_row, end_row);
11159 start_row = Some(start);
11160 end_row = Some(end);
11161 } else {
11162 // Merge two hunks.
11163 *current_end = end;
11164 }
11165 } else {
11166 unreachable!();
11167 }
11168 }
11169 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
11170 push_region(start_row, end_row);
11171 results
11172 }
11173
11174 pub fn gutter_highlights_in_range(
11175 &self,
11176 search_range: Range<Anchor>,
11177 display_snapshot: &DisplaySnapshot,
11178 cx: &AppContext,
11179 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11180 let mut results = Vec::new();
11181 for (color_fetcher, ranges) in self.gutter_highlights.values() {
11182 let color = color_fetcher(cx);
11183 let start_ix = match ranges.binary_search_by(|probe| {
11184 let cmp = probe
11185 .end
11186 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11187 if cmp.is_gt() {
11188 Ordering::Greater
11189 } else {
11190 Ordering::Less
11191 }
11192 }) {
11193 Ok(i) | Err(i) => i,
11194 };
11195 for range in &ranges[start_ix..] {
11196 if range
11197 .start
11198 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11199 .is_ge()
11200 {
11201 break;
11202 }
11203
11204 let start = range.start.to_display_point(&display_snapshot);
11205 let end = range.end.to_display_point(&display_snapshot);
11206 results.push((start..end, color))
11207 }
11208 }
11209 results
11210 }
11211
11212 /// Get the text ranges corresponding to the redaction query
11213 pub fn redacted_ranges(
11214 &self,
11215 search_range: Range<Anchor>,
11216 display_snapshot: &DisplaySnapshot,
11217 cx: &WindowContext,
11218 ) -> Vec<Range<DisplayPoint>> {
11219 display_snapshot
11220 .buffer_snapshot
11221 .redacted_ranges(search_range, |file| {
11222 if let Some(file) = file {
11223 file.is_private()
11224 && EditorSettings::get(Some(file.as_ref().into()), cx).redact_private_values
11225 } else {
11226 false
11227 }
11228 })
11229 .map(|range| {
11230 range.start.to_display_point(display_snapshot)
11231 ..range.end.to_display_point(display_snapshot)
11232 })
11233 .collect()
11234 }
11235
11236 pub fn highlight_text<T: 'static>(
11237 &mut self,
11238 ranges: Vec<Range<Anchor>>,
11239 style: HighlightStyle,
11240 cx: &mut ViewContext<Self>,
11241 ) {
11242 self.display_map.update(cx, |map, _| {
11243 map.highlight_text(TypeId::of::<T>(), ranges, style)
11244 });
11245 cx.notify();
11246 }
11247
11248 pub(crate) fn highlight_inlays<T: 'static>(
11249 &mut self,
11250 highlights: Vec<InlayHighlight>,
11251 style: HighlightStyle,
11252 cx: &mut ViewContext<Self>,
11253 ) {
11254 self.display_map.update(cx, |map, _| {
11255 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
11256 });
11257 cx.notify();
11258 }
11259
11260 pub fn text_highlights<'a, T: 'static>(
11261 &'a self,
11262 cx: &'a AppContext,
11263 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
11264 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
11265 }
11266
11267 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
11268 let cleared = self
11269 .display_map
11270 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
11271 if cleared {
11272 cx.notify();
11273 }
11274 }
11275
11276 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
11277 (self.read_only(cx) || self.blink_manager.read(cx).visible())
11278 && self.focus_handle.is_focused(cx)
11279 }
11280
11281 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut ViewContext<Self>) {
11282 self.show_cursor_when_unfocused = is_enabled;
11283 cx.notify();
11284 }
11285
11286 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
11287 cx.notify();
11288 }
11289
11290 fn on_buffer_event(
11291 &mut self,
11292 multibuffer: Model<MultiBuffer>,
11293 event: &multi_buffer::Event,
11294 cx: &mut ViewContext<Self>,
11295 ) {
11296 match event {
11297 multi_buffer::Event::Edited {
11298 singleton_buffer_edited,
11299 } => {
11300 self.scrollbar_marker_state.dirty = true;
11301 self.active_indent_guides_state.dirty = true;
11302 self.refresh_active_diagnostics(cx);
11303 self.refresh_code_actions(cx);
11304 if self.has_active_inline_completion(cx) {
11305 self.update_visible_inline_completion(cx);
11306 }
11307 cx.emit(EditorEvent::BufferEdited);
11308 cx.emit(SearchEvent::MatchesInvalidated);
11309 if *singleton_buffer_edited {
11310 if let Some(project) = &self.project {
11311 let project = project.read(cx);
11312 #[allow(clippy::mutable_key_type)]
11313 let languages_affected = multibuffer
11314 .read(cx)
11315 .all_buffers()
11316 .into_iter()
11317 .filter_map(|buffer| {
11318 let buffer = buffer.read(cx);
11319 let language = buffer.language()?;
11320 if project.is_local()
11321 && project.language_servers_for_buffer(buffer, cx).count() == 0
11322 {
11323 None
11324 } else {
11325 Some(language)
11326 }
11327 })
11328 .cloned()
11329 .collect::<HashSet<_>>();
11330 if !languages_affected.is_empty() {
11331 self.refresh_inlay_hints(
11332 InlayHintRefreshReason::BufferEdited(languages_affected),
11333 cx,
11334 );
11335 }
11336 }
11337 }
11338
11339 let Some(project) = &self.project else { return };
11340 let telemetry = project.read(cx).client().telemetry().clone();
11341 refresh_linked_ranges(self, cx);
11342 telemetry.log_edit_event("editor");
11343 }
11344 multi_buffer::Event::ExcerptsAdded {
11345 buffer,
11346 predecessor,
11347 excerpts,
11348 } => {
11349 self.tasks_update_task = Some(self.refresh_runnables(cx));
11350 cx.emit(EditorEvent::ExcerptsAdded {
11351 buffer: buffer.clone(),
11352 predecessor: *predecessor,
11353 excerpts: excerpts.clone(),
11354 });
11355 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
11356 }
11357 multi_buffer::Event::ExcerptsRemoved { ids } => {
11358 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
11359 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
11360 }
11361 multi_buffer::Event::ExcerptsEdited { ids } => {
11362 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
11363 }
11364 multi_buffer::Event::ExcerptsExpanded { ids } => {
11365 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
11366 }
11367 multi_buffer::Event::Reparsed(buffer_id) => {
11368 self.tasks_update_task = Some(self.refresh_runnables(cx));
11369
11370 cx.emit(EditorEvent::Reparsed(*buffer_id));
11371 }
11372 multi_buffer::Event::LanguageChanged(buffer_id) => {
11373 linked_editing_ranges::refresh_linked_ranges(self, cx);
11374 cx.emit(EditorEvent::Reparsed(*buffer_id));
11375 cx.notify();
11376 }
11377 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
11378 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
11379 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
11380 cx.emit(EditorEvent::TitleChanged)
11381 }
11382 multi_buffer::Event::DiffBaseChanged => {
11383 self.scrollbar_marker_state.dirty = true;
11384 cx.emit(EditorEvent::DiffBaseChanged);
11385 cx.notify();
11386 }
11387 multi_buffer::Event::DiffUpdated { buffer } => {
11388 self.sync_expanded_diff_hunks(buffer.clone(), cx);
11389 cx.notify();
11390 }
11391 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
11392 multi_buffer::Event::DiagnosticsUpdated => {
11393 self.refresh_active_diagnostics(cx);
11394 self.scrollbar_marker_state.dirty = true;
11395 cx.notify();
11396 }
11397 _ => {}
11398 };
11399 }
11400
11401 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
11402 cx.notify();
11403 }
11404
11405 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
11406 self.tasks_update_task = Some(self.refresh_runnables(cx));
11407 self.refresh_inline_completion(true, cx);
11408 self.refresh_inlay_hints(
11409 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
11410 self.selections.newest_anchor().head(),
11411 &self.buffer.read(cx).snapshot(cx),
11412 cx,
11413 )),
11414 cx,
11415 );
11416 let editor_settings = EditorSettings::get_global(cx);
11417 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
11418 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
11419
11420 let project_settings = ProjectSettings::get_global(cx);
11421 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
11422
11423 if self.mode == EditorMode::Full {
11424 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
11425 if self.git_blame_inline_enabled != inline_blame_enabled {
11426 self.toggle_git_blame_inline_internal(false, cx);
11427 }
11428 }
11429
11430 cx.notify();
11431 }
11432
11433 pub fn set_searchable(&mut self, searchable: bool) {
11434 self.searchable = searchable;
11435 }
11436
11437 pub fn searchable(&self) -> bool {
11438 self.searchable
11439 }
11440
11441 fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext<Self>) {
11442 self.open_excerpts_common(true, cx)
11443 }
11444
11445 fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
11446 self.open_excerpts_common(false, cx)
11447 }
11448
11449 fn open_excerpts_common(&mut self, split: bool, cx: &mut ViewContext<Self>) {
11450 let buffer = self.buffer.read(cx);
11451 if buffer.is_singleton() {
11452 cx.propagate();
11453 return;
11454 }
11455
11456 let Some(workspace) = self.workspace() else {
11457 cx.propagate();
11458 return;
11459 };
11460
11461 let mut new_selections_by_buffer = HashMap::default();
11462 for selection in self.selections.all::<usize>(cx) {
11463 for (buffer, mut range, _) in
11464 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
11465 {
11466 if selection.reversed {
11467 mem::swap(&mut range.start, &mut range.end);
11468 }
11469 new_selections_by_buffer
11470 .entry(buffer)
11471 .or_insert(Vec::new())
11472 .push(range)
11473 }
11474 }
11475
11476 // We defer the pane interaction because we ourselves are a workspace item
11477 // and activating a new item causes the pane to call a method on us reentrantly,
11478 // which panics if we're on the stack.
11479 cx.window_context().defer(move |cx| {
11480 workspace.update(cx, |workspace, cx| {
11481 let pane = if split {
11482 workspace.adjacent_pane(cx)
11483 } else {
11484 workspace.active_pane().clone()
11485 };
11486
11487 for (buffer, ranges) in new_selections_by_buffer {
11488 let editor =
11489 workspace.open_project_item::<Self>(pane.clone(), buffer, true, true, cx);
11490 editor.update(cx, |editor, cx| {
11491 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
11492 s.select_ranges(ranges);
11493 });
11494 });
11495 }
11496 })
11497 });
11498 }
11499
11500 fn jump(
11501 &mut self,
11502 path: ProjectPath,
11503 position: Point,
11504 anchor: language::Anchor,
11505 offset_from_top: u32,
11506 cx: &mut ViewContext<Self>,
11507 ) {
11508 let workspace = self.workspace();
11509 cx.spawn(|_, mut cx| async move {
11510 let workspace = workspace.ok_or_else(|| anyhow!("cannot jump without workspace"))?;
11511 let editor = workspace.update(&mut cx, |workspace, cx| {
11512 // Reset the preview item id before opening the new item
11513 workspace.active_pane().update(cx, |pane, cx| {
11514 pane.set_preview_item_id(None, cx);
11515 });
11516 workspace.open_path_preview(path, None, true, true, cx)
11517 })?;
11518 let editor = editor
11519 .await?
11520 .downcast::<Editor>()
11521 .ok_or_else(|| anyhow!("opened item was not an editor"))?
11522 .downgrade();
11523 editor.update(&mut cx, |editor, cx| {
11524 let buffer = editor
11525 .buffer()
11526 .read(cx)
11527 .as_singleton()
11528 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
11529 let buffer = buffer.read(cx);
11530 let cursor = if buffer.can_resolve(&anchor) {
11531 language::ToPoint::to_point(&anchor, buffer)
11532 } else {
11533 buffer.clip_point(position, Bias::Left)
11534 };
11535
11536 let nav_history = editor.nav_history.take();
11537 editor.change_selections(
11538 Some(Autoscroll::top_relative(offset_from_top as usize)),
11539 cx,
11540 |s| {
11541 s.select_ranges([cursor..cursor]);
11542 },
11543 );
11544 editor.nav_history = nav_history;
11545
11546 anyhow::Ok(())
11547 })??;
11548
11549 anyhow::Ok(())
11550 })
11551 .detach_and_log_err(cx);
11552 }
11553
11554 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
11555 let snapshot = self.buffer.read(cx).read(cx);
11556 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
11557 Some(
11558 ranges
11559 .iter()
11560 .map(move |range| {
11561 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
11562 })
11563 .collect(),
11564 )
11565 }
11566
11567 fn selection_replacement_ranges(
11568 &self,
11569 range: Range<OffsetUtf16>,
11570 cx: &AppContext,
11571 ) -> Vec<Range<OffsetUtf16>> {
11572 let selections = self.selections.all::<OffsetUtf16>(cx);
11573 let newest_selection = selections
11574 .iter()
11575 .max_by_key(|selection| selection.id)
11576 .unwrap();
11577 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
11578 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
11579 let snapshot = self.buffer.read(cx).read(cx);
11580 selections
11581 .into_iter()
11582 .map(|mut selection| {
11583 selection.start.0 =
11584 (selection.start.0 as isize).saturating_add(start_delta) as usize;
11585 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
11586 snapshot.clip_offset_utf16(selection.start, Bias::Left)
11587 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
11588 })
11589 .collect()
11590 }
11591
11592 fn report_editor_event(
11593 &self,
11594 operation: &'static str,
11595 file_extension: Option<String>,
11596 cx: &AppContext,
11597 ) {
11598 if cfg!(any(test, feature = "test-support")) {
11599 return;
11600 }
11601
11602 let Some(project) = &self.project else { return };
11603
11604 // If None, we are in a file without an extension
11605 let file = self
11606 .buffer
11607 .read(cx)
11608 .as_singleton()
11609 .and_then(|b| b.read(cx).file());
11610 let file_extension = file_extension.or(file
11611 .as_ref()
11612 .and_then(|file| Path::new(file.file_name(cx)).extension())
11613 .and_then(|e| e.to_str())
11614 .map(|a| a.to_string()));
11615
11616 let vim_mode = cx
11617 .global::<SettingsStore>()
11618 .raw_user_settings()
11619 .get("vim_mode")
11620 == Some(&serde_json::Value::Bool(true));
11621
11622 let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
11623 == language::language_settings::InlineCompletionProvider::Copilot;
11624 let copilot_enabled_for_language = self
11625 .buffer
11626 .read(cx)
11627 .settings_at(0, cx)
11628 .show_inline_completions;
11629
11630 let telemetry = project.read(cx).client().telemetry().clone();
11631 telemetry.report_editor_event(
11632 file_extension,
11633 vim_mode,
11634 operation,
11635 copilot_enabled,
11636 copilot_enabled_for_language,
11637 )
11638 }
11639
11640 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
11641 /// with each line being an array of {text, highlight} objects.
11642 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
11643 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
11644 return;
11645 };
11646
11647 #[derive(Serialize)]
11648 struct Chunk<'a> {
11649 text: String,
11650 highlight: Option<&'a str>,
11651 }
11652
11653 let snapshot = buffer.read(cx).snapshot();
11654 let range = self
11655 .selected_text_range(cx)
11656 .and_then(|selected_range| {
11657 if selected_range.is_empty() {
11658 None
11659 } else {
11660 Some(selected_range)
11661 }
11662 })
11663 .unwrap_or_else(|| 0..snapshot.len());
11664
11665 let chunks = snapshot.chunks(range, true);
11666 let mut lines = Vec::new();
11667 let mut line: VecDeque<Chunk> = VecDeque::new();
11668
11669 let Some(style) = self.style.as_ref() else {
11670 return;
11671 };
11672
11673 for chunk in chunks {
11674 let highlight = chunk
11675 .syntax_highlight_id
11676 .and_then(|id| id.name(&style.syntax));
11677 let mut chunk_lines = chunk.text.split('\n').peekable();
11678 while let Some(text) = chunk_lines.next() {
11679 let mut merged_with_last_token = false;
11680 if let Some(last_token) = line.back_mut() {
11681 if last_token.highlight == highlight {
11682 last_token.text.push_str(text);
11683 merged_with_last_token = true;
11684 }
11685 }
11686
11687 if !merged_with_last_token {
11688 line.push_back(Chunk {
11689 text: text.into(),
11690 highlight,
11691 });
11692 }
11693
11694 if chunk_lines.peek().is_some() {
11695 if line.len() > 1 && line.front().unwrap().text.is_empty() {
11696 line.pop_front();
11697 }
11698 if line.len() > 1 && line.back().unwrap().text.is_empty() {
11699 line.pop_back();
11700 }
11701
11702 lines.push(mem::take(&mut line));
11703 }
11704 }
11705 }
11706
11707 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
11708 return;
11709 };
11710 cx.write_to_clipboard(ClipboardItem::new_string(lines));
11711 }
11712
11713 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
11714 &self.inlay_hint_cache
11715 }
11716
11717 pub fn replay_insert_event(
11718 &mut self,
11719 text: &str,
11720 relative_utf16_range: Option<Range<isize>>,
11721 cx: &mut ViewContext<Self>,
11722 ) {
11723 if !self.input_enabled {
11724 cx.emit(EditorEvent::InputIgnored { text: text.into() });
11725 return;
11726 }
11727 if let Some(relative_utf16_range) = relative_utf16_range {
11728 let selections = self.selections.all::<OffsetUtf16>(cx);
11729 self.change_selections(None, cx, |s| {
11730 let new_ranges = selections.into_iter().map(|range| {
11731 let start = OffsetUtf16(
11732 range
11733 .head()
11734 .0
11735 .saturating_add_signed(relative_utf16_range.start),
11736 );
11737 let end = OffsetUtf16(
11738 range
11739 .head()
11740 .0
11741 .saturating_add_signed(relative_utf16_range.end),
11742 );
11743 start..end
11744 });
11745 s.select_ranges(new_ranges);
11746 });
11747 }
11748
11749 self.handle_input(text, cx);
11750 }
11751
11752 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
11753 let Some(project) = self.project.as_ref() else {
11754 return false;
11755 };
11756 let project = project.read(cx);
11757
11758 let mut supports = false;
11759 self.buffer().read(cx).for_each_buffer(|buffer| {
11760 if !supports {
11761 supports = project
11762 .language_servers_for_buffer(buffer.read(cx), cx)
11763 .any(
11764 |(_, server)| match server.capabilities().inlay_hint_provider {
11765 Some(lsp::OneOf::Left(enabled)) => enabled,
11766 Some(lsp::OneOf::Right(_)) => true,
11767 None => false,
11768 },
11769 )
11770 }
11771 });
11772 supports
11773 }
11774
11775 pub fn focus(&self, cx: &mut WindowContext) {
11776 cx.focus(&self.focus_handle)
11777 }
11778
11779 pub fn is_focused(&self, cx: &WindowContext) -> bool {
11780 self.focus_handle.is_focused(cx)
11781 }
11782
11783 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
11784 cx.emit(EditorEvent::Focused);
11785
11786 if let Some(descendant) = self
11787 .last_focused_descendant
11788 .take()
11789 .and_then(|descendant| descendant.upgrade())
11790 {
11791 cx.focus(&descendant);
11792 } else {
11793 if let Some(blame) = self.blame.as_ref() {
11794 blame.update(cx, GitBlame::focus)
11795 }
11796
11797 self.blink_manager.update(cx, BlinkManager::enable);
11798 self.show_cursor_names(cx);
11799 self.buffer.update(cx, |buffer, cx| {
11800 buffer.finalize_last_transaction(cx);
11801 if self.leader_peer_id.is_none() {
11802 buffer.set_active_selections(
11803 &self.selections.disjoint_anchors(),
11804 self.selections.line_mode,
11805 self.cursor_shape,
11806 cx,
11807 );
11808 }
11809 });
11810 }
11811 }
11812
11813 fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
11814 cx.emit(EditorEvent::FocusedIn)
11815 }
11816
11817 fn handle_focus_out(&mut self, event: FocusOutEvent, _cx: &mut ViewContext<Self>) {
11818 if event.blurred != self.focus_handle {
11819 self.last_focused_descendant = Some(event.blurred);
11820 }
11821 }
11822
11823 pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
11824 self.blink_manager.update(cx, BlinkManager::disable);
11825 self.buffer
11826 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
11827
11828 if let Some(blame) = self.blame.as_ref() {
11829 blame.update(cx, GitBlame::blur)
11830 }
11831 if !self.hover_state.focused(cx) {
11832 hide_hover(self, cx);
11833 }
11834
11835 self.hide_context_menu(cx);
11836 cx.emit(EditorEvent::Blurred);
11837 cx.notify();
11838 }
11839
11840 pub fn register_action<A: Action>(
11841 &mut self,
11842 listener: impl Fn(&A, &mut WindowContext) + 'static,
11843 ) -> Subscription {
11844 let id = self.next_editor_action_id.post_inc();
11845 let listener = Arc::new(listener);
11846 self.editor_actions.borrow_mut().insert(
11847 id,
11848 Box::new(move |cx| {
11849 let _view = cx.view().clone();
11850 let cx = cx.window_context();
11851 let listener = listener.clone();
11852 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
11853 let action = action.downcast_ref().unwrap();
11854 if phase == DispatchPhase::Bubble {
11855 listener(action, cx)
11856 }
11857 })
11858 }),
11859 );
11860
11861 let editor_actions = self.editor_actions.clone();
11862 Subscription::new(move || {
11863 editor_actions.borrow_mut().remove(&id);
11864 })
11865 }
11866
11867 pub fn file_header_size(&self) -> u32 {
11868 self.file_header_size
11869 }
11870
11871 pub fn revert(
11872 &mut self,
11873 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
11874 cx: &mut ViewContext<Self>,
11875 ) {
11876 self.buffer().update(cx, |multi_buffer, cx| {
11877 for (buffer_id, changes) in revert_changes {
11878 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
11879 buffer.update(cx, |buffer, cx| {
11880 buffer.edit(
11881 changes.into_iter().map(|(range, text)| {
11882 (range, text.to_string().map(Arc::<str>::from))
11883 }),
11884 None,
11885 cx,
11886 );
11887 });
11888 }
11889 }
11890 });
11891 self.change_selections(None, cx, |selections| selections.refresh());
11892 }
11893
11894 pub fn to_pixel_point(
11895 &mut self,
11896 source: multi_buffer::Anchor,
11897 editor_snapshot: &EditorSnapshot,
11898 cx: &mut ViewContext<Self>,
11899 ) -> Option<gpui::Point<Pixels>> {
11900 let source_point = source.to_display_point(editor_snapshot);
11901 self.display_to_pixel_point(source_point, editor_snapshot, cx)
11902 }
11903
11904 pub fn display_to_pixel_point(
11905 &mut self,
11906 source: DisplayPoint,
11907 editor_snapshot: &EditorSnapshot,
11908 cx: &mut ViewContext<Self>,
11909 ) -> Option<gpui::Point<Pixels>> {
11910 let line_height = self.style()?.text.line_height_in_pixels(cx.rem_size());
11911 let text_layout_details = self.text_layout_details(cx);
11912 let scroll_top = text_layout_details
11913 .scroll_anchor
11914 .scroll_position(editor_snapshot)
11915 .y;
11916
11917 if source.row().as_f32() < scroll_top.floor() {
11918 return None;
11919 }
11920 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
11921 let source_y = line_height * (source.row().as_f32() - scroll_top);
11922 Some(gpui::Point::new(source_x, source_y))
11923 }
11924
11925 fn gutter_bounds(&self) -> Option<Bounds<Pixels>> {
11926 let bounds = self.last_bounds?;
11927 Some(element::gutter_bounds(bounds, self.gutter_dimensions))
11928 }
11929
11930 pub fn has_active_completions_menu(&self) -> bool {
11931 self.context_menu.read().as_ref().map_or(false, |menu| {
11932 menu.visible() && matches!(menu, ContextMenu::Completions(_))
11933 })
11934 }
11935}
11936
11937fn hunks_for_selections(
11938 multi_buffer_snapshot: &MultiBufferSnapshot,
11939 selections: &[Selection<Anchor>],
11940) -> Vec<DiffHunk<MultiBufferRow>> {
11941 let buffer_rows_for_selections = selections.iter().map(|selection| {
11942 let head = selection.head();
11943 let tail = selection.tail();
11944 let start = MultiBufferRow(tail.to_point(&multi_buffer_snapshot).row);
11945 let end = MultiBufferRow(head.to_point(&multi_buffer_snapshot).row);
11946 if start > end {
11947 end..start
11948 } else {
11949 start..end
11950 }
11951 });
11952
11953 hunks_for_rows(buffer_rows_for_selections, multi_buffer_snapshot)
11954}
11955
11956pub fn hunks_for_rows(
11957 rows: impl Iterator<Item = Range<MultiBufferRow>>,
11958 multi_buffer_snapshot: &MultiBufferSnapshot,
11959) -> Vec<DiffHunk<MultiBufferRow>> {
11960 let mut hunks = Vec::new();
11961 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
11962 HashMap::default();
11963 for selected_multi_buffer_rows in rows {
11964 let query_rows =
11965 selected_multi_buffer_rows.start..selected_multi_buffer_rows.end.next_row();
11966 for hunk in multi_buffer_snapshot.git_diff_hunks_in_range(query_rows.clone()) {
11967 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
11968 // when the caret is just above or just below the deleted hunk.
11969 let allow_adjacent = hunk_status(&hunk) == DiffHunkStatus::Removed;
11970 let related_to_selection = if allow_adjacent {
11971 hunk.associated_range.overlaps(&query_rows)
11972 || hunk.associated_range.start == query_rows.end
11973 || hunk.associated_range.end == query_rows.start
11974 } else {
11975 // `selected_multi_buffer_rows` are inclusive (e.g. [2..2] means 2nd row is selected)
11976 // `hunk.associated_range` is exclusive (e.g. [2..3] means 2nd row is selected)
11977 hunk.associated_range.overlaps(&selected_multi_buffer_rows)
11978 || selected_multi_buffer_rows.end == hunk.associated_range.start
11979 };
11980 if related_to_selection {
11981 if !processed_buffer_rows
11982 .entry(hunk.buffer_id)
11983 .or_default()
11984 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
11985 {
11986 continue;
11987 }
11988 hunks.push(hunk);
11989 }
11990 }
11991 }
11992
11993 hunks
11994}
11995
11996pub trait CollaborationHub {
11997 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
11998 fn user_participant_indices<'a>(
11999 &self,
12000 cx: &'a AppContext,
12001 ) -> &'a HashMap<u64, ParticipantIndex>;
12002 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
12003}
12004
12005impl CollaborationHub for Model<Project> {
12006 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
12007 self.read(cx).collaborators()
12008 }
12009
12010 fn user_participant_indices<'a>(
12011 &self,
12012 cx: &'a AppContext,
12013 ) -> &'a HashMap<u64, ParticipantIndex> {
12014 self.read(cx).user_store().read(cx).participant_indices()
12015 }
12016
12017 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
12018 let this = self.read(cx);
12019 let user_ids = this.collaborators().values().map(|c| c.user_id);
12020 this.user_store().read_with(cx, |user_store, cx| {
12021 user_store.participant_names(user_ids, cx)
12022 })
12023 }
12024}
12025
12026pub trait CompletionProvider {
12027 fn completions(
12028 &self,
12029 buffer: &Model<Buffer>,
12030 buffer_position: text::Anchor,
12031 trigger: CompletionContext,
12032 cx: &mut ViewContext<Editor>,
12033 ) -> Task<Result<Vec<Completion>>>;
12034
12035 fn resolve_completions(
12036 &self,
12037 buffer: Model<Buffer>,
12038 completion_indices: Vec<usize>,
12039 completions: Arc<RwLock<Box<[Completion]>>>,
12040 cx: &mut ViewContext<Editor>,
12041 ) -> Task<Result<bool>>;
12042
12043 fn apply_additional_edits_for_completion(
12044 &self,
12045 buffer: Model<Buffer>,
12046 completion: Completion,
12047 push_to_history: bool,
12048 cx: &mut ViewContext<Editor>,
12049 ) -> Task<Result<Option<language::Transaction>>>;
12050
12051 fn is_completion_trigger(
12052 &self,
12053 buffer: &Model<Buffer>,
12054 position: language::Anchor,
12055 text: &str,
12056 trigger_in_words: bool,
12057 cx: &mut ViewContext<Editor>,
12058 ) -> bool;
12059
12060 fn sort_completions(&self) -> bool {
12061 true
12062 }
12063}
12064
12065fn snippet_completions(
12066 project: &Project,
12067 buffer: &Model<Buffer>,
12068 buffer_position: text::Anchor,
12069 cx: &mut AppContext,
12070) -> Vec<Completion> {
12071 let language = buffer.read(cx).language_at(buffer_position);
12072 let language_name = language.as_ref().map(|language| language.lsp_id());
12073 let snippet_store = project.snippets().read(cx);
12074 let snippets = snippet_store.snippets_for(language_name, cx);
12075
12076 if snippets.is_empty() {
12077 return vec![];
12078 }
12079 let snapshot = buffer.read(cx).text_snapshot();
12080 let chunks = snapshot.reversed_chunks_in_range(text::Anchor::MIN..buffer_position);
12081
12082 let mut lines = chunks.lines();
12083 let Some(line_at) = lines.next().filter(|line| !line.is_empty()) else {
12084 return vec![];
12085 };
12086
12087 let scope = language.map(|language| language.default_scope());
12088 let mut last_word = line_at
12089 .chars()
12090 .rev()
12091 .take_while(|c| char_kind(&scope, *c) == CharKind::Word)
12092 .collect::<String>();
12093 last_word = last_word.chars().rev().collect();
12094 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
12095 let to_lsp = |point: &text::Anchor| {
12096 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
12097 point_to_lsp(end)
12098 };
12099 let lsp_end = to_lsp(&buffer_position);
12100 snippets
12101 .into_iter()
12102 .filter_map(|snippet| {
12103 let matching_prefix = snippet
12104 .prefix
12105 .iter()
12106 .find(|prefix| prefix.starts_with(&last_word))?;
12107 let start = as_offset - last_word.len();
12108 let start = snapshot.anchor_before(start);
12109 let range = start..buffer_position;
12110 let lsp_start = to_lsp(&start);
12111 let lsp_range = lsp::Range {
12112 start: lsp_start,
12113 end: lsp_end,
12114 };
12115 Some(Completion {
12116 old_range: range,
12117 new_text: snippet.body.clone(),
12118 label: CodeLabel {
12119 text: matching_prefix.clone(),
12120 runs: vec![],
12121 filter_range: 0..matching_prefix.len(),
12122 },
12123 server_id: LanguageServerId(usize::MAX),
12124 documentation: snippet
12125 .description
12126 .clone()
12127 .map(|description| Documentation::SingleLine(description)),
12128 lsp_completion: lsp::CompletionItem {
12129 label: snippet.prefix.first().unwrap().clone(),
12130 kind: Some(CompletionItemKind::SNIPPET),
12131 label_details: snippet.description.as_ref().map(|description| {
12132 lsp::CompletionItemLabelDetails {
12133 detail: Some(description.clone()),
12134 description: None,
12135 }
12136 }),
12137 insert_text_format: Some(InsertTextFormat::SNIPPET),
12138 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
12139 lsp::InsertReplaceEdit {
12140 new_text: snippet.body.clone(),
12141 insert: lsp_range,
12142 replace: lsp_range,
12143 },
12144 )),
12145 filter_text: Some(snippet.body.clone()),
12146 sort_text: Some(char::MAX.to_string()),
12147 ..Default::default()
12148 },
12149 confirm: None,
12150 })
12151 })
12152 .collect()
12153}
12154
12155impl CompletionProvider for Model<Project> {
12156 fn completions(
12157 &self,
12158 buffer: &Model<Buffer>,
12159 buffer_position: text::Anchor,
12160 options: CompletionContext,
12161 cx: &mut ViewContext<Editor>,
12162 ) -> Task<Result<Vec<Completion>>> {
12163 self.update(cx, |project, cx| {
12164 let snippets = snippet_completions(project, buffer, buffer_position, cx);
12165 let project_completions = project.completions(&buffer, buffer_position, options, cx);
12166 cx.background_executor().spawn(async move {
12167 let mut completions = project_completions.await?;
12168 //let snippets = snippets.into_iter().;
12169 completions.extend(snippets);
12170 Ok(completions)
12171 })
12172 })
12173 }
12174
12175 fn resolve_completions(
12176 &self,
12177 buffer: Model<Buffer>,
12178 completion_indices: Vec<usize>,
12179 completions: Arc<RwLock<Box<[Completion]>>>,
12180 cx: &mut ViewContext<Editor>,
12181 ) -> Task<Result<bool>> {
12182 self.update(cx, |project, cx| {
12183 project.resolve_completions(buffer, completion_indices, completions, cx)
12184 })
12185 }
12186
12187 fn apply_additional_edits_for_completion(
12188 &self,
12189 buffer: Model<Buffer>,
12190 completion: Completion,
12191 push_to_history: bool,
12192 cx: &mut ViewContext<Editor>,
12193 ) -> Task<Result<Option<language::Transaction>>> {
12194 self.update(cx, |project, cx| {
12195 project.apply_additional_edits_for_completion(buffer, completion, push_to_history, cx)
12196 })
12197 }
12198
12199 fn is_completion_trigger(
12200 &self,
12201 buffer: &Model<Buffer>,
12202 position: language::Anchor,
12203 text: &str,
12204 trigger_in_words: bool,
12205 cx: &mut ViewContext<Editor>,
12206 ) -> bool {
12207 if !EditorSettings::get_global(cx).show_completions_on_input {
12208 return false;
12209 }
12210
12211 let mut chars = text.chars();
12212 let char = if let Some(char) = chars.next() {
12213 char
12214 } else {
12215 return false;
12216 };
12217 if chars.next().is_some() {
12218 return false;
12219 }
12220
12221 let buffer = buffer.read(cx);
12222 let scope = buffer.snapshot().language_scope_at(position);
12223 if trigger_in_words && char_kind(&scope, char) == CharKind::Word {
12224 return true;
12225 }
12226
12227 buffer
12228 .completion_triggers()
12229 .iter()
12230 .any(|string| string == text)
12231 }
12232}
12233
12234fn inlay_hint_settings(
12235 location: Anchor,
12236 snapshot: &MultiBufferSnapshot,
12237 cx: &mut ViewContext<'_, Editor>,
12238) -> InlayHintSettings {
12239 let file = snapshot.file_at(location);
12240 let language = snapshot.language_at(location);
12241 let settings = all_language_settings(file, cx);
12242 settings
12243 .language(language.map(|l| l.name()).as_deref())
12244 .inlay_hints
12245}
12246
12247fn consume_contiguous_rows(
12248 contiguous_row_selections: &mut Vec<Selection<Point>>,
12249 selection: &Selection<Point>,
12250 display_map: &DisplaySnapshot,
12251 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
12252) -> (MultiBufferRow, MultiBufferRow) {
12253 contiguous_row_selections.push(selection.clone());
12254 let start_row = MultiBufferRow(selection.start.row);
12255 let mut end_row = ending_row(selection, display_map);
12256
12257 while let Some(next_selection) = selections.peek() {
12258 if next_selection.start.row <= end_row.0 {
12259 end_row = ending_row(next_selection, display_map);
12260 contiguous_row_selections.push(selections.next().unwrap().clone());
12261 } else {
12262 break;
12263 }
12264 }
12265 (start_row, end_row)
12266}
12267
12268fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
12269 if next_selection.end.column > 0 || next_selection.is_empty() {
12270 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
12271 } else {
12272 MultiBufferRow(next_selection.end.row)
12273 }
12274}
12275
12276impl EditorSnapshot {
12277 pub fn remote_selections_in_range<'a>(
12278 &'a self,
12279 range: &'a Range<Anchor>,
12280 collaboration_hub: &dyn CollaborationHub,
12281 cx: &'a AppContext,
12282 ) -> impl 'a + Iterator<Item = RemoteSelection> {
12283 let participant_names = collaboration_hub.user_names(cx);
12284 let participant_indices = collaboration_hub.user_participant_indices(cx);
12285 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
12286 let collaborators_by_replica_id = collaborators_by_peer_id
12287 .iter()
12288 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
12289 .collect::<HashMap<_, _>>();
12290 self.buffer_snapshot
12291 .selections_in_range(range, false)
12292 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
12293 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
12294 let participant_index = participant_indices.get(&collaborator.user_id).copied();
12295 let user_name = participant_names.get(&collaborator.user_id).cloned();
12296 Some(RemoteSelection {
12297 replica_id,
12298 selection,
12299 cursor_shape,
12300 line_mode,
12301 participant_index,
12302 peer_id: collaborator.peer_id,
12303 user_name,
12304 })
12305 })
12306 }
12307
12308 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
12309 self.display_snapshot.buffer_snapshot.language_at(position)
12310 }
12311
12312 pub fn is_focused(&self) -> bool {
12313 self.is_focused
12314 }
12315
12316 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
12317 self.placeholder_text.as_ref()
12318 }
12319
12320 pub fn scroll_position(&self) -> gpui::Point<f32> {
12321 self.scroll_anchor.scroll_position(&self.display_snapshot)
12322 }
12323
12324 fn gutter_dimensions(
12325 &self,
12326 font_id: FontId,
12327 font_size: Pixels,
12328 em_width: Pixels,
12329 max_line_number_width: Pixels,
12330 cx: &AppContext,
12331 ) -> GutterDimensions {
12332 if !self.show_gutter {
12333 return GutterDimensions::default();
12334 }
12335 let descent = cx.text_system().descent(font_id, font_size);
12336
12337 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
12338 matches!(
12339 ProjectSettings::get_global(cx).git.git_gutter,
12340 Some(GitGutterSetting::TrackedFiles)
12341 )
12342 });
12343 let gutter_settings = EditorSettings::get_global(cx).gutter;
12344 let show_line_numbers = self
12345 .show_line_numbers
12346 .unwrap_or(gutter_settings.line_numbers);
12347 let line_gutter_width = if show_line_numbers {
12348 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
12349 let min_width_for_number_on_gutter = em_width * 4.0;
12350 max_line_number_width.max(min_width_for_number_on_gutter)
12351 } else {
12352 0.0.into()
12353 };
12354
12355 let show_code_actions = self
12356 .show_code_actions
12357 .unwrap_or(gutter_settings.code_actions);
12358
12359 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
12360
12361 let git_blame_entries_width = self
12362 .render_git_blame_gutter
12363 .then_some(em_width * GIT_BLAME_GUTTER_WIDTH_CHARS);
12364
12365 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
12366 left_padding += if show_code_actions || show_runnables {
12367 em_width * 3.0
12368 } else if show_git_gutter && show_line_numbers {
12369 em_width * 2.0
12370 } else if show_git_gutter || show_line_numbers {
12371 em_width
12372 } else {
12373 px(0.)
12374 };
12375
12376 let right_padding = if gutter_settings.folds && show_line_numbers {
12377 em_width * 4.0
12378 } else if gutter_settings.folds {
12379 em_width * 3.0
12380 } else if show_line_numbers {
12381 em_width
12382 } else {
12383 px(0.)
12384 };
12385
12386 GutterDimensions {
12387 left_padding,
12388 right_padding,
12389 width: line_gutter_width + left_padding + right_padding,
12390 margin: -descent,
12391 git_blame_entries_width,
12392 }
12393 }
12394
12395 pub fn render_fold_toggle(
12396 &self,
12397 buffer_row: MultiBufferRow,
12398 row_contains_cursor: bool,
12399 editor: View<Editor>,
12400 cx: &mut WindowContext,
12401 ) -> Option<AnyElement> {
12402 let folded = self.is_line_folded(buffer_row);
12403
12404 if let Some(crease) = self
12405 .crease_snapshot
12406 .query_row(buffer_row, &self.buffer_snapshot)
12407 {
12408 let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
12409 if folded {
12410 editor.update(cx, |editor, cx| {
12411 editor.fold_at(&crate::FoldAt { buffer_row }, cx)
12412 });
12413 } else {
12414 editor.update(cx, |editor, cx| {
12415 editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
12416 });
12417 }
12418 });
12419
12420 Some((crease.render_toggle)(
12421 buffer_row,
12422 folded,
12423 toggle_callback,
12424 cx,
12425 ))
12426 } else if folded
12427 || (self.starts_indent(buffer_row) && (row_contains_cursor || self.gutter_hovered))
12428 {
12429 Some(
12430 Disclosure::new(("indent-fold-indicator", buffer_row.0), !folded)
12431 .selected(folded)
12432 .on_click(cx.listener_for(&editor, move |this, _e, cx| {
12433 if folded {
12434 this.unfold_at(&UnfoldAt { buffer_row }, cx);
12435 } else {
12436 this.fold_at(&FoldAt { buffer_row }, cx);
12437 }
12438 }))
12439 .into_any_element(),
12440 )
12441 } else {
12442 None
12443 }
12444 }
12445
12446 pub fn render_crease_trailer(
12447 &self,
12448 buffer_row: MultiBufferRow,
12449 cx: &mut WindowContext,
12450 ) -> Option<AnyElement> {
12451 let folded = self.is_line_folded(buffer_row);
12452 let crease = self
12453 .crease_snapshot
12454 .query_row(buffer_row, &self.buffer_snapshot)?;
12455 Some((crease.render_trailer)(buffer_row, folded, cx))
12456 }
12457}
12458
12459impl Deref for EditorSnapshot {
12460 type Target = DisplaySnapshot;
12461
12462 fn deref(&self) -> &Self::Target {
12463 &self.display_snapshot
12464 }
12465}
12466
12467#[derive(Clone, Debug, PartialEq, Eq)]
12468pub enum EditorEvent {
12469 InputIgnored {
12470 text: Arc<str>,
12471 },
12472 InputHandled {
12473 utf16_range_to_replace: Option<Range<isize>>,
12474 text: Arc<str>,
12475 },
12476 ExcerptsAdded {
12477 buffer: Model<Buffer>,
12478 predecessor: ExcerptId,
12479 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
12480 },
12481 ExcerptsRemoved {
12482 ids: Vec<ExcerptId>,
12483 },
12484 ExcerptsEdited {
12485 ids: Vec<ExcerptId>,
12486 },
12487 ExcerptsExpanded {
12488 ids: Vec<ExcerptId>,
12489 },
12490 BufferEdited,
12491 Edited {
12492 transaction_id: clock::Lamport,
12493 },
12494 Reparsed(BufferId),
12495 Focused,
12496 FocusedIn,
12497 Blurred,
12498 DirtyChanged,
12499 Saved,
12500 TitleChanged,
12501 DiffBaseChanged,
12502 SelectionsChanged {
12503 local: bool,
12504 },
12505 ScrollPositionChanged {
12506 local: bool,
12507 autoscroll: bool,
12508 },
12509 Closed,
12510 TransactionUndone {
12511 transaction_id: clock::Lamport,
12512 },
12513 TransactionBegun {
12514 transaction_id: clock::Lamport,
12515 },
12516}
12517
12518impl EventEmitter<EditorEvent> for Editor {}
12519
12520impl FocusableView for Editor {
12521 fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
12522 self.focus_handle.clone()
12523 }
12524}
12525
12526impl Render for Editor {
12527 fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
12528 let settings = ThemeSettings::get_global(cx);
12529
12530 let text_style = match self.mode {
12531 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
12532 color: cx.theme().colors().editor_foreground,
12533 font_family: settings.ui_font.family.clone(),
12534 font_features: settings.ui_font.features.clone(),
12535 font_fallbacks: settings.ui_font.fallbacks.clone(),
12536 font_size: rems(0.875).into(),
12537 font_weight: settings.ui_font.weight,
12538 line_height: relative(settings.buffer_line_height.value()),
12539 ..Default::default()
12540 },
12541 EditorMode::Full => TextStyle {
12542 color: cx.theme().colors().editor_foreground,
12543 font_family: settings.buffer_font.family.clone(),
12544 font_features: settings.buffer_font.features.clone(),
12545 font_fallbacks: settings.buffer_font.fallbacks.clone(),
12546 font_size: settings.buffer_font_size(cx).into(),
12547 font_weight: settings.buffer_font.weight,
12548 line_height: relative(settings.buffer_line_height.value()),
12549 ..Default::default()
12550 },
12551 };
12552
12553 let background = match self.mode {
12554 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
12555 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
12556 EditorMode::Full => cx.theme().colors().editor_background,
12557 };
12558
12559 EditorElement::new(
12560 cx.view(),
12561 EditorStyle {
12562 background,
12563 local_player: cx.theme().players().local(),
12564 text: text_style,
12565 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
12566 syntax: cx.theme().syntax().clone(),
12567 status: cx.theme().status().clone(),
12568 inlay_hints_style: HighlightStyle {
12569 color: Some(cx.theme().status().hint),
12570 ..HighlightStyle::default()
12571 },
12572 suggestions_style: HighlightStyle {
12573 color: Some(cx.theme().status().predictive),
12574 ..HighlightStyle::default()
12575 },
12576 },
12577 )
12578 }
12579}
12580
12581impl ViewInputHandler for Editor {
12582 fn text_for_range(
12583 &mut self,
12584 range_utf16: Range<usize>,
12585 cx: &mut ViewContext<Self>,
12586 ) -> Option<String> {
12587 Some(
12588 self.buffer
12589 .read(cx)
12590 .read(cx)
12591 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
12592 .collect(),
12593 )
12594 }
12595
12596 fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
12597 // Prevent the IME menu from appearing when holding down an alphabetic key
12598 // while input is disabled.
12599 if !self.input_enabled {
12600 return None;
12601 }
12602
12603 let range = self.selections.newest::<OffsetUtf16>(cx).range();
12604 Some(range.start.0..range.end.0)
12605 }
12606
12607 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
12608 let snapshot = self.buffer.read(cx).read(cx);
12609 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
12610 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
12611 }
12612
12613 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
12614 self.clear_highlights::<InputComposition>(cx);
12615 self.ime_transaction.take();
12616 }
12617
12618 fn replace_text_in_range(
12619 &mut self,
12620 range_utf16: Option<Range<usize>>,
12621 text: &str,
12622 cx: &mut ViewContext<Self>,
12623 ) {
12624 if !self.input_enabled {
12625 cx.emit(EditorEvent::InputIgnored { text: text.into() });
12626 return;
12627 }
12628
12629 self.transact(cx, |this, cx| {
12630 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
12631 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
12632 Some(this.selection_replacement_ranges(range_utf16, cx))
12633 } else {
12634 this.marked_text_ranges(cx)
12635 };
12636
12637 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
12638 let newest_selection_id = this.selections.newest_anchor().id;
12639 this.selections
12640 .all::<OffsetUtf16>(cx)
12641 .iter()
12642 .zip(ranges_to_replace.iter())
12643 .find_map(|(selection, range)| {
12644 if selection.id == newest_selection_id {
12645 Some(
12646 (range.start.0 as isize - selection.head().0 as isize)
12647 ..(range.end.0 as isize - selection.head().0 as isize),
12648 )
12649 } else {
12650 None
12651 }
12652 })
12653 });
12654
12655 cx.emit(EditorEvent::InputHandled {
12656 utf16_range_to_replace: range_to_replace,
12657 text: text.into(),
12658 });
12659
12660 if let Some(new_selected_ranges) = new_selected_ranges {
12661 this.change_selections(None, cx, |selections| {
12662 selections.select_ranges(new_selected_ranges)
12663 });
12664 this.backspace(&Default::default(), cx);
12665 }
12666
12667 this.handle_input(text, cx);
12668 });
12669
12670 if let Some(transaction) = self.ime_transaction {
12671 self.buffer.update(cx, |buffer, cx| {
12672 buffer.group_until_transaction(transaction, cx);
12673 });
12674 }
12675
12676 self.unmark_text(cx);
12677 }
12678
12679 fn replace_and_mark_text_in_range(
12680 &mut self,
12681 range_utf16: Option<Range<usize>>,
12682 text: &str,
12683 new_selected_range_utf16: Option<Range<usize>>,
12684 cx: &mut ViewContext<Self>,
12685 ) {
12686 if !self.input_enabled {
12687 return;
12688 }
12689
12690 let transaction = self.transact(cx, |this, cx| {
12691 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
12692 let snapshot = this.buffer.read(cx).read(cx);
12693 if let Some(relative_range_utf16) = range_utf16.as_ref() {
12694 for marked_range in &mut marked_ranges {
12695 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
12696 marked_range.start.0 += relative_range_utf16.start;
12697 marked_range.start =
12698 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
12699 marked_range.end =
12700 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
12701 }
12702 }
12703 Some(marked_ranges)
12704 } else if let Some(range_utf16) = range_utf16 {
12705 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
12706 Some(this.selection_replacement_ranges(range_utf16, cx))
12707 } else {
12708 None
12709 };
12710
12711 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
12712 let newest_selection_id = this.selections.newest_anchor().id;
12713 this.selections
12714 .all::<OffsetUtf16>(cx)
12715 .iter()
12716 .zip(ranges_to_replace.iter())
12717 .find_map(|(selection, range)| {
12718 if selection.id == newest_selection_id {
12719 Some(
12720 (range.start.0 as isize - selection.head().0 as isize)
12721 ..(range.end.0 as isize - selection.head().0 as isize),
12722 )
12723 } else {
12724 None
12725 }
12726 })
12727 });
12728
12729 cx.emit(EditorEvent::InputHandled {
12730 utf16_range_to_replace: range_to_replace,
12731 text: text.into(),
12732 });
12733
12734 if let Some(ranges) = ranges_to_replace {
12735 this.change_selections(None, cx, |s| s.select_ranges(ranges));
12736 }
12737
12738 let marked_ranges = {
12739 let snapshot = this.buffer.read(cx).read(cx);
12740 this.selections
12741 .disjoint_anchors()
12742 .iter()
12743 .map(|selection| {
12744 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
12745 })
12746 .collect::<Vec<_>>()
12747 };
12748
12749 if text.is_empty() {
12750 this.unmark_text(cx);
12751 } else {
12752 this.highlight_text::<InputComposition>(
12753 marked_ranges.clone(),
12754 HighlightStyle {
12755 underline: Some(UnderlineStyle {
12756 thickness: px(1.),
12757 color: None,
12758 wavy: false,
12759 }),
12760 ..Default::default()
12761 },
12762 cx,
12763 );
12764 }
12765
12766 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
12767 let use_autoclose = this.use_autoclose;
12768 let use_auto_surround = this.use_auto_surround;
12769 this.set_use_autoclose(false);
12770 this.set_use_auto_surround(false);
12771 this.handle_input(text, cx);
12772 this.set_use_autoclose(use_autoclose);
12773 this.set_use_auto_surround(use_auto_surround);
12774
12775 if let Some(new_selected_range) = new_selected_range_utf16 {
12776 let snapshot = this.buffer.read(cx).read(cx);
12777 let new_selected_ranges = marked_ranges
12778 .into_iter()
12779 .map(|marked_range| {
12780 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
12781 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
12782 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
12783 snapshot.clip_offset_utf16(new_start, Bias::Left)
12784 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
12785 })
12786 .collect::<Vec<_>>();
12787
12788 drop(snapshot);
12789 this.change_selections(None, cx, |selections| {
12790 selections.select_ranges(new_selected_ranges)
12791 });
12792 }
12793 });
12794
12795 self.ime_transaction = self.ime_transaction.or(transaction);
12796 if let Some(transaction) = self.ime_transaction {
12797 self.buffer.update(cx, |buffer, cx| {
12798 buffer.group_until_transaction(transaction, cx);
12799 });
12800 }
12801
12802 if self.text_highlights::<InputComposition>(cx).is_none() {
12803 self.ime_transaction.take();
12804 }
12805 }
12806
12807 fn bounds_for_range(
12808 &mut self,
12809 range_utf16: Range<usize>,
12810 element_bounds: gpui::Bounds<Pixels>,
12811 cx: &mut ViewContext<Self>,
12812 ) -> Option<gpui::Bounds<Pixels>> {
12813 let text_layout_details = self.text_layout_details(cx);
12814 let style = &text_layout_details.editor_style;
12815 let font_id = cx.text_system().resolve_font(&style.text.font());
12816 let font_size = style.text.font_size.to_pixels(cx.rem_size());
12817 let line_height = style.text.line_height_in_pixels(cx.rem_size());
12818
12819 let em_width = cx
12820 .text_system()
12821 .typographic_bounds(font_id, font_size, 'm')
12822 .unwrap()
12823 .size
12824 .width;
12825
12826 let snapshot = self.snapshot(cx);
12827 let scroll_position = snapshot.scroll_position();
12828 let scroll_left = scroll_position.x * em_width;
12829
12830 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
12831 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
12832 + self.gutter_dimensions.width;
12833 let y = line_height * (start.row().as_f32() - scroll_position.y);
12834
12835 Some(Bounds {
12836 origin: element_bounds.origin + point(x, y),
12837 size: size(em_width, line_height),
12838 })
12839 }
12840}
12841
12842trait SelectionExt {
12843 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
12844 fn spanned_rows(
12845 &self,
12846 include_end_if_at_line_start: bool,
12847 map: &DisplaySnapshot,
12848 ) -> Range<MultiBufferRow>;
12849}
12850
12851impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
12852 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
12853 let start = self
12854 .start
12855 .to_point(&map.buffer_snapshot)
12856 .to_display_point(map);
12857 let end = self
12858 .end
12859 .to_point(&map.buffer_snapshot)
12860 .to_display_point(map);
12861 if self.reversed {
12862 end..start
12863 } else {
12864 start..end
12865 }
12866 }
12867
12868 fn spanned_rows(
12869 &self,
12870 include_end_if_at_line_start: bool,
12871 map: &DisplaySnapshot,
12872 ) -> Range<MultiBufferRow> {
12873 let start = self.start.to_point(&map.buffer_snapshot);
12874 let mut end = self.end.to_point(&map.buffer_snapshot);
12875 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
12876 end.row -= 1;
12877 }
12878
12879 let buffer_start = map.prev_line_boundary(start).0;
12880 let buffer_end = map.next_line_boundary(end).0;
12881 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
12882 }
12883}
12884
12885impl<T: InvalidationRegion> InvalidationStack<T> {
12886 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
12887 where
12888 S: Clone + ToOffset,
12889 {
12890 while let Some(region) = self.last() {
12891 let all_selections_inside_invalidation_ranges =
12892 if selections.len() == region.ranges().len() {
12893 selections
12894 .iter()
12895 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
12896 .all(|(selection, invalidation_range)| {
12897 let head = selection.head().to_offset(buffer);
12898 invalidation_range.start <= head && invalidation_range.end >= head
12899 })
12900 } else {
12901 false
12902 };
12903
12904 if all_selections_inside_invalidation_ranges {
12905 break;
12906 } else {
12907 self.pop();
12908 }
12909 }
12910 }
12911}
12912
12913impl<T> Default for InvalidationStack<T> {
12914 fn default() -> Self {
12915 Self(Default::default())
12916 }
12917}
12918
12919impl<T> Deref for InvalidationStack<T> {
12920 type Target = Vec<T>;
12921
12922 fn deref(&self) -> &Self::Target {
12923 &self.0
12924 }
12925}
12926
12927impl<T> DerefMut for InvalidationStack<T> {
12928 fn deref_mut(&mut self) -> &mut Self::Target {
12929 &mut self.0
12930 }
12931}
12932
12933impl InvalidationRegion for SnippetState {
12934 fn ranges(&self) -> &[Range<Anchor>] {
12935 &self.ranges[self.active_index]
12936 }
12937}
12938
12939pub fn diagnostic_block_renderer(
12940 diagnostic: Diagnostic,
12941 max_message_rows: Option<u8>,
12942 allow_closing: bool,
12943 _is_valid: bool,
12944) -> RenderBlock {
12945 let (text_without_backticks, code_ranges) =
12946 highlight_diagnostic_message(&diagnostic, max_message_rows);
12947
12948 Box::new(move |cx: &mut BlockContext| {
12949 let group_id: SharedString = cx.block_id.to_string().into();
12950
12951 let mut text_style = cx.text_style().clone();
12952 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
12953 let theme_settings = ThemeSettings::get_global(cx);
12954 text_style.font_family = theme_settings.buffer_font.family.clone();
12955 text_style.font_style = theme_settings.buffer_font.style;
12956 text_style.font_features = theme_settings.buffer_font.features.clone();
12957 text_style.font_weight = theme_settings.buffer_font.weight;
12958
12959 let multi_line_diagnostic = diagnostic.message.contains('\n');
12960
12961 let buttons = |diagnostic: &Diagnostic, block_id: BlockId| {
12962 if multi_line_diagnostic {
12963 v_flex()
12964 } else {
12965 h_flex()
12966 }
12967 .when(allow_closing, |div| {
12968 div.children(diagnostic.is_primary.then(|| {
12969 IconButton::new(("close-block", EntityId::from(block_id)), IconName::XCircle)
12970 .icon_color(Color::Muted)
12971 .size(ButtonSize::Compact)
12972 .style(ButtonStyle::Transparent)
12973 .visible_on_hover(group_id.clone())
12974 .on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
12975 .tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
12976 }))
12977 })
12978 .child(
12979 IconButton::new(("copy-block", EntityId::from(block_id)), IconName::Copy)
12980 .icon_color(Color::Muted)
12981 .size(ButtonSize::Compact)
12982 .style(ButtonStyle::Transparent)
12983 .visible_on_hover(group_id.clone())
12984 .on_click({
12985 let message = diagnostic.message.clone();
12986 move |_click, cx| {
12987 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
12988 }
12989 })
12990 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
12991 )
12992 };
12993
12994 let icon_size = buttons(&diagnostic, cx.block_id)
12995 .into_any_element()
12996 .layout_as_root(AvailableSpace::min_size(), cx);
12997
12998 h_flex()
12999 .id(cx.block_id)
13000 .group(group_id.clone())
13001 .relative()
13002 .size_full()
13003 .pl(cx.gutter_dimensions.width)
13004 .w(cx.max_width + cx.gutter_dimensions.width)
13005 .child(
13006 div()
13007 .flex()
13008 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
13009 .flex_shrink(),
13010 )
13011 .child(buttons(&diagnostic, cx.block_id))
13012 .child(div().flex().flex_shrink_0().child(
13013 StyledText::new(text_without_backticks.clone()).with_highlights(
13014 &text_style,
13015 code_ranges.iter().map(|range| {
13016 (
13017 range.clone(),
13018 HighlightStyle {
13019 font_weight: Some(FontWeight::BOLD),
13020 ..Default::default()
13021 },
13022 )
13023 }),
13024 ),
13025 ))
13026 .into_any_element()
13027 })
13028}
13029
13030pub fn highlight_diagnostic_message(
13031 diagnostic: &Diagnostic,
13032 mut max_message_rows: Option<u8>,
13033) -> (SharedString, Vec<Range<usize>>) {
13034 let mut text_without_backticks = String::new();
13035 let mut code_ranges = Vec::new();
13036
13037 if let Some(source) = &diagnostic.source {
13038 text_without_backticks.push_str(&source);
13039 code_ranges.push(0..source.len());
13040 text_without_backticks.push_str(": ");
13041 }
13042
13043 let mut prev_offset = 0;
13044 let mut in_code_block = false;
13045 let has_row_limit = max_message_rows.is_some();
13046 let mut newline_indices = diagnostic
13047 .message
13048 .match_indices('\n')
13049 .filter(|_| has_row_limit)
13050 .map(|(ix, _)| ix)
13051 .fuse()
13052 .peekable();
13053
13054 for (quote_ix, _) in diagnostic
13055 .message
13056 .match_indices('`')
13057 .chain([(diagnostic.message.len(), "")])
13058 {
13059 let mut first_newline_ix = None;
13060 let mut last_newline_ix = None;
13061 while let Some(newline_ix) = newline_indices.peek() {
13062 if *newline_ix < quote_ix {
13063 if first_newline_ix.is_none() {
13064 first_newline_ix = Some(*newline_ix);
13065 }
13066 last_newline_ix = Some(*newline_ix);
13067
13068 if let Some(rows_left) = &mut max_message_rows {
13069 if *rows_left == 0 {
13070 break;
13071 } else {
13072 *rows_left -= 1;
13073 }
13074 }
13075 let _ = newline_indices.next();
13076 } else {
13077 break;
13078 }
13079 }
13080 let prev_len = text_without_backticks.len();
13081 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
13082 text_without_backticks.push_str(new_text);
13083 if in_code_block {
13084 code_ranges.push(prev_len..text_without_backticks.len());
13085 }
13086 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
13087 in_code_block = !in_code_block;
13088 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
13089 text_without_backticks.push_str("...");
13090 break;
13091 }
13092 }
13093
13094 (text_without_backticks.into(), code_ranges)
13095}
13096
13097fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
13098 match severity {
13099 DiagnosticSeverity::ERROR => colors.error,
13100 DiagnosticSeverity::WARNING => colors.warning,
13101 DiagnosticSeverity::INFORMATION => colors.info,
13102 DiagnosticSeverity::HINT => colors.info,
13103 _ => colors.ignored,
13104 }
13105}
13106
13107pub fn styled_runs_for_code_label<'a>(
13108 label: &'a CodeLabel,
13109 syntax_theme: &'a theme::SyntaxTheme,
13110) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
13111 let fade_out = HighlightStyle {
13112 fade_out: Some(0.35),
13113 ..Default::default()
13114 };
13115
13116 let mut prev_end = label.filter_range.end;
13117 label
13118 .runs
13119 .iter()
13120 .enumerate()
13121 .flat_map(move |(ix, (range, highlight_id))| {
13122 let style = if let Some(style) = highlight_id.style(syntax_theme) {
13123 style
13124 } else {
13125 return Default::default();
13126 };
13127 let mut muted_style = style;
13128 muted_style.highlight(fade_out);
13129
13130 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
13131 if range.start >= label.filter_range.end {
13132 if range.start > prev_end {
13133 runs.push((prev_end..range.start, fade_out));
13134 }
13135 runs.push((range.clone(), muted_style));
13136 } else if range.end <= label.filter_range.end {
13137 runs.push((range.clone(), style));
13138 } else {
13139 runs.push((range.start..label.filter_range.end, style));
13140 runs.push((label.filter_range.end..range.end, muted_style));
13141 }
13142 prev_end = cmp::max(prev_end, range.end);
13143
13144 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
13145 runs.push((prev_end..label.text.len(), fade_out));
13146 }
13147
13148 runs
13149 })
13150}
13151
13152pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
13153 let mut prev_index = 0;
13154 let mut prev_codepoint: Option<char> = None;
13155 text.char_indices()
13156 .chain([(text.len(), '\0')])
13157 .filter_map(move |(index, codepoint)| {
13158 let prev_codepoint = prev_codepoint.replace(codepoint)?;
13159 let is_boundary = index == text.len()
13160 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
13161 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
13162 if is_boundary {
13163 let chunk = &text[prev_index..index];
13164 prev_index = index;
13165 Some(chunk)
13166 } else {
13167 None
13168 }
13169 })
13170}
13171
13172pub trait RangeToAnchorExt: Sized {
13173 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
13174
13175 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
13176 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
13177 anchor_range.start.to_display_point(&snapshot)..anchor_range.end.to_display_point(&snapshot)
13178 }
13179}
13180
13181impl<T: ToOffset> RangeToAnchorExt for Range<T> {
13182 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
13183 let start_offset = self.start.to_offset(snapshot);
13184 let end_offset = self.end.to_offset(snapshot);
13185 if start_offset == end_offset {
13186 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
13187 } else {
13188 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
13189 }
13190 }
13191}
13192
13193pub trait RowExt {
13194 fn as_f32(&self) -> f32;
13195
13196 fn next_row(&self) -> Self;
13197
13198 fn previous_row(&self) -> Self;
13199
13200 fn minus(&self, other: Self) -> u32;
13201}
13202
13203impl RowExt for DisplayRow {
13204 fn as_f32(&self) -> f32 {
13205 self.0 as f32
13206 }
13207
13208 fn next_row(&self) -> Self {
13209 Self(self.0 + 1)
13210 }
13211
13212 fn previous_row(&self) -> Self {
13213 Self(self.0.saturating_sub(1))
13214 }
13215
13216 fn minus(&self, other: Self) -> u32 {
13217 self.0 - other.0
13218 }
13219}
13220
13221impl RowExt for MultiBufferRow {
13222 fn as_f32(&self) -> f32 {
13223 self.0 as f32
13224 }
13225
13226 fn next_row(&self) -> Self {
13227 Self(self.0 + 1)
13228 }
13229
13230 fn previous_row(&self) -> Self {
13231 Self(self.0.saturating_sub(1))
13232 }
13233
13234 fn minus(&self, other: Self) -> u32 {
13235 self.0 - other.0
13236 }
13237}
13238
13239trait RowRangeExt {
13240 type Row;
13241
13242 fn len(&self) -> usize;
13243
13244 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
13245}
13246
13247impl RowRangeExt for Range<MultiBufferRow> {
13248 type Row = MultiBufferRow;
13249
13250 fn len(&self) -> usize {
13251 (self.end.0 - self.start.0) as usize
13252 }
13253
13254 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
13255 (self.start.0..self.end.0).map(MultiBufferRow)
13256 }
13257}
13258
13259impl RowRangeExt for Range<DisplayRow> {
13260 type Row = DisplayRow;
13261
13262 fn len(&self) -> usize {
13263 (self.end.0 - self.start.0) as usize
13264 }
13265
13266 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
13267 (self.start.0..self.end.0).map(DisplayRow)
13268 }
13269}
13270
13271fn hunk_status(hunk: &DiffHunk<MultiBufferRow>) -> DiffHunkStatus {
13272 if hunk.diff_base_byte_range.is_empty() {
13273 DiffHunkStatus::Added
13274 } else if hunk.associated_range.is_empty() {
13275 DiffHunkStatus::Removed
13276 } else {
13277 DiffHunkStatus::Modified
13278 }
13279}