1#![allow(rustdoc::private_intra_doc_links)]
2//! This is the place where everything editor-related is stored (data-wise) and displayed (ui-wise).
3//! The main point of interest in this crate is [`Editor`] type, which is used in every other Zed part as a user input element.
4//! It comes in different flavors: single line, multiline and a fixed height one.
5//!
6//! Editor contains of multiple large submodules:
7//! * [`element`] — the place where all rendering happens
8//! * [`display_map`] - chunks up text in the editor into the logical blocks, establishes coordinates and mapping between each of them.
9//! Contains all metadata related to text transformations (folds, fake inlay text insertions, soft wraps, tab markup, etc.).
10//! * [`inlay_hint_cache`] - is a storage of inlay hints out of LSP requests, responsible for querying LSP and updating `display_map`'s state accordingly.
11//!
12//! All other submodules and structs are mostly concerned with holding editor data about the way it displays current buffer region(s).
13//!
14//! If you're looking to improve Vim mode, you should check out Vim crate that wraps Editor and overrides its behaviour.
15pub mod actions;
16mod blame_entry_tooltip;
17mod blink_manager;
18mod debounced_delay;
19pub mod display_map;
20mod editor_settings;
21mod element;
22mod git;
23mod highlight_matching_bracket;
24mod hover_links;
25mod hover_popover;
26mod hunk_diff;
27mod indent_guides;
28mod inlay_hint_cache;
29mod inline_completion_provider;
30pub mod items;
31mod linked_editing_ranges;
32mod mouse_context_menu;
33pub mod movement;
34mod persistence;
35mod rust_analyzer_ext;
36pub mod scroll;
37mod selections_collection;
38pub mod tasks;
39
40#[cfg(test)]
41mod editor_tests;
42mod signature_help;
43#[cfg(any(test, feature = "test-support"))]
44pub mod test;
45
46use ::git::diff::{DiffHunk, DiffHunkStatus};
47use ::git::{parse_git_remote_url, BuildPermalinkParams, GitHostingProviderRegistry};
48pub(crate) use actions::*;
49use aho_corasick::AhoCorasick;
50use anyhow::{anyhow, Context as _, Result};
51use blink_manager::BlinkManager;
52use client::{Collaborator, ParticipantIndex};
53use clock::ReplicaId;
54use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
55use convert_case::{Case, Casing};
56use debounced_delay::DebouncedDelay;
57use display_map::*;
58pub use display_map::{DisplayPoint, FoldPlaceholder};
59pub use editor_settings::{CurrentLineHighlight, EditorSettings};
60use element::LineWithInvisibles;
61pub use element::{
62 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
63};
64use futures::FutureExt;
65use fuzzy::{StringMatch, StringMatchCandidate};
66use git::blame::GitBlame;
67use git::diff_hunk_to_display;
68use gpui::{
69 div, impl_actions, point, prelude::*, px, relative, size, uniform_list, Action, AnyElement,
70 AppContext, AsyncWindowContext, AvailableSpace, BackgroundExecutor, Bounds, ClipboardItem,
71 Context, DispatchPhase, ElementId, EntityId, EventEmitter, FocusHandle, FocusOutEvent,
72 FocusableView, FontId, FontWeight, HighlightStyle, Hsla, InteractiveText, KeyContext,
73 ListSizingBehavior, Model, MouseButton, PaintQuad, ParentElement, Pixels, Render, SharedString,
74 Size, StrikethroughStyle, Styled, StyledText, Subscription, Task, TextStyle, UnderlineStyle,
75 UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext, WeakFocusHandle,
76 WeakView, WindowContext,
77};
78use highlight_matching_bracket::refresh_matching_bracket_highlights;
79use hover_popover::{hide_hover, HoverState};
80use hunk_diff::ExpandedHunks;
81pub(crate) use hunk_diff::HoveredHunk;
82use indent_guides::ActiveIndentGuidesState;
83use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
84pub use inline_completion_provider::*;
85pub use items::MAX_TAB_TITLE_LEN;
86use itertools::Itertools;
87use language::{
88 char_kind,
89 language_settings::{self, all_language_settings, InlayHintSettings},
90 markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
91 CursorShape, Diagnostic, Documentation, IndentKind, IndentSize, Language, OffsetRangeExt,
92 Point, Selection, SelectionGoal, TransactionId,
93};
94use language::{point_to_lsp, BufferRow, Runnable, RunnableRange};
95use linked_editing_ranges::refresh_linked_ranges;
96use task::{ResolvedTask, TaskTemplate, TaskVariables};
97
98use hover_links::{HoverLink, HoveredLinkState, InlayHighlight};
99pub use lsp::CompletionContext;
100use lsp::{
101 CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity, InsertTextFormat,
102 LanguageServerId,
103};
104use mouse_context_menu::MouseContextMenu;
105use movement::TextLayoutDetails;
106pub use multi_buffer::{
107 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
108 ToPoint,
109};
110use multi_buffer::{ExpandExcerptDirection, MultiBufferPoint, MultiBufferRow, ToOffsetUtf16};
111use ordered_float::OrderedFloat;
112use parking_lot::{Mutex, RwLock};
113use project::project_settings::{GitGutterSetting, ProjectSettings};
114use project::{
115 CodeAction, Completion, FormatTrigger, Item, Location, Project, ProjectPath,
116 ProjectTransaction, TaskSourceKind, WorktreeId,
117};
118use rand::prelude::*;
119use rpc::{proto::*, ErrorExt};
120use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
121use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
122use serde::{Deserialize, Serialize};
123use settings::{update_settings_file, Settings, SettingsStore};
124use smallvec::SmallVec;
125use snippet::Snippet;
126use std::{
127 any::TypeId,
128 borrow::Cow,
129 cell::RefCell,
130 cmp::{self, Ordering, Reverse},
131 mem,
132 num::NonZeroU32,
133 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
134 path::{Path, PathBuf},
135 rc::Rc,
136 sync::Arc,
137 time::{Duration, Instant},
138};
139pub use sum_tree::Bias;
140use sum_tree::TreeMap;
141use text::{BufferId, OffsetUtf16, Rope};
142use theme::{
143 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
144 ThemeColors, ThemeSettings,
145};
146use ui::{
147 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize,
148 ListItem, Popover, Tooltip,
149};
150use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
151use workspace::item::{ItemHandle, PreviewTabsSettings};
152use workspace::notifications::{DetachAndPromptErr, NotificationId};
153use workspace::{
154 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
155};
156use workspace::{OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
157
158use crate::hover_links::find_url;
159use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
160
161pub const FILE_HEADER_HEIGHT: u8 = 1;
162pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u8 = 1;
163pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u8 = 1;
164pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
165const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
166const MAX_LINE_LEN: usize = 1024;
167const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
168const MAX_SELECTION_HISTORY_LEN: usize = 1024;
169pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
170#[doc(hidden)]
171pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
172#[doc(hidden)]
173pub const DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
174
175pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
176
177pub fn render_parsed_markdown(
178 element_id: impl Into<ElementId>,
179 parsed: &language::ParsedMarkdown,
180 editor_style: &EditorStyle,
181 workspace: Option<WeakView<Workspace>>,
182 cx: &mut WindowContext,
183) -> InteractiveText {
184 let code_span_background_color = cx
185 .theme()
186 .colors()
187 .editor_document_highlight_read_background;
188
189 let highlights = gpui::combine_highlights(
190 parsed.highlights.iter().filter_map(|(range, highlight)| {
191 let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
192 Some((range.clone(), highlight))
193 }),
194 parsed
195 .regions
196 .iter()
197 .zip(&parsed.region_ranges)
198 .filter_map(|(region, range)| {
199 if region.code {
200 Some((
201 range.clone(),
202 HighlightStyle {
203 background_color: Some(code_span_background_color),
204 ..Default::default()
205 },
206 ))
207 } else {
208 None
209 }
210 }),
211 );
212
213 let mut links = Vec::new();
214 let mut link_ranges = Vec::new();
215 for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
216 if let Some(link) = region.link.clone() {
217 links.push(link);
218 link_ranges.push(range.clone());
219 }
220 }
221
222 InteractiveText::new(
223 element_id,
224 StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
225 )
226 .on_click(link_ranges, move |clicked_range_ix, cx| {
227 match &links[clicked_range_ix] {
228 markdown::Link::Web { url } => cx.open_url(url),
229 markdown::Link::Path { path } => {
230 if let Some(workspace) = &workspace {
231 _ = workspace.update(cx, |workspace, cx| {
232 workspace.open_abs_path(path.clone(), false, cx).detach();
233 });
234 }
235 }
236 }
237 })
238}
239
240#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
241pub(crate) enum InlayId {
242 Suggestion(usize),
243 Hint(usize),
244}
245
246impl InlayId {
247 fn id(&self) -> usize {
248 match self {
249 Self::Suggestion(id) => *id,
250 Self::Hint(id) => *id,
251 }
252 }
253}
254
255enum DiffRowHighlight {}
256enum DocumentHighlightRead {}
257enum DocumentHighlightWrite {}
258enum InputComposition {}
259
260#[derive(Copy, Clone, PartialEq, Eq)]
261pub enum Direction {
262 Prev,
263 Next,
264}
265
266pub fn init_settings(cx: &mut AppContext) {
267 EditorSettings::register(cx);
268}
269
270pub fn init(cx: &mut AppContext) {
271 init_settings(cx);
272
273 workspace::register_project_item::<Editor>(cx);
274 workspace::FollowableViewRegistry::register::<Editor>(cx);
275 workspace::register_serializable_item::<Editor>(cx);
276
277 cx.observe_new_views(
278 |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
279 workspace.register_action(Editor::new_file);
280 workspace.register_action(Editor::new_file_in_direction);
281 },
282 )
283 .detach();
284
285 cx.on_action(move |_: &workspace::NewFile, cx| {
286 let app_state = workspace::AppState::global(cx);
287 if let Some(app_state) = app_state.upgrade() {
288 workspace::open_new(app_state, cx, |workspace, cx| {
289 Editor::new_file(workspace, &Default::default(), cx)
290 })
291 .detach();
292 }
293 });
294 cx.on_action(move |_: &workspace::NewWindow, cx| {
295 let app_state = workspace::AppState::global(cx);
296 if let Some(app_state) = app_state.upgrade() {
297 workspace::open_new(app_state, cx, |workspace, cx| {
298 Editor::new_file(workspace, &Default::default(), cx)
299 })
300 .detach();
301 }
302 });
303}
304
305pub struct SearchWithinRange;
306
307trait InvalidationRegion {
308 fn ranges(&self) -> &[Range<Anchor>];
309}
310
311#[derive(Clone, Debug, PartialEq)]
312pub enum SelectPhase {
313 Begin {
314 position: DisplayPoint,
315 add: bool,
316 click_count: usize,
317 },
318 BeginColumnar {
319 position: DisplayPoint,
320 reset: bool,
321 goal_column: u32,
322 },
323 Extend {
324 position: DisplayPoint,
325 click_count: usize,
326 },
327 Update {
328 position: DisplayPoint,
329 goal_column: u32,
330 scroll_delta: gpui::Point<f32>,
331 },
332 End,
333}
334
335#[derive(Clone, Debug)]
336pub enum SelectMode {
337 Character,
338 Word(Range<Anchor>),
339 Line(Range<Anchor>),
340 All,
341}
342
343#[derive(Copy, Clone, PartialEq, Eq, Debug)]
344pub enum EditorMode {
345 SingleLine { auto_width: bool },
346 AutoHeight { max_lines: usize },
347 Full,
348}
349
350#[derive(Clone, Debug)]
351pub enum SoftWrap {
352 None,
353 PreferLine,
354 EditorWidth,
355 Column(u32),
356}
357
358#[derive(Clone)]
359pub struct EditorStyle {
360 pub background: Hsla,
361 pub local_player: PlayerColor,
362 pub text: TextStyle,
363 pub scrollbar_width: Pixels,
364 pub syntax: Arc<SyntaxTheme>,
365 pub status: StatusColors,
366 pub inlay_hints_style: HighlightStyle,
367 pub suggestions_style: HighlightStyle,
368}
369
370impl Default for EditorStyle {
371 fn default() -> Self {
372 Self {
373 background: Hsla::default(),
374 local_player: PlayerColor::default(),
375 text: TextStyle::default(),
376 scrollbar_width: Pixels::default(),
377 syntax: Default::default(),
378 // HACK: Status colors don't have a real default.
379 // We should look into removing the status colors from the editor
380 // style and retrieve them directly from the theme.
381 status: StatusColors::dark(),
382 inlay_hints_style: HighlightStyle::default(),
383 suggestions_style: HighlightStyle::default(),
384 }
385 }
386}
387
388type CompletionId = usize;
389
390#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
391struct EditorActionId(usize);
392
393impl EditorActionId {
394 pub fn post_inc(&mut self) -> Self {
395 let answer = self.0;
396
397 *self = Self(answer + 1);
398
399 Self(answer)
400 }
401}
402
403// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
404// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
405
406type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
407type GutterHighlight = (fn(&AppContext) -> Hsla, Arc<[Range<Anchor>]>);
408
409struct ScrollbarMarkerState {
410 scrollbar_size: Size<Pixels>,
411 dirty: bool,
412 markers: Arc<[PaintQuad]>,
413 pending_refresh: Option<Task<Result<()>>>,
414}
415
416impl ScrollbarMarkerState {
417 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
418 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
419 }
420}
421
422impl Default for ScrollbarMarkerState {
423 fn default() -> Self {
424 Self {
425 scrollbar_size: Size::default(),
426 dirty: false,
427 markers: Arc::from([]),
428 pending_refresh: None,
429 }
430 }
431}
432
433#[derive(Clone, Debug)]
434struct RunnableTasks {
435 templates: Vec<(TaskSourceKind, TaskTemplate)>,
436 offset: MultiBufferOffset,
437 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
438 column: u32,
439 // Values of all named captures, including those starting with '_'
440 extra_variables: HashMap<String, String>,
441 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
442 context_range: Range<BufferOffset>,
443}
444
445#[derive(Clone)]
446struct ResolvedTasks {
447 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
448 position: Anchor,
449}
450#[derive(Copy, Clone, Debug)]
451struct MultiBufferOffset(usize);
452#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
453struct BufferOffset(usize);
454/// Zed's primary text input `View`, allowing users to edit a [`MultiBuffer`]
455///
456/// See the [module level documentation](self) for more information.
457pub struct Editor {
458 focus_handle: FocusHandle,
459 last_focused_descendant: Option<WeakFocusHandle>,
460 /// The text buffer being edited
461 buffer: Model<MultiBuffer>,
462 /// Map of how text in the buffer should be displayed.
463 /// Handles soft wraps, folds, fake inlay text insertions, etc.
464 pub display_map: Model<DisplayMap>,
465 pub selections: SelectionsCollection,
466 pub scroll_manager: ScrollManager,
467 /// When inline assist editors are linked, they all render cursors because
468 /// typing enters text into each of them, even the ones that aren't focused.
469 pub(crate) show_cursor_when_unfocused: bool,
470 columnar_selection_tail: Option<Anchor>,
471 add_selections_state: Option<AddSelectionsState>,
472 select_next_state: Option<SelectNextState>,
473 select_prev_state: Option<SelectNextState>,
474 selection_history: SelectionHistory,
475 autoclose_regions: Vec<AutocloseRegion>,
476 snippet_stack: InvalidationStack<SnippetState>,
477 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
478 ime_transaction: Option<TransactionId>,
479 active_diagnostics: Option<ActiveDiagnosticGroup>,
480 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
481 project: Option<Model<Project>>,
482 completion_provider: Option<Box<dyn CompletionProvider>>,
483 collaboration_hub: Option<Box<dyn CollaborationHub>>,
484 blink_manager: Model<BlinkManager>,
485 show_cursor_names: bool,
486 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
487 pub show_local_selections: bool,
488 mode: EditorMode,
489 show_breadcrumbs: bool,
490 show_gutter: bool,
491 redact_all: bool,
492 show_line_numbers: Option<bool>,
493 show_git_diff_gutter: Option<bool>,
494 show_code_actions: Option<bool>,
495 show_runnables: Option<bool>,
496 show_wrap_guides: Option<bool>,
497 show_indent_guides: Option<bool>,
498 placeholder_text: Option<Arc<str>>,
499 highlight_order: usize,
500 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
501 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
502 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
503 scrollbar_marker_state: ScrollbarMarkerState,
504 active_indent_guides_state: ActiveIndentGuidesState,
505 nav_history: Option<ItemNavHistory>,
506 context_menu: RwLock<Option<ContextMenu>>,
507 mouse_context_menu: Option<MouseContextMenu>,
508 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
509 signature_help_state: SignatureHelpState,
510 auto_signature_help: Option<bool>,
511 find_all_references_task_sources: Vec<Anchor>,
512 next_completion_id: CompletionId,
513 completion_documentation_pre_resolve_debounce: DebouncedDelay,
514 available_code_actions: Option<(Location, Arc<[CodeAction]>)>,
515 code_actions_task: Option<Task<()>>,
516 document_highlights_task: Option<Task<()>>,
517 linked_editing_range_task: Option<Task<Option<()>>>,
518 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
519 pending_rename: Option<RenameState>,
520 searchable: bool,
521 cursor_shape: CursorShape,
522 current_line_highlight: Option<CurrentLineHighlight>,
523 collapse_matches: bool,
524 autoindent_mode: Option<AutoindentMode>,
525 workspace: Option<(WeakView<Workspace>, Option<WorkspaceId>)>,
526 keymap_context_layers: BTreeMap<TypeId, KeyContext>,
527 input_enabled: bool,
528 use_modal_editing: bool,
529 read_only: bool,
530 leader_peer_id: Option<PeerId>,
531 remote_id: Option<ViewId>,
532 hover_state: HoverState,
533 gutter_hovered: bool,
534 hovered_link_state: Option<HoveredLinkState>,
535 inline_completion_provider: Option<RegisteredInlineCompletionProvider>,
536 active_inline_completion: Option<(Inlay, Option<Range<Anchor>>)>,
537 show_inline_completions: bool,
538 inlay_hint_cache: InlayHintCache,
539 expanded_hunks: ExpandedHunks,
540 next_inlay_id: usize,
541 _subscriptions: Vec<Subscription>,
542 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
543 gutter_dimensions: GutterDimensions,
544 pub vim_replace_map: HashMap<Range<usize>, String>,
545 style: Option<EditorStyle>,
546 next_editor_action_id: EditorActionId,
547 editor_actions: Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut ViewContext<Self>)>>>>,
548 use_autoclose: bool,
549 use_auto_surround: bool,
550 auto_replace_emoji_shortcode: bool,
551 show_git_blame_gutter: bool,
552 show_git_blame_inline: bool,
553 show_git_blame_inline_delay_task: Option<Task<()>>,
554 git_blame_inline_enabled: bool,
555 serialize_dirty_buffers: bool,
556 show_selection_menu: Option<bool>,
557 blame: Option<Model<GitBlame>>,
558 blame_subscription: Option<Subscription>,
559 custom_context_menu: Option<
560 Box<
561 dyn 'static
562 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
563 >,
564 >,
565 last_bounds: Option<Bounds<Pixels>>,
566 expect_bounds_change: Option<Bounds<Pixels>>,
567 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
568 tasks_update_task: Option<Task<()>>,
569 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
570 file_header_size: u8,
571 breadcrumb_header: Option<String>,
572 focused_block: Option<FocusedBlock>,
573}
574
575#[derive(Clone)]
576pub struct EditorSnapshot {
577 pub mode: EditorMode,
578 show_gutter: bool,
579 show_line_numbers: Option<bool>,
580 show_git_diff_gutter: Option<bool>,
581 show_code_actions: Option<bool>,
582 show_runnables: Option<bool>,
583 render_git_blame_gutter: bool,
584 pub display_snapshot: DisplaySnapshot,
585 pub placeholder_text: Option<Arc<str>>,
586 is_focused: bool,
587 scroll_anchor: ScrollAnchor,
588 ongoing_scroll: OngoingScroll,
589 current_line_highlight: CurrentLineHighlight,
590 gutter_hovered: bool,
591}
592
593const GIT_BLAME_GUTTER_WIDTH_CHARS: f32 = 53.;
594
595#[derive(Debug, Clone, Copy)]
596pub struct GutterDimensions {
597 pub left_padding: Pixels,
598 pub right_padding: Pixels,
599 pub width: Pixels,
600 pub margin: Pixels,
601 pub git_blame_entries_width: Option<Pixels>,
602}
603
604impl GutterDimensions {
605 /// The full width of the space taken up by the gutter.
606 pub fn full_width(&self) -> Pixels {
607 self.margin + self.width
608 }
609
610 /// The width of the space reserved for the fold indicators,
611 /// use alongside 'justify_end' and `gutter_width` to
612 /// right align content with the line numbers
613 pub fn fold_area_width(&self) -> Pixels {
614 self.margin + self.right_padding
615 }
616}
617
618impl Default for GutterDimensions {
619 fn default() -> Self {
620 Self {
621 left_padding: Pixels::ZERO,
622 right_padding: Pixels::ZERO,
623 width: Pixels::ZERO,
624 margin: Pixels::ZERO,
625 git_blame_entries_width: None,
626 }
627 }
628}
629
630#[derive(Debug)]
631pub struct RemoteSelection {
632 pub replica_id: ReplicaId,
633 pub selection: Selection<Anchor>,
634 pub cursor_shape: CursorShape,
635 pub peer_id: PeerId,
636 pub line_mode: bool,
637 pub participant_index: Option<ParticipantIndex>,
638 pub user_name: Option<SharedString>,
639}
640
641#[derive(Clone, Debug)]
642struct SelectionHistoryEntry {
643 selections: Arc<[Selection<Anchor>]>,
644 select_next_state: Option<SelectNextState>,
645 select_prev_state: Option<SelectNextState>,
646 add_selections_state: Option<AddSelectionsState>,
647}
648
649enum SelectionHistoryMode {
650 Normal,
651 Undoing,
652 Redoing,
653}
654
655#[derive(Clone, PartialEq, Eq, Hash)]
656struct HoveredCursor {
657 replica_id: u16,
658 selection_id: usize,
659}
660
661impl Default for SelectionHistoryMode {
662 fn default() -> Self {
663 Self::Normal
664 }
665}
666
667#[derive(Default)]
668struct SelectionHistory {
669 #[allow(clippy::type_complexity)]
670 selections_by_transaction:
671 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
672 mode: SelectionHistoryMode,
673 undo_stack: VecDeque<SelectionHistoryEntry>,
674 redo_stack: VecDeque<SelectionHistoryEntry>,
675}
676
677impl SelectionHistory {
678 fn insert_transaction(
679 &mut self,
680 transaction_id: TransactionId,
681 selections: Arc<[Selection<Anchor>]>,
682 ) {
683 self.selections_by_transaction
684 .insert(transaction_id, (selections, None));
685 }
686
687 #[allow(clippy::type_complexity)]
688 fn transaction(
689 &self,
690 transaction_id: TransactionId,
691 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
692 self.selections_by_transaction.get(&transaction_id)
693 }
694
695 #[allow(clippy::type_complexity)]
696 fn transaction_mut(
697 &mut self,
698 transaction_id: TransactionId,
699 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
700 self.selections_by_transaction.get_mut(&transaction_id)
701 }
702
703 fn push(&mut self, entry: SelectionHistoryEntry) {
704 if !entry.selections.is_empty() {
705 match self.mode {
706 SelectionHistoryMode::Normal => {
707 self.push_undo(entry);
708 self.redo_stack.clear();
709 }
710 SelectionHistoryMode::Undoing => self.push_redo(entry),
711 SelectionHistoryMode::Redoing => self.push_undo(entry),
712 }
713 }
714 }
715
716 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
717 if self
718 .undo_stack
719 .back()
720 .map_or(true, |e| e.selections != entry.selections)
721 {
722 self.undo_stack.push_back(entry);
723 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
724 self.undo_stack.pop_front();
725 }
726 }
727 }
728
729 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
730 if self
731 .redo_stack
732 .back()
733 .map_or(true, |e| e.selections != entry.selections)
734 {
735 self.redo_stack.push_back(entry);
736 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
737 self.redo_stack.pop_front();
738 }
739 }
740 }
741}
742
743struct RowHighlight {
744 index: usize,
745 range: RangeInclusive<Anchor>,
746 color: Option<Hsla>,
747 should_autoscroll: bool,
748}
749
750#[derive(Clone, Debug)]
751struct AddSelectionsState {
752 above: bool,
753 stack: Vec<usize>,
754}
755
756#[derive(Clone)]
757struct SelectNextState {
758 query: AhoCorasick,
759 wordwise: bool,
760 done: bool,
761}
762
763impl std::fmt::Debug for SelectNextState {
764 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
765 f.debug_struct(std::any::type_name::<Self>())
766 .field("wordwise", &self.wordwise)
767 .field("done", &self.done)
768 .finish()
769 }
770}
771
772#[derive(Debug)]
773struct AutocloseRegion {
774 selection_id: usize,
775 range: Range<Anchor>,
776 pair: BracketPair,
777}
778
779#[derive(Debug)]
780struct SnippetState {
781 ranges: Vec<Vec<Range<Anchor>>>,
782 active_index: usize,
783}
784
785#[doc(hidden)]
786pub struct RenameState {
787 pub range: Range<Anchor>,
788 pub old_name: Arc<str>,
789 pub editor: View<Editor>,
790 block_id: CustomBlockId,
791}
792
793struct InvalidationStack<T>(Vec<T>);
794
795struct RegisteredInlineCompletionProvider {
796 provider: Arc<dyn InlineCompletionProviderHandle>,
797 _subscription: Subscription,
798}
799
800enum ContextMenu {
801 Completions(CompletionsMenu),
802 CodeActions(CodeActionsMenu),
803}
804
805impl ContextMenu {
806 fn select_first(
807 &mut self,
808 project: Option<&Model<Project>>,
809 cx: &mut ViewContext<Editor>,
810 ) -> bool {
811 if self.visible() {
812 match self {
813 ContextMenu::Completions(menu) => menu.select_first(project, cx),
814 ContextMenu::CodeActions(menu) => menu.select_first(cx),
815 }
816 true
817 } else {
818 false
819 }
820 }
821
822 fn select_prev(
823 &mut self,
824 project: Option<&Model<Project>>,
825 cx: &mut ViewContext<Editor>,
826 ) -> bool {
827 if self.visible() {
828 match self {
829 ContextMenu::Completions(menu) => menu.select_prev(project, cx),
830 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
831 }
832 true
833 } else {
834 false
835 }
836 }
837
838 fn select_next(
839 &mut self,
840 project: Option<&Model<Project>>,
841 cx: &mut ViewContext<Editor>,
842 ) -> bool {
843 if self.visible() {
844 match self {
845 ContextMenu::Completions(menu) => menu.select_next(project, cx),
846 ContextMenu::CodeActions(menu) => menu.select_next(cx),
847 }
848 true
849 } else {
850 false
851 }
852 }
853
854 fn select_last(
855 &mut self,
856 project: Option<&Model<Project>>,
857 cx: &mut ViewContext<Editor>,
858 ) -> bool {
859 if self.visible() {
860 match self {
861 ContextMenu::Completions(menu) => menu.select_last(project, cx),
862 ContextMenu::CodeActions(menu) => menu.select_last(cx),
863 }
864 true
865 } else {
866 false
867 }
868 }
869
870 fn visible(&self) -> bool {
871 match self {
872 ContextMenu::Completions(menu) => menu.visible(),
873 ContextMenu::CodeActions(menu) => menu.visible(),
874 }
875 }
876
877 fn render(
878 &self,
879 cursor_position: DisplayPoint,
880 style: &EditorStyle,
881 max_height: Pixels,
882 workspace: Option<WeakView<Workspace>>,
883 cx: &mut ViewContext<Editor>,
884 ) -> (ContextMenuOrigin, AnyElement) {
885 match self {
886 ContextMenu::Completions(menu) => (
887 ContextMenuOrigin::EditorPoint(cursor_position),
888 menu.render(style, max_height, workspace, cx),
889 ),
890 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, max_height, cx),
891 }
892 }
893}
894
895enum ContextMenuOrigin {
896 EditorPoint(DisplayPoint),
897 GutterIndicator(DisplayRow),
898}
899
900#[derive(Clone)]
901struct CompletionsMenu {
902 id: CompletionId,
903 initial_position: Anchor,
904 buffer: Model<Buffer>,
905 completions: Arc<RwLock<Box<[Completion]>>>,
906 match_candidates: Arc<[StringMatchCandidate]>,
907 matches: Arc<[StringMatch]>,
908 selected_item: usize,
909 scroll_handle: UniformListScrollHandle,
910 selected_completion_documentation_resolve_debounce: Arc<Mutex<DebouncedDelay>>,
911}
912
913impl CompletionsMenu {
914 fn select_first(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
915 self.selected_item = 0;
916 self.scroll_handle.scroll_to_item(self.selected_item);
917 self.attempt_resolve_selected_completion_documentation(project, cx);
918 cx.notify();
919 }
920
921 fn select_prev(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
922 if self.selected_item > 0 {
923 self.selected_item -= 1;
924 } else {
925 self.selected_item = self.matches.len() - 1;
926 }
927 self.scroll_handle.scroll_to_item(self.selected_item);
928 self.attempt_resolve_selected_completion_documentation(project, cx);
929 cx.notify();
930 }
931
932 fn select_next(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
933 if self.selected_item + 1 < self.matches.len() {
934 self.selected_item += 1;
935 } else {
936 self.selected_item = 0;
937 }
938 self.scroll_handle.scroll_to_item(self.selected_item);
939 self.attempt_resolve_selected_completion_documentation(project, cx);
940 cx.notify();
941 }
942
943 fn select_last(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
944 self.selected_item = self.matches.len() - 1;
945 self.scroll_handle.scroll_to_item(self.selected_item);
946 self.attempt_resolve_selected_completion_documentation(project, cx);
947 cx.notify();
948 }
949
950 fn pre_resolve_completion_documentation(
951 buffer: Model<Buffer>,
952 completions: Arc<RwLock<Box<[Completion]>>>,
953 matches: Arc<[StringMatch]>,
954 editor: &Editor,
955 cx: &mut ViewContext<Editor>,
956 ) -> Task<()> {
957 let settings = EditorSettings::get_global(cx);
958 if !settings.show_completion_documentation {
959 return Task::ready(());
960 }
961
962 let Some(provider) = editor.completion_provider.as_ref() else {
963 return Task::ready(());
964 };
965
966 let resolve_task = provider.resolve_completions(
967 buffer,
968 matches.iter().map(|m| m.candidate_id).collect(),
969 completions.clone(),
970 cx,
971 );
972
973 return cx.spawn(move |this, mut cx| async move {
974 if let Some(true) = resolve_task.await.log_err() {
975 this.update(&mut cx, |_, cx| cx.notify()).ok();
976 }
977 });
978 }
979
980 fn attempt_resolve_selected_completion_documentation(
981 &mut self,
982 project: Option<&Model<Project>>,
983 cx: &mut ViewContext<Editor>,
984 ) {
985 let settings = EditorSettings::get_global(cx);
986 if !settings.show_completion_documentation {
987 return;
988 }
989
990 let completion_index = self.matches[self.selected_item].candidate_id;
991 let Some(project) = project else {
992 return;
993 };
994
995 let resolve_task = project.update(cx, |project, cx| {
996 project.resolve_completions(
997 self.buffer.clone(),
998 vec![completion_index],
999 self.completions.clone(),
1000 cx,
1001 )
1002 });
1003
1004 let delay_ms =
1005 EditorSettings::get_global(cx).completion_documentation_secondary_query_debounce;
1006 let delay = Duration::from_millis(delay_ms);
1007
1008 self.selected_completion_documentation_resolve_debounce
1009 .lock()
1010 .fire_new(delay, cx, |_, cx| {
1011 cx.spawn(move |this, mut cx| async move {
1012 if let Some(true) = resolve_task.await.log_err() {
1013 this.update(&mut cx, |_, cx| cx.notify()).ok();
1014 }
1015 })
1016 });
1017 }
1018
1019 fn visible(&self) -> bool {
1020 !self.matches.is_empty()
1021 }
1022
1023 fn render(
1024 &self,
1025 style: &EditorStyle,
1026 max_height: Pixels,
1027 workspace: Option<WeakView<Workspace>>,
1028 cx: &mut ViewContext<Editor>,
1029 ) -> AnyElement {
1030 let settings = EditorSettings::get_global(cx);
1031 let show_completion_documentation = settings.show_completion_documentation;
1032
1033 let widest_completion_ix = self
1034 .matches
1035 .iter()
1036 .enumerate()
1037 .max_by_key(|(_, mat)| {
1038 let completions = self.completions.read();
1039 let completion = &completions[mat.candidate_id];
1040 let documentation = &completion.documentation;
1041
1042 let mut len = completion.label.text.chars().count();
1043 if let Some(Documentation::SingleLine(text)) = documentation {
1044 if show_completion_documentation {
1045 len += text.chars().count();
1046 }
1047 }
1048
1049 len
1050 })
1051 .map(|(ix, _)| ix);
1052
1053 let completions = self.completions.clone();
1054 let matches = self.matches.clone();
1055 let selected_item = self.selected_item;
1056 let style = style.clone();
1057
1058 let multiline_docs = if show_completion_documentation {
1059 let mat = &self.matches[selected_item];
1060 let multiline_docs = match &self.completions.read()[mat.candidate_id].documentation {
1061 Some(Documentation::MultiLinePlainText(text)) => {
1062 Some(div().child(SharedString::from(text.clone())))
1063 }
1064 Some(Documentation::MultiLineMarkdown(parsed)) if !parsed.text.is_empty() => {
1065 Some(div().child(render_parsed_markdown(
1066 "completions_markdown",
1067 parsed,
1068 &style,
1069 workspace,
1070 cx,
1071 )))
1072 }
1073 _ => None,
1074 };
1075 multiline_docs.map(|div| {
1076 div.id("multiline_docs")
1077 .max_h(max_height)
1078 .flex_1()
1079 .px_1p5()
1080 .py_1()
1081 .min_w(px(260.))
1082 .max_w(px(640.))
1083 .w(px(500.))
1084 .overflow_y_scroll()
1085 .occlude()
1086 })
1087 } else {
1088 None
1089 };
1090
1091 let list = uniform_list(
1092 cx.view().clone(),
1093 "completions",
1094 matches.len(),
1095 move |_editor, range, cx| {
1096 let start_ix = range.start;
1097 let completions_guard = completions.read();
1098
1099 matches[range]
1100 .iter()
1101 .enumerate()
1102 .map(|(ix, mat)| {
1103 let item_ix = start_ix + ix;
1104 let candidate_id = mat.candidate_id;
1105 let completion = &completions_guard[candidate_id];
1106
1107 let documentation = if show_completion_documentation {
1108 &completion.documentation
1109 } else {
1110 &None
1111 };
1112
1113 let highlights = gpui::combine_highlights(
1114 mat.ranges().map(|range| (range, FontWeight::BOLD.into())),
1115 styled_runs_for_code_label(&completion.label, &style.syntax).map(
1116 |(range, mut highlight)| {
1117 // Ignore font weight for syntax highlighting, as we'll use it
1118 // for fuzzy matches.
1119 highlight.font_weight = None;
1120
1121 if completion.lsp_completion.deprecated.unwrap_or(false) {
1122 highlight.strikethrough = Some(StrikethroughStyle {
1123 thickness: 1.0.into(),
1124 ..Default::default()
1125 });
1126 highlight.color = Some(cx.theme().colors().text_muted);
1127 }
1128
1129 (range, highlight)
1130 },
1131 ),
1132 );
1133 let completion_label = StyledText::new(completion.label.text.clone())
1134 .with_highlights(&style.text, highlights);
1135 let documentation_label =
1136 if let Some(Documentation::SingleLine(text)) = documentation {
1137 if text.trim().is_empty() {
1138 None
1139 } else {
1140 Some(
1141 Label::new(text.clone())
1142 .ml_4()
1143 .size(LabelSize::Small)
1144 .color(Color::Muted),
1145 )
1146 }
1147 } else {
1148 None
1149 };
1150
1151 div().min_w(px(220.)).max_w(px(540.)).child(
1152 ListItem::new(mat.candidate_id)
1153 .inset(true)
1154 .selected(item_ix == selected_item)
1155 .on_click(cx.listener(move |editor, _event, cx| {
1156 cx.stop_propagation();
1157 if let Some(task) = editor.confirm_completion(
1158 &ConfirmCompletion {
1159 item_ix: Some(item_ix),
1160 },
1161 cx,
1162 ) {
1163 task.detach_and_log_err(cx)
1164 }
1165 }))
1166 .child(h_flex().overflow_hidden().child(completion_label))
1167 .end_slot::<Label>(documentation_label),
1168 )
1169 })
1170 .collect()
1171 },
1172 )
1173 .occlude()
1174 .max_h(max_height)
1175 .track_scroll(self.scroll_handle.clone())
1176 .with_width_from_item(widest_completion_ix)
1177 .with_sizing_behavior(ListSizingBehavior::Infer);
1178
1179 Popover::new()
1180 .child(list)
1181 .when_some(multiline_docs, |popover, multiline_docs| {
1182 popover.aside(multiline_docs)
1183 })
1184 .into_any_element()
1185 }
1186
1187 pub async fn filter(&mut self, query: Option<&str>, executor: BackgroundExecutor) {
1188 let mut matches = if let Some(query) = query {
1189 fuzzy::match_strings(
1190 &self.match_candidates,
1191 query,
1192 query.chars().any(|c| c.is_uppercase()),
1193 100,
1194 &Default::default(),
1195 executor,
1196 )
1197 .await
1198 } else {
1199 self.match_candidates
1200 .iter()
1201 .enumerate()
1202 .map(|(candidate_id, candidate)| StringMatch {
1203 candidate_id,
1204 score: Default::default(),
1205 positions: Default::default(),
1206 string: candidate.string.clone(),
1207 })
1208 .collect()
1209 };
1210
1211 // Remove all candidates where the query's start does not match the start of any word in the candidate
1212 if let Some(query) = query {
1213 if let Some(query_start) = query.chars().next() {
1214 matches.retain(|string_match| {
1215 split_words(&string_match.string).any(|word| {
1216 // Check that the first codepoint of the word as lowercase matches the first
1217 // codepoint of the query as lowercase
1218 word.chars()
1219 .flat_map(|codepoint| codepoint.to_lowercase())
1220 .zip(query_start.to_lowercase())
1221 .all(|(word_cp, query_cp)| word_cp == query_cp)
1222 })
1223 });
1224 }
1225 }
1226
1227 let completions = self.completions.read();
1228 matches.sort_unstable_by_key(|mat| {
1229 // We do want to strike a balance here between what the language server tells us
1230 // to sort by (the sort_text) and what are "obvious" good matches (i.e. when you type
1231 // `Creat` and there is a local variable called `CreateComponent`).
1232 // So what we do is: we bucket all matches into two buckets
1233 // - Strong matches
1234 // - Weak matches
1235 // Strong matches are the ones with a high fuzzy-matcher score (the "obvious" matches)
1236 // and the Weak matches are the rest.
1237 //
1238 // For the strong matches, we sort by the language-servers score first and for the weak
1239 // matches, we prefer our fuzzy finder first.
1240 //
1241 // The thinking behind that: it's useless to take the sort_text the language-server gives
1242 // us into account when it's obviously a bad match.
1243
1244 #[derive(PartialEq, Eq, PartialOrd, Ord)]
1245 enum MatchScore<'a> {
1246 Strong {
1247 sort_text: Option<&'a str>,
1248 score: Reverse<OrderedFloat<f64>>,
1249 sort_key: (usize, &'a str),
1250 },
1251 Weak {
1252 score: Reverse<OrderedFloat<f64>>,
1253 sort_text: Option<&'a str>,
1254 sort_key: (usize, &'a str),
1255 },
1256 }
1257
1258 let completion = &completions[mat.candidate_id];
1259 let sort_key = completion.sort_key();
1260 let sort_text = completion.lsp_completion.sort_text.as_deref();
1261 let score = Reverse(OrderedFloat(mat.score));
1262
1263 if mat.score >= 0.2 {
1264 MatchScore::Strong {
1265 sort_text,
1266 score,
1267 sort_key,
1268 }
1269 } else {
1270 MatchScore::Weak {
1271 score,
1272 sort_text,
1273 sort_key,
1274 }
1275 }
1276 });
1277
1278 for mat in &mut matches {
1279 let completion = &completions[mat.candidate_id];
1280 mat.string.clone_from(&completion.label.text);
1281 for position in &mut mat.positions {
1282 *position += completion.label.filter_range.start;
1283 }
1284 }
1285 drop(completions);
1286
1287 self.matches = matches.into();
1288 self.selected_item = 0;
1289 }
1290}
1291
1292#[derive(Clone)]
1293struct CodeActionContents {
1294 tasks: Option<Arc<ResolvedTasks>>,
1295 actions: Option<Arc<[CodeAction]>>,
1296}
1297
1298impl CodeActionContents {
1299 fn len(&self) -> usize {
1300 match (&self.tasks, &self.actions) {
1301 (Some(tasks), Some(actions)) => actions.len() + tasks.templates.len(),
1302 (Some(tasks), None) => tasks.templates.len(),
1303 (None, Some(actions)) => actions.len(),
1304 (None, None) => 0,
1305 }
1306 }
1307
1308 fn is_empty(&self) -> bool {
1309 match (&self.tasks, &self.actions) {
1310 (Some(tasks), Some(actions)) => actions.is_empty() && tasks.templates.is_empty(),
1311 (Some(tasks), None) => tasks.templates.is_empty(),
1312 (None, Some(actions)) => actions.is_empty(),
1313 (None, None) => true,
1314 }
1315 }
1316
1317 fn iter(&self) -> impl Iterator<Item = CodeActionsItem> + '_ {
1318 self.tasks
1319 .iter()
1320 .flat_map(|tasks| {
1321 tasks
1322 .templates
1323 .iter()
1324 .map(|(kind, task)| CodeActionsItem::Task(kind.clone(), task.clone()))
1325 })
1326 .chain(self.actions.iter().flat_map(|actions| {
1327 actions
1328 .iter()
1329 .map(|action| CodeActionsItem::CodeAction(action.clone()))
1330 }))
1331 }
1332 fn get(&self, index: usize) -> Option<CodeActionsItem> {
1333 match (&self.tasks, &self.actions) {
1334 (Some(tasks), Some(actions)) => {
1335 if index < tasks.templates.len() {
1336 tasks
1337 .templates
1338 .get(index)
1339 .cloned()
1340 .map(|(kind, task)| CodeActionsItem::Task(kind, task))
1341 } else {
1342 actions
1343 .get(index - tasks.templates.len())
1344 .cloned()
1345 .map(CodeActionsItem::CodeAction)
1346 }
1347 }
1348 (Some(tasks), None) => tasks
1349 .templates
1350 .get(index)
1351 .cloned()
1352 .map(|(kind, task)| CodeActionsItem::Task(kind, task)),
1353 (None, Some(actions)) => actions.get(index).cloned().map(CodeActionsItem::CodeAction),
1354 (None, None) => None,
1355 }
1356 }
1357}
1358
1359#[allow(clippy::large_enum_variant)]
1360#[derive(Clone)]
1361enum CodeActionsItem {
1362 Task(TaskSourceKind, ResolvedTask),
1363 CodeAction(CodeAction),
1364}
1365
1366impl CodeActionsItem {
1367 fn as_task(&self) -> Option<&ResolvedTask> {
1368 let Self::Task(_, task) = self else {
1369 return None;
1370 };
1371 Some(task)
1372 }
1373 fn as_code_action(&self) -> Option<&CodeAction> {
1374 let Self::CodeAction(action) = self else {
1375 return None;
1376 };
1377 Some(action)
1378 }
1379 fn label(&self) -> String {
1380 match self {
1381 Self::CodeAction(action) => action.lsp_action.title.clone(),
1382 Self::Task(_, task) => task.resolved_label.clone(),
1383 }
1384 }
1385}
1386
1387struct CodeActionsMenu {
1388 actions: CodeActionContents,
1389 buffer: Model<Buffer>,
1390 selected_item: usize,
1391 scroll_handle: UniformListScrollHandle,
1392 deployed_from_indicator: Option<DisplayRow>,
1393}
1394
1395impl CodeActionsMenu {
1396 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
1397 self.selected_item = 0;
1398 self.scroll_handle.scroll_to_item(self.selected_item);
1399 cx.notify()
1400 }
1401
1402 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
1403 if self.selected_item > 0 {
1404 self.selected_item -= 1;
1405 } else {
1406 self.selected_item = self.actions.len() - 1;
1407 }
1408 self.scroll_handle.scroll_to_item(self.selected_item);
1409 cx.notify();
1410 }
1411
1412 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
1413 if self.selected_item + 1 < self.actions.len() {
1414 self.selected_item += 1;
1415 } else {
1416 self.selected_item = 0;
1417 }
1418 self.scroll_handle.scroll_to_item(self.selected_item);
1419 cx.notify();
1420 }
1421
1422 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
1423 self.selected_item = self.actions.len() - 1;
1424 self.scroll_handle.scroll_to_item(self.selected_item);
1425 cx.notify()
1426 }
1427
1428 fn visible(&self) -> bool {
1429 !self.actions.is_empty()
1430 }
1431
1432 fn render(
1433 &self,
1434 cursor_position: DisplayPoint,
1435 _style: &EditorStyle,
1436 max_height: Pixels,
1437 cx: &mut ViewContext<Editor>,
1438 ) -> (ContextMenuOrigin, AnyElement) {
1439 let actions = self.actions.clone();
1440 let selected_item = self.selected_item;
1441 let element = uniform_list(
1442 cx.view().clone(),
1443 "code_actions_menu",
1444 self.actions.len(),
1445 move |_this, range, cx| {
1446 actions
1447 .iter()
1448 .skip(range.start)
1449 .take(range.end - range.start)
1450 .enumerate()
1451 .map(|(ix, action)| {
1452 let item_ix = range.start + ix;
1453 let selected = selected_item == item_ix;
1454 let colors = cx.theme().colors();
1455 div()
1456 .px_2()
1457 .text_color(colors.text)
1458 .when(selected, |style| {
1459 style
1460 .bg(colors.element_active)
1461 .text_color(colors.text_accent)
1462 })
1463 .hover(|style| {
1464 style
1465 .bg(colors.element_hover)
1466 .text_color(colors.text_accent)
1467 })
1468 .whitespace_nowrap()
1469 .when_some(action.as_code_action(), |this, action| {
1470 this.on_mouse_down(
1471 MouseButton::Left,
1472 cx.listener(move |editor, _, cx| {
1473 cx.stop_propagation();
1474 if let Some(task) = editor.confirm_code_action(
1475 &ConfirmCodeAction {
1476 item_ix: Some(item_ix),
1477 },
1478 cx,
1479 ) {
1480 task.detach_and_log_err(cx)
1481 }
1482 }),
1483 )
1484 // TASK: It would be good to make lsp_action.title a SharedString to avoid allocating here.
1485 .child(SharedString::from(action.lsp_action.title.clone()))
1486 })
1487 .when_some(action.as_task(), |this, task| {
1488 this.on_mouse_down(
1489 MouseButton::Left,
1490 cx.listener(move |editor, _, cx| {
1491 cx.stop_propagation();
1492 if let Some(task) = editor.confirm_code_action(
1493 &ConfirmCodeAction {
1494 item_ix: Some(item_ix),
1495 },
1496 cx,
1497 ) {
1498 task.detach_and_log_err(cx)
1499 }
1500 }),
1501 )
1502 .child(SharedString::from(task.resolved_label.clone()))
1503 })
1504 })
1505 .collect()
1506 },
1507 )
1508 .elevation_1(cx)
1509 .px_2()
1510 .py_1()
1511 .max_h(max_height)
1512 .occlude()
1513 .track_scroll(self.scroll_handle.clone())
1514 .with_width_from_item(
1515 self.actions
1516 .iter()
1517 .enumerate()
1518 .max_by_key(|(_, action)| match action {
1519 CodeActionsItem::Task(_, task) => task.resolved_label.chars().count(),
1520 CodeActionsItem::CodeAction(action) => action.lsp_action.title.chars().count(),
1521 })
1522 .map(|(ix, _)| ix),
1523 )
1524 .with_sizing_behavior(ListSizingBehavior::Infer)
1525 .into_any_element();
1526
1527 let cursor_position = if let Some(row) = self.deployed_from_indicator {
1528 ContextMenuOrigin::GutterIndicator(row)
1529 } else {
1530 ContextMenuOrigin::EditorPoint(cursor_position)
1531 };
1532
1533 (cursor_position, element)
1534 }
1535}
1536
1537#[derive(Debug)]
1538struct ActiveDiagnosticGroup {
1539 primary_range: Range<Anchor>,
1540 primary_message: String,
1541 group_id: usize,
1542 blocks: HashMap<CustomBlockId, Diagnostic>,
1543 is_valid: bool,
1544}
1545
1546#[derive(Serialize, Deserialize, Clone, Debug)]
1547pub struct ClipboardSelection {
1548 pub len: usize,
1549 pub is_entire_line: bool,
1550 pub first_line_indent: u32,
1551}
1552
1553#[derive(Debug)]
1554pub(crate) struct NavigationData {
1555 cursor_anchor: Anchor,
1556 cursor_position: Point,
1557 scroll_anchor: ScrollAnchor,
1558 scroll_top_row: u32,
1559}
1560
1561enum GotoDefinitionKind {
1562 Symbol,
1563 Type,
1564 Implementation,
1565}
1566
1567#[derive(Debug, Clone)]
1568enum InlayHintRefreshReason {
1569 Toggle(bool),
1570 SettingsChange(InlayHintSettings),
1571 NewLinesShown,
1572 BufferEdited(HashSet<Arc<Language>>),
1573 RefreshRequested,
1574 ExcerptsRemoved(Vec<ExcerptId>),
1575}
1576
1577impl InlayHintRefreshReason {
1578 fn description(&self) -> &'static str {
1579 match self {
1580 Self::Toggle(_) => "toggle",
1581 Self::SettingsChange(_) => "settings change",
1582 Self::NewLinesShown => "new lines shown",
1583 Self::BufferEdited(_) => "buffer edited",
1584 Self::RefreshRequested => "refresh requested",
1585 Self::ExcerptsRemoved(_) => "excerpts removed",
1586 }
1587 }
1588}
1589
1590pub(crate) struct FocusedBlock {
1591 id: BlockId,
1592 focus_handle: WeakFocusHandle,
1593}
1594
1595impl Editor {
1596 pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
1597 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1598 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1599 Self::new(
1600 EditorMode::SingleLine { auto_width: false },
1601 buffer,
1602 None,
1603 false,
1604 cx,
1605 )
1606 }
1607
1608 pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
1609 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1610 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1611 Self::new(EditorMode::Full, buffer, None, false, cx)
1612 }
1613
1614 pub fn auto_width(cx: &mut ViewContext<Self>) -> Self {
1615 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1616 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1617 Self::new(
1618 EditorMode::SingleLine { auto_width: true },
1619 buffer,
1620 None,
1621 false,
1622 cx,
1623 )
1624 }
1625
1626 pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
1627 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1628 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1629 Self::new(
1630 EditorMode::AutoHeight { max_lines },
1631 buffer,
1632 None,
1633 false,
1634 cx,
1635 )
1636 }
1637
1638 pub fn for_buffer(
1639 buffer: Model<Buffer>,
1640 project: Option<Model<Project>>,
1641 cx: &mut ViewContext<Self>,
1642 ) -> Self {
1643 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1644 Self::new(EditorMode::Full, buffer, project, false, cx)
1645 }
1646
1647 pub fn for_multibuffer(
1648 buffer: Model<MultiBuffer>,
1649 project: Option<Model<Project>>,
1650 show_excerpt_controls: bool,
1651 cx: &mut ViewContext<Self>,
1652 ) -> Self {
1653 Self::new(EditorMode::Full, buffer, project, show_excerpt_controls, cx)
1654 }
1655
1656 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1657 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1658 let mut clone = Self::new(
1659 self.mode,
1660 self.buffer.clone(),
1661 self.project.clone(),
1662 show_excerpt_controls,
1663 cx,
1664 );
1665 self.display_map.update(cx, |display_map, cx| {
1666 let snapshot = display_map.snapshot(cx);
1667 clone.display_map.update(cx, |display_map, cx| {
1668 display_map.set_state(&snapshot, cx);
1669 });
1670 });
1671 clone.selections.clone_state(&self.selections);
1672 clone.scroll_manager.clone_state(&self.scroll_manager);
1673 clone.searchable = self.searchable;
1674 clone
1675 }
1676
1677 pub fn new(
1678 mode: EditorMode,
1679 buffer: Model<MultiBuffer>,
1680 project: Option<Model<Project>>,
1681 show_excerpt_controls: bool,
1682 cx: &mut ViewContext<Self>,
1683 ) -> Self {
1684 let style = cx.text_style();
1685 let font_size = style.font_size.to_pixels(cx.rem_size());
1686 let editor = cx.view().downgrade();
1687 let fold_placeholder = FoldPlaceholder {
1688 constrain_width: true,
1689 render: Arc::new(move |fold_id, fold_range, cx| {
1690 let editor = editor.clone();
1691 div()
1692 .id(fold_id)
1693 .bg(cx.theme().colors().ghost_element_background)
1694 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1695 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1696 .rounded_sm()
1697 .size_full()
1698 .cursor_pointer()
1699 .child("⋯")
1700 .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
1701 .on_click(move |_, cx| {
1702 editor
1703 .update(cx, |editor, cx| {
1704 editor.unfold_ranges(
1705 [fold_range.start..fold_range.end],
1706 true,
1707 false,
1708 cx,
1709 );
1710 cx.stop_propagation();
1711 })
1712 .ok();
1713 })
1714 .into_any()
1715 }),
1716 merge_adjacent: true,
1717 };
1718 let file_header_size = if show_excerpt_controls { 3 } else { 2 };
1719 let display_map = cx.new_model(|cx| {
1720 DisplayMap::new(
1721 buffer.clone(),
1722 style.font(),
1723 font_size,
1724 None,
1725 show_excerpt_controls,
1726 file_header_size,
1727 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1728 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1729 fold_placeholder,
1730 cx,
1731 )
1732 });
1733
1734 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1735
1736 let blink_manager = cx.new_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1737
1738 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1739 .then(|| language_settings::SoftWrap::PreferLine);
1740
1741 let mut project_subscriptions = Vec::new();
1742 if mode == EditorMode::Full {
1743 if let Some(project) = project.as_ref() {
1744 if buffer.read(cx).is_singleton() {
1745 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1746 cx.emit(EditorEvent::TitleChanged);
1747 }));
1748 }
1749 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1750 if let project::Event::RefreshInlayHints = event {
1751 editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1752 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1753 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1754 let focus_handle = editor.focus_handle(cx);
1755 if focus_handle.is_focused(cx) {
1756 let snapshot = buffer.read(cx).snapshot();
1757 for (range, snippet) in snippet_edits {
1758 let editor_range =
1759 language::range_from_lsp(*range).to_offset(&snapshot);
1760 editor
1761 .insert_snippet(&[editor_range], snippet.clone(), cx)
1762 .ok();
1763 }
1764 }
1765 }
1766 }
1767 }));
1768 let task_inventory = project.read(cx).task_inventory().clone();
1769 project_subscriptions.push(cx.observe(&task_inventory, |editor, _, cx| {
1770 editor.tasks_update_task = Some(editor.refresh_runnables(cx));
1771 }));
1772 }
1773 }
1774
1775 let inlay_hint_settings = inlay_hint_settings(
1776 selections.newest_anchor().head(),
1777 &buffer.read(cx).snapshot(cx),
1778 cx,
1779 );
1780 let focus_handle = cx.focus_handle();
1781 cx.on_focus(&focus_handle, Self::handle_focus).detach();
1782 cx.on_focus_in(&focus_handle, Self::handle_focus_in)
1783 .detach();
1784 cx.on_focus_out(&focus_handle, Self::handle_focus_out)
1785 .detach();
1786 cx.on_blur(&focus_handle, Self::handle_blur).detach();
1787
1788 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1789 Some(false)
1790 } else {
1791 None
1792 };
1793
1794 let mut this = Self {
1795 focus_handle,
1796 show_cursor_when_unfocused: false,
1797 last_focused_descendant: None,
1798 buffer: buffer.clone(),
1799 display_map: display_map.clone(),
1800 selections,
1801 scroll_manager: ScrollManager::new(cx),
1802 columnar_selection_tail: None,
1803 add_selections_state: None,
1804 select_next_state: None,
1805 select_prev_state: None,
1806 selection_history: Default::default(),
1807 autoclose_regions: Default::default(),
1808 snippet_stack: Default::default(),
1809 select_larger_syntax_node_stack: Vec::new(),
1810 ime_transaction: Default::default(),
1811 active_diagnostics: None,
1812 soft_wrap_mode_override,
1813 completion_provider: project.clone().map(|project| Box::new(project) as _),
1814 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1815 project,
1816 blink_manager: blink_manager.clone(),
1817 show_local_selections: true,
1818 mode,
1819 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1820 show_gutter: mode == EditorMode::Full,
1821 show_line_numbers: None,
1822 show_git_diff_gutter: None,
1823 show_code_actions: None,
1824 show_runnables: None,
1825 show_wrap_guides: None,
1826 redact_all: false,
1827 show_indent_guides,
1828 placeholder_text: None,
1829 highlight_order: 0,
1830 highlighted_rows: HashMap::default(),
1831 background_highlights: Default::default(),
1832 gutter_highlights: TreeMap::default(),
1833 scrollbar_marker_state: ScrollbarMarkerState::default(),
1834 active_indent_guides_state: ActiveIndentGuidesState::default(),
1835 nav_history: None,
1836 context_menu: RwLock::new(None),
1837 mouse_context_menu: None,
1838 completion_tasks: Default::default(),
1839 signature_help_state: SignatureHelpState::default(),
1840 auto_signature_help: None,
1841 find_all_references_task_sources: Vec::new(),
1842 next_completion_id: 0,
1843 completion_documentation_pre_resolve_debounce: DebouncedDelay::new(),
1844 next_inlay_id: 0,
1845 available_code_actions: Default::default(),
1846 code_actions_task: Default::default(),
1847 document_highlights_task: Default::default(),
1848 linked_editing_range_task: Default::default(),
1849 pending_rename: Default::default(),
1850 searchable: true,
1851 cursor_shape: Default::default(),
1852 current_line_highlight: None,
1853 autoindent_mode: Some(AutoindentMode::EachLine),
1854 collapse_matches: false,
1855 workspace: None,
1856 keymap_context_layers: Default::default(),
1857 input_enabled: true,
1858 use_modal_editing: mode == EditorMode::Full,
1859 read_only: false,
1860 use_autoclose: true,
1861 use_auto_surround: true,
1862 auto_replace_emoji_shortcode: false,
1863 leader_peer_id: None,
1864 remote_id: None,
1865 hover_state: Default::default(),
1866 hovered_link_state: Default::default(),
1867 inline_completion_provider: None,
1868 active_inline_completion: None,
1869 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1870 expanded_hunks: ExpandedHunks::default(),
1871 gutter_hovered: false,
1872 pixel_position_of_newest_cursor: None,
1873 last_bounds: None,
1874 expect_bounds_change: None,
1875 gutter_dimensions: GutterDimensions::default(),
1876 style: None,
1877 show_cursor_names: false,
1878 hovered_cursors: Default::default(),
1879 next_editor_action_id: EditorActionId::default(),
1880 editor_actions: Rc::default(),
1881 vim_replace_map: Default::default(),
1882 show_inline_completions: mode == EditorMode::Full,
1883 custom_context_menu: None,
1884 show_git_blame_gutter: false,
1885 show_git_blame_inline: false,
1886 show_selection_menu: None,
1887 show_git_blame_inline_delay_task: None,
1888 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1889 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1890 .session
1891 .restore_unsaved_buffers,
1892 blame: None,
1893 blame_subscription: None,
1894 file_header_size,
1895 tasks: Default::default(),
1896 _subscriptions: vec![
1897 cx.observe(&buffer, Self::on_buffer_changed),
1898 cx.subscribe(&buffer, Self::on_buffer_event),
1899 cx.observe(&display_map, Self::on_display_map_changed),
1900 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1901 cx.observe_global::<SettingsStore>(Self::settings_changed),
1902 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1903 cx.observe_window_activation(|editor, cx| {
1904 let active = cx.is_window_active();
1905 editor.blink_manager.update(cx, |blink_manager, cx| {
1906 if active {
1907 blink_manager.enable(cx);
1908 } else {
1909 blink_manager.show_cursor(cx);
1910 blink_manager.disable(cx);
1911 }
1912 });
1913 }),
1914 ],
1915 tasks_update_task: None,
1916 linked_edit_ranges: Default::default(),
1917 previous_search_ranges: None,
1918 breadcrumb_header: None,
1919 focused_block: None,
1920 };
1921 this.tasks_update_task = Some(this.refresh_runnables(cx));
1922 this._subscriptions.extend(project_subscriptions);
1923
1924 this.end_selection(cx);
1925 this.scroll_manager.show_scrollbar(cx);
1926
1927 if mode == EditorMode::Full {
1928 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1929 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1930
1931 if this.git_blame_inline_enabled {
1932 this.git_blame_inline_enabled = true;
1933 this.start_git_blame_inline(false, cx);
1934 }
1935 }
1936
1937 this.report_editor_event("open", None, cx);
1938 this
1939 }
1940
1941 pub fn mouse_menu_is_focused(&self, cx: &mut WindowContext) -> bool {
1942 self.mouse_context_menu
1943 .as_ref()
1944 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(cx))
1945 }
1946
1947 fn key_context(&self, cx: &AppContext) -> KeyContext {
1948 let mut key_context = KeyContext::new_with_defaults();
1949 key_context.add("Editor");
1950 let mode = match self.mode {
1951 EditorMode::SingleLine { .. } => "single_line",
1952 EditorMode::AutoHeight { .. } => "auto_height",
1953 EditorMode::Full => "full",
1954 };
1955
1956 if EditorSettings::get_global(cx).jupyter.enabled {
1957 key_context.add("jupyter");
1958 }
1959
1960 key_context.set("mode", mode);
1961 if self.pending_rename.is_some() {
1962 key_context.add("renaming");
1963 }
1964 if self.context_menu_visible() {
1965 match self.context_menu.read().as_ref() {
1966 Some(ContextMenu::Completions(_)) => {
1967 key_context.add("menu");
1968 key_context.add("showing_completions")
1969 }
1970 Some(ContextMenu::CodeActions(_)) => {
1971 key_context.add("menu");
1972 key_context.add("showing_code_actions")
1973 }
1974 None => {}
1975 }
1976 }
1977
1978 for layer in self.keymap_context_layers.values() {
1979 key_context.extend(layer);
1980 }
1981
1982 if let Some(extension) = self
1983 .buffer
1984 .read(cx)
1985 .as_singleton()
1986 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
1987 {
1988 key_context.set("extension", extension.to_string());
1989 }
1990
1991 if self.has_active_inline_completion(cx) {
1992 key_context.add("copilot_suggestion");
1993 key_context.add("inline_completion");
1994 }
1995
1996 key_context
1997 }
1998
1999 pub fn new_file(
2000 workspace: &mut Workspace,
2001 _: &workspace::NewFile,
2002 cx: &mut ViewContext<Workspace>,
2003 ) {
2004 Self::new_in_workspace(workspace, cx).detach_and_prompt_err(
2005 "Failed to create buffer",
2006 cx,
2007 |e, _| match e.error_code() {
2008 ErrorCode::RemoteUpgradeRequired => Some(format!(
2009 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2010 e.error_tag("required").unwrap_or("the latest version")
2011 )),
2012 _ => None,
2013 },
2014 );
2015 }
2016
2017 pub fn new_in_workspace(
2018 workspace: &mut Workspace,
2019 cx: &mut ViewContext<Workspace>,
2020 ) -> Task<Result<View<Editor>>> {
2021 let project = workspace.project().clone();
2022 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2023
2024 cx.spawn(|workspace, mut cx| async move {
2025 let buffer = create.await?;
2026 workspace.update(&mut cx, |workspace, cx| {
2027 let editor =
2028 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx));
2029 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
2030 editor
2031 })
2032 })
2033 }
2034
2035 pub fn new_file_in_direction(
2036 workspace: &mut Workspace,
2037 action: &workspace::NewFileInDirection,
2038 cx: &mut ViewContext<Workspace>,
2039 ) {
2040 let project = workspace.project().clone();
2041 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2042 let direction = action.0;
2043
2044 cx.spawn(|workspace, mut cx| async move {
2045 let buffer = create.await?;
2046 workspace.update(&mut cx, move |workspace, cx| {
2047 workspace.split_item(
2048 direction,
2049 Box::new(
2050 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)),
2051 ),
2052 cx,
2053 )
2054 })?;
2055 anyhow::Ok(())
2056 })
2057 .detach_and_prompt_err("Failed to create buffer", cx, |e, _| match e.error_code() {
2058 ErrorCode::RemoteUpgradeRequired => Some(format!(
2059 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2060 e.error_tag("required").unwrap_or("the latest version")
2061 )),
2062 _ => None,
2063 });
2064 }
2065
2066 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
2067 self.buffer.read(cx).replica_id()
2068 }
2069
2070 pub fn leader_peer_id(&self) -> Option<PeerId> {
2071 self.leader_peer_id
2072 }
2073
2074 pub fn buffer(&self) -> &Model<MultiBuffer> {
2075 &self.buffer
2076 }
2077
2078 pub fn workspace(&self) -> Option<View<Workspace>> {
2079 self.workspace.as_ref()?.0.upgrade()
2080 }
2081
2082 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
2083 self.buffer().read(cx).title(cx)
2084 }
2085
2086 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
2087 EditorSnapshot {
2088 mode: self.mode,
2089 show_gutter: self.show_gutter,
2090 show_line_numbers: self.show_line_numbers,
2091 show_git_diff_gutter: self.show_git_diff_gutter,
2092 show_code_actions: self.show_code_actions,
2093 show_runnables: self.show_runnables,
2094 render_git_blame_gutter: self.render_git_blame_gutter(cx),
2095 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
2096 scroll_anchor: self.scroll_manager.anchor(),
2097 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
2098 placeholder_text: self.placeholder_text.clone(),
2099 is_focused: self.focus_handle.is_focused(cx),
2100 current_line_highlight: self
2101 .current_line_highlight
2102 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
2103 gutter_hovered: self.gutter_hovered,
2104 }
2105 }
2106
2107 pub fn language_at<T: ToOffset>(&self, point: T, cx: &AppContext) -> Option<Arc<Language>> {
2108 self.buffer.read(cx).language_at(point, cx)
2109 }
2110
2111 pub fn file_at<T: ToOffset>(
2112 &self,
2113 point: T,
2114 cx: &AppContext,
2115 ) -> Option<Arc<dyn language::File>> {
2116 self.buffer.read(cx).read(cx).file_at(point).cloned()
2117 }
2118
2119 pub fn active_excerpt(
2120 &self,
2121 cx: &AppContext,
2122 ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
2123 self.buffer
2124 .read(cx)
2125 .excerpt_containing(self.selections.newest_anchor().head(), cx)
2126 }
2127
2128 pub fn mode(&self) -> EditorMode {
2129 self.mode
2130 }
2131
2132 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
2133 self.collaboration_hub.as_deref()
2134 }
2135
2136 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
2137 self.collaboration_hub = Some(hub);
2138 }
2139
2140 pub fn set_custom_context_menu(
2141 &mut self,
2142 f: impl 'static
2143 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
2144 ) {
2145 self.custom_context_menu = Some(Box::new(f))
2146 }
2147
2148 pub fn set_completion_provider(&mut self, provider: Box<dyn CompletionProvider>) {
2149 self.completion_provider = Some(provider);
2150 }
2151
2152 pub fn set_inline_completion_provider<T>(
2153 &mut self,
2154 provider: Option<Model<T>>,
2155 cx: &mut ViewContext<Self>,
2156 ) where
2157 T: InlineCompletionProvider,
2158 {
2159 self.inline_completion_provider =
2160 provider.map(|provider| RegisteredInlineCompletionProvider {
2161 _subscription: cx.observe(&provider, |this, _, cx| {
2162 if this.focus_handle.is_focused(cx) {
2163 this.update_visible_inline_completion(cx);
2164 }
2165 }),
2166 provider: Arc::new(provider),
2167 });
2168 self.refresh_inline_completion(false, cx);
2169 }
2170
2171 pub fn placeholder_text(&self, _cx: &WindowContext) -> Option<&str> {
2172 self.placeholder_text.as_deref()
2173 }
2174
2175 pub fn set_placeholder_text(
2176 &mut self,
2177 placeholder_text: impl Into<Arc<str>>,
2178 cx: &mut ViewContext<Self>,
2179 ) {
2180 let placeholder_text = Some(placeholder_text.into());
2181 if self.placeholder_text != placeholder_text {
2182 self.placeholder_text = placeholder_text;
2183 cx.notify();
2184 }
2185 }
2186
2187 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
2188 self.cursor_shape = cursor_shape;
2189
2190 // Disrupt blink for immediate user feedback that the cursor shape has changed
2191 self.blink_manager.update(cx, BlinkManager::show_cursor);
2192
2193 cx.notify();
2194 }
2195
2196 pub fn set_current_line_highlight(
2197 &mut self,
2198 current_line_highlight: Option<CurrentLineHighlight>,
2199 ) {
2200 self.current_line_highlight = current_line_highlight;
2201 }
2202
2203 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
2204 self.collapse_matches = collapse_matches;
2205 }
2206
2207 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
2208 if self.collapse_matches {
2209 return range.start..range.start;
2210 }
2211 range.clone()
2212 }
2213
2214 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
2215 if self.display_map.read(cx).clip_at_line_ends != clip {
2216 self.display_map
2217 .update(cx, |map, _| map.clip_at_line_ends = clip);
2218 }
2219 }
2220
2221 pub fn set_keymap_context_layer<Tag: 'static>(
2222 &mut self,
2223 context: KeyContext,
2224 cx: &mut ViewContext<Self>,
2225 ) {
2226 self.keymap_context_layers
2227 .insert(TypeId::of::<Tag>(), context);
2228 cx.notify();
2229 }
2230
2231 pub fn remove_keymap_context_layer<Tag: 'static>(&mut self, cx: &mut ViewContext<Self>) {
2232 self.keymap_context_layers.remove(&TypeId::of::<Tag>());
2233 cx.notify();
2234 }
2235
2236 pub fn set_input_enabled(&mut self, input_enabled: bool) {
2237 self.input_enabled = input_enabled;
2238 }
2239
2240 pub fn set_autoindent(&mut self, autoindent: bool) {
2241 if autoindent {
2242 self.autoindent_mode = Some(AutoindentMode::EachLine);
2243 } else {
2244 self.autoindent_mode = None;
2245 }
2246 }
2247
2248 pub fn read_only(&self, cx: &AppContext) -> bool {
2249 self.read_only || self.buffer.read(cx).read_only()
2250 }
2251
2252 pub fn set_read_only(&mut self, read_only: bool) {
2253 self.read_only = read_only;
2254 }
2255
2256 pub fn set_use_autoclose(&mut self, autoclose: bool) {
2257 self.use_autoclose = autoclose;
2258 }
2259
2260 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
2261 self.use_auto_surround = auto_surround;
2262 }
2263
2264 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
2265 self.auto_replace_emoji_shortcode = auto_replace;
2266 }
2267
2268 pub fn set_show_inline_completions(&mut self, show_inline_completions: bool) {
2269 self.show_inline_completions = show_inline_completions;
2270 }
2271
2272 pub fn set_use_modal_editing(&mut self, to: bool) {
2273 self.use_modal_editing = to;
2274 }
2275
2276 pub fn use_modal_editing(&self) -> bool {
2277 self.use_modal_editing
2278 }
2279
2280 fn selections_did_change(
2281 &mut self,
2282 local: bool,
2283 old_cursor_position: &Anchor,
2284 show_completions: bool,
2285 cx: &mut ViewContext<Self>,
2286 ) {
2287 // Copy selections to primary selection buffer
2288 #[cfg(target_os = "linux")]
2289 if local {
2290 let selections = self.selections.all::<usize>(cx);
2291 let buffer_handle = self.buffer.read(cx).read(cx);
2292
2293 let mut text = String::new();
2294 for (index, selection) in selections.iter().enumerate() {
2295 let text_for_selection = buffer_handle
2296 .text_for_range(selection.start..selection.end)
2297 .collect::<String>();
2298
2299 text.push_str(&text_for_selection);
2300 if index != selections.len() - 1 {
2301 text.push('\n');
2302 }
2303 }
2304
2305 if !text.is_empty() {
2306 cx.write_to_primary(ClipboardItem::new(text));
2307 }
2308 }
2309
2310 if self.focus_handle.is_focused(cx) && self.leader_peer_id.is_none() {
2311 self.buffer.update(cx, |buffer, cx| {
2312 buffer.set_active_selections(
2313 &self.selections.disjoint_anchors(),
2314 self.selections.line_mode,
2315 self.cursor_shape,
2316 cx,
2317 )
2318 });
2319 }
2320 let display_map = self
2321 .display_map
2322 .update(cx, |display_map, cx| display_map.snapshot(cx));
2323 let buffer = &display_map.buffer_snapshot;
2324 self.add_selections_state = None;
2325 self.select_next_state = None;
2326 self.select_prev_state = None;
2327 self.select_larger_syntax_node_stack.clear();
2328 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2329 self.snippet_stack
2330 .invalidate(&self.selections.disjoint_anchors(), buffer);
2331 self.take_rename(false, cx);
2332
2333 let new_cursor_position = self.selections.newest_anchor().head();
2334
2335 self.push_to_nav_history(
2336 *old_cursor_position,
2337 Some(new_cursor_position.to_point(buffer)),
2338 cx,
2339 );
2340
2341 if local {
2342 let new_cursor_position = self.selections.newest_anchor().head();
2343 let mut context_menu = self.context_menu.write();
2344 let completion_menu = match context_menu.as_ref() {
2345 Some(ContextMenu::Completions(menu)) => Some(menu),
2346
2347 _ => {
2348 *context_menu = None;
2349 None
2350 }
2351 };
2352
2353 if let Some(completion_menu) = completion_menu {
2354 let cursor_position = new_cursor_position.to_offset(buffer);
2355 let (word_range, kind) = buffer.surrounding_word(completion_menu.initial_position);
2356 if kind == Some(CharKind::Word)
2357 && word_range.to_inclusive().contains(&cursor_position)
2358 {
2359 let mut completion_menu = completion_menu.clone();
2360 drop(context_menu);
2361
2362 let query = Self::completion_query(buffer, cursor_position);
2363 cx.spawn(move |this, mut cx| async move {
2364 completion_menu
2365 .filter(query.as_deref(), cx.background_executor().clone())
2366 .await;
2367
2368 this.update(&mut cx, |this, cx| {
2369 let mut context_menu = this.context_menu.write();
2370 let Some(ContextMenu::Completions(menu)) = context_menu.as_ref() else {
2371 return;
2372 };
2373
2374 if menu.id > completion_menu.id {
2375 return;
2376 }
2377
2378 *context_menu = Some(ContextMenu::Completions(completion_menu));
2379 drop(context_menu);
2380 cx.notify();
2381 })
2382 })
2383 .detach();
2384
2385 if show_completions {
2386 self.show_completions(&ShowCompletions { trigger: None }, cx);
2387 }
2388 } else {
2389 drop(context_menu);
2390 self.hide_context_menu(cx);
2391 }
2392 } else {
2393 drop(context_menu);
2394 }
2395
2396 hide_hover(self, cx);
2397
2398 if old_cursor_position.to_display_point(&display_map).row()
2399 != new_cursor_position.to_display_point(&display_map).row()
2400 {
2401 self.available_code_actions.take();
2402 }
2403 self.refresh_code_actions(cx);
2404 self.refresh_document_highlights(cx);
2405 refresh_matching_bracket_highlights(self, cx);
2406 self.discard_inline_completion(false, cx);
2407 linked_editing_ranges::refresh_linked_ranges(self, cx);
2408 if self.git_blame_inline_enabled {
2409 self.start_inline_blame_timer(cx);
2410 }
2411 }
2412
2413 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2414 cx.emit(EditorEvent::SelectionsChanged { local });
2415
2416 if self.selections.disjoint_anchors().len() == 1 {
2417 cx.emit(SearchEvent::ActiveMatchChanged)
2418 }
2419 cx.notify();
2420 }
2421
2422 pub fn change_selections<R>(
2423 &mut self,
2424 autoscroll: Option<Autoscroll>,
2425 cx: &mut ViewContext<Self>,
2426 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2427 ) -> R {
2428 self.change_selections_inner(autoscroll, true, cx, change)
2429 }
2430
2431 pub fn change_selections_inner<R>(
2432 &mut self,
2433 autoscroll: Option<Autoscroll>,
2434 request_completions: bool,
2435 cx: &mut ViewContext<Self>,
2436 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2437 ) -> R {
2438 let old_cursor_position = self.selections.newest_anchor().head();
2439 self.push_to_selection_history();
2440
2441 let (changed, result) = self.selections.change_with(cx, change);
2442
2443 if changed {
2444 if let Some(autoscroll) = autoscroll {
2445 self.request_autoscroll(autoscroll, cx);
2446 }
2447 self.selections_did_change(true, &old_cursor_position, request_completions, cx);
2448
2449 if self.should_open_signature_help_automatically(
2450 &old_cursor_position,
2451 self.signature_help_state.backspace_pressed(),
2452 cx,
2453 ) {
2454 self.show_signature_help(&ShowSignatureHelp, cx);
2455 }
2456 self.signature_help_state.set_backspace_pressed(false);
2457 }
2458
2459 result
2460 }
2461
2462 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2463 where
2464 I: IntoIterator<Item = (Range<S>, T)>,
2465 S: ToOffset,
2466 T: Into<Arc<str>>,
2467 {
2468 if self.read_only(cx) {
2469 return;
2470 }
2471
2472 self.buffer
2473 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2474 }
2475
2476 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2477 where
2478 I: IntoIterator<Item = (Range<S>, T)>,
2479 S: ToOffset,
2480 T: Into<Arc<str>>,
2481 {
2482 if self.read_only(cx) {
2483 return;
2484 }
2485
2486 self.buffer.update(cx, |buffer, cx| {
2487 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2488 });
2489 }
2490
2491 pub fn edit_with_block_indent<I, S, T>(
2492 &mut self,
2493 edits: I,
2494 original_indent_columns: Vec<u32>,
2495 cx: &mut ViewContext<Self>,
2496 ) where
2497 I: IntoIterator<Item = (Range<S>, T)>,
2498 S: ToOffset,
2499 T: Into<Arc<str>>,
2500 {
2501 if self.read_only(cx) {
2502 return;
2503 }
2504
2505 self.buffer.update(cx, |buffer, cx| {
2506 buffer.edit(
2507 edits,
2508 Some(AutoindentMode::Block {
2509 original_indent_columns,
2510 }),
2511 cx,
2512 )
2513 });
2514 }
2515
2516 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
2517 self.hide_context_menu(cx);
2518
2519 match phase {
2520 SelectPhase::Begin {
2521 position,
2522 add,
2523 click_count,
2524 } => self.begin_selection(position, add, click_count, cx),
2525 SelectPhase::BeginColumnar {
2526 position,
2527 goal_column,
2528 reset,
2529 } => self.begin_columnar_selection(position, goal_column, reset, cx),
2530 SelectPhase::Extend {
2531 position,
2532 click_count,
2533 } => self.extend_selection(position, click_count, cx),
2534 SelectPhase::Update {
2535 position,
2536 goal_column,
2537 scroll_delta,
2538 } => self.update_selection(position, goal_column, scroll_delta, cx),
2539 SelectPhase::End => self.end_selection(cx),
2540 }
2541 }
2542
2543 fn extend_selection(
2544 &mut self,
2545 position: DisplayPoint,
2546 click_count: usize,
2547 cx: &mut ViewContext<Self>,
2548 ) {
2549 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2550 let tail = self.selections.newest::<usize>(cx).tail();
2551 self.begin_selection(position, false, click_count, cx);
2552
2553 let position = position.to_offset(&display_map, Bias::Left);
2554 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2555
2556 let mut pending_selection = self
2557 .selections
2558 .pending_anchor()
2559 .expect("extend_selection not called with pending selection");
2560 if position >= tail {
2561 pending_selection.start = tail_anchor;
2562 } else {
2563 pending_selection.end = tail_anchor;
2564 pending_selection.reversed = true;
2565 }
2566
2567 let mut pending_mode = self.selections.pending_mode().unwrap();
2568 match &mut pending_mode {
2569 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2570 _ => {}
2571 }
2572
2573 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
2574 s.set_pending(pending_selection, pending_mode)
2575 });
2576 }
2577
2578 fn begin_selection(
2579 &mut self,
2580 position: DisplayPoint,
2581 add: bool,
2582 click_count: usize,
2583 cx: &mut ViewContext<Self>,
2584 ) {
2585 if !self.focus_handle.is_focused(cx) {
2586 self.last_focused_descendant = None;
2587 cx.focus(&self.focus_handle);
2588 }
2589
2590 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2591 let buffer = &display_map.buffer_snapshot;
2592 let newest_selection = self.selections.newest_anchor().clone();
2593 let position = display_map.clip_point(position, Bias::Left);
2594
2595 let start;
2596 let end;
2597 let mode;
2598 let auto_scroll;
2599 match click_count {
2600 1 => {
2601 start = buffer.anchor_before(position.to_point(&display_map));
2602 end = start;
2603 mode = SelectMode::Character;
2604 auto_scroll = true;
2605 }
2606 2 => {
2607 let range = movement::surrounding_word(&display_map, position);
2608 start = buffer.anchor_before(range.start.to_point(&display_map));
2609 end = buffer.anchor_before(range.end.to_point(&display_map));
2610 mode = SelectMode::Word(start..end);
2611 auto_scroll = true;
2612 }
2613 3 => {
2614 let position = display_map
2615 .clip_point(position, Bias::Left)
2616 .to_point(&display_map);
2617 let line_start = display_map.prev_line_boundary(position).0;
2618 let next_line_start = buffer.clip_point(
2619 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2620 Bias::Left,
2621 );
2622 start = buffer.anchor_before(line_start);
2623 end = buffer.anchor_before(next_line_start);
2624 mode = SelectMode::Line(start..end);
2625 auto_scroll = true;
2626 }
2627 _ => {
2628 start = buffer.anchor_before(0);
2629 end = buffer.anchor_before(buffer.len());
2630 mode = SelectMode::All;
2631 auto_scroll = false;
2632 }
2633 }
2634
2635 let point_to_delete: Option<usize> = {
2636 let selected_points: Vec<Selection<Point>> =
2637 self.selections.disjoint_in_range(start..end, cx);
2638
2639 if !add || click_count > 1 {
2640 None
2641 } else if selected_points.len() > 0 {
2642 Some(selected_points[0].id)
2643 } else {
2644 let clicked_point_already_selected =
2645 self.selections.disjoint.iter().find(|selection| {
2646 selection.start.to_point(buffer) == start.to_point(buffer)
2647 || selection.end.to_point(buffer) == end.to_point(buffer)
2648 });
2649
2650 if let Some(selection) = clicked_point_already_selected {
2651 Some(selection.id)
2652 } else {
2653 None
2654 }
2655 }
2656 };
2657
2658 let selections_count = self.selections.count();
2659
2660 self.change_selections(auto_scroll.then(|| Autoscroll::newest()), cx, |s| {
2661 if let Some(point_to_delete) = point_to_delete {
2662 s.delete(point_to_delete);
2663
2664 if selections_count == 1 {
2665 s.set_pending_anchor_range(start..end, mode);
2666 }
2667 } else {
2668 if !add {
2669 s.clear_disjoint();
2670 } else if click_count > 1 {
2671 s.delete(newest_selection.id)
2672 }
2673
2674 s.set_pending_anchor_range(start..end, mode);
2675 }
2676 });
2677 }
2678
2679 fn begin_columnar_selection(
2680 &mut self,
2681 position: DisplayPoint,
2682 goal_column: u32,
2683 reset: bool,
2684 cx: &mut ViewContext<Self>,
2685 ) {
2686 if !self.focus_handle.is_focused(cx) {
2687 self.last_focused_descendant = None;
2688 cx.focus(&self.focus_handle);
2689 }
2690
2691 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2692
2693 if reset {
2694 let pointer_position = display_map
2695 .buffer_snapshot
2696 .anchor_before(position.to_point(&display_map));
2697
2698 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
2699 s.clear_disjoint();
2700 s.set_pending_anchor_range(
2701 pointer_position..pointer_position,
2702 SelectMode::Character,
2703 );
2704 });
2705 }
2706
2707 let tail = self.selections.newest::<Point>(cx).tail();
2708 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2709
2710 if !reset {
2711 self.select_columns(
2712 tail.to_display_point(&display_map),
2713 position,
2714 goal_column,
2715 &display_map,
2716 cx,
2717 );
2718 }
2719 }
2720
2721 fn update_selection(
2722 &mut self,
2723 position: DisplayPoint,
2724 goal_column: u32,
2725 scroll_delta: gpui::Point<f32>,
2726 cx: &mut ViewContext<Self>,
2727 ) {
2728 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2729
2730 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2731 let tail = tail.to_display_point(&display_map);
2732 self.select_columns(tail, position, goal_column, &display_map, cx);
2733 } else if let Some(mut pending) = self.selections.pending_anchor() {
2734 let buffer = self.buffer.read(cx).snapshot(cx);
2735 let head;
2736 let tail;
2737 let mode = self.selections.pending_mode().unwrap();
2738 match &mode {
2739 SelectMode::Character => {
2740 head = position.to_point(&display_map);
2741 tail = pending.tail().to_point(&buffer);
2742 }
2743 SelectMode::Word(original_range) => {
2744 let original_display_range = original_range.start.to_display_point(&display_map)
2745 ..original_range.end.to_display_point(&display_map);
2746 let original_buffer_range = original_display_range.start.to_point(&display_map)
2747 ..original_display_range.end.to_point(&display_map);
2748 if movement::is_inside_word(&display_map, position)
2749 || original_display_range.contains(&position)
2750 {
2751 let word_range = movement::surrounding_word(&display_map, position);
2752 if word_range.start < original_display_range.start {
2753 head = word_range.start.to_point(&display_map);
2754 } else {
2755 head = word_range.end.to_point(&display_map);
2756 }
2757 } else {
2758 head = position.to_point(&display_map);
2759 }
2760
2761 if head <= original_buffer_range.start {
2762 tail = original_buffer_range.end;
2763 } else {
2764 tail = original_buffer_range.start;
2765 }
2766 }
2767 SelectMode::Line(original_range) => {
2768 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2769
2770 let position = display_map
2771 .clip_point(position, Bias::Left)
2772 .to_point(&display_map);
2773 let line_start = display_map.prev_line_boundary(position).0;
2774 let next_line_start = buffer.clip_point(
2775 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2776 Bias::Left,
2777 );
2778
2779 if line_start < original_range.start {
2780 head = line_start
2781 } else {
2782 head = next_line_start
2783 }
2784
2785 if head <= original_range.start {
2786 tail = original_range.end;
2787 } else {
2788 tail = original_range.start;
2789 }
2790 }
2791 SelectMode::All => {
2792 return;
2793 }
2794 };
2795
2796 if head < tail {
2797 pending.start = buffer.anchor_before(head);
2798 pending.end = buffer.anchor_before(tail);
2799 pending.reversed = true;
2800 } else {
2801 pending.start = buffer.anchor_before(tail);
2802 pending.end = buffer.anchor_before(head);
2803 pending.reversed = false;
2804 }
2805
2806 self.change_selections(None, cx, |s| {
2807 s.set_pending(pending, mode);
2808 });
2809 } else {
2810 log::error!("update_selection dispatched with no pending selection");
2811 return;
2812 }
2813
2814 self.apply_scroll_delta(scroll_delta, cx);
2815 cx.notify();
2816 }
2817
2818 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
2819 self.columnar_selection_tail.take();
2820 if self.selections.pending_anchor().is_some() {
2821 let selections = self.selections.all::<usize>(cx);
2822 self.change_selections(None, cx, |s| {
2823 s.select(selections);
2824 s.clear_pending();
2825 });
2826 }
2827 }
2828
2829 fn select_columns(
2830 &mut self,
2831 tail: DisplayPoint,
2832 head: DisplayPoint,
2833 goal_column: u32,
2834 display_map: &DisplaySnapshot,
2835 cx: &mut ViewContext<Self>,
2836 ) {
2837 let start_row = cmp::min(tail.row(), head.row());
2838 let end_row = cmp::max(tail.row(), head.row());
2839 let start_column = cmp::min(tail.column(), goal_column);
2840 let end_column = cmp::max(tail.column(), goal_column);
2841 let reversed = start_column < tail.column();
2842
2843 let selection_ranges = (start_row.0..=end_row.0)
2844 .map(DisplayRow)
2845 .filter_map(|row| {
2846 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2847 let start = display_map
2848 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2849 .to_point(display_map);
2850 let end = display_map
2851 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2852 .to_point(display_map);
2853 if reversed {
2854 Some(end..start)
2855 } else {
2856 Some(start..end)
2857 }
2858 } else {
2859 None
2860 }
2861 })
2862 .collect::<Vec<_>>();
2863
2864 self.change_selections(None, cx, |s| {
2865 s.select_ranges(selection_ranges);
2866 });
2867 cx.notify();
2868 }
2869
2870 pub fn has_pending_nonempty_selection(&self) -> bool {
2871 let pending_nonempty_selection = match self.selections.pending_anchor() {
2872 Some(Selection { start, end, .. }) => start != end,
2873 None => false,
2874 };
2875
2876 pending_nonempty_selection
2877 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2878 }
2879
2880 pub fn has_pending_selection(&self) -> bool {
2881 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2882 }
2883
2884 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
2885 if self.clear_clicked_diff_hunks(cx) {
2886 cx.notify();
2887 return;
2888 }
2889 if self.dismiss_menus_and_popups(true, cx) {
2890 return;
2891 }
2892
2893 if self.mode == EditorMode::Full {
2894 if self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel()) {
2895 return;
2896 }
2897 }
2898
2899 cx.propagate();
2900 }
2901
2902 pub fn dismiss_menus_and_popups(
2903 &mut self,
2904 should_report_inline_completion_event: bool,
2905 cx: &mut ViewContext<Self>,
2906 ) -> bool {
2907 if self.take_rename(false, cx).is_some() {
2908 return true;
2909 }
2910
2911 if hide_hover(self, cx) {
2912 return true;
2913 }
2914
2915 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2916 return true;
2917 }
2918
2919 if self.hide_context_menu(cx).is_some() {
2920 return true;
2921 }
2922
2923 if self.mouse_context_menu.take().is_some() {
2924 return true;
2925 }
2926
2927 if self.discard_inline_completion(should_report_inline_completion_event, cx) {
2928 return true;
2929 }
2930
2931 if self.snippet_stack.pop().is_some() {
2932 return true;
2933 }
2934
2935 if self.mode == EditorMode::Full {
2936 if self.active_diagnostics.is_some() {
2937 self.dismiss_diagnostics(cx);
2938 return true;
2939 }
2940 }
2941
2942 false
2943 }
2944
2945 fn linked_editing_ranges_for(
2946 &self,
2947 selection: Range<text::Anchor>,
2948 cx: &AppContext,
2949 ) -> Option<HashMap<Model<Buffer>, Vec<Range<text::Anchor>>>> {
2950 if self.linked_edit_ranges.is_empty() {
2951 return None;
2952 }
2953 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2954 selection.end.buffer_id.and_then(|end_buffer_id| {
2955 if selection.start.buffer_id != Some(end_buffer_id) {
2956 return None;
2957 }
2958 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2959 let snapshot = buffer.read(cx).snapshot();
2960 self.linked_edit_ranges
2961 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2962 .map(|ranges| (ranges, snapshot, buffer))
2963 })?;
2964 use text::ToOffset as TO;
2965 // find offset from the start of current range to current cursor position
2966 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2967
2968 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2969 let start_difference = start_offset - start_byte_offset;
2970 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2971 let end_difference = end_offset - start_byte_offset;
2972 // Current range has associated linked ranges.
2973 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2974 for range in linked_ranges.iter() {
2975 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2976 let end_offset = start_offset + end_difference;
2977 let start_offset = start_offset + start_difference;
2978 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2979 continue;
2980 }
2981 let start = buffer_snapshot.anchor_after(start_offset);
2982 let end = buffer_snapshot.anchor_after(end_offset);
2983 linked_edits
2984 .entry(buffer.clone())
2985 .or_default()
2986 .push(start..end);
2987 }
2988 Some(linked_edits)
2989 }
2990
2991 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2992 let text: Arc<str> = text.into();
2993
2994 if self.read_only(cx) {
2995 return;
2996 }
2997
2998 let selections = self.selections.all_adjusted(cx);
2999 let mut bracket_inserted = false;
3000 let mut edits = Vec::new();
3001 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3002 let mut new_selections = Vec::with_capacity(selections.len());
3003 let mut new_autoclose_regions = Vec::new();
3004 let snapshot = self.buffer.read(cx).read(cx);
3005
3006 for (selection, autoclose_region) in
3007 self.selections_with_autoclose_regions(selections, &snapshot)
3008 {
3009 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
3010 // Determine if the inserted text matches the opening or closing
3011 // bracket of any of this language's bracket pairs.
3012 let mut bracket_pair = None;
3013 let mut is_bracket_pair_start = false;
3014 let mut is_bracket_pair_end = false;
3015 if !text.is_empty() {
3016 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
3017 // and they are removing the character that triggered IME popup.
3018 for (pair, enabled) in scope.brackets() {
3019 if !pair.close && !pair.surround {
3020 continue;
3021 }
3022
3023 if enabled && pair.start.ends_with(text.as_ref()) {
3024 bracket_pair = Some(pair.clone());
3025 is_bracket_pair_start = true;
3026 break;
3027 }
3028 if pair.end.as_str() == text.as_ref() {
3029 bracket_pair = Some(pair.clone());
3030 is_bracket_pair_end = true;
3031 break;
3032 }
3033 }
3034 }
3035
3036 if let Some(bracket_pair) = bracket_pair {
3037 let snapshot_settings = snapshot.settings_at(selection.start, cx);
3038 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
3039 let auto_surround =
3040 self.use_auto_surround && snapshot_settings.use_auto_surround;
3041 if selection.is_empty() {
3042 if is_bracket_pair_start {
3043 let prefix_len = bracket_pair.start.len() - text.len();
3044
3045 // If the inserted text is a suffix of an opening bracket and the
3046 // selection is preceded by the rest of the opening bracket, then
3047 // insert the closing bracket.
3048 let following_text_allows_autoclose = snapshot
3049 .chars_at(selection.start)
3050 .next()
3051 .map_or(true, |c| scope.should_autoclose_before(c));
3052 let preceding_text_matches_prefix = prefix_len == 0
3053 || (selection.start.column >= (prefix_len as u32)
3054 && snapshot.contains_str_at(
3055 Point::new(
3056 selection.start.row,
3057 selection.start.column - (prefix_len as u32),
3058 ),
3059 &bracket_pair.start[..prefix_len],
3060 ));
3061
3062 if autoclose
3063 && bracket_pair.close
3064 && following_text_allows_autoclose
3065 && preceding_text_matches_prefix
3066 {
3067 let anchor = snapshot.anchor_before(selection.end);
3068 new_selections.push((selection.map(|_| anchor), text.len()));
3069 new_autoclose_regions.push((
3070 anchor,
3071 text.len(),
3072 selection.id,
3073 bracket_pair.clone(),
3074 ));
3075 edits.push((
3076 selection.range(),
3077 format!("{}{}", text, bracket_pair.end).into(),
3078 ));
3079 bracket_inserted = true;
3080 continue;
3081 }
3082 }
3083
3084 if let Some(region) = autoclose_region {
3085 // If the selection is followed by an auto-inserted closing bracket,
3086 // then don't insert that closing bracket again; just move the selection
3087 // past the closing bracket.
3088 let should_skip = selection.end == region.range.end.to_point(&snapshot)
3089 && text.as_ref() == region.pair.end.as_str();
3090 if should_skip {
3091 let anchor = snapshot.anchor_after(selection.end);
3092 new_selections
3093 .push((selection.map(|_| anchor), region.pair.end.len()));
3094 continue;
3095 }
3096 }
3097
3098 let always_treat_brackets_as_autoclosed = snapshot
3099 .settings_at(selection.start, cx)
3100 .always_treat_brackets_as_autoclosed;
3101 if always_treat_brackets_as_autoclosed
3102 && is_bracket_pair_end
3103 && snapshot.contains_str_at(selection.end, text.as_ref())
3104 {
3105 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
3106 // and the inserted text is a closing bracket and the selection is followed
3107 // by the closing bracket then move the selection past the closing bracket.
3108 let anchor = snapshot.anchor_after(selection.end);
3109 new_selections.push((selection.map(|_| anchor), text.len()));
3110 continue;
3111 }
3112 }
3113 // If an opening bracket is 1 character long and is typed while
3114 // text is selected, then surround that text with the bracket pair.
3115 else if auto_surround
3116 && bracket_pair.surround
3117 && is_bracket_pair_start
3118 && bracket_pair.start.chars().count() == 1
3119 {
3120 edits.push((selection.start..selection.start, text.clone()));
3121 edits.push((
3122 selection.end..selection.end,
3123 bracket_pair.end.as_str().into(),
3124 ));
3125 bracket_inserted = true;
3126 new_selections.push((
3127 Selection {
3128 id: selection.id,
3129 start: snapshot.anchor_after(selection.start),
3130 end: snapshot.anchor_before(selection.end),
3131 reversed: selection.reversed,
3132 goal: selection.goal,
3133 },
3134 0,
3135 ));
3136 continue;
3137 }
3138 }
3139 }
3140
3141 if self.auto_replace_emoji_shortcode
3142 && selection.is_empty()
3143 && text.as_ref().ends_with(':')
3144 {
3145 if let Some(possible_emoji_short_code) =
3146 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
3147 {
3148 if !possible_emoji_short_code.is_empty() {
3149 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
3150 let emoji_shortcode_start = Point::new(
3151 selection.start.row,
3152 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
3153 );
3154
3155 // Remove shortcode from buffer
3156 edits.push((
3157 emoji_shortcode_start..selection.start,
3158 "".to_string().into(),
3159 ));
3160 new_selections.push((
3161 Selection {
3162 id: selection.id,
3163 start: snapshot.anchor_after(emoji_shortcode_start),
3164 end: snapshot.anchor_before(selection.start),
3165 reversed: selection.reversed,
3166 goal: selection.goal,
3167 },
3168 0,
3169 ));
3170
3171 // Insert emoji
3172 let selection_start_anchor = snapshot.anchor_after(selection.start);
3173 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3174 edits.push((selection.start..selection.end, emoji.to_string().into()));
3175
3176 continue;
3177 }
3178 }
3179 }
3180 }
3181
3182 // If not handling any auto-close operation, then just replace the selected
3183 // text with the given input and move the selection to the end of the
3184 // newly inserted text.
3185 let anchor = snapshot.anchor_after(selection.end);
3186 if !self.linked_edit_ranges.is_empty() {
3187 let start_anchor = snapshot.anchor_before(selection.start);
3188
3189 let is_word_char = text.chars().next().map_or(true, |char| {
3190 let scope = snapshot.language_scope_at(start_anchor.to_offset(&snapshot));
3191 let kind = char_kind(&scope, char);
3192
3193 kind == CharKind::Word
3194 });
3195
3196 if is_word_char {
3197 if let Some(ranges) = self
3198 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3199 {
3200 for (buffer, edits) in ranges {
3201 linked_edits
3202 .entry(buffer.clone())
3203 .or_default()
3204 .extend(edits.into_iter().map(|range| (range, text.clone())));
3205 }
3206 }
3207 }
3208 }
3209
3210 new_selections.push((selection.map(|_| anchor), 0));
3211 edits.push((selection.start..selection.end, text.clone()));
3212 }
3213
3214 drop(snapshot);
3215
3216 self.transact(cx, |this, cx| {
3217 this.buffer.update(cx, |buffer, cx| {
3218 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3219 });
3220 for (buffer, edits) in linked_edits {
3221 buffer.update(cx, |buffer, cx| {
3222 let snapshot = buffer.snapshot();
3223 let edits = edits
3224 .into_iter()
3225 .map(|(range, text)| {
3226 use text::ToPoint as TP;
3227 let end_point = TP::to_point(&range.end, &snapshot);
3228 let start_point = TP::to_point(&range.start, &snapshot);
3229 (start_point..end_point, text)
3230 })
3231 .sorted_by_key(|(range, _)| range.start)
3232 .collect::<Vec<_>>();
3233 buffer.edit(edits, None, cx);
3234 })
3235 }
3236 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3237 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3238 let snapshot = this.buffer.read(cx).read(cx);
3239 let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
3240 .zip(new_selection_deltas)
3241 .map(|(selection, delta)| Selection {
3242 id: selection.id,
3243 start: selection.start + delta,
3244 end: selection.end + delta,
3245 reversed: selection.reversed,
3246 goal: SelectionGoal::None,
3247 })
3248 .collect::<Vec<_>>();
3249
3250 let mut i = 0;
3251 for (position, delta, selection_id, pair) in new_autoclose_regions {
3252 let position = position.to_offset(&snapshot) + delta;
3253 let start = snapshot.anchor_before(position);
3254 let end = snapshot.anchor_after(position);
3255 while let Some(existing_state) = this.autoclose_regions.get(i) {
3256 match existing_state.range.start.cmp(&start, &snapshot) {
3257 Ordering::Less => i += 1,
3258 Ordering::Greater => break,
3259 Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
3260 Ordering::Less => i += 1,
3261 Ordering::Equal => break,
3262 Ordering::Greater => break,
3263 },
3264 }
3265 }
3266 this.autoclose_regions.insert(
3267 i,
3268 AutocloseRegion {
3269 selection_id,
3270 range: start..end,
3271 pair,
3272 },
3273 );
3274 }
3275
3276 drop(snapshot);
3277 let had_active_inline_completion = this.has_active_inline_completion(cx);
3278 this.change_selections_inner(Some(Autoscroll::fit()), false, cx, |s| {
3279 s.select(new_selections)
3280 });
3281
3282 if !bracket_inserted && EditorSettings::get_global(cx).use_on_type_format {
3283 if let Some(on_type_format_task) =
3284 this.trigger_on_type_formatting(text.to_string(), cx)
3285 {
3286 on_type_format_task.detach_and_log_err(cx);
3287 }
3288 }
3289
3290 let editor_settings = EditorSettings::get_global(cx);
3291 if bracket_inserted
3292 && (editor_settings.auto_signature_help
3293 || editor_settings.show_signature_help_after_edits)
3294 {
3295 this.show_signature_help(&ShowSignatureHelp, cx);
3296 }
3297
3298 let trigger_in_words = !had_active_inline_completion;
3299 this.trigger_completion_on_input(&text, trigger_in_words, cx);
3300 linked_editing_ranges::refresh_linked_ranges(this, cx);
3301 this.refresh_inline_completion(true, cx);
3302 });
3303 }
3304
3305 fn find_possible_emoji_shortcode_at_position(
3306 snapshot: &MultiBufferSnapshot,
3307 position: Point,
3308 ) -> Option<String> {
3309 let mut chars = Vec::new();
3310 let mut found_colon = false;
3311 for char in snapshot.reversed_chars_at(position).take(100) {
3312 // Found a possible emoji shortcode in the middle of the buffer
3313 if found_colon {
3314 if char.is_whitespace() {
3315 chars.reverse();
3316 return Some(chars.iter().collect());
3317 }
3318 // If the previous character is not a whitespace, we are in the middle of a word
3319 // and we only want to complete the shortcode if the word is made up of other emojis
3320 let mut containing_word = String::new();
3321 for ch in snapshot
3322 .reversed_chars_at(position)
3323 .skip(chars.len() + 1)
3324 .take(100)
3325 {
3326 if ch.is_whitespace() {
3327 break;
3328 }
3329 containing_word.push(ch);
3330 }
3331 let containing_word = containing_word.chars().rev().collect::<String>();
3332 if util::word_consists_of_emojis(containing_word.as_str()) {
3333 chars.reverse();
3334 return Some(chars.iter().collect());
3335 }
3336 }
3337
3338 if char.is_whitespace() || !char.is_ascii() {
3339 return None;
3340 }
3341 if char == ':' {
3342 found_colon = true;
3343 } else {
3344 chars.push(char);
3345 }
3346 }
3347 // Found a possible emoji shortcode at the beginning of the buffer
3348 chars.reverse();
3349 Some(chars.iter().collect())
3350 }
3351
3352 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
3353 self.transact(cx, |this, cx| {
3354 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3355 let selections = this.selections.all::<usize>(cx);
3356 let multi_buffer = this.buffer.read(cx);
3357 let buffer = multi_buffer.snapshot(cx);
3358 selections
3359 .iter()
3360 .map(|selection| {
3361 let start_point = selection.start.to_point(&buffer);
3362 let mut indent =
3363 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3364 indent.len = cmp::min(indent.len, start_point.column);
3365 let start = selection.start;
3366 let end = selection.end;
3367 let selection_is_empty = start == end;
3368 let language_scope = buffer.language_scope_at(start);
3369 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3370 &language_scope
3371 {
3372 let leading_whitespace_len = buffer
3373 .reversed_chars_at(start)
3374 .take_while(|c| c.is_whitespace() && *c != '\n')
3375 .map(|c| c.len_utf8())
3376 .sum::<usize>();
3377
3378 let trailing_whitespace_len = buffer
3379 .chars_at(end)
3380 .take_while(|c| c.is_whitespace() && *c != '\n')
3381 .map(|c| c.len_utf8())
3382 .sum::<usize>();
3383
3384 let insert_extra_newline =
3385 language.brackets().any(|(pair, enabled)| {
3386 let pair_start = pair.start.trim_end();
3387 let pair_end = pair.end.trim_start();
3388
3389 enabled
3390 && pair.newline
3391 && buffer.contains_str_at(
3392 end + trailing_whitespace_len,
3393 pair_end,
3394 )
3395 && buffer.contains_str_at(
3396 (start - leading_whitespace_len)
3397 .saturating_sub(pair_start.len()),
3398 pair_start,
3399 )
3400 });
3401
3402 // Comment extension on newline is allowed only for cursor selections
3403 let comment_delimiter = maybe!({
3404 if !selection_is_empty {
3405 return None;
3406 }
3407
3408 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
3409 return None;
3410 }
3411
3412 let delimiters = language.line_comment_prefixes();
3413 let max_len_of_delimiter =
3414 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3415 let (snapshot, range) =
3416 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3417
3418 let mut index_of_first_non_whitespace = 0;
3419 let comment_candidate = snapshot
3420 .chars_for_range(range)
3421 .skip_while(|c| {
3422 let should_skip = c.is_whitespace();
3423 if should_skip {
3424 index_of_first_non_whitespace += 1;
3425 }
3426 should_skip
3427 })
3428 .take(max_len_of_delimiter)
3429 .collect::<String>();
3430 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3431 comment_candidate.starts_with(comment_prefix.as_ref())
3432 })?;
3433 let cursor_is_placed_after_comment_marker =
3434 index_of_first_non_whitespace + comment_prefix.len()
3435 <= start_point.column as usize;
3436 if cursor_is_placed_after_comment_marker {
3437 Some(comment_prefix.clone())
3438 } else {
3439 None
3440 }
3441 });
3442 (comment_delimiter, insert_extra_newline)
3443 } else {
3444 (None, false)
3445 };
3446
3447 let capacity_for_delimiter = comment_delimiter
3448 .as_deref()
3449 .map(str::len)
3450 .unwrap_or_default();
3451 let mut new_text =
3452 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3453 new_text.push_str("\n");
3454 new_text.extend(indent.chars());
3455 if let Some(delimiter) = &comment_delimiter {
3456 new_text.push_str(&delimiter);
3457 }
3458 if insert_extra_newline {
3459 new_text = new_text.repeat(2);
3460 }
3461
3462 let anchor = buffer.anchor_after(end);
3463 let new_selection = selection.map(|_| anchor);
3464 (
3465 (start..end, new_text),
3466 (insert_extra_newline, new_selection),
3467 )
3468 })
3469 .unzip()
3470 };
3471
3472 this.edit_with_autoindent(edits, cx);
3473 let buffer = this.buffer.read(cx).snapshot(cx);
3474 let new_selections = selection_fixup_info
3475 .into_iter()
3476 .map(|(extra_newline_inserted, new_selection)| {
3477 let mut cursor = new_selection.end.to_point(&buffer);
3478 if extra_newline_inserted {
3479 cursor.row -= 1;
3480 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3481 }
3482 new_selection.map(|_| cursor)
3483 })
3484 .collect();
3485
3486 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
3487 this.refresh_inline_completion(true, cx);
3488 });
3489 }
3490
3491 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
3492 let buffer = self.buffer.read(cx);
3493 let snapshot = buffer.snapshot(cx);
3494
3495 let mut edits = Vec::new();
3496 let mut rows = Vec::new();
3497
3498 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3499 let cursor = selection.head();
3500 let row = cursor.row;
3501
3502 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3503
3504 let newline = "\n".to_string();
3505 edits.push((start_of_line..start_of_line, newline));
3506
3507 rows.push(row + rows_inserted as u32);
3508 }
3509
3510 self.transact(cx, |editor, cx| {
3511 editor.edit(edits, cx);
3512
3513 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3514 let mut index = 0;
3515 s.move_cursors_with(|map, _, _| {
3516 let row = rows[index];
3517 index += 1;
3518
3519 let point = Point::new(row, 0);
3520 let boundary = map.next_line_boundary(point).1;
3521 let clipped = map.clip_point(boundary, Bias::Left);
3522
3523 (clipped, SelectionGoal::None)
3524 });
3525 });
3526
3527 let mut indent_edits = Vec::new();
3528 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3529 for row in rows {
3530 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3531 for (row, indent) in indents {
3532 if indent.len == 0 {
3533 continue;
3534 }
3535
3536 let text = match indent.kind {
3537 IndentKind::Space => " ".repeat(indent.len as usize),
3538 IndentKind::Tab => "\t".repeat(indent.len as usize),
3539 };
3540 let point = Point::new(row.0, 0);
3541 indent_edits.push((point..point, text));
3542 }
3543 }
3544 editor.edit(indent_edits, cx);
3545 });
3546 }
3547
3548 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
3549 let buffer = self.buffer.read(cx);
3550 let snapshot = buffer.snapshot(cx);
3551
3552 let mut edits = Vec::new();
3553 let mut rows = Vec::new();
3554 let mut rows_inserted = 0;
3555
3556 for selection in self.selections.all_adjusted(cx) {
3557 let cursor = selection.head();
3558 let row = cursor.row;
3559
3560 let point = Point::new(row + 1, 0);
3561 let start_of_line = snapshot.clip_point(point, Bias::Left);
3562
3563 let newline = "\n".to_string();
3564 edits.push((start_of_line..start_of_line, newline));
3565
3566 rows_inserted += 1;
3567 rows.push(row + rows_inserted);
3568 }
3569
3570 self.transact(cx, |editor, cx| {
3571 editor.edit(edits, cx);
3572
3573 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3574 let mut index = 0;
3575 s.move_cursors_with(|map, _, _| {
3576 let row = rows[index];
3577 index += 1;
3578
3579 let point = Point::new(row, 0);
3580 let boundary = map.next_line_boundary(point).1;
3581 let clipped = map.clip_point(boundary, Bias::Left);
3582
3583 (clipped, SelectionGoal::None)
3584 });
3585 });
3586
3587 let mut indent_edits = Vec::new();
3588 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3589 for row in rows {
3590 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3591 for (row, indent) in indents {
3592 if indent.len == 0 {
3593 continue;
3594 }
3595
3596 let text = match indent.kind {
3597 IndentKind::Space => " ".repeat(indent.len as usize),
3598 IndentKind::Tab => "\t".repeat(indent.len as usize),
3599 };
3600 let point = Point::new(row.0, 0);
3601 indent_edits.push((point..point, text));
3602 }
3603 }
3604 editor.edit(indent_edits, cx);
3605 });
3606 }
3607
3608 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3609 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3610 original_indent_columns: Vec::new(),
3611 });
3612 self.insert_with_autoindent_mode(text, autoindent, cx);
3613 }
3614
3615 fn insert_with_autoindent_mode(
3616 &mut self,
3617 text: &str,
3618 autoindent_mode: Option<AutoindentMode>,
3619 cx: &mut ViewContext<Self>,
3620 ) {
3621 if self.read_only(cx) {
3622 return;
3623 }
3624
3625 let text: Arc<str> = text.into();
3626 self.transact(cx, |this, cx| {
3627 let old_selections = this.selections.all_adjusted(cx);
3628 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3629 let anchors = {
3630 let snapshot = buffer.read(cx);
3631 old_selections
3632 .iter()
3633 .map(|s| {
3634 let anchor = snapshot.anchor_after(s.head());
3635 s.map(|_| anchor)
3636 })
3637 .collect::<Vec<_>>()
3638 };
3639 buffer.edit(
3640 old_selections
3641 .iter()
3642 .map(|s| (s.start..s.end, text.clone())),
3643 autoindent_mode,
3644 cx,
3645 );
3646 anchors
3647 });
3648
3649 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3650 s.select_anchors(selection_anchors);
3651 })
3652 });
3653 }
3654
3655 fn trigger_completion_on_input(
3656 &mut self,
3657 text: &str,
3658 trigger_in_words: bool,
3659 cx: &mut ViewContext<Self>,
3660 ) {
3661 if self.is_completion_trigger(text, trigger_in_words, cx) {
3662 self.show_completions(
3663 &ShowCompletions {
3664 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3665 },
3666 cx,
3667 );
3668 } else {
3669 self.hide_context_menu(cx);
3670 }
3671 }
3672
3673 fn is_completion_trigger(
3674 &self,
3675 text: &str,
3676 trigger_in_words: bool,
3677 cx: &mut ViewContext<Self>,
3678 ) -> bool {
3679 let position = self.selections.newest_anchor().head();
3680 let multibuffer = self.buffer.read(cx);
3681 let Some(buffer) = position
3682 .buffer_id
3683 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3684 else {
3685 return false;
3686 };
3687
3688 if let Some(completion_provider) = &self.completion_provider {
3689 completion_provider.is_completion_trigger(
3690 &buffer,
3691 position.text_anchor,
3692 text,
3693 trigger_in_words,
3694 cx,
3695 )
3696 } else {
3697 false
3698 }
3699 }
3700
3701 /// If any empty selections is touching the start of its innermost containing autoclose
3702 /// region, expand it to select the brackets.
3703 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
3704 let selections = self.selections.all::<usize>(cx);
3705 let buffer = self.buffer.read(cx).read(cx);
3706 let new_selections = self
3707 .selections_with_autoclose_regions(selections, &buffer)
3708 .map(|(mut selection, region)| {
3709 if !selection.is_empty() {
3710 return selection;
3711 }
3712
3713 if let Some(region) = region {
3714 let mut range = region.range.to_offset(&buffer);
3715 if selection.start == range.start && range.start >= region.pair.start.len() {
3716 range.start -= region.pair.start.len();
3717 if buffer.contains_str_at(range.start, ®ion.pair.start)
3718 && buffer.contains_str_at(range.end, ®ion.pair.end)
3719 {
3720 range.end += region.pair.end.len();
3721 selection.start = range.start;
3722 selection.end = range.end;
3723
3724 return selection;
3725 }
3726 }
3727 }
3728
3729 let always_treat_brackets_as_autoclosed = buffer
3730 .settings_at(selection.start, cx)
3731 .always_treat_brackets_as_autoclosed;
3732
3733 if !always_treat_brackets_as_autoclosed {
3734 return selection;
3735 }
3736
3737 if let Some(scope) = buffer.language_scope_at(selection.start) {
3738 for (pair, enabled) in scope.brackets() {
3739 if !enabled || !pair.close {
3740 continue;
3741 }
3742
3743 if buffer.contains_str_at(selection.start, &pair.end) {
3744 let pair_start_len = pair.start.len();
3745 if buffer.contains_str_at(selection.start - pair_start_len, &pair.start)
3746 {
3747 selection.start -= pair_start_len;
3748 selection.end += pair.end.len();
3749
3750 return selection;
3751 }
3752 }
3753 }
3754 }
3755
3756 selection
3757 })
3758 .collect();
3759
3760 drop(buffer);
3761 self.change_selections(None, cx, |selections| selections.select(new_selections));
3762 }
3763
3764 /// Iterate the given selections, and for each one, find the smallest surrounding
3765 /// autoclose region. This uses the ordering of the selections and the autoclose
3766 /// regions to avoid repeated comparisons.
3767 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3768 &'a self,
3769 selections: impl IntoIterator<Item = Selection<D>>,
3770 buffer: &'a MultiBufferSnapshot,
3771 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3772 let mut i = 0;
3773 let mut regions = self.autoclose_regions.as_slice();
3774 selections.into_iter().map(move |selection| {
3775 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3776
3777 let mut enclosing = None;
3778 while let Some(pair_state) = regions.get(i) {
3779 if pair_state.range.end.to_offset(buffer) < range.start {
3780 regions = ®ions[i + 1..];
3781 i = 0;
3782 } else if pair_state.range.start.to_offset(buffer) > range.end {
3783 break;
3784 } else {
3785 if pair_state.selection_id == selection.id {
3786 enclosing = Some(pair_state);
3787 }
3788 i += 1;
3789 }
3790 }
3791
3792 (selection.clone(), enclosing)
3793 })
3794 }
3795
3796 /// Remove any autoclose regions that no longer contain their selection.
3797 fn invalidate_autoclose_regions(
3798 &mut self,
3799 mut selections: &[Selection<Anchor>],
3800 buffer: &MultiBufferSnapshot,
3801 ) {
3802 self.autoclose_regions.retain(|state| {
3803 let mut i = 0;
3804 while let Some(selection) = selections.get(i) {
3805 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3806 selections = &selections[1..];
3807 continue;
3808 }
3809 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3810 break;
3811 }
3812 if selection.id == state.selection_id {
3813 return true;
3814 } else {
3815 i += 1;
3816 }
3817 }
3818 false
3819 });
3820 }
3821
3822 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3823 let offset = position.to_offset(buffer);
3824 let (word_range, kind) = buffer.surrounding_word(offset);
3825 if offset > word_range.start && kind == Some(CharKind::Word) {
3826 Some(
3827 buffer
3828 .text_for_range(word_range.start..offset)
3829 .collect::<String>(),
3830 )
3831 } else {
3832 None
3833 }
3834 }
3835
3836 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
3837 self.refresh_inlay_hints(
3838 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
3839 cx,
3840 );
3841 }
3842
3843 pub fn inlay_hints_enabled(&self) -> bool {
3844 self.inlay_hint_cache.enabled
3845 }
3846
3847 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
3848 if self.project.is_none() || self.mode != EditorMode::Full {
3849 return;
3850 }
3851
3852 let reason_description = reason.description();
3853 let ignore_debounce = matches!(
3854 reason,
3855 InlayHintRefreshReason::SettingsChange(_)
3856 | InlayHintRefreshReason::Toggle(_)
3857 | InlayHintRefreshReason::ExcerptsRemoved(_)
3858 );
3859 let (invalidate_cache, required_languages) = match reason {
3860 InlayHintRefreshReason::Toggle(enabled) => {
3861 self.inlay_hint_cache.enabled = enabled;
3862 if enabled {
3863 (InvalidationStrategy::RefreshRequested, None)
3864 } else {
3865 self.inlay_hint_cache.clear();
3866 self.splice_inlays(
3867 self.visible_inlay_hints(cx)
3868 .iter()
3869 .map(|inlay| inlay.id)
3870 .collect(),
3871 Vec::new(),
3872 cx,
3873 );
3874 return;
3875 }
3876 }
3877 InlayHintRefreshReason::SettingsChange(new_settings) => {
3878 match self.inlay_hint_cache.update_settings(
3879 &self.buffer,
3880 new_settings,
3881 self.visible_inlay_hints(cx),
3882 cx,
3883 ) {
3884 ControlFlow::Break(Some(InlaySplice {
3885 to_remove,
3886 to_insert,
3887 })) => {
3888 self.splice_inlays(to_remove, to_insert, cx);
3889 return;
3890 }
3891 ControlFlow::Break(None) => return,
3892 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3893 }
3894 }
3895 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3896 if let Some(InlaySplice {
3897 to_remove,
3898 to_insert,
3899 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3900 {
3901 self.splice_inlays(to_remove, to_insert, cx);
3902 }
3903 return;
3904 }
3905 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3906 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3907 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3908 }
3909 InlayHintRefreshReason::RefreshRequested => {
3910 (InvalidationStrategy::RefreshRequested, None)
3911 }
3912 };
3913
3914 if let Some(InlaySplice {
3915 to_remove,
3916 to_insert,
3917 }) = self.inlay_hint_cache.spawn_hint_refresh(
3918 reason_description,
3919 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3920 invalidate_cache,
3921 ignore_debounce,
3922 cx,
3923 ) {
3924 self.splice_inlays(to_remove, to_insert, cx);
3925 }
3926 }
3927
3928 fn visible_inlay_hints(&self, cx: &ViewContext<'_, Editor>) -> Vec<Inlay> {
3929 self.display_map
3930 .read(cx)
3931 .current_inlays()
3932 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3933 .cloned()
3934 .collect()
3935 }
3936
3937 pub fn excerpts_for_inlay_hints_query(
3938 &self,
3939 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3940 cx: &mut ViewContext<Editor>,
3941 ) -> HashMap<ExcerptId, (Model<Buffer>, clock::Global, Range<usize>)> {
3942 let Some(project) = self.project.as_ref() else {
3943 return HashMap::default();
3944 };
3945 let project = project.read(cx);
3946 let multi_buffer = self.buffer().read(cx);
3947 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3948 let multi_buffer_visible_start = self
3949 .scroll_manager
3950 .anchor()
3951 .anchor
3952 .to_point(&multi_buffer_snapshot);
3953 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3954 multi_buffer_visible_start
3955 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3956 Bias::Left,
3957 );
3958 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3959 multi_buffer
3960 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
3961 .into_iter()
3962 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3963 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
3964 let buffer = buffer_handle.read(cx);
3965 let buffer_file = project::File::from_dyn(buffer.file())?;
3966 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3967 let worktree_entry = buffer_worktree
3968 .read(cx)
3969 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3970 if worktree_entry.is_ignored {
3971 return None;
3972 }
3973
3974 let language = buffer.language()?;
3975 if let Some(restrict_to_languages) = restrict_to_languages {
3976 if !restrict_to_languages.contains(language) {
3977 return None;
3978 }
3979 }
3980 Some((
3981 excerpt_id,
3982 (
3983 buffer_handle,
3984 buffer.version().clone(),
3985 excerpt_visible_range,
3986 ),
3987 ))
3988 })
3989 .collect()
3990 }
3991
3992 pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
3993 TextLayoutDetails {
3994 text_system: cx.text_system().clone(),
3995 editor_style: self.style.clone().unwrap(),
3996 rem_size: cx.rem_size(),
3997 scroll_anchor: self.scroll_manager.anchor(),
3998 visible_rows: self.visible_line_count(),
3999 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
4000 }
4001 }
4002
4003 fn splice_inlays(
4004 &self,
4005 to_remove: Vec<InlayId>,
4006 to_insert: Vec<Inlay>,
4007 cx: &mut ViewContext<Self>,
4008 ) {
4009 self.display_map.update(cx, |display_map, cx| {
4010 display_map.splice_inlays(to_remove, to_insert, cx);
4011 });
4012 cx.notify();
4013 }
4014
4015 fn trigger_on_type_formatting(
4016 &self,
4017 input: String,
4018 cx: &mut ViewContext<Self>,
4019 ) -> Option<Task<Result<()>>> {
4020 if input.len() != 1 {
4021 return None;
4022 }
4023
4024 let project = self.project.as_ref()?;
4025 let position = self.selections.newest_anchor().head();
4026 let (buffer, buffer_position) = self
4027 .buffer
4028 .read(cx)
4029 .text_anchor_for_position(position, cx)?;
4030
4031 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
4032 // hence we do LSP request & edit on host side only — add formats to host's history.
4033 let push_to_lsp_host_history = true;
4034 // If this is not the host, append its history with new edits.
4035 let push_to_client_history = project.read(cx).is_remote();
4036
4037 let on_type_formatting = project.update(cx, |project, cx| {
4038 project.on_type_format(
4039 buffer.clone(),
4040 buffer_position,
4041 input,
4042 push_to_lsp_host_history,
4043 cx,
4044 )
4045 });
4046 Some(cx.spawn(|editor, mut cx| async move {
4047 if let Some(transaction) = on_type_formatting.await? {
4048 if push_to_client_history {
4049 buffer
4050 .update(&mut cx, |buffer, _| {
4051 buffer.push_transaction(transaction, Instant::now());
4052 })
4053 .ok();
4054 }
4055 editor.update(&mut cx, |editor, cx| {
4056 editor.refresh_document_highlights(cx);
4057 })?;
4058 }
4059 Ok(())
4060 }))
4061 }
4062
4063 pub fn show_completions(&mut self, options: &ShowCompletions, cx: &mut ViewContext<Self>) {
4064 if self.pending_rename.is_some() {
4065 return;
4066 }
4067
4068 let Some(provider) = self.completion_provider.as_ref() else {
4069 return;
4070 };
4071
4072 let position = self.selections.newest_anchor().head();
4073 let (buffer, buffer_position) =
4074 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
4075 output
4076 } else {
4077 return;
4078 };
4079
4080 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
4081 let is_followup_invoke = {
4082 let context_menu_state = self.context_menu.read();
4083 matches!(
4084 context_menu_state.deref(),
4085 Some(ContextMenu::Completions(_))
4086 )
4087 };
4088 let trigger_kind = match (&options.trigger, is_followup_invoke) {
4089 (_, true) => CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS,
4090 (Some(trigger), _) if buffer.read(cx).completion_triggers().contains(&trigger) => {
4091 CompletionTriggerKind::TRIGGER_CHARACTER
4092 }
4093
4094 _ => CompletionTriggerKind::INVOKED,
4095 };
4096 let completion_context = CompletionContext {
4097 trigger_character: options.trigger.as_ref().and_then(|trigger| {
4098 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
4099 Some(String::from(trigger))
4100 } else {
4101 None
4102 }
4103 }),
4104 trigger_kind,
4105 };
4106 let completions = provider.completions(&buffer, buffer_position, completion_context, cx);
4107
4108 let id = post_inc(&mut self.next_completion_id);
4109 let task = cx.spawn(|this, mut cx| {
4110 async move {
4111 this.update(&mut cx, |this, _| {
4112 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
4113 })?;
4114 let completions = completions.await.log_err();
4115 let menu = if let Some(completions) = completions {
4116 let mut menu = CompletionsMenu {
4117 id,
4118 initial_position: position,
4119 match_candidates: completions
4120 .iter()
4121 .enumerate()
4122 .map(|(id, completion)| {
4123 StringMatchCandidate::new(
4124 id,
4125 completion.label.text[completion.label.filter_range.clone()]
4126 .into(),
4127 )
4128 })
4129 .collect(),
4130 buffer: buffer.clone(),
4131 completions: Arc::new(RwLock::new(completions.into())),
4132 matches: Vec::new().into(),
4133 selected_item: 0,
4134 scroll_handle: UniformListScrollHandle::new(),
4135 selected_completion_documentation_resolve_debounce: Arc::new(Mutex::new(
4136 DebouncedDelay::new(),
4137 )),
4138 };
4139 menu.filter(query.as_deref(), cx.background_executor().clone())
4140 .await;
4141
4142 if menu.matches.is_empty() {
4143 None
4144 } else {
4145 this.update(&mut cx, |editor, cx| {
4146 let completions = menu.completions.clone();
4147 let matches = menu.matches.clone();
4148
4149 let delay_ms = EditorSettings::get_global(cx)
4150 .completion_documentation_secondary_query_debounce;
4151 let delay = Duration::from_millis(delay_ms);
4152 editor
4153 .completion_documentation_pre_resolve_debounce
4154 .fire_new(delay, cx, |editor, cx| {
4155 CompletionsMenu::pre_resolve_completion_documentation(
4156 buffer,
4157 completions,
4158 matches,
4159 editor,
4160 cx,
4161 )
4162 });
4163 })
4164 .ok();
4165 Some(menu)
4166 }
4167 } else {
4168 None
4169 };
4170
4171 this.update(&mut cx, |this, cx| {
4172 let mut context_menu = this.context_menu.write();
4173 match context_menu.as_ref() {
4174 None => {}
4175
4176 Some(ContextMenu::Completions(prev_menu)) => {
4177 if prev_menu.id > id {
4178 return;
4179 }
4180 }
4181
4182 _ => return,
4183 }
4184
4185 if this.focus_handle.is_focused(cx) && menu.is_some() {
4186 let menu = menu.unwrap();
4187 *context_menu = Some(ContextMenu::Completions(menu));
4188 drop(context_menu);
4189 this.discard_inline_completion(false, cx);
4190 cx.notify();
4191 } else if this.completion_tasks.len() <= 1 {
4192 // If there are no more completion tasks and the last menu was
4193 // empty, we should hide it. If it was already hidden, we should
4194 // also show the copilot completion when available.
4195 drop(context_menu);
4196 if this.hide_context_menu(cx).is_none() {
4197 this.update_visible_inline_completion(cx);
4198 }
4199 }
4200 })?;
4201
4202 Ok::<_, anyhow::Error>(())
4203 }
4204 .log_err()
4205 });
4206
4207 self.completion_tasks.push((id, task));
4208 }
4209
4210 pub fn confirm_completion(
4211 &mut self,
4212 action: &ConfirmCompletion,
4213 cx: &mut ViewContext<Self>,
4214 ) -> Option<Task<Result<()>>> {
4215 use language::ToOffset as _;
4216
4217 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
4218 menu
4219 } else {
4220 return None;
4221 };
4222
4223 let mat = completions_menu
4224 .matches
4225 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
4226 let buffer_handle = completions_menu.buffer;
4227 let completions = completions_menu.completions.read();
4228 let completion = completions.get(mat.candidate_id)?;
4229 cx.stop_propagation();
4230
4231 let snippet;
4232 let text;
4233
4234 if completion.is_snippet() {
4235 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4236 text = snippet.as_ref().unwrap().text.clone();
4237 } else {
4238 snippet = None;
4239 text = completion.new_text.clone();
4240 };
4241 let selections = self.selections.all::<usize>(cx);
4242 let buffer = buffer_handle.read(cx);
4243 let old_range = completion.old_range.to_offset(buffer);
4244 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4245
4246 let newest_selection = self.selections.newest_anchor();
4247 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4248 return None;
4249 }
4250
4251 let lookbehind = newest_selection
4252 .start
4253 .text_anchor
4254 .to_offset(buffer)
4255 .saturating_sub(old_range.start);
4256 let lookahead = old_range
4257 .end
4258 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4259 let mut common_prefix_len = old_text
4260 .bytes()
4261 .zip(text.bytes())
4262 .take_while(|(a, b)| a == b)
4263 .count();
4264
4265 let snapshot = self.buffer.read(cx).snapshot(cx);
4266 let mut range_to_replace: Option<Range<isize>> = None;
4267 let mut ranges = Vec::new();
4268 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4269 for selection in &selections {
4270 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4271 let start = selection.start.saturating_sub(lookbehind);
4272 let end = selection.end + lookahead;
4273 if selection.id == newest_selection.id {
4274 range_to_replace = Some(
4275 ((start + common_prefix_len) as isize - selection.start as isize)
4276 ..(end as isize - selection.start as isize),
4277 );
4278 }
4279 ranges.push(start + common_prefix_len..end);
4280 } else {
4281 common_prefix_len = 0;
4282 ranges.clear();
4283 ranges.extend(selections.iter().map(|s| {
4284 if s.id == newest_selection.id {
4285 range_to_replace = Some(
4286 old_range.start.to_offset_utf16(&snapshot).0 as isize
4287 - selection.start as isize
4288 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4289 - selection.start as isize,
4290 );
4291 old_range.clone()
4292 } else {
4293 s.start..s.end
4294 }
4295 }));
4296 break;
4297 }
4298 if !self.linked_edit_ranges.is_empty() {
4299 let start_anchor = snapshot.anchor_before(selection.head());
4300 let end_anchor = snapshot.anchor_after(selection.tail());
4301 if let Some(ranges) = self
4302 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4303 {
4304 for (buffer, edits) in ranges {
4305 linked_edits.entry(buffer.clone()).or_default().extend(
4306 edits
4307 .into_iter()
4308 .map(|range| (range, text[common_prefix_len..].to_owned())),
4309 );
4310 }
4311 }
4312 }
4313 }
4314 let text = &text[common_prefix_len..];
4315
4316 cx.emit(EditorEvent::InputHandled {
4317 utf16_range_to_replace: range_to_replace,
4318 text: text.into(),
4319 });
4320
4321 self.transact(cx, |this, cx| {
4322 if let Some(mut snippet) = snippet {
4323 snippet.text = text.to_string();
4324 for tabstop in snippet.tabstops.iter_mut().flatten() {
4325 tabstop.start -= common_prefix_len as isize;
4326 tabstop.end -= common_prefix_len as isize;
4327 }
4328
4329 this.insert_snippet(&ranges, snippet, cx).log_err();
4330 } else {
4331 this.buffer.update(cx, |buffer, cx| {
4332 buffer.edit(
4333 ranges.iter().map(|range| (range.clone(), text)),
4334 this.autoindent_mode.clone(),
4335 cx,
4336 );
4337 });
4338 }
4339 for (buffer, edits) in linked_edits {
4340 buffer.update(cx, |buffer, cx| {
4341 let snapshot = buffer.snapshot();
4342 let edits = edits
4343 .into_iter()
4344 .map(|(range, text)| {
4345 use text::ToPoint as TP;
4346 let end_point = TP::to_point(&range.end, &snapshot);
4347 let start_point = TP::to_point(&range.start, &snapshot);
4348 (start_point..end_point, text)
4349 })
4350 .sorted_by_key(|(range, _)| range.start)
4351 .collect::<Vec<_>>();
4352 buffer.edit(edits, None, cx);
4353 })
4354 }
4355
4356 this.refresh_inline_completion(true, cx);
4357 });
4358
4359 if let Some(confirm) = completion.confirm.as_ref() {
4360 (confirm)(cx);
4361 }
4362
4363 if completion.show_new_completions_on_confirm {
4364 self.show_completions(&ShowCompletions { trigger: None }, cx);
4365 }
4366
4367 let provider = self.completion_provider.as_ref()?;
4368 let apply_edits = provider.apply_additional_edits_for_completion(
4369 buffer_handle,
4370 completion.clone(),
4371 true,
4372 cx,
4373 );
4374
4375 let editor_settings = EditorSettings::get_global(cx);
4376 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4377 // After the code completion is finished, users often want to know what signatures are needed.
4378 // so we should automatically call signature_help
4379 self.show_signature_help(&ShowSignatureHelp, cx);
4380 }
4381
4382 Some(cx.foreground_executor().spawn(async move {
4383 apply_edits.await?;
4384 Ok(())
4385 }))
4386 }
4387
4388 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
4389 let mut context_menu = self.context_menu.write();
4390 if let Some(ContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4391 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4392 // Toggle if we're selecting the same one
4393 *context_menu = None;
4394 cx.notify();
4395 return;
4396 } else {
4397 // Otherwise, clear it and start a new one
4398 *context_menu = None;
4399 cx.notify();
4400 }
4401 }
4402 drop(context_menu);
4403 let snapshot = self.snapshot(cx);
4404 let deployed_from_indicator = action.deployed_from_indicator;
4405 let mut task = self.code_actions_task.take();
4406 let action = action.clone();
4407 cx.spawn(|editor, mut cx| async move {
4408 while let Some(prev_task) = task {
4409 prev_task.await;
4410 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4411 }
4412
4413 let spawned_test_task = editor.update(&mut cx, |editor, cx| {
4414 if editor.focus_handle.is_focused(cx) {
4415 let multibuffer_point = action
4416 .deployed_from_indicator
4417 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4418 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4419 let (buffer, buffer_row) = snapshot
4420 .buffer_snapshot
4421 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4422 .and_then(|(buffer_snapshot, range)| {
4423 editor
4424 .buffer
4425 .read(cx)
4426 .buffer(buffer_snapshot.remote_id())
4427 .map(|buffer| (buffer, range.start.row))
4428 })?;
4429 let (_, code_actions) = editor
4430 .available_code_actions
4431 .clone()
4432 .and_then(|(location, code_actions)| {
4433 let snapshot = location.buffer.read(cx).snapshot();
4434 let point_range = location.range.to_point(&snapshot);
4435 let point_range = point_range.start.row..=point_range.end.row;
4436 if point_range.contains(&buffer_row) {
4437 Some((location, code_actions))
4438 } else {
4439 None
4440 }
4441 })
4442 .unzip();
4443 let buffer_id = buffer.read(cx).remote_id();
4444 let tasks = editor
4445 .tasks
4446 .get(&(buffer_id, buffer_row))
4447 .map(|t| Arc::new(t.to_owned()));
4448 if tasks.is_none() && code_actions.is_none() {
4449 return None;
4450 }
4451
4452 editor.completion_tasks.clear();
4453 editor.discard_inline_completion(false, cx);
4454 let task_context =
4455 tasks
4456 .as_ref()
4457 .zip(editor.project.clone())
4458 .map(|(tasks, project)| {
4459 let position = Point::new(buffer_row, tasks.column);
4460 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
4461 let location = Location {
4462 buffer: buffer.clone(),
4463 range: range_start..range_start,
4464 };
4465 // Fill in the environmental variables from the tree-sitter captures
4466 let mut captured_task_variables = TaskVariables::default();
4467 for (capture_name, value) in tasks.extra_variables.clone() {
4468 captured_task_variables.insert(
4469 task::VariableName::Custom(capture_name.into()),
4470 value.clone(),
4471 );
4472 }
4473 project.update(cx, |project, cx| {
4474 project.task_context_for_location(
4475 captured_task_variables,
4476 location,
4477 cx,
4478 )
4479 })
4480 });
4481
4482 Some(cx.spawn(|editor, mut cx| async move {
4483 let task_context = match task_context {
4484 Some(task_context) => task_context.await,
4485 None => None,
4486 };
4487 let resolved_tasks =
4488 tasks.zip(task_context).map(|(tasks, task_context)| {
4489 Arc::new(ResolvedTasks {
4490 templates: tasks
4491 .templates
4492 .iter()
4493 .filter_map(|(kind, template)| {
4494 template
4495 .resolve_task(&kind.to_id_base(), &task_context)
4496 .map(|task| (kind.clone(), task))
4497 })
4498 .collect(),
4499 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4500 multibuffer_point.row,
4501 tasks.column,
4502 )),
4503 })
4504 });
4505 let spawn_straight_away = resolved_tasks
4506 .as_ref()
4507 .map_or(false, |tasks| tasks.templates.len() == 1)
4508 && code_actions
4509 .as_ref()
4510 .map_or(true, |actions| actions.is_empty());
4511 if let Some(task) = editor
4512 .update(&mut cx, |editor, cx| {
4513 *editor.context_menu.write() =
4514 Some(ContextMenu::CodeActions(CodeActionsMenu {
4515 buffer,
4516 actions: CodeActionContents {
4517 tasks: resolved_tasks,
4518 actions: code_actions,
4519 },
4520 selected_item: Default::default(),
4521 scroll_handle: UniformListScrollHandle::default(),
4522 deployed_from_indicator,
4523 }));
4524 if spawn_straight_away {
4525 if let Some(task) = editor.confirm_code_action(
4526 &ConfirmCodeAction { item_ix: Some(0) },
4527 cx,
4528 ) {
4529 cx.notify();
4530 return task;
4531 }
4532 }
4533 cx.notify();
4534 Task::ready(Ok(()))
4535 })
4536 .ok()
4537 {
4538 task.await
4539 } else {
4540 Ok(())
4541 }
4542 }))
4543 } else {
4544 Some(Task::ready(Ok(())))
4545 }
4546 })?;
4547 if let Some(task) = spawned_test_task {
4548 task.await?;
4549 }
4550
4551 Ok::<_, anyhow::Error>(())
4552 })
4553 .detach_and_log_err(cx);
4554 }
4555
4556 pub fn confirm_code_action(
4557 &mut self,
4558 action: &ConfirmCodeAction,
4559 cx: &mut ViewContext<Self>,
4560 ) -> Option<Task<Result<()>>> {
4561 let actions_menu = if let ContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
4562 menu
4563 } else {
4564 return None;
4565 };
4566 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4567 let action = actions_menu.actions.get(action_ix)?;
4568 let title = action.label();
4569 let buffer = actions_menu.buffer;
4570 let workspace = self.workspace()?;
4571
4572 match action {
4573 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4574 workspace.update(cx, |workspace, cx| {
4575 workspace::tasks::schedule_resolved_task(
4576 workspace,
4577 task_source_kind,
4578 resolved_task,
4579 false,
4580 cx,
4581 );
4582
4583 Some(Task::ready(Ok(())))
4584 })
4585 }
4586 CodeActionsItem::CodeAction(action) => {
4587 let apply_code_actions = workspace
4588 .read(cx)
4589 .project()
4590 .clone()
4591 .update(cx, |project, cx| {
4592 project.apply_code_action(buffer, action, true, cx)
4593 });
4594 let workspace = workspace.downgrade();
4595 Some(cx.spawn(|editor, cx| async move {
4596 let project_transaction = apply_code_actions.await?;
4597 Self::open_project_transaction(
4598 &editor,
4599 workspace,
4600 project_transaction,
4601 title,
4602 cx,
4603 )
4604 .await
4605 }))
4606 }
4607 }
4608 }
4609
4610 pub async fn open_project_transaction(
4611 this: &WeakView<Editor>,
4612 workspace: WeakView<Workspace>,
4613 transaction: ProjectTransaction,
4614 title: String,
4615 mut cx: AsyncWindowContext,
4616 ) -> Result<()> {
4617 let replica_id = this.update(&mut cx, |this, cx| this.replica_id(cx))?;
4618
4619 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4620 cx.update(|cx| {
4621 entries.sort_unstable_by_key(|(buffer, _)| {
4622 buffer.read(cx).file().map(|f| f.path().clone())
4623 });
4624 })?;
4625
4626 // If the project transaction's edits are all contained within this editor, then
4627 // avoid opening a new editor to display them.
4628
4629 if let Some((buffer, transaction)) = entries.first() {
4630 if entries.len() == 1 {
4631 let excerpt = this.update(&mut cx, |editor, cx| {
4632 editor
4633 .buffer()
4634 .read(cx)
4635 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4636 })?;
4637 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4638 if excerpted_buffer == *buffer {
4639 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4640 let excerpt_range = excerpt_range.to_offset(buffer);
4641 buffer
4642 .edited_ranges_for_transaction::<usize>(transaction)
4643 .all(|range| {
4644 excerpt_range.start <= range.start
4645 && excerpt_range.end >= range.end
4646 })
4647 })?;
4648
4649 if all_edits_within_excerpt {
4650 return Ok(());
4651 }
4652 }
4653 }
4654 }
4655 } else {
4656 return Ok(());
4657 }
4658
4659 let mut ranges_to_highlight = Vec::new();
4660 let excerpt_buffer = cx.new_model(|cx| {
4661 let mut multibuffer =
4662 MultiBuffer::new(replica_id, Capability::ReadWrite).with_title(title);
4663 for (buffer_handle, transaction) in &entries {
4664 let buffer = buffer_handle.read(cx);
4665 ranges_to_highlight.extend(
4666 multibuffer.push_excerpts_with_context_lines(
4667 buffer_handle.clone(),
4668 buffer
4669 .edited_ranges_for_transaction::<usize>(transaction)
4670 .collect(),
4671 DEFAULT_MULTIBUFFER_CONTEXT,
4672 cx,
4673 ),
4674 );
4675 }
4676 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4677 multibuffer
4678 })?;
4679
4680 workspace.update(&mut cx, |workspace, cx| {
4681 let project = workspace.project().clone();
4682 let editor =
4683 cx.new_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, cx));
4684 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
4685 editor.update(cx, |editor, cx| {
4686 editor.highlight_background::<Self>(
4687 &ranges_to_highlight,
4688 |theme| theme.editor_highlighted_line_background,
4689 cx,
4690 );
4691 });
4692 })?;
4693
4694 Ok(())
4695 }
4696
4697 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4698 let project = self.project.clone()?;
4699 let buffer = self.buffer.read(cx);
4700 let newest_selection = self.selections.newest_anchor().clone();
4701 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4702 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4703 if start_buffer != end_buffer {
4704 return None;
4705 }
4706
4707 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
4708 cx.background_executor()
4709 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4710 .await;
4711
4712 let actions = if let Ok(code_actions) = project.update(&mut cx, |project, cx| {
4713 project.code_actions(&start_buffer, start..end, cx)
4714 }) {
4715 code_actions.await
4716 } else {
4717 Vec::new()
4718 };
4719
4720 this.update(&mut cx, |this, cx| {
4721 this.available_code_actions = if actions.is_empty() {
4722 None
4723 } else {
4724 Some((
4725 Location {
4726 buffer: start_buffer,
4727 range: start..end,
4728 },
4729 actions.into(),
4730 ))
4731 };
4732 cx.notify();
4733 })
4734 .log_err();
4735 }));
4736 None
4737 }
4738
4739 fn start_inline_blame_timer(&mut self, cx: &mut ViewContext<Self>) {
4740 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4741 self.show_git_blame_inline = false;
4742
4743 self.show_git_blame_inline_delay_task = Some(cx.spawn(|this, mut cx| async move {
4744 cx.background_executor().timer(delay).await;
4745
4746 this.update(&mut cx, |this, cx| {
4747 this.show_git_blame_inline = true;
4748 cx.notify();
4749 })
4750 .log_err();
4751 }));
4752 }
4753 }
4754
4755 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4756 if self.pending_rename.is_some() {
4757 return None;
4758 }
4759
4760 let project = self.project.clone()?;
4761 let buffer = self.buffer.read(cx);
4762 let newest_selection = self.selections.newest_anchor().clone();
4763 let cursor_position = newest_selection.head();
4764 let (cursor_buffer, cursor_buffer_position) =
4765 buffer.text_anchor_for_position(cursor_position, cx)?;
4766 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4767 if cursor_buffer != tail_buffer {
4768 return None;
4769 }
4770
4771 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4772 cx.background_executor()
4773 .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT)
4774 .await;
4775
4776 let highlights = if let Some(highlights) = project
4777 .update(&mut cx, |project, cx| {
4778 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4779 })
4780 .log_err()
4781 {
4782 highlights.await.log_err()
4783 } else {
4784 None
4785 };
4786
4787 if let Some(highlights) = highlights {
4788 this.update(&mut cx, |this, cx| {
4789 if this.pending_rename.is_some() {
4790 return;
4791 }
4792
4793 let buffer_id = cursor_position.buffer_id;
4794 let buffer = this.buffer.read(cx);
4795 if !buffer
4796 .text_anchor_for_position(cursor_position, cx)
4797 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4798 {
4799 return;
4800 }
4801
4802 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4803 let mut write_ranges = Vec::new();
4804 let mut read_ranges = Vec::new();
4805 for highlight in highlights {
4806 for (excerpt_id, excerpt_range) in
4807 buffer.excerpts_for_buffer(&cursor_buffer, cx)
4808 {
4809 let start = highlight
4810 .range
4811 .start
4812 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4813 let end = highlight
4814 .range
4815 .end
4816 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4817 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4818 continue;
4819 }
4820
4821 let range = Anchor {
4822 buffer_id,
4823 excerpt_id: excerpt_id,
4824 text_anchor: start,
4825 }..Anchor {
4826 buffer_id,
4827 excerpt_id,
4828 text_anchor: end,
4829 };
4830 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4831 write_ranges.push(range);
4832 } else {
4833 read_ranges.push(range);
4834 }
4835 }
4836 }
4837
4838 this.highlight_background::<DocumentHighlightRead>(
4839 &read_ranges,
4840 |theme| theme.editor_document_highlight_read_background,
4841 cx,
4842 );
4843 this.highlight_background::<DocumentHighlightWrite>(
4844 &write_ranges,
4845 |theme| theme.editor_document_highlight_write_background,
4846 cx,
4847 );
4848 cx.notify();
4849 })
4850 .log_err();
4851 }
4852 }));
4853 None
4854 }
4855
4856 fn refresh_inline_completion(
4857 &mut self,
4858 debounce: bool,
4859 cx: &mut ViewContext<Self>,
4860 ) -> Option<()> {
4861 let provider = self.inline_completion_provider()?;
4862 let cursor = self.selections.newest_anchor().head();
4863 let (buffer, cursor_buffer_position) =
4864 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4865 if !self.show_inline_completions
4866 || !provider.is_enabled(&buffer, cursor_buffer_position, cx)
4867 {
4868 self.discard_inline_completion(false, cx);
4869 return None;
4870 }
4871
4872 self.update_visible_inline_completion(cx);
4873 provider.refresh(buffer, cursor_buffer_position, debounce, cx);
4874 Some(())
4875 }
4876
4877 fn cycle_inline_completion(
4878 &mut self,
4879 direction: Direction,
4880 cx: &mut ViewContext<Self>,
4881 ) -> Option<()> {
4882 let provider = self.inline_completion_provider()?;
4883 let cursor = self.selections.newest_anchor().head();
4884 let (buffer, cursor_buffer_position) =
4885 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4886 if !self.show_inline_completions
4887 || !provider.is_enabled(&buffer, cursor_buffer_position, cx)
4888 {
4889 return None;
4890 }
4891
4892 provider.cycle(buffer, cursor_buffer_position, direction, cx);
4893 self.update_visible_inline_completion(cx);
4894
4895 Some(())
4896 }
4897
4898 pub fn show_inline_completion(&mut self, _: &ShowInlineCompletion, cx: &mut ViewContext<Self>) {
4899 if !self.has_active_inline_completion(cx) {
4900 self.refresh_inline_completion(false, cx);
4901 return;
4902 }
4903
4904 self.update_visible_inline_completion(cx);
4905 }
4906
4907 pub fn display_cursor_names(&mut self, _: &DisplayCursorNames, cx: &mut ViewContext<Self>) {
4908 self.show_cursor_names(cx);
4909 }
4910
4911 fn show_cursor_names(&mut self, cx: &mut ViewContext<Self>) {
4912 self.show_cursor_names = true;
4913 cx.notify();
4914 cx.spawn(|this, mut cx| async move {
4915 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
4916 this.update(&mut cx, |this, cx| {
4917 this.show_cursor_names = false;
4918 cx.notify()
4919 })
4920 .ok()
4921 })
4922 .detach();
4923 }
4924
4925 pub fn next_inline_completion(&mut self, _: &NextInlineCompletion, cx: &mut ViewContext<Self>) {
4926 if self.has_active_inline_completion(cx) {
4927 self.cycle_inline_completion(Direction::Next, cx);
4928 } else {
4929 let is_copilot_disabled = self.refresh_inline_completion(false, cx).is_none();
4930 if is_copilot_disabled {
4931 cx.propagate();
4932 }
4933 }
4934 }
4935
4936 pub fn previous_inline_completion(
4937 &mut self,
4938 _: &PreviousInlineCompletion,
4939 cx: &mut ViewContext<Self>,
4940 ) {
4941 if self.has_active_inline_completion(cx) {
4942 self.cycle_inline_completion(Direction::Prev, cx);
4943 } else {
4944 let is_copilot_disabled = self.refresh_inline_completion(false, cx).is_none();
4945 if is_copilot_disabled {
4946 cx.propagate();
4947 }
4948 }
4949 }
4950
4951 pub fn accept_inline_completion(
4952 &mut self,
4953 _: &AcceptInlineCompletion,
4954 cx: &mut ViewContext<Self>,
4955 ) {
4956 let Some((completion, delete_range)) = self.take_active_inline_completion(cx) else {
4957 return;
4958 };
4959 if let Some(provider) = self.inline_completion_provider() {
4960 provider.accept(cx);
4961 }
4962
4963 cx.emit(EditorEvent::InputHandled {
4964 utf16_range_to_replace: None,
4965 text: completion.text.to_string().into(),
4966 });
4967
4968 if let Some(range) = delete_range {
4969 self.change_selections(None, cx, |s| s.select_ranges([range]))
4970 }
4971 self.insert_with_autoindent_mode(&completion.text.to_string(), None, cx);
4972 self.refresh_inline_completion(true, cx);
4973 cx.notify();
4974 }
4975
4976 pub fn accept_partial_inline_completion(
4977 &mut self,
4978 _: &AcceptPartialInlineCompletion,
4979 cx: &mut ViewContext<Self>,
4980 ) {
4981 if self.selections.count() == 1 && self.has_active_inline_completion(cx) {
4982 if let Some((completion, delete_range)) = self.take_active_inline_completion(cx) {
4983 let mut partial_completion = completion
4984 .text
4985 .chars()
4986 .by_ref()
4987 .take_while(|c| c.is_alphabetic())
4988 .collect::<String>();
4989 if partial_completion.is_empty() {
4990 partial_completion = completion
4991 .text
4992 .chars()
4993 .by_ref()
4994 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
4995 .collect::<String>();
4996 }
4997
4998 cx.emit(EditorEvent::InputHandled {
4999 utf16_range_to_replace: None,
5000 text: partial_completion.clone().into(),
5001 });
5002
5003 if let Some(range) = delete_range {
5004 self.change_selections(None, cx, |s| s.select_ranges([range]))
5005 }
5006 self.insert_with_autoindent_mode(&partial_completion, None, cx);
5007
5008 self.refresh_inline_completion(true, cx);
5009 cx.notify();
5010 }
5011 }
5012 }
5013
5014 fn discard_inline_completion(
5015 &mut self,
5016 should_report_inline_completion_event: bool,
5017 cx: &mut ViewContext<Self>,
5018 ) -> bool {
5019 if let Some(provider) = self.inline_completion_provider() {
5020 provider.discard(should_report_inline_completion_event, cx);
5021 }
5022
5023 self.take_active_inline_completion(cx).is_some()
5024 }
5025
5026 pub fn has_active_inline_completion(&self, cx: &AppContext) -> bool {
5027 if let Some(completion) = self.active_inline_completion.as_ref() {
5028 let buffer = self.buffer.read(cx).read(cx);
5029 completion.0.position.is_valid(&buffer)
5030 } else {
5031 false
5032 }
5033 }
5034
5035 fn take_active_inline_completion(
5036 &mut self,
5037 cx: &mut ViewContext<Self>,
5038 ) -> Option<(Inlay, Option<Range<Anchor>>)> {
5039 let completion = self.active_inline_completion.take()?;
5040 self.display_map.update(cx, |map, cx| {
5041 map.splice_inlays(vec![completion.0.id], Default::default(), cx);
5042 });
5043 let buffer = self.buffer.read(cx).read(cx);
5044
5045 if completion.0.position.is_valid(&buffer) {
5046 Some(completion)
5047 } else {
5048 None
5049 }
5050 }
5051
5052 fn update_visible_inline_completion(&mut self, cx: &mut ViewContext<Self>) {
5053 let selection = self.selections.newest_anchor();
5054 let cursor = selection.head();
5055
5056 let excerpt_id = cursor.excerpt_id;
5057
5058 if self.context_menu.read().is_none()
5059 && self.completion_tasks.is_empty()
5060 && selection.start == selection.end
5061 {
5062 if let Some(provider) = self.inline_completion_provider() {
5063 if let Some((buffer, cursor_buffer_position)) =
5064 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5065 {
5066 if let Some((text, text_anchor_range)) =
5067 provider.active_completion_text(&buffer, cursor_buffer_position, cx)
5068 {
5069 let text = Rope::from(text);
5070 let mut to_remove = Vec::new();
5071 if let Some(completion) = self.active_inline_completion.take() {
5072 to_remove.push(completion.0.id);
5073 }
5074
5075 let completion_inlay =
5076 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
5077
5078 let multibuffer_anchor_range = text_anchor_range.and_then(|range| {
5079 let snapshot = self.buffer.read(cx).snapshot(cx);
5080 Some(
5081 snapshot.anchor_in_excerpt(excerpt_id, range.start)?
5082 ..snapshot.anchor_in_excerpt(excerpt_id, range.end)?,
5083 )
5084 });
5085 self.active_inline_completion =
5086 Some((completion_inlay.clone(), multibuffer_anchor_range));
5087
5088 self.display_map.update(cx, move |map, cx| {
5089 map.splice_inlays(to_remove, vec![completion_inlay], cx)
5090 });
5091 cx.notify();
5092 return;
5093 }
5094 }
5095 }
5096 }
5097
5098 self.discard_inline_completion(false, cx);
5099 }
5100
5101 fn inline_completion_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5102 Some(self.inline_completion_provider.as_ref()?.provider.clone())
5103 }
5104
5105 fn render_code_actions_indicator(
5106 &self,
5107 _style: &EditorStyle,
5108 row: DisplayRow,
5109 is_active: bool,
5110 cx: &mut ViewContext<Self>,
5111 ) -> Option<IconButton> {
5112 if self.available_code_actions.is_some() {
5113 Some(
5114 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5115 .shape(ui::IconButtonShape::Square)
5116 .icon_size(IconSize::XSmall)
5117 .icon_color(Color::Muted)
5118 .selected(is_active)
5119 .on_click(cx.listener(move |editor, _e, cx| {
5120 editor.focus(cx);
5121 editor.toggle_code_actions(
5122 &ToggleCodeActions {
5123 deployed_from_indicator: Some(row),
5124 },
5125 cx,
5126 );
5127 })),
5128 )
5129 } else {
5130 None
5131 }
5132 }
5133
5134 fn clear_tasks(&mut self) {
5135 self.tasks.clear()
5136 }
5137
5138 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5139 if let Some(_) = self.tasks.insert(key, value) {
5140 // This case should hopefully be rare, but just in case...
5141 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5142 }
5143 }
5144
5145 fn render_run_indicator(
5146 &self,
5147 _style: &EditorStyle,
5148 is_active: bool,
5149 row: DisplayRow,
5150 cx: &mut ViewContext<Self>,
5151 ) -> IconButton {
5152 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5153 .shape(ui::IconButtonShape::Square)
5154 .icon_size(IconSize::XSmall)
5155 .icon_color(Color::Muted)
5156 .selected(is_active)
5157 .on_click(cx.listener(move |editor, _e, cx| {
5158 editor.focus(cx);
5159 editor.toggle_code_actions(
5160 &ToggleCodeActions {
5161 deployed_from_indicator: Some(row),
5162 },
5163 cx,
5164 );
5165 }))
5166 }
5167
5168 fn render_close_hunk_diff_button(
5169 &self,
5170 hunk: HoveredHunk,
5171 row: DisplayRow,
5172 cx: &mut ViewContext<Self>,
5173 ) -> IconButton {
5174 IconButton::new(
5175 ("close_hunk_diff_indicator", row.0 as usize),
5176 ui::IconName::Close,
5177 )
5178 .shape(ui::IconButtonShape::Square)
5179 .icon_size(IconSize::XSmall)
5180 .icon_color(Color::Muted)
5181 .tooltip(|cx| Tooltip::for_action("Close hunk diff", &ToggleHunkDiff, cx))
5182 .on_click(cx.listener(move |editor, _e, cx| editor.toggle_hovered_hunk(&hunk, cx)))
5183 }
5184
5185 pub fn context_menu_visible(&self) -> bool {
5186 self.context_menu
5187 .read()
5188 .as_ref()
5189 .map_or(false, |menu| menu.visible())
5190 }
5191
5192 fn render_context_menu(
5193 &self,
5194 cursor_position: DisplayPoint,
5195 style: &EditorStyle,
5196 max_height: Pixels,
5197 cx: &mut ViewContext<Editor>,
5198 ) -> Option<(ContextMenuOrigin, AnyElement)> {
5199 self.context_menu.read().as_ref().map(|menu| {
5200 menu.render(
5201 cursor_position,
5202 style,
5203 max_height,
5204 self.workspace.as_ref().map(|(w, _)| w.clone()),
5205 cx,
5206 )
5207 })
5208 }
5209
5210 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
5211 cx.notify();
5212 self.completion_tasks.clear();
5213 let context_menu = self.context_menu.write().take();
5214 if context_menu.is_some() {
5215 self.update_visible_inline_completion(cx);
5216 }
5217 context_menu
5218 }
5219
5220 pub fn insert_snippet(
5221 &mut self,
5222 insertion_ranges: &[Range<usize>],
5223 snippet: Snippet,
5224 cx: &mut ViewContext<Self>,
5225 ) -> Result<()> {
5226 struct Tabstop<T> {
5227 is_end_tabstop: bool,
5228 ranges: Vec<Range<T>>,
5229 }
5230
5231 let tabstops = self.buffer.update(cx, |buffer, cx| {
5232 let snippet_text: Arc<str> = snippet.text.clone().into();
5233 buffer.edit(
5234 insertion_ranges
5235 .iter()
5236 .cloned()
5237 .map(|range| (range, snippet_text.clone())),
5238 Some(AutoindentMode::EachLine),
5239 cx,
5240 );
5241
5242 let snapshot = &*buffer.read(cx);
5243 let snippet = &snippet;
5244 snippet
5245 .tabstops
5246 .iter()
5247 .map(|tabstop| {
5248 let is_end_tabstop = tabstop.first().map_or(false, |tabstop| {
5249 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
5250 });
5251 let mut tabstop_ranges = tabstop
5252 .iter()
5253 .flat_map(|tabstop_range| {
5254 let mut delta = 0_isize;
5255 insertion_ranges.iter().map(move |insertion_range| {
5256 let insertion_start = insertion_range.start as isize + delta;
5257 delta +=
5258 snippet.text.len() as isize - insertion_range.len() as isize;
5259
5260 let start = ((insertion_start + tabstop_range.start) as usize)
5261 .min(snapshot.len());
5262 let end = ((insertion_start + tabstop_range.end) as usize)
5263 .min(snapshot.len());
5264 snapshot.anchor_before(start)..snapshot.anchor_after(end)
5265 })
5266 })
5267 .collect::<Vec<_>>();
5268 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
5269
5270 Tabstop {
5271 is_end_tabstop,
5272 ranges: tabstop_ranges,
5273 }
5274 })
5275 .collect::<Vec<_>>()
5276 });
5277 if let Some(tabstop) = tabstops.first() {
5278 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5279 s.select_ranges(tabstop.ranges.iter().cloned());
5280 });
5281
5282 // If we're already at the last tabstop and it's at the end of the snippet,
5283 // we're done, we don't need to keep the state around.
5284 if !tabstop.is_end_tabstop {
5285 let ranges = tabstops
5286 .into_iter()
5287 .map(|tabstop| tabstop.ranges)
5288 .collect::<Vec<_>>();
5289 self.snippet_stack.push(SnippetState {
5290 active_index: 0,
5291 ranges,
5292 });
5293 }
5294
5295 // Check whether the just-entered snippet ends with an auto-closable bracket.
5296 if self.autoclose_regions.is_empty() {
5297 let snapshot = self.buffer.read(cx).snapshot(cx);
5298 for selection in &mut self.selections.all::<Point>(cx) {
5299 let selection_head = selection.head();
5300 let Some(scope) = snapshot.language_scope_at(selection_head) else {
5301 continue;
5302 };
5303
5304 let mut bracket_pair = None;
5305 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
5306 let prev_chars = snapshot
5307 .reversed_chars_at(selection_head)
5308 .collect::<String>();
5309 for (pair, enabled) in scope.brackets() {
5310 if enabled
5311 && pair.close
5312 && prev_chars.starts_with(pair.start.as_str())
5313 && next_chars.starts_with(pair.end.as_str())
5314 {
5315 bracket_pair = Some(pair.clone());
5316 break;
5317 }
5318 }
5319 if let Some(pair) = bracket_pair {
5320 let start = snapshot.anchor_after(selection_head);
5321 let end = snapshot.anchor_after(selection_head);
5322 self.autoclose_regions.push(AutocloseRegion {
5323 selection_id: selection.id,
5324 range: start..end,
5325 pair,
5326 });
5327 }
5328 }
5329 }
5330 }
5331 Ok(())
5332 }
5333
5334 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5335 self.move_to_snippet_tabstop(Bias::Right, cx)
5336 }
5337
5338 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5339 self.move_to_snippet_tabstop(Bias::Left, cx)
5340 }
5341
5342 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
5343 if let Some(mut snippet) = self.snippet_stack.pop() {
5344 match bias {
5345 Bias::Left => {
5346 if snippet.active_index > 0 {
5347 snippet.active_index -= 1;
5348 } else {
5349 self.snippet_stack.push(snippet);
5350 return false;
5351 }
5352 }
5353 Bias::Right => {
5354 if snippet.active_index + 1 < snippet.ranges.len() {
5355 snippet.active_index += 1;
5356 } else {
5357 self.snippet_stack.push(snippet);
5358 return false;
5359 }
5360 }
5361 }
5362 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
5363 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5364 s.select_anchor_ranges(current_ranges.iter().cloned())
5365 });
5366 // If snippet state is not at the last tabstop, push it back on the stack
5367 if snippet.active_index + 1 < snippet.ranges.len() {
5368 self.snippet_stack.push(snippet);
5369 }
5370 return true;
5371 }
5372 }
5373
5374 false
5375 }
5376
5377 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
5378 self.transact(cx, |this, cx| {
5379 this.select_all(&SelectAll, cx);
5380 this.insert("", cx);
5381 });
5382 }
5383
5384 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
5385 self.transact(cx, |this, cx| {
5386 this.select_autoclose_pair(cx);
5387 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
5388 if !this.linked_edit_ranges.is_empty() {
5389 let selections = this.selections.all::<MultiBufferPoint>(cx);
5390 let snapshot = this.buffer.read(cx).snapshot(cx);
5391
5392 for selection in selections.iter() {
5393 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
5394 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
5395 if selection_start.buffer_id != selection_end.buffer_id {
5396 continue;
5397 }
5398 if let Some(ranges) =
5399 this.linked_editing_ranges_for(selection_start..selection_end, cx)
5400 {
5401 for (buffer, entries) in ranges {
5402 linked_ranges.entry(buffer).or_default().extend(entries);
5403 }
5404 }
5405 }
5406 }
5407
5408 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
5409 if !this.selections.line_mode {
5410 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
5411 for selection in &mut selections {
5412 if selection.is_empty() {
5413 let old_head = selection.head();
5414 let mut new_head =
5415 movement::left(&display_map, old_head.to_display_point(&display_map))
5416 .to_point(&display_map);
5417 if let Some((buffer, line_buffer_range)) = display_map
5418 .buffer_snapshot
5419 .buffer_line_for_row(MultiBufferRow(old_head.row))
5420 {
5421 let indent_size =
5422 buffer.indent_size_for_line(line_buffer_range.start.row);
5423 let indent_len = match indent_size.kind {
5424 IndentKind::Space => {
5425 buffer.settings_at(line_buffer_range.start, cx).tab_size
5426 }
5427 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
5428 };
5429 if old_head.column <= indent_size.len && old_head.column > 0 {
5430 let indent_len = indent_len.get();
5431 new_head = cmp::min(
5432 new_head,
5433 MultiBufferPoint::new(
5434 old_head.row,
5435 ((old_head.column - 1) / indent_len) * indent_len,
5436 ),
5437 );
5438 }
5439 }
5440
5441 selection.set_head(new_head, SelectionGoal::None);
5442 }
5443 }
5444 }
5445
5446 this.signature_help_state.set_backspace_pressed(true);
5447 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5448 this.insert("", cx);
5449 let empty_str: Arc<str> = Arc::from("");
5450 for (buffer, edits) in linked_ranges {
5451 let snapshot = buffer.read(cx).snapshot();
5452 use text::ToPoint as TP;
5453
5454 let edits = edits
5455 .into_iter()
5456 .map(|range| {
5457 let end_point = TP::to_point(&range.end, &snapshot);
5458 let mut start_point = TP::to_point(&range.start, &snapshot);
5459
5460 if end_point == start_point {
5461 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
5462 .saturating_sub(1);
5463 start_point = TP::to_point(&offset, &snapshot);
5464 };
5465
5466 (start_point..end_point, empty_str.clone())
5467 })
5468 .sorted_by_key(|(range, _)| range.start)
5469 .collect::<Vec<_>>();
5470 buffer.update(cx, |this, cx| {
5471 this.edit(edits, None, cx);
5472 })
5473 }
5474 this.refresh_inline_completion(true, cx);
5475 linked_editing_ranges::refresh_linked_ranges(this, cx);
5476 });
5477 }
5478
5479 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
5480 self.transact(cx, |this, cx| {
5481 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5482 let line_mode = s.line_mode;
5483 s.move_with(|map, selection| {
5484 if selection.is_empty() && !line_mode {
5485 let cursor = movement::right(map, selection.head());
5486 selection.end = cursor;
5487 selection.reversed = true;
5488 selection.goal = SelectionGoal::None;
5489 }
5490 })
5491 });
5492 this.insert("", cx);
5493 this.refresh_inline_completion(true, cx);
5494 });
5495 }
5496
5497 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
5498 if self.move_to_prev_snippet_tabstop(cx) {
5499 return;
5500 }
5501
5502 self.outdent(&Outdent, cx);
5503 }
5504
5505 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
5506 if self.move_to_next_snippet_tabstop(cx) || self.read_only(cx) {
5507 return;
5508 }
5509
5510 let mut selections = self.selections.all_adjusted(cx);
5511 let buffer = self.buffer.read(cx);
5512 let snapshot = buffer.snapshot(cx);
5513 let rows_iter = selections.iter().map(|s| s.head().row);
5514 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
5515
5516 let mut edits = Vec::new();
5517 let mut prev_edited_row = 0;
5518 let mut row_delta = 0;
5519 for selection in &mut selections {
5520 if selection.start.row != prev_edited_row {
5521 row_delta = 0;
5522 }
5523 prev_edited_row = selection.end.row;
5524
5525 // If the selection is non-empty, then increase the indentation of the selected lines.
5526 if !selection.is_empty() {
5527 row_delta =
5528 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5529 continue;
5530 }
5531
5532 // If the selection is empty and the cursor is in the leading whitespace before the
5533 // suggested indentation, then auto-indent the line.
5534 let cursor = selection.head();
5535 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
5536 if let Some(suggested_indent) =
5537 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
5538 {
5539 if cursor.column < suggested_indent.len
5540 && cursor.column <= current_indent.len
5541 && current_indent.len <= suggested_indent.len
5542 {
5543 selection.start = Point::new(cursor.row, suggested_indent.len);
5544 selection.end = selection.start;
5545 if row_delta == 0 {
5546 edits.extend(Buffer::edit_for_indent_size_adjustment(
5547 cursor.row,
5548 current_indent,
5549 suggested_indent,
5550 ));
5551 row_delta = suggested_indent.len - current_indent.len;
5552 }
5553 continue;
5554 }
5555 }
5556
5557 // Otherwise, insert a hard or soft tab.
5558 let settings = buffer.settings_at(cursor, cx);
5559 let tab_size = if settings.hard_tabs {
5560 IndentSize::tab()
5561 } else {
5562 let tab_size = settings.tab_size.get();
5563 let char_column = snapshot
5564 .text_for_range(Point::new(cursor.row, 0)..cursor)
5565 .flat_map(str::chars)
5566 .count()
5567 + row_delta as usize;
5568 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
5569 IndentSize::spaces(chars_to_next_tab_stop)
5570 };
5571 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
5572 selection.end = selection.start;
5573 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
5574 row_delta += tab_size.len;
5575 }
5576
5577 self.transact(cx, |this, cx| {
5578 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5579 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5580 this.refresh_inline_completion(true, cx);
5581 });
5582 }
5583
5584 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
5585 if self.read_only(cx) {
5586 return;
5587 }
5588 let mut selections = self.selections.all::<Point>(cx);
5589 let mut prev_edited_row = 0;
5590 let mut row_delta = 0;
5591 let mut edits = Vec::new();
5592 let buffer = self.buffer.read(cx);
5593 let snapshot = buffer.snapshot(cx);
5594 for selection in &mut selections {
5595 if selection.start.row != prev_edited_row {
5596 row_delta = 0;
5597 }
5598 prev_edited_row = selection.end.row;
5599
5600 row_delta =
5601 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5602 }
5603
5604 self.transact(cx, |this, cx| {
5605 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5606 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5607 });
5608 }
5609
5610 fn indent_selection(
5611 buffer: &MultiBuffer,
5612 snapshot: &MultiBufferSnapshot,
5613 selection: &mut Selection<Point>,
5614 edits: &mut Vec<(Range<Point>, String)>,
5615 delta_for_start_row: u32,
5616 cx: &AppContext,
5617 ) -> u32 {
5618 let settings = buffer.settings_at(selection.start, cx);
5619 let tab_size = settings.tab_size.get();
5620 let indent_kind = if settings.hard_tabs {
5621 IndentKind::Tab
5622 } else {
5623 IndentKind::Space
5624 };
5625 let mut start_row = selection.start.row;
5626 let mut end_row = selection.end.row + 1;
5627
5628 // If a selection ends at the beginning of a line, don't indent
5629 // that last line.
5630 if selection.end.column == 0 && selection.end.row > selection.start.row {
5631 end_row -= 1;
5632 }
5633
5634 // Avoid re-indenting a row that has already been indented by a
5635 // previous selection, but still update this selection's column
5636 // to reflect that indentation.
5637 if delta_for_start_row > 0 {
5638 start_row += 1;
5639 selection.start.column += delta_for_start_row;
5640 if selection.end.row == selection.start.row {
5641 selection.end.column += delta_for_start_row;
5642 }
5643 }
5644
5645 let mut delta_for_end_row = 0;
5646 let has_multiple_rows = start_row + 1 != end_row;
5647 for row in start_row..end_row {
5648 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
5649 let indent_delta = match (current_indent.kind, indent_kind) {
5650 (IndentKind::Space, IndentKind::Space) => {
5651 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
5652 IndentSize::spaces(columns_to_next_tab_stop)
5653 }
5654 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
5655 (_, IndentKind::Tab) => IndentSize::tab(),
5656 };
5657
5658 let start = if has_multiple_rows || current_indent.len < selection.start.column {
5659 0
5660 } else {
5661 selection.start.column
5662 };
5663 let row_start = Point::new(row, start);
5664 edits.push((
5665 row_start..row_start,
5666 indent_delta.chars().collect::<String>(),
5667 ));
5668
5669 // Update this selection's endpoints to reflect the indentation.
5670 if row == selection.start.row {
5671 selection.start.column += indent_delta.len;
5672 }
5673 if row == selection.end.row {
5674 selection.end.column += indent_delta.len;
5675 delta_for_end_row = indent_delta.len;
5676 }
5677 }
5678
5679 if selection.start.row == selection.end.row {
5680 delta_for_start_row + delta_for_end_row
5681 } else {
5682 delta_for_end_row
5683 }
5684 }
5685
5686 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
5687 if self.read_only(cx) {
5688 return;
5689 }
5690 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5691 let selections = self.selections.all::<Point>(cx);
5692 let mut deletion_ranges = Vec::new();
5693 let mut last_outdent = None;
5694 {
5695 let buffer = self.buffer.read(cx);
5696 let snapshot = buffer.snapshot(cx);
5697 for selection in &selections {
5698 let settings = buffer.settings_at(selection.start, cx);
5699 let tab_size = settings.tab_size.get();
5700 let mut rows = selection.spanned_rows(false, &display_map);
5701
5702 // Avoid re-outdenting a row that has already been outdented by a
5703 // previous selection.
5704 if let Some(last_row) = last_outdent {
5705 if last_row == rows.start {
5706 rows.start = rows.start.next_row();
5707 }
5708 }
5709 let has_multiple_rows = rows.len() > 1;
5710 for row in rows.iter_rows() {
5711 let indent_size = snapshot.indent_size_for_line(row);
5712 if indent_size.len > 0 {
5713 let deletion_len = match indent_size.kind {
5714 IndentKind::Space => {
5715 let columns_to_prev_tab_stop = indent_size.len % tab_size;
5716 if columns_to_prev_tab_stop == 0 {
5717 tab_size
5718 } else {
5719 columns_to_prev_tab_stop
5720 }
5721 }
5722 IndentKind::Tab => 1,
5723 };
5724 let start = if has_multiple_rows
5725 || deletion_len > selection.start.column
5726 || indent_size.len < selection.start.column
5727 {
5728 0
5729 } else {
5730 selection.start.column - deletion_len
5731 };
5732 deletion_ranges.push(
5733 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
5734 );
5735 last_outdent = Some(row);
5736 }
5737 }
5738 }
5739 }
5740
5741 self.transact(cx, |this, cx| {
5742 this.buffer.update(cx, |buffer, cx| {
5743 let empty_str: Arc<str> = "".into();
5744 buffer.edit(
5745 deletion_ranges
5746 .into_iter()
5747 .map(|range| (range, empty_str.clone())),
5748 None,
5749 cx,
5750 );
5751 });
5752 let selections = this.selections.all::<usize>(cx);
5753 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5754 });
5755 }
5756
5757 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
5758 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5759 let selections = self.selections.all::<Point>(cx);
5760
5761 let mut new_cursors = Vec::new();
5762 let mut edit_ranges = Vec::new();
5763 let mut selections = selections.iter().peekable();
5764 while let Some(selection) = selections.next() {
5765 let mut rows = selection.spanned_rows(false, &display_map);
5766 let goal_display_column = selection.head().to_display_point(&display_map).column();
5767
5768 // Accumulate contiguous regions of rows that we want to delete.
5769 while let Some(next_selection) = selections.peek() {
5770 let next_rows = next_selection.spanned_rows(false, &display_map);
5771 if next_rows.start <= rows.end {
5772 rows.end = next_rows.end;
5773 selections.next().unwrap();
5774 } else {
5775 break;
5776 }
5777 }
5778
5779 let buffer = &display_map.buffer_snapshot;
5780 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
5781 let edit_end;
5782 let cursor_buffer_row;
5783 if buffer.max_point().row >= rows.end.0 {
5784 // If there's a line after the range, delete the \n from the end of the row range
5785 // and position the cursor on the next line.
5786 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
5787 cursor_buffer_row = rows.end;
5788 } else {
5789 // If there isn't a line after the range, delete the \n from the line before the
5790 // start of the row range and position the cursor there.
5791 edit_start = edit_start.saturating_sub(1);
5792 edit_end = buffer.len();
5793 cursor_buffer_row = rows.start.previous_row();
5794 }
5795
5796 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
5797 *cursor.column_mut() =
5798 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
5799
5800 new_cursors.push((
5801 selection.id,
5802 buffer.anchor_after(cursor.to_point(&display_map)),
5803 ));
5804 edit_ranges.push(edit_start..edit_end);
5805 }
5806
5807 self.transact(cx, |this, cx| {
5808 let buffer = this.buffer.update(cx, |buffer, cx| {
5809 let empty_str: Arc<str> = "".into();
5810 buffer.edit(
5811 edit_ranges
5812 .into_iter()
5813 .map(|range| (range, empty_str.clone())),
5814 None,
5815 cx,
5816 );
5817 buffer.snapshot(cx)
5818 });
5819 let new_selections = new_cursors
5820 .into_iter()
5821 .map(|(id, cursor)| {
5822 let cursor = cursor.to_point(&buffer);
5823 Selection {
5824 id,
5825 start: cursor,
5826 end: cursor,
5827 reversed: false,
5828 goal: SelectionGoal::None,
5829 }
5830 })
5831 .collect();
5832
5833 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5834 s.select(new_selections);
5835 });
5836 });
5837 }
5838
5839 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
5840 if self.read_only(cx) {
5841 return;
5842 }
5843 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
5844 for selection in self.selections.all::<Point>(cx) {
5845 let start = MultiBufferRow(selection.start.row);
5846 let end = if selection.start.row == selection.end.row {
5847 MultiBufferRow(selection.start.row + 1)
5848 } else {
5849 MultiBufferRow(selection.end.row)
5850 };
5851
5852 if let Some(last_row_range) = row_ranges.last_mut() {
5853 if start <= last_row_range.end {
5854 last_row_range.end = end;
5855 continue;
5856 }
5857 }
5858 row_ranges.push(start..end);
5859 }
5860
5861 let snapshot = self.buffer.read(cx).snapshot(cx);
5862 let mut cursor_positions = Vec::new();
5863 for row_range in &row_ranges {
5864 let anchor = snapshot.anchor_before(Point::new(
5865 row_range.end.previous_row().0,
5866 snapshot.line_len(row_range.end.previous_row()),
5867 ));
5868 cursor_positions.push(anchor..anchor);
5869 }
5870
5871 self.transact(cx, |this, cx| {
5872 for row_range in row_ranges.into_iter().rev() {
5873 for row in row_range.iter_rows().rev() {
5874 let end_of_line = Point::new(row.0, snapshot.line_len(row));
5875 let next_line_row = row.next_row();
5876 let indent = snapshot.indent_size_for_line(next_line_row);
5877 let start_of_next_line = Point::new(next_line_row.0, indent.len);
5878
5879 let replace = if snapshot.line_len(next_line_row) > indent.len {
5880 " "
5881 } else {
5882 ""
5883 };
5884
5885 this.buffer.update(cx, |buffer, cx| {
5886 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
5887 });
5888 }
5889 }
5890
5891 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5892 s.select_anchor_ranges(cursor_positions)
5893 });
5894 });
5895 }
5896
5897 pub fn sort_lines_case_sensitive(
5898 &mut self,
5899 _: &SortLinesCaseSensitive,
5900 cx: &mut ViewContext<Self>,
5901 ) {
5902 self.manipulate_lines(cx, |lines| lines.sort())
5903 }
5904
5905 pub fn sort_lines_case_insensitive(
5906 &mut self,
5907 _: &SortLinesCaseInsensitive,
5908 cx: &mut ViewContext<Self>,
5909 ) {
5910 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
5911 }
5912
5913 pub fn unique_lines_case_insensitive(
5914 &mut self,
5915 _: &UniqueLinesCaseInsensitive,
5916 cx: &mut ViewContext<Self>,
5917 ) {
5918 self.manipulate_lines(cx, |lines| {
5919 let mut seen = HashSet::default();
5920 lines.retain(|line| seen.insert(line.to_lowercase()));
5921 })
5922 }
5923
5924 pub fn unique_lines_case_sensitive(
5925 &mut self,
5926 _: &UniqueLinesCaseSensitive,
5927 cx: &mut ViewContext<Self>,
5928 ) {
5929 self.manipulate_lines(cx, |lines| {
5930 let mut seen = HashSet::default();
5931 lines.retain(|line| seen.insert(*line));
5932 })
5933 }
5934
5935 pub fn revert_selected_hunks(&mut self, _: &RevertSelectedHunks, cx: &mut ViewContext<Self>) {
5936 let revert_changes = self.gather_revert_changes(&self.selections.disjoint_anchors(), cx);
5937 if !revert_changes.is_empty() {
5938 self.transact(cx, |editor, cx| {
5939 editor.revert(revert_changes, cx);
5940 });
5941 }
5942 }
5943
5944 pub fn open_active_item_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
5945 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
5946 let project_path = buffer.read(cx).project_path(cx)?;
5947 let project = self.project.as_ref()?.read(cx);
5948 let entry = project.entry_for_path(&project_path, cx)?;
5949 let abs_path = project.absolute_path(&project_path, cx)?;
5950 let parent = if entry.is_symlink {
5951 abs_path.canonicalize().ok()?
5952 } else {
5953 abs_path
5954 }
5955 .parent()?
5956 .to_path_buf();
5957 Some(parent)
5958 }) {
5959 cx.dispatch_action(OpenTerminal { working_directory }.boxed_clone());
5960 }
5961 }
5962
5963 fn gather_revert_changes(
5964 &mut self,
5965 selections: &[Selection<Anchor>],
5966 cx: &mut ViewContext<'_, Editor>,
5967 ) -> HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>> {
5968 let mut revert_changes = HashMap::default();
5969 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
5970 for hunk in hunks_for_selections(&multi_buffer_snapshot, selections) {
5971 Self::prepare_revert_change(&mut revert_changes, self.buffer(), &hunk, cx);
5972 }
5973 revert_changes
5974 }
5975
5976 pub fn prepare_revert_change(
5977 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
5978 multi_buffer: &Model<MultiBuffer>,
5979 hunk: &DiffHunk<MultiBufferRow>,
5980 cx: &AppContext,
5981 ) -> Option<()> {
5982 let buffer = multi_buffer.read(cx).buffer(hunk.buffer_id)?;
5983 let buffer = buffer.read(cx);
5984 let original_text = buffer.diff_base()?.slice(hunk.diff_base_byte_range.clone());
5985 let buffer_snapshot = buffer.snapshot();
5986 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
5987 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
5988 probe
5989 .0
5990 .start
5991 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
5992 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
5993 }) {
5994 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
5995 Some(())
5996 } else {
5997 None
5998 }
5999 }
6000
6001 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
6002 self.manipulate_lines(cx, |lines| lines.reverse())
6003 }
6004
6005 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
6006 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
6007 }
6008
6009 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6010 where
6011 Fn: FnMut(&mut Vec<&str>),
6012 {
6013 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6014 let buffer = self.buffer.read(cx).snapshot(cx);
6015
6016 let mut edits = Vec::new();
6017
6018 let selections = self.selections.all::<Point>(cx);
6019 let mut selections = selections.iter().peekable();
6020 let mut contiguous_row_selections = Vec::new();
6021 let mut new_selections = Vec::new();
6022 let mut added_lines = 0;
6023 let mut removed_lines = 0;
6024
6025 while let Some(selection) = selections.next() {
6026 let (start_row, end_row) = consume_contiguous_rows(
6027 &mut contiguous_row_selections,
6028 selection,
6029 &display_map,
6030 &mut selections,
6031 );
6032
6033 let start_point = Point::new(start_row.0, 0);
6034 let end_point = Point::new(
6035 end_row.previous_row().0,
6036 buffer.line_len(end_row.previous_row()),
6037 );
6038 let text = buffer
6039 .text_for_range(start_point..end_point)
6040 .collect::<String>();
6041
6042 let mut lines = text.split('\n').collect_vec();
6043
6044 let lines_before = lines.len();
6045 callback(&mut lines);
6046 let lines_after = lines.len();
6047
6048 edits.push((start_point..end_point, lines.join("\n")));
6049
6050 // Selections must change based on added and removed line count
6051 let start_row =
6052 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
6053 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
6054 new_selections.push(Selection {
6055 id: selection.id,
6056 start: start_row,
6057 end: end_row,
6058 goal: SelectionGoal::None,
6059 reversed: selection.reversed,
6060 });
6061
6062 if lines_after > lines_before {
6063 added_lines += lines_after - lines_before;
6064 } else if lines_before > lines_after {
6065 removed_lines += lines_before - lines_after;
6066 }
6067 }
6068
6069 self.transact(cx, |this, cx| {
6070 let buffer = this.buffer.update(cx, |buffer, cx| {
6071 buffer.edit(edits, None, cx);
6072 buffer.snapshot(cx)
6073 });
6074
6075 // Recalculate offsets on newly edited buffer
6076 let new_selections = new_selections
6077 .iter()
6078 .map(|s| {
6079 let start_point = Point::new(s.start.0, 0);
6080 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
6081 Selection {
6082 id: s.id,
6083 start: buffer.point_to_offset(start_point),
6084 end: buffer.point_to_offset(end_point),
6085 goal: s.goal,
6086 reversed: s.reversed,
6087 }
6088 })
6089 .collect();
6090
6091 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6092 s.select(new_selections);
6093 });
6094
6095 this.request_autoscroll(Autoscroll::fit(), cx);
6096 });
6097 }
6098
6099 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
6100 self.manipulate_text(cx, |text| text.to_uppercase())
6101 }
6102
6103 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
6104 self.manipulate_text(cx, |text| text.to_lowercase())
6105 }
6106
6107 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
6108 self.manipulate_text(cx, |text| {
6109 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6110 // https://github.com/rutrum/convert-case/issues/16
6111 text.split('\n')
6112 .map(|line| line.to_case(Case::Title))
6113 .join("\n")
6114 })
6115 }
6116
6117 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
6118 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
6119 }
6120
6121 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
6122 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
6123 }
6124
6125 pub fn convert_to_upper_camel_case(
6126 &mut self,
6127 _: &ConvertToUpperCamelCase,
6128 cx: &mut ViewContext<Self>,
6129 ) {
6130 self.manipulate_text(cx, |text| {
6131 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6132 // https://github.com/rutrum/convert-case/issues/16
6133 text.split('\n')
6134 .map(|line| line.to_case(Case::UpperCamel))
6135 .join("\n")
6136 })
6137 }
6138
6139 pub fn convert_to_lower_camel_case(
6140 &mut self,
6141 _: &ConvertToLowerCamelCase,
6142 cx: &mut ViewContext<Self>,
6143 ) {
6144 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
6145 }
6146
6147 pub fn convert_to_opposite_case(
6148 &mut self,
6149 _: &ConvertToOppositeCase,
6150 cx: &mut ViewContext<Self>,
6151 ) {
6152 self.manipulate_text(cx, |text| {
6153 text.chars()
6154 .fold(String::with_capacity(text.len()), |mut t, c| {
6155 if c.is_uppercase() {
6156 t.extend(c.to_lowercase());
6157 } else {
6158 t.extend(c.to_uppercase());
6159 }
6160 t
6161 })
6162 })
6163 }
6164
6165 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6166 where
6167 Fn: FnMut(&str) -> String,
6168 {
6169 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6170 let buffer = self.buffer.read(cx).snapshot(cx);
6171
6172 let mut new_selections = Vec::new();
6173 let mut edits = Vec::new();
6174 let mut selection_adjustment = 0i32;
6175
6176 for selection in self.selections.all::<usize>(cx) {
6177 let selection_is_empty = selection.is_empty();
6178
6179 let (start, end) = if selection_is_empty {
6180 let word_range = movement::surrounding_word(
6181 &display_map,
6182 selection.start.to_display_point(&display_map),
6183 );
6184 let start = word_range.start.to_offset(&display_map, Bias::Left);
6185 let end = word_range.end.to_offset(&display_map, Bias::Left);
6186 (start, end)
6187 } else {
6188 (selection.start, selection.end)
6189 };
6190
6191 let text = buffer.text_for_range(start..end).collect::<String>();
6192 let old_length = text.len() as i32;
6193 let text = callback(&text);
6194
6195 new_selections.push(Selection {
6196 start: (start as i32 - selection_adjustment) as usize,
6197 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
6198 goal: SelectionGoal::None,
6199 ..selection
6200 });
6201
6202 selection_adjustment += old_length - text.len() as i32;
6203
6204 edits.push((start..end, text));
6205 }
6206
6207 self.transact(cx, |this, cx| {
6208 this.buffer.update(cx, |buffer, cx| {
6209 buffer.edit(edits, None, cx);
6210 });
6211
6212 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6213 s.select(new_selections);
6214 });
6215
6216 this.request_autoscroll(Autoscroll::fit(), cx);
6217 });
6218 }
6219
6220 pub fn duplicate_line(&mut self, upwards: bool, cx: &mut ViewContext<Self>) {
6221 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6222 let buffer = &display_map.buffer_snapshot;
6223 let selections = self.selections.all::<Point>(cx);
6224
6225 let mut edits = Vec::new();
6226 let mut selections_iter = selections.iter().peekable();
6227 while let Some(selection) = selections_iter.next() {
6228 // Avoid duplicating the same lines twice.
6229 let mut rows = selection.spanned_rows(false, &display_map);
6230
6231 while let Some(next_selection) = selections_iter.peek() {
6232 let next_rows = next_selection.spanned_rows(false, &display_map);
6233 if next_rows.start < rows.end {
6234 rows.end = next_rows.end;
6235 selections_iter.next().unwrap();
6236 } else {
6237 break;
6238 }
6239 }
6240
6241 // Copy the text from the selected row region and splice it either at the start
6242 // or end of the region.
6243 let start = Point::new(rows.start.0, 0);
6244 let end = Point::new(
6245 rows.end.previous_row().0,
6246 buffer.line_len(rows.end.previous_row()),
6247 );
6248 let text = buffer
6249 .text_for_range(start..end)
6250 .chain(Some("\n"))
6251 .collect::<String>();
6252 let insert_location = if upwards {
6253 Point::new(rows.end.0, 0)
6254 } else {
6255 start
6256 };
6257 edits.push((insert_location..insert_location, text));
6258 }
6259
6260 self.transact(cx, |this, cx| {
6261 this.buffer.update(cx, |buffer, cx| {
6262 buffer.edit(edits, None, cx);
6263 });
6264
6265 this.request_autoscroll(Autoscroll::fit(), cx);
6266 });
6267 }
6268
6269 pub fn duplicate_line_up(&mut self, _: &DuplicateLineUp, cx: &mut ViewContext<Self>) {
6270 self.duplicate_line(true, cx);
6271 }
6272
6273 pub fn duplicate_line_down(&mut self, _: &DuplicateLineDown, cx: &mut ViewContext<Self>) {
6274 self.duplicate_line(false, cx);
6275 }
6276
6277 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
6278 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6279 let buffer = self.buffer.read(cx).snapshot(cx);
6280
6281 let mut edits = Vec::new();
6282 let mut unfold_ranges = Vec::new();
6283 let mut refold_ranges = Vec::new();
6284
6285 let selections = self.selections.all::<Point>(cx);
6286 let mut selections = selections.iter().peekable();
6287 let mut contiguous_row_selections = Vec::new();
6288 let mut new_selections = Vec::new();
6289
6290 while let Some(selection) = selections.next() {
6291 // Find all the selections that span a contiguous row range
6292 let (start_row, end_row) = consume_contiguous_rows(
6293 &mut contiguous_row_selections,
6294 selection,
6295 &display_map,
6296 &mut selections,
6297 );
6298
6299 // Move the text spanned by the row range to be before the line preceding the row range
6300 if start_row.0 > 0 {
6301 let range_to_move = Point::new(
6302 start_row.previous_row().0,
6303 buffer.line_len(start_row.previous_row()),
6304 )
6305 ..Point::new(
6306 end_row.previous_row().0,
6307 buffer.line_len(end_row.previous_row()),
6308 );
6309 let insertion_point = display_map
6310 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
6311 .0;
6312
6313 // Don't move lines across excerpts
6314 if buffer
6315 .excerpt_boundaries_in_range((
6316 Bound::Excluded(insertion_point),
6317 Bound::Included(range_to_move.end),
6318 ))
6319 .next()
6320 .is_none()
6321 {
6322 let text = buffer
6323 .text_for_range(range_to_move.clone())
6324 .flat_map(|s| s.chars())
6325 .skip(1)
6326 .chain(['\n'])
6327 .collect::<String>();
6328
6329 edits.push((
6330 buffer.anchor_after(range_to_move.start)
6331 ..buffer.anchor_before(range_to_move.end),
6332 String::new(),
6333 ));
6334 let insertion_anchor = buffer.anchor_after(insertion_point);
6335 edits.push((insertion_anchor..insertion_anchor, text));
6336
6337 let row_delta = range_to_move.start.row - insertion_point.row + 1;
6338
6339 // Move selections up
6340 new_selections.extend(contiguous_row_selections.drain(..).map(
6341 |mut selection| {
6342 selection.start.row -= row_delta;
6343 selection.end.row -= row_delta;
6344 selection
6345 },
6346 ));
6347
6348 // Move folds up
6349 unfold_ranges.push(range_to_move.clone());
6350 for fold in display_map.folds_in_range(
6351 buffer.anchor_before(range_to_move.start)
6352 ..buffer.anchor_after(range_to_move.end),
6353 ) {
6354 let mut start = fold.range.start.to_point(&buffer);
6355 let mut end = fold.range.end.to_point(&buffer);
6356 start.row -= row_delta;
6357 end.row -= row_delta;
6358 refold_ranges.push((start..end, fold.placeholder.clone()));
6359 }
6360 }
6361 }
6362
6363 // If we didn't move line(s), preserve the existing selections
6364 new_selections.append(&mut contiguous_row_selections);
6365 }
6366
6367 self.transact(cx, |this, cx| {
6368 this.unfold_ranges(unfold_ranges, true, true, cx);
6369 this.buffer.update(cx, |buffer, cx| {
6370 for (range, text) in edits {
6371 buffer.edit([(range, text)], None, cx);
6372 }
6373 });
6374 this.fold_ranges(refold_ranges, true, cx);
6375 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6376 s.select(new_selections);
6377 })
6378 });
6379 }
6380
6381 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
6382 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6383 let buffer = self.buffer.read(cx).snapshot(cx);
6384
6385 let mut edits = Vec::new();
6386 let mut unfold_ranges = Vec::new();
6387 let mut refold_ranges = Vec::new();
6388
6389 let selections = self.selections.all::<Point>(cx);
6390 let mut selections = selections.iter().peekable();
6391 let mut contiguous_row_selections = Vec::new();
6392 let mut new_selections = Vec::new();
6393
6394 while let Some(selection) = selections.next() {
6395 // Find all the selections that span a contiguous row range
6396 let (start_row, end_row) = consume_contiguous_rows(
6397 &mut contiguous_row_selections,
6398 selection,
6399 &display_map,
6400 &mut selections,
6401 );
6402
6403 // Move the text spanned by the row range to be after the last line of the row range
6404 if end_row.0 <= buffer.max_point().row {
6405 let range_to_move =
6406 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
6407 let insertion_point = display_map
6408 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
6409 .0;
6410
6411 // Don't move lines across excerpt boundaries
6412 if buffer
6413 .excerpt_boundaries_in_range((
6414 Bound::Excluded(range_to_move.start),
6415 Bound::Included(insertion_point),
6416 ))
6417 .next()
6418 .is_none()
6419 {
6420 let mut text = String::from("\n");
6421 text.extend(buffer.text_for_range(range_to_move.clone()));
6422 text.pop(); // Drop trailing newline
6423 edits.push((
6424 buffer.anchor_after(range_to_move.start)
6425 ..buffer.anchor_before(range_to_move.end),
6426 String::new(),
6427 ));
6428 let insertion_anchor = buffer.anchor_after(insertion_point);
6429 edits.push((insertion_anchor..insertion_anchor, text));
6430
6431 let row_delta = insertion_point.row - range_to_move.end.row + 1;
6432
6433 // Move selections down
6434 new_selections.extend(contiguous_row_selections.drain(..).map(
6435 |mut selection| {
6436 selection.start.row += row_delta;
6437 selection.end.row += row_delta;
6438 selection
6439 },
6440 ));
6441
6442 // Move folds down
6443 unfold_ranges.push(range_to_move.clone());
6444 for fold in display_map.folds_in_range(
6445 buffer.anchor_before(range_to_move.start)
6446 ..buffer.anchor_after(range_to_move.end),
6447 ) {
6448 let mut start = fold.range.start.to_point(&buffer);
6449 let mut end = fold.range.end.to_point(&buffer);
6450 start.row += row_delta;
6451 end.row += row_delta;
6452 refold_ranges.push((start..end, fold.placeholder.clone()));
6453 }
6454 }
6455 }
6456
6457 // If we didn't move line(s), preserve the existing selections
6458 new_selections.append(&mut contiguous_row_selections);
6459 }
6460
6461 self.transact(cx, |this, cx| {
6462 this.unfold_ranges(unfold_ranges, true, true, cx);
6463 this.buffer.update(cx, |buffer, cx| {
6464 for (range, text) in edits {
6465 buffer.edit([(range, text)], None, cx);
6466 }
6467 });
6468 this.fold_ranges(refold_ranges, true, cx);
6469 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
6470 });
6471 }
6472
6473 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
6474 let text_layout_details = &self.text_layout_details(cx);
6475 self.transact(cx, |this, cx| {
6476 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6477 let mut edits: Vec<(Range<usize>, String)> = Default::default();
6478 let line_mode = s.line_mode;
6479 s.move_with(|display_map, selection| {
6480 if !selection.is_empty() || line_mode {
6481 return;
6482 }
6483
6484 let mut head = selection.head();
6485 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
6486 if head.column() == display_map.line_len(head.row()) {
6487 transpose_offset = display_map
6488 .buffer_snapshot
6489 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6490 }
6491
6492 if transpose_offset == 0 {
6493 return;
6494 }
6495
6496 *head.column_mut() += 1;
6497 head = display_map.clip_point(head, Bias::Right);
6498 let goal = SelectionGoal::HorizontalPosition(
6499 display_map
6500 .x_for_display_point(head, &text_layout_details)
6501 .into(),
6502 );
6503 selection.collapse_to(head, goal);
6504
6505 let transpose_start = display_map
6506 .buffer_snapshot
6507 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6508 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
6509 let transpose_end = display_map
6510 .buffer_snapshot
6511 .clip_offset(transpose_offset + 1, Bias::Right);
6512 if let Some(ch) =
6513 display_map.buffer_snapshot.chars_at(transpose_start).next()
6514 {
6515 edits.push((transpose_start..transpose_offset, String::new()));
6516 edits.push((transpose_end..transpose_end, ch.to_string()));
6517 }
6518 }
6519 });
6520 edits
6521 });
6522 this.buffer
6523 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
6524 let selections = this.selections.all::<usize>(cx);
6525 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6526 s.select(selections);
6527 });
6528 });
6529 }
6530
6531 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
6532 let mut text = String::new();
6533 let buffer = self.buffer.read(cx).snapshot(cx);
6534 let mut selections = self.selections.all::<Point>(cx);
6535 let mut clipboard_selections = Vec::with_capacity(selections.len());
6536 {
6537 let max_point = buffer.max_point();
6538 let mut is_first = true;
6539 for selection in &mut selections {
6540 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6541 if is_entire_line {
6542 selection.start = Point::new(selection.start.row, 0);
6543 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
6544 selection.goal = SelectionGoal::None;
6545 }
6546 if is_first {
6547 is_first = false;
6548 } else {
6549 text += "\n";
6550 }
6551 let mut len = 0;
6552 for chunk in buffer.text_for_range(selection.start..selection.end) {
6553 text.push_str(chunk);
6554 len += chunk.len();
6555 }
6556 clipboard_selections.push(ClipboardSelection {
6557 len,
6558 is_entire_line,
6559 first_line_indent: buffer
6560 .indent_size_for_line(MultiBufferRow(selection.start.row))
6561 .len,
6562 });
6563 }
6564 }
6565
6566 self.transact(cx, |this, cx| {
6567 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6568 s.select(selections);
6569 });
6570 this.insert("", cx);
6571 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
6572 });
6573 }
6574
6575 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
6576 let selections = self.selections.all::<Point>(cx);
6577 let buffer = self.buffer.read(cx).read(cx);
6578 let mut text = String::new();
6579
6580 let mut clipboard_selections = Vec::with_capacity(selections.len());
6581 {
6582 let max_point = buffer.max_point();
6583 let mut is_first = true;
6584 for selection in selections.iter() {
6585 let mut start = selection.start;
6586 let mut end = selection.end;
6587 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6588 if is_entire_line {
6589 start = Point::new(start.row, 0);
6590 end = cmp::min(max_point, Point::new(end.row + 1, 0));
6591 }
6592 if is_first {
6593 is_first = false;
6594 } else {
6595 text += "\n";
6596 }
6597 let mut len = 0;
6598 for chunk in buffer.text_for_range(start..end) {
6599 text.push_str(chunk);
6600 len += chunk.len();
6601 }
6602 clipboard_selections.push(ClipboardSelection {
6603 len,
6604 is_entire_line,
6605 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
6606 });
6607 }
6608 }
6609
6610 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
6611 }
6612
6613 pub fn do_paste(
6614 &mut self,
6615 text: &String,
6616 clipboard_selections: Option<Vec<ClipboardSelection>>,
6617 handle_entire_lines: bool,
6618 cx: &mut ViewContext<Self>,
6619 ) {
6620 if self.read_only(cx) {
6621 return;
6622 }
6623
6624 let clipboard_text = Cow::Borrowed(text);
6625
6626 self.transact(cx, |this, cx| {
6627 if let Some(mut clipboard_selections) = clipboard_selections {
6628 let old_selections = this.selections.all::<usize>(cx);
6629 let all_selections_were_entire_line =
6630 clipboard_selections.iter().all(|s| s.is_entire_line);
6631 let first_selection_indent_column =
6632 clipboard_selections.first().map(|s| s.first_line_indent);
6633 if clipboard_selections.len() != old_selections.len() {
6634 clipboard_selections.drain(..);
6635 }
6636
6637 this.buffer.update(cx, |buffer, cx| {
6638 let snapshot = buffer.read(cx);
6639 let mut start_offset = 0;
6640 let mut edits = Vec::new();
6641 let mut original_indent_columns = Vec::new();
6642 for (ix, selection) in old_selections.iter().enumerate() {
6643 let to_insert;
6644 let entire_line;
6645 let original_indent_column;
6646 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
6647 let end_offset = start_offset + clipboard_selection.len;
6648 to_insert = &clipboard_text[start_offset..end_offset];
6649 entire_line = clipboard_selection.is_entire_line;
6650 start_offset = end_offset + 1;
6651 original_indent_column = Some(clipboard_selection.first_line_indent);
6652 } else {
6653 to_insert = clipboard_text.as_str();
6654 entire_line = all_selections_were_entire_line;
6655 original_indent_column = first_selection_indent_column
6656 }
6657
6658 // If the corresponding selection was empty when this slice of the
6659 // clipboard text was written, then the entire line containing the
6660 // selection was copied. If this selection is also currently empty,
6661 // then paste the line before the current line of the buffer.
6662 let range = if selection.is_empty() && handle_entire_lines && entire_line {
6663 let column = selection.start.to_point(&snapshot).column as usize;
6664 let line_start = selection.start - column;
6665 line_start..line_start
6666 } else {
6667 selection.range()
6668 };
6669
6670 edits.push((range, to_insert));
6671 original_indent_columns.extend(original_indent_column);
6672 }
6673 drop(snapshot);
6674
6675 buffer.edit(
6676 edits,
6677 Some(AutoindentMode::Block {
6678 original_indent_columns,
6679 }),
6680 cx,
6681 );
6682 });
6683
6684 let selections = this.selections.all::<usize>(cx);
6685 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6686 } else {
6687 this.insert(&clipboard_text, cx);
6688 }
6689 });
6690 }
6691
6692 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
6693 if let Some(item) = cx.read_from_clipboard() {
6694 self.do_paste(
6695 item.text(),
6696 item.metadata::<Vec<ClipboardSelection>>(),
6697 true,
6698 cx,
6699 )
6700 };
6701 }
6702
6703 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
6704 if self.read_only(cx) {
6705 return;
6706 }
6707
6708 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
6709 if let Some((selections, _)) =
6710 self.selection_history.transaction(transaction_id).cloned()
6711 {
6712 self.change_selections(None, cx, |s| {
6713 s.select_anchors(selections.to_vec());
6714 });
6715 }
6716 self.request_autoscroll(Autoscroll::fit(), cx);
6717 self.unmark_text(cx);
6718 self.refresh_inline_completion(true, cx);
6719 cx.emit(EditorEvent::Edited { transaction_id });
6720 cx.emit(EditorEvent::TransactionUndone { transaction_id });
6721 }
6722 }
6723
6724 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
6725 if self.read_only(cx) {
6726 return;
6727 }
6728
6729 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
6730 if let Some((_, Some(selections))) =
6731 self.selection_history.transaction(transaction_id).cloned()
6732 {
6733 self.change_selections(None, cx, |s| {
6734 s.select_anchors(selections.to_vec());
6735 });
6736 }
6737 self.request_autoscroll(Autoscroll::fit(), cx);
6738 self.unmark_text(cx);
6739 self.refresh_inline_completion(true, cx);
6740 cx.emit(EditorEvent::Edited { transaction_id });
6741 }
6742 }
6743
6744 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
6745 self.buffer
6746 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
6747 }
6748
6749 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut ViewContext<Self>) {
6750 self.buffer
6751 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
6752 }
6753
6754 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
6755 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6756 let line_mode = s.line_mode;
6757 s.move_with(|map, selection| {
6758 let cursor = if selection.is_empty() && !line_mode {
6759 movement::left(map, selection.start)
6760 } else {
6761 selection.start
6762 };
6763 selection.collapse_to(cursor, SelectionGoal::None);
6764 });
6765 })
6766 }
6767
6768 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
6769 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6770 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
6771 })
6772 }
6773
6774 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
6775 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6776 let line_mode = s.line_mode;
6777 s.move_with(|map, selection| {
6778 let cursor = if selection.is_empty() && !line_mode {
6779 movement::right(map, selection.end)
6780 } else {
6781 selection.end
6782 };
6783 selection.collapse_to(cursor, SelectionGoal::None)
6784 });
6785 })
6786 }
6787
6788 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
6789 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6790 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
6791 })
6792 }
6793
6794 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
6795 if self.take_rename(true, cx).is_some() {
6796 return;
6797 }
6798
6799 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6800 cx.propagate();
6801 return;
6802 }
6803
6804 let text_layout_details = &self.text_layout_details(cx);
6805 let selection_count = self.selections.count();
6806 let first_selection = self.selections.first_anchor();
6807
6808 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6809 let line_mode = s.line_mode;
6810 s.move_with(|map, selection| {
6811 if !selection.is_empty() && !line_mode {
6812 selection.goal = SelectionGoal::None;
6813 }
6814 let (cursor, goal) = movement::up(
6815 map,
6816 selection.start,
6817 selection.goal,
6818 false,
6819 &text_layout_details,
6820 );
6821 selection.collapse_to(cursor, goal);
6822 });
6823 });
6824
6825 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
6826 {
6827 cx.propagate();
6828 }
6829 }
6830
6831 pub fn move_up_by_lines(&mut self, action: &MoveUpByLines, cx: &mut ViewContext<Self>) {
6832 if self.take_rename(true, cx).is_some() {
6833 return;
6834 }
6835
6836 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6837 cx.propagate();
6838 return;
6839 }
6840
6841 let text_layout_details = &self.text_layout_details(cx);
6842
6843 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6844 let line_mode = s.line_mode;
6845 s.move_with(|map, selection| {
6846 if !selection.is_empty() && !line_mode {
6847 selection.goal = SelectionGoal::None;
6848 }
6849 let (cursor, goal) = movement::up_by_rows(
6850 map,
6851 selection.start,
6852 action.lines,
6853 selection.goal,
6854 false,
6855 &text_layout_details,
6856 );
6857 selection.collapse_to(cursor, goal);
6858 });
6859 })
6860 }
6861
6862 pub fn move_down_by_lines(&mut self, action: &MoveDownByLines, cx: &mut ViewContext<Self>) {
6863 if self.take_rename(true, cx).is_some() {
6864 return;
6865 }
6866
6867 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6868 cx.propagate();
6869 return;
6870 }
6871
6872 let text_layout_details = &self.text_layout_details(cx);
6873
6874 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6875 let line_mode = s.line_mode;
6876 s.move_with(|map, selection| {
6877 if !selection.is_empty() && !line_mode {
6878 selection.goal = SelectionGoal::None;
6879 }
6880 let (cursor, goal) = movement::down_by_rows(
6881 map,
6882 selection.start,
6883 action.lines,
6884 selection.goal,
6885 false,
6886 &text_layout_details,
6887 );
6888 selection.collapse_to(cursor, goal);
6889 });
6890 })
6891 }
6892
6893 pub fn select_down_by_lines(&mut self, action: &SelectDownByLines, cx: &mut ViewContext<Self>) {
6894 let text_layout_details = &self.text_layout_details(cx);
6895 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6896 s.move_heads_with(|map, head, goal| {
6897 movement::down_by_rows(map, head, action.lines, goal, false, &text_layout_details)
6898 })
6899 })
6900 }
6901
6902 pub fn select_up_by_lines(&mut self, action: &SelectUpByLines, cx: &mut ViewContext<Self>) {
6903 let text_layout_details = &self.text_layout_details(cx);
6904 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6905 s.move_heads_with(|map, head, goal| {
6906 movement::up_by_rows(map, head, action.lines, goal, false, &text_layout_details)
6907 })
6908 })
6909 }
6910
6911 pub fn select_page_up(&mut self, _: &SelectPageUp, cx: &mut ViewContext<Self>) {
6912 let Some(row_count) = self.visible_row_count() else {
6913 return;
6914 };
6915
6916 let text_layout_details = &self.text_layout_details(cx);
6917
6918 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6919 s.move_heads_with(|map, head, goal| {
6920 movement::up_by_rows(map, head, row_count, goal, false, &text_layout_details)
6921 })
6922 })
6923 }
6924
6925 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
6926 if self.take_rename(true, cx).is_some() {
6927 return;
6928 }
6929
6930 if self
6931 .context_menu
6932 .write()
6933 .as_mut()
6934 .map(|menu| menu.select_first(self.project.as_ref(), cx))
6935 .unwrap_or(false)
6936 {
6937 return;
6938 }
6939
6940 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6941 cx.propagate();
6942 return;
6943 }
6944
6945 let Some(row_count) = self.visible_row_count() else {
6946 return;
6947 };
6948
6949 let autoscroll = if action.center_cursor {
6950 Autoscroll::center()
6951 } else {
6952 Autoscroll::fit()
6953 };
6954
6955 let text_layout_details = &self.text_layout_details(cx);
6956
6957 self.change_selections(Some(autoscroll), cx, |s| {
6958 let line_mode = s.line_mode;
6959 s.move_with(|map, selection| {
6960 if !selection.is_empty() && !line_mode {
6961 selection.goal = SelectionGoal::None;
6962 }
6963 let (cursor, goal) = movement::up_by_rows(
6964 map,
6965 selection.end,
6966 row_count,
6967 selection.goal,
6968 false,
6969 &text_layout_details,
6970 );
6971 selection.collapse_to(cursor, goal);
6972 });
6973 });
6974 }
6975
6976 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
6977 let text_layout_details = &self.text_layout_details(cx);
6978 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6979 s.move_heads_with(|map, head, goal| {
6980 movement::up(map, head, goal, false, &text_layout_details)
6981 })
6982 })
6983 }
6984
6985 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
6986 self.take_rename(true, cx);
6987
6988 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6989 cx.propagate();
6990 return;
6991 }
6992
6993 let text_layout_details = &self.text_layout_details(cx);
6994 let selection_count = self.selections.count();
6995 let first_selection = self.selections.first_anchor();
6996
6997 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6998 let line_mode = s.line_mode;
6999 s.move_with(|map, selection| {
7000 if !selection.is_empty() && !line_mode {
7001 selection.goal = SelectionGoal::None;
7002 }
7003 let (cursor, goal) = movement::down(
7004 map,
7005 selection.end,
7006 selection.goal,
7007 false,
7008 &text_layout_details,
7009 );
7010 selection.collapse_to(cursor, goal);
7011 });
7012 });
7013
7014 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7015 {
7016 cx.propagate();
7017 }
7018 }
7019
7020 pub fn select_page_down(&mut self, _: &SelectPageDown, cx: &mut ViewContext<Self>) {
7021 let Some(row_count) = self.visible_row_count() else {
7022 return;
7023 };
7024
7025 let text_layout_details = &self.text_layout_details(cx);
7026
7027 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7028 s.move_heads_with(|map, head, goal| {
7029 movement::down_by_rows(map, head, row_count, goal, false, &text_layout_details)
7030 })
7031 })
7032 }
7033
7034 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
7035 if self.take_rename(true, cx).is_some() {
7036 return;
7037 }
7038
7039 if self
7040 .context_menu
7041 .write()
7042 .as_mut()
7043 .map(|menu| menu.select_last(self.project.as_ref(), cx))
7044 .unwrap_or(false)
7045 {
7046 return;
7047 }
7048
7049 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7050 cx.propagate();
7051 return;
7052 }
7053
7054 let Some(row_count) = self.visible_row_count() else {
7055 return;
7056 };
7057
7058 let autoscroll = if action.center_cursor {
7059 Autoscroll::center()
7060 } else {
7061 Autoscroll::fit()
7062 };
7063
7064 let text_layout_details = &self.text_layout_details(cx);
7065 self.change_selections(Some(autoscroll), cx, |s| {
7066 let line_mode = s.line_mode;
7067 s.move_with(|map, selection| {
7068 if !selection.is_empty() && !line_mode {
7069 selection.goal = SelectionGoal::None;
7070 }
7071 let (cursor, goal) = movement::down_by_rows(
7072 map,
7073 selection.end,
7074 row_count,
7075 selection.goal,
7076 false,
7077 &text_layout_details,
7078 );
7079 selection.collapse_to(cursor, goal);
7080 });
7081 });
7082 }
7083
7084 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
7085 let text_layout_details = &self.text_layout_details(cx);
7086 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7087 s.move_heads_with(|map, head, goal| {
7088 movement::down(map, head, goal, false, &text_layout_details)
7089 })
7090 });
7091 }
7092
7093 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
7094 if let Some(context_menu) = self.context_menu.write().as_mut() {
7095 context_menu.select_first(self.project.as_ref(), cx);
7096 }
7097 }
7098
7099 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
7100 if let Some(context_menu) = self.context_menu.write().as_mut() {
7101 context_menu.select_prev(self.project.as_ref(), cx);
7102 }
7103 }
7104
7105 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
7106 if let Some(context_menu) = self.context_menu.write().as_mut() {
7107 context_menu.select_next(self.project.as_ref(), cx);
7108 }
7109 }
7110
7111 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
7112 if let Some(context_menu) = self.context_menu.write().as_mut() {
7113 context_menu.select_last(self.project.as_ref(), cx);
7114 }
7115 }
7116
7117 pub fn move_to_previous_word_start(
7118 &mut self,
7119 _: &MoveToPreviousWordStart,
7120 cx: &mut ViewContext<Self>,
7121 ) {
7122 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7123 s.move_cursors_with(|map, head, _| {
7124 (
7125 movement::previous_word_start(map, head),
7126 SelectionGoal::None,
7127 )
7128 });
7129 })
7130 }
7131
7132 pub fn move_to_previous_subword_start(
7133 &mut self,
7134 _: &MoveToPreviousSubwordStart,
7135 cx: &mut ViewContext<Self>,
7136 ) {
7137 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7138 s.move_cursors_with(|map, head, _| {
7139 (
7140 movement::previous_subword_start(map, head),
7141 SelectionGoal::None,
7142 )
7143 });
7144 })
7145 }
7146
7147 pub fn select_to_previous_word_start(
7148 &mut self,
7149 _: &SelectToPreviousWordStart,
7150 cx: &mut ViewContext<Self>,
7151 ) {
7152 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7153 s.move_heads_with(|map, head, _| {
7154 (
7155 movement::previous_word_start(map, head),
7156 SelectionGoal::None,
7157 )
7158 });
7159 })
7160 }
7161
7162 pub fn select_to_previous_subword_start(
7163 &mut self,
7164 _: &SelectToPreviousSubwordStart,
7165 cx: &mut ViewContext<Self>,
7166 ) {
7167 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7168 s.move_heads_with(|map, head, _| {
7169 (
7170 movement::previous_subword_start(map, head),
7171 SelectionGoal::None,
7172 )
7173 });
7174 })
7175 }
7176
7177 pub fn delete_to_previous_word_start(
7178 &mut self,
7179 _: &DeleteToPreviousWordStart,
7180 cx: &mut ViewContext<Self>,
7181 ) {
7182 self.transact(cx, |this, cx| {
7183 this.select_autoclose_pair(cx);
7184 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7185 let line_mode = s.line_mode;
7186 s.move_with(|map, selection| {
7187 if selection.is_empty() && !line_mode {
7188 let cursor = movement::previous_word_start(map, selection.head());
7189 selection.set_head(cursor, SelectionGoal::None);
7190 }
7191 });
7192 });
7193 this.insert("", cx);
7194 });
7195 }
7196
7197 pub fn delete_to_previous_subword_start(
7198 &mut self,
7199 _: &DeleteToPreviousSubwordStart,
7200 cx: &mut ViewContext<Self>,
7201 ) {
7202 self.transact(cx, |this, cx| {
7203 this.select_autoclose_pair(cx);
7204 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7205 let line_mode = s.line_mode;
7206 s.move_with(|map, selection| {
7207 if selection.is_empty() && !line_mode {
7208 let cursor = movement::previous_subword_start(map, selection.head());
7209 selection.set_head(cursor, SelectionGoal::None);
7210 }
7211 });
7212 });
7213 this.insert("", cx);
7214 });
7215 }
7216
7217 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
7218 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7219 s.move_cursors_with(|map, head, _| {
7220 (movement::next_word_end(map, head), SelectionGoal::None)
7221 });
7222 })
7223 }
7224
7225 pub fn move_to_next_subword_end(
7226 &mut self,
7227 _: &MoveToNextSubwordEnd,
7228 cx: &mut ViewContext<Self>,
7229 ) {
7230 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7231 s.move_cursors_with(|map, head, _| {
7232 (movement::next_subword_end(map, head), SelectionGoal::None)
7233 });
7234 })
7235 }
7236
7237 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
7238 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7239 s.move_heads_with(|map, head, _| {
7240 (movement::next_word_end(map, head), SelectionGoal::None)
7241 });
7242 })
7243 }
7244
7245 pub fn select_to_next_subword_end(
7246 &mut self,
7247 _: &SelectToNextSubwordEnd,
7248 cx: &mut ViewContext<Self>,
7249 ) {
7250 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7251 s.move_heads_with(|map, head, _| {
7252 (movement::next_subword_end(map, head), SelectionGoal::None)
7253 });
7254 })
7255 }
7256
7257 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
7258 self.transact(cx, |this, cx| {
7259 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7260 let line_mode = s.line_mode;
7261 s.move_with(|map, selection| {
7262 if selection.is_empty() && !line_mode {
7263 let cursor = movement::next_word_end(map, selection.head());
7264 selection.set_head(cursor, SelectionGoal::None);
7265 }
7266 });
7267 });
7268 this.insert("", cx);
7269 });
7270 }
7271
7272 pub fn delete_to_next_subword_end(
7273 &mut self,
7274 _: &DeleteToNextSubwordEnd,
7275 cx: &mut ViewContext<Self>,
7276 ) {
7277 self.transact(cx, |this, cx| {
7278 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7279 s.move_with(|map, selection| {
7280 if selection.is_empty() {
7281 let cursor = movement::next_subword_end(map, selection.head());
7282 selection.set_head(cursor, SelectionGoal::None);
7283 }
7284 });
7285 });
7286 this.insert("", cx);
7287 });
7288 }
7289
7290 pub fn move_to_beginning_of_line(
7291 &mut self,
7292 action: &MoveToBeginningOfLine,
7293 cx: &mut ViewContext<Self>,
7294 ) {
7295 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7296 s.move_cursors_with(|map, head, _| {
7297 (
7298 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7299 SelectionGoal::None,
7300 )
7301 });
7302 })
7303 }
7304
7305 pub fn select_to_beginning_of_line(
7306 &mut self,
7307 action: &SelectToBeginningOfLine,
7308 cx: &mut ViewContext<Self>,
7309 ) {
7310 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7311 s.move_heads_with(|map, head, _| {
7312 (
7313 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7314 SelectionGoal::None,
7315 )
7316 });
7317 });
7318 }
7319
7320 pub fn delete_to_beginning_of_line(
7321 &mut self,
7322 _: &DeleteToBeginningOfLine,
7323 cx: &mut ViewContext<Self>,
7324 ) {
7325 self.transact(cx, |this, cx| {
7326 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7327 s.move_with(|_, selection| {
7328 selection.reversed = true;
7329 });
7330 });
7331
7332 this.select_to_beginning_of_line(
7333 &SelectToBeginningOfLine {
7334 stop_at_soft_wraps: false,
7335 },
7336 cx,
7337 );
7338 this.backspace(&Backspace, cx);
7339 });
7340 }
7341
7342 pub fn move_to_end_of_line(&mut self, action: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
7343 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7344 s.move_cursors_with(|map, head, _| {
7345 (
7346 movement::line_end(map, head, action.stop_at_soft_wraps),
7347 SelectionGoal::None,
7348 )
7349 });
7350 })
7351 }
7352
7353 pub fn select_to_end_of_line(
7354 &mut self,
7355 action: &SelectToEndOfLine,
7356 cx: &mut ViewContext<Self>,
7357 ) {
7358 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7359 s.move_heads_with(|map, head, _| {
7360 (
7361 movement::line_end(map, head, action.stop_at_soft_wraps),
7362 SelectionGoal::None,
7363 )
7364 });
7365 })
7366 }
7367
7368 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
7369 self.transact(cx, |this, cx| {
7370 this.select_to_end_of_line(
7371 &SelectToEndOfLine {
7372 stop_at_soft_wraps: false,
7373 },
7374 cx,
7375 );
7376 this.delete(&Delete, cx);
7377 });
7378 }
7379
7380 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
7381 self.transact(cx, |this, cx| {
7382 this.select_to_end_of_line(
7383 &SelectToEndOfLine {
7384 stop_at_soft_wraps: false,
7385 },
7386 cx,
7387 );
7388 this.cut(&Cut, cx);
7389 });
7390 }
7391
7392 pub fn move_to_start_of_paragraph(
7393 &mut self,
7394 _: &MoveToStartOfParagraph,
7395 cx: &mut ViewContext<Self>,
7396 ) {
7397 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7398 cx.propagate();
7399 return;
7400 }
7401
7402 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7403 s.move_with(|map, selection| {
7404 selection.collapse_to(
7405 movement::start_of_paragraph(map, selection.head(), 1),
7406 SelectionGoal::None,
7407 )
7408 });
7409 })
7410 }
7411
7412 pub fn move_to_end_of_paragraph(
7413 &mut self,
7414 _: &MoveToEndOfParagraph,
7415 cx: &mut ViewContext<Self>,
7416 ) {
7417 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7418 cx.propagate();
7419 return;
7420 }
7421
7422 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7423 s.move_with(|map, selection| {
7424 selection.collapse_to(
7425 movement::end_of_paragraph(map, selection.head(), 1),
7426 SelectionGoal::None,
7427 )
7428 });
7429 })
7430 }
7431
7432 pub fn select_to_start_of_paragraph(
7433 &mut self,
7434 _: &SelectToStartOfParagraph,
7435 cx: &mut ViewContext<Self>,
7436 ) {
7437 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7438 cx.propagate();
7439 return;
7440 }
7441
7442 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7443 s.move_heads_with(|map, head, _| {
7444 (
7445 movement::start_of_paragraph(map, head, 1),
7446 SelectionGoal::None,
7447 )
7448 });
7449 })
7450 }
7451
7452 pub fn select_to_end_of_paragraph(
7453 &mut self,
7454 _: &SelectToEndOfParagraph,
7455 cx: &mut ViewContext<Self>,
7456 ) {
7457 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7458 cx.propagate();
7459 return;
7460 }
7461
7462 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7463 s.move_heads_with(|map, head, _| {
7464 (
7465 movement::end_of_paragraph(map, head, 1),
7466 SelectionGoal::None,
7467 )
7468 });
7469 })
7470 }
7471
7472 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
7473 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7474 cx.propagate();
7475 return;
7476 }
7477
7478 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7479 s.select_ranges(vec![0..0]);
7480 });
7481 }
7482
7483 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
7484 let mut selection = self.selections.last::<Point>(cx);
7485 selection.set_head(Point::zero(), SelectionGoal::None);
7486
7487 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7488 s.select(vec![selection]);
7489 });
7490 }
7491
7492 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
7493 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7494 cx.propagate();
7495 return;
7496 }
7497
7498 let cursor = self.buffer.read(cx).read(cx).len();
7499 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7500 s.select_ranges(vec![cursor..cursor])
7501 });
7502 }
7503
7504 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
7505 self.nav_history = nav_history;
7506 }
7507
7508 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
7509 self.nav_history.as_ref()
7510 }
7511
7512 fn push_to_nav_history(
7513 &mut self,
7514 cursor_anchor: Anchor,
7515 new_position: Option<Point>,
7516 cx: &mut ViewContext<Self>,
7517 ) {
7518 if let Some(nav_history) = self.nav_history.as_mut() {
7519 let buffer = self.buffer.read(cx).read(cx);
7520 let cursor_position = cursor_anchor.to_point(&buffer);
7521 let scroll_state = self.scroll_manager.anchor();
7522 let scroll_top_row = scroll_state.top_row(&buffer);
7523 drop(buffer);
7524
7525 if let Some(new_position) = new_position {
7526 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
7527 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
7528 return;
7529 }
7530 }
7531
7532 nav_history.push(
7533 Some(NavigationData {
7534 cursor_anchor,
7535 cursor_position,
7536 scroll_anchor: scroll_state,
7537 scroll_top_row,
7538 }),
7539 cx,
7540 );
7541 }
7542 }
7543
7544 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
7545 let buffer = self.buffer.read(cx).snapshot(cx);
7546 let mut selection = self.selections.first::<usize>(cx);
7547 selection.set_head(buffer.len(), SelectionGoal::None);
7548 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7549 s.select(vec![selection]);
7550 });
7551 }
7552
7553 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
7554 let end = self.buffer.read(cx).read(cx).len();
7555 self.change_selections(None, cx, |s| {
7556 s.select_ranges(vec![0..end]);
7557 });
7558 }
7559
7560 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
7561 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7562 let mut selections = self.selections.all::<Point>(cx);
7563 let max_point = display_map.buffer_snapshot.max_point();
7564 for selection in &mut selections {
7565 let rows = selection.spanned_rows(true, &display_map);
7566 selection.start = Point::new(rows.start.0, 0);
7567 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
7568 selection.reversed = false;
7569 }
7570 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7571 s.select(selections);
7572 });
7573 }
7574
7575 pub fn split_selection_into_lines(
7576 &mut self,
7577 _: &SplitSelectionIntoLines,
7578 cx: &mut ViewContext<Self>,
7579 ) {
7580 let mut to_unfold = Vec::new();
7581 let mut new_selection_ranges = Vec::new();
7582 {
7583 let selections = self.selections.all::<Point>(cx);
7584 let buffer = self.buffer.read(cx).read(cx);
7585 for selection in selections {
7586 for row in selection.start.row..selection.end.row {
7587 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
7588 new_selection_ranges.push(cursor..cursor);
7589 }
7590 new_selection_ranges.push(selection.end..selection.end);
7591 to_unfold.push(selection.start..selection.end);
7592 }
7593 }
7594 self.unfold_ranges(to_unfold, true, true, cx);
7595 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7596 s.select_ranges(new_selection_ranges);
7597 });
7598 }
7599
7600 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
7601 self.add_selection(true, cx);
7602 }
7603
7604 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
7605 self.add_selection(false, cx);
7606 }
7607
7608 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
7609 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7610 let mut selections = self.selections.all::<Point>(cx);
7611 let text_layout_details = self.text_layout_details(cx);
7612 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
7613 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
7614 let range = oldest_selection.display_range(&display_map).sorted();
7615
7616 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
7617 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
7618 let positions = start_x.min(end_x)..start_x.max(end_x);
7619
7620 selections.clear();
7621 let mut stack = Vec::new();
7622 for row in range.start.row().0..=range.end.row().0 {
7623 if let Some(selection) = self.selections.build_columnar_selection(
7624 &display_map,
7625 DisplayRow(row),
7626 &positions,
7627 oldest_selection.reversed,
7628 &text_layout_details,
7629 ) {
7630 stack.push(selection.id);
7631 selections.push(selection);
7632 }
7633 }
7634
7635 if above {
7636 stack.reverse();
7637 }
7638
7639 AddSelectionsState { above, stack }
7640 });
7641
7642 let last_added_selection = *state.stack.last().unwrap();
7643 let mut new_selections = Vec::new();
7644 if above == state.above {
7645 let end_row = if above {
7646 DisplayRow(0)
7647 } else {
7648 display_map.max_point().row()
7649 };
7650
7651 'outer: for selection in selections {
7652 if selection.id == last_added_selection {
7653 let range = selection.display_range(&display_map).sorted();
7654 debug_assert_eq!(range.start.row(), range.end.row());
7655 let mut row = range.start.row();
7656 let positions =
7657 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
7658 px(start)..px(end)
7659 } else {
7660 let start_x =
7661 display_map.x_for_display_point(range.start, &text_layout_details);
7662 let end_x =
7663 display_map.x_for_display_point(range.end, &text_layout_details);
7664 start_x.min(end_x)..start_x.max(end_x)
7665 };
7666
7667 while row != end_row {
7668 if above {
7669 row.0 -= 1;
7670 } else {
7671 row.0 += 1;
7672 }
7673
7674 if let Some(new_selection) = self.selections.build_columnar_selection(
7675 &display_map,
7676 row,
7677 &positions,
7678 selection.reversed,
7679 &text_layout_details,
7680 ) {
7681 state.stack.push(new_selection.id);
7682 if above {
7683 new_selections.push(new_selection);
7684 new_selections.push(selection);
7685 } else {
7686 new_selections.push(selection);
7687 new_selections.push(new_selection);
7688 }
7689
7690 continue 'outer;
7691 }
7692 }
7693 }
7694
7695 new_selections.push(selection);
7696 }
7697 } else {
7698 new_selections = selections;
7699 new_selections.retain(|s| s.id != last_added_selection);
7700 state.stack.pop();
7701 }
7702
7703 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7704 s.select(new_selections);
7705 });
7706 if state.stack.len() > 1 {
7707 self.add_selections_state = Some(state);
7708 }
7709 }
7710
7711 pub fn select_next_match_internal(
7712 &mut self,
7713 display_map: &DisplaySnapshot,
7714 replace_newest: bool,
7715 autoscroll: Option<Autoscroll>,
7716 cx: &mut ViewContext<Self>,
7717 ) -> Result<()> {
7718 fn select_next_match_ranges(
7719 this: &mut Editor,
7720 range: Range<usize>,
7721 replace_newest: bool,
7722 auto_scroll: Option<Autoscroll>,
7723 cx: &mut ViewContext<Editor>,
7724 ) {
7725 this.unfold_ranges([range.clone()], false, true, cx);
7726 this.change_selections(auto_scroll, cx, |s| {
7727 if replace_newest {
7728 s.delete(s.newest_anchor().id);
7729 }
7730 s.insert_range(range.clone());
7731 });
7732 }
7733
7734 let buffer = &display_map.buffer_snapshot;
7735 let mut selections = self.selections.all::<usize>(cx);
7736 if let Some(mut select_next_state) = self.select_next_state.take() {
7737 let query = &select_next_state.query;
7738 if !select_next_state.done {
7739 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
7740 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
7741 let mut next_selected_range = None;
7742
7743 let bytes_after_last_selection =
7744 buffer.bytes_in_range(last_selection.end..buffer.len());
7745 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
7746 let query_matches = query
7747 .stream_find_iter(bytes_after_last_selection)
7748 .map(|result| (last_selection.end, result))
7749 .chain(
7750 query
7751 .stream_find_iter(bytes_before_first_selection)
7752 .map(|result| (0, result)),
7753 );
7754
7755 for (start_offset, query_match) in query_matches {
7756 let query_match = query_match.unwrap(); // can only fail due to I/O
7757 let offset_range =
7758 start_offset + query_match.start()..start_offset + query_match.end();
7759 let display_range = offset_range.start.to_display_point(&display_map)
7760 ..offset_range.end.to_display_point(&display_map);
7761
7762 if !select_next_state.wordwise
7763 || (!movement::is_inside_word(&display_map, display_range.start)
7764 && !movement::is_inside_word(&display_map, display_range.end))
7765 {
7766 // TODO: This is n^2, because we might check all the selections
7767 if !selections
7768 .iter()
7769 .any(|selection| selection.range().overlaps(&offset_range))
7770 {
7771 next_selected_range = Some(offset_range);
7772 break;
7773 }
7774 }
7775 }
7776
7777 if let Some(next_selected_range) = next_selected_range {
7778 select_next_match_ranges(
7779 self,
7780 next_selected_range,
7781 replace_newest,
7782 autoscroll,
7783 cx,
7784 );
7785 } else {
7786 select_next_state.done = true;
7787 }
7788 }
7789
7790 self.select_next_state = Some(select_next_state);
7791 } else {
7792 let mut only_carets = true;
7793 let mut same_text_selected = true;
7794 let mut selected_text = None;
7795
7796 let mut selections_iter = selections.iter().peekable();
7797 while let Some(selection) = selections_iter.next() {
7798 if selection.start != selection.end {
7799 only_carets = false;
7800 }
7801
7802 if same_text_selected {
7803 if selected_text.is_none() {
7804 selected_text =
7805 Some(buffer.text_for_range(selection.range()).collect::<String>());
7806 }
7807
7808 if let Some(next_selection) = selections_iter.peek() {
7809 if next_selection.range().len() == selection.range().len() {
7810 let next_selected_text = buffer
7811 .text_for_range(next_selection.range())
7812 .collect::<String>();
7813 if Some(next_selected_text) != selected_text {
7814 same_text_selected = false;
7815 selected_text = None;
7816 }
7817 } else {
7818 same_text_selected = false;
7819 selected_text = None;
7820 }
7821 }
7822 }
7823 }
7824
7825 if only_carets {
7826 for selection in &mut selections {
7827 let word_range = movement::surrounding_word(
7828 &display_map,
7829 selection.start.to_display_point(&display_map),
7830 );
7831 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
7832 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
7833 selection.goal = SelectionGoal::None;
7834 selection.reversed = false;
7835 select_next_match_ranges(
7836 self,
7837 selection.start..selection.end,
7838 replace_newest,
7839 autoscroll,
7840 cx,
7841 );
7842 }
7843
7844 if selections.len() == 1 {
7845 let selection = selections
7846 .last()
7847 .expect("ensured that there's only one selection");
7848 let query = buffer
7849 .text_for_range(selection.start..selection.end)
7850 .collect::<String>();
7851 let is_empty = query.is_empty();
7852 let select_state = SelectNextState {
7853 query: AhoCorasick::new(&[query])?,
7854 wordwise: true,
7855 done: is_empty,
7856 };
7857 self.select_next_state = Some(select_state);
7858 } else {
7859 self.select_next_state = None;
7860 }
7861 } else if let Some(selected_text) = selected_text {
7862 self.select_next_state = Some(SelectNextState {
7863 query: AhoCorasick::new(&[selected_text])?,
7864 wordwise: false,
7865 done: false,
7866 });
7867 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
7868 }
7869 }
7870 Ok(())
7871 }
7872
7873 pub fn select_all_matches(
7874 &mut self,
7875 _action: &SelectAllMatches,
7876 cx: &mut ViewContext<Self>,
7877 ) -> Result<()> {
7878 self.push_to_selection_history();
7879 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7880
7881 self.select_next_match_internal(&display_map, false, None, cx)?;
7882 let Some(select_next_state) = self.select_next_state.as_mut() else {
7883 return Ok(());
7884 };
7885 if select_next_state.done {
7886 return Ok(());
7887 }
7888
7889 let mut new_selections = self.selections.all::<usize>(cx);
7890
7891 let buffer = &display_map.buffer_snapshot;
7892 let query_matches = select_next_state
7893 .query
7894 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
7895
7896 for query_match in query_matches {
7897 let query_match = query_match.unwrap(); // can only fail due to I/O
7898 let offset_range = query_match.start()..query_match.end();
7899 let display_range = offset_range.start.to_display_point(&display_map)
7900 ..offset_range.end.to_display_point(&display_map);
7901
7902 if !select_next_state.wordwise
7903 || (!movement::is_inside_word(&display_map, display_range.start)
7904 && !movement::is_inside_word(&display_map, display_range.end))
7905 {
7906 self.selections.change_with(cx, |selections| {
7907 new_selections.push(Selection {
7908 id: selections.new_selection_id(),
7909 start: offset_range.start,
7910 end: offset_range.end,
7911 reversed: false,
7912 goal: SelectionGoal::None,
7913 });
7914 });
7915 }
7916 }
7917
7918 new_selections.sort_by_key(|selection| selection.start);
7919 let mut ix = 0;
7920 while ix + 1 < new_selections.len() {
7921 let current_selection = &new_selections[ix];
7922 let next_selection = &new_selections[ix + 1];
7923 if current_selection.range().overlaps(&next_selection.range()) {
7924 if current_selection.id < next_selection.id {
7925 new_selections.remove(ix + 1);
7926 } else {
7927 new_selections.remove(ix);
7928 }
7929 } else {
7930 ix += 1;
7931 }
7932 }
7933
7934 select_next_state.done = true;
7935 self.unfold_ranges(
7936 new_selections.iter().map(|selection| selection.range()),
7937 false,
7938 false,
7939 cx,
7940 );
7941 self.change_selections(Some(Autoscroll::fit()), cx, |selections| {
7942 selections.select(new_selections)
7943 });
7944
7945 Ok(())
7946 }
7947
7948 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
7949 self.push_to_selection_history();
7950 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7951 self.select_next_match_internal(
7952 &display_map,
7953 action.replace_newest,
7954 Some(Autoscroll::newest()),
7955 cx,
7956 )?;
7957 Ok(())
7958 }
7959
7960 pub fn select_previous(
7961 &mut self,
7962 action: &SelectPrevious,
7963 cx: &mut ViewContext<Self>,
7964 ) -> Result<()> {
7965 self.push_to_selection_history();
7966 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7967 let buffer = &display_map.buffer_snapshot;
7968 let mut selections = self.selections.all::<usize>(cx);
7969 if let Some(mut select_prev_state) = self.select_prev_state.take() {
7970 let query = &select_prev_state.query;
7971 if !select_prev_state.done {
7972 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
7973 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
7974 let mut next_selected_range = None;
7975 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
7976 let bytes_before_last_selection =
7977 buffer.reversed_bytes_in_range(0..last_selection.start);
7978 let bytes_after_first_selection =
7979 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
7980 let query_matches = query
7981 .stream_find_iter(bytes_before_last_selection)
7982 .map(|result| (last_selection.start, result))
7983 .chain(
7984 query
7985 .stream_find_iter(bytes_after_first_selection)
7986 .map(|result| (buffer.len(), result)),
7987 );
7988 for (end_offset, query_match) in query_matches {
7989 let query_match = query_match.unwrap(); // can only fail due to I/O
7990 let offset_range =
7991 end_offset - query_match.end()..end_offset - query_match.start();
7992 let display_range = offset_range.start.to_display_point(&display_map)
7993 ..offset_range.end.to_display_point(&display_map);
7994
7995 if !select_prev_state.wordwise
7996 || (!movement::is_inside_word(&display_map, display_range.start)
7997 && !movement::is_inside_word(&display_map, display_range.end))
7998 {
7999 next_selected_range = Some(offset_range);
8000 break;
8001 }
8002 }
8003
8004 if let Some(next_selected_range) = next_selected_range {
8005 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
8006 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8007 if action.replace_newest {
8008 s.delete(s.newest_anchor().id);
8009 }
8010 s.insert_range(next_selected_range);
8011 });
8012 } else {
8013 select_prev_state.done = true;
8014 }
8015 }
8016
8017 self.select_prev_state = Some(select_prev_state);
8018 } else {
8019 let mut only_carets = true;
8020 let mut same_text_selected = true;
8021 let mut selected_text = None;
8022
8023 let mut selections_iter = selections.iter().peekable();
8024 while let Some(selection) = selections_iter.next() {
8025 if selection.start != selection.end {
8026 only_carets = false;
8027 }
8028
8029 if same_text_selected {
8030 if selected_text.is_none() {
8031 selected_text =
8032 Some(buffer.text_for_range(selection.range()).collect::<String>());
8033 }
8034
8035 if let Some(next_selection) = selections_iter.peek() {
8036 if next_selection.range().len() == selection.range().len() {
8037 let next_selected_text = buffer
8038 .text_for_range(next_selection.range())
8039 .collect::<String>();
8040 if Some(next_selected_text) != selected_text {
8041 same_text_selected = false;
8042 selected_text = None;
8043 }
8044 } else {
8045 same_text_selected = false;
8046 selected_text = None;
8047 }
8048 }
8049 }
8050 }
8051
8052 if only_carets {
8053 for selection in &mut selections {
8054 let word_range = movement::surrounding_word(
8055 &display_map,
8056 selection.start.to_display_point(&display_map),
8057 );
8058 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
8059 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
8060 selection.goal = SelectionGoal::None;
8061 selection.reversed = false;
8062 }
8063 if selections.len() == 1 {
8064 let selection = selections
8065 .last()
8066 .expect("ensured that there's only one selection");
8067 let query = buffer
8068 .text_for_range(selection.start..selection.end)
8069 .collect::<String>();
8070 let is_empty = query.is_empty();
8071 let select_state = SelectNextState {
8072 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
8073 wordwise: true,
8074 done: is_empty,
8075 };
8076 self.select_prev_state = Some(select_state);
8077 } else {
8078 self.select_prev_state = None;
8079 }
8080
8081 self.unfold_ranges(
8082 selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
8083 false,
8084 true,
8085 cx,
8086 );
8087 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8088 s.select(selections);
8089 });
8090 } else if let Some(selected_text) = selected_text {
8091 self.select_prev_state = Some(SelectNextState {
8092 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
8093 wordwise: false,
8094 done: false,
8095 });
8096 self.select_previous(action, cx)?;
8097 }
8098 }
8099 Ok(())
8100 }
8101
8102 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
8103 let text_layout_details = &self.text_layout_details(cx);
8104 self.transact(cx, |this, cx| {
8105 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
8106 let mut edits = Vec::new();
8107 let mut selection_edit_ranges = Vec::new();
8108 let mut last_toggled_row = None;
8109 let snapshot = this.buffer.read(cx).read(cx);
8110 let empty_str: Arc<str> = "".into();
8111 let mut suffixes_inserted = Vec::new();
8112
8113 fn comment_prefix_range(
8114 snapshot: &MultiBufferSnapshot,
8115 row: MultiBufferRow,
8116 comment_prefix: &str,
8117 comment_prefix_whitespace: &str,
8118 ) -> Range<Point> {
8119 let start = Point::new(row.0, snapshot.indent_size_for_line(row).len);
8120
8121 let mut line_bytes = snapshot
8122 .bytes_in_range(start..snapshot.max_point())
8123 .flatten()
8124 .copied();
8125
8126 // If this line currently begins with the line comment prefix, then record
8127 // the range containing the prefix.
8128 if line_bytes
8129 .by_ref()
8130 .take(comment_prefix.len())
8131 .eq(comment_prefix.bytes())
8132 {
8133 // Include any whitespace that matches the comment prefix.
8134 let matching_whitespace_len = line_bytes
8135 .zip(comment_prefix_whitespace.bytes())
8136 .take_while(|(a, b)| a == b)
8137 .count() as u32;
8138 let end = Point::new(
8139 start.row,
8140 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
8141 );
8142 start..end
8143 } else {
8144 start..start
8145 }
8146 }
8147
8148 fn comment_suffix_range(
8149 snapshot: &MultiBufferSnapshot,
8150 row: MultiBufferRow,
8151 comment_suffix: &str,
8152 comment_suffix_has_leading_space: bool,
8153 ) -> Range<Point> {
8154 let end = Point::new(row.0, snapshot.line_len(row));
8155 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
8156
8157 let mut line_end_bytes = snapshot
8158 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
8159 .flatten()
8160 .copied();
8161
8162 let leading_space_len = if suffix_start_column > 0
8163 && line_end_bytes.next() == Some(b' ')
8164 && comment_suffix_has_leading_space
8165 {
8166 1
8167 } else {
8168 0
8169 };
8170
8171 // If this line currently begins with the line comment prefix, then record
8172 // the range containing the prefix.
8173 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
8174 let start = Point::new(end.row, suffix_start_column - leading_space_len);
8175 start..end
8176 } else {
8177 end..end
8178 }
8179 }
8180
8181 // TODO: Handle selections that cross excerpts
8182 for selection in &mut selections {
8183 let start_column = snapshot
8184 .indent_size_for_line(MultiBufferRow(selection.start.row))
8185 .len;
8186 let language = if let Some(language) =
8187 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
8188 {
8189 language
8190 } else {
8191 continue;
8192 };
8193
8194 selection_edit_ranges.clear();
8195
8196 // If multiple selections contain a given row, avoid processing that
8197 // row more than once.
8198 let mut start_row = MultiBufferRow(selection.start.row);
8199 if last_toggled_row == Some(start_row) {
8200 start_row = start_row.next_row();
8201 }
8202 let end_row =
8203 if selection.end.row > selection.start.row && selection.end.column == 0 {
8204 MultiBufferRow(selection.end.row - 1)
8205 } else {
8206 MultiBufferRow(selection.end.row)
8207 };
8208 last_toggled_row = Some(end_row);
8209
8210 if start_row > end_row {
8211 continue;
8212 }
8213
8214 // If the language has line comments, toggle those.
8215 let full_comment_prefixes = language.line_comment_prefixes();
8216 if !full_comment_prefixes.is_empty() {
8217 let first_prefix = full_comment_prefixes
8218 .first()
8219 .expect("prefixes is non-empty");
8220 let prefix_trimmed_lengths = full_comment_prefixes
8221 .iter()
8222 .map(|p| p.trim_end_matches(' ').len())
8223 .collect::<SmallVec<[usize; 4]>>();
8224
8225 let mut all_selection_lines_are_comments = true;
8226
8227 for row in start_row.0..=end_row.0 {
8228 let row = MultiBufferRow(row);
8229 if start_row < end_row && snapshot.is_line_blank(row) {
8230 continue;
8231 }
8232
8233 let prefix_range = full_comment_prefixes
8234 .iter()
8235 .zip(prefix_trimmed_lengths.iter().copied())
8236 .map(|(prefix, trimmed_prefix_len)| {
8237 comment_prefix_range(
8238 snapshot.deref(),
8239 row,
8240 &prefix[..trimmed_prefix_len],
8241 &prefix[trimmed_prefix_len..],
8242 )
8243 })
8244 .max_by_key(|range| range.end.column - range.start.column)
8245 .expect("prefixes is non-empty");
8246
8247 if prefix_range.is_empty() {
8248 all_selection_lines_are_comments = false;
8249 }
8250
8251 selection_edit_ranges.push(prefix_range);
8252 }
8253
8254 if all_selection_lines_are_comments {
8255 edits.extend(
8256 selection_edit_ranges
8257 .iter()
8258 .cloned()
8259 .map(|range| (range, empty_str.clone())),
8260 );
8261 } else {
8262 let min_column = selection_edit_ranges
8263 .iter()
8264 .map(|range| range.start.column)
8265 .min()
8266 .unwrap_or(0);
8267 edits.extend(selection_edit_ranges.iter().map(|range| {
8268 let position = Point::new(range.start.row, min_column);
8269 (position..position, first_prefix.clone())
8270 }));
8271 }
8272 } else if let Some((full_comment_prefix, comment_suffix)) =
8273 language.block_comment_delimiters()
8274 {
8275 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
8276 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
8277 let prefix_range = comment_prefix_range(
8278 snapshot.deref(),
8279 start_row,
8280 comment_prefix,
8281 comment_prefix_whitespace,
8282 );
8283 let suffix_range = comment_suffix_range(
8284 snapshot.deref(),
8285 end_row,
8286 comment_suffix.trim_start_matches(' '),
8287 comment_suffix.starts_with(' '),
8288 );
8289
8290 if prefix_range.is_empty() || suffix_range.is_empty() {
8291 edits.push((
8292 prefix_range.start..prefix_range.start,
8293 full_comment_prefix.clone(),
8294 ));
8295 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
8296 suffixes_inserted.push((end_row, comment_suffix.len()));
8297 } else {
8298 edits.push((prefix_range, empty_str.clone()));
8299 edits.push((suffix_range, empty_str.clone()));
8300 }
8301 } else {
8302 continue;
8303 }
8304 }
8305
8306 drop(snapshot);
8307 this.buffer.update(cx, |buffer, cx| {
8308 buffer.edit(edits, None, cx);
8309 });
8310
8311 // Adjust selections so that they end before any comment suffixes that
8312 // were inserted.
8313 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
8314 let mut selections = this.selections.all::<Point>(cx);
8315 let snapshot = this.buffer.read(cx).read(cx);
8316 for selection in &mut selections {
8317 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
8318 match row.cmp(&MultiBufferRow(selection.end.row)) {
8319 Ordering::Less => {
8320 suffixes_inserted.next();
8321 continue;
8322 }
8323 Ordering::Greater => break,
8324 Ordering::Equal => {
8325 if selection.end.column == snapshot.line_len(row) {
8326 if selection.is_empty() {
8327 selection.start.column -= suffix_len as u32;
8328 }
8329 selection.end.column -= suffix_len as u32;
8330 }
8331 break;
8332 }
8333 }
8334 }
8335 }
8336
8337 drop(snapshot);
8338 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
8339
8340 let selections = this.selections.all::<Point>(cx);
8341 let selections_on_single_row = selections.windows(2).all(|selections| {
8342 selections[0].start.row == selections[1].start.row
8343 && selections[0].end.row == selections[1].end.row
8344 && selections[0].start.row == selections[0].end.row
8345 });
8346 let selections_selecting = selections
8347 .iter()
8348 .any(|selection| selection.start != selection.end);
8349 let advance_downwards = action.advance_downwards
8350 && selections_on_single_row
8351 && !selections_selecting
8352 && !matches!(this.mode, EditorMode::SingleLine { .. });
8353
8354 if advance_downwards {
8355 let snapshot = this.buffer.read(cx).snapshot(cx);
8356
8357 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8358 s.move_cursors_with(|display_snapshot, display_point, _| {
8359 let mut point = display_point.to_point(display_snapshot);
8360 point.row += 1;
8361 point = snapshot.clip_point(point, Bias::Left);
8362 let display_point = point.to_display_point(display_snapshot);
8363 let goal = SelectionGoal::HorizontalPosition(
8364 display_snapshot
8365 .x_for_display_point(display_point, &text_layout_details)
8366 .into(),
8367 );
8368 (display_point, goal)
8369 })
8370 });
8371 }
8372 });
8373 }
8374
8375 pub fn select_enclosing_symbol(
8376 &mut self,
8377 _: &SelectEnclosingSymbol,
8378 cx: &mut ViewContext<Self>,
8379 ) {
8380 let buffer = self.buffer.read(cx).snapshot(cx);
8381 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8382
8383 fn update_selection(
8384 selection: &Selection<usize>,
8385 buffer_snap: &MultiBufferSnapshot,
8386 ) -> Option<Selection<usize>> {
8387 let cursor = selection.head();
8388 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
8389 for symbol in symbols.iter().rev() {
8390 let start = symbol.range.start.to_offset(&buffer_snap);
8391 let end = symbol.range.end.to_offset(&buffer_snap);
8392 let new_range = start..end;
8393 if start < selection.start || end > selection.end {
8394 return Some(Selection {
8395 id: selection.id,
8396 start: new_range.start,
8397 end: new_range.end,
8398 goal: SelectionGoal::None,
8399 reversed: selection.reversed,
8400 });
8401 }
8402 }
8403 None
8404 }
8405
8406 let mut selected_larger_symbol = false;
8407 let new_selections = old_selections
8408 .iter()
8409 .map(|selection| match update_selection(selection, &buffer) {
8410 Some(new_selection) => {
8411 if new_selection.range() != selection.range() {
8412 selected_larger_symbol = true;
8413 }
8414 new_selection
8415 }
8416 None => selection.clone(),
8417 })
8418 .collect::<Vec<_>>();
8419
8420 if selected_larger_symbol {
8421 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8422 s.select(new_selections);
8423 });
8424 }
8425 }
8426
8427 pub fn select_larger_syntax_node(
8428 &mut self,
8429 _: &SelectLargerSyntaxNode,
8430 cx: &mut ViewContext<Self>,
8431 ) {
8432 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8433 let buffer = self.buffer.read(cx).snapshot(cx);
8434 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8435
8436 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8437 let mut selected_larger_node = false;
8438 let new_selections = old_selections
8439 .iter()
8440 .map(|selection| {
8441 let old_range = selection.start..selection.end;
8442 let mut new_range = old_range.clone();
8443 while let Some(containing_range) =
8444 buffer.range_for_syntax_ancestor(new_range.clone())
8445 {
8446 new_range = containing_range;
8447 if !display_map.intersects_fold(new_range.start)
8448 && !display_map.intersects_fold(new_range.end)
8449 {
8450 break;
8451 }
8452 }
8453
8454 selected_larger_node |= new_range != old_range;
8455 Selection {
8456 id: selection.id,
8457 start: new_range.start,
8458 end: new_range.end,
8459 goal: SelectionGoal::None,
8460 reversed: selection.reversed,
8461 }
8462 })
8463 .collect::<Vec<_>>();
8464
8465 if selected_larger_node {
8466 stack.push(old_selections);
8467 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8468 s.select(new_selections);
8469 });
8470 }
8471 self.select_larger_syntax_node_stack = stack;
8472 }
8473
8474 pub fn select_smaller_syntax_node(
8475 &mut self,
8476 _: &SelectSmallerSyntaxNode,
8477 cx: &mut ViewContext<Self>,
8478 ) {
8479 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8480 if let Some(selections) = stack.pop() {
8481 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8482 s.select(selections.to_vec());
8483 });
8484 }
8485 self.select_larger_syntax_node_stack = stack;
8486 }
8487
8488 fn refresh_runnables(&mut self, cx: &mut ViewContext<Self>) -> Task<()> {
8489 if !EditorSettings::get_global(cx).gutter.runnables {
8490 self.clear_tasks();
8491 return Task::ready(());
8492 }
8493 let project = self.project.clone();
8494 cx.spawn(|this, mut cx| async move {
8495 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
8496 this.display_map.update(cx, |map, cx| map.snapshot(cx))
8497 }) else {
8498 return;
8499 };
8500
8501 let Some(project) = project else {
8502 return;
8503 };
8504
8505 let hide_runnables = project
8506 .update(&mut cx, |project, cx| {
8507 // Do not display any test indicators in non-dev server remote projects.
8508 project.is_remote() && project.ssh_connection_string(cx).is_none()
8509 })
8510 .unwrap_or(true);
8511 if hide_runnables {
8512 return;
8513 }
8514 let new_rows =
8515 cx.background_executor()
8516 .spawn({
8517 let snapshot = display_snapshot.clone();
8518 async move {
8519 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
8520 }
8521 })
8522 .await;
8523 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
8524
8525 this.update(&mut cx, |this, _| {
8526 this.clear_tasks();
8527 for (key, value) in rows {
8528 this.insert_tasks(key, value);
8529 }
8530 })
8531 .ok();
8532 })
8533 }
8534 fn fetch_runnable_ranges(
8535 snapshot: &DisplaySnapshot,
8536 range: Range<Anchor>,
8537 ) -> Vec<language::RunnableRange> {
8538 snapshot.buffer_snapshot.runnable_ranges(range).collect()
8539 }
8540
8541 fn runnable_rows(
8542 project: Model<Project>,
8543 snapshot: DisplaySnapshot,
8544 runnable_ranges: Vec<RunnableRange>,
8545 mut cx: AsyncWindowContext,
8546 ) -> Vec<((BufferId, u32), RunnableTasks)> {
8547 runnable_ranges
8548 .into_iter()
8549 .filter_map(|mut runnable| {
8550 let tasks = cx
8551 .update(|cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
8552 .ok()?;
8553 if tasks.is_empty() {
8554 return None;
8555 }
8556
8557 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
8558
8559 let row = snapshot
8560 .buffer_snapshot
8561 .buffer_line_for_row(MultiBufferRow(point.row))?
8562 .1
8563 .start
8564 .row;
8565
8566 let context_range =
8567 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
8568 Some((
8569 (runnable.buffer_id, row),
8570 RunnableTasks {
8571 templates: tasks,
8572 offset: MultiBufferOffset(runnable.run_range.start),
8573 context_range,
8574 column: point.column,
8575 extra_variables: runnable.extra_captures,
8576 },
8577 ))
8578 })
8579 .collect()
8580 }
8581
8582 fn templates_with_tags(
8583 project: &Model<Project>,
8584 runnable: &mut Runnable,
8585 cx: &WindowContext<'_>,
8586 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
8587 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
8588 let (worktree_id, file) = project
8589 .buffer_for_id(runnable.buffer, cx)
8590 .and_then(|buffer| buffer.read(cx).file())
8591 .map(|file| (WorktreeId::from_usize(file.worktree_id()), file.clone()))
8592 .unzip();
8593
8594 (project.task_inventory().clone(), worktree_id, file)
8595 });
8596
8597 let inventory = inventory.read(cx);
8598 let tags = mem::take(&mut runnable.tags);
8599 let mut tags: Vec<_> = tags
8600 .into_iter()
8601 .flat_map(|tag| {
8602 let tag = tag.0.clone();
8603 inventory
8604 .list_tasks(
8605 file.clone(),
8606 Some(runnable.language.clone()),
8607 worktree_id,
8608 cx,
8609 )
8610 .into_iter()
8611 .filter(move |(_, template)| {
8612 template.tags.iter().any(|source_tag| source_tag == &tag)
8613 })
8614 })
8615 .sorted_by_key(|(kind, _)| kind.to_owned())
8616 .collect();
8617 if let Some((leading_tag_source, _)) = tags.first() {
8618 // Strongest source wins; if we have worktree tag binding, prefer that to
8619 // global and language bindings;
8620 // if we have a global binding, prefer that to language binding.
8621 let first_mismatch = tags
8622 .iter()
8623 .position(|(tag_source, _)| tag_source != leading_tag_source);
8624 if let Some(index) = first_mismatch {
8625 tags.truncate(index);
8626 }
8627 }
8628
8629 tags
8630 }
8631
8632 pub fn move_to_enclosing_bracket(
8633 &mut self,
8634 _: &MoveToEnclosingBracket,
8635 cx: &mut ViewContext<Self>,
8636 ) {
8637 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8638 s.move_offsets_with(|snapshot, selection| {
8639 let Some(enclosing_bracket_ranges) =
8640 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
8641 else {
8642 return;
8643 };
8644
8645 let mut best_length = usize::MAX;
8646 let mut best_inside = false;
8647 let mut best_in_bracket_range = false;
8648 let mut best_destination = None;
8649 for (open, close) in enclosing_bracket_ranges {
8650 let close = close.to_inclusive();
8651 let length = close.end() - open.start;
8652 let inside = selection.start >= open.end && selection.end <= *close.start();
8653 let in_bracket_range = open.to_inclusive().contains(&selection.head())
8654 || close.contains(&selection.head());
8655
8656 // If best is next to a bracket and current isn't, skip
8657 if !in_bracket_range && best_in_bracket_range {
8658 continue;
8659 }
8660
8661 // Prefer smaller lengths unless best is inside and current isn't
8662 if length > best_length && (best_inside || !inside) {
8663 continue;
8664 }
8665
8666 best_length = length;
8667 best_inside = inside;
8668 best_in_bracket_range = in_bracket_range;
8669 best_destination = Some(
8670 if close.contains(&selection.start) && close.contains(&selection.end) {
8671 if inside {
8672 open.end
8673 } else {
8674 open.start
8675 }
8676 } else {
8677 if inside {
8678 *close.start()
8679 } else {
8680 *close.end()
8681 }
8682 },
8683 );
8684 }
8685
8686 if let Some(destination) = best_destination {
8687 selection.collapse_to(destination, SelectionGoal::None);
8688 }
8689 })
8690 });
8691 }
8692
8693 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
8694 self.end_selection(cx);
8695 self.selection_history.mode = SelectionHistoryMode::Undoing;
8696 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
8697 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8698 self.select_next_state = entry.select_next_state;
8699 self.select_prev_state = entry.select_prev_state;
8700 self.add_selections_state = entry.add_selections_state;
8701 self.request_autoscroll(Autoscroll::newest(), cx);
8702 }
8703 self.selection_history.mode = SelectionHistoryMode::Normal;
8704 }
8705
8706 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
8707 self.end_selection(cx);
8708 self.selection_history.mode = SelectionHistoryMode::Redoing;
8709 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
8710 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8711 self.select_next_state = entry.select_next_state;
8712 self.select_prev_state = entry.select_prev_state;
8713 self.add_selections_state = entry.add_selections_state;
8714 self.request_autoscroll(Autoscroll::newest(), cx);
8715 }
8716 self.selection_history.mode = SelectionHistoryMode::Normal;
8717 }
8718
8719 pub fn expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
8720 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
8721 }
8722
8723 pub fn expand_excerpts_down(
8724 &mut self,
8725 action: &ExpandExcerptsDown,
8726 cx: &mut ViewContext<Self>,
8727 ) {
8728 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
8729 }
8730
8731 pub fn expand_excerpts_up(&mut self, action: &ExpandExcerptsUp, cx: &mut ViewContext<Self>) {
8732 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
8733 }
8734
8735 pub fn expand_excerpts_for_direction(
8736 &mut self,
8737 lines: u32,
8738 direction: ExpandExcerptDirection,
8739 cx: &mut ViewContext<Self>,
8740 ) {
8741 let selections = self.selections.disjoint_anchors();
8742
8743 let lines = if lines == 0 {
8744 EditorSettings::get_global(cx).expand_excerpt_lines
8745 } else {
8746 lines
8747 };
8748
8749 self.buffer.update(cx, |buffer, cx| {
8750 buffer.expand_excerpts(
8751 selections
8752 .into_iter()
8753 .map(|selection| selection.head().excerpt_id)
8754 .dedup(),
8755 lines,
8756 direction,
8757 cx,
8758 )
8759 })
8760 }
8761
8762 pub fn expand_excerpt(
8763 &mut self,
8764 excerpt: ExcerptId,
8765 direction: ExpandExcerptDirection,
8766 cx: &mut ViewContext<Self>,
8767 ) {
8768 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
8769 self.buffer.update(cx, |buffer, cx| {
8770 buffer.expand_excerpts([excerpt], lines, direction, cx)
8771 })
8772 }
8773
8774 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
8775 self.go_to_diagnostic_impl(Direction::Next, cx)
8776 }
8777
8778 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
8779 self.go_to_diagnostic_impl(Direction::Prev, cx)
8780 }
8781
8782 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
8783 let buffer = self.buffer.read(cx).snapshot(cx);
8784 let selection = self.selections.newest::<usize>(cx);
8785
8786 // If there is an active Diagnostic Popover jump to its diagnostic instead.
8787 if direction == Direction::Next {
8788 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
8789 let (group_id, jump_to) = popover.activation_info();
8790 if self.activate_diagnostics(group_id, cx) {
8791 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8792 let mut new_selection = s.newest_anchor().clone();
8793 new_selection.collapse_to(jump_to, SelectionGoal::None);
8794 s.select_anchors(vec![new_selection.clone()]);
8795 });
8796 }
8797 return;
8798 }
8799 }
8800
8801 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
8802 active_diagnostics
8803 .primary_range
8804 .to_offset(&buffer)
8805 .to_inclusive()
8806 });
8807 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
8808 if active_primary_range.contains(&selection.head()) {
8809 *active_primary_range.start()
8810 } else {
8811 selection.head()
8812 }
8813 } else {
8814 selection.head()
8815 };
8816 let snapshot = self.snapshot(cx);
8817 loop {
8818 let diagnostics = if direction == Direction::Prev {
8819 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
8820 } else {
8821 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
8822 }
8823 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start));
8824 let group = diagnostics
8825 // relies on diagnostics_in_range to return diagnostics with the same starting range to
8826 // be sorted in a stable way
8827 // skip until we are at current active diagnostic, if it exists
8828 .skip_while(|entry| {
8829 (match direction {
8830 Direction::Prev => entry.range.start >= search_start,
8831 Direction::Next => entry.range.start <= search_start,
8832 }) && self
8833 .active_diagnostics
8834 .as_ref()
8835 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
8836 })
8837 .find_map(|entry| {
8838 if entry.diagnostic.is_primary
8839 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
8840 && !entry.range.is_empty()
8841 // if we match with the active diagnostic, skip it
8842 && Some(entry.diagnostic.group_id)
8843 != self.active_diagnostics.as_ref().map(|d| d.group_id)
8844 {
8845 Some((entry.range, entry.diagnostic.group_id))
8846 } else {
8847 None
8848 }
8849 });
8850
8851 if let Some((primary_range, group_id)) = group {
8852 if self.activate_diagnostics(group_id, cx) {
8853 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8854 s.select(vec![Selection {
8855 id: selection.id,
8856 start: primary_range.start,
8857 end: primary_range.start,
8858 reversed: false,
8859 goal: SelectionGoal::None,
8860 }]);
8861 });
8862 }
8863 break;
8864 } else {
8865 // Cycle around to the start of the buffer, potentially moving back to the start of
8866 // the currently active diagnostic.
8867 active_primary_range.take();
8868 if direction == Direction::Prev {
8869 if search_start == buffer.len() {
8870 break;
8871 } else {
8872 search_start = buffer.len();
8873 }
8874 } else if search_start == 0 {
8875 break;
8876 } else {
8877 search_start = 0;
8878 }
8879 }
8880 }
8881 }
8882
8883 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
8884 let snapshot = self
8885 .display_map
8886 .update(cx, |display_map, cx| display_map.snapshot(cx));
8887 let selection = self.selections.newest::<Point>(cx);
8888
8889 if !self.seek_in_direction(
8890 &snapshot,
8891 selection.head(),
8892 false,
8893 snapshot.buffer_snapshot.git_diff_hunks_in_range(
8894 MultiBufferRow(selection.head().row + 1)..MultiBufferRow::MAX,
8895 ),
8896 cx,
8897 ) {
8898 let wrapped_point = Point::zero();
8899 self.seek_in_direction(
8900 &snapshot,
8901 wrapped_point,
8902 true,
8903 snapshot.buffer_snapshot.git_diff_hunks_in_range(
8904 MultiBufferRow(wrapped_point.row + 1)..MultiBufferRow::MAX,
8905 ),
8906 cx,
8907 );
8908 }
8909 }
8910
8911 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
8912 let snapshot = self
8913 .display_map
8914 .update(cx, |display_map, cx| display_map.snapshot(cx));
8915 let selection = self.selections.newest::<Point>(cx);
8916
8917 if !self.seek_in_direction(
8918 &snapshot,
8919 selection.head(),
8920 false,
8921 snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
8922 MultiBufferRow(0)..MultiBufferRow(selection.head().row),
8923 ),
8924 cx,
8925 ) {
8926 let wrapped_point = snapshot.buffer_snapshot.max_point();
8927 self.seek_in_direction(
8928 &snapshot,
8929 wrapped_point,
8930 true,
8931 snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
8932 MultiBufferRow(0)..MultiBufferRow(wrapped_point.row),
8933 ),
8934 cx,
8935 );
8936 }
8937 }
8938
8939 fn seek_in_direction(
8940 &mut self,
8941 snapshot: &DisplaySnapshot,
8942 initial_point: Point,
8943 is_wrapped: bool,
8944 hunks: impl Iterator<Item = DiffHunk<MultiBufferRow>>,
8945 cx: &mut ViewContext<Editor>,
8946 ) -> bool {
8947 let display_point = initial_point.to_display_point(snapshot);
8948 let mut hunks = hunks
8949 .map(|hunk| diff_hunk_to_display(&hunk, &snapshot))
8950 .filter(|hunk| is_wrapped || !hunk.contains_display_row(display_point.row()))
8951 .dedup();
8952
8953 if let Some(hunk) = hunks.next() {
8954 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8955 let row = hunk.start_display_row();
8956 let point = DisplayPoint::new(row, 0);
8957 s.select_display_ranges([point..point]);
8958 });
8959
8960 true
8961 } else {
8962 false
8963 }
8964 }
8965
8966 pub fn go_to_definition(
8967 &mut self,
8968 _: &GoToDefinition,
8969 cx: &mut ViewContext<Self>,
8970 ) -> Task<Result<bool>> {
8971 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx)
8972 }
8973
8974 pub fn go_to_implementation(
8975 &mut self,
8976 _: &GoToImplementation,
8977 cx: &mut ViewContext<Self>,
8978 ) -> Task<Result<bool>> {
8979 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx)
8980 }
8981
8982 pub fn go_to_implementation_split(
8983 &mut self,
8984 _: &GoToImplementationSplit,
8985 cx: &mut ViewContext<Self>,
8986 ) -> Task<Result<bool>> {
8987 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx)
8988 }
8989
8990 pub fn go_to_type_definition(
8991 &mut self,
8992 _: &GoToTypeDefinition,
8993 cx: &mut ViewContext<Self>,
8994 ) -> Task<Result<bool>> {
8995 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx)
8996 }
8997
8998 pub fn go_to_definition_split(
8999 &mut self,
9000 _: &GoToDefinitionSplit,
9001 cx: &mut ViewContext<Self>,
9002 ) -> Task<Result<bool>> {
9003 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx)
9004 }
9005
9006 pub fn go_to_type_definition_split(
9007 &mut self,
9008 _: &GoToTypeDefinitionSplit,
9009 cx: &mut ViewContext<Self>,
9010 ) -> Task<Result<bool>> {
9011 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx)
9012 }
9013
9014 fn go_to_definition_of_kind(
9015 &mut self,
9016 kind: GotoDefinitionKind,
9017 split: bool,
9018 cx: &mut ViewContext<Self>,
9019 ) -> Task<Result<bool>> {
9020 let Some(workspace) = self.workspace() else {
9021 return Task::ready(Ok(false));
9022 };
9023 let buffer = self.buffer.read(cx);
9024 let head = self.selections.newest::<usize>(cx).head();
9025 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
9026 text_anchor
9027 } else {
9028 return Task::ready(Ok(false));
9029 };
9030
9031 let project = workspace.read(cx).project().clone();
9032 let definitions = project.update(cx, |project, cx| match kind {
9033 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
9034 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
9035 GotoDefinitionKind::Implementation => project.implementation(&buffer, head, cx),
9036 });
9037
9038 cx.spawn(|editor, mut cx| async move {
9039 let definitions = definitions.await?;
9040 let navigated = editor
9041 .update(&mut cx, |editor, cx| {
9042 editor.navigate_to_hover_links(
9043 Some(kind),
9044 definitions
9045 .into_iter()
9046 .filter(|location| {
9047 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
9048 })
9049 .map(HoverLink::Text)
9050 .collect::<Vec<_>>(),
9051 split,
9052 cx,
9053 )
9054 })?
9055 .await?;
9056 anyhow::Ok(navigated)
9057 })
9058 }
9059
9060 pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext<Self>) {
9061 let position = self.selections.newest_anchor().head();
9062 let Some((buffer, buffer_position)) =
9063 self.buffer.read(cx).text_anchor_for_position(position, cx)
9064 else {
9065 return;
9066 };
9067
9068 cx.spawn(|editor, mut cx| async move {
9069 if let Some((_, url)) = find_url(&buffer, buffer_position, cx.clone()) {
9070 editor.update(&mut cx, |_, cx| {
9071 cx.open_url(&url);
9072 })
9073 } else {
9074 Ok(())
9075 }
9076 })
9077 .detach();
9078 }
9079
9080 pub(crate) fn navigate_to_hover_links(
9081 &mut self,
9082 kind: Option<GotoDefinitionKind>,
9083 mut definitions: Vec<HoverLink>,
9084 split: bool,
9085 cx: &mut ViewContext<Editor>,
9086 ) -> Task<Result<bool>> {
9087 // If there is one definition, just open it directly
9088 if definitions.len() == 1 {
9089 let definition = definitions.pop().unwrap();
9090 let target_task = match definition {
9091 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
9092 HoverLink::InlayHint(lsp_location, server_id) => {
9093 self.compute_target_location(lsp_location, server_id, cx)
9094 }
9095 HoverLink::Url(url) => {
9096 cx.open_url(&url);
9097 Task::ready(Ok(None))
9098 }
9099 };
9100 cx.spawn(|editor, mut cx| async move {
9101 let target = target_task.await.context("target resolution task")?;
9102 if let Some(target) = target {
9103 editor.update(&mut cx, |editor, cx| {
9104 let Some(workspace) = editor.workspace() else {
9105 return false;
9106 };
9107 let pane = workspace.read(cx).active_pane().clone();
9108
9109 let range = target.range.to_offset(target.buffer.read(cx));
9110 let range = editor.range_for_match(&range);
9111
9112 /// If select range has more than one line, we
9113 /// just point the cursor to range.start.
9114 fn check_multiline_range(
9115 buffer: &Buffer,
9116 range: Range<usize>,
9117 ) -> Range<usize> {
9118 if buffer.offset_to_point(range.start).row
9119 == buffer.offset_to_point(range.end).row
9120 {
9121 range
9122 } else {
9123 range.start..range.start
9124 }
9125 }
9126
9127 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
9128 let buffer = target.buffer.read(cx);
9129 let range = check_multiline_range(buffer, range);
9130 editor.change_selections(Some(Autoscroll::focused()), cx, |s| {
9131 s.select_ranges([range]);
9132 });
9133 } else {
9134 cx.window_context().defer(move |cx| {
9135 let target_editor: View<Self> =
9136 workspace.update(cx, |workspace, cx| {
9137 let pane = if split {
9138 workspace.adjacent_pane(cx)
9139 } else {
9140 workspace.active_pane().clone()
9141 };
9142
9143 workspace.open_project_item(
9144 pane,
9145 target.buffer.clone(),
9146 true,
9147 true,
9148 cx,
9149 )
9150 });
9151 target_editor.update(cx, |target_editor, cx| {
9152 // When selecting a definition in a different buffer, disable the nav history
9153 // to avoid creating a history entry at the previous cursor location.
9154 pane.update(cx, |pane, _| pane.disable_history());
9155 let buffer = target.buffer.read(cx);
9156 let range = check_multiline_range(buffer, range);
9157 target_editor.change_selections(
9158 Some(Autoscroll::focused()),
9159 cx,
9160 |s| {
9161 s.select_ranges([range]);
9162 },
9163 );
9164 pane.update(cx, |pane, _| pane.enable_history());
9165 });
9166 });
9167 }
9168 true
9169 })
9170 } else {
9171 Ok(false)
9172 }
9173 })
9174 } else if !definitions.is_empty() {
9175 let replica_id = self.replica_id(cx);
9176 cx.spawn(|editor, mut cx| async move {
9177 let (title, location_tasks, workspace) = editor
9178 .update(&mut cx, |editor, cx| {
9179 let tab_kind = match kind {
9180 Some(GotoDefinitionKind::Implementation) => "Implementations",
9181 _ => "Definitions",
9182 };
9183 let title = definitions
9184 .iter()
9185 .find_map(|definition| match definition {
9186 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
9187 let buffer = origin.buffer.read(cx);
9188 format!(
9189 "{} for {}",
9190 tab_kind,
9191 buffer
9192 .text_for_range(origin.range.clone())
9193 .collect::<String>()
9194 )
9195 }),
9196 HoverLink::InlayHint(_, _) => None,
9197 HoverLink::Url(_) => None,
9198 })
9199 .unwrap_or(tab_kind.to_string());
9200 let location_tasks = definitions
9201 .into_iter()
9202 .map(|definition| match definition {
9203 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
9204 HoverLink::InlayHint(lsp_location, server_id) => {
9205 editor.compute_target_location(lsp_location, server_id, cx)
9206 }
9207 HoverLink::Url(_) => Task::ready(Ok(None)),
9208 })
9209 .collect::<Vec<_>>();
9210 (title, location_tasks, editor.workspace().clone())
9211 })
9212 .context("location tasks preparation")?;
9213
9214 let locations = futures::future::join_all(location_tasks)
9215 .await
9216 .into_iter()
9217 .filter_map(|location| location.transpose())
9218 .collect::<Result<_>>()
9219 .context("location tasks")?;
9220
9221 let Some(workspace) = workspace else {
9222 return Ok(false);
9223 };
9224 let opened = workspace
9225 .update(&mut cx, |workspace, cx| {
9226 Self::open_locations_in_multibuffer(
9227 workspace, locations, replica_id, title, split, cx,
9228 )
9229 })
9230 .ok();
9231
9232 anyhow::Ok(opened.is_some())
9233 })
9234 } else {
9235 Task::ready(Ok(false))
9236 }
9237 }
9238
9239 fn compute_target_location(
9240 &self,
9241 lsp_location: lsp::Location,
9242 server_id: LanguageServerId,
9243 cx: &mut ViewContext<Editor>,
9244 ) -> Task<anyhow::Result<Option<Location>>> {
9245 let Some(project) = self.project.clone() else {
9246 return Task::Ready(Some(Ok(None)));
9247 };
9248
9249 cx.spawn(move |editor, mut cx| async move {
9250 let location_task = editor.update(&mut cx, |editor, cx| {
9251 project.update(cx, |project, cx| {
9252 let language_server_name =
9253 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
9254 project
9255 .language_server_for_buffer(buffer.read(cx), server_id, cx)
9256 .map(|(lsp_adapter, _)| lsp_adapter.name.clone())
9257 });
9258 language_server_name.map(|language_server_name| {
9259 project.open_local_buffer_via_lsp(
9260 lsp_location.uri.clone(),
9261 server_id,
9262 language_server_name,
9263 cx,
9264 )
9265 })
9266 })
9267 })?;
9268 let location = match location_task {
9269 Some(task) => Some({
9270 let target_buffer_handle = task.await.context("open local buffer")?;
9271 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
9272 let target_start = target_buffer
9273 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
9274 let target_end = target_buffer
9275 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
9276 target_buffer.anchor_after(target_start)
9277 ..target_buffer.anchor_before(target_end)
9278 })?;
9279 Location {
9280 buffer: target_buffer_handle,
9281 range,
9282 }
9283 }),
9284 None => None,
9285 };
9286 Ok(location)
9287 })
9288 }
9289
9290 pub fn find_all_references(
9291 &mut self,
9292 _: &FindAllReferences,
9293 cx: &mut ViewContext<Self>,
9294 ) -> Option<Task<Result<()>>> {
9295 let multi_buffer = self.buffer.read(cx);
9296 let selection = self.selections.newest::<usize>(cx);
9297 let head = selection.head();
9298
9299 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
9300 let head_anchor = multi_buffer_snapshot.anchor_at(
9301 head,
9302 if head < selection.tail() {
9303 Bias::Right
9304 } else {
9305 Bias::Left
9306 },
9307 );
9308
9309 match self
9310 .find_all_references_task_sources
9311 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
9312 {
9313 Ok(_) => {
9314 log::info!(
9315 "Ignoring repeated FindAllReferences invocation with the position of already running task"
9316 );
9317 return None;
9318 }
9319 Err(i) => {
9320 self.find_all_references_task_sources.insert(i, head_anchor);
9321 }
9322 }
9323
9324 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
9325 let replica_id = self.replica_id(cx);
9326 let workspace = self.workspace()?;
9327 let project = workspace.read(cx).project().clone();
9328 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
9329 Some(cx.spawn(|editor, mut cx| async move {
9330 let _cleanup = defer({
9331 let mut cx = cx.clone();
9332 move || {
9333 let _ = editor.update(&mut cx, |editor, _| {
9334 if let Ok(i) =
9335 editor
9336 .find_all_references_task_sources
9337 .binary_search_by(|anchor| {
9338 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
9339 })
9340 {
9341 editor.find_all_references_task_sources.remove(i);
9342 }
9343 });
9344 }
9345 });
9346
9347 let locations = references.await?;
9348 if locations.is_empty() {
9349 return anyhow::Ok(());
9350 }
9351
9352 workspace.update(&mut cx, |workspace, cx| {
9353 let title = locations
9354 .first()
9355 .as_ref()
9356 .map(|location| {
9357 let buffer = location.buffer.read(cx);
9358 format!(
9359 "References to `{}`",
9360 buffer
9361 .text_for_range(location.range.clone())
9362 .collect::<String>()
9363 )
9364 })
9365 .unwrap();
9366 Self::open_locations_in_multibuffer(
9367 workspace, locations, replica_id, title, false, cx,
9368 );
9369 })
9370 }))
9371 }
9372
9373 /// Opens a multibuffer with the given project locations in it
9374 pub fn open_locations_in_multibuffer(
9375 workspace: &mut Workspace,
9376 mut locations: Vec<Location>,
9377 replica_id: ReplicaId,
9378 title: String,
9379 split: bool,
9380 cx: &mut ViewContext<Workspace>,
9381 ) {
9382 // If there are multiple definitions, open them in a multibuffer
9383 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
9384 let mut locations = locations.into_iter().peekable();
9385 let mut ranges_to_highlight = Vec::new();
9386 let capability = workspace.project().read(cx).capability();
9387
9388 let excerpt_buffer = cx.new_model(|cx| {
9389 let mut multibuffer = MultiBuffer::new(replica_id, capability);
9390 while let Some(location) = locations.next() {
9391 let buffer = location.buffer.read(cx);
9392 let mut ranges_for_buffer = Vec::new();
9393 let range = location.range.to_offset(buffer);
9394 ranges_for_buffer.push(range.clone());
9395
9396 while let Some(next_location) = locations.peek() {
9397 if next_location.buffer == location.buffer {
9398 ranges_for_buffer.push(next_location.range.to_offset(buffer));
9399 locations.next();
9400 } else {
9401 break;
9402 }
9403 }
9404
9405 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
9406 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
9407 location.buffer.clone(),
9408 ranges_for_buffer,
9409 DEFAULT_MULTIBUFFER_CONTEXT,
9410 cx,
9411 ))
9412 }
9413
9414 multibuffer.with_title(title)
9415 });
9416
9417 let editor = cx.new_view(|cx| {
9418 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), true, cx)
9419 });
9420 editor.update(cx, |editor, cx| {
9421 if let Some(first_range) = ranges_to_highlight.first() {
9422 editor.change_selections(None, cx, |selections| {
9423 selections.clear_disjoint();
9424 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
9425 });
9426 }
9427 editor.highlight_background::<Self>(
9428 &ranges_to_highlight,
9429 |theme| theme.editor_highlighted_line_background,
9430 cx,
9431 );
9432 });
9433
9434 let item = Box::new(editor);
9435 let item_id = item.item_id();
9436
9437 if split {
9438 workspace.split_item(SplitDirection::Right, item.clone(), cx);
9439 } else {
9440 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
9441 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
9442 pane.close_current_preview_item(cx)
9443 } else {
9444 None
9445 }
9446 });
9447 workspace.add_item_to_active_pane(item.clone(), destination_index, true, cx);
9448 }
9449 workspace.active_pane().update(cx, |pane, cx| {
9450 pane.set_preview_item_id(Some(item_id), cx);
9451 });
9452 }
9453
9454 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9455 use language::ToOffset as _;
9456
9457 let project = self.project.clone()?;
9458 let selection = self.selections.newest_anchor().clone();
9459 let (cursor_buffer, cursor_buffer_position) = self
9460 .buffer
9461 .read(cx)
9462 .text_anchor_for_position(selection.head(), cx)?;
9463 let (tail_buffer, cursor_buffer_position_end) = self
9464 .buffer
9465 .read(cx)
9466 .text_anchor_for_position(selection.tail(), cx)?;
9467 if tail_buffer != cursor_buffer {
9468 return None;
9469 }
9470
9471 let snapshot = cursor_buffer.read(cx).snapshot();
9472 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
9473 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
9474 let prepare_rename = project.update(cx, |project, cx| {
9475 project.prepare_rename(cursor_buffer.clone(), cursor_buffer_offset, cx)
9476 });
9477 drop(snapshot);
9478
9479 Some(cx.spawn(|this, mut cx| async move {
9480 let rename_range = if let Some(range) = prepare_rename.await? {
9481 Some(range)
9482 } else {
9483 this.update(&mut cx, |this, cx| {
9484 let buffer = this.buffer.read(cx).snapshot(cx);
9485 let mut buffer_highlights = this
9486 .document_highlights_for_position(selection.head(), &buffer)
9487 .filter(|highlight| {
9488 highlight.start.excerpt_id == selection.head().excerpt_id
9489 && highlight.end.excerpt_id == selection.head().excerpt_id
9490 });
9491 buffer_highlights
9492 .next()
9493 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
9494 })?
9495 };
9496 if let Some(rename_range) = rename_range {
9497 this.update(&mut cx, |this, cx| {
9498 let snapshot = cursor_buffer.read(cx).snapshot();
9499 let rename_buffer_range = rename_range.to_offset(&snapshot);
9500 let cursor_offset_in_rename_range =
9501 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
9502 let cursor_offset_in_rename_range_end =
9503 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
9504
9505 this.take_rename(false, cx);
9506 let buffer = this.buffer.read(cx).read(cx);
9507 let cursor_offset = selection.head().to_offset(&buffer);
9508 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
9509 let rename_end = rename_start + rename_buffer_range.len();
9510 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
9511 let mut old_highlight_id = None;
9512 let old_name: Arc<str> = buffer
9513 .chunks(rename_start..rename_end, true)
9514 .map(|chunk| {
9515 if old_highlight_id.is_none() {
9516 old_highlight_id = chunk.syntax_highlight_id;
9517 }
9518 chunk.text
9519 })
9520 .collect::<String>()
9521 .into();
9522
9523 drop(buffer);
9524
9525 // Position the selection in the rename editor so that it matches the current selection.
9526 this.show_local_selections = false;
9527 let rename_editor = cx.new_view(|cx| {
9528 let mut editor = Editor::single_line(cx);
9529 editor.buffer.update(cx, |buffer, cx| {
9530 buffer.edit([(0..0, old_name.clone())], None, cx)
9531 });
9532 let rename_selection_range = match cursor_offset_in_rename_range
9533 .cmp(&cursor_offset_in_rename_range_end)
9534 {
9535 Ordering::Equal => {
9536 editor.select_all(&SelectAll, cx);
9537 return editor;
9538 }
9539 Ordering::Less => {
9540 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
9541 }
9542 Ordering::Greater => {
9543 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
9544 }
9545 };
9546 if rename_selection_range.end > old_name.len() {
9547 editor.select_all(&SelectAll, cx);
9548 } else {
9549 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
9550 s.select_ranges([rename_selection_range]);
9551 });
9552 }
9553 editor
9554 });
9555 cx.subscribe(&rename_editor, |_, _, e, cx| match e {
9556 EditorEvent::Focused => cx.emit(EditorEvent::FocusedIn),
9557 _ => {}
9558 })
9559 .detach();
9560
9561 let write_highlights =
9562 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
9563 let read_highlights =
9564 this.clear_background_highlights::<DocumentHighlightRead>(cx);
9565 let ranges = write_highlights
9566 .iter()
9567 .flat_map(|(_, ranges)| ranges.iter())
9568 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
9569 .cloned()
9570 .collect();
9571
9572 this.highlight_text::<Rename>(
9573 ranges,
9574 HighlightStyle {
9575 fade_out: Some(0.6),
9576 ..Default::default()
9577 },
9578 cx,
9579 );
9580 let rename_focus_handle = rename_editor.focus_handle(cx);
9581 cx.focus(&rename_focus_handle);
9582 let block_id = this.insert_blocks(
9583 [BlockProperties {
9584 style: BlockStyle::Flex,
9585 position: range.start,
9586 height: 1,
9587 render: Box::new({
9588 let rename_editor = rename_editor.clone();
9589 move |cx: &mut BlockContext| {
9590 let mut text_style = cx.editor_style.text.clone();
9591 if let Some(highlight_style) = old_highlight_id
9592 .and_then(|h| h.style(&cx.editor_style.syntax))
9593 {
9594 text_style = text_style.highlight(highlight_style);
9595 }
9596 div()
9597 .pl(cx.anchor_x)
9598 .child(EditorElement::new(
9599 &rename_editor,
9600 EditorStyle {
9601 background: cx.theme().system().transparent,
9602 local_player: cx.editor_style.local_player,
9603 text: text_style,
9604 scrollbar_width: cx.editor_style.scrollbar_width,
9605 syntax: cx.editor_style.syntax.clone(),
9606 status: cx.editor_style.status.clone(),
9607 inlay_hints_style: HighlightStyle {
9608 color: Some(cx.theme().status().hint),
9609 font_weight: Some(FontWeight::BOLD),
9610 ..HighlightStyle::default()
9611 },
9612 suggestions_style: HighlightStyle {
9613 color: Some(cx.theme().status().predictive),
9614 ..HighlightStyle::default()
9615 },
9616 },
9617 ))
9618 .into_any_element()
9619 }
9620 }),
9621 disposition: BlockDisposition::Below,
9622 }],
9623 Some(Autoscroll::fit()),
9624 cx,
9625 )[0];
9626 this.pending_rename = Some(RenameState {
9627 range,
9628 old_name,
9629 editor: rename_editor,
9630 block_id,
9631 });
9632 })?;
9633 }
9634
9635 Ok(())
9636 }))
9637 }
9638
9639 pub fn confirm_rename(
9640 &mut self,
9641 _: &ConfirmRename,
9642 cx: &mut ViewContext<Self>,
9643 ) -> Option<Task<Result<()>>> {
9644 let rename = self.take_rename(false, cx)?;
9645 let workspace = self.workspace()?;
9646 let (start_buffer, start) = self
9647 .buffer
9648 .read(cx)
9649 .text_anchor_for_position(rename.range.start, cx)?;
9650 let (end_buffer, end) = self
9651 .buffer
9652 .read(cx)
9653 .text_anchor_for_position(rename.range.end, cx)?;
9654 if start_buffer != end_buffer {
9655 return None;
9656 }
9657
9658 let buffer = start_buffer;
9659 let range = start..end;
9660 let old_name = rename.old_name;
9661 let new_name = rename.editor.read(cx).text(cx);
9662
9663 let rename = workspace
9664 .read(cx)
9665 .project()
9666 .clone()
9667 .update(cx, |project, cx| {
9668 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
9669 });
9670 let workspace = workspace.downgrade();
9671
9672 Some(cx.spawn(|editor, mut cx| async move {
9673 let project_transaction = rename.await?;
9674 Self::open_project_transaction(
9675 &editor,
9676 workspace,
9677 project_transaction,
9678 format!("Rename: {} → {}", old_name, new_name),
9679 cx.clone(),
9680 )
9681 .await?;
9682
9683 editor.update(&mut cx, |editor, cx| {
9684 editor.refresh_document_highlights(cx);
9685 })?;
9686 Ok(())
9687 }))
9688 }
9689
9690 fn take_rename(
9691 &mut self,
9692 moving_cursor: bool,
9693 cx: &mut ViewContext<Self>,
9694 ) -> Option<RenameState> {
9695 let rename = self.pending_rename.take()?;
9696 if rename.editor.focus_handle(cx).is_focused(cx) {
9697 cx.focus(&self.focus_handle);
9698 }
9699
9700 self.remove_blocks(
9701 [rename.block_id].into_iter().collect(),
9702 Some(Autoscroll::fit()),
9703 cx,
9704 );
9705 self.clear_highlights::<Rename>(cx);
9706 self.show_local_selections = true;
9707
9708 if moving_cursor {
9709 let rename_editor = rename.editor.read(cx);
9710 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
9711
9712 // Update the selection to match the position of the selection inside
9713 // the rename editor.
9714 let snapshot = self.buffer.read(cx).read(cx);
9715 let rename_range = rename.range.to_offset(&snapshot);
9716 let cursor_in_editor = snapshot
9717 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
9718 .min(rename_range.end);
9719 drop(snapshot);
9720
9721 self.change_selections(None, cx, |s| {
9722 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
9723 });
9724 } else {
9725 self.refresh_document_highlights(cx);
9726 }
9727
9728 Some(rename)
9729 }
9730
9731 pub fn pending_rename(&self) -> Option<&RenameState> {
9732 self.pending_rename.as_ref()
9733 }
9734
9735 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9736 let project = match &self.project {
9737 Some(project) => project.clone(),
9738 None => return None,
9739 };
9740
9741 Some(self.perform_format(project, FormatTrigger::Manual, cx))
9742 }
9743
9744 fn perform_format(
9745 &mut self,
9746 project: Model<Project>,
9747 trigger: FormatTrigger,
9748 cx: &mut ViewContext<Self>,
9749 ) -> Task<Result<()>> {
9750 let buffer = self.buffer().clone();
9751 let mut buffers = buffer.read(cx).all_buffers();
9752 if trigger == FormatTrigger::Save {
9753 buffers.retain(|buffer| buffer.read(cx).is_dirty());
9754 }
9755
9756 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
9757 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
9758
9759 cx.spawn(|_, mut cx| async move {
9760 let transaction = futures::select_biased! {
9761 () = timeout => {
9762 log::warn!("timed out waiting for formatting");
9763 None
9764 }
9765 transaction = format.log_err().fuse() => transaction,
9766 };
9767
9768 buffer
9769 .update(&mut cx, |buffer, cx| {
9770 if let Some(transaction) = transaction {
9771 if !buffer.is_singleton() {
9772 buffer.push_transaction(&transaction.0, cx);
9773 }
9774 }
9775
9776 cx.notify();
9777 })
9778 .ok();
9779
9780 Ok(())
9781 })
9782 }
9783
9784 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
9785 if let Some(project) = self.project.clone() {
9786 self.buffer.update(cx, |multi_buffer, cx| {
9787 project.update(cx, |project, cx| {
9788 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
9789 });
9790 })
9791 }
9792 }
9793
9794 fn cancel_language_server_work(
9795 &mut self,
9796 _: &CancelLanguageServerWork,
9797 cx: &mut ViewContext<Self>,
9798 ) {
9799 if let Some(project) = self.project.clone() {
9800 self.buffer.update(cx, |multi_buffer, cx| {
9801 project.update(cx, |project, cx| {
9802 project.cancel_language_server_work_for_buffers(multi_buffer.all_buffers(), cx);
9803 });
9804 })
9805 }
9806 }
9807
9808 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
9809 cx.show_character_palette();
9810 }
9811
9812 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
9813 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
9814 let buffer = self.buffer.read(cx).snapshot(cx);
9815 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
9816 let is_valid = buffer
9817 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
9818 .any(|entry| {
9819 entry.diagnostic.is_primary
9820 && !entry.range.is_empty()
9821 && entry.range.start == primary_range_start
9822 && entry.diagnostic.message == active_diagnostics.primary_message
9823 });
9824
9825 if is_valid != active_diagnostics.is_valid {
9826 active_diagnostics.is_valid = is_valid;
9827 let mut new_styles = HashMap::default();
9828 for (block_id, diagnostic) in &active_diagnostics.blocks {
9829 new_styles.insert(
9830 *block_id,
9831 (
9832 None,
9833 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
9834 ),
9835 );
9836 }
9837 self.display_map.update(cx, |display_map, cx| {
9838 display_map.replace_blocks(new_styles, cx)
9839 });
9840 }
9841 }
9842 }
9843
9844 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
9845 self.dismiss_diagnostics(cx);
9846 let snapshot = self.snapshot(cx);
9847 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
9848 let buffer = self.buffer.read(cx).snapshot(cx);
9849
9850 let mut primary_range = None;
9851 let mut primary_message = None;
9852 let mut group_end = Point::zero();
9853 let diagnostic_group = buffer
9854 .diagnostic_group::<MultiBufferPoint>(group_id)
9855 .filter_map(|entry| {
9856 if snapshot.is_line_folded(MultiBufferRow(entry.range.start.row))
9857 && (entry.range.start.row == entry.range.end.row
9858 || snapshot.is_line_folded(MultiBufferRow(entry.range.end.row)))
9859 {
9860 return None;
9861 }
9862 if entry.range.end > group_end {
9863 group_end = entry.range.end;
9864 }
9865 if entry.diagnostic.is_primary {
9866 primary_range = Some(entry.range.clone());
9867 primary_message = Some(entry.diagnostic.message.clone());
9868 }
9869 Some(entry)
9870 })
9871 .collect::<Vec<_>>();
9872 let primary_range = primary_range?;
9873 let primary_message = primary_message?;
9874 let primary_range =
9875 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
9876
9877 let blocks = display_map
9878 .insert_blocks(
9879 diagnostic_group.iter().map(|entry| {
9880 let diagnostic = entry.diagnostic.clone();
9881 let message_height = diagnostic.message.matches('\n').count() as u8 + 1;
9882 BlockProperties {
9883 style: BlockStyle::Fixed,
9884 position: buffer.anchor_after(entry.range.start),
9885 height: message_height,
9886 render: diagnostic_block_renderer(diagnostic, None, true, true),
9887 disposition: BlockDisposition::Below,
9888 }
9889 }),
9890 cx,
9891 )
9892 .into_iter()
9893 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
9894 .collect();
9895
9896 Some(ActiveDiagnosticGroup {
9897 primary_range,
9898 primary_message,
9899 group_id,
9900 blocks,
9901 is_valid: true,
9902 })
9903 });
9904 self.active_diagnostics.is_some()
9905 }
9906
9907 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
9908 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
9909 self.display_map.update(cx, |display_map, cx| {
9910 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
9911 });
9912 cx.notify();
9913 }
9914 }
9915
9916 pub fn set_selections_from_remote(
9917 &mut self,
9918 selections: Vec<Selection<Anchor>>,
9919 pending_selection: Option<Selection<Anchor>>,
9920 cx: &mut ViewContext<Self>,
9921 ) {
9922 let old_cursor_position = self.selections.newest_anchor().head();
9923 self.selections.change_with(cx, |s| {
9924 s.select_anchors(selections);
9925 if let Some(pending_selection) = pending_selection {
9926 s.set_pending(pending_selection, SelectMode::Character);
9927 } else {
9928 s.clear_pending();
9929 }
9930 });
9931 self.selections_did_change(false, &old_cursor_position, true, cx);
9932 }
9933
9934 fn push_to_selection_history(&mut self) {
9935 self.selection_history.push(SelectionHistoryEntry {
9936 selections: self.selections.disjoint_anchors(),
9937 select_next_state: self.select_next_state.clone(),
9938 select_prev_state: self.select_prev_state.clone(),
9939 add_selections_state: self.add_selections_state.clone(),
9940 });
9941 }
9942
9943 pub fn transact(
9944 &mut self,
9945 cx: &mut ViewContext<Self>,
9946 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
9947 ) -> Option<TransactionId> {
9948 self.start_transaction_at(Instant::now(), cx);
9949 update(self, cx);
9950 self.end_transaction_at(Instant::now(), cx)
9951 }
9952
9953 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
9954 self.end_selection(cx);
9955 if let Some(tx_id) = self
9956 .buffer
9957 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
9958 {
9959 self.selection_history
9960 .insert_transaction(tx_id, self.selections.disjoint_anchors());
9961 cx.emit(EditorEvent::TransactionBegun {
9962 transaction_id: tx_id,
9963 })
9964 }
9965 }
9966
9967 fn end_transaction_at(
9968 &mut self,
9969 now: Instant,
9970 cx: &mut ViewContext<Self>,
9971 ) -> Option<TransactionId> {
9972 if let Some(transaction_id) = self
9973 .buffer
9974 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
9975 {
9976 if let Some((_, end_selections)) =
9977 self.selection_history.transaction_mut(transaction_id)
9978 {
9979 *end_selections = Some(self.selections.disjoint_anchors());
9980 } else {
9981 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
9982 }
9983
9984 cx.emit(EditorEvent::Edited { transaction_id });
9985 Some(transaction_id)
9986 } else {
9987 None
9988 }
9989 }
9990
9991 pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
9992 let mut fold_ranges = Vec::new();
9993
9994 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9995
9996 let selections = self.selections.all_adjusted(cx);
9997 for selection in selections {
9998 let range = selection.range().sorted();
9999 let buffer_start_row = range.start.row;
10000
10001 for row in (0..=range.end.row).rev() {
10002 if let Some((foldable_range, fold_text)) =
10003 display_map.foldable_range(MultiBufferRow(row))
10004 {
10005 if foldable_range.end.row >= buffer_start_row {
10006 fold_ranges.push((foldable_range, fold_text));
10007 if row <= range.start.row {
10008 break;
10009 }
10010 }
10011 }
10012 }
10013 }
10014
10015 self.fold_ranges(fold_ranges, true, cx);
10016 }
10017
10018 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
10019 let buffer_row = fold_at.buffer_row;
10020 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10021
10022 if let Some((fold_range, placeholder)) = display_map.foldable_range(buffer_row) {
10023 let autoscroll = self
10024 .selections
10025 .all::<Point>(cx)
10026 .iter()
10027 .any(|selection| fold_range.overlaps(&selection.range()));
10028
10029 self.fold_ranges([(fold_range, placeholder)], autoscroll, cx);
10030 }
10031 }
10032
10033 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
10034 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10035 let buffer = &display_map.buffer_snapshot;
10036 let selections = self.selections.all::<Point>(cx);
10037 let ranges = selections
10038 .iter()
10039 .map(|s| {
10040 let range = s.display_range(&display_map).sorted();
10041 let mut start = range.start.to_point(&display_map);
10042 let mut end = range.end.to_point(&display_map);
10043 start.column = 0;
10044 end.column = buffer.line_len(MultiBufferRow(end.row));
10045 start..end
10046 })
10047 .collect::<Vec<_>>();
10048
10049 self.unfold_ranges(ranges, true, true, cx);
10050 }
10051
10052 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
10053 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10054
10055 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
10056 ..Point::new(
10057 unfold_at.buffer_row.0,
10058 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
10059 );
10060
10061 let autoscroll = self
10062 .selections
10063 .all::<Point>(cx)
10064 .iter()
10065 .any(|selection| selection.range().overlaps(&intersection_range));
10066
10067 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
10068 }
10069
10070 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
10071 let selections = self.selections.all::<Point>(cx);
10072 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10073 let line_mode = self.selections.line_mode;
10074 let ranges = selections.into_iter().map(|s| {
10075 if line_mode {
10076 let start = Point::new(s.start.row, 0);
10077 let end = Point::new(
10078 s.end.row,
10079 display_map
10080 .buffer_snapshot
10081 .line_len(MultiBufferRow(s.end.row)),
10082 );
10083 (start..end, display_map.fold_placeholder.clone())
10084 } else {
10085 (s.start..s.end, display_map.fold_placeholder.clone())
10086 }
10087 });
10088 self.fold_ranges(ranges, true, cx);
10089 }
10090
10091 pub fn fold_ranges<T: ToOffset + Clone>(
10092 &mut self,
10093 ranges: impl IntoIterator<Item = (Range<T>, FoldPlaceholder)>,
10094 auto_scroll: bool,
10095 cx: &mut ViewContext<Self>,
10096 ) {
10097 let mut fold_ranges = Vec::new();
10098 let mut buffers_affected = HashMap::default();
10099 let multi_buffer = self.buffer().read(cx);
10100 for (fold_range, fold_text) in ranges {
10101 if let Some((_, buffer, _)) =
10102 multi_buffer.excerpt_containing(fold_range.start.clone(), cx)
10103 {
10104 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
10105 };
10106 fold_ranges.push((fold_range, fold_text));
10107 }
10108
10109 let mut ranges = fold_ranges.into_iter().peekable();
10110 if ranges.peek().is_some() {
10111 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
10112
10113 if auto_scroll {
10114 self.request_autoscroll(Autoscroll::fit(), cx);
10115 }
10116
10117 for buffer in buffers_affected.into_values() {
10118 self.sync_expanded_diff_hunks(buffer, cx);
10119 }
10120
10121 cx.notify();
10122
10123 if let Some(active_diagnostics) = self.active_diagnostics.take() {
10124 // Clear diagnostics block when folding a range that contains it.
10125 let snapshot = self.snapshot(cx);
10126 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
10127 drop(snapshot);
10128 self.active_diagnostics = Some(active_diagnostics);
10129 self.dismiss_diagnostics(cx);
10130 } else {
10131 self.active_diagnostics = Some(active_diagnostics);
10132 }
10133 }
10134
10135 self.scrollbar_marker_state.dirty = true;
10136 }
10137 }
10138
10139 pub fn unfold_ranges<T: ToOffset + Clone>(
10140 &mut self,
10141 ranges: impl IntoIterator<Item = Range<T>>,
10142 inclusive: bool,
10143 auto_scroll: bool,
10144 cx: &mut ViewContext<Self>,
10145 ) {
10146 let mut unfold_ranges = Vec::new();
10147 let mut buffers_affected = HashMap::default();
10148 let multi_buffer = self.buffer().read(cx);
10149 for range in ranges {
10150 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
10151 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
10152 };
10153 unfold_ranges.push(range);
10154 }
10155
10156 let mut ranges = unfold_ranges.into_iter().peekable();
10157 if ranges.peek().is_some() {
10158 self.display_map
10159 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
10160 if auto_scroll {
10161 self.request_autoscroll(Autoscroll::fit(), cx);
10162 }
10163
10164 for buffer in buffers_affected.into_values() {
10165 self.sync_expanded_diff_hunks(buffer, cx);
10166 }
10167
10168 cx.notify();
10169 self.scrollbar_marker_state.dirty = true;
10170 self.active_indent_guides_state.dirty = true;
10171 }
10172 }
10173
10174 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
10175 if hovered != self.gutter_hovered {
10176 self.gutter_hovered = hovered;
10177 cx.notify();
10178 }
10179 }
10180
10181 pub fn insert_blocks(
10182 &mut self,
10183 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
10184 autoscroll: Option<Autoscroll>,
10185 cx: &mut ViewContext<Self>,
10186 ) -> Vec<CustomBlockId> {
10187 let blocks = self
10188 .display_map
10189 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
10190 if let Some(autoscroll) = autoscroll {
10191 self.request_autoscroll(autoscroll, cx);
10192 }
10193 blocks
10194 }
10195
10196 pub fn replace_blocks(
10197 &mut self,
10198 blocks: HashMap<CustomBlockId, (Option<u8>, RenderBlock)>,
10199 autoscroll: Option<Autoscroll>,
10200 cx: &mut ViewContext<Self>,
10201 ) {
10202 self.display_map
10203 .update(cx, |display_map, cx| display_map.replace_blocks(blocks, cx));
10204 if let Some(autoscroll) = autoscroll {
10205 self.request_autoscroll(autoscroll, cx);
10206 }
10207 }
10208
10209 pub fn remove_blocks(
10210 &mut self,
10211 block_ids: HashSet<CustomBlockId>,
10212 autoscroll: Option<Autoscroll>,
10213 cx: &mut ViewContext<Self>,
10214 ) {
10215 self.display_map.update(cx, |display_map, cx| {
10216 display_map.remove_blocks(block_ids, cx)
10217 });
10218 if let Some(autoscroll) = autoscroll {
10219 self.request_autoscroll(autoscroll, cx);
10220 }
10221 }
10222
10223 pub fn row_for_block(
10224 &self,
10225 block_id: CustomBlockId,
10226 cx: &mut ViewContext<Self>,
10227 ) -> Option<DisplayRow> {
10228 self.display_map
10229 .update(cx, |map, cx| map.row_for_block(block_id, cx))
10230 }
10231
10232 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
10233 self.focused_block = Some(focused_block);
10234 }
10235
10236 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
10237 self.focused_block.take()
10238 }
10239
10240 pub fn insert_creases(
10241 &mut self,
10242 creases: impl IntoIterator<Item = Crease>,
10243 cx: &mut ViewContext<Self>,
10244 ) -> Vec<CreaseId> {
10245 self.display_map
10246 .update(cx, |map, cx| map.insert_creases(creases, cx))
10247 }
10248
10249 pub fn remove_creases(
10250 &mut self,
10251 ids: impl IntoIterator<Item = CreaseId>,
10252 cx: &mut ViewContext<Self>,
10253 ) {
10254 self.display_map
10255 .update(cx, |map, cx| map.remove_creases(ids, cx));
10256 }
10257
10258 pub fn longest_row(&self, cx: &mut AppContext) -> DisplayRow {
10259 self.display_map
10260 .update(cx, |map, cx| map.snapshot(cx))
10261 .longest_row()
10262 }
10263
10264 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
10265 self.display_map
10266 .update(cx, |map, cx| map.snapshot(cx))
10267 .max_point()
10268 }
10269
10270 pub fn text(&self, cx: &AppContext) -> String {
10271 self.buffer.read(cx).read(cx).text()
10272 }
10273
10274 pub fn text_option(&self, cx: &AppContext) -> Option<String> {
10275 let text = self.text(cx);
10276 let text = text.trim();
10277
10278 if text.is_empty() {
10279 return None;
10280 }
10281
10282 Some(text.to_string())
10283 }
10284
10285 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
10286 self.transact(cx, |this, cx| {
10287 this.buffer
10288 .read(cx)
10289 .as_singleton()
10290 .expect("you can only call set_text on editors for singleton buffers")
10291 .update(cx, |buffer, cx| buffer.set_text(text, cx));
10292 });
10293 }
10294
10295 pub fn display_text(&self, cx: &mut AppContext) -> String {
10296 self.display_map
10297 .update(cx, |map, cx| map.snapshot(cx))
10298 .text()
10299 }
10300
10301 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
10302 let mut wrap_guides = smallvec::smallvec![];
10303
10304 if self.show_wrap_guides == Some(false) {
10305 return wrap_guides;
10306 }
10307
10308 let settings = self.buffer.read(cx).settings_at(0, cx);
10309 if settings.show_wrap_guides {
10310 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
10311 wrap_guides.push((soft_wrap as usize, true));
10312 }
10313 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
10314 }
10315
10316 wrap_guides
10317 }
10318
10319 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
10320 let settings = self.buffer.read(cx).settings_at(0, cx);
10321 let mode = self
10322 .soft_wrap_mode_override
10323 .unwrap_or_else(|| settings.soft_wrap);
10324 match mode {
10325 language_settings::SoftWrap::None => SoftWrap::None,
10326 language_settings::SoftWrap::PreferLine => SoftWrap::PreferLine,
10327 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
10328 language_settings::SoftWrap::PreferredLineLength => {
10329 SoftWrap::Column(settings.preferred_line_length)
10330 }
10331 }
10332 }
10333
10334 pub fn set_soft_wrap_mode(
10335 &mut self,
10336 mode: language_settings::SoftWrap,
10337 cx: &mut ViewContext<Self>,
10338 ) {
10339 self.soft_wrap_mode_override = Some(mode);
10340 cx.notify();
10341 }
10342
10343 pub fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
10344 let rem_size = cx.rem_size();
10345 self.display_map.update(cx, |map, cx| {
10346 map.set_font(
10347 style.text.font(),
10348 style.text.font_size.to_pixels(rem_size),
10349 cx,
10350 )
10351 });
10352 self.style = Some(style);
10353 }
10354
10355 pub fn style(&self) -> Option<&EditorStyle> {
10356 self.style.as_ref()
10357 }
10358
10359 // Called by the element. This method is not designed to be called outside of the editor
10360 // element's layout code because it does not notify when rewrapping is computed synchronously.
10361 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
10362 self.display_map
10363 .update(cx, |map, cx| map.set_wrap_width(width, cx))
10364 }
10365
10366 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
10367 if self.soft_wrap_mode_override.is_some() {
10368 self.soft_wrap_mode_override.take();
10369 } else {
10370 let soft_wrap = match self.soft_wrap_mode(cx) {
10371 SoftWrap::None | SoftWrap::PreferLine => language_settings::SoftWrap::EditorWidth,
10372 SoftWrap::EditorWidth | SoftWrap::Column(_) => {
10373 language_settings::SoftWrap::PreferLine
10374 }
10375 };
10376 self.soft_wrap_mode_override = Some(soft_wrap);
10377 }
10378 cx.notify();
10379 }
10380
10381 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, cx: &mut ViewContext<Self>) {
10382 let Some(workspace) = self.workspace() else {
10383 return;
10384 };
10385 let fs = workspace.read(cx).app_state().fs.clone();
10386 let current_show = TabBarSettings::get_global(cx).show;
10387 update_settings_file::<TabBarSettings>(fs, cx, move |setting| {
10388 setting.show = Some(!current_show);
10389 });
10390 }
10391
10392 pub fn toggle_indent_guides(&mut self, _: &ToggleIndentGuides, cx: &mut ViewContext<Self>) {
10393 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
10394 self.buffer
10395 .read(cx)
10396 .settings_at(0, cx)
10397 .indent_guides
10398 .enabled
10399 });
10400 self.show_indent_guides = Some(!currently_enabled);
10401 cx.notify();
10402 }
10403
10404 fn should_show_indent_guides(&self) -> Option<bool> {
10405 self.show_indent_guides
10406 }
10407
10408 pub fn toggle_line_numbers(&mut self, _: &ToggleLineNumbers, cx: &mut ViewContext<Self>) {
10409 let mut editor_settings = EditorSettings::get_global(cx).clone();
10410 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
10411 EditorSettings::override_global(editor_settings, cx);
10412 }
10413
10414 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
10415 self.show_gutter = show_gutter;
10416 cx.notify();
10417 }
10418
10419 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut ViewContext<Self>) {
10420 self.show_line_numbers = Some(show_line_numbers);
10421 cx.notify();
10422 }
10423
10424 pub fn set_show_git_diff_gutter(
10425 &mut self,
10426 show_git_diff_gutter: bool,
10427 cx: &mut ViewContext<Self>,
10428 ) {
10429 self.show_git_diff_gutter = Some(show_git_diff_gutter);
10430 cx.notify();
10431 }
10432
10433 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut ViewContext<Self>) {
10434 self.show_code_actions = Some(show_code_actions);
10435 cx.notify();
10436 }
10437
10438 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut ViewContext<Self>) {
10439 self.show_runnables = Some(show_runnables);
10440 cx.notify();
10441 }
10442
10443 pub fn set_redact_all(&mut self, redact_all: bool, cx: &mut ViewContext<Self>) {
10444 self.redact_all = redact_all;
10445 cx.notify();
10446 }
10447
10448 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut ViewContext<Self>) {
10449 self.show_wrap_guides = Some(show_wrap_guides);
10450 cx.notify();
10451 }
10452
10453 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut ViewContext<Self>) {
10454 self.show_indent_guides = Some(show_indent_guides);
10455 cx.notify();
10456 }
10457
10458 pub fn working_directory(&self, cx: &WindowContext) -> Option<PathBuf> {
10459 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10460 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10461 if let Some(dir) = file.abs_path(cx).parent() {
10462 return Some(dir.to_owned());
10463 }
10464 }
10465
10466 if let Some(project_path) = buffer.read(cx).project_path(cx) {
10467 return Some(project_path.path.to_path_buf());
10468 }
10469 }
10470
10471 None
10472 }
10473
10474 pub fn reveal_in_finder(&mut self, _: &RevealInFileManager, cx: &mut ViewContext<Self>) {
10475 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10476 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10477 cx.reveal_path(&file.abs_path(cx));
10478 }
10479 }
10480 }
10481
10482 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
10483 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10484 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10485 if let Some(path) = file.abs_path(cx).to_str() {
10486 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
10487 }
10488 }
10489 }
10490 }
10491
10492 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
10493 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10494 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10495 if let Some(path) = file.path().to_str() {
10496 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
10497 }
10498 }
10499 }
10500 }
10501
10502 pub fn toggle_git_blame(&mut self, _: &ToggleGitBlame, cx: &mut ViewContext<Self>) {
10503 self.show_git_blame_gutter = !self.show_git_blame_gutter;
10504
10505 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
10506 self.start_git_blame(true, cx);
10507 }
10508
10509 cx.notify();
10510 }
10511
10512 pub fn toggle_git_blame_inline(
10513 &mut self,
10514 _: &ToggleGitBlameInline,
10515 cx: &mut ViewContext<Self>,
10516 ) {
10517 self.toggle_git_blame_inline_internal(true, cx);
10518 cx.notify();
10519 }
10520
10521 pub fn git_blame_inline_enabled(&self) -> bool {
10522 self.git_blame_inline_enabled
10523 }
10524
10525 pub fn toggle_selection_menu(&mut self, _: &ToggleSelectionMenu, cx: &mut ViewContext<Self>) {
10526 self.show_selection_menu = self
10527 .show_selection_menu
10528 .map(|show_selections_menu| !show_selections_menu)
10529 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
10530
10531 cx.notify();
10532 }
10533
10534 pub fn selection_menu_enabled(&self, cx: &AppContext) -> bool {
10535 self.show_selection_menu
10536 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
10537 }
10538
10539 fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
10540 if let Some(project) = self.project.as_ref() {
10541 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
10542 return;
10543 };
10544
10545 if buffer.read(cx).file().is_none() {
10546 return;
10547 }
10548
10549 let focused = self.focus_handle(cx).contains_focused(cx);
10550
10551 let project = project.clone();
10552 let blame =
10553 cx.new_model(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
10554 self.blame_subscription = Some(cx.observe(&blame, |_, _, cx| cx.notify()));
10555 self.blame = Some(blame);
10556 }
10557 }
10558
10559 fn toggle_git_blame_inline_internal(
10560 &mut self,
10561 user_triggered: bool,
10562 cx: &mut ViewContext<Self>,
10563 ) {
10564 if self.git_blame_inline_enabled {
10565 self.git_blame_inline_enabled = false;
10566 self.show_git_blame_inline = false;
10567 self.show_git_blame_inline_delay_task.take();
10568 } else {
10569 self.git_blame_inline_enabled = true;
10570 self.start_git_blame_inline(user_triggered, cx);
10571 }
10572
10573 cx.notify();
10574 }
10575
10576 fn start_git_blame_inline(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
10577 self.start_git_blame(user_triggered, cx);
10578
10579 if ProjectSettings::get_global(cx)
10580 .git
10581 .inline_blame_delay()
10582 .is_some()
10583 {
10584 self.start_inline_blame_timer(cx);
10585 } else {
10586 self.show_git_blame_inline = true
10587 }
10588 }
10589
10590 pub fn blame(&self) -> Option<&Model<GitBlame>> {
10591 self.blame.as_ref()
10592 }
10593
10594 pub fn render_git_blame_gutter(&mut self, cx: &mut WindowContext) -> bool {
10595 self.show_git_blame_gutter && self.has_blame_entries(cx)
10596 }
10597
10598 pub fn render_git_blame_inline(&mut self, cx: &mut WindowContext) -> bool {
10599 self.show_git_blame_inline
10600 && self.focus_handle.is_focused(cx)
10601 && !self.newest_selection_head_on_empty_line(cx)
10602 && self.has_blame_entries(cx)
10603 }
10604
10605 fn has_blame_entries(&self, cx: &mut WindowContext) -> bool {
10606 self.blame()
10607 .map_or(false, |blame| blame.read(cx).has_generated_entries())
10608 }
10609
10610 fn newest_selection_head_on_empty_line(&mut self, cx: &mut WindowContext) -> bool {
10611 let cursor_anchor = self.selections.newest_anchor().head();
10612
10613 let snapshot = self.buffer.read(cx).snapshot(cx);
10614 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
10615
10616 snapshot.line_len(buffer_row) == 0
10617 }
10618
10619 fn get_permalink_to_line(&mut self, cx: &mut ViewContext<Self>) -> Result<url::Url> {
10620 let (path, selection, repo) = maybe!({
10621 let project_handle = self.project.as_ref()?.clone();
10622 let project = project_handle.read(cx);
10623
10624 let selection = self.selections.newest::<Point>(cx);
10625 let selection_range = selection.range();
10626
10627 let (buffer, selection) = if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10628 (buffer, selection_range.start.row..selection_range.end.row)
10629 } else {
10630 let buffer_ranges = self
10631 .buffer()
10632 .read(cx)
10633 .range_to_buffer_ranges(selection_range, cx);
10634
10635 let (buffer, range, _) = if selection.reversed {
10636 buffer_ranges.first()
10637 } else {
10638 buffer_ranges.last()
10639 }?;
10640
10641 let snapshot = buffer.read(cx).snapshot();
10642 let selection = text::ToPoint::to_point(&range.start, &snapshot).row
10643 ..text::ToPoint::to_point(&range.end, &snapshot).row;
10644 (buffer.clone(), selection)
10645 };
10646
10647 let path = buffer
10648 .read(cx)
10649 .file()?
10650 .as_local()?
10651 .path()
10652 .to_str()?
10653 .to_string();
10654 let repo = project.get_repo(&buffer.read(cx).project_path(cx)?, cx)?;
10655 Some((path, selection, repo))
10656 })
10657 .ok_or_else(|| anyhow!("unable to open git repository"))?;
10658
10659 const REMOTE_NAME: &str = "origin";
10660 let origin_url = repo
10661 .remote_url(REMOTE_NAME)
10662 .ok_or_else(|| anyhow!("remote \"{REMOTE_NAME}\" not found"))?;
10663 let sha = repo
10664 .head_sha()
10665 .ok_or_else(|| anyhow!("failed to read HEAD SHA"))?;
10666
10667 let (provider, remote) =
10668 parse_git_remote_url(GitHostingProviderRegistry::default_global(cx), &origin_url)
10669 .ok_or_else(|| anyhow!("failed to parse Git remote URL"))?;
10670
10671 Ok(provider.build_permalink(
10672 remote,
10673 BuildPermalinkParams {
10674 sha: &sha,
10675 path: &path,
10676 selection: Some(selection),
10677 },
10678 ))
10679 }
10680
10681 pub fn copy_permalink_to_line(&mut self, _: &CopyPermalinkToLine, cx: &mut ViewContext<Self>) {
10682 let permalink = self.get_permalink_to_line(cx);
10683
10684 match permalink {
10685 Ok(permalink) => {
10686 cx.write_to_clipboard(ClipboardItem::new(permalink.to_string()));
10687 }
10688 Err(err) => {
10689 let message = format!("Failed to copy permalink: {err}");
10690
10691 Err::<(), anyhow::Error>(err).log_err();
10692
10693 if let Some(workspace) = self.workspace() {
10694 workspace.update(cx, |workspace, cx| {
10695 struct CopyPermalinkToLine;
10696
10697 workspace.show_toast(
10698 Toast::new(NotificationId::unique::<CopyPermalinkToLine>(), message),
10699 cx,
10700 )
10701 })
10702 }
10703 }
10704 }
10705 }
10706
10707 pub fn open_permalink_to_line(&mut self, _: &OpenPermalinkToLine, cx: &mut ViewContext<Self>) {
10708 let permalink = self.get_permalink_to_line(cx);
10709
10710 match permalink {
10711 Ok(permalink) => {
10712 cx.open_url(permalink.as_ref());
10713 }
10714 Err(err) => {
10715 let message = format!("Failed to open permalink: {err}");
10716
10717 Err::<(), anyhow::Error>(err).log_err();
10718
10719 if let Some(workspace) = self.workspace() {
10720 workspace.update(cx, |workspace, cx| {
10721 struct OpenPermalinkToLine;
10722
10723 workspace.show_toast(
10724 Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
10725 cx,
10726 )
10727 })
10728 }
10729 }
10730 }
10731 }
10732
10733 /// Adds or removes (on `None` color) a highlight for the rows corresponding to the anchor range given.
10734 /// On matching anchor range, replaces the old highlight; does not clear the other existing highlights.
10735 /// If multiple anchor ranges will produce highlights for the same row, the last range added will be used.
10736 pub fn highlight_rows<T: 'static>(
10737 &mut self,
10738 rows: RangeInclusive<Anchor>,
10739 color: Option<Hsla>,
10740 should_autoscroll: bool,
10741 cx: &mut ViewContext<Self>,
10742 ) {
10743 let snapshot = self.buffer().read(cx).snapshot(cx);
10744 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
10745 let existing_highlight_index = row_highlights.binary_search_by(|highlight| {
10746 highlight
10747 .range
10748 .start()
10749 .cmp(&rows.start(), &snapshot)
10750 .then(highlight.range.end().cmp(&rows.end(), &snapshot))
10751 });
10752 match (color, existing_highlight_index) {
10753 (Some(_), Ok(ix)) | (_, Err(ix)) => row_highlights.insert(
10754 ix,
10755 RowHighlight {
10756 index: post_inc(&mut self.highlight_order),
10757 range: rows,
10758 should_autoscroll,
10759 color,
10760 },
10761 ),
10762 (None, Ok(i)) => {
10763 row_highlights.remove(i);
10764 }
10765 }
10766 }
10767
10768 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
10769 pub fn clear_row_highlights<T: 'static>(&mut self) {
10770 self.highlighted_rows.remove(&TypeId::of::<T>());
10771 }
10772
10773 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
10774 pub fn highlighted_rows<T: 'static>(
10775 &self,
10776 ) -> Option<impl Iterator<Item = (&RangeInclusive<Anchor>, Option<&Hsla>)>> {
10777 Some(
10778 self.highlighted_rows
10779 .get(&TypeId::of::<T>())?
10780 .iter()
10781 .map(|highlight| (&highlight.range, highlight.color.as_ref())),
10782 )
10783 }
10784
10785 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
10786 /// Rerturns a map of display rows that are highlighted and their corresponding highlight color.
10787 /// Allows to ignore certain kinds of highlights.
10788 pub fn highlighted_display_rows(
10789 &mut self,
10790 cx: &mut WindowContext,
10791 ) -> BTreeMap<DisplayRow, Hsla> {
10792 let snapshot = self.snapshot(cx);
10793 let mut used_highlight_orders = HashMap::default();
10794 self.highlighted_rows
10795 .iter()
10796 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
10797 .fold(
10798 BTreeMap::<DisplayRow, Hsla>::new(),
10799 |mut unique_rows, highlight| {
10800 let start_row = highlight.range.start().to_display_point(&snapshot).row();
10801 let end_row = highlight.range.end().to_display_point(&snapshot).row();
10802 for row in start_row.0..=end_row.0 {
10803 let used_index =
10804 used_highlight_orders.entry(row).or_insert(highlight.index);
10805 if highlight.index >= *used_index {
10806 *used_index = highlight.index;
10807 match highlight.color {
10808 Some(hsla) => unique_rows.insert(DisplayRow(row), hsla),
10809 None => unique_rows.remove(&DisplayRow(row)),
10810 };
10811 }
10812 }
10813 unique_rows
10814 },
10815 )
10816 }
10817
10818 pub fn highlighted_display_row_for_autoscroll(
10819 &self,
10820 snapshot: &DisplaySnapshot,
10821 ) -> Option<DisplayRow> {
10822 self.highlighted_rows
10823 .values()
10824 .flat_map(|highlighted_rows| highlighted_rows.iter())
10825 .filter_map(|highlight| {
10826 if highlight.color.is_none() || !highlight.should_autoscroll {
10827 return None;
10828 }
10829 Some(highlight.range.start().to_display_point(&snapshot).row())
10830 })
10831 .min()
10832 }
10833
10834 pub fn set_search_within_ranges(
10835 &mut self,
10836 ranges: &[Range<Anchor>],
10837 cx: &mut ViewContext<Self>,
10838 ) {
10839 self.highlight_background::<SearchWithinRange>(
10840 ranges,
10841 |colors| colors.editor_document_highlight_read_background,
10842 cx,
10843 )
10844 }
10845
10846 pub fn set_breadcrumb_header(&mut self, new_header: String) {
10847 self.breadcrumb_header = Some(new_header);
10848 }
10849
10850 pub fn clear_search_within_ranges(&mut self, cx: &mut ViewContext<Self>) {
10851 self.clear_background_highlights::<SearchWithinRange>(cx);
10852 }
10853
10854 pub fn highlight_background<T: 'static>(
10855 &mut self,
10856 ranges: &[Range<Anchor>],
10857 color_fetcher: fn(&ThemeColors) -> Hsla,
10858 cx: &mut ViewContext<Self>,
10859 ) {
10860 let snapshot = self.snapshot(cx);
10861 // this is to try and catch a panic sooner
10862 for range in ranges {
10863 snapshot
10864 .buffer_snapshot
10865 .summary_for_anchor::<usize>(&range.start);
10866 snapshot
10867 .buffer_snapshot
10868 .summary_for_anchor::<usize>(&range.end);
10869 }
10870
10871 self.background_highlights
10872 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
10873 self.scrollbar_marker_state.dirty = true;
10874 cx.notify();
10875 }
10876
10877 pub fn clear_background_highlights<T: 'static>(
10878 &mut self,
10879 cx: &mut ViewContext<Self>,
10880 ) -> Option<BackgroundHighlight> {
10881 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
10882 if !text_highlights.1.is_empty() {
10883 self.scrollbar_marker_state.dirty = true;
10884 cx.notify();
10885 }
10886 Some(text_highlights)
10887 }
10888
10889 pub fn highlight_gutter<T: 'static>(
10890 &mut self,
10891 ranges: &[Range<Anchor>],
10892 color_fetcher: fn(&AppContext) -> Hsla,
10893 cx: &mut ViewContext<Self>,
10894 ) {
10895 self.gutter_highlights
10896 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
10897 cx.notify();
10898 }
10899
10900 pub fn clear_gutter_highlights<T: 'static>(
10901 &mut self,
10902 cx: &mut ViewContext<Self>,
10903 ) -> Option<GutterHighlight> {
10904 cx.notify();
10905 self.gutter_highlights.remove(&TypeId::of::<T>())
10906 }
10907
10908 #[cfg(feature = "test-support")]
10909 pub fn all_text_background_highlights(
10910 &mut self,
10911 cx: &mut ViewContext<Self>,
10912 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
10913 let snapshot = self.snapshot(cx);
10914 let buffer = &snapshot.buffer_snapshot;
10915 let start = buffer.anchor_before(0);
10916 let end = buffer.anchor_after(buffer.len());
10917 let theme = cx.theme().colors();
10918 self.background_highlights_in_range(start..end, &snapshot, theme)
10919 }
10920
10921 #[cfg(feature = "test-support")]
10922 pub fn search_background_highlights(
10923 &mut self,
10924 cx: &mut ViewContext<Self>,
10925 ) -> Vec<Range<Point>> {
10926 let snapshot = self.buffer().read(cx).snapshot(cx);
10927
10928 let highlights = self
10929 .background_highlights
10930 .get(&TypeId::of::<items::BufferSearchHighlights>());
10931
10932 if let Some((_color, ranges)) = highlights {
10933 ranges
10934 .iter()
10935 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
10936 .collect_vec()
10937 } else {
10938 vec![]
10939 }
10940 }
10941
10942 fn document_highlights_for_position<'a>(
10943 &'a self,
10944 position: Anchor,
10945 buffer: &'a MultiBufferSnapshot,
10946 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
10947 let read_highlights = self
10948 .background_highlights
10949 .get(&TypeId::of::<DocumentHighlightRead>())
10950 .map(|h| &h.1);
10951 let write_highlights = self
10952 .background_highlights
10953 .get(&TypeId::of::<DocumentHighlightWrite>())
10954 .map(|h| &h.1);
10955 let left_position = position.bias_left(buffer);
10956 let right_position = position.bias_right(buffer);
10957 read_highlights
10958 .into_iter()
10959 .chain(write_highlights)
10960 .flat_map(move |ranges| {
10961 let start_ix = match ranges.binary_search_by(|probe| {
10962 let cmp = probe.end.cmp(&left_position, buffer);
10963 if cmp.is_ge() {
10964 Ordering::Greater
10965 } else {
10966 Ordering::Less
10967 }
10968 }) {
10969 Ok(i) | Err(i) => i,
10970 };
10971
10972 ranges[start_ix..]
10973 .iter()
10974 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
10975 })
10976 }
10977
10978 pub fn has_background_highlights<T: 'static>(&self) -> bool {
10979 self.background_highlights
10980 .get(&TypeId::of::<T>())
10981 .map_or(false, |(_, highlights)| !highlights.is_empty())
10982 }
10983
10984 pub fn background_highlights_in_range(
10985 &self,
10986 search_range: Range<Anchor>,
10987 display_snapshot: &DisplaySnapshot,
10988 theme: &ThemeColors,
10989 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
10990 let mut results = Vec::new();
10991 for (color_fetcher, ranges) in self.background_highlights.values() {
10992 let color = color_fetcher(theme);
10993 let start_ix = match ranges.binary_search_by(|probe| {
10994 let cmp = probe
10995 .end
10996 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
10997 if cmp.is_gt() {
10998 Ordering::Greater
10999 } else {
11000 Ordering::Less
11001 }
11002 }) {
11003 Ok(i) | Err(i) => i,
11004 };
11005 for range in &ranges[start_ix..] {
11006 if range
11007 .start
11008 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11009 .is_ge()
11010 {
11011 break;
11012 }
11013
11014 let start = range.start.to_display_point(&display_snapshot);
11015 let end = range.end.to_display_point(&display_snapshot);
11016 results.push((start..end, color))
11017 }
11018 }
11019 results
11020 }
11021
11022 pub fn background_highlight_row_ranges<T: 'static>(
11023 &self,
11024 search_range: Range<Anchor>,
11025 display_snapshot: &DisplaySnapshot,
11026 count: usize,
11027 ) -> Vec<RangeInclusive<DisplayPoint>> {
11028 let mut results = Vec::new();
11029 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
11030 return vec![];
11031 };
11032
11033 let start_ix = match ranges.binary_search_by(|probe| {
11034 let cmp = probe
11035 .end
11036 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11037 if cmp.is_gt() {
11038 Ordering::Greater
11039 } else {
11040 Ordering::Less
11041 }
11042 }) {
11043 Ok(i) | Err(i) => i,
11044 };
11045 let mut push_region = |start: Option<Point>, end: Option<Point>| {
11046 if let (Some(start_display), Some(end_display)) = (start, end) {
11047 results.push(
11048 start_display.to_display_point(display_snapshot)
11049 ..=end_display.to_display_point(display_snapshot),
11050 );
11051 }
11052 };
11053 let mut start_row: Option<Point> = None;
11054 let mut end_row: Option<Point> = None;
11055 if ranges.len() > count {
11056 return Vec::new();
11057 }
11058 for range in &ranges[start_ix..] {
11059 if range
11060 .start
11061 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11062 .is_ge()
11063 {
11064 break;
11065 }
11066 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
11067 if let Some(current_row) = &end_row {
11068 if end.row == current_row.row {
11069 continue;
11070 }
11071 }
11072 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
11073 if start_row.is_none() {
11074 assert_eq!(end_row, None);
11075 start_row = Some(start);
11076 end_row = Some(end);
11077 continue;
11078 }
11079 if let Some(current_end) = end_row.as_mut() {
11080 if start.row > current_end.row + 1 {
11081 push_region(start_row, end_row);
11082 start_row = Some(start);
11083 end_row = Some(end);
11084 } else {
11085 // Merge two hunks.
11086 *current_end = end;
11087 }
11088 } else {
11089 unreachable!();
11090 }
11091 }
11092 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
11093 push_region(start_row, end_row);
11094 results
11095 }
11096
11097 pub fn gutter_highlights_in_range(
11098 &self,
11099 search_range: Range<Anchor>,
11100 display_snapshot: &DisplaySnapshot,
11101 cx: &AppContext,
11102 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11103 let mut results = Vec::new();
11104 for (color_fetcher, ranges) in self.gutter_highlights.values() {
11105 let color = color_fetcher(cx);
11106 let start_ix = match ranges.binary_search_by(|probe| {
11107 let cmp = probe
11108 .end
11109 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11110 if cmp.is_gt() {
11111 Ordering::Greater
11112 } else {
11113 Ordering::Less
11114 }
11115 }) {
11116 Ok(i) | Err(i) => i,
11117 };
11118 for range in &ranges[start_ix..] {
11119 if range
11120 .start
11121 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11122 .is_ge()
11123 {
11124 break;
11125 }
11126
11127 let start = range.start.to_display_point(&display_snapshot);
11128 let end = range.end.to_display_point(&display_snapshot);
11129 results.push((start..end, color))
11130 }
11131 }
11132 results
11133 }
11134
11135 /// Get the text ranges corresponding to the redaction query
11136 pub fn redacted_ranges(
11137 &self,
11138 search_range: Range<Anchor>,
11139 display_snapshot: &DisplaySnapshot,
11140 cx: &WindowContext,
11141 ) -> Vec<Range<DisplayPoint>> {
11142 if self.redact_all {
11143 return vec![DisplayPoint::zero()..display_snapshot.max_point()];
11144 }
11145
11146 display_snapshot
11147 .buffer_snapshot
11148 .redacted_ranges(search_range, |file| {
11149 if let Some(file) = file {
11150 file.is_private()
11151 && EditorSettings::get(Some(file.as_ref().into()), cx).redact_private_values
11152 } else {
11153 false
11154 }
11155 })
11156 .map(|range| {
11157 range.start.to_display_point(display_snapshot)
11158 ..range.end.to_display_point(display_snapshot)
11159 })
11160 .collect()
11161 }
11162
11163 pub fn highlight_text<T: 'static>(
11164 &mut self,
11165 ranges: Vec<Range<Anchor>>,
11166 style: HighlightStyle,
11167 cx: &mut ViewContext<Self>,
11168 ) {
11169 self.display_map.update(cx, |map, _| {
11170 map.highlight_text(TypeId::of::<T>(), ranges, style)
11171 });
11172 cx.notify();
11173 }
11174
11175 pub(crate) fn highlight_inlays<T: 'static>(
11176 &mut self,
11177 highlights: Vec<InlayHighlight>,
11178 style: HighlightStyle,
11179 cx: &mut ViewContext<Self>,
11180 ) {
11181 self.display_map.update(cx, |map, _| {
11182 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
11183 });
11184 cx.notify();
11185 }
11186
11187 pub fn text_highlights<'a, T: 'static>(
11188 &'a self,
11189 cx: &'a AppContext,
11190 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
11191 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
11192 }
11193
11194 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
11195 let cleared = self
11196 .display_map
11197 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
11198 if cleared {
11199 cx.notify();
11200 }
11201 }
11202
11203 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
11204 (self.read_only(cx) || self.blink_manager.read(cx).visible())
11205 && self.focus_handle.is_focused(cx)
11206 }
11207
11208 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut ViewContext<Self>) {
11209 self.show_cursor_when_unfocused = is_enabled;
11210 cx.notify();
11211 }
11212
11213 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
11214 cx.notify();
11215 }
11216
11217 fn on_buffer_event(
11218 &mut self,
11219 multibuffer: Model<MultiBuffer>,
11220 event: &multi_buffer::Event,
11221 cx: &mut ViewContext<Self>,
11222 ) {
11223 match event {
11224 multi_buffer::Event::Edited {
11225 singleton_buffer_edited,
11226 } => {
11227 self.scrollbar_marker_state.dirty = true;
11228 self.active_indent_guides_state.dirty = true;
11229 self.refresh_active_diagnostics(cx);
11230 self.refresh_code_actions(cx);
11231 if self.has_active_inline_completion(cx) {
11232 self.update_visible_inline_completion(cx);
11233 }
11234 cx.emit(EditorEvent::BufferEdited);
11235 cx.emit(SearchEvent::MatchesInvalidated);
11236 if *singleton_buffer_edited {
11237 if let Some(project) = &self.project {
11238 let project = project.read(cx);
11239 #[allow(clippy::mutable_key_type)]
11240 let languages_affected = multibuffer
11241 .read(cx)
11242 .all_buffers()
11243 .into_iter()
11244 .filter_map(|buffer| {
11245 let buffer = buffer.read(cx);
11246 let language = buffer.language()?;
11247 if project.is_local()
11248 && project.language_servers_for_buffer(buffer, cx).count() == 0
11249 {
11250 None
11251 } else {
11252 Some(language)
11253 }
11254 })
11255 .cloned()
11256 .collect::<HashSet<_>>();
11257 if !languages_affected.is_empty() {
11258 self.refresh_inlay_hints(
11259 InlayHintRefreshReason::BufferEdited(languages_affected),
11260 cx,
11261 );
11262 }
11263 }
11264 }
11265
11266 let Some(project) = &self.project else { return };
11267 let telemetry = project.read(cx).client().telemetry().clone();
11268 refresh_linked_ranges(self, cx);
11269 telemetry.log_edit_event("editor");
11270 }
11271 multi_buffer::Event::ExcerptsAdded {
11272 buffer,
11273 predecessor,
11274 excerpts,
11275 } => {
11276 self.tasks_update_task = Some(self.refresh_runnables(cx));
11277 cx.emit(EditorEvent::ExcerptsAdded {
11278 buffer: buffer.clone(),
11279 predecessor: *predecessor,
11280 excerpts: excerpts.clone(),
11281 });
11282 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
11283 }
11284 multi_buffer::Event::ExcerptsRemoved { ids } => {
11285 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
11286 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
11287 }
11288 multi_buffer::Event::ExcerptsEdited { ids } => {
11289 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
11290 }
11291 multi_buffer::Event::ExcerptsExpanded { ids } => {
11292 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
11293 }
11294 multi_buffer::Event::Reparsed(buffer_id) => {
11295 self.tasks_update_task = Some(self.refresh_runnables(cx));
11296
11297 cx.emit(EditorEvent::Reparsed(*buffer_id));
11298 }
11299 multi_buffer::Event::LanguageChanged(buffer_id) => {
11300 linked_editing_ranges::refresh_linked_ranges(self, cx);
11301 cx.emit(EditorEvent::Reparsed(*buffer_id));
11302 cx.notify();
11303 }
11304 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
11305 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
11306 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
11307 cx.emit(EditorEvent::TitleChanged)
11308 }
11309 multi_buffer::Event::DiffBaseChanged => {
11310 self.scrollbar_marker_state.dirty = true;
11311 cx.emit(EditorEvent::DiffBaseChanged);
11312 cx.notify();
11313 }
11314 multi_buffer::Event::DiffUpdated { buffer } => {
11315 self.sync_expanded_diff_hunks(buffer.clone(), cx);
11316 cx.notify();
11317 }
11318 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
11319 multi_buffer::Event::DiagnosticsUpdated => {
11320 self.refresh_active_diagnostics(cx);
11321 self.scrollbar_marker_state.dirty = true;
11322 cx.notify();
11323 }
11324 _ => {}
11325 };
11326 }
11327
11328 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
11329 cx.notify();
11330 }
11331
11332 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
11333 self.tasks_update_task = Some(self.refresh_runnables(cx));
11334 self.refresh_inline_completion(true, cx);
11335 self.refresh_inlay_hints(
11336 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
11337 self.selections.newest_anchor().head(),
11338 &self.buffer.read(cx).snapshot(cx),
11339 cx,
11340 )),
11341 cx,
11342 );
11343 let editor_settings = EditorSettings::get_global(cx);
11344 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
11345 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
11346
11347 let project_settings = ProjectSettings::get_global(cx);
11348 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
11349
11350 if self.mode == EditorMode::Full {
11351 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
11352 if self.git_blame_inline_enabled != inline_blame_enabled {
11353 self.toggle_git_blame_inline_internal(false, cx);
11354 }
11355 }
11356
11357 cx.notify();
11358 }
11359
11360 pub fn set_searchable(&mut self, searchable: bool) {
11361 self.searchable = searchable;
11362 }
11363
11364 pub fn searchable(&self) -> bool {
11365 self.searchable
11366 }
11367
11368 fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext<Self>) {
11369 self.open_excerpts_common(true, cx)
11370 }
11371
11372 fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
11373 self.open_excerpts_common(false, cx)
11374 }
11375
11376 fn open_excerpts_common(&mut self, split: bool, cx: &mut ViewContext<Self>) {
11377 let buffer = self.buffer.read(cx);
11378 if buffer.is_singleton() {
11379 cx.propagate();
11380 return;
11381 }
11382
11383 let Some(workspace) = self.workspace() else {
11384 cx.propagate();
11385 return;
11386 };
11387
11388 let mut new_selections_by_buffer = HashMap::default();
11389 for selection in self.selections.all::<usize>(cx) {
11390 for (buffer, mut range, _) in
11391 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
11392 {
11393 if selection.reversed {
11394 mem::swap(&mut range.start, &mut range.end);
11395 }
11396 new_selections_by_buffer
11397 .entry(buffer)
11398 .or_insert(Vec::new())
11399 .push(range)
11400 }
11401 }
11402
11403 // We defer the pane interaction because we ourselves are a workspace item
11404 // and activating a new item causes the pane to call a method on us reentrantly,
11405 // which panics if we're on the stack.
11406 cx.window_context().defer(move |cx| {
11407 workspace.update(cx, |workspace, cx| {
11408 let pane = if split {
11409 workspace.adjacent_pane(cx)
11410 } else {
11411 workspace.active_pane().clone()
11412 };
11413
11414 for (buffer, ranges) in new_selections_by_buffer {
11415 let editor =
11416 workspace.open_project_item::<Self>(pane.clone(), buffer, true, true, cx);
11417 editor.update(cx, |editor, cx| {
11418 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
11419 s.select_ranges(ranges);
11420 });
11421 });
11422 }
11423 })
11424 });
11425 }
11426
11427 fn jump(
11428 &mut self,
11429 path: ProjectPath,
11430 position: Point,
11431 anchor: language::Anchor,
11432 offset_from_top: u32,
11433 cx: &mut ViewContext<Self>,
11434 ) {
11435 let workspace = self.workspace();
11436 cx.spawn(|_, mut cx| async move {
11437 let workspace = workspace.ok_or_else(|| anyhow!("cannot jump without workspace"))?;
11438 let editor = workspace.update(&mut cx, |workspace, cx| {
11439 // Reset the preview item id before opening the new item
11440 workspace.active_pane().update(cx, |pane, cx| {
11441 pane.set_preview_item_id(None, cx);
11442 });
11443 workspace.open_path_preview(path, None, true, true, cx)
11444 })?;
11445 let editor = editor
11446 .await?
11447 .downcast::<Editor>()
11448 .ok_or_else(|| anyhow!("opened item was not an editor"))?
11449 .downgrade();
11450 editor.update(&mut cx, |editor, cx| {
11451 let buffer = editor
11452 .buffer()
11453 .read(cx)
11454 .as_singleton()
11455 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
11456 let buffer = buffer.read(cx);
11457 let cursor = if buffer.can_resolve(&anchor) {
11458 language::ToPoint::to_point(&anchor, buffer)
11459 } else {
11460 buffer.clip_point(position, Bias::Left)
11461 };
11462
11463 let nav_history = editor.nav_history.take();
11464 editor.change_selections(
11465 Some(Autoscroll::top_relative(offset_from_top as usize)),
11466 cx,
11467 |s| {
11468 s.select_ranges([cursor..cursor]);
11469 },
11470 );
11471 editor.nav_history = nav_history;
11472
11473 anyhow::Ok(())
11474 })??;
11475
11476 anyhow::Ok(())
11477 })
11478 .detach_and_log_err(cx);
11479 }
11480
11481 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
11482 let snapshot = self.buffer.read(cx).read(cx);
11483 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
11484 Some(
11485 ranges
11486 .iter()
11487 .map(move |range| {
11488 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
11489 })
11490 .collect(),
11491 )
11492 }
11493
11494 fn selection_replacement_ranges(
11495 &self,
11496 range: Range<OffsetUtf16>,
11497 cx: &AppContext,
11498 ) -> Vec<Range<OffsetUtf16>> {
11499 let selections = self.selections.all::<OffsetUtf16>(cx);
11500 let newest_selection = selections
11501 .iter()
11502 .max_by_key(|selection| selection.id)
11503 .unwrap();
11504 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
11505 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
11506 let snapshot = self.buffer.read(cx).read(cx);
11507 selections
11508 .into_iter()
11509 .map(|mut selection| {
11510 selection.start.0 =
11511 (selection.start.0 as isize).saturating_add(start_delta) as usize;
11512 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
11513 snapshot.clip_offset_utf16(selection.start, Bias::Left)
11514 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
11515 })
11516 .collect()
11517 }
11518
11519 fn report_editor_event(
11520 &self,
11521 operation: &'static str,
11522 file_extension: Option<String>,
11523 cx: &AppContext,
11524 ) {
11525 if cfg!(any(test, feature = "test-support")) {
11526 return;
11527 }
11528
11529 let Some(project) = &self.project else { return };
11530
11531 // If None, we are in a file without an extension
11532 let file = self
11533 .buffer
11534 .read(cx)
11535 .as_singleton()
11536 .and_then(|b| b.read(cx).file());
11537 let file_extension = file_extension.or(file
11538 .as_ref()
11539 .and_then(|file| Path::new(file.file_name(cx)).extension())
11540 .and_then(|e| e.to_str())
11541 .map(|a| a.to_string()));
11542
11543 let vim_mode = cx
11544 .global::<SettingsStore>()
11545 .raw_user_settings()
11546 .get("vim_mode")
11547 == Some(&serde_json::Value::Bool(true));
11548
11549 let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
11550 == language::language_settings::InlineCompletionProvider::Copilot;
11551 let copilot_enabled_for_language = self
11552 .buffer
11553 .read(cx)
11554 .settings_at(0, cx)
11555 .show_inline_completions;
11556
11557 let telemetry = project.read(cx).client().telemetry().clone();
11558 telemetry.report_editor_event(
11559 file_extension,
11560 vim_mode,
11561 operation,
11562 copilot_enabled,
11563 copilot_enabled_for_language,
11564 )
11565 }
11566
11567 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
11568 /// with each line being an array of {text, highlight} objects.
11569 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
11570 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
11571 return;
11572 };
11573
11574 #[derive(Serialize)]
11575 struct Chunk<'a> {
11576 text: String,
11577 highlight: Option<&'a str>,
11578 }
11579
11580 let snapshot = buffer.read(cx).snapshot();
11581 let range = self
11582 .selected_text_range(cx)
11583 .and_then(|selected_range| {
11584 if selected_range.is_empty() {
11585 None
11586 } else {
11587 Some(selected_range)
11588 }
11589 })
11590 .unwrap_or_else(|| 0..snapshot.len());
11591
11592 let chunks = snapshot.chunks(range, true);
11593 let mut lines = Vec::new();
11594 let mut line: VecDeque<Chunk> = VecDeque::new();
11595
11596 let Some(style) = self.style.as_ref() else {
11597 return;
11598 };
11599
11600 for chunk in chunks {
11601 let highlight = chunk
11602 .syntax_highlight_id
11603 .and_then(|id| id.name(&style.syntax));
11604 let mut chunk_lines = chunk.text.split('\n').peekable();
11605 while let Some(text) = chunk_lines.next() {
11606 let mut merged_with_last_token = false;
11607 if let Some(last_token) = line.back_mut() {
11608 if last_token.highlight == highlight {
11609 last_token.text.push_str(text);
11610 merged_with_last_token = true;
11611 }
11612 }
11613
11614 if !merged_with_last_token {
11615 line.push_back(Chunk {
11616 text: text.into(),
11617 highlight,
11618 });
11619 }
11620
11621 if chunk_lines.peek().is_some() {
11622 if line.len() > 1 && line.front().unwrap().text.is_empty() {
11623 line.pop_front();
11624 }
11625 if line.len() > 1 && line.back().unwrap().text.is_empty() {
11626 line.pop_back();
11627 }
11628
11629 lines.push(mem::take(&mut line));
11630 }
11631 }
11632 }
11633
11634 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
11635 return;
11636 };
11637 cx.write_to_clipboard(ClipboardItem::new(lines));
11638 }
11639
11640 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
11641 &self.inlay_hint_cache
11642 }
11643
11644 pub fn replay_insert_event(
11645 &mut self,
11646 text: &str,
11647 relative_utf16_range: Option<Range<isize>>,
11648 cx: &mut ViewContext<Self>,
11649 ) {
11650 if !self.input_enabled {
11651 cx.emit(EditorEvent::InputIgnored { text: text.into() });
11652 return;
11653 }
11654 if let Some(relative_utf16_range) = relative_utf16_range {
11655 let selections = self.selections.all::<OffsetUtf16>(cx);
11656 self.change_selections(None, cx, |s| {
11657 let new_ranges = selections.into_iter().map(|range| {
11658 let start = OffsetUtf16(
11659 range
11660 .head()
11661 .0
11662 .saturating_add_signed(relative_utf16_range.start),
11663 );
11664 let end = OffsetUtf16(
11665 range
11666 .head()
11667 .0
11668 .saturating_add_signed(relative_utf16_range.end),
11669 );
11670 start..end
11671 });
11672 s.select_ranges(new_ranges);
11673 });
11674 }
11675
11676 self.handle_input(text, cx);
11677 }
11678
11679 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
11680 let Some(project) = self.project.as_ref() else {
11681 return false;
11682 };
11683 let project = project.read(cx);
11684
11685 let mut supports = false;
11686 self.buffer().read(cx).for_each_buffer(|buffer| {
11687 if !supports {
11688 supports = project
11689 .language_servers_for_buffer(buffer.read(cx), cx)
11690 .any(
11691 |(_, server)| match server.capabilities().inlay_hint_provider {
11692 Some(lsp::OneOf::Left(enabled)) => enabled,
11693 Some(lsp::OneOf::Right(_)) => true,
11694 None => false,
11695 },
11696 )
11697 }
11698 });
11699 supports
11700 }
11701
11702 pub fn focus(&self, cx: &mut WindowContext) {
11703 cx.focus(&self.focus_handle)
11704 }
11705
11706 pub fn is_focused(&self, cx: &WindowContext) -> bool {
11707 self.focus_handle.is_focused(cx)
11708 }
11709
11710 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
11711 cx.emit(EditorEvent::Focused);
11712
11713 if let Some(descendant) = self
11714 .last_focused_descendant
11715 .take()
11716 .and_then(|descendant| descendant.upgrade())
11717 {
11718 cx.focus(&descendant);
11719 } else {
11720 if let Some(blame) = self.blame.as_ref() {
11721 blame.update(cx, GitBlame::focus)
11722 }
11723
11724 self.blink_manager.update(cx, BlinkManager::enable);
11725 self.show_cursor_names(cx);
11726 self.buffer.update(cx, |buffer, cx| {
11727 buffer.finalize_last_transaction(cx);
11728 if self.leader_peer_id.is_none() {
11729 buffer.set_active_selections(
11730 &self.selections.disjoint_anchors(),
11731 self.selections.line_mode,
11732 self.cursor_shape,
11733 cx,
11734 );
11735 }
11736 });
11737 }
11738 }
11739
11740 fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
11741 cx.emit(EditorEvent::FocusedIn)
11742 }
11743
11744 fn handle_focus_out(&mut self, event: FocusOutEvent, _cx: &mut ViewContext<Self>) {
11745 if event.blurred != self.focus_handle {
11746 self.last_focused_descendant = Some(event.blurred);
11747 }
11748 }
11749
11750 pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
11751 self.blink_manager.update(cx, BlinkManager::disable);
11752 self.buffer
11753 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
11754
11755 if let Some(blame) = self.blame.as_ref() {
11756 blame.update(cx, GitBlame::blur)
11757 }
11758 if !self.hover_state.focused(cx) {
11759 hide_hover(self, cx);
11760 }
11761
11762 self.hide_context_menu(cx);
11763 cx.emit(EditorEvent::Blurred);
11764 cx.notify();
11765 }
11766
11767 pub fn register_action<A: Action>(
11768 &mut self,
11769 listener: impl Fn(&A, &mut WindowContext) + 'static,
11770 ) -> Subscription {
11771 let id = self.next_editor_action_id.post_inc();
11772 let listener = Arc::new(listener);
11773 self.editor_actions.borrow_mut().insert(
11774 id,
11775 Box::new(move |cx| {
11776 let _view = cx.view().clone();
11777 let cx = cx.window_context();
11778 let listener = listener.clone();
11779 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
11780 let action = action.downcast_ref().unwrap();
11781 if phase == DispatchPhase::Bubble {
11782 listener(action, cx)
11783 }
11784 })
11785 }),
11786 );
11787
11788 let editor_actions = self.editor_actions.clone();
11789 Subscription::new(move || {
11790 editor_actions.borrow_mut().remove(&id);
11791 })
11792 }
11793
11794 pub fn file_header_size(&self) -> u8 {
11795 self.file_header_size
11796 }
11797
11798 pub fn revert(
11799 &mut self,
11800 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
11801 cx: &mut ViewContext<Self>,
11802 ) {
11803 self.buffer().update(cx, |multi_buffer, cx| {
11804 for (buffer_id, changes) in revert_changes {
11805 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
11806 buffer.update(cx, |buffer, cx| {
11807 buffer.edit(
11808 changes.into_iter().map(|(range, text)| {
11809 (range, text.to_string().map(Arc::<str>::from))
11810 }),
11811 None,
11812 cx,
11813 );
11814 });
11815 }
11816 }
11817 });
11818 self.change_selections(None, cx, |selections| selections.refresh());
11819 }
11820
11821 pub fn to_pixel_point(
11822 &mut self,
11823 source: multi_buffer::Anchor,
11824 editor_snapshot: &EditorSnapshot,
11825 cx: &mut ViewContext<Self>,
11826 ) -> Option<gpui::Point<Pixels>> {
11827 let text_layout_details = self.text_layout_details(cx);
11828 let line_height = text_layout_details
11829 .editor_style
11830 .text
11831 .line_height_in_pixels(cx.rem_size());
11832 let source_point = source.to_display_point(editor_snapshot);
11833 let first_visible_line = text_layout_details
11834 .scroll_anchor
11835 .anchor
11836 .to_display_point(editor_snapshot);
11837 if first_visible_line > source_point {
11838 return None;
11839 }
11840 let source_x = editor_snapshot.x_for_display_point(source_point, &text_layout_details);
11841 let source_y = line_height
11842 * ((source_point.row() - first_visible_line.row()).0 as f32
11843 - text_layout_details.scroll_anchor.offset.y);
11844 Some(gpui::Point::new(source_x, source_y))
11845 }
11846
11847 pub fn display_to_pixel_point(
11848 &mut self,
11849 source: DisplayPoint,
11850 editor_snapshot: &EditorSnapshot,
11851 cx: &mut ViewContext<Self>,
11852 ) -> Option<gpui::Point<Pixels>> {
11853 let line_height = self.style()?.text.line_height_in_pixels(cx.rem_size());
11854 let text_layout_details = self.text_layout_details(cx);
11855 let first_visible_line = text_layout_details
11856 .scroll_anchor
11857 .anchor
11858 .to_display_point(editor_snapshot);
11859 if first_visible_line > source {
11860 return None;
11861 }
11862 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
11863 let source_y = line_height * (source.row() - first_visible_line.row()).0 as f32;
11864 Some(gpui::Point::new(source_x, source_y))
11865 }
11866}
11867
11868fn hunks_for_selections(
11869 multi_buffer_snapshot: &MultiBufferSnapshot,
11870 selections: &[Selection<Anchor>],
11871) -> Vec<DiffHunk<MultiBufferRow>> {
11872 let buffer_rows_for_selections = selections.iter().map(|selection| {
11873 let head = selection.head();
11874 let tail = selection.tail();
11875 let start = MultiBufferRow(tail.to_point(&multi_buffer_snapshot).row);
11876 let end = MultiBufferRow(head.to_point(&multi_buffer_snapshot).row);
11877 if start > end {
11878 end..start
11879 } else {
11880 start..end
11881 }
11882 });
11883
11884 hunks_for_rows(buffer_rows_for_selections, multi_buffer_snapshot)
11885}
11886
11887pub fn hunks_for_rows(
11888 rows: impl Iterator<Item = Range<MultiBufferRow>>,
11889 multi_buffer_snapshot: &MultiBufferSnapshot,
11890) -> Vec<DiffHunk<MultiBufferRow>> {
11891 let mut hunks = Vec::new();
11892 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
11893 HashMap::default();
11894 for selected_multi_buffer_rows in rows {
11895 let query_rows =
11896 selected_multi_buffer_rows.start..selected_multi_buffer_rows.end.next_row();
11897 for hunk in multi_buffer_snapshot.git_diff_hunks_in_range(query_rows.clone()) {
11898 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
11899 // when the caret is just above or just below the deleted hunk.
11900 let allow_adjacent = hunk_status(&hunk) == DiffHunkStatus::Removed;
11901 let related_to_selection = if allow_adjacent {
11902 hunk.associated_range.overlaps(&query_rows)
11903 || hunk.associated_range.start == query_rows.end
11904 || hunk.associated_range.end == query_rows.start
11905 } else {
11906 // `selected_multi_buffer_rows` are inclusive (e.g. [2..2] means 2nd row is selected)
11907 // `hunk.associated_range` is exclusive (e.g. [2..3] means 2nd row is selected)
11908 hunk.associated_range.overlaps(&selected_multi_buffer_rows)
11909 || selected_multi_buffer_rows.end == hunk.associated_range.start
11910 };
11911 if related_to_selection {
11912 if !processed_buffer_rows
11913 .entry(hunk.buffer_id)
11914 .or_default()
11915 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
11916 {
11917 continue;
11918 }
11919 hunks.push(hunk);
11920 }
11921 }
11922 }
11923
11924 hunks
11925}
11926
11927pub trait CollaborationHub {
11928 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
11929 fn user_participant_indices<'a>(
11930 &self,
11931 cx: &'a AppContext,
11932 ) -> &'a HashMap<u64, ParticipantIndex>;
11933 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
11934}
11935
11936impl CollaborationHub for Model<Project> {
11937 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
11938 self.read(cx).collaborators()
11939 }
11940
11941 fn user_participant_indices<'a>(
11942 &self,
11943 cx: &'a AppContext,
11944 ) -> &'a HashMap<u64, ParticipantIndex> {
11945 self.read(cx).user_store().read(cx).participant_indices()
11946 }
11947
11948 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
11949 let this = self.read(cx);
11950 let user_ids = this.collaborators().values().map(|c| c.user_id);
11951 this.user_store().read_with(cx, |user_store, cx| {
11952 user_store.participant_names(user_ids, cx)
11953 })
11954 }
11955}
11956
11957pub trait CompletionProvider {
11958 fn completions(
11959 &self,
11960 buffer: &Model<Buffer>,
11961 buffer_position: text::Anchor,
11962 trigger: CompletionContext,
11963 cx: &mut ViewContext<Editor>,
11964 ) -> Task<Result<Vec<Completion>>>;
11965
11966 fn resolve_completions(
11967 &self,
11968 buffer: Model<Buffer>,
11969 completion_indices: Vec<usize>,
11970 completions: Arc<RwLock<Box<[Completion]>>>,
11971 cx: &mut ViewContext<Editor>,
11972 ) -> Task<Result<bool>>;
11973
11974 fn apply_additional_edits_for_completion(
11975 &self,
11976 buffer: Model<Buffer>,
11977 completion: Completion,
11978 push_to_history: bool,
11979 cx: &mut ViewContext<Editor>,
11980 ) -> Task<Result<Option<language::Transaction>>>;
11981
11982 fn is_completion_trigger(
11983 &self,
11984 buffer: &Model<Buffer>,
11985 position: language::Anchor,
11986 text: &str,
11987 trigger_in_words: bool,
11988 cx: &mut ViewContext<Editor>,
11989 ) -> bool;
11990}
11991
11992fn snippet_completions(
11993 project: &Project,
11994 buffer: &Model<Buffer>,
11995 buffer_position: text::Anchor,
11996 cx: &mut AppContext,
11997) -> Vec<Completion> {
11998 let language = buffer.read(cx).language_at(buffer_position);
11999 let language_name = language.as_ref().map(|language| language.lsp_id());
12000 let snippet_store = project.snippets().read(cx);
12001 let snippets = snippet_store.snippets_for(language_name, cx);
12002
12003 if snippets.is_empty() {
12004 return vec![];
12005 }
12006 let snapshot = buffer.read(cx).text_snapshot();
12007 let chunks = snapshot.reversed_chunks_in_range(text::Anchor::MIN..buffer_position);
12008
12009 let mut lines = chunks.lines();
12010 let Some(line_at) = lines.next().filter(|line| !line.is_empty()) else {
12011 return vec![];
12012 };
12013
12014 let scope = language.map(|language| language.default_scope());
12015 let mut last_word = line_at
12016 .chars()
12017 .rev()
12018 .take_while(|c| char_kind(&scope, *c) == CharKind::Word)
12019 .collect::<String>();
12020 last_word = last_word.chars().rev().collect();
12021 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
12022 let to_lsp = |point: &text::Anchor| {
12023 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
12024 point_to_lsp(end)
12025 };
12026 let lsp_end = to_lsp(&buffer_position);
12027 snippets
12028 .into_iter()
12029 .filter_map(|snippet| {
12030 let matching_prefix = snippet
12031 .prefix
12032 .iter()
12033 .find(|prefix| prefix.starts_with(&last_word))?;
12034 let start = as_offset - last_word.len();
12035 let start = snapshot.anchor_before(start);
12036 let range = start..buffer_position;
12037 let lsp_start = to_lsp(&start);
12038 let lsp_range = lsp::Range {
12039 start: lsp_start,
12040 end: lsp_end,
12041 };
12042 Some(Completion {
12043 old_range: range,
12044 new_text: snippet.body.clone(),
12045 label: CodeLabel {
12046 text: matching_prefix.clone(),
12047 runs: vec![],
12048 filter_range: 0..matching_prefix.len(),
12049 },
12050 server_id: LanguageServerId(usize::MAX),
12051 documentation: snippet
12052 .description
12053 .clone()
12054 .map(|description| Documentation::SingleLine(description)),
12055 lsp_completion: lsp::CompletionItem {
12056 label: snippet.prefix.first().unwrap().clone(),
12057 kind: Some(CompletionItemKind::SNIPPET),
12058 label_details: snippet.description.as_ref().map(|description| {
12059 lsp::CompletionItemLabelDetails {
12060 detail: Some(description.clone()),
12061 description: None,
12062 }
12063 }),
12064 insert_text_format: Some(InsertTextFormat::SNIPPET),
12065 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
12066 lsp::InsertReplaceEdit {
12067 new_text: snippet.body.clone(),
12068 insert: lsp_range,
12069 replace: lsp_range,
12070 },
12071 )),
12072 filter_text: Some(snippet.body.clone()),
12073 sort_text: Some(char::MAX.to_string()),
12074 ..Default::default()
12075 },
12076 confirm: None,
12077 show_new_completions_on_confirm: false,
12078 })
12079 })
12080 .collect()
12081}
12082
12083impl CompletionProvider for Model<Project> {
12084 fn completions(
12085 &self,
12086 buffer: &Model<Buffer>,
12087 buffer_position: text::Anchor,
12088 options: CompletionContext,
12089 cx: &mut ViewContext<Editor>,
12090 ) -> Task<Result<Vec<Completion>>> {
12091 self.update(cx, |project, cx| {
12092 let snippets = snippet_completions(project, buffer, buffer_position, cx);
12093 let project_completions = project.completions(&buffer, buffer_position, options, cx);
12094 cx.background_executor().spawn(async move {
12095 let mut completions = project_completions.await?;
12096 //let snippets = snippets.into_iter().;
12097 completions.extend(snippets);
12098 Ok(completions)
12099 })
12100 })
12101 }
12102
12103 fn resolve_completions(
12104 &self,
12105 buffer: Model<Buffer>,
12106 completion_indices: Vec<usize>,
12107 completions: Arc<RwLock<Box<[Completion]>>>,
12108 cx: &mut ViewContext<Editor>,
12109 ) -> Task<Result<bool>> {
12110 self.update(cx, |project, cx| {
12111 project.resolve_completions(buffer, completion_indices, completions, cx)
12112 })
12113 }
12114
12115 fn apply_additional_edits_for_completion(
12116 &self,
12117 buffer: Model<Buffer>,
12118 completion: Completion,
12119 push_to_history: bool,
12120 cx: &mut ViewContext<Editor>,
12121 ) -> Task<Result<Option<language::Transaction>>> {
12122 self.update(cx, |project, cx| {
12123 project.apply_additional_edits_for_completion(buffer, completion, push_to_history, cx)
12124 })
12125 }
12126
12127 fn is_completion_trigger(
12128 &self,
12129 buffer: &Model<Buffer>,
12130 position: language::Anchor,
12131 text: &str,
12132 trigger_in_words: bool,
12133 cx: &mut ViewContext<Editor>,
12134 ) -> bool {
12135 if !EditorSettings::get_global(cx).show_completions_on_input {
12136 return false;
12137 }
12138
12139 let mut chars = text.chars();
12140 let char = if let Some(char) = chars.next() {
12141 char
12142 } else {
12143 return false;
12144 };
12145 if chars.next().is_some() {
12146 return false;
12147 }
12148
12149 let buffer = buffer.read(cx);
12150 let scope = buffer.snapshot().language_scope_at(position);
12151 if trigger_in_words && char_kind(&scope, char) == CharKind::Word {
12152 return true;
12153 }
12154
12155 buffer
12156 .completion_triggers()
12157 .iter()
12158 .any(|string| string == text)
12159 }
12160}
12161
12162fn inlay_hint_settings(
12163 location: Anchor,
12164 snapshot: &MultiBufferSnapshot,
12165 cx: &mut ViewContext<'_, Editor>,
12166) -> InlayHintSettings {
12167 let file = snapshot.file_at(location);
12168 let language = snapshot.language_at(location);
12169 let settings = all_language_settings(file, cx);
12170 settings
12171 .language(language.map(|l| l.name()).as_deref())
12172 .inlay_hints
12173}
12174
12175fn consume_contiguous_rows(
12176 contiguous_row_selections: &mut Vec<Selection<Point>>,
12177 selection: &Selection<Point>,
12178 display_map: &DisplaySnapshot,
12179 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
12180) -> (MultiBufferRow, MultiBufferRow) {
12181 contiguous_row_selections.push(selection.clone());
12182 let start_row = MultiBufferRow(selection.start.row);
12183 let mut end_row = ending_row(selection, display_map);
12184
12185 while let Some(next_selection) = selections.peek() {
12186 if next_selection.start.row <= end_row.0 {
12187 end_row = ending_row(next_selection, display_map);
12188 contiguous_row_selections.push(selections.next().unwrap().clone());
12189 } else {
12190 break;
12191 }
12192 }
12193 (start_row, end_row)
12194}
12195
12196fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
12197 if next_selection.end.column > 0 || next_selection.is_empty() {
12198 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
12199 } else {
12200 MultiBufferRow(next_selection.end.row)
12201 }
12202}
12203
12204impl EditorSnapshot {
12205 pub fn remote_selections_in_range<'a>(
12206 &'a self,
12207 range: &'a Range<Anchor>,
12208 collaboration_hub: &dyn CollaborationHub,
12209 cx: &'a AppContext,
12210 ) -> impl 'a + Iterator<Item = RemoteSelection> {
12211 let participant_names = collaboration_hub.user_names(cx);
12212 let participant_indices = collaboration_hub.user_participant_indices(cx);
12213 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
12214 let collaborators_by_replica_id = collaborators_by_peer_id
12215 .iter()
12216 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
12217 .collect::<HashMap<_, _>>();
12218 self.buffer_snapshot
12219 .selections_in_range(range, false)
12220 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
12221 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
12222 let participant_index = participant_indices.get(&collaborator.user_id).copied();
12223 let user_name = participant_names.get(&collaborator.user_id).cloned();
12224 Some(RemoteSelection {
12225 replica_id,
12226 selection,
12227 cursor_shape,
12228 line_mode,
12229 participant_index,
12230 peer_id: collaborator.peer_id,
12231 user_name,
12232 })
12233 })
12234 }
12235
12236 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
12237 self.display_snapshot.buffer_snapshot.language_at(position)
12238 }
12239
12240 pub fn is_focused(&self) -> bool {
12241 self.is_focused
12242 }
12243
12244 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
12245 self.placeholder_text.as_ref()
12246 }
12247
12248 pub fn scroll_position(&self) -> gpui::Point<f32> {
12249 self.scroll_anchor.scroll_position(&self.display_snapshot)
12250 }
12251
12252 pub fn gutter_dimensions(
12253 &self,
12254 font_id: FontId,
12255 font_size: Pixels,
12256 em_width: Pixels,
12257 max_line_number_width: Pixels,
12258 cx: &AppContext,
12259 ) -> GutterDimensions {
12260 if !self.show_gutter {
12261 return GutterDimensions::default();
12262 }
12263 let descent = cx.text_system().descent(font_id, font_size);
12264
12265 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
12266 matches!(
12267 ProjectSettings::get_global(cx).git.git_gutter,
12268 Some(GitGutterSetting::TrackedFiles)
12269 )
12270 });
12271 let gutter_settings = EditorSettings::get_global(cx).gutter;
12272 let show_line_numbers = self
12273 .show_line_numbers
12274 .unwrap_or(gutter_settings.line_numbers);
12275 let line_gutter_width = if show_line_numbers {
12276 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
12277 let min_width_for_number_on_gutter = em_width * 4.0;
12278 max_line_number_width.max(min_width_for_number_on_gutter)
12279 } else {
12280 0.0.into()
12281 };
12282
12283 let show_code_actions = self
12284 .show_code_actions
12285 .unwrap_or(gutter_settings.code_actions);
12286
12287 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
12288
12289 let git_blame_entries_width = self
12290 .render_git_blame_gutter
12291 .then_some(em_width * GIT_BLAME_GUTTER_WIDTH_CHARS);
12292
12293 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
12294 left_padding += if show_code_actions || show_runnables {
12295 em_width * 3.0
12296 } else if show_git_gutter && show_line_numbers {
12297 em_width * 2.0
12298 } else if show_git_gutter || show_line_numbers {
12299 em_width
12300 } else {
12301 px(0.)
12302 };
12303
12304 let right_padding = if gutter_settings.folds && show_line_numbers {
12305 em_width * 4.0
12306 } else if gutter_settings.folds {
12307 em_width * 3.0
12308 } else if show_line_numbers {
12309 em_width
12310 } else {
12311 px(0.)
12312 };
12313
12314 GutterDimensions {
12315 left_padding,
12316 right_padding,
12317 width: line_gutter_width + left_padding + right_padding,
12318 margin: -descent,
12319 git_blame_entries_width,
12320 }
12321 }
12322
12323 pub fn render_fold_toggle(
12324 &self,
12325 buffer_row: MultiBufferRow,
12326 row_contains_cursor: bool,
12327 editor: View<Editor>,
12328 cx: &mut WindowContext,
12329 ) -> Option<AnyElement> {
12330 let folded = self.is_line_folded(buffer_row);
12331
12332 if let Some(crease) = self
12333 .crease_snapshot
12334 .query_row(buffer_row, &self.buffer_snapshot)
12335 {
12336 let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
12337 if folded {
12338 editor.update(cx, |editor, cx| {
12339 editor.fold_at(&crate::FoldAt { buffer_row }, cx)
12340 });
12341 } else {
12342 editor.update(cx, |editor, cx| {
12343 editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
12344 });
12345 }
12346 });
12347
12348 Some((crease.render_toggle)(
12349 buffer_row,
12350 folded,
12351 toggle_callback,
12352 cx,
12353 ))
12354 } else if folded
12355 || (self.starts_indent(buffer_row) && (row_contains_cursor || self.gutter_hovered))
12356 {
12357 Some(
12358 Disclosure::new(("indent-fold-indicator", buffer_row.0), !folded)
12359 .selected(folded)
12360 .on_click(cx.listener_for(&editor, move |this, _e, cx| {
12361 if folded {
12362 this.unfold_at(&UnfoldAt { buffer_row }, cx);
12363 } else {
12364 this.fold_at(&FoldAt { buffer_row }, cx);
12365 }
12366 }))
12367 .into_any_element(),
12368 )
12369 } else {
12370 None
12371 }
12372 }
12373
12374 pub fn render_crease_trailer(
12375 &self,
12376 buffer_row: MultiBufferRow,
12377 cx: &mut WindowContext,
12378 ) -> Option<AnyElement> {
12379 let folded = self.is_line_folded(buffer_row);
12380 let crease = self
12381 .crease_snapshot
12382 .query_row(buffer_row, &self.buffer_snapshot)?;
12383 Some((crease.render_trailer)(buffer_row, folded, cx))
12384 }
12385}
12386
12387impl Deref for EditorSnapshot {
12388 type Target = DisplaySnapshot;
12389
12390 fn deref(&self) -> &Self::Target {
12391 &self.display_snapshot
12392 }
12393}
12394
12395#[derive(Clone, Debug, PartialEq, Eq)]
12396pub enum EditorEvent {
12397 InputIgnored {
12398 text: Arc<str>,
12399 },
12400 InputHandled {
12401 utf16_range_to_replace: Option<Range<isize>>,
12402 text: Arc<str>,
12403 },
12404 ExcerptsAdded {
12405 buffer: Model<Buffer>,
12406 predecessor: ExcerptId,
12407 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
12408 },
12409 ExcerptsRemoved {
12410 ids: Vec<ExcerptId>,
12411 },
12412 ExcerptsEdited {
12413 ids: Vec<ExcerptId>,
12414 },
12415 ExcerptsExpanded {
12416 ids: Vec<ExcerptId>,
12417 },
12418 BufferEdited,
12419 Edited {
12420 transaction_id: clock::Lamport,
12421 },
12422 Reparsed(BufferId),
12423 Focused,
12424 FocusedIn,
12425 Blurred,
12426 DirtyChanged,
12427 Saved,
12428 TitleChanged,
12429 DiffBaseChanged,
12430 SelectionsChanged {
12431 local: bool,
12432 },
12433 ScrollPositionChanged {
12434 local: bool,
12435 autoscroll: bool,
12436 },
12437 Closed,
12438 TransactionUndone {
12439 transaction_id: clock::Lamport,
12440 },
12441 TransactionBegun {
12442 transaction_id: clock::Lamport,
12443 },
12444}
12445
12446impl EventEmitter<EditorEvent> for Editor {}
12447
12448impl FocusableView for Editor {
12449 fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
12450 self.focus_handle.clone()
12451 }
12452}
12453
12454impl Render for Editor {
12455 fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
12456 let settings = ThemeSettings::get_global(cx);
12457
12458 let text_style = match self.mode {
12459 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
12460 color: cx.theme().colors().editor_foreground,
12461 font_family: settings.ui_font.family.clone(),
12462 font_features: settings.ui_font.features.clone(),
12463 font_size: rems(0.875).into(),
12464 font_weight: settings.ui_font.weight,
12465 line_height: relative(settings.buffer_line_height.value()),
12466 ..Default::default()
12467 },
12468 EditorMode::Full => TextStyle {
12469 color: cx.theme().colors().editor_foreground,
12470 font_family: settings.buffer_font.family.clone(),
12471 font_features: settings.buffer_font.features.clone(),
12472 font_size: settings.buffer_font_size(cx).into(),
12473 font_weight: settings.buffer_font.weight,
12474 line_height: relative(settings.buffer_line_height.value()),
12475 ..Default::default()
12476 },
12477 };
12478
12479 let background = match self.mode {
12480 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
12481 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
12482 EditorMode::Full => cx.theme().colors().editor_background,
12483 };
12484
12485 EditorElement::new(
12486 cx.view(),
12487 EditorStyle {
12488 background,
12489 local_player: cx.theme().players().local(),
12490 text: text_style,
12491 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
12492 syntax: cx.theme().syntax().clone(),
12493 status: cx.theme().status().clone(),
12494 inlay_hints_style: HighlightStyle {
12495 color: Some(cx.theme().status().hint),
12496 ..HighlightStyle::default()
12497 },
12498 suggestions_style: HighlightStyle {
12499 color: Some(cx.theme().status().predictive),
12500 ..HighlightStyle::default()
12501 },
12502 },
12503 )
12504 }
12505}
12506
12507impl ViewInputHandler for Editor {
12508 fn text_for_range(
12509 &mut self,
12510 range_utf16: Range<usize>,
12511 cx: &mut ViewContext<Self>,
12512 ) -> Option<String> {
12513 Some(
12514 self.buffer
12515 .read(cx)
12516 .read(cx)
12517 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
12518 .collect(),
12519 )
12520 }
12521
12522 fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
12523 // Prevent the IME menu from appearing when holding down an alphabetic key
12524 // while input is disabled.
12525 if !self.input_enabled {
12526 return None;
12527 }
12528
12529 let range = self.selections.newest::<OffsetUtf16>(cx).range();
12530 Some(range.start.0..range.end.0)
12531 }
12532
12533 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
12534 let snapshot = self.buffer.read(cx).read(cx);
12535 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
12536 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
12537 }
12538
12539 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
12540 self.clear_highlights::<InputComposition>(cx);
12541 self.ime_transaction.take();
12542 }
12543
12544 fn replace_text_in_range(
12545 &mut self,
12546 range_utf16: Option<Range<usize>>,
12547 text: &str,
12548 cx: &mut ViewContext<Self>,
12549 ) {
12550 if !self.input_enabled {
12551 cx.emit(EditorEvent::InputIgnored { text: text.into() });
12552 return;
12553 }
12554
12555 self.transact(cx, |this, cx| {
12556 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
12557 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
12558 Some(this.selection_replacement_ranges(range_utf16, cx))
12559 } else {
12560 this.marked_text_ranges(cx)
12561 };
12562
12563 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
12564 let newest_selection_id = this.selections.newest_anchor().id;
12565 this.selections
12566 .all::<OffsetUtf16>(cx)
12567 .iter()
12568 .zip(ranges_to_replace.iter())
12569 .find_map(|(selection, range)| {
12570 if selection.id == newest_selection_id {
12571 Some(
12572 (range.start.0 as isize - selection.head().0 as isize)
12573 ..(range.end.0 as isize - selection.head().0 as isize),
12574 )
12575 } else {
12576 None
12577 }
12578 })
12579 });
12580
12581 cx.emit(EditorEvent::InputHandled {
12582 utf16_range_to_replace: range_to_replace,
12583 text: text.into(),
12584 });
12585
12586 if let Some(new_selected_ranges) = new_selected_ranges {
12587 this.change_selections(None, cx, |selections| {
12588 selections.select_ranges(new_selected_ranges)
12589 });
12590 this.backspace(&Default::default(), cx);
12591 }
12592
12593 this.handle_input(text, cx);
12594 });
12595
12596 if let Some(transaction) = self.ime_transaction {
12597 self.buffer.update(cx, |buffer, cx| {
12598 buffer.group_until_transaction(transaction, cx);
12599 });
12600 }
12601
12602 self.unmark_text(cx);
12603 }
12604
12605 fn replace_and_mark_text_in_range(
12606 &mut self,
12607 range_utf16: Option<Range<usize>>,
12608 text: &str,
12609 new_selected_range_utf16: Option<Range<usize>>,
12610 cx: &mut ViewContext<Self>,
12611 ) {
12612 if !self.input_enabled {
12613 return;
12614 }
12615
12616 let transaction = self.transact(cx, |this, cx| {
12617 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
12618 let snapshot = this.buffer.read(cx).read(cx);
12619 if let Some(relative_range_utf16) = range_utf16.as_ref() {
12620 for marked_range in &mut marked_ranges {
12621 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
12622 marked_range.start.0 += relative_range_utf16.start;
12623 marked_range.start =
12624 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
12625 marked_range.end =
12626 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
12627 }
12628 }
12629 Some(marked_ranges)
12630 } else 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 None
12635 };
12636
12637 let range_to_replace = ranges_to_replace.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(ranges) = ranges_to_replace {
12661 this.change_selections(None, cx, |s| s.select_ranges(ranges));
12662 }
12663
12664 let marked_ranges = {
12665 let snapshot = this.buffer.read(cx).read(cx);
12666 this.selections
12667 .disjoint_anchors()
12668 .iter()
12669 .map(|selection| {
12670 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
12671 })
12672 .collect::<Vec<_>>()
12673 };
12674
12675 if text.is_empty() {
12676 this.unmark_text(cx);
12677 } else {
12678 this.highlight_text::<InputComposition>(
12679 marked_ranges.clone(),
12680 HighlightStyle {
12681 underline: Some(UnderlineStyle {
12682 thickness: px(1.),
12683 color: None,
12684 wavy: false,
12685 }),
12686 ..Default::default()
12687 },
12688 cx,
12689 );
12690 }
12691
12692 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
12693 let use_autoclose = this.use_autoclose;
12694 let use_auto_surround = this.use_auto_surround;
12695 this.set_use_autoclose(false);
12696 this.set_use_auto_surround(false);
12697 this.handle_input(text, cx);
12698 this.set_use_autoclose(use_autoclose);
12699 this.set_use_auto_surround(use_auto_surround);
12700
12701 if let Some(new_selected_range) = new_selected_range_utf16 {
12702 let snapshot = this.buffer.read(cx).read(cx);
12703 let new_selected_ranges = marked_ranges
12704 .into_iter()
12705 .map(|marked_range| {
12706 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
12707 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
12708 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
12709 snapshot.clip_offset_utf16(new_start, Bias::Left)
12710 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
12711 })
12712 .collect::<Vec<_>>();
12713
12714 drop(snapshot);
12715 this.change_selections(None, cx, |selections| {
12716 selections.select_ranges(new_selected_ranges)
12717 });
12718 }
12719 });
12720
12721 self.ime_transaction = self.ime_transaction.or(transaction);
12722 if let Some(transaction) = self.ime_transaction {
12723 self.buffer.update(cx, |buffer, cx| {
12724 buffer.group_until_transaction(transaction, cx);
12725 });
12726 }
12727
12728 if self.text_highlights::<InputComposition>(cx).is_none() {
12729 self.ime_transaction.take();
12730 }
12731 }
12732
12733 fn bounds_for_range(
12734 &mut self,
12735 range_utf16: Range<usize>,
12736 element_bounds: gpui::Bounds<Pixels>,
12737 cx: &mut ViewContext<Self>,
12738 ) -> Option<gpui::Bounds<Pixels>> {
12739 let text_layout_details = self.text_layout_details(cx);
12740 let style = &text_layout_details.editor_style;
12741 let font_id = cx.text_system().resolve_font(&style.text.font());
12742 let font_size = style.text.font_size.to_pixels(cx.rem_size());
12743 let line_height = style.text.line_height_in_pixels(cx.rem_size());
12744
12745 let em_width = cx
12746 .text_system()
12747 .typographic_bounds(font_id, font_size, 'm')
12748 .unwrap()
12749 .size
12750 .width;
12751
12752 let snapshot = self.snapshot(cx);
12753 let scroll_position = snapshot.scroll_position();
12754 let scroll_left = scroll_position.x * em_width;
12755
12756 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
12757 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
12758 + self.gutter_dimensions.width;
12759 let y = line_height * (start.row().as_f32() - scroll_position.y);
12760
12761 Some(Bounds {
12762 origin: element_bounds.origin + point(x, y),
12763 size: size(em_width, line_height),
12764 })
12765 }
12766}
12767
12768trait SelectionExt {
12769 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
12770 fn spanned_rows(
12771 &self,
12772 include_end_if_at_line_start: bool,
12773 map: &DisplaySnapshot,
12774 ) -> Range<MultiBufferRow>;
12775}
12776
12777impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
12778 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
12779 let start = self
12780 .start
12781 .to_point(&map.buffer_snapshot)
12782 .to_display_point(map);
12783 let end = self
12784 .end
12785 .to_point(&map.buffer_snapshot)
12786 .to_display_point(map);
12787 if self.reversed {
12788 end..start
12789 } else {
12790 start..end
12791 }
12792 }
12793
12794 fn spanned_rows(
12795 &self,
12796 include_end_if_at_line_start: bool,
12797 map: &DisplaySnapshot,
12798 ) -> Range<MultiBufferRow> {
12799 let start = self.start.to_point(&map.buffer_snapshot);
12800 let mut end = self.end.to_point(&map.buffer_snapshot);
12801 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
12802 end.row -= 1;
12803 }
12804
12805 let buffer_start = map.prev_line_boundary(start).0;
12806 let buffer_end = map.next_line_boundary(end).0;
12807 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
12808 }
12809}
12810
12811impl<T: InvalidationRegion> InvalidationStack<T> {
12812 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
12813 where
12814 S: Clone + ToOffset,
12815 {
12816 while let Some(region) = self.last() {
12817 let all_selections_inside_invalidation_ranges =
12818 if selections.len() == region.ranges().len() {
12819 selections
12820 .iter()
12821 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
12822 .all(|(selection, invalidation_range)| {
12823 let head = selection.head().to_offset(buffer);
12824 invalidation_range.start <= head && invalidation_range.end >= head
12825 })
12826 } else {
12827 false
12828 };
12829
12830 if all_selections_inside_invalidation_ranges {
12831 break;
12832 } else {
12833 self.pop();
12834 }
12835 }
12836 }
12837}
12838
12839impl<T> Default for InvalidationStack<T> {
12840 fn default() -> Self {
12841 Self(Default::default())
12842 }
12843}
12844
12845impl<T> Deref for InvalidationStack<T> {
12846 type Target = Vec<T>;
12847
12848 fn deref(&self) -> &Self::Target {
12849 &self.0
12850 }
12851}
12852
12853impl<T> DerefMut for InvalidationStack<T> {
12854 fn deref_mut(&mut self) -> &mut Self::Target {
12855 &mut self.0
12856 }
12857}
12858
12859impl InvalidationRegion for SnippetState {
12860 fn ranges(&self) -> &[Range<Anchor>] {
12861 &self.ranges[self.active_index]
12862 }
12863}
12864
12865pub fn diagnostic_block_renderer(
12866 diagnostic: Diagnostic,
12867 max_message_rows: Option<u8>,
12868 allow_closing: bool,
12869 _is_valid: bool,
12870) -> RenderBlock {
12871 let (text_without_backticks, code_ranges) =
12872 highlight_diagnostic_message(&diagnostic, max_message_rows);
12873
12874 Box::new(move |cx: &mut BlockContext| {
12875 let group_id: SharedString = cx.block_id.to_string().into();
12876
12877 let mut text_style = cx.text_style().clone();
12878 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
12879 let theme_settings = ThemeSettings::get_global(cx);
12880 text_style.font_family = theme_settings.buffer_font.family.clone();
12881 text_style.font_style = theme_settings.buffer_font.style;
12882 text_style.font_features = theme_settings.buffer_font.features.clone();
12883 text_style.font_weight = theme_settings.buffer_font.weight;
12884
12885 let multi_line_diagnostic = diagnostic.message.contains('\n');
12886
12887 let buttons = |diagnostic: &Diagnostic, block_id: BlockId| {
12888 if multi_line_diagnostic {
12889 v_flex()
12890 } else {
12891 h_flex()
12892 }
12893 .when(allow_closing, |div| {
12894 div.children(diagnostic.is_primary.then(|| {
12895 IconButton::new(("close-block", EntityId::from(block_id)), IconName::XCircle)
12896 .icon_color(Color::Muted)
12897 .size(ButtonSize::Compact)
12898 .style(ButtonStyle::Transparent)
12899 .visible_on_hover(group_id.clone())
12900 .on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
12901 .tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
12902 }))
12903 })
12904 .child(
12905 IconButton::new(("copy-block", EntityId::from(block_id)), IconName::Copy)
12906 .icon_color(Color::Muted)
12907 .size(ButtonSize::Compact)
12908 .style(ButtonStyle::Transparent)
12909 .visible_on_hover(group_id.clone())
12910 .on_click({
12911 let message = diagnostic.message.clone();
12912 move |_click, cx| cx.write_to_clipboard(ClipboardItem::new(message.clone()))
12913 })
12914 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
12915 )
12916 };
12917
12918 let icon_size = buttons(&diagnostic, cx.block_id)
12919 .into_any_element()
12920 .layout_as_root(AvailableSpace::min_size(), cx);
12921
12922 h_flex()
12923 .id(cx.block_id)
12924 .group(group_id.clone())
12925 .relative()
12926 .size_full()
12927 .pl(cx.gutter_dimensions.width)
12928 .w(cx.max_width + cx.gutter_dimensions.width)
12929 .child(
12930 div()
12931 .flex()
12932 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
12933 .flex_shrink(),
12934 )
12935 .child(buttons(&diagnostic, cx.block_id))
12936 .child(div().flex().flex_shrink_0().child(
12937 StyledText::new(text_without_backticks.clone()).with_highlights(
12938 &text_style,
12939 code_ranges.iter().map(|range| {
12940 (
12941 range.clone(),
12942 HighlightStyle {
12943 font_weight: Some(FontWeight::BOLD),
12944 ..Default::default()
12945 },
12946 )
12947 }),
12948 ),
12949 ))
12950 .into_any_element()
12951 })
12952}
12953
12954pub fn highlight_diagnostic_message(
12955 diagnostic: &Diagnostic,
12956 mut max_message_rows: Option<u8>,
12957) -> (SharedString, Vec<Range<usize>>) {
12958 let mut text_without_backticks = String::new();
12959 let mut code_ranges = Vec::new();
12960
12961 if let Some(source) = &diagnostic.source {
12962 text_without_backticks.push_str(&source);
12963 code_ranges.push(0..source.len());
12964 text_without_backticks.push_str(": ");
12965 }
12966
12967 let mut prev_offset = 0;
12968 let mut in_code_block = false;
12969 let has_row_limit = max_message_rows.is_some();
12970 let mut newline_indices = diagnostic
12971 .message
12972 .match_indices('\n')
12973 .filter(|_| has_row_limit)
12974 .map(|(ix, _)| ix)
12975 .fuse()
12976 .peekable();
12977
12978 for (quote_ix, _) in diagnostic
12979 .message
12980 .match_indices('`')
12981 .chain([(diagnostic.message.len(), "")])
12982 {
12983 let mut first_newline_ix = None;
12984 let mut last_newline_ix = None;
12985 while let Some(newline_ix) = newline_indices.peek() {
12986 if *newline_ix < quote_ix {
12987 if first_newline_ix.is_none() {
12988 first_newline_ix = Some(*newline_ix);
12989 }
12990 last_newline_ix = Some(*newline_ix);
12991
12992 if let Some(rows_left) = &mut max_message_rows {
12993 if *rows_left == 0 {
12994 break;
12995 } else {
12996 *rows_left -= 1;
12997 }
12998 }
12999 let _ = newline_indices.next();
13000 } else {
13001 break;
13002 }
13003 }
13004 let prev_len = text_without_backticks.len();
13005 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
13006 text_without_backticks.push_str(new_text);
13007 if in_code_block {
13008 code_ranges.push(prev_len..text_without_backticks.len());
13009 }
13010 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
13011 in_code_block = !in_code_block;
13012 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
13013 text_without_backticks.push_str("...");
13014 break;
13015 }
13016 }
13017
13018 (text_without_backticks.into(), code_ranges)
13019}
13020
13021fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
13022 match severity {
13023 DiagnosticSeverity::ERROR => colors.error,
13024 DiagnosticSeverity::WARNING => colors.warning,
13025 DiagnosticSeverity::INFORMATION => colors.info,
13026 DiagnosticSeverity::HINT => colors.info,
13027 _ => colors.ignored,
13028 }
13029}
13030
13031pub fn styled_runs_for_code_label<'a>(
13032 label: &'a CodeLabel,
13033 syntax_theme: &'a theme::SyntaxTheme,
13034) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
13035 let fade_out = HighlightStyle {
13036 fade_out: Some(0.35),
13037 ..Default::default()
13038 };
13039
13040 let mut prev_end = label.filter_range.end;
13041 label
13042 .runs
13043 .iter()
13044 .enumerate()
13045 .flat_map(move |(ix, (range, highlight_id))| {
13046 let style = if let Some(style) = highlight_id.style(syntax_theme) {
13047 style
13048 } else {
13049 return Default::default();
13050 };
13051 let mut muted_style = style;
13052 muted_style.highlight(fade_out);
13053
13054 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
13055 if range.start >= label.filter_range.end {
13056 if range.start > prev_end {
13057 runs.push((prev_end..range.start, fade_out));
13058 }
13059 runs.push((range.clone(), muted_style));
13060 } else if range.end <= label.filter_range.end {
13061 runs.push((range.clone(), style));
13062 } else {
13063 runs.push((range.start..label.filter_range.end, style));
13064 runs.push((label.filter_range.end..range.end, muted_style));
13065 }
13066 prev_end = cmp::max(prev_end, range.end);
13067
13068 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
13069 runs.push((prev_end..label.text.len(), fade_out));
13070 }
13071
13072 runs
13073 })
13074}
13075
13076pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
13077 let mut prev_index = 0;
13078 let mut prev_codepoint: Option<char> = None;
13079 text.char_indices()
13080 .chain([(text.len(), '\0')])
13081 .filter_map(move |(index, codepoint)| {
13082 let prev_codepoint = prev_codepoint.replace(codepoint)?;
13083 let is_boundary = index == text.len()
13084 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
13085 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
13086 if is_boundary {
13087 let chunk = &text[prev_index..index];
13088 prev_index = index;
13089 Some(chunk)
13090 } else {
13091 None
13092 }
13093 })
13094}
13095
13096pub trait RangeToAnchorExt: Sized {
13097 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
13098
13099 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
13100 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
13101 anchor_range.start.to_display_point(&snapshot)..anchor_range.end.to_display_point(&snapshot)
13102 }
13103}
13104
13105impl<T: ToOffset> RangeToAnchorExt for Range<T> {
13106 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
13107 let start_offset = self.start.to_offset(snapshot);
13108 let end_offset = self.end.to_offset(snapshot);
13109 if start_offset == end_offset {
13110 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
13111 } else {
13112 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
13113 }
13114 }
13115}
13116
13117pub trait RowExt {
13118 fn as_f32(&self) -> f32;
13119
13120 fn next_row(&self) -> Self;
13121
13122 fn previous_row(&self) -> Self;
13123
13124 fn minus(&self, other: Self) -> u32;
13125}
13126
13127impl RowExt for DisplayRow {
13128 fn as_f32(&self) -> f32 {
13129 self.0 as f32
13130 }
13131
13132 fn next_row(&self) -> Self {
13133 Self(self.0 + 1)
13134 }
13135
13136 fn previous_row(&self) -> Self {
13137 Self(self.0.saturating_sub(1))
13138 }
13139
13140 fn minus(&self, other: Self) -> u32 {
13141 self.0 - other.0
13142 }
13143}
13144
13145impl RowExt for MultiBufferRow {
13146 fn as_f32(&self) -> f32 {
13147 self.0 as f32
13148 }
13149
13150 fn next_row(&self) -> Self {
13151 Self(self.0 + 1)
13152 }
13153
13154 fn previous_row(&self) -> Self {
13155 Self(self.0.saturating_sub(1))
13156 }
13157
13158 fn minus(&self, other: Self) -> u32 {
13159 self.0 - other.0
13160 }
13161}
13162
13163trait RowRangeExt {
13164 type Row;
13165
13166 fn len(&self) -> usize;
13167
13168 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
13169}
13170
13171impl RowRangeExt for Range<MultiBufferRow> {
13172 type Row = MultiBufferRow;
13173
13174 fn len(&self) -> usize {
13175 (self.end.0 - self.start.0) as usize
13176 }
13177
13178 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
13179 (self.start.0..self.end.0).map(MultiBufferRow)
13180 }
13181}
13182
13183impl RowRangeExt for Range<DisplayRow> {
13184 type Row = DisplayRow;
13185
13186 fn len(&self) -> usize {
13187 (self.end.0 - self.start.0) as usize
13188 }
13189
13190 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
13191 (self.start.0..self.end.0).map(DisplayRow)
13192 }
13193}
13194
13195fn hunk_status(hunk: &DiffHunk<MultiBufferRow>) -> DiffHunkStatus {
13196 if hunk.diff_base_byte_range.is_empty() {
13197 DiffHunkStatus::Added
13198 } else if hunk.associated_range.is_empty() {
13199 DiffHunkStatus::Removed
13200 } else {
13201 DiffHunkStatus::Modified
13202 }
13203}