1#![allow(rustdoc::private_intra_doc_links)]
2//! This is the place where everything editor-related is stored (data-wise) and displayed (ui-wise).
3//! The main point of interest in this crate is [`Editor`] type, which is used in every other Zed part as a user input element.
4//! It comes in different flavors: single line, multiline and a fixed height one.
5//!
6//! Editor contains of multiple large submodules:
7//! * [`element`] — the place where all rendering happens
8//! * [`display_map`] - chunks up text in the editor into the logical blocks, establishes coordinates and mapping between each of them.
9//! Contains all metadata related to text transformations (folds, fake inlay text insertions, soft wraps, tab markup, etc.).
10//! * [`inlay_hint_cache`] - is a storage of inlay hints out of LSP requests, responsible for querying LSP and updating `display_map`'s state accordingly.
11//!
12//! All other submodules and structs are mostly concerned with holding editor data about the way it displays current buffer region(s).
13//!
14//! If you're looking to improve Vim mode, you should check out Vim crate that wraps Editor and overrides its behavior.
15pub mod actions;
16mod blame_entry_tooltip;
17mod blink_manager;
18mod debounced_delay;
19pub mod display_map;
20mod editor_settings;
21mod editor_settings_controls;
22mod element;
23mod git;
24mod highlight_matching_bracket;
25mod hover_links;
26mod hover_popover;
27mod hunk_diff;
28mod indent_guides;
29mod inlay_hint_cache;
30mod inline_completion_provider;
31pub mod items;
32mod linked_editing_ranges;
33mod mouse_context_menu;
34pub mod movement;
35mod persistence;
36mod rust_analyzer_ext;
37pub mod scroll;
38mod selections_collection;
39pub mod tasks;
40
41#[cfg(test)]
42mod editor_tests;
43mod signature_help;
44#[cfg(any(test, feature = "test-support"))]
45pub mod test;
46
47use ::git::diff::{DiffHunk, DiffHunkStatus};
48use ::git::{parse_git_remote_url, BuildPermalinkParams, GitHostingProviderRegistry};
49pub(crate) use actions::*;
50use aho_corasick::AhoCorasick;
51use anyhow::{anyhow, Context as _, Result};
52use blink_manager::BlinkManager;
53use client::{Collaborator, ParticipantIndex};
54use clock::ReplicaId;
55use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
56use convert_case::{Case, Casing};
57use debounced_delay::DebouncedDelay;
58use display_map::*;
59pub use display_map::{DisplayPoint, FoldPlaceholder};
60pub use editor_settings::{CurrentLineHighlight, EditorSettings};
61pub use editor_settings_controls::*;
62use element::LineWithInvisibles;
63pub use element::{
64 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
65};
66use futures::FutureExt;
67use fuzzy::{StringMatch, StringMatchCandidate};
68use git::blame::GitBlame;
69use git::diff_hunk_to_display;
70use gpui::{
71 div, impl_actions, point, prelude::*, px, relative, size, uniform_list, Action, AnyElement,
72 AppContext, AsyncWindowContext, AvailableSpace, BackgroundExecutor, Bounds, ClipboardItem,
73 Context, DispatchPhase, ElementId, EntityId, EventEmitter, FocusHandle, FocusOutEvent,
74 FocusableView, FontId, FontWeight, HighlightStyle, Hsla, InteractiveText, KeyContext,
75 ListSizingBehavior, Model, MouseButton, PaintQuad, ParentElement, Pixels, Render, SharedString,
76 Size, StrikethroughStyle, Styled, StyledText, Subscription, Task, TextStyle, UnderlineStyle,
77 UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext, WeakFocusHandle,
78 WeakView, WindowContext,
79};
80use highlight_matching_bracket::refresh_matching_bracket_highlights;
81use hover_popover::{hide_hover, HoverState};
82use hunk_diff::ExpandedHunks;
83pub(crate) use hunk_diff::HoveredHunk;
84use indent_guides::ActiveIndentGuidesState;
85use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
86pub use inline_completion_provider::*;
87pub use items::MAX_TAB_TITLE_LEN;
88use itertools::Itertools;
89use language::{
90 char_kind,
91 language_settings::{self, all_language_settings, InlayHintSettings},
92 markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
93 CursorShape, Diagnostic, Documentation, IndentKind, IndentSize, Language, OffsetRangeExt,
94 Point, Selection, SelectionGoal, TransactionId,
95};
96use language::{point_to_lsp, BufferRow, Runnable, RunnableRange};
97use linked_editing_ranges::refresh_linked_ranges;
98use task::{ResolvedTask, TaskTemplate, TaskVariables};
99
100use hover_links::{HoverLink, HoveredLinkState, InlayHighlight};
101pub use lsp::CompletionContext;
102use lsp::{
103 CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity, InsertTextFormat,
104 LanguageServerId,
105};
106use mouse_context_menu::MouseContextMenu;
107use movement::TextLayoutDetails;
108pub use multi_buffer::{
109 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
110 ToPoint,
111};
112use multi_buffer::{ExpandExcerptDirection, MultiBufferPoint, MultiBufferRow, ToOffsetUtf16};
113use ordered_float::OrderedFloat;
114use parking_lot::{Mutex, RwLock};
115use project::project_settings::{GitGutterSetting, ProjectSettings};
116use project::{
117 CodeAction, Completion, FormatTrigger, Item, Location, Project, ProjectPath,
118 ProjectTransaction, TaskSourceKind, WorktreeId,
119};
120use rand::prelude::*;
121use rpc::{proto::*, ErrorExt};
122use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
123use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
124use serde::{Deserialize, Serialize};
125use settings::{update_settings_file, Settings, SettingsStore};
126use smallvec::SmallVec;
127use snippet::Snippet;
128use std::{
129 any::TypeId,
130 borrow::Cow,
131 cell::RefCell,
132 cmp::{self, Ordering, Reverse},
133 mem,
134 num::NonZeroU32,
135 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
136 path::{Path, PathBuf},
137 rc::Rc,
138 sync::Arc,
139 time::{Duration, Instant},
140};
141pub use sum_tree::Bias;
142use sum_tree::TreeMap;
143use text::{BufferId, OffsetUtf16, Rope};
144use theme::{
145 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
146 ThemeColors, ThemeSettings,
147};
148use ui::{
149 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize,
150 ListItem, Popover, Tooltip,
151};
152use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
153use workspace::item::{ItemHandle, PreviewTabsSettings};
154use workspace::notifications::{DetachAndPromptErr, NotificationId};
155use workspace::{
156 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
157};
158use workspace::{OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
159
160use crate::hover_links::find_url;
161use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
162
163pub const FILE_HEADER_HEIGHT: u32 = 1;
164pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
165pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u32 = 1;
166pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
167const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
168const MAX_LINE_LEN: usize = 1024;
169const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
170const MAX_SELECTION_HISTORY_LEN: usize = 1024;
171pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
172#[doc(hidden)]
173pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
174#[doc(hidden)]
175pub const DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
176
177pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
178pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
179
180pub fn render_parsed_markdown(
181 element_id: impl Into<ElementId>,
182 parsed: &language::ParsedMarkdown,
183 editor_style: &EditorStyle,
184 workspace: Option<WeakView<Workspace>>,
185 cx: &mut WindowContext,
186) -> InteractiveText {
187 let code_span_background_color = cx
188 .theme()
189 .colors()
190 .editor_document_highlight_read_background;
191
192 let highlights = gpui::combine_highlights(
193 parsed.highlights.iter().filter_map(|(range, highlight)| {
194 let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
195 Some((range.clone(), highlight))
196 }),
197 parsed
198 .regions
199 .iter()
200 .zip(&parsed.region_ranges)
201 .filter_map(|(region, range)| {
202 if region.code {
203 Some((
204 range.clone(),
205 HighlightStyle {
206 background_color: Some(code_span_background_color),
207 ..Default::default()
208 },
209 ))
210 } else {
211 None
212 }
213 }),
214 );
215
216 let mut links = Vec::new();
217 let mut link_ranges = Vec::new();
218 for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
219 if let Some(link) = region.link.clone() {
220 links.push(link);
221 link_ranges.push(range.clone());
222 }
223 }
224
225 InteractiveText::new(
226 element_id,
227 StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
228 )
229 .on_click(link_ranges, move |clicked_range_ix, cx| {
230 match &links[clicked_range_ix] {
231 markdown::Link::Web { url } => cx.open_url(url),
232 markdown::Link::Path { path } => {
233 if let Some(workspace) = &workspace {
234 _ = workspace.update(cx, |workspace, cx| {
235 workspace.open_abs_path(path.clone(), false, cx).detach();
236 });
237 }
238 }
239 }
240 })
241}
242
243#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
244pub(crate) enum InlayId {
245 Suggestion(usize),
246 Hint(usize),
247}
248
249impl InlayId {
250 fn id(&self) -> usize {
251 match self {
252 Self::Suggestion(id) => *id,
253 Self::Hint(id) => *id,
254 }
255 }
256}
257
258enum DiffRowHighlight {}
259enum DocumentHighlightRead {}
260enum DocumentHighlightWrite {}
261enum InputComposition {}
262
263#[derive(Copy, Clone, PartialEq, Eq)]
264pub enum Direction {
265 Prev,
266 Next,
267}
268
269pub fn init_settings(cx: &mut AppContext) {
270 EditorSettings::register(cx);
271}
272
273pub fn init(cx: &mut AppContext) {
274 init_settings(cx);
275
276 workspace::register_project_item::<Editor>(cx);
277 workspace::FollowableViewRegistry::register::<Editor>(cx);
278 workspace::register_serializable_item::<Editor>(cx);
279
280 cx.observe_new_views(
281 |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
282 workspace.register_action(Editor::new_file);
283 workspace.register_action(Editor::new_file_in_direction);
284 },
285 )
286 .detach();
287
288 cx.on_action(move |_: &workspace::NewFile, cx| {
289 let app_state = workspace::AppState::global(cx);
290 if let Some(app_state) = app_state.upgrade() {
291 workspace::open_new(app_state, cx, |workspace, cx| {
292 Editor::new_file(workspace, &Default::default(), cx)
293 })
294 .detach();
295 }
296 });
297 cx.on_action(move |_: &workspace::NewWindow, cx| {
298 let app_state = workspace::AppState::global(cx);
299 if let Some(app_state) = app_state.upgrade() {
300 workspace::open_new(app_state, cx, |workspace, cx| {
301 Editor::new_file(workspace, &Default::default(), cx)
302 })
303 .detach();
304 }
305 });
306}
307
308pub struct SearchWithinRange;
309
310trait InvalidationRegion {
311 fn ranges(&self) -> &[Range<Anchor>];
312}
313
314#[derive(Clone, Debug, PartialEq)]
315pub enum SelectPhase {
316 Begin {
317 position: DisplayPoint,
318 add: bool,
319 click_count: usize,
320 },
321 BeginColumnar {
322 position: DisplayPoint,
323 reset: bool,
324 goal_column: u32,
325 },
326 Extend {
327 position: DisplayPoint,
328 click_count: usize,
329 },
330 Update {
331 position: DisplayPoint,
332 goal_column: u32,
333 scroll_delta: gpui::Point<f32>,
334 },
335 End,
336}
337
338#[derive(Clone, Debug)]
339pub enum SelectMode {
340 Character,
341 Word(Range<Anchor>),
342 Line(Range<Anchor>),
343 All,
344}
345
346#[derive(Copy, Clone, PartialEq, Eq, Debug)]
347pub enum EditorMode {
348 SingleLine { auto_width: bool },
349 AutoHeight { max_lines: usize },
350 Full,
351}
352
353#[derive(Clone, Debug)]
354pub enum SoftWrap {
355 None,
356 PreferLine,
357 EditorWidth,
358 Column(u32),
359}
360
361#[derive(Clone)]
362pub struct EditorStyle {
363 pub background: Hsla,
364 pub local_player: PlayerColor,
365 pub text: TextStyle,
366 pub scrollbar_width: Pixels,
367 pub syntax: Arc<SyntaxTheme>,
368 pub status: StatusColors,
369 pub inlay_hints_style: HighlightStyle,
370 pub suggestions_style: HighlightStyle,
371}
372
373impl Default for EditorStyle {
374 fn default() -> Self {
375 Self {
376 background: Hsla::default(),
377 local_player: PlayerColor::default(),
378 text: TextStyle::default(),
379 scrollbar_width: Pixels::default(),
380 syntax: Default::default(),
381 // HACK: Status colors don't have a real default.
382 // We should look into removing the status colors from the editor
383 // style and retrieve them directly from the theme.
384 status: StatusColors::dark(),
385 inlay_hints_style: HighlightStyle::default(),
386 suggestions_style: HighlightStyle::default(),
387 }
388 }
389}
390
391type CompletionId = usize;
392
393#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
394struct EditorActionId(usize);
395
396impl EditorActionId {
397 pub fn post_inc(&mut self) -> Self {
398 let answer = self.0;
399
400 *self = Self(answer + 1);
401
402 Self(answer)
403 }
404}
405
406// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
407// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
408
409type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
410type GutterHighlight = (fn(&AppContext) -> Hsla, Arc<[Range<Anchor>]>);
411
412#[derive(Default)]
413struct ScrollbarMarkerState {
414 scrollbar_size: Size<Pixels>,
415 dirty: bool,
416 markers: Arc<[PaintQuad]>,
417 pending_refresh: Option<Task<Result<()>>>,
418}
419
420impl ScrollbarMarkerState {
421 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
422 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
423 }
424}
425
426#[derive(Clone, Debug)]
427struct RunnableTasks {
428 templates: Vec<(TaskSourceKind, TaskTemplate)>,
429 offset: MultiBufferOffset,
430 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
431 column: u32,
432 // Values of all named captures, including those starting with '_'
433 extra_variables: HashMap<String, String>,
434 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
435 context_range: Range<BufferOffset>,
436}
437
438#[derive(Clone)]
439struct ResolvedTasks {
440 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
441 position: Anchor,
442}
443#[derive(Copy, Clone, Debug)]
444struct MultiBufferOffset(usize);
445#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
446struct BufferOffset(usize);
447/// Zed's primary text input `View`, allowing users to edit a [`MultiBuffer`]
448///
449/// See the [module level documentation](self) for more information.
450pub struct Editor {
451 focus_handle: FocusHandle,
452 last_focused_descendant: Option<WeakFocusHandle>,
453 /// The text buffer being edited
454 buffer: Model<MultiBuffer>,
455 /// Map of how text in the buffer should be displayed.
456 /// Handles soft wraps, folds, fake inlay text insertions, etc.
457 pub display_map: Model<DisplayMap>,
458 pub selections: SelectionsCollection,
459 pub scroll_manager: ScrollManager,
460 /// When inline assist editors are linked, they all render cursors because
461 /// typing enters text into each of them, even the ones that aren't focused.
462 pub(crate) show_cursor_when_unfocused: bool,
463 columnar_selection_tail: Option<Anchor>,
464 add_selections_state: Option<AddSelectionsState>,
465 select_next_state: Option<SelectNextState>,
466 select_prev_state: Option<SelectNextState>,
467 selection_history: SelectionHistory,
468 autoclose_regions: Vec<AutocloseRegion>,
469 snippet_stack: InvalidationStack<SnippetState>,
470 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
471 ime_transaction: Option<TransactionId>,
472 active_diagnostics: Option<ActiveDiagnosticGroup>,
473 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
474 project: Option<Model<Project>>,
475 completion_provider: Option<Box<dyn CompletionProvider>>,
476 collaboration_hub: Option<Box<dyn CollaborationHub>>,
477 blink_manager: Model<BlinkManager>,
478 show_cursor_names: bool,
479 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
480 pub show_local_selections: bool,
481 mode: EditorMode,
482 show_breadcrumbs: bool,
483 show_gutter: bool,
484 show_line_numbers: Option<bool>,
485 show_git_diff_gutter: Option<bool>,
486 show_code_actions: Option<bool>,
487 show_runnables: Option<bool>,
488 show_wrap_guides: Option<bool>,
489 show_indent_guides: Option<bool>,
490 placeholder_text: Option<Arc<str>>,
491 highlight_order: usize,
492 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
493 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
494 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
495 scrollbar_marker_state: ScrollbarMarkerState,
496 active_indent_guides_state: ActiveIndentGuidesState,
497 nav_history: Option<ItemNavHistory>,
498 context_menu: RwLock<Option<ContextMenu>>,
499 mouse_context_menu: Option<MouseContextMenu>,
500 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
501 signature_help_state: SignatureHelpState,
502 auto_signature_help: Option<bool>,
503 find_all_references_task_sources: Vec<Anchor>,
504 next_completion_id: CompletionId,
505 completion_documentation_pre_resolve_debounce: DebouncedDelay,
506 available_code_actions: Option<(Location, Arc<[CodeAction]>)>,
507 code_actions_task: Option<Task<()>>,
508 document_highlights_task: Option<Task<()>>,
509 linked_editing_range_task: Option<Task<Option<()>>>,
510 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
511 pending_rename: Option<RenameState>,
512 searchable: bool,
513 cursor_shape: CursorShape,
514 current_line_highlight: Option<CurrentLineHighlight>,
515 collapse_matches: bool,
516 autoindent_mode: Option<AutoindentMode>,
517 workspace: Option<(WeakView<Workspace>, Option<WorkspaceId>)>,
518 keymap_context_layers: BTreeMap<TypeId, KeyContext>,
519 input_enabled: bool,
520 use_modal_editing: bool,
521 read_only: bool,
522 leader_peer_id: Option<PeerId>,
523 remote_id: Option<ViewId>,
524 hover_state: HoverState,
525 gutter_hovered: bool,
526 hovered_link_state: Option<HoveredLinkState>,
527 inline_completion_provider: Option<RegisteredInlineCompletionProvider>,
528 active_inline_completion: Option<(Inlay, Option<Range<Anchor>>)>,
529 show_inline_completions: bool,
530 inlay_hint_cache: InlayHintCache,
531 expanded_hunks: ExpandedHunks,
532 next_inlay_id: usize,
533 _subscriptions: Vec<Subscription>,
534 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
535 gutter_dimensions: GutterDimensions,
536 pub vim_replace_map: HashMap<Range<usize>, String>,
537 style: Option<EditorStyle>,
538 next_editor_action_id: EditorActionId,
539 editor_actions: Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut ViewContext<Self>)>>>>,
540 use_autoclose: bool,
541 use_auto_surround: bool,
542 auto_replace_emoji_shortcode: bool,
543 show_git_blame_gutter: bool,
544 show_git_blame_inline: bool,
545 show_git_blame_inline_delay_task: Option<Task<()>>,
546 git_blame_inline_enabled: bool,
547 serialize_dirty_buffers: bool,
548 show_selection_menu: Option<bool>,
549 blame: Option<Model<GitBlame>>,
550 blame_subscription: Option<Subscription>,
551 custom_context_menu: Option<
552 Box<
553 dyn 'static
554 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
555 >,
556 >,
557 last_bounds: Option<Bounds<Pixels>>,
558 expect_bounds_change: Option<Bounds<Pixels>>,
559 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
560 tasks_update_task: Option<Task<()>>,
561 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
562 file_header_size: u32,
563 breadcrumb_header: Option<String>,
564 focused_block: Option<FocusedBlock>,
565 next_scroll_position: NextScrollCursorCenterTopBottom,
566 _scroll_cursor_center_top_bottom_task: Task<()>,
567}
568
569#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
570enum NextScrollCursorCenterTopBottom {
571 #[default]
572 Center,
573 Top,
574 Bottom,
575}
576
577impl NextScrollCursorCenterTopBottom {
578 fn next(&self) -> Self {
579 match self {
580 Self::Center => Self::Top,
581 Self::Top => Self::Bottom,
582 Self::Bottom => Self::Center,
583 }
584 }
585}
586
587#[derive(Clone)]
588pub struct EditorSnapshot {
589 pub mode: EditorMode,
590 show_gutter: bool,
591 show_line_numbers: Option<bool>,
592 show_git_diff_gutter: Option<bool>,
593 show_code_actions: Option<bool>,
594 show_runnables: Option<bool>,
595 render_git_blame_gutter: bool,
596 pub display_snapshot: DisplaySnapshot,
597 pub placeholder_text: Option<Arc<str>>,
598 is_focused: bool,
599 scroll_anchor: ScrollAnchor,
600 ongoing_scroll: OngoingScroll,
601 current_line_highlight: CurrentLineHighlight,
602 gutter_hovered: bool,
603}
604
605const GIT_BLAME_GUTTER_WIDTH_CHARS: f32 = 53.;
606
607#[derive(Default, Debug, Clone, Copy)]
608pub struct GutterDimensions {
609 pub left_padding: Pixels,
610 pub right_padding: Pixels,
611 pub width: Pixels,
612 pub margin: Pixels,
613 pub git_blame_entries_width: Option<Pixels>,
614}
615
616impl GutterDimensions {
617 /// The full width of the space taken up by the gutter.
618 pub fn full_width(&self) -> Pixels {
619 self.margin + self.width
620 }
621
622 /// The width of the space reserved for the fold indicators,
623 /// use alongside 'justify_end' and `gutter_width` to
624 /// right align content with the line numbers
625 pub fn fold_area_width(&self) -> Pixels {
626 self.margin + self.right_padding
627 }
628}
629
630#[derive(Debug)]
631pub struct RemoteSelection {
632 pub replica_id: ReplicaId,
633 pub selection: Selection<Anchor>,
634 pub cursor_shape: CursorShape,
635 pub peer_id: PeerId,
636 pub line_mode: bool,
637 pub participant_index: Option<ParticipantIndex>,
638 pub user_name: Option<SharedString>,
639}
640
641#[derive(Clone, Debug)]
642struct SelectionHistoryEntry {
643 selections: Arc<[Selection<Anchor>]>,
644 select_next_state: Option<SelectNextState>,
645 select_prev_state: Option<SelectNextState>,
646 add_selections_state: Option<AddSelectionsState>,
647}
648
649enum SelectionHistoryMode {
650 Normal,
651 Undoing,
652 Redoing,
653}
654
655#[derive(Clone, PartialEq, Eq, Hash)]
656struct HoveredCursor {
657 replica_id: u16,
658 selection_id: usize,
659}
660
661impl Default for SelectionHistoryMode {
662 fn default() -> Self {
663 Self::Normal
664 }
665}
666
667#[derive(Default)]
668struct SelectionHistory {
669 #[allow(clippy::type_complexity)]
670 selections_by_transaction:
671 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
672 mode: SelectionHistoryMode,
673 undo_stack: VecDeque<SelectionHistoryEntry>,
674 redo_stack: VecDeque<SelectionHistoryEntry>,
675}
676
677impl SelectionHistory {
678 fn insert_transaction(
679 &mut self,
680 transaction_id: TransactionId,
681 selections: Arc<[Selection<Anchor>]>,
682 ) {
683 self.selections_by_transaction
684 .insert(transaction_id, (selections, None));
685 }
686
687 #[allow(clippy::type_complexity)]
688 fn transaction(
689 &self,
690 transaction_id: TransactionId,
691 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
692 self.selections_by_transaction.get(&transaction_id)
693 }
694
695 #[allow(clippy::type_complexity)]
696 fn transaction_mut(
697 &mut self,
698 transaction_id: TransactionId,
699 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
700 self.selections_by_transaction.get_mut(&transaction_id)
701 }
702
703 fn push(&mut self, entry: SelectionHistoryEntry) {
704 if !entry.selections.is_empty() {
705 match self.mode {
706 SelectionHistoryMode::Normal => {
707 self.push_undo(entry);
708 self.redo_stack.clear();
709 }
710 SelectionHistoryMode::Undoing => self.push_redo(entry),
711 SelectionHistoryMode::Redoing => self.push_undo(entry),
712 }
713 }
714 }
715
716 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
717 if self
718 .undo_stack
719 .back()
720 .map_or(true, |e| e.selections != entry.selections)
721 {
722 self.undo_stack.push_back(entry);
723 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
724 self.undo_stack.pop_front();
725 }
726 }
727 }
728
729 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
730 if self
731 .redo_stack
732 .back()
733 .map_or(true, |e| e.selections != entry.selections)
734 {
735 self.redo_stack.push_back(entry);
736 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
737 self.redo_stack.pop_front();
738 }
739 }
740 }
741}
742
743struct RowHighlight {
744 index: usize,
745 range: RangeInclusive<Anchor>,
746 color: Option<Hsla>,
747 should_autoscroll: bool,
748}
749
750#[derive(Clone, Debug)]
751struct AddSelectionsState {
752 above: bool,
753 stack: Vec<usize>,
754}
755
756#[derive(Clone)]
757struct SelectNextState {
758 query: AhoCorasick,
759 wordwise: bool,
760 done: bool,
761}
762
763impl std::fmt::Debug for SelectNextState {
764 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
765 f.debug_struct(std::any::type_name::<Self>())
766 .field("wordwise", &self.wordwise)
767 .field("done", &self.done)
768 .finish()
769 }
770}
771
772#[derive(Debug)]
773struct AutocloseRegion {
774 selection_id: usize,
775 range: Range<Anchor>,
776 pair: BracketPair,
777}
778
779#[derive(Debug)]
780struct SnippetState {
781 ranges: Vec<Vec<Range<Anchor>>>,
782 active_index: usize,
783}
784
785#[doc(hidden)]
786pub struct RenameState {
787 pub range: Range<Anchor>,
788 pub old_name: Arc<str>,
789 pub editor: View<Editor>,
790 block_id: CustomBlockId,
791}
792
793struct InvalidationStack<T>(Vec<T>);
794
795struct RegisteredInlineCompletionProvider {
796 provider: Arc<dyn InlineCompletionProviderHandle>,
797 _subscription: Subscription,
798}
799
800enum ContextMenu {
801 Completions(CompletionsMenu),
802 CodeActions(CodeActionsMenu),
803}
804
805impl ContextMenu {
806 fn select_first(
807 &mut self,
808 project: Option<&Model<Project>>,
809 cx: &mut ViewContext<Editor>,
810 ) -> bool {
811 if self.visible() {
812 match self {
813 ContextMenu::Completions(menu) => menu.select_first(project, cx),
814 ContextMenu::CodeActions(menu) => menu.select_first(cx),
815 }
816 true
817 } else {
818 false
819 }
820 }
821
822 fn select_prev(
823 &mut self,
824 project: Option<&Model<Project>>,
825 cx: &mut ViewContext<Editor>,
826 ) -> bool {
827 if self.visible() {
828 match self {
829 ContextMenu::Completions(menu) => menu.select_prev(project, cx),
830 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
831 }
832 true
833 } else {
834 false
835 }
836 }
837
838 fn select_next(
839 &mut self,
840 project: Option<&Model<Project>>,
841 cx: &mut ViewContext<Editor>,
842 ) -> bool {
843 if self.visible() {
844 match self {
845 ContextMenu::Completions(menu) => menu.select_next(project, cx),
846 ContextMenu::CodeActions(menu) => menu.select_next(cx),
847 }
848 true
849 } else {
850 false
851 }
852 }
853
854 fn select_last(
855 &mut self,
856 project: Option<&Model<Project>>,
857 cx: &mut ViewContext<Editor>,
858 ) -> bool {
859 if self.visible() {
860 match self {
861 ContextMenu::Completions(menu) => menu.select_last(project, cx),
862 ContextMenu::CodeActions(menu) => menu.select_last(cx),
863 }
864 true
865 } else {
866 false
867 }
868 }
869
870 fn visible(&self) -> bool {
871 match self {
872 ContextMenu::Completions(menu) => menu.visible(),
873 ContextMenu::CodeActions(menu) => menu.visible(),
874 }
875 }
876
877 fn render(
878 &self,
879 cursor_position: DisplayPoint,
880 style: &EditorStyle,
881 max_height: Pixels,
882 workspace: Option<WeakView<Workspace>>,
883 cx: &mut ViewContext<Editor>,
884 ) -> (ContextMenuOrigin, AnyElement) {
885 match self {
886 ContextMenu::Completions(menu) => (
887 ContextMenuOrigin::EditorPoint(cursor_position),
888 menu.render(style, max_height, workspace, cx),
889 ),
890 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, max_height, cx),
891 }
892 }
893}
894
895enum ContextMenuOrigin {
896 EditorPoint(DisplayPoint),
897 GutterIndicator(DisplayRow),
898}
899
900#[derive(Clone)]
901struct CompletionsMenu {
902 id: CompletionId,
903 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 Declaration,
1564 Type,
1565 Implementation,
1566}
1567
1568#[derive(Debug, Clone)]
1569enum InlayHintRefreshReason {
1570 Toggle(bool),
1571 SettingsChange(InlayHintSettings),
1572 NewLinesShown,
1573 BufferEdited(HashSet<Arc<Language>>),
1574 RefreshRequested,
1575 ExcerptsRemoved(Vec<ExcerptId>),
1576}
1577
1578impl InlayHintRefreshReason {
1579 fn description(&self) -> &'static str {
1580 match self {
1581 Self::Toggle(_) => "toggle",
1582 Self::SettingsChange(_) => "settings change",
1583 Self::NewLinesShown => "new lines shown",
1584 Self::BufferEdited(_) => "buffer edited",
1585 Self::RefreshRequested => "refresh requested",
1586 Self::ExcerptsRemoved(_) => "excerpts removed",
1587 }
1588 }
1589}
1590
1591pub(crate) struct FocusedBlock {
1592 id: BlockId,
1593 focus_handle: WeakFocusHandle,
1594}
1595
1596impl Editor {
1597 pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
1598 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1599 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1600 Self::new(
1601 EditorMode::SingleLine { auto_width: false },
1602 buffer,
1603 None,
1604 false,
1605 cx,
1606 )
1607 }
1608
1609 pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
1610 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1611 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1612 Self::new(EditorMode::Full, buffer, None, false, cx)
1613 }
1614
1615 pub fn auto_width(cx: &mut ViewContext<Self>) -> Self {
1616 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1617 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1618 Self::new(
1619 EditorMode::SingleLine { auto_width: true },
1620 buffer,
1621 None,
1622 false,
1623 cx,
1624 )
1625 }
1626
1627 pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
1628 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1629 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1630 Self::new(
1631 EditorMode::AutoHeight { max_lines },
1632 buffer,
1633 None,
1634 false,
1635 cx,
1636 )
1637 }
1638
1639 pub fn for_buffer(
1640 buffer: Model<Buffer>,
1641 project: Option<Model<Project>>,
1642 cx: &mut ViewContext<Self>,
1643 ) -> Self {
1644 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1645 Self::new(EditorMode::Full, buffer, project, false, cx)
1646 }
1647
1648 pub fn for_multibuffer(
1649 buffer: Model<MultiBuffer>,
1650 project: Option<Model<Project>>,
1651 show_excerpt_controls: bool,
1652 cx: &mut ViewContext<Self>,
1653 ) -> Self {
1654 Self::new(EditorMode::Full, buffer, project, show_excerpt_controls, cx)
1655 }
1656
1657 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1658 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1659 let mut clone = Self::new(
1660 self.mode,
1661 self.buffer.clone(),
1662 self.project.clone(),
1663 show_excerpt_controls,
1664 cx,
1665 );
1666 self.display_map.update(cx, |display_map, cx| {
1667 let snapshot = display_map.snapshot(cx);
1668 clone.display_map.update(cx, |display_map, cx| {
1669 display_map.set_state(&snapshot, cx);
1670 });
1671 });
1672 clone.selections.clone_state(&self.selections);
1673 clone.scroll_manager.clone_state(&self.scroll_manager);
1674 clone.searchable = self.searchable;
1675 clone
1676 }
1677
1678 pub fn new(
1679 mode: EditorMode,
1680 buffer: Model<MultiBuffer>,
1681 project: Option<Model<Project>>,
1682 show_excerpt_controls: bool,
1683 cx: &mut ViewContext<Self>,
1684 ) -> Self {
1685 let style = cx.text_style();
1686 let font_size = style.font_size.to_pixels(cx.rem_size());
1687 let editor = cx.view().downgrade();
1688 let fold_placeholder = FoldPlaceholder {
1689 constrain_width: true,
1690 render: Arc::new(move |fold_id, fold_range, cx| {
1691 let editor = editor.clone();
1692 div()
1693 .id(fold_id)
1694 .bg(cx.theme().colors().ghost_element_background)
1695 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1696 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1697 .rounded_sm()
1698 .size_full()
1699 .cursor_pointer()
1700 .child("⋯")
1701 .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
1702 .on_click(move |_, cx| {
1703 editor
1704 .update(cx, |editor, cx| {
1705 editor.unfold_ranges(
1706 [fold_range.start..fold_range.end],
1707 true,
1708 false,
1709 cx,
1710 );
1711 cx.stop_propagation();
1712 })
1713 .ok();
1714 })
1715 .into_any()
1716 }),
1717 merge_adjacent: true,
1718 };
1719 let file_header_size = if show_excerpt_controls { 3 } else { 2 };
1720 let display_map = cx.new_model(|cx| {
1721 DisplayMap::new(
1722 buffer.clone(),
1723 style.font(),
1724 font_size,
1725 None,
1726 show_excerpt_controls,
1727 file_header_size,
1728 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1729 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1730 fold_placeholder,
1731 cx,
1732 )
1733 });
1734
1735 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1736
1737 let blink_manager = cx.new_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1738
1739 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1740 .then(|| language_settings::SoftWrap::PreferLine);
1741
1742 let mut project_subscriptions = Vec::new();
1743 if mode == EditorMode::Full {
1744 if let Some(project) = project.as_ref() {
1745 if buffer.read(cx).is_singleton() {
1746 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1747 cx.emit(EditorEvent::TitleChanged);
1748 }));
1749 }
1750 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1751 if let project::Event::RefreshInlayHints = event {
1752 editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1753 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1754 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1755 let focus_handle = editor.focus_handle(cx);
1756 if focus_handle.is_focused(cx) {
1757 let snapshot = buffer.read(cx).snapshot();
1758 for (range, snippet) in snippet_edits {
1759 let editor_range =
1760 language::range_from_lsp(*range).to_offset(&snapshot);
1761 editor
1762 .insert_snippet(&[editor_range], snippet.clone(), cx)
1763 .ok();
1764 }
1765 }
1766 }
1767 }
1768 }));
1769 let task_inventory = project.read(cx).task_inventory().clone();
1770 project_subscriptions.push(cx.observe(&task_inventory, |editor, _, cx| {
1771 editor.tasks_update_task = Some(editor.refresh_runnables(cx));
1772 }));
1773 }
1774 }
1775
1776 let inlay_hint_settings = inlay_hint_settings(
1777 selections.newest_anchor().head(),
1778 &buffer.read(cx).snapshot(cx),
1779 cx,
1780 );
1781 let focus_handle = cx.focus_handle();
1782 cx.on_focus(&focus_handle, Self::handle_focus).detach();
1783 cx.on_focus_in(&focus_handle, Self::handle_focus_in)
1784 .detach();
1785 cx.on_focus_out(&focus_handle, Self::handle_focus_out)
1786 .detach();
1787 cx.on_blur(&focus_handle, Self::handle_blur).detach();
1788
1789 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1790 Some(false)
1791 } else {
1792 None
1793 };
1794
1795 let mut this = Self {
1796 focus_handle,
1797 show_cursor_when_unfocused: false,
1798 last_focused_descendant: None,
1799 buffer: buffer.clone(),
1800 display_map: display_map.clone(),
1801 selections,
1802 scroll_manager: ScrollManager::new(cx),
1803 columnar_selection_tail: None,
1804 add_selections_state: None,
1805 select_next_state: None,
1806 select_prev_state: None,
1807 selection_history: Default::default(),
1808 autoclose_regions: Default::default(),
1809 snippet_stack: Default::default(),
1810 select_larger_syntax_node_stack: Vec::new(),
1811 ime_transaction: Default::default(),
1812 active_diagnostics: None,
1813 soft_wrap_mode_override,
1814 completion_provider: project.clone().map(|project| Box::new(project) as _),
1815 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1816 project,
1817 blink_manager: blink_manager.clone(),
1818 show_local_selections: true,
1819 mode,
1820 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1821 show_gutter: mode == EditorMode::Full,
1822 show_line_numbers: None,
1823 show_git_diff_gutter: None,
1824 show_code_actions: None,
1825 show_runnables: None,
1826 show_wrap_guides: None,
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.disable(cx);
1910 }
1911 });
1912 }),
1913 ],
1914 tasks_update_task: None,
1915 linked_edit_ranges: Default::default(),
1916 previous_search_ranges: None,
1917 breadcrumb_header: None,
1918 focused_block: None,
1919 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1920 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1921 };
1922 this.tasks_update_task = Some(this.refresh_runnables(cx));
1923 this._subscriptions.extend(project_subscriptions);
1924
1925 this.end_selection(cx);
1926 this.scroll_manager.show_scrollbar(cx);
1927
1928 if mode == EditorMode::Full {
1929 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1930 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1931
1932 if this.git_blame_inline_enabled {
1933 this.git_blame_inline_enabled = true;
1934 this.start_git_blame_inline(false, cx);
1935 }
1936 }
1937
1938 this.report_editor_event("open", None, cx);
1939 this
1940 }
1941
1942 pub fn mouse_menu_is_focused(&self, cx: &mut WindowContext) -> bool {
1943 self.mouse_context_menu
1944 .as_ref()
1945 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(cx))
1946 }
1947
1948 fn key_context(&self, cx: &AppContext) -> KeyContext {
1949 let mut key_context = KeyContext::new_with_defaults();
1950 key_context.add("Editor");
1951 let mode = match self.mode {
1952 EditorMode::SingleLine { .. } => "single_line",
1953 EditorMode::AutoHeight { .. } => "auto_height",
1954 EditorMode::Full => "full",
1955 };
1956
1957 if EditorSettings::jupyter_enabled(cx) {
1958 key_context.add("jupyter");
1959 }
1960
1961 key_context.set("mode", mode);
1962 if self.pending_rename.is_some() {
1963 key_context.add("renaming");
1964 }
1965 if self.context_menu_visible() {
1966 match self.context_menu.read().as_ref() {
1967 Some(ContextMenu::Completions(_)) => {
1968 key_context.add("menu");
1969 key_context.add("showing_completions")
1970 }
1971 Some(ContextMenu::CodeActions(_)) => {
1972 key_context.add("menu");
1973 key_context.add("showing_code_actions")
1974 }
1975 None => {}
1976 }
1977 }
1978
1979 for layer in self.keymap_context_layers.values() {
1980 key_context.extend(layer);
1981 }
1982
1983 if let Some(extension) = self
1984 .buffer
1985 .read(cx)
1986 .as_singleton()
1987 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
1988 {
1989 key_context.set("extension", extension.to_string());
1990 }
1991
1992 if self.has_active_inline_completion(cx) {
1993 key_context.add("copilot_suggestion");
1994 key_context.add("inline_completion");
1995 }
1996
1997 key_context
1998 }
1999
2000 pub fn new_file(
2001 workspace: &mut Workspace,
2002 _: &workspace::NewFile,
2003 cx: &mut ViewContext<Workspace>,
2004 ) {
2005 Self::new_in_workspace(workspace, cx).detach_and_prompt_err(
2006 "Failed to create buffer",
2007 cx,
2008 |e, _| match e.error_code() {
2009 ErrorCode::RemoteUpgradeRequired => Some(format!(
2010 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2011 e.error_tag("required").unwrap_or("the latest version")
2012 )),
2013 _ => None,
2014 },
2015 );
2016 }
2017
2018 pub fn new_in_workspace(
2019 workspace: &mut Workspace,
2020 cx: &mut ViewContext<Workspace>,
2021 ) -> Task<Result<View<Editor>>> {
2022 let project = workspace.project().clone();
2023 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2024
2025 cx.spawn(|workspace, mut cx| async move {
2026 let buffer = create.await?;
2027 workspace.update(&mut cx, |workspace, cx| {
2028 let editor =
2029 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx));
2030 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
2031 editor
2032 })
2033 })
2034 }
2035
2036 pub fn new_file_in_direction(
2037 workspace: &mut Workspace,
2038 action: &workspace::NewFileInDirection,
2039 cx: &mut ViewContext<Workspace>,
2040 ) {
2041 let project = workspace.project().clone();
2042 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2043 let direction = action.0;
2044
2045 cx.spawn(|workspace, mut cx| async move {
2046 let buffer = create.await?;
2047 workspace.update(&mut cx, move |workspace, cx| {
2048 workspace.split_item(
2049 direction,
2050 Box::new(
2051 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)),
2052 ),
2053 cx,
2054 )
2055 })?;
2056 anyhow::Ok(())
2057 })
2058 .detach_and_prompt_err("Failed to create buffer", cx, |e, _| match e.error_code() {
2059 ErrorCode::RemoteUpgradeRequired => Some(format!(
2060 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2061 e.error_tag("required").unwrap_or("the latest version")
2062 )),
2063 _ => None,
2064 });
2065 }
2066
2067 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
2068 self.buffer.read(cx).replica_id()
2069 }
2070
2071 pub fn leader_peer_id(&self) -> Option<PeerId> {
2072 self.leader_peer_id
2073 }
2074
2075 pub fn buffer(&self) -> &Model<MultiBuffer> {
2076 &self.buffer
2077 }
2078
2079 pub fn workspace(&self) -> Option<View<Workspace>> {
2080 self.workspace.as_ref()?.0.upgrade()
2081 }
2082
2083 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
2084 self.buffer().read(cx).title(cx)
2085 }
2086
2087 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
2088 EditorSnapshot {
2089 mode: self.mode,
2090 show_gutter: self.show_gutter,
2091 show_line_numbers: self.show_line_numbers,
2092 show_git_diff_gutter: self.show_git_diff_gutter,
2093 show_code_actions: self.show_code_actions,
2094 show_runnables: self.show_runnables,
2095 render_git_blame_gutter: self.render_git_blame_gutter(cx),
2096 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
2097 scroll_anchor: self.scroll_manager.anchor(),
2098 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
2099 placeholder_text: self.placeholder_text.clone(),
2100 is_focused: self.focus_handle.is_focused(cx),
2101 current_line_highlight: self
2102 .current_line_highlight
2103 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
2104 gutter_hovered: self.gutter_hovered,
2105 }
2106 }
2107
2108 pub fn language_at<T: ToOffset>(&self, point: T, cx: &AppContext) -> Option<Arc<Language>> {
2109 self.buffer.read(cx).language_at(point, cx)
2110 }
2111
2112 pub fn file_at<T: ToOffset>(
2113 &self,
2114 point: T,
2115 cx: &AppContext,
2116 ) -> Option<Arc<dyn language::File>> {
2117 self.buffer.read(cx).read(cx).file_at(point).cloned()
2118 }
2119
2120 pub fn active_excerpt(
2121 &self,
2122 cx: &AppContext,
2123 ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
2124 self.buffer
2125 .read(cx)
2126 .excerpt_containing(self.selections.newest_anchor().head(), cx)
2127 }
2128
2129 pub fn mode(&self) -> EditorMode {
2130 self.mode
2131 }
2132
2133 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
2134 self.collaboration_hub.as_deref()
2135 }
2136
2137 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
2138 self.collaboration_hub = Some(hub);
2139 }
2140
2141 pub fn set_custom_context_menu(
2142 &mut self,
2143 f: impl 'static
2144 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
2145 ) {
2146 self.custom_context_menu = Some(Box::new(f))
2147 }
2148
2149 pub fn set_completion_provider(&mut self, provider: Box<dyn CompletionProvider>) {
2150 self.completion_provider = Some(provider);
2151 }
2152
2153 pub fn set_inline_completion_provider<T>(
2154 &mut self,
2155 provider: Option<Model<T>>,
2156 cx: &mut ViewContext<Self>,
2157 ) where
2158 T: InlineCompletionProvider,
2159 {
2160 self.inline_completion_provider =
2161 provider.map(|provider| RegisteredInlineCompletionProvider {
2162 _subscription: cx.observe(&provider, |this, _, cx| {
2163 if this.focus_handle.is_focused(cx) {
2164 this.update_visible_inline_completion(cx);
2165 }
2166 }),
2167 provider: Arc::new(provider),
2168 });
2169 self.refresh_inline_completion(false, cx);
2170 }
2171
2172 pub fn placeholder_text(&self, _cx: &WindowContext) -> Option<&str> {
2173 self.placeholder_text.as_deref()
2174 }
2175
2176 pub fn set_placeholder_text(
2177 &mut self,
2178 placeholder_text: impl Into<Arc<str>>,
2179 cx: &mut ViewContext<Self>,
2180 ) {
2181 let placeholder_text = Some(placeholder_text.into());
2182 if self.placeholder_text != placeholder_text {
2183 self.placeholder_text = placeholder_text;
2184 cx.notify();
2185 }
2186 }
2187
2188 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
2189 self.cursor_shape = cursor_shape;
2190
2191 // Disrupt blink for immediate user feedback that the cursor shape has changed
2192 self.blink_manager.update(cx, BlinkManager::show_cursor);
2193
2194 cx.notify();
2195 }
2196
2197 pub fn set_current_line_highlight(
2198 &mut self,
2199 current_line_highlight: Option<CurrentLineHighlight>,
2200 ) {
2201 self.current_line_highlight = current_line_highlight;
2202 }
2203
2204 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
2205 self.collapse_matches = collapse_matches;
2206 }
2207
2208 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
2209 if self.collapse_matches {
2210 return range.start..range.start;
2211 }
2212 range.clone()
2213 }
2214
2215 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
2216 if self.display_map.read(cx).clip_at_line_ends != clip {
2217 self.display_map
2218 .update(cx, |map, _| map.clip_at_line_ends = clip);
2219 }
2220 }
2221
2222 pub fn set_keymap_context_layer<Tag: 'static>(
2223 &mut self,
2224 context: KeyContext,
2225 cx: &mut ViewContext<Self>,
2226 ) {
2227 self.keymap_context_layers
2228 .insert(TypeId::of::<Tag>(), context);
2229 cx.notify();
2230 }
2231
2232 pub fn remove_keymap_context_layer<Tag: 'static>(&mut self, cx: &mut ViewContext<Self>) {
2233 self.keymap_context_layers.remove(&TypeId::of::<Tag>());
2234 cx.notify();
2235 }
2236
2237 pub fn set_input_enabled(&mut self, input_enabled: bool) {
2238 self.input_enabled = input_enabled;
2239 }
2240
2241 pub fn set_autoindent(&mut self, autoindent: bool) {
2242 if autoindent {
2243 self.autoindent_mode = Some(AutoindentMode::EachLine);
2244 } else {
2245 self.autoindent_mode = None;
2246 }
2247 }
2248
2249 pub fn read_only(&self, cx: &AppContext) -> bool {
2250 self.read_only || self.buffer.read(cx).read_only()
2251 }
2252
2253 pub fn set_read_only(&mut self, read_only: bool) {
2254 self.read_only = read_only;
2255 }
2256
2257 pub fn set_use_autoclose(&mut self, autoclose: bool) {
2258 self.use_autoclose = autoclose;
2259 }
2260
2261 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
2262 self.use_auto_surround = auto_surround;
2263 }
2264
2265 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
2266 self.auto_replace_emoji_shortcode = auto_replace;
2267 }
2268
2269 pub fn set_show_inline_completions(&mut self, show_inline_completions: bool) {
2270 self.show_inline_completions = show_inline_completions;
2271 }
2272
2273 pub fn set_use_modal_editing(&mut self, to: bool) {
2274 self.use_modal_editing = to;
2275 }
2276
2277 pub fn use_modal_editing(&self) -> bool {
2278 self.use_modal_editing
2279 }
2280
2281 fn selections_did_change(
2282 &mut self,
2283 local: bool,
2284 old_cursor_position: &Anchor,
2285 show_completions: bool,
2286 cx: &mut ViewContext<Self>,
2287 ) {
2288 // Copy selections to primary selection buffer
2289 #[cfg(target_os = "linux")]
2290 if local {
2291 let selections = self.selections.all::<usize>(cx);
2292 let buffer_handle = self.buffer.read(cx).read(cx);
2293
2294 let mut text = String::new();
2295 for (index, selection) in selections.iter().enumerate() {
2296 let text_for_selection = buffer_handle
2297 .text_for_range(selection.start..selection.end)
2298 .collect::<String>();
2299
2300 text.push_str(&text_for_selection);
2301 if index != selections.len() - 1 {
2302 text.push('\n');
2303 }
2304 }
2305
2306 if !text.is_empty() {
2307 cx.write_to_primary(ClipboardItem::new(text));
2308 }
2309 }
2310
2311 if self.focus_handle.is_focused(cx) && self.leader_peer_id.is_none() {
2312 self.buffer.update(cx, |buffer, cx| {
2313 buffer.set_active_selections(
2314 &self.selections.disjoint_anchors(),
2315 self.selections.line_mode,
2316 self.cursor_shape,
2317 cx,
2318 )
2319 });
2320 }
2321 let display_map = self
2322 .display_map
2323 .update(cx, |display_map, cx| display_map.snapshot(cx));
2324 let buffer = &display_map.buffer_snapshot;
2325 self.add_selections_state = None;
2326 self.select_next_state = None;
2327 self.select_prev_state = None;
2328 self.select_larger_syntax_node_stack.clear();
2329 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2330 self.snippet_stack
2331 .invalidate(&self.selections.disjoint_anchors(), buffer);
2332 self.take_rename(false, cx);
2333
2334 let new_cursor_position = self.selections.newest_anchor().head();
2335
2336 self.push_to_nav_history(
2337 *old_cursor_position,
2338 Some(new_cursor_position.to_point(buffer)),
2339 cx,
2340 );
2341
2342 if local {
2343 let new_cursor_position = self.selections.newest_anchor().head();
2344 let mut context_menu = self.context_menu.write();
2345 let completion_menu = match context_menu.as_ref() {
2346 Some(ContextMenu::Completions(menu)) => Some(menu),
2347
2348 _ => {
2349 *context_menu = None;
2350 None
2351 }
2352 };
2353
2354 if let Some(completion_menu) = completion_menu {
2355 let cursor_position = new_cursor_position.to_offset(buffer);
2356 let (word_range, kind) = buffer.surrounding_word(completion_menu.initial_position);
2357 if kind == Some(CharKind::Word)
2358 && word_range.to_inclusive().contains(&cursor_position)
2359 {
2360 let mut completion_menu = completion_menu.clone();
2361 drop(context_menu);
2362
2363 let query = Self::completion_query(buffer, cursor_position);
2364 cx.spawn(move |this, mut cx| async move {
2365 completion_menu
2366 .filter(query.as_deref(), cx.background_executor().clone())
2367 .await;
2368
2369 this.update(&mut cx, |this, cx| {
2370 let mut context_menu = this.context_menu.write();
2371 let Some(ContextMenu::Completions(menu)) = context_menu.as_ref() else {
2372 return;
2373 };
2374
2375 if menu.id > completion_menu.id {
2376 return;
2377 }
2378
2379 *context_menu = Some(ContextMenu::Completions(completion_menu));
2380 drop(context_menu);
2381 cx.notify();
2382 })
2383 })
2384 .detach();
2385
2386 if show_completions {
2387 self.show_completions(&ShowCompletions { trigger: None }, cx);
2388 }
2389 } else {
2390 drop(context_menu);
2391 self.hide_context_menu(cx);
2392 }
2393 } else {
2394 drop(context_menu);
2395 }
2396
2397 hide_hover(self, cx);
2398
2399 if old_cursor_position.to_display_point(&display_map).row()
2400 != new_cursor_position.to_display_point(&display_map).row()
2401 {
2402 self.available_code_actions.take();
2403 }
2404 self.refresh_code_actions(cx);
2405 self.refresh_document_highlights(cx);
2406 refresh_matching_bracket_highlights(self, cx);
2407 self.discard_inline_completion(false, cx);
2408 linked_editing_ranges::refresh_linked_ranges(self, cx);
2409 if self.git_blame_inline_enabled {
2410 self.start_inline_blame_timer(cx);
2411 }
2412 }
2413
2414 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2415 cx.emit(EditorEvent::SelectionsChanged { local });
2416
2417 if self.selections.disjoint_anchors().len() == 1 {
2418 cx.emit(SearchEvent::ActiveMatchChanged)
2419 }
2420 cx.notify();
2421 }
2422
2423 pub fn change_selections<R>(
2424 &mut self,
2425 autoscroll: Option<Autoscroll>,
2426 cx: &mut ViewContext<Self>,
2427 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2428 ) -> R {
2429 self.change_selections_inner(autoscroll, true, cx, change)
2430 }
2431
2432 pub fn change_selections_inner<R>(
2433 &mut self,
2434 autoscroll: Option<Autoscroll>,
2435 request_completions: bool,
2436 cx: &mut ViewContext<Self>,
2437 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2438 ) -> R {
2439 let old_cursor_position = self.selections.newest_anchor().head();
2440 self.push_to_selection_history();
2441
2442 let (changed, result) = self.selections.change_with(cx, change);
2443
2444 if changed {
2445 if let Some(autoscroll) = autoscroll {
2446 self.request_autoscroll(autoscroll, cx);
2447 }
2448 self.selections_did_change(true, &old_cursor_position, request_completions, cx);
2449
2450 if self.should_open_signature_help_automatically(
2451 &old_cursor_position,
2452 self.signature_help_state.backspace_pressed(),
2453 cx,
2454 ) {
2455 self.show_signature_help(&ShowSignatureHelp, cx);
2456 }
2457 self.signature_help_state.set_backspace_pressed(false);
2458 }
2459
2460 result
2461 }
2462
2463 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2464 where
2465 I: IntoIterator<Item = (Range<S>, T)>,
2466 S: ToOffset,
2467 T: Into<Arc<str>>,
2468 {
2469 if self.read_only(cx) {
2470 return;
2471 }
2472
2473 self.buffer
2474 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2475 }
2476
2477 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2478 where
2479 I: IntoIterator<Item = (Range<S>, T)>,
2480 S: ToOffset,
2481 T: Into<Arc<str>>,
2482 {
2483 if self.read_only(cx) {
2484 return;
2485 }
2486
2487 self.buffer.update(cx, |buffer, cx| {
2488 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2489 });
2490 }
2491
2492 pub fn edit_with_block_indent<I, S, T>(
2493 &mut self,
2494 edits: I,
2495 original_indent_columns: Vec<u32>,
2496 cx: &mut ViewContext<Self>,
2497 ) where
2498 I: IntoIterator<Item = (Range<S>, T)>,
2499 S: ToOffset,
2500 T: Into<Arc<str>>,
2501 {
2502 if self.read_only(cx) {
2503 return;
2504 }
2505
2506 self.buffer.update(cx, |buffer, cx| {
2507 buffer.edit(
2508 edits,
2509 Some(AutoindentMode::Block {
2510 original_indent_columns,
2511 }),
2512 cx,
2513 )
2514 });
2515 }
2516
2517 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
2518 self.hide_context_menu(cx);
2519
2520 match phase {
2521 SelectPhase::Begin {
2522 position,
2523 add,
2524 click_count,
2525 } => self.begin_selection(position, add, click_count, cx),
2526 SelectPhase::BeginColumnar {
2527 position,
2528 goal_column,
2529 reset,
2530 } => self.begin_columnar_selection(position, goal_column, reset, cx),
2531 SelectPhase::Extend {
2532 position,
2533 click_count,
2534 } => self.extend_selection(position, click_count, cx),
2535 SelectPhase::Update {
2536 position,
2537 goal_column,
2538 scroll_delta,
2539 } => self.update_selection(position, goal_column, scroll_delta, cx),
2540 SelectPhase::End => self.end_selection(cx),
2541 }
2542 }
2543
2544 fn extend_selection(
2545 &mut self,
2546 position: DisplayPoint,
2547 click_count: usize,
2548 cx: &mut ViewContext<Self>,
2549 ) {
2550 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2551 let tail = self.selections.newest::<usize>(cx).tail();
2552 self.begin_selection(position, false, click_count, cx);
2553
2554 let position = position.to_offset(&display_map, Bias::Left);
2555 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2556
2557 let mut pending_selection = self
2558 .selections
2559 .pending_anchor()
2560 .expect("extend_selection not called with pending selection");
2561 if position >= tail {
2562 pending_selection.start = tail_anchor;
2563 } else {
2564 pending_selection.end = tail_anchor;
2565 pending_selection.reversed = true;
2566 }
2567
2568 let mut pending_mode = self.selections.pending_mode().unwrap();
2569 match &mut pending_mode {
2570 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2571 _ => {}
2572 }
2573
2574 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
2575 s.set_pending(pending_selection, pending_mode)
2576 });
2577 }
2578
2579 fn begin_selection(
2580 &mut self,
2581 position: DisplayPoint,
2582 add: bool,
2583 click_count: usize,
2584 cx: &mut ViewContext<Self>,
2585 ) {
2586 if !self.focus_handle.is_focused(cx) {
2587 self.last_focused_descendant = None;
2588 cx.focus(&self.focus_handle);
2589 }
2590
2591 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2592 let buffer = &display_map.buffer_snapshot;
2593 let newest_selection = self.selections.newest_anchor().clone();
2594 let position = display_map.clip_point(position, Bias::Left);
2595
2596 let start;
2597 let end;
2598 let mode;
2599 let auto_scroll;
2600 match click_count {
2601 1 => {
2602 start = buffer.anchor_before(position.to_point(&display_map));
2603 end = start;
2604 mode = SelectMode::Character;
2605 auto_scroll = true;
2606 }
2607 2 => {
2608 let range = movement::surrounding_word(&display_map, position);
2609 start = buffer.anchor_before(range.start.to_point(&display_map));
2610 end = buffer.anchor_before(range.end.to_point(&display_map));
2611 mode = SelectMode::Word(start..end);
2612 auto_scroll = true;
2613 }
2614 3 => {
2615 let position = display_map
2616 .clip_point(position, Bias::Left)
2617 .to_point(&display_map);
2618 let line_start = display_map.prev_line_boundary(position).0;
2619 let next_line_start = buffer.clip_point(
2620 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2621 Bias::Left,
2622 );
2623 start = buffer.anchor_before(line_start);
2624 end = buffer.anchor_before(next_line_start);
2625 mode = SelectMode::Line(start..end);
2626 auto_scroll = true;
2627 }
2628 _ => {
2629 start = buffer.anchor_before(0);
2630 end = buffer.anchor_before(buffer.len());
2631 mode = SelectMode::All;
2632 auto_scroll = false;
2633 }
2634 }
2635
2636 let point_to_delete: Option<usize> = {
2637 let selected_points: Vec<Selection<Point>> =
2638 self.selections.disjoint_in_range(start..end, cx);
2639
2640 if !add || click_count > 1 {
2641 None
2642 } else if selected_points.len() > 0 {
2643 Some(selected_points[0].id)
2644 } else {
2645 let clicked_point_already_selected =
2646 self.selections.disjoint.iter().find(|selection| {
2647 selection.start.to_point(buffer) == start.to_point(buffer)
2648 || selection.end.to_point(buffer) == end.to_point(buffer)
2649 });
2650
2651 if let Some(selection) = clicked_point_already_selected {
2652 Some(selection.id)
2653 } else {
2654 None
2655 }
2656 }
2657 };
2658
2659 let selections_count = self.selections.count();
2660
2661 self.change_selections(auto_scroll.then(|| Autoscroll::newest()), cx, |s| {
2662 if let Some(point_to_delete) = point_to_delete {
2663 s.delete(point_to_delete);
2664
2665 if selections_count == 1 {
2666 s.set_pending_anchor_range(start..end, mode);
2667 }
2668 } else {
2669 if !add {
2670 s.clear_disjoint();
2671 } else if click_count > 1 {
2672 s.delete(newest_selection.id)
2673 }
2674
2675 s.set_pending_anchor_range(start..end, mode);
2676 }
2677 });
2678 }
2679
2680 fn begin_columnar_selection(
2681 &mut self,
2682 position: DisplayPoint,
2683 goal_column: u32,
2684 reset: bool,
2685 cx: &mut ViewContext<Self>,
2686 ) {
2687 if !self.focus_handle.is_focused(cx) {
2688 self.last_focused_descendant = None;
2689 cx.focus(&self.focus_handle);
2690 }
2691
2692 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2693
2694 if reset {
2695 let pointer_position = display_map
2696 .buffer_snapshot
2697 .anchor_before(position.to_point(&display_map));
2698
2699 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
2700 s.clear_disjoint();
2701 s.set_pending_anchor_range(
2702 pointer_position..pointer_position,
2703 SelectMode::Character,
2704 );
2705 });
2706 }
2707
2708 let tail = self.selections.newest::<Point>(cx).tail();
2709 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2710
2711 if !reset {
2712 self.select_columns(
2713 tail.to_display_point(&display_map),
2714 position,
2715 goal_column,
2716 &display_map,
2717 cx,
2718 );
2719 }
2720 }
2721
2722 fn update_selection(
2723 &mut self,
2724 position: DisplayPoint,
2725 goal_column: u32,
2726 scroll_delta: gpui::Point<f32>,
2727 cx: &mut ViewContext<Self>,
2728 ) {
2729 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2730
2731 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2732 let tail = tail.to_display_point(&display_map);
2733 self.select_columns(tail, position, goal_column, &display_map, cx);
2734 } else if let Some(mut pending) = self.selections.pending_anchor() {
2735 let buffer = self.buffer.read(cx).snapshot(cx);
2736 let head;
2737 let tail;
2738 let mode = self.selections.pending_mode().unwrap();
2739 match &mode {
2740 SelectMode::Character => {
2741 head = position.to_point(&display_map);
2742 tail = pending.tail().to_point(&buffer);
2743 }
2744 SelectMode::Word(original_range) => {
2745 let original_display_range = original_range.start.to_display_point(&display_map)
2746 ..original_range.end.to_display_point(&display_map);
2747 let original_buffer_range = original_display_range.start.to_point(&display_map)
2748 ..original_display_range.end.to_point(&display_map);
2749 if movement::is_inside_word(&display_map, position)
2750 || original_display_range.contains(&position)
2751 {
2752 let word_range = movement::surrounding_word(&display_map, position);
2753 if word_range.start < original_display_range.start {
2754 head = word_range.start.to_point(&display_map);
2755 } else {
2756 head = word_range.end.to_point(&display_map);
2757 }
2758 } else {
2759 head = position.to_point(&display_map);
2760 }
2761
2762 if head <= original_buffer_range.start {
2763 tail = original_buffer_range.end;
2764 } else {
2765 tail = original_buffer_range.start;
2766 }
2767 }
2768 SelectMode::Line(original_range) => {
2769 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2770
2771 let position = display_map
2772 .clip_point(position, Bias::Left)
2773 .to_point(&display_map);
2774 let line_start = display_map.prev_line_boundary(position).0;
2775 let next_line_start = buffer.clip_point(
2776 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2777 Bias::Left,
2778 );
2779
2780 if line_start < original_range.start {
2781 head = line_start
2782 } else {
2783 head = next_line_start
2784 }
2785
2786 if head <= original_range.start {
2787 tail = original_range.end;
2788 } else {
2789 tail = original_range.start;
2790 }
2791 }
2792 SelectMode::All => {
2793 return;
2794 }
2795 };
2796
2797 if head < tail {
2798 pending.start = buffer.anchor_before(head);
2799 pending.end = buffer.anchor_before(tail);
2800 pending.reversed = true;
2801 } else {
2802 pending.start = buffer.anchor_before(tail);
2803 pending.end = buffer.anchor_before(head);
2804 pending.reversed = false;
2805 }
2806
2807 self.change_selections(None, cx, |s| {
2808 s.set_pending(pending, mode);
2809 });
2810 } else {
2811 log::error!("update_selection dispatched with no pending selection");
2812 return;
2813 }
2814
2815 self.apply_scroll_delta(scroll_delta, cx);
2816 cx.notify();
2817 }
2818
2819 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
2820 self.columnar_selection_tail.take();
2821 if self.selections.pending_anchor().is_some() {
2822 let selections = self.selections.all::<usize>(cx);
2823 self.change_selections(None, cx, |s| {
2824 s.select(selections);
2825 s.clear_pending();
2826 });
2827 }
2828 }
2829
2830 fn select_columns(
2831 &mut self,
2832 tail: DisplayPoint,
2833 head: DisplayPoint,
2834 goal_column: u32,
2835 display_map: &DisplaySnapshot,
2836 cx: &mut ViewContext<Self>,
2837 ) {
2838 let start_row = cmp::min(tail.row(), head.row());
2839 let end_row = cmp::max(tail.row(), head.row());
2840 let start_column = cmp::min(tail.column(), goal_column);
2841 let end_column = cmp::max(tail.column(), goal_column);
2842 let reversed = start_column < tail.column();
2843
2844 let selection_ranges = (start_row.0..=end_row.0)
2845 .map(DisplayRow)
2846 .filter_map(|row| {
2847 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2848 let start = display_map
2849 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2850 .to_point(display_map);
2851 let end = display_map
2852 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2853 .to_point(display_map);
2854 if reversed {
2855 Some(end..start)
2856 } else {
2857 Some(start..end)
2858 }
2859 } else {
2860 None
2861 }
2862 })
2863 .collect::<Vec<_>>();
2864
2865 self.change_selections(None, cx, |s| {
2866 s.select_ranges(selection_ranges);
2867 });
2868 cx.notify();
2869 }
2870
2871 pub fn has_pending_nonempty_selection(&self) -> bool {
2872 let pending_nonempty_selection = match self.selections.pending_anchor() {
2873 Some(Selection { start, end, .. }) => start != end,
2874 None => false,
2875 };
2876
2877 pending_nonempty_selection
2878 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2879 }
2880
2881 pub fn has_pending_selection(&self) -> bool {
2882 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2883 }
2884
2885 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
2886 if self.clear_clicked_diff_hunks(cx) {
2887 cx.notify();
2888 return;
2889 }
2890 if self.dismiss_menus_and_popups(true, cx) {
2891 return;
2892 }
2893
2894 if self.mode == EditorMode::Full {
2895 if self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel()) {
2896 return;
2897 }
2898 }
2899
2900 cx.propagate();
2901 }
2902
2903 pub fn dismiss_menus_and_popups(
2904 &mut self,
2905 should_report_inline_completion_event: bool,
2906 cx: &mut ViewContext<Self>,
2907 ) -> bool {
2908 if self.take_rename(false, cx).is_some() {
2909 return true;
2910 }
2911
2912 if hide_hover(self, cx) {
2913 return true;
2914 }
2915
2916 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2917 return true;
2918 }
2919
2920 if self.hide_context_menu(cx).is_some() {
2921 return true;
2922 }
2923
2924 if self.mouse_context_menu.take().is_some() {
2925 return true;
2926 }
2927
2928 if self.discard_inline_completion(should_report_inline_completion_event, cx) {
2929 return true;
2930 }
2931
2932 if self.snippet_stack.pop().is_some() {
2933 return true;
2934 }
2935
2936 if self.mode == EditorMode::Full {
2937 if self.active_diagnostics.is_some() {
2938 self.dismiss_diagnostics(cx);
2939 return true;
2940 }
2941 }
2942
2943 false
2944 }
2945
2946 fn linked_editing_ranges_for(
2947 &self,
2948 selection: Range<text::Anchor>,
2949 cx: &AppContext,
2950 ) -> Option<HashMap<Model<Buffer>, Vec<Range<text::Anchor>>>> {
2951 if self.linked_edit_ranges.is_empty() {
2952 return None;
2953 }
2954 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2955 selection.end.buffer_id.and_then(|end_buffer_id| {
2956 if selection.start.buffer_id != Some(end_buffer_id) {
2957 return None;
2958 }
2959 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2960 let snapshot = buffer.read(cx).snapshot();
2961 self.linked_edit_ranges
2962 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2963 .map(|ranges| (ranges, snapshot, buffer))
2964 })?;
2965 use text::ToOffset as TO;
2966 // find offset from the start of current range to current cursor position
2967 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2968
2969 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2970 let start_difference = start_offset - start_byte_offset;
2971 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2972 let end_difference = end_offset - start_byte_offset;
2973 // Current range has associated linked ranges.
2974 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2975 for range in linked_ranges.iter() {
2976 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2977 let end_offset = start_offset + end_difference;
2978 let start_offset = start_offset + start_difference;
2979 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2980 continue;
2981 }
2982 let start = buffer_snapshot.anchor_after(start_offset);
2983 let end = buffer_snapshot.anchor_after(end_offset);
2984 linked_edits
2985 .entry(buffer.clone())
2986 .or_default()
2987 .push(start..end);
2988 }
2989 Some(linked_edits)
2990 }
2991
2992 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2993 let text: Arc<str> = text.into();
2994
2995 if self.read_only(cx) {
2996 return;
2997 }
2998
2999 let selections = self.selections.all_adjusted(cx);
3000 let mut bracket_inserted = false;
3001 let mut edits = Vec::new();
3002 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3003 let mut new_selections = Vec::with_capacity(selections.len());
3004 let mut new_autoclose_regions = Vec::new();
3005 let snapshot = self.buffer.read(cx).read(cx);
3006
3007 for (selection, autoclose_region) in
3008 self.selections_with_autoclose_regions(selections, &snapshot)
3009 {
3010 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
3011 // Determine if the inserted text matches the opening or closing
3012 // bracket of any of this language's bracket pairs.
3013 let mut bracket_pair = None;
3014 let mut is_bracket_pair_start = false;
3015 let mut is_bracket_pair_end = false;
3016 if !text.is_empty() {
3017 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
3018 // and they are removing the character that triggered IME popup.
3019 for (pair, enabled) in scope.brackets() {
3020 if !pair.close && !pair.surround {
3021 continue;
3022 }
3023
3024 if enabled && pair.start.ends_with(text.as_ref()) {
3025 bracket_pair = Some(pair.clone());
3026 is_bracket_pair_start = true;
3027 break;
3028 }
3029 if pair.end.as_str() == text.as_ref() {
3030 bracket_pair = Some(pair.clone());
3031 is_bracket_pair_end = true;
3032 break;
3033 }
3034 }
3035 }
3036
3037 if let Some(bracket_pair) = bracket_pair {
3038 let snapshot_settings = snapshot.settings_at(selection.start, cx);
3039 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
3040 let auto_surround =
3041 self.use_auto_surround && snapshot_settings.use_auto_surround;
3042 if selection.is_empty() {
3043 if is_bracket_pair_start {
3044 let prefix_len = bracket_pair.start.len() - text.len();
3045
3046 // If the inserted text is a suffix of an opening bracket and the
3047 // selection is preceded by the rest of the opening bracket, then
3048 // insert the closing bracket.
3049 let following_text_allows_autoclose = snapshot
3050 .chars_at(selection.start)
3051 .next()
3052 .map_or(true, |c| scope.should_autoclose_before(c));
3053 let preceding_text_matches_prefix = prefix_len == 0
3054 || (selection.start.column >= (prefix_len as u32)
3055 && snapshot.contains_str_at(
3056 Point::new(
3057 selection.start.row,
3058 selection.start.column - (prefix_len as u32),
3059 ),
3060 &bracket_pair.start[..prefix_len],
3061 ));
3062
3063 if autoclose
3064 && bracket_pair.close
3065 && following_text_allows_autoclose
3066 && preceding_text_matches_prefix
3067 {
3068 let anchor = snapshot.anchor_before(selection.end);
3069 new_selections.push((selection.map(|_| anchor), text.len()));
3070 new_autoclose_regions.push((
3071 anchor,
3072 text.len(),
3073 selection.id,
3074 bracket_pair.clone(),
3075 ));
3076 edits.push((
3077 selection.range(),
3078 format!("{}{}", text, bracket_pair.end).into(),
3079 ));
3080 bracket_inserted = true;
3081 continue;
3082 }
3083 }
3084
3085 if let Some(region) = autoclose_region {
3086 // If the selection is followed by an auto-inserted closing bracket,
3087 // then don't insert that closing bracket again; just move the selection
3088 // past the closing bracket.
3089 let should_skip = selection.end == region.range.end.to_point(&snapshot)
3090 && text.as_ref() == region.pair.end.as_str();
3091 if should_skip {
3092 let anchor = snapshot.anchor_after(selection.end);
3093 new_selections
3094 .push((selection.map(|_| anchor), region.pair.end.len()));
3095 continue;
3096 }
3097 }
3098
3099 let always_treat_brackets_as_autoclosed = snapshot
3100 .settings_at(selection.start, cx)
3101 .always_treat_brackets_as_autoclosed;
3102 if always_treat_brackets_as_autoclosed
3103 && is_bracket_pair_end
3104 && snapshot.contains_str_at(selection.end, text.as_ref())
3105 {
3106 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
3107 // and the inserted text is a closing bracket and the selection is followed
3108 // by the closing bracket then move the selection past the closing bracket.
3109 let anchor = snapshot.anchor_after(selection.end);
3110 new_selections.push((selection.map(|_| anchor), text.len()));
3111 continue;
3112 }
3113 }
3114 // If an opening bracket is 1 character long and is typed while
3115 // text is selected, then surround that text with the bracket pair.
3116 else if auto_surround
3117 && bracket_pair.surround
3118 && is_bracket_pair_start
3119 && bracket_pair.start.chars().count() == 1
3120 {
3121 edits.push((selection.start..selection.start, text.clone()));
3122 edits.push((
3123 selection.end..selection.end,
3124 bracket_pair.end.as_str().into(),
3125 ));
3126 bracket_inserted = true;
3127 new_selections.push((
3128 Selection {
3129 id: selection.id,
3130 start: snapshot.anchor_after(selection.start),
3131 end: snapshot.anchor_before(selection.end),
3132 reversed: selection.reversed,
3133 goal: selection.goal,
3134 },
3135 0,
3136 ));
3137 continue;
3138 }
3139 }
3140 }
3141
3142 if self.auto_replace_emoji_shortcode
3143 && selection.is_empty()
3144 && text.as_ref().ends_with(':')
3145 {
3146 if let Some(possible_emoji_short_code) =
3147 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
3148 {
3149 if !possible_emoji_short_code.is_empty() {
3150 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
3151 let emoji_shortcode_start = Point::new(
3152 selection.start.row,
3153 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
3154 );
3155
3156 // Remove shortcode from buffer
3157 edits.push((
3158 emoji_shortcode_start..selection.start,
3159 "".to_string().into(),
3160 ));
3161 new_selections.push((
3162 Selection {
3163 id: selection.id,
3164 start: snapshot.anchor_after(emoji_shortcode_start),
3165 end: snapshot.anchor_before(selection.start),
3166 reversed: selection.reversed,
3167 goal: selection.goal,
3168 },
3169 0,
3170 ));
3171
3172 // Insert emoji
3173 let selection_start_anchor = snapshot.anchor_after(selection.start);
3174 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3175 edits.push((selection.start..selection.end, emoji.to_string().into()));
3176
3177 continue;
3178 }
3179 }
3180 }
3181 }
3182
3183 // If not handling any auto-close operation, then just replace the selected
3184 // text with the given input and move the selection to the end of the
3185 // newly inserted text.
3186 let anchor = snapshot.anchor_after(selection.end);
3187 if !self.linked_edit_ranges.is_empty() {
3188 let start_anchor = snapshot.anchor_before(selection.start);
3189
3190 let is_word_char = text.chars().next().map_or(true, |char| {
3191 let scope = snapshot.language_scope_at(start_anchor.to_offset(&snapshot));
3192 let kind = char_kind(&scope, char);
3193
3194 kind == CharKind::Word
3195 });
3196
3197 if is_word_char {
3198 if let Some(ranges) = self
3199 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3200 {
3201 for (buffer, edits) in ranges {
3202 linked_edits
3203 .entry(buffer.clone())
3204 .or_default()
3205 .extend(edits.into_iter().map(|range| (range, text.clone())));
3206 }
3207 }
3208 }
3209 }
3210
3211 new_selections.push((selection.map(|_| anchor), 0));
3212 edits.push((selection.start..selection.end, text.clone()));
3213 }
3214
3215 drop(snapshot);
3216
3217 self.transact(cx, |this, cx| {
3218 this.buffer.update(cx, |buffer, cx| {
3219 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3220 });
3221 for (buffer, edits) in linked_edits {
3222 buffer.update(cx, |buffer, cx| {
3223 let snapshot = buffer.snapshot();
3224 let edits = edits
3225 .into_iter()
3226 .map(|(range, text)| {
3227 use text::ToPoint as TP;
3228 let end_point = TP::to_point(&range.end, &snapshot);
3229 let start_point = TP::to_point(&range.start, &snapshot);
3230 (start_point..end_point, text)
3231 })
3232 .sorted_by_key(|(range, _)| range.start)
3233 .collect::<Vec<_>>();
3234 buffer.edit(edits, None, cx);
3235 })
3236 }
3237 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3238 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3239 let snapshot = this.buffer.read(cx).read(cx);
3240 let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
3241 .zip(new_selection_deltas)
3242 .map(|(selection, delta)| Selection {
3243 id: selection.id,
3244 start: selection.start + delta,
3245 end: selection.end + delta,
3246 reversed: selection.reversed,
3247 goal: SelectionGoal::None,
3248 })
3249 .collect::<Vec<_>>();
3250
3251 let mut i = 0;
3252 for (position, delta, selection_id, pair) in new_autoclose_regions {
3253 let position = position.to_offset(&snapshot) + delta;
3254 let start = snapshot.anchor_before(position);
3255 let end = snapshot.anchor_after(position);
3256 while let Some(existing_state) = this.autoclose_regions.get(i) {
3257 match existing_state.range.start.cmp(&start, &snapshot) {
3258 Ordering::Less => i += 1,
3259 Ordering::Greater => break,
3260 Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
3261 Ordering::Less => i += 1,
3262 Ordering::Equal => break,
3263 Ordering::Greater => break,
3264 },
3265 }
3266 }
3267 this.autoclose_regions.insert(
3268 i,
3269 AutocloseRegion {
3270 selection_id,
3271 range: start..end,
3272 pair,
3273 },
3274 );
3275 }
3276
3277 drop(snapshot);
3278 let had_active_inline_completion = this.has_active_inline_completion(cx);
3279 this.change_selections_inner(Some(Autoscroll::fit()), false, cx, |s| {
3280 s.select(new_selections)
3281 });
3282
3283 if !bracket_inserted && EditorSettings::get_global(cx).use_on_type_format {
3284 if let Some(on_type_format_task) =
3285 this.trigger_on_type_formatting(text.to_string(), cx)
3286 {
3287 on_type_format_task.detach_and_log_err(cx);
3288 }
3289 }
3290
3291 let editor_settings = EditorSettings::get_global(cx);
3292 if bracket_inserted
3293 && (editor_settings.auto_signature_help
3294 || editor_settings.show_signature_help_after_edits)
3295 {
3296 this.show_signature_help(&ShowSignatureHelp, cx);
3297 }
3298
3299 let trigger_in_words = !had_active_inline_completion;
3300 this.trigger_completion_on_input(&text, trigger_in_words, cx);
3301 linked_editing_ranges::refresh_linked_ranges(this, cx);
3302 this.refresh_inline_completion(true, cx);
3303 });
3304 }
3305
3306 fn find_possible_emoji_shortcode_at_position(
3307 snapshot: &MultiBufferSnapshot,
3308 position: Point,
3309 ) -> Option<String> {
3310 let mut chars = Vec::new();
3311 let mut found_colon = false;
3312 for char in snapshot.reversed_chars_at(position).take(100) {
3313 // Found a possible emoji shortcode in the middle of the buffer
3314 if found_colon {
3315 if char.is_whitespace() {
3316 chars.reverse();
3317 return Some(chars.iter().collect());
3318 }
3319 // If the previous character is not a whitespace, we are in the middle of a word
3320 // and we only want to complete the shortcode if the word is made up of other emojis
3321 let mut containing_word = String::new();
3322 for ch in snapshot
3323 .reversed_chars_at(position)
3324 .skip(chars.len() + 1)
3325 .take(100)
3326 {
3327 if ch.is_whitespace() {
3328 break;
3329 }
3330 containing_word.push(ch);
3331 }
3332 let containing_word = containing_word.chars().rev().collect::<String>();
3333 if util::word_consists_of_emojis(containing_word.as_str()) {
3334 chars.reverse();
3335 return Some(chars.iter().collect());
3336 }
3337 }
3338
3339 if char.is_whitespace() || !char.is_ascii() {
3340 return None;
3341 }
3342 if char == ':' {
3343 found_colon = true;
3344 } else {
3345 chars.push(char);
3346 }
3347 }
3348 // Found a possible emoji shortcode at the beginning of the buffer
3349 chars.reverse();
3350 Some(chars.iter().collect())
3351 }
3352
3353 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
3354 self.transact(cx, |this, cx| {
3355 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3356 let selections = this.selections.all::<usize>(cx);
3357 let multi_buffer = this.buffer.read(cx);
3358 let buffer = multi_buffer.snapshot(cx);
3359 selections
3360 .iter()
3361 .map(|selection| {
3362 let start_point = selection.start.to_point(&buffer);
3363 let mut indent =
3364 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3365 indent.len = cmp::min(indent.len, start_point.column);
3366 let start = selection.start;
3367 let end = selection.end;
3368 let selection_is_empty = start == end;
3369 let language_scope = buffer.language_scope_at(start);
3370 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3371 &language_scope
3372 {
3373 let leading_whitespace_len = buffer
3374 .reversed_chars_at(start)
3375 .take_while(|c| c.is_whitespace() && *c != '\n')
3376 .map(|c| c.len_utf8())
3377 .sum::<usize>();
3378
3379 let trailing_whitespace_len = buffer
3380 .chars_at(end)
3381 .take_while(|c| c.is_whitespace() && *c != '\n')
3382 .map(|c| c.len_utf8())
3383 .sum::<usize>();
3384
3385 let insert_extra_newline =
3386 language.brackets().any(|(pair, enabled)| {
3387 let pair_start = pair.start.trim_end();
3388 let pair_end = pair.end.trim_start();
3389
3390 enabled
3391 && pair.newline
3392 && buffer.contains_str_at(
3393 end + trailing_whitespace_len,
3394 pair_end,
3395 )
3396 && buffer.contains_str_at(
3397 (start - leading_whitespace_len)
3398 .saturating_sub(pair_start.len()),
3399 pair_start,
3400 )
3401 });
3402
3403 // Comment extension on newline is allowed only for cursor selections
3404 let comment_delimiter = maybe!({
3405 if !selection_is_empty {
3406 return None;
3407 }
3408
3409 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
3410 return None;
3411 }
3412
3413 let delimiters = language.line_comment_prefixes();
3414 let max_len_of_delimiter =
3415 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3416 let (snapshot, range) =
3417 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3418
3419 let mut index_of_first_non_whitespace = 0;
3420 let comment_candidate = snapshot
3421 .chars_for_range(range)
3422 .skip_while(|c| {
3423 let should_skip = c.is_whitespace();
3424 if should_skip {
3425 index_of_first_non_whitespace += 1;
3426 }
3427 should_skip
3428 })
3429 .take(max_len_of_delimiter)
3430 .collect::<String>();
3431 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3432 comment_candidate.starts_with(comment_prefix.as_ref())
3433 })?;
3434 let cursor_is_placed_after_comment_marker =
3435 index_of_first_non_whitespace + comment_prefix.len()
3436 <= start_point.column as usize;
3437 if cursor_is_placed_after_comment_marker {
3438 Some(comment_prefix.clone())
3439 } else {
3440 None
3441 }
3442 });
3443 (comment_delimiter, insert_extra_newline)
3444 } else {
3445 (None, false)
3446 };
3447
3448 let capacity_for_delimiter = comment_delimiter
3449 .as_deref()
3450 .map(str::len)
3451 .unwrap_or_default();
3452 let mut new_text =
3453 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3454 new_text.push_str("\n");
3455 new_text.extend(indent.chars());
3456 if let Some(delimiter) = &comment_delimiter {
3457 new_text.push_str(&delimiter);
3458 }
3459 if insert_extra_newline {
3460 new_text = new_text.repeat(2);
3461 }
3462
3463 let anchor = buffer.anchor_after(end);
3464 let new_selection = selection.map(|_| anchor);
3465 (
3466 (start..end, new_text),
3467 (insert_extra_newline, new_selection),
3468 )
3469 })
3470 .unzip()
3471 };
3472
3473 this.edit_with_autoindent(edits, cx);
3474 let buffer = this.buffer.read(cx).snapshot(cx);
3475 let new_selections = selection_fixup_info
3476 .into_iter()
3477 .map(|(extra_newline_inserted, new_selection)| {
3478 let mut cursor = new_selection.end.to_point(&buffer);
3479 if extra_newline_inserted {
3480 cursor.row -= 1;
3481 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3482 }
3483 new_selection.map(|_| cursor)
3484 })
3485 .collect();
3486
3487 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
3488 this.refresh_inline_completion(true, cx);
3489 });
3490 }
3491
3492 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
3493 let buffer = self.buffer.read(cx);
3494 let snapshot = buffer.snapshot(cx);
3495
3496 let mut edits = Vec::new();
3497 let mut rows = Vec::new();
3498
3499 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3500 let cursor = selection.head();
3501 let row = cursor.row;
3502
3503 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3504
3505 let newline = "\n".to_string();
3506 edits.push((start_of_line..start_of_line, newline));
3507
3508 rows.push(row + rows_inserted as u32);
3509 }
3510
3511 self.transact(cx, |editor, cx| {
3512 editor.edit(edits, cx);
3513
3514 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3515 let mut index = 0;
3516 s.move_cursors_with(|map, _, _| {
3517 let row = rows[index];
3518 index += 1;
3519
3520 let point = Point::new(row, 0);
3521 let boundary = map.next_line_boundary(point).1;
3522 let clipped = map.clip_point(boundary, Bias::Left);
3523
3524 (clipped, SelectionGoal::None)
3525 });
3526 });
3527
3528 let mut indent_edits = Vec::new();
3529 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3530 for row in rows {
3531 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3532 for (row, indent) in indents {
3533 if indent.len == 0 {
3534 continue;
3535 }
3536
3537 let text = match indent.kind {
3538 IndentKind::Space => " ".repeat(indent.len as usize),
3539 IndentKind::Tab => "\t".repeat(indent.len as usize),
3540 };
3541 let point = Point::new(row.0, 0);
3542 indent_edits.push((point..point, text));
3543 }
3544 }
3545 editor.edit(indent_edits, cx);
3546 });
3547 }
3548
3549 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
3550 let buffer = self.buffer.read(cx);
3551 let snapshot = buffer.snapshot(cx);
3552
3553 let mut edits = Vec::new();
3554 let mut rows = Vec::new();
3555 let mut rows_inserted = 0;
3556
3557 for selection in self.selections.all_adjusted(cx) {
3558 let cursor = selection.head();
3559 let row = cursor.row;
3560
3561 let point = Point::new(row + 1, 0);
3562 let start_of_line = snapshot.clip_point(point, Bias::Left);
3563
3564 let newline = "\n".to_string();
3565 edits.push((start_of_line..start_of_line, newline));
3566
3567 rows_inserted += 1;
3568 rows.push(row + rows_inserted);
3569 }
3570
3571 self.transact(cx, |editor, cx| {
3572 editor.edit(edits, cx);
3573
3574 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3575 let mut index = 0;
3576 s.move_cursors_with(|map, _, _| {
3577 let row = rows[index];
3578 index += 1;
3579
3580 let point = Point::new(row, 0);
3581 let boundary = map.next_line_boundary(point).1;
3582 let clipped = map.clip_point(boundary, Bias::Left);
3583
3584 (clipped, SelectionGoal::None)
3585 });
3586 });
3587
3588 let mut indent_edits = Vec::new();
3589 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3590 for row in rows {
3591 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3592 for (row, indent) in indents {
3593 if indent.len == 0 {
3594 continue;
3595 }
3596
3597 let text = match indent.kind {
3598 IndentKind::Space => " ".repeat(indent.len as usize),
3599 IndentKind::Tab => "\t".repeat(indent.len as usize),
3600 };
3601 let point = Point::new(row.0, 0);
3602 indent_edits.push((point..point, text));
3603 }
3604 }
3605 editor.edit(indent_edits, cx);
3606 });
3607 }
3608
3609 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3610 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3611 original_indent_columns: Vec::new(),
3612 });
3613 self.insert_with_autoindent_mode(text, autoindent, cx);
3614 }
3615
3616 fn insert_with_autoindent_mode(
3617 &mut self,
3618 text: &str,
3619 autoindent_mode: Option<AutoindentMode>,
3620 cx: &mut ViewContext<Self>,
3621 ) {
3622 if self.read_only(cx) {
3623 return;
3624 }
3625
3626 let text: Arc<str> = text.into();
3627 self.transact(cx, |this, cx| {
3628 let old_selections = this.selections.all_adjusted(cx);
3629 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3630 let anchors = {
3631 let snapshot = buffer.read(cx);
3632 old_selections
3633 .iter()
3634 .map(|s| {
3635 let anchor = snapshot.anchor_after(s.head());
3636 s.map(|_| anchor)
3637 })
3638 .collect::<Vec<_>>()
3639 };
3640 buffer.edit(
3641 old_selections
3642 .iter()
3643 .map(|s| (s.start..s.end, text.clone())),
3644 autoindent_mode,
3645 cx,
3646 );
3647 anchors
3648 });
3649
3650 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3651 s.select_anchors(selection_anchors);
3652 })
3653 });
3654 }
3655
3656 fn trigger_completion_on_input(
3657 &mut self,
3658 text: &str,
3659 trigger_in_words: bool,
3660 cx: &mut ViewContext<Self>,
3661 ) {
3662 if self.is_completion_trigger(text, trigger_in_words, cx) {
3663 self.show_completions(
3664 &ShowCompletions {
3665 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3666 },
3667 cx,
3668 );
3669 } else {
3670 self.hide_context_menu(cx);
3671 }
3672 }
3673
3674 fn is_completion_trigger(
3675 &self,
3676 text: &str,
3677 trigger_in_words: bool,
3678 cx: &mut ViewContext<Self>,
3679 ) -> bool {
3680 let position = self.selections.newest_anchor().head();
3681 let multibuffer = self.buffer.read(cx);
3682 let Some(buffer) = position
3683 .buffer_id
3684 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3685 else {
3686 return false;
3687 };
3688
3689 if let Some(completion_provider) = &self.completion_provider {
3690 completion_provider.is_completion_trigger(
3691 &buffer,
3692 position.text_anchor,
3693 text,
3694 trigger_in_words,
3695 cx,
3696 )
3697 } else {
3698 false
3699 }
3700 }
3701
3702 /// If any empty selections is touching the start of its innermost containing autoclose
3703 /// region, expand it to select the brackets.
3704 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
3705 let selections = self.selections.all::<usize>(cx);
3706 let buffer = self.buffer.read(cx).read(cx);
3707 let new_selections = self
3708 .selections_with_autoclose_regions(selections, &buffer)
3709 .map(|(mut selection, region)| {
3710 if !selection.is_empty() {
3711 return selection;
3712 }
3713
3714 if let Some(region) = region {
3715 let mut range = region.range.to_offset(&buffer);
3716 if selection.start == range.start && range.start >= region.pair.start.len() {
3717 range.start -= region.pair.start.len();
3718 if buffer.contains_str_at(range.start, ®ion.pair.start)
3719 && buffer.contains_str_at(range.end, ®ion.pair.end)
3720 {
3721 range.end += region.pair.end.len();
3722 selection.start = range.start;
3723 selection.end = range.end;
3724
3725 return selection;
3726 }
3727 }
3728 }
3729
3730 let always_treat_brackets_as_autoclosed = buffer
3731 .settings_at(selection.start, cx)
3732 .always_treat_brackets_as_autoclosed;
3733
3734 if !always_treat_brackets_as_autoclosed {
3735 return selection;
3736 }
3737
3738 if let Some(scope) = buffer.language_scope_at(selection.start) {
3739 for (pair, enabled) in scope.brackets() {
3740 if !enabled || !pair.close {
3741 continue;
3742 }
3743
3744 if buffer.contains_str_at(selection.start, &pair.end) {
3745 let pair_start_len = pair.start.len();
3746 if buffer.contains_str_at(selection.start - pair_start_len, &pair.start)
3747 {
3748 selection.start -= pair_start_len;
3749 selection.end += pair.end.len();
3750
3751 return selection;
3752 }
3753 }
3754 }
3755 }
3756
3757 selection
3758 })
3759 .collect();
3760
3761 drop(buffer);
3762 self.change_selections(None, cx, |selections| selections.select(new_selections));
3763 }
3764
3765 /// Iterate the given selections, and for each one, find the smallest surrounding
3766 /// autoclose region. This uses the ordering of the selections and the autoclose
3767 /// regions to avoid repeated comparisons.
3768 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3769 &'a self,
3770 selections: impl IntoIterator<Item = Selection<D>>,
3771 buffer: &'a MultiBufferSnapshot,
3772 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3773 let mut i = 0;
3774 let mut regions = self.autoclose_regions.as_slice();
3775 selections.into_iter().map(move |selection| {
3776 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3777
3778 let mut enclosing = None;
3779 while let Some(pair_state) = regions.get(i) {
3780 if pair_state.range.end.to_offset(buffer) < range.start {
3781 regions = ®ions[i + 1..];
3782 i = 0;
3783 } else if pair_state.range.start.to_offset(buffer) > range.end {
3784 break;
3785 } else {
3786 if pair_state.selection_id == selection.id {
3787 enclosing = Some(pair_state);
3788 }
3789 i += 1;
3790 }
3791 }
3792
3793 (selection.clone(), enclosing)
3794 })
3795 }
3796
3797 /// Remove any autoclose regions that no longer contain their selection.
3798 fn invalidate_autoclose_regions(
3799 &mut self,
3800 mut selections: &[Selection<Anchor>],
3801 buffer: &MultiBufferSnapshot,
3802 ) {
3803 self.autoclose_regions.retain(|state| {
3804 let mut i = 0;
3805 while let Some(selection) = selections.get(i) {
3806 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3807 selections = &selections[1..];
3808 continue;
3809 }
3810 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3811 break;
3812 }
3813 if selection.id == state.selection_id {
3814 return true;
3815 } else {
3816 i += 1;
3817 }
3818 }
3819 false
3820 });
3821 }
3822
3823 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3824 let offset = position.to_offset(buffer);
3825 let (word_range, kind) = buffer.surrounding_word(offset);
3826 if offset > word_range.start && kind == Some(CharKind::Word) {
3827 Some(
3828 buffer
3829 .text_for_range(word_range.start..offset)
3830 .collect::<String>(),
3831 )
3832 } else {
3833 None
3834 }
3835 }
3836
3837 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
3838 self.refresh_inlay_hints(
3839 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
3840 cx,
3841 );
3842 }
3843
3844 pub fn inlay_hints_enabled(&self) -> bool {
3845 self.inlay_hint_cache.enabled
3846 }
3847
3848 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
3849 if self.project.is_none() || self.mode != EditorMode::Full {
3850 return;
3851 }
3852
3853 let reason_description = reason.description();
3854 let ignore_debounce = matches!(
3855 reason,
3856 InlayHintRefreshReason::SettingsChange(_)
3857 | InlayHintRefreshReason::Toggle(_)
3858 | InlayHintRefreshReason::ExcerptsRemoved(_)
3859 );
3860 let (invalidate_cache, required_languages) = match reason {
3861 InlayHintRefreshReason::Toggle(enabled) => {
3862 self.inlay_hint_cache.enabled = enabled;
3863 if enabled {
3864 (InvalidationStrategy::RefreshRequested, None)
3865 } else {
3866 self.inlay_hint_cache.clear();
3867 self.splice_inlays(
3868 self.visible_inlay_hints(cx)
3869 .iter()
3870 .map(|inlay| inlay.id)
3871 .collect(),
3872 Vec::new(),
3873 cx,
3874 );
3875 return;
3876 }
3877 }
3878 InlayHintRefreshReason::SettingsChange(new_settings) => {
3879 match self.inlay_hint_cache.update_settings(
3880 &self.buffer,
3881 new_settings,
3882 self.visible_inlay_hints(cx),
3883 cx,
3884 ) {
3885 ControlFlow::Break(Some(InlaySplice {
3886 to_remove,
3887 to_insert,
3888 })) => {
3889 self.splice_inlays(to_remove, to_insert, cx);
3890 return;
3891 }
3892 ControlFlow::Break(None) => return,
3893 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3894 }
3895 }
3896 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3897 if let Some(InlaySplice {
3898 to_remove,
3899 to_insert,
3900 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3901 {
3902 self.splice_inlays(to_remove, to_insert, cx);
3903 }
3904 return;
3905 }
3906 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3907 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3908 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3909 }
3910 InlayHintRefreshReason::RefreshRequested => {
3911 (InvalidationStrategy::RefreshRequested, None)
3912 }
3913 };
3914
3915 if let Some(InlaySplice {
3916 to_remove,
3917 to_insert,
3918 }) = self.inlay_hint_cache.spawn_hint_refresh(
3919 reason_description,
3920 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3921 invalidate_cache,
3922 ignore_debounce,
3923 cx,
3924 ) {
3925 self.splice_inlays(to_remove, to_insert, cx);
3926 }
3927 }
3928
3929 fn visible_inlay_hints(&self, cx: &ViewContext<'_, Editor>) -> Vec<Inlay> {
3930 self.display_map
3931 .read(cx)
3932 .current_inlays()
3933 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3934 .cloned()
3935 .collect()
3936 }
3937
3938 pub fn excerpts_for_inlay_hints_query(
3939 &self,
3940 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3941 cx: &mut ViewContext<Editor>,
3942 ) -> HashMap<ExcerptId, (Model<Buffer>, clock::Global, Range<usize>)> {
3943 let Some(project) = self.project.as_ref() else {
3944 return HashMap::default();
3945 };
3946 let project = project.read(cx);
3947 let multi_buffer = self.buffer().read(cx);
3948 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3949 let multi_buffer_visible_start = self
3950 .scroll_manager
3951 .anchor()
3952 .anchor
3953 .to_point(&multi_buffer_snapshot);
3954 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3955 multi_buffer_visible_start
3956 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3957 Bias::Left,
3958 );
3959 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3960 multi_buffer
3961 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
3962 .into_iter()
3963 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3964 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
3965 let buffer = buffer_handle.read(cx);
3966 let buffer_file = project::File::from_dyn(buffer.file())?;
3967 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3968 let worktree_entry = buffer_worktree
3969 .read(cx)
3970 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3971 if worktree_entry.is_ignored {
3972 return None;
3973 }
3974
3975 let language = buffer.language()?;
3976 if let Some(restrict_to_languages) = restrict_to_languages {
3977 if !restrict_to_languages.contains(language) {
3978 return None;
3979 }
3980 }
3981 Some((
3982 excerpt_id,
3983 (
3984 buffer_handle,
3985 buffer.version().clone(),
3986 excerpt_visible_range,
3987 ),
3988 ))
3989 })
3990 .collect()
3991 }
3992
3993 pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
3994 TextLayoutDetails {
3995 text_system: cx.text_system().clone(),
3996 editor_style: self.style.clone().unwrap(),
3997 rem_size: cx.rem_size(),
3998 scroll_anchor: self.scroll_manager.anchor(),
3999 visible_rows: self.visible_line_count(),
4000 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
4001 }
4002 }
4003
4004 fn splice_inlays(
4005 &self,
4006 to_remove: Vec<InlayId>,
4007 to_insert: Vec<Inlay>,
4008 cx: &mut ViewContext<Self>,
4009 ) {
4010 self.display_map.update(cx, |display_map, cx| {
4011 display_map.splice_inlays(to_remove, to_insert, cx);
4012 });
4013 cx.notify();
4014 }
4015
4016 fn trigger_on_type_formatting(
4017 &self,
4018 input: String,
4019 cx: &mut ViewContext<Self>,
4020 ) -> Option<Task<Result<()>>> {
4021 if input.len() != 1 {
4022 return None;
4023 }
4024
4025 let project = self.project.as_ref()?;
4026 let position = self.selections.newest_anchor().head();
4027 let (buffer, buffer_position) = self
4028 .buffer
4029 .read(cx)
4030 .text_anchor_for_position(position, cx)?;
4031
4032 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
4033 // hence we do LSP request & edit on host side only — add formats to host's history.
4034 let push_to_lsp_host_history = true;
4035 // If this is not the host, append its history with new edits.
4036 let push_to_client_history = project.read(cx).is_remote();
4037
4038 let on_type_formatting = project.update(cx, |project, cx| {
4039 project.on_type_format(
4040 buffer.clone(),
4041 buffer_position,
4042 input,
4043 push_to_lsp_host_history,
4044 cx,
4045 )
4046 });
4047 Some(cx.spawn(|editor, mut cx| async move {
4048 if let Some(transaction) = on_type_formatting.await? {
4049 if push_to_client_history {
4050 buffer
4051 .update(&mut cx, |buffer, _| {
4052 buffer.push_transaction(transaction, Instant::now());
4053 })
4054 .ok();
4055 }
4056 editor.update(&mut cx, |editor, cx| {
4057 editor.refresh_document_highlights(cx);
4058 })?;
4059 }
4060 Ok(())
4061 }))
4062 }
4063
4064 pub fn show_completions(&mut self, options: &ShowCompletions, cx: &mut ViewContext<Self>) {
4065 if self.pending_rename.is_some() {
4066 return;
4067 }
4068
4069 let Some(provider) = self.completion_provider.as_ref() else {
4070 return;
4071 };
4072
4073 let position = self.selections.newest_anchor().head();
4074 let (buffer, buffer_position) =
4075 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
4076 output
4077 } else {
4078 return;
4079 };
4080
4081 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
4082 let is_followup_invoke = {
4083 let context_menu_state = self.context_menu.read();
4084 matches!(
4085 context_menu_state.deref(),
4086 Some(ContextMenu::Completions(_))
4087 )
4088 };
4089 let trigger_kind = match (&options.trigger, is_followup_invoke) {
4090 (_, true) => CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS,
4091 (Some(trigger), _) if buffer.read(cx).completion_triggers().contains(&trigger) => {
4092 CompletionTriggerKind::TRIGGER_CHARACTER
4093 }
4094
4095 _ => CompletionTriggerKind::INVOKED,
4096 };
4097 let completion_context = CompletionContext {
4098 trigger_character: options.trigger.as_ref().and_then(|trigger| {
4099 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
4100 Some(String::from(trigger))
4101 } else {
4102 None
4103 }
4104 }),
4105 trigger_kind,
4106 };
4107 let completions = provider.completions(&buffer, buffer_position, completion_context, cx);
4108
4109 let id = post_inc(&mut self.next_completion_id);
4110 let task = cx.spawn(|this, mut cx| {
4111 async move {
4112 this.update(&mut cx, |this, _| {
4113 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
4114 })?;
4115 let completions = completions.await.log_err();
4116 let menu = if let Some(completions) = completions {
4117 let mut menu = CompletionsMenu {
4118 id,
4119 initial_position: position,
4120 match_candidates: completions
4121 .iter()
4122 .enumerate()
4123 .map(|(id, completion)| {
4124 StringMatchCandidate::new(
4125 id,
4126 completion.label.text[completion.label.filter_range.clone()]
4127 .into(),
4128 )
4129 })
4130 .collect(),
4131 buffer: buffer.clone(),
4132 completions: Arc::new(RwLock::new(completions.into())),
4133 matches: Vec::new().into(),
4134 selected_item: 0,
4135 scroll_handle: UniformListScrollHandle::new(),
4136 selected_completion_documentation_resolve_debounce: Arc::new(Mutex::new(
4137 DebouncedDelay::new(),
4138 )),
4139 };
4140 menu.filter(query.as_deref(), cx.background_executor().clone())
4141 .await;
4142
4143 if menu.matches.is_empty() {
4144 None
4145 } else {
4146 this.update(&mut cx, |editor, cx| {
4147 let completions = menu.completions.clone();
4148 let matches = menu.matches.clone();
4149
4150 let delay_ms = EditorSettings::get_global(cx)
4151 .completion_documentation_secondary_query_debounce;
4152 let delay = Duration::from_millis(delay_ms);
4153 editor
4154 .completion_documentation_pre_resolve_debounce
4155 .fire_new(delay, cx, |editor, cx| {
4156 CompletionsMenu::pre_resolve_completion_documentation(
4157 buffer,
4158 completions,
4159 matches,
4160 editor,
4161 cx,
4162 )
4163 });
4164 })
4165 .ok();
4166 Some(menu)
4167 }
4168 } else {
4169 None
4170 };
4171
4172 this.update(&mut cx, |this, cx| {
4173 let mut context_menu = this.context_menu.write();
4174 match context_menu.as_ref() {
4175 None => {}
4176
4177 Some(ContextMenu::Completions(prev_menu)) => {
4178 if prev_menu.id > id {
4179 return;
4180 }
4181 }
4182
4183 _ => return,
4184 }
4185
4186 if this.focus_handle.is_focused(cx) && menu.is_some() {
4187 let menu = menu.unwrap();
4188 *context_menu = Some(ContextMenu::Completions(menu));
4189 drop(context_menu);
4190 this.discard_inline_completion(false, cx);
4191 cx.notify();
4192 } else if this.completion_tasks.len() <= 1 {
4193 // If there are no more completion tasks and the last menu was
4194 // empty, we should hide it. If it was already hidden, we should
4195 // also show the copilot completion when available.
4196 drop(context_menu);
4197 if this.hide_context_menu(cx).is_none() {
4198 this.update_visible_inline_completion(cx);
4199 }
4200 }
4201 })?;
4202
4203 Ok::<_, anyhow::Error>(())
4204 }
4205 .log_err()
4206 });
4207
4208 self.completion_tasks.push((id, task));
4209 }
4210
4211 pub fn confirm_completion(
4212 &mut self,
4213 action: &ConfirmCompletion,
4214 cx: &mut ViewContext<Self>,
4215 ) -> Option<Task<Result<()>>> {
4216 use language::ToOffset as _;
4217
4218 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
4219 menu
4220 } else {
4221 return None;
4222 };
4223
4224 let mat = completions_menu
4225 .matches
4226 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
4227 let buffer_handle = completions_menu.buffer;
4228 let completions = completions_menu.completions.read();
4229 let completion = completions.get(mat.candidate_id)?;
4230 cx.stop_propagation();
4231
4232 let snippet;
4233 let text;
4234
4235 if completion.is_snippet() {
4236 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4237 text = snippet.as_ref().unwrap().text.clone();
4238 } else {
4239 snippet = None;
4240 text = completion.new_text.clone();
4241 };
4242 let selections = self.selections.all::<usize>(cx);
4243 let buffer = buffer_handle.read(cx);
4244 let old_range = completion.old_range.to_offset(buffer);
4245 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4246
4247 let newest_selection = self.selections.newest_anchor();
4248 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4249 return None;
4250 }
4251
4252 let lookbehind = newest_selection
4253 .start
4254 .text_anchor
4255 .to_offset(buffer)
4256 .saturating_sub(old_range.start);
4257 let lookahead = old_range
4258 .end
4259 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4260 let mut common_prefix_len = old_text
4261 .bytes()
4262 .zip(text.bytes())
4263 .take_while(|(a, b)| a == b)
4264 .count();
4265
4266 let snapshot = self.buffer.read(cx).snapshot(cx);
4267 let mut range_to_replace: Option<Range<isize>> = None;
4268 let mut ranges = Vec::new();
4269 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4270 for selection in &selections {
4271 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4272 let start = selection.start.saturating_sub(lookbehind);
4273 let end = selection.end + lookahead;
4274 if selection.id == newest_selection.id {
4275 range_to_replace = Some(
4276 ((start + common_prefix_len) as isize - selection.start as isize)
4277 ..(end as isize - selection.start as isize),
4278 );
4279 }
4280 ranges.push(start + common_prefix_len..end);
4281 } else {
4282 common_prefix_len = 0;
4283 ranges.clear();
4284 ranges.extend(selections.iter().map(|s| {
4285 if s.id == newest_selection.id {
4286 range_to_replace = Some(
4287 old_range.start.to_offset_utf16(&snapshot).0 as isize
4288 - selection.start as isize
4289 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4290 - selection.start as isize,
4291 );
4292 old_range.clone()
4293 } else {
4294 s.start..s.end
4295 }
4296 }));
4297 break;
4298 }
4299 if !self.linked_edit_ranges.is_empty() {
4300 let start_anchor = snapshot.anchor_before(selection.head());
4301 let end_anchor = snapshot.anchor_after(selection.tail());
4302 if let Some(ranges) = self
4303 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4304 {
4305 for (buffer, edits) in ranges {
4306 linked_edits.entry(buffer.clone()).or_default().extend(
4307 edits
4308 .into_iter()
4309 .map(|range| (range, text[common_prefix_len..].to_owned())),
4310 );
4311 }
4312 }
4313 }
4314 }
4315 let text = &text[common_prefix_len..];
4316
4317 cx.emit(EditorEvent::InputHandled {
4318 utf16_range_to_replace: range_to_replace,
4319 text: text.into(),
4320 });
4321
4322 self.transact(cx, |this, cx| {
4323 if let Some(mut snippet) = snippet {
4324 snippet.text = text.to_string();
4325 for tabstop in snippet.tabstops.iter_mut().flatten() {
4326 tabstop.start -= common_prefix_len as isize;
4327 tabstop.end -= common_prefix_len as isize;
4328 }
4329
4330 this.insert_snippet(&ranges, snippet, cx).log_err();
4331 } else {
4332 this.buffer.update(cx, |buffer, cx| {
4333 buffer.edit(
4334 ranges.iter().map(|range| (range.clone(), text)),
4335 this.autoindent_mode.clone(),
4336 cx,
4337 );
4338 });
4339 }
4340 for (buffer, edits) in linked_edits {
4341 buffer.update(cx, |buffer, cx| {
4342 let snapshot = buffer.snapshot();
4343 let edits = edits
4344 .into_iter()
4345 .map(|(range, text)| {
4346 use text::ToPoint as TP;
4347 let end_point = TP::to_point(&range.end, &snapshot);
4348 let start_point = TP::to_point(&range.start, &snapshot);
4349 (start_point..end_point, text)
4350 })
4351 .sorted_by_key(|(range, _)| range.start)
4352 .collect::<Vec<_>>();
4353 buffer.edit(edits, None, cx);
4354 })
4355 }
4356
4357 this.refresh_inline_completion(true, cx);
4358 });
4359
4360 if let Some(confirm) = completion.confirm.as_ref() {
4361 (confirm)(cx);
4362 }
4363
4364 if completion.show_new_completions_on_confirm {
4365 self.show_completions(&ShowCompletions { trigger: None }, cx);
4366 }
4367
4368 let provider = self.completion_provider.as_ref()?;
4369 let apply_edits = provider.apply_additional_edits_for_completion(
4370 buffer_handle,
4371 completion.clone(),
4372 true,
4373 cx,
4374 );
4375
4376 let editor_settings = EditorSettings::get_global(cx);
4377 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4378 // After the code completion is finished, users often want to know what signatures are needed.
4379 // so we should automatically call signature_help
4380 self.show_signature_help(&ShowSignatureHelp, cx);
4381 }
4382
4383 Some(cx.foreground_executor().spawn(async move {
4384 apply_edits.await?;
4385 Ok(())
4386 }))
4387 }
4388
4389 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
4390 let mut context_menu = self.context_menu.write();
4391 if let Some(ContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4392 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4393 // Toggle if we're selecting the same one
4394 *context_menu = None;
4395 cx.notify();
4396 return;
4397 } else {
4398 // Otherwise, clear it and start a new one
4399 *context_menu = None;
4400 cx.notify();
4401 }
4402 }
4403 drop(context_menu);
4404 let snapshot = self.snapshot(cx);
4405 let deployed_from_indicator = action.deployed_from_indicator;
4406 let mut task = self.code_actions_task.take();
4407 let action = action.clone();
4408 cx.spawn(|editor, mut cx| async move {
4409 while let Some(prev_task) = task {
4410 prev_task.await;
4411 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4412 }
4413
4414 let spawned_test_task = editor.update(&mut cx, |editor, cx| {
4415 if editor.focus_handle.is_focused(cx) {
4416 let multibuffer_point = action
4417 .deployed_from_indicator
4418 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4419 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4420 let (buffer, buffer_row) = snapshot
4421 .buffer_snapshot
4422 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4423 .and_then(|(buffer_snapshot, range)| {
4424 editor
4425 .buffer
4426 .read(cx)
4427 .buffer(buffer_snapshot.remote_id())
4428 .map(|buffer| (buffer, range.start.row))
4429 })?;
4430 let (_, code_actions) = editor
4431 .available_code_actions
4432 .clone()
4433 .and_then(|(location, code_actions)| {
4434 let snapshot = location.buffer.read(cx).snapshot();
4435 let point_range = location.range.to_point(&snapshot);
4436 let point_range = point_range.start.row..=point_range.end.row;
4437 if point_range.contains(&buffer_row) {
4438 Some((location, code_actions))
4439 } else {
4440 None
4441 }
4442 })
4443 .unzip();
4444 let buffer_id = buffer.read(cx).remote_id();
4445 let tasks = editor
4446 .tasks
4447 .get(&(buffer_id, buffer_row))
4448 .map(|t| Arc::new(t.to_owned()));
4449 if tasks.is_none() && code_actions.is_none() {
4450 return None;
4451 }
4452
4453 editor.completion_tasks.clear();
4454 editor.discard_inline_completion(false, cx);
4455 let task_context =
4456 tasks
4457 .as_ref()
4458 .zip(editor.project.clone())
4459 .map(|(tasks, project)| {
4460 let position = Point::new(buffer_row, tasks.column);
4461 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
4462 let location = Location {
4463 buffer: buffer.clone(),
4464 range: range_start..range_start,
4465 };
4466 // Fill in the environmental variables from the tree-sitter captures
4467 let mut captured_task_variables = TaskVariables::default();
4468 for (capture_name, value) in tasks.extra_variables.clone() {
4469 captured_task_variables.insert(
4470 task::VariableName::Custom(capture_name.into()),
4471 value.clone(),
4472 );
4473 }
4474 project.update(cx, |project, cx| {
4475 project.task_context_for_location(
4476 captured_task_variables,
4477 location,
4478 cx,
4479 )
4480 })
4481 });
4482
4483 Some(cx.spawn(|editor, mut cx| async move {
4484 let task_context = match task_context {
4485 Some(task_context) => task_context.await,
4486 None => None,
4487 };
4488 let resolved_tasks =
4489 tasks.zip(task_context).map(|(tasks, task_context)| {
4490 Arc::new(ResolvedTasks {
4491 templates: tasks
4492 .templates
4493 .iter()
4494 .filter_map(|(kind, template)| {
4495 template
4496 .resolve_task(&kind.to_id_base(), &task_context)
4497 .map(|task| (kind.clone(), task))
4498 })
4499 .collect(),
4500 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4501 multibuffer_point.row,
4502 tasks.column,
4503 )),
4504 })
4505 });
4506 let spawn_straight_away = resolved_tasks
4507 .as_ref()
4508 .map_or(false, |tasks| tasks.templates.len() == 1)
4509 && code_actions
4510 .as_ref()
4511 .map_or(true, |actions| actions.is_empty());
4512 if let Some(task) = editor
4513 .update(&mut cx, |editor, cx| {
4514 *editor.context_menu.write() =
4515 Some(ContextMenu::CodeActions(CodeActionsMenu {
4516 buffer,
4517 actions: CodeActionContents {
4518 tasks: resolved_tasks,
4519 actions: code_actions,
4520 },
4521 selected_item: Default::default(),
4522 scroll_handle: UniformListScrollHandle::default(),
4523 deployed_from_indicator,
4524 }));
4525 if spawn_straight_away {
4526 if let Some(task) = editor.confirm_code_action(
4527 &ConfirmCodeAction { item_ix: Some(0) },
4528 cx,
4529 ) {
4530 cx.notify();
4531 return task;
4532 }
4533 }
4534 cx.notify();
4535 Task::ready(Ok(()))
4536 })
4537 .ok()
4538 {
4539 task.await
4540 } else {
4541 Ok(())
4542 }
4543 }))
4544 } else {
4545 Some(Task::ready(Ok(())))
4546 }
4547 })?;
4548 if let Some(task) = spawned_test_task {
4549 task.await?;
4550 }
4551
4552 Ok::<_, anyhow::Error>(())
4553 })
4554 .detach_and_log_err(cx);
4555 }
4556
4557 pub fn confirm_code_action(
4558 &mut self,
4559 action: &ConfirmCodeAction,
4560 cx: &mut ViewContext<Self>,
4561 ) -> Option<Task<Result<()>>> {
4562 let actions_menu = if let ContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
4563 menu
4564 } else {
4565 return None;
4566 };
4567 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4568 let action = actions_menu.actions.get(action_ix)?;
4569 let title = action.label();
4570 let buffer = actions_menu.buffer;
4571 let workspace = self.workspace()?;
4572
4573 match action {
4574 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4575 workspace.update(cx, |workspace, cx| {
4576 workspace::tasks::schedule_resolved_task(
4577 workspace,
4578 task_source_kind,
4579 resolved_task,
4580 false,
4581 cx,
4582 );
4583
4584 Some(Task::ready(Ok(())))
4585 })
4586 }
4587 CodeActionsItem::CodeAction(action) => {
4588 let apply_code_actions = workspace
4589 .read(cx)
4590 .project()
4591 .clone()
4592 .update(cx, |project, cx| {
4593 project.apply_code_action(buffer, action, true, cx)
4594 });
4595 let workspace = workspace.downgrade();
4596 Some(cx.spawn(|editor, cx| async move {
4597 let project_transaction = apply_code_actions.await?;
4598 Self::open_project_transaction(
4599 &editor,
4600 workspace,
4601 project_transaction,
4602 title,
4603 cx,
4604 )
4605 .await
4606 }))
4607 }
4608 }
4609 }
4610
4611 pub async fn open_project_transaction(
4612 this: &WeakView<Editor>,
4613 workspace: WeakView<Workspace>,
4614 transaction: ProjectTransaction,
4615 title: String,
4616 mut cx: AsyncWindowContext,
4617 ) -> Result<()> {
4618 let replica_id = this.update(&mut cx, |this, cx| this.replica_id(cx))?;
4619
4620 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4621 cx.update(|cx| {
4622 entries.sort_unstable_by_key(|(buffer, _)| {
4623 buffer.read(cx).file().map(|f| f.path().clone())
4624 });
4625 })?;
4626
4627 // If the project transaction's edits are all contained within this editor, then
4628 // avoid opening a new editor to display them.
4629
4630 if let Some((buffer, transaction)) = entries.first() {
4631 if entries.len() == 1 {
4632 let excerpt = this.update(&mut cx, |editor, cx| {
4633 editor
4634 .buffer()
4635 .read(cx)
4636 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4637 })?;
4638 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4639 if excerpted_buffer == *buffer {
4640 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4641 let excerpt_range = excerpt_range.to_offset(buffer);
4642 buffer
4643 .edited_ranges_for_transaction::<usize>(transaction)
4644 .all(|range| {
4645 excerpt_range.start <= range.start
4646 && excerpt_range.end >= range.end
4647 })
4648 })?;
4649
4650 if all_edits_within_excerpt {
4651 return Ok(());
4652 }
4653 }
4654 }
4655 }
4656 } else {
4657 return Ok(());
4658 }
4659
4660 let mut ranges_to_highlight = Vec::new();
4661 let excerpt_buffer = cx.new_model(|cx| {
4662 let mut multibuffer =
4663 MultiBuffer::new(replica_id, Capability::ReadWrite).with_title(title);
4664 for (buffer_handle, transaction) in &entries {
4665 let buffer = buffer_handle.read(cx);
4666 ranges_to_highlight.extend(
4667 multibuffer.push_excerpts_with_context_lines(
4668 buffer_handle.clone(),
4669 buffer
4670 .edited_ranges_for_transaction::<usize>(transaction)
4671 .collect(),
4672 DEFAULT_MULTIBUFFER_CONTEXT,
4673 cx,
4674 ),
4675 );
4676 }
4677 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4678 multibuffer
4679 })?;
4680
4681 workspace.update(&mut cx, |workspace, cx| {
4682 let project = workspace.project().clone();
4683 let editor =
4684 cx.new_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, cx));
4685 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
4686 editor.update(cx, |editor, cx| {
4687 editor.highlight_background::<Self>(
4688 &ranges_to_highlight,
4689 |theme| theme.editor_highlighted_line_background,
4690 cx,
4691 );
4692 });
4693 })?;
4694
4695 Ok(())
4696 }
4697
4698 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4699 let project = self.project.clone()?;
4700 let buffer = self.buffer.read(cx);
4701 let newest_selection = self.selections.newest_anchor().clone();
4702 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4703 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4704 if start_buffer != end_buffer {
4705 return None;
4706 }
4707
4708 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
4709 cx.background_executor()
4710 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4711 .await;
4712
4713 let actions = if let Ok(code_actions) = project.update(&mut cx, |project, cx| {
4714 project.code_actions(&start_buffer, start..end, cx)
4715 }) {
4716 code_actions.await
4717 } else {
4718 Vec::new()
4719 };
4720
4721 this.update(&mut cx, |this, cx| {
4722 this.available_code_actions = if actions.is_empty() {
4723 None
4724 } else {
4725 Some((
4726 Location {
4727 buffer: start_buffer,
4728 range: start..end,
4729 },
4730 actions.into(),
4731 ))
4732 };
4733 cx.notify();
4734 })
4735 .log_err();
4736 }));
4737 None
4738 }
4739
4740 fn start_inline_blame_timer(&mut self, cx: &mut ViewContext<Self>) {
4741 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4742 self.show_git_blame_inline = false;
4743
4744 self.show_git_blame_inline_delay_task = Some(cx.spawn(|this, mut cx| async move {
4745 cx.background_executor().timer(delay).await;
4746
4747 this.update(&mut cx, |this, cx| {
4748 this.show_git_blame_inline = true;
4749 cx.notify();
4750 })
4751 .log_err();
4752 }));
4753 }
4754 }
4755
4756 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4757 if self.pending_rename.is_some() {
4758 return None;
4759 }
4760
4761 let project = self.project.clone()?;
4762 let buffer = self.buffer.read(cx);
4763 let newest_selection = self.selections.newest_anchor().clone();
4764 let cursor_position = newest_selection.head();
4765 let (cursor_buffer, cursor_buffer_position) =
4766 buffer.text_anchor_for_position(cursor_position, cx)?;
4767 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4768 if cursor_buffer != tail_buffer {
4769 return None;
4770 }
4771
4772 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4773 cx.background_executor()
4774 .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT)
4775 .await;
4776
4777 let highlights = if let Some(highlights) = project
4778 .update(&mut cx, |project, cx| {
4779 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4780 })
4781 .log_err()
4782 {
4783 highlights.await.log_err()
4784 } else {
4785 None
4786 };
4787
4788 if let Some(highlights) = highlights {
4789 this.update(&mut cx, |this, cx| {
4790 if this.pending_rename.is_some() {
4791 return;
4792 }
4793
4794 let buffer_id = cursor_position.buffer_id;
4795 let buffer = this.buffer.read(cx);
4796 if !buffer
4797 .text_anchor_for_position(cursor_position, cx)
4798 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4799 {
4800 return;
4801 }
4802
4803 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4804 let mut write_ranges = Vec::new();
4805 let mut read_ranges = Vec::new();
4806 for highlight in highlights {
4807 for (excerpt_id, excerpt_range) in
4808 buffer.excerpts_for_buffer(&cursor_buffer, cx)
4809 {
4810 let start = highlight
4811 .range
4812 .start
4813 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4814 let end = highlight
4815 .range
4816 .end
4817 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4818 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4819 continue;
4820 }
4821
4822 let range = Anchor {
4823 buffer_id,
4824 excerpt_id: excerpt_id,
4825 text_anchor: start,
4826 }..Anchor {
4827 buffer_id,
4828 excerpt_id,
4829 text_anchor: end,
4830 };
4831 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4832 write_ranges.push(range);
4833 } else {
4834 read_ranges.push(range);
4835 }
4836 }
4837 }
4838
4839 this.highlight_background::<DocumentHighlightRead>(
4840 &read_ranges,
4841 |theme| theme.editor_document_highlight_read_background,
4842 cx,
4843 );
4844 this.highlight_background::<DocumentHighlightWrite>(
4845 &write_ranges,
4846 |theme| theme.editor_document_highlight_write_background,
4847 cx,
4848 );
4849 cx.notify();
4850 })
4851 .log_err();
4852 }
4853 }));
4854 None
4855 }
4856
4857 fn refresh_inline_completion(
4858 &mut self,
4859 debounce: bool,
4860 cx: &mut ViewContext<Self>,
4861 ) -> Option<()> {
4862 let provider = self.inline_completion_provider()?;
4863 let cursor = self.selections.newest_anchor().head();
4864 let (buffer, cursor_buffer_position) =
4865 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4866 if !self.show_inline_completions
4867 || !provider.is_enabled(&buffer, cursor_buffer_position, cx)
4868 {
4869 self.discard_inline_completion(false, cx);
4870 return None;
4871 }
4872
4873 self.update_visible_inline_completion(cx);
4874 provider.refresh(buffer, cursor_buffer_position, debounce, cx);
4875 Some(())
4876 }
4877
4878 fn cycle_inline_completion(
4879 &mut self,
4880 direction: Direction,
4881 cx: &mut ViewContext<Self>,
4882 ) -> Option<()> {
4883 let provider = self.inline_completion_provider()?;
4884 let cursor = self.selections.newest_anchor().head();
4885 let (buffer, cursor_buffer_position) =
4886 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4887 if !self.show_inline_completions
4888 || !provider.is_enabled(&buffer, cursor_buffer_position, cx)
4889 {
4890 return None;
4891 }
4892
4893 provider.cycle(buffer, cursor_buffer_position, direction, cx);
4894 self.update_visible_inline_completion(cx);
4895
4896 Some(())
4897 }
4898
4899 pub fn show_inline_completion(&mut self, _: &ShowInlineCompletion, cx: &mut ViewContext<Self>) {
4900 if !self.has_active_inline_completion(cx) {
4901 self.refresh_inline_completion(false, cx);
4902 return;
4903 }
4904
4905 self.update_visible_inline_completion(cx);
4906 }
4907
4908 pub fn display_cursor_names(&mut self, _: &DisplayCursorNames, cx: &mut ViewContext<Self>) {
4909 self.show_cursor_names(cx);
4910 }
4911
4912 fn show_cursor_names(&mut self, cx: &mut ViewContext<Self>) {
4913 self.show_cursor_names = true;
4914 cx.notify();
4915 cx.spawn(|this, mut cx| async move {
4916 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
4917 this.update(&mut cx, |this, cx| {
4918 this.show_cursor_names = false;
4919 cx.notify()
4920 })
4921 .ok()
4922 })
4923 .detach();
4924 }
4925
4926 pub fn next_inline_completion(&mut self, _: &NextInlineCompletion, cx: &mut ViewContext<Self>) {
4927 if self.has_active_inline_completion(cx) {
4928 self.cycle_inline_completion(Direction::Next, cx);
4929 } else {
4930 let is_copilot_disabled = self.refresh_inline_completion(false, cx).is_none();
4931 if is_copilot_disabled {
4932 cx.propagate();
4933 }
4934 }
4935 }
4936
4937 pub fn previous_inline_completion(
4938 &mut self,
4939 _: &PreviousInlineCompletion,
4940 cx: &mut ViewContext<Self>,
4941 ) {
4942 if self.has_active_inline_completion(cx) {
4943 self.cycle_inline_completion(Direction::Prev, cx);
4944 } else {
4945 let is_copilot_disabled = self.refresh_inline_completion(false, cx).is_none();
4946 if is_copilot_disabled {
4947 cx.propagate();
4948 }
4949 }
4950 }
4951
4952 pub fn accept_inline_completion(
4953 &mut self,
4954 _: &AcceptInlineCompletion,
4955 cx: &mut ViewContext<Self>,
4956 ) {
4957 let Some((completion, delete_range)) = self.take_active_inline_completion(cx) else {
4958 return;
4959 };
4960 if let Some(provider) = self.inline_completion_provider() {
4961 provider.accept(cx);
4962 }
4963
4964 cx.emit(EditorEvent::InputHandled {
4965 utf16_range_to_replace: None,
4966 text: completion.text.to_string().into(),
4967 });
4968
4969 if let Some(range) = delete_range {
4970 self.change_selections(None, cx, |s| s.select_ranges([range]))
4971 }
4972 self.insert_with_autoindent_mode(&completion.text.to_string(), None, cx);
4973 self.refresh_inline_completion(true, cx);
4974 cx.notify();
4975 }
4976
4977 pub fn accept_partial_inline_completion(
4978 &mut self,
4979 _: &AcceptPartialInlineCompletion,
4980 cx: &mut ViewContext<Self>,
4981 ) {
4982 if self.selections.count() == 1 && self.has_active_inline_completion(cx) {
4983 if let Some((completion, delete_range)) = self.take_active_inline_completion(cx) {
4984 let mut partial_completion = completion
4985 .text
4986 .chars()
4987 .by_ref()
4988 .take_while(|c| c.is_alphabetic())
4989 .collect::<String>();
4990 if partial_completion.is_empty() {
4991 partial_completion = completion
4992 .text
4993 .chars()
4994 .by_ref()
4995 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
4996 .collect::<String>();
4997 }
4998
4999 cx.emit(EditorEvent::InputHandled {
5000 utf16_range_to_replace: None,
5001 text: partial_completion.clone().into(),
5002 });
5003
5004 if let Some(range) = delete_range {
5005 self.change_selections(None, cx, |s| s.select_ranges([range]))
5006 }
5007 self.insert_with_autoindent_mode(&partial_completion, None, cx);
5008
5009 self.refresh_inline_completion(true, cx);
5010 cx.notify();
5011 }
5012 }
5013 }
5014
5015 fn discard_inline_completion(
5016 &mut self,
5017 should_report_inline_completion_event: bool,
5018 cx: &mut ViewContext<Self>,
5019 ) -> bool {
5020 if let Some(provider) = self.inline_completion_provider() {
5021 provider.discard(should_report_inline_completion_event, cx);
5022 }
5023
5024 self.take_active_inline_completion(cx).is_some()
5025 }
5026
5027 pub fn has_active_inline_completion(&self, cx: &AppContext) -> bool {
5028 if let Some(completion) = self.active_inline_completion.as_ref() {
5029 let buffer = self.buffer.read(cx).read(cx);
5030 completion.0.position.is_valid(&buffer)
5031 } else {
5032 false
5033 }
5034 }
5035
5036 fn take_active_inline_completion(
5037 &mut self,
5038 cx: &mut ViewContext<Self>,
5039 ) -> Option<(Inlay, Option<Range<Anchor>>)> {
5040 let completion = self.active_inline_completion.take()?;
5041 self.display_map.update(cx, |map, cx| {
5042 map.splice_inlays(vec![completion.0.id], Default::default(), cx);
5043 });
5044 let buffer = self.buffer.read(cx).read(cx);
5045
5046 if completion.0.position.is_valid(&buffer) {
5047 Some(completion)
5048 } else {
5049 None
5050 }
5051 }
5052
5053 fn update_visible_inline_completion(&mut self, cx: &mut ViewContext<Self>) {
5054 let selection = self.selections.newest_anchor();
5055 let cursor = selection.head();
5056
5057 let excerpt_id = cursor.excerpt_id;
5058
5059 if self.context_menu.read().is_none()
5060 && self.completion_tasks.is_empty()
5061 && selection.start == selection.end
5062 {
5063 if let Some(provider) = self.inline_completion_provider() {
5064 if let Some((buffer, cursor_buffer_position)) =
5065 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5066 {
5067 if let Some((text, text_anchor_range)) =
5068 provider.active_completion_text(&buffer, cursor_buffer_position, cx)
5069 {
5070 let text = Rope::from(text);
5071 let mut to_remove = Vec::new();
5072 if let Some(completion) = self.active_inline_completion.take() {
5073 to_remove.push(completion.0.id);
5074 }
5075
5076 let completion_inlay =
5077 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
5078
5079 let multibuffer_anchor_range = text_anchor_range.and_then(|range| {
5080 let snapshot = self.buffer.read(cx).snapshot(cx);
5081 Some(
5082 snapshot.anchor_in_excerpt(excerpt_id, range.start)?
5083 ..snapshot.anchor_in_excerpt(excerpt_id, range.end)?,
5084 )
5085 });
5086 self.active_inline_completion =
5087 Some((completion_inlay.clone(), multibuffer_anchor_range));
5088
5089 self.display_map.update(cx, move |map, cx| {
5090 map.splice_inlays(to_remove, vec![completion_inlay], cx)
5091 });
5092 cx.notify();
5093 return;
5094 }
5095 }
5096 }
5097 }
5098
5099 self.discard_inline_completion(false, cx);
5100 }
5101
5102 fn inline_completion_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5103 Some(self.inline_completion_provider.as_ref()?.provider.clone())
5104 }
5105
5106 fn render_code_actions_indicator(
5107 &self,
5108 _style: &EditorStyle,
5109 row: DisplayRow,
5110 is_active: bool,
5111 cx: &mut ViewContext<Self>,
5112 ) -> Option<IconButton> {
5113 if self.available_code_actions.is_some() {
5114 Some(
5115 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5116 .shape(ui::IconButtonShape::Square)
5117 .icon_size(IconSize::XSmall)
5118 .icon_color(Color::Muted)
5119 .selected(is_active)
5120 .on_click(cx.listener(move |editor, _e, cx| {
5121 editor.focus(cx);
5122 editor.toggle_code_actions(
5123 &ToggleCodeActions {
5124 deployed_from_indicator: Some(row),
5125 },
5126 cx,
5127 );
5128 })),
5129 )
5130 } else {
5131 None
5132 }
5133 }
5134
5135 fn clear_tasks(&mut self) {
5136 self.tasks.clear()
5137 }
5138
5139 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5140 if let Some(_) = self.tasks.insert(key, value) {
5141 // This case should hopefully be rare, but just in case...
5142 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5143 }
5144 }
5145
5146 fn render_run_indicator(
5147 &self,
5148 _style: &EditorStyle,
5149 is_active: bool,
5150 row: DisplayRow,
5151 cx: &mut ViewContext<Self>,
5152 ) -> IconButton {
5153 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5154 .shape(ui::IconButtonShape::Square)
5155 .icon_size(IconSize::XSmall)
5156 .icon_color(Color::Muted)
5157 .selected(is_active)
5158 .on_click(cx.listener(move |editor, _e, cx| {
5159 editor.focus(cx);
5160 editor.toggle_code_actions(
5161 &ToggleCodeActions {
5162 deployed_from_indicator: Some(row),
5163 },
5164 cx,
5165 );
5166 }))
5167 }
5168
5169 fn close_hunk_diff_button(
5170 &self,
5171 hunk: HoveredHunk,
5172 row: DisplayRow,
5173 cx: &mut ViewContext<Self>,
5174 ) -> IconButton {
5175 IconButton::new(
5176 ("close_hunk_diff_indicator", row.0 as usize),
5177 ui::IconName::Close,
5178 )
5179 .shape(ui::IconButtonShape::Square)
5180 .icon_size(IconSize::XSmall)
5181 .icon_color(Color::Muted)
5182 .tooltip(|cx| Tooltip::for_action("Close hunk diff", &ToggleHunkDiff, cx))
5183 .on_click(cx.listener(move |editor, _e, cx| editor.toggle_hovered_hunk(&hunk, cx)))
5184 }
5185
5186 pub fn context_menu_visible(&self) -> bool {
5187 self.context_menu
5188 .read()
5189 .as_ref()
5190 .map_or(false, |menu| menu.visible())
5191 }
5192
5193 fn render_context_menu(
5194 &self,
5195 cursor_position: DisplayPoint,
5196 style: &EditorStyle,
5197 max_height: Pixels,
5198 cx: &mut ViewContext<Editor>,
5199 ) -> Option<(ContextMenuOrigin, AnyElement)> {
5200 self.context_menu.read().as_ref().map(|menu| {
5201 menu.render(
5202 cursor_position,
5203 style,
5204 max_height,
5205 self.workspace.as_ref().map(|(w, _)| w.clone()),
5206 cx,
5207 )
5208 })
5209 }
5210
5211 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
5212 cx.notify();
5213 self.completion_tasks.clear();
5214 let context_menu = self.context_menu.write().take();
5215 if context_menu.is_some() {
5216 self.update_visible_inline_completion(cx);
5217 }
5218 context_menu
5219 }
5220
5221 pub fn insert_snippet(
5222 &mut self,
5223 insertion_ranges: &[Range<usize>],
5224 snippet: Snippet,
5225 cx: &mut ViewContext<Self>,
5226 ) -> Result<()> {
5227 struct Tabstop<T> {
5228 is_end_tabstop: bool,
5229 ranges: Vec<Range<T>>,
5230 }
5231
5232 let tabstops = self.buffer.update(cx, |buffer, cx| {
5233 let snippet_text: Arc<str> = snippet.text.clone().into();
5234 buffer.edit(
5235 insertion_ranges
5236 .iter()
5237 .cloned()
5238 .map(|range| (range, snippet_text.clone())),
5239 Some(AutoindentMode::EachLine),
5240 cx,
5241 );
5242
5243 let snapshot = &*buffer.read(cx);
5244 let snippet = &snippet;
5245 snippet
5246 .tabstops
5247 .iter()
5248 .map(|tabstop| {
5249 let is_end_tabstop = tabstop.first().map_or(false, |tabstop| {
5250 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
5251 });
5252 let mut tabstop_ranges = tabstop
5253 .iter()
5254 .flat_map(|tabstop_range| {
5255 let mut delta = 0_isize;
5256 insertion_ranges.iter().map(move |insertion_range| {
5257 let insertion_start = insertion_range.start as isize + delta;
5258 delta +=
5259 snippet.text.len() as isize - insertion_range.len() as isize;
5260
5261 let start = ((insertion_start + tabstop_range.start) as usize)
5262 .min(snapshot.len());
5263 let end = ((insertion_start + tabstop_range.end) as usize)
5264 .min(snapshot.len());
5265 snapshot.anchor_before(start)..snapshot.anchor_after(end)
5266 })
5267 })
5268 .collect::<Vec<_>>();
5269 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
5270
5271 Tabstop {
5272 is_end_tabstop,
5273 ranges: tabstop_ranges,
5274 }
5275 })
5276 .collect::<Vec<_>>()
5277 });
5278 if let Some(tabstop) = tabstops.first() {
5279 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5280 s.select_ranges(tabstop.ranges.iter().cloned());
5281 });
5282
5283 // If we're already at the last tabstop and it's at the end of the snippet,
5284 // we're done, we don't need to keep the state around.
5285 if !tabstop.is_end_tabstop {
5286 let ranges = tabstops
5287 .into_iter()
5288 .map(|tabstop| tabstop.ranges)
5289 .collect::<Vec<_>>();
5290 self.snippet_stack.push(SnippetState {
5291 active_index: 0,
5292 ranges,
5293 });
5294 }
5295
5296 // Check whether the just-entered snippet ends with an auto-closable bracket.
5297 if self.autoclose_regions.is_empty() {
5298 let snapshot = self.buffer.read(cx).snapshot(cx);
5299 for selection in &mut self.selections.all::<Point>(cx) {
5300 let selection_head = selection.head();
5301 let Some(scope) = snapshot.language_scope_at(selection_head) else {
5302 continue;
5303 };
5304
5305 let mut bracket_pair = None;
5306 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
5307 let prev_chars = snapshot
5308 .reversed_chars_at(selection_head)
5309 .collect::<String>();
5310 for (pair, enabled) in scope.brackets() {
5311 if enabled
5312 && pair.close
5313 && prev_chars.starts_with(pair.start.as_str())
5314 && next_chars.starts_with(pair.end.as_str())
5315 {
5316 bracket_pair = Some(pair.clone());
5317 break;
5318 }
5319 }
5320 if let Some(pair) = bracket_pair {
5321 let start = snapshot.anchor_after(selection_head);
5322 let end = snapshot.anchor_after(selection_head);
5323 self.autoclose_regions.push(AutocloseRegion {
5324 selection_id: selection.id,
5325 range: start..end,
5326 pair,
5327 });
5328 }
5329 }
5330 }
5331 }
5332 Ok(())
5333 }
5334
5335 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5336 self.move_to_snippet_tabstop(Bias::Right, cx)
5337 }
5338
5339 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5340 self.move_to_snippet_tabstop(Bias::Left, cx)
5341 }
5342
5343 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
5344 if let Some(mut snippet) = self.snippet_stack.pop() {
5345 match bias {
5346 Bias::Left => {
5347 if snippet.active_index > 0 {
5348 snippet.active_index -= 1;
5349 } else {
5350 self.snippet_stack.push(snippet);
5351 return false;
5352 }
5353 }
5354 Bias::Right => {
5355 if snippet.active_index + 1 < snippet.ranges.len() {
5356 snippet.active_index += 1;
5357 } else {
5358 self.snippet_stack.push(snippet);
5359 return false;
5360 }
5361 }
5362 }
5363 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
5364 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5365 s.select_anchor_ranges(current_ranges.iter().cloned())
5366 });
5367 // If snippet state is not at the last tabstop, push it back on the stack
5368 if snippet.active_index + 1 < snippet.ranges.len() {
5369 self.snippet_stack.push(snippet);
5370 }
5371 return true;
5372 }
5373 }
5374
5375 false
5376 }
5377
5378 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
5379 self.transact(cx, |this, cx| {
5380 this.select_all(&SelectAll, cx);
5381 this.insert("", cx);
5382 });
5383 }
5384
5385 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
5386 self.transact(cx, |this, cx| {
5387 this.select_autoclose_pair(cx);
5388 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
5389 if !this.linked_edit_ranges.is_empty() {
5390 let selections = this.selections.all::<MultiBufferPoint>(cx);
5391 let snapshot = this.buffer.read(cx).snapshot(cx);
5392
5393 for selection in selections.iter() {
5394 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
5395 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
5396 if selection_start.buffer_id != selection_end.buffer_id {
5397 continue;
5398 }
5399 if let Some(ranges) =
5400 this.linked_editing_ranges_for(selection_start..selection_end, cx)
5401 {
5402 for (buffer, entries) in ranges {
5403 linked_ranges.entry(buffer).or_default().extend(entries);
5404 }
5405 }
5406 }
5407 }
5408
5409 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
5410 if !this.selections.line_mode {
5411 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
5412 for selection in &mut selections {
5413 if selection.is_empty() {
5414 let old_head = selection.head();
5415 let mut new_head =
5416 movement::left(&display_map, old_head.to_display_point(&display_map))
5417 .to_point(&display_map);
5418 if let Some((buffer, line_buffer_range)) = display_map
5419 .buffer_snapshot
5420 .buffer_line_for_row(MultiBufferRow(old_head.row))
5421 {
5422 let indent_size =
5423 buffer.indent_size_for_line(line_buffer_range.start.row);
5424 let indent_len = match indent_size.kind {
5425 IndentKind::Space => {
5426 buffer.settings_at(line_buffer_range.start, cx).tab_size
5427 }
5428 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
5429 };
5430 if old_head.column <= indent_size.len && old_head.column > 0 {
5431 let indent_len = indent_len.get();
5432 new_head = cmp::min(
5433 new_head,
5434 MultiBufferPoint::new(
5435 old_head.row,
5436 ((old_head.column - 1) / indent_len) * indent_len,
5437 ),
5438 );
5439 }
5440 }
5441
5442 selection.set_head(new_head, SelectionGoal::None);
5443 }
5444 }
5445 }
5446
5447 this.signature_help_state.set_backspace_pressed(true);
5448 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5449 this.insert("", cx);
5450 let empty_str: Arc<str> = Arc::from("");
5451 for (buffer, edits) in linked_ranges {
5452 let snapshot = buffer.read(cx).snapshot();
5453 use text::ToPoint as TP;
5454
5455 let edits = edits
5456 .into_iter()
5457 .map(|range| {
5458 let end_point = TP::to_point(&range.end, &snapshot);
5459 let mut start_point = TP::to_point(&range.start, &snapshot);
5460
5461 if end_point == start_point {
5462 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
5463 .saturating_sub(1);
5464 start_point = TP::to_point(&offset, &snapshot);
5465 };
5466
5467 (start_point..end_point, empty_str.clone())
5468 })
5469 .sorted_by_key(|(range, _)| range.start)
5470 .collect::<Vec<_>>();
5471 buffer.update(cx, |this, cx| {
5472 this.edit(edits, None, cx);
5473 })
5474 }
5475 this.refresh_inline_completion(true, cx);
5476 linked_editing_ranges::refresh_linked_ranges(this, cx);
5477 });
5478 }
5479
5480 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
5481 self.transact(cx, |this, cx| {
5482 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5483 let line_mode = s.line_mode;
5484 s.move_with(|map, selection| {
5485 if selection.is_empty() && !line_mode {
5486 let cursor = movement::right(map, selection.head());
5487 selection.end = cursor;
5488 selection.reversed = true;
5489 selection.goal = SelectionGoal::None;
5490 }
5491 })
5492 });
5493 this.insert("", cx);
5494 this.refresh_inline_completion(true, cx);
5495 });
5496 }
5497
5498 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
5499 if self.move_to_prev_snippet_tabstop(cx) {
5500 return;
5501 }
5502
5503 self.outdent(&Outdent, cx);
5504 }
5505
5506 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
5507 if self.move_to_next_snippet_tabstop(cx) || self.read_only(cx) {
5508 return;
5509 }
5510
5511 let mut selections = self.selections.all_adjusted(cx);
5512 let buffer = self.buffer.read(cx);
5513 let snapshot = buffer.snapshot(cx);
5514 let rows_iter = selections.iter().map(|s| s.head().row);
5515 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
5516
5517 let mut edits = Vec::new();
5518 let mut prev_edited_row = 0;
5519 let mut row_delta = 0;
5520 for selection in &mut selections {
5521 if selection.start.row != prev_edited_row {
5522 row_delta = 0;
5523 }
5524 prev_edited_row = selection.end.row;
5525
5526 // If the selection is non-empty, then increase the indentation of the selected lines.
5527 if !selection.is_empty() {
5528 row_delta =
5529 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5530 continue;
5531 }
5532
5533 // If the selection is empty and the cursor is in the leading whitespace before the
5534 // suggested indentation, then auto-indent the line.
5535 let cursor = selection.head();
5536 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
5537 if let Some(suggested_indent) =
5538 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
5539 {
5540 if cursor.column < suggested_indent.len
5541 && cursor.column <= current_indent.len
5542 && current_indent.len <= suggested_indent.len
5543 {
5544 selection.start = Point::new(cursor.row, suggested_indent.len);
5545 selection.end = selection.start;
5546 if row_delta == 0 {
5547 edits.extend(Buffer::edit_for_indent_size_adjustment(
5548 cursor.row,
5549 current_indent,
5550 suggested_indent,
5551 ));
5552 row_delta = suggested_indent.len - current_indent.len;
5553 }
5554 continue;
5555 }
5556 }
5557
5558 // Otherwise, insert a hard or soft tab.
5559 let settings = buffer.settings_at(cursor, cx);
5560 let tab_size = if settings.hard_tabs {
5561 IndentSize::tab()
5562 } else {
5563 let tab_size = settings.tab_size.get();
5564 let char_column = snapshot
5565 .text_for_range(Point::new(cursor.row, 0)..cursor)
5566 .flat_map(str::chars)
5567 .count()
5568 + row_delta as usize;
5569 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
5570 IndentSize::spaces(chars_to_next_tab_stop)
5571 };
5572 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
5573 selection.end = selection.start;
5574 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
5575 row_delta += tab_size.len;
5576 }
5577
5578 self.transact(cx, |this, cx| {
5579 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5580 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5581 this.refresh_inline_completion(true, cx);
5582 });
5583 }
5584
5585 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
5586 if self.read_only(cx) {
5587 return;
5588 }
5589 let mut selections = self.selections.all::<Point>(cx);
5590 let mut prev_edited_row = 0;
5591 let mut row_delta = 0;
5592 let mut edits = Vec::new();
5593 let buffer = self.buffer.read(cx);
5594 let snapshot = buffer.snapshot(cx);
5595 for selection in &mut selections {
5596 if selection.start.row != prev_edited_row {
5597 row_delta = 0;
5598 }
5599 prev_edited_row = selection.end.row;
5600
5601 row_delta =
5602 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5603 }
5604
5605 self.transact(cx, |this, cx| {
5606 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5607 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5608 });
5609 }
5610
5611 fn indent_selection(
5612 buffer: &MultiBuffer,
5613 snapshot: &MultiBufferSnapshot,
5614 selection: &mut Selection<Point>,
5615 edits: &mut Vec<(Range<Point>, String)>,
5616 delta_for_start_row: u32,
5617 cx: &AppContext,
5618 ) -> u32 {
5619 let settings = buffer.settings_at(selection.start, cx);
5620 let tab_size = settings.tab_size.get();
5621 let indent_kind = if settings.hard_tabs {
5622 IndentKind::Tab
5623 } else {
5624 IndentKind::Space
5625 };
5626 let mut start_row = selection.start.row;
5627 let mut end_row = selection.end.row + 1;
5628
5629 // If a selection ends at the beginning of a line, don't indent
5630 // that last line.
5631 if selection.end.column == 0 && selection.end.row > selection.start.row {
5632 end_row -= 1;
5633 }
5634
5635 // Avoid re-indenting a row that has already been indented by a
5636 // previous selection, but still update this selection's column
5637 // to reflect that indentation.
5638 if delta_for_start_row > 0 {
5639 start_row += 1;
5640 selection.start.column += delta_for_start_row;
5641 if selection.end.row == selection.start.row {
5642 selection.end.column += delta_for_start_row;
5643 }
5644 }
5645
5646 let mut delta_for_end_row = 0;
5647 let has_multiple_rows = start_row + 1 != end_row;
5648 for row in start_row..end_row {
5649 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
5650 let indent_delta = match (current_indent.kind, indent_kind) {
5651 (IndentKind::Space, IndentKind::Space) => {
5652 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
5653 IndentSize::spaces(columns_to_next_tab_stop)
5654 }
5655 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
5656 (_, IndentKind::Tab) => IndentSize::tab(),
5657 };
5658
5659 let start = if has_multiple_rows || current_indent.len < selection.start.column {
5660 0
5661 } else {
5662 selection.start.column
5663 };
5664 let row_start = Point::new(row, start);
5665 edits.push((
5666 row_start..row_start,
5667 indent_delta.chars().collect::<String>(),
5668 ));
5669
5670 // Update this selection's endpoints to reflect the indentation.
5671 if row == selection.start.row {
5672 selection.start.column += indent_delta.len;
5673 }
5674 if row == selection.end.row {
5675 selection.end.column += indent_delta.len;
5676 delta_for_end_row = indent_delta.len;
5677 }
5678 }
5679
5680 if selection.start.row == selection.end.row {
5681 delta_for_start_row + delta_for_end_row
5682 } else {
5683 delta_for_end_row
5684 }
5685 }
5686
5687 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
5688 if self.read_only(cx) {
5689 return;
5690 }
5691 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5692 let selections = self.selections.all::<Point>(cx);
5693 let mut deletion_ranges = Vec::new();
5694 let mut last_outdent = None;
5695 {
5696 let buffer = self.buffer.read(cx);
5697 let snapshot = buffer.snapshot(cx);
5698 for selection in &selections {
5699 let settings = buffer.settings_at(selection.start, cx);
5700 let tab_size = settings.tab_size.get();
5701 let mut rows = selection.spanned_rows(false, &display_map);
5702
5703 // Avoid re-outdenting a row that has already been outdented by a
5704 // previous selection.
5705 if let Some(last_row) = last_outdent {
5706 if last_row == rows.start {
5707 rows.start = rows.start.next_row();
5708 }
5709 }
5710 let has_multiple_rows = rows.len() > 1;
5711 for row in rows.iter_rows() {
5712 let indent_size = snapshot.indent_size_for_line(row);
5713 if indent_size.len > 0 {
5714 let deletion_len = match indent_size.kind {
5715 IndentKind::Space => {
5716 let columns_to_prev_tab_stop = indent_size.len % tab_size;
5717 if columns_to_prev_tab_stop == 0 {
5718 tab_size
5719 } else {
5720 columns_to_prev_tab_stop
5721 }
5722 }
5723 IndentKind::Tab => 1,
5724 };
5725 let start = if has_multiple_rows
5726 || deletion_len > selection.start.column
5727 || indent_size.len < selection.start.column
5728 {
5729 0
5730 } else {
5731 selection.start.column - deletion_len
5732 };
5733 deletion_ranges.push(
5734 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
5735 );
5736 last_outdent = Some(row);
5737 }
5738 }
5739 }
5740 }
5741
5742 self.transact(cx, |this, cx| {
5743 this.buffer.update(cx, |buffer, cx| {
5744 let empty_str: Arc<str> = Arc::default();
5745 buffer.edit(
5746 deletion_ranges
5747 .into_iter()
5748 .map(|range| (range, empty_str.clone())),
5749 None,
5750 cx,
5751 );
5752 });
5753 let selections = this.selections.all::<usize>(cx);
5754 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5755 });
5756 }
5757
5758 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
5759 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5760 let selections = self.selections.all::<Point>(cx);
5761
5762 let mut new_cursors = Vec::new();
5763 let mut edit_ranges = Vec::new();
5764 let mut selections = selections.iter().peekable();
5765 while let Some(selection) = selections.next() {
5766 let mut rows = selection.spanned_rows(false, &display_map);
5767 let goal_display_column = selection.head().to_display_point(&display_map).column();
5768
5769 // Accumulate contiguous regions of rows that we want to delete.
5770 while let Some(next_selection) = selections.peek() {
5771 let next_rows = next_selection.spanned_rows(false, &display_map);
5772 if next_rows.start <= rows.end {
5773 rows.end = next_rows.end;
5774 selections.next().unwrap();
5775 } else {
5776 break;
5777 }
5778 }
5779
5780 let buffer = &display_map.buffer_snapshot;
5781 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
5782 let edit_end;
5783 let cursor_buffer_row;
5784 if buffer.max_point().row >= rows.end.0 {
5785 // If there's a line after the range, delete the \n from the end of the row range
5786 // and position the cursor on the next line.
5787 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
5788 cursor_buffer_row = rows.end;
5789 } else {
5790 // If there isn't a line after the range, delete the \n from the line before the
5791 // start of the row range and position the cursor there.
5792 edit_start = edit_start.saturating_sub(1);
5793 edit_end = buffer.len();
5794 cursor_buffer_row = rows.start.previous_row();
5795 }
5796
5797 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
5798 *cursor.column_mut() =
5799 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
5800
5801 new_cursors.push((
5802 selection.id,
5803 buffer.anchor_after(cursor.to_point(&display_map)),
5804 ));
5805 edit_ranges.push(edit_start..edit_end);
5806 }
5807
5808 self.transact(cx, |this, cx| {
5809 let buffer = this.buffer.update(cx, |buffer, cx| {
5810 let empty_str: Arc<str> = Arc::default();
5811 buffer.edit(
5812 edit_ranges
5813 .into_iter()
5814 .map(|range| (range, empty_str.clone())),
5815 None,
5816 cx,
5817 );
5818 buffer.snapshot(cx)
5819 });
5820 let new_selections = new_cursors
5821 .into_iter()
5822 .map(|(id, cursor)| {
5823 let cursor = cursor.to_point(&buffer);
5824 Selection {
5825 id,
5826 start: cursor,
5827 end: cursor,
5828 reversed: false,
5829 goal: SelectionGoal::None,
5830 }
5831 })
5832 .collect();
5833
5834 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5835 s.select(new_selections);
5836 });
5837 });
5838 }
5839
5840 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
5841 if self.read_only(cx) {
5842 return;
5843 }
5844 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
5845 for selection in self.selections.all::<Point>(cx) {
5846 let start = MultiBufferRow(selection.start.row);
5847 let end = if selection.start.row == selection.end.row {
5848 MultiBufferRow(selection.start.row + 1)
5849 } else {
5850 MultiBufferRow(selection.end.row)
5851 };
5852
5853 if let Some(last_row_range) = row_ranges.last_mut() {
5854 if start <= last_row_range.end {
5855 last_row_range.end = end;
5856 continue;
5857 }
5858 }
5859 row_ranges.push(start..end);
5860 }
5861
5862 let snapshot = self.buffer.read(cx).snapshot(cx);
5863 let mut cursor_positions = Vec::new();
5864 for row_range in &row_ranges {
5865 let anchor = snapshot.anchor_before(Point::new(
5866 row_range.end.previous_row().0,
5867 snapshot.line_len(row_range.end.previous_row()),
5868 ));
5869 cursor_positions.push(anchor..anchor);
5870 }
5871
5872 self.transact(cx, |this, cx| {
5873 for row_range in row_ranges.into_iter().rev() {
5874 for row in row_range.iter_rows().rev() {
5875 let end_of_line = Point::new(row.0, snapshot.line_len(row));
5876 let next_line_row = row.next_row();
5877 let indent = snapshot.indent_size_for_line(next_line_row);
5878 let start_of_next_line = Point::new(next_line_row.0, indent.len);
5879
5880 let replace = if snapshot.line_len(next_line_row) > indent.len {
5881 " "
5882 } else {
5883 ""
5884 };
5885
5886 this.buffer.update(cx, |buffer, cx| {
5887 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
5888 });
5889 }
5890 }
5891
5892 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5893 s.select_anchor_ranges(cursor_positions)
5894 });
5895 });
5896 }
5897
5898 pub fn sort_lines_case_sensitive(
5899 &mut self,
5900 _: &SortLinesCaseSensitive,
5901 cx: &mut ViewContext<Self>,
5902 ) {
5903 self.manipulate_lines(cx, |lines| lines.sort())
5904 }
5905
5906 pub fn sort_lines_case_insensitive(
5907 &mut self,
5908 _: &SortLinesCaseInsensitive,
5909 cx: &mut ViewContext<Self>,
5910 ) {
5911 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
5912 }
5913
5914 pub fn unique_lines_case_insensitive(
5915 &mut self,
5916 _: &UniqueLinesCaseInsensitive,
5917 cx: &mut ViewContext<Self>,
5918 ) {
5919 self.manipulate_lines(cx, |lines| {
5920 let mut seen = HashSet::default();
5921 lines.retain(|line| seen.insert(line.to_lowercase()));
5922 })
5923 }
5924
5925 pub fn unique_lines_case_sensitive(
5926 &mut self,
5927 _: &UniqueLinesCaseSensitive,
5928 cx: &mut ViewContext<Self>,
5929 ) {
5930 self.manipulate_lines(cx, |lines| {
5931 let mut seen = HashSet::default();
5932 lines.retain(|line| seen.insert(*line));
5933 })
5934 }
5935
5936 pub fn revert_selected_hunks(&mut self, _: &RevertSelectedHunks, cx: &mut ViewContext<Self>) {
5937 let revert_changes = self.gather_revert_changes(&self.selections.disjoint_anchors(), cx);
5938 if !revert_changes.is_empty() {
5939 self.transact(cx, |editor, cx| {
5940 editor.revert(revert_changes, cx);
5941 });
5942 }
5943 }
5944
5945 pub fn open_active_item_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
5946 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
5947 let project_path = buffer.read(cx).project_path(cx)?;
5948 let project = self.project.as_ref()?.read(cx);
5949 let entry = project.entry_for_path(&project_path, cx)?;
5950 let abs_path = project.absolute_path(&project_path, cx)?;
5951 let parent = if entry.is_symlink {
5952 abs_path.canonicalize().ok()?
5953 } else {
5954 abs_path
5955 }
5956 .parent()?
5957 .to_path_buf();
5958 Some(parent)
5959 }) {
5960 cx.dispatch_action(OpenTerminal { working_directory }.boxed_clone());
5961 }
5962 }
5963
5964 fn gather_revert_changes(
5965 &mut self,
5966 selections: &[Selection<Anchor>],
5967 cx: &mut ViewContext<'_, Editor>,
5968 ) -> HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>> {
5969 let mut revert_changes = HashMap::default();
5970 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
5971 for hunk in hunks_for_selections(&multi_buffer_snapshot, selections) {
5972 Self::prepare_revert_change(&mut revert_changes, self.buffer(), &hunk, cx);
5973 }
5974 revert_changes
5975 }
5976
5977 pub fn prepare_revert_change(
5978 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
5979 multi_buffer: &Model<MultiBuffer>,
5980 hunk: &DiffHunk<MultiBufferRow>,
5981 cx: &AppContext,
5982 ) -> Option<()> {
5983 let buffer = multi_buffer.read(cx).buffer(hunk.buffer_id)?;
5984 let buffer = buffer.read(cx);
5985 let original_text = buffer.diff_base()?.slice(hunk.diff_base_byte_range.clone());
5986 let buffer_snapshot = buffer.snapshot();
5987 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
5988 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
5989 probe
5990 .0
5991 .start
5992 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
5993 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
5994 }) {
5995 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
5996 Some(())
5997 } else {
5998 None
5999 }
6000 }
6001
6002 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
6003 self.manipulate_lines(cx, |lines| lines.reverse())
6004 }
6005
6006 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
6007 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
6008 }
6009
6010 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6011 where
6012 Fn: FnMut(&mut Vec<&str>),
6013 {
6014 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6015 let buffer = self.buffer.read(cx).snapshot(cx);
6016
6017 let mut edits = Vec::new();
6018
6019 let selections = self.selections.all::<Point>(cx);
6020 let mut selections = selections.iter().peekable();
6021 let mut contiguous_row_selections = Vec::new();
6022 let mut new_selections = Vec::new();
6023 let mut added_lines = 0;
6024 let mut removed_lines = 0;
6025
6026 while let Some(selection) = selections.next() {
6027 let (start_row, end_row) = consume_contiguous_rows(
6028 &mut contiguous_row_selections,
6029 selection,
6030 &display_map,
6031 &mut selections,
6032 );
6033
6034 let start_point = Point::new(start_row.0, 0);
6035 let end_point = Point::new(
6036 end_row.previous_row().0,
6037 buffer.line_len(end_row.previous_row()),
6038 );
6039 let text = buffer
6040 .text_for_range(start_point..end_point)
6041 .collect::<String>();
6042
6043 let mut lines = text.split('\n').collect_vec();
6044
6045 let lines_before = lines.len();
6046 callback(&mut lines);
6047 let lines_after = lines.len();
6048
6049 edits.push((start_point..end_point, lines.join("\n")));
6050
6051 // Selections must change based on added and removed line count
6052 let start_row =
6053 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
6054 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
6055 new_selections.push(Selection {
6056 id: selection.id,
6057 start: start_row,
6058 end: end_row,
6059 goal: SelectionGoal::None,
6060 reversed: selection.reversed,
6061 });
6062
6063 if lines_after > lines_before {
6064 added_lines += lines_after - lines_before;
6065 } else if lines_before > lines_after {
6066 removed_lines += lines_before - lines_after;
6067 }
6068 }
6069
6070 self.transact(cx, |this, cx| {
6071 let buffer = this.buffer.update(cx, |buffer, cx| {
6072 buffer.edit(edits, None, cx);
6073 buffer.snapshot(cx)
6074 });
6075
6076 // Recalculate offsets on newly edited buffer
6077 let new_selections = new_selections
6078 .iter()
6079 .map(|s| {
6080 let start_point = Point::new(s.start.0, 0);
6081 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
6082 Selection {
6083 id: s.id,
6084 start: buffer.point_to_offset(start_point),
6085 end: buffer.point_to_offset(end_point),
6086 goal: s.goal,
6087 reversed: s.reversed,
6088 }
6089 })
6090 .collect();
6091
6092 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6093 s.select(new_selections);
6094 });
6095
6096 this.request_autoscroll(Autoscroll::fit(), cx);
6097 });
6098 }
6099
6100 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
6101 self.manipulate_text(cx, |text| text.to_uppercase())
6102 }
6103
6104 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
6105 self.manipulate_text(cx, |text| text.to_lowercase())
6106 }
6107
6108 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
6109 self.manipulate_text(cx, |text| {
6110 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6111 // https://github.com/rutrum/convert-case/issues/16
6112 text.split('\n')
6113 .map(|line| line.to_case(Case::Title))
6114 .join("\n")
6115 })
6116 }
6117
6118 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
6119 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
6120 }
6121
6122 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
6123 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
6124 }
6125
6126 pub fn convert_to_upper_camel_case(
6127 &mut self,
6128 _: &ConvertToUpperCamelCase,
6129 cx: &mut ViewContext<Self>,
6130 ) {
6131 self.manipulate_text(cx, |text| {
6132 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6133 // https://github.com/rutrum/convert-case/issues/16
6134 text.split('\n')
6135 .map(|line| line.to_case(Case::UpperCamel))
6136 .join("\n")
6137 })
6138 }
6139
6140 pub fn convert_to_lower_camel_case(
6141 &mut self,
6142 _: &ConvertToLowerCamelCase,
6143 cx: &mut ViewContext<Self>,
6144 ) {
6145 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
6146 }
6147
6148 pub fn convert_to_opposite_case(
6149 &mut self,
6150 _: &ConvertToOppositeCase,
6151 cx: &mut ViewContext<Self>,
6152 ) {
6153 self.manipulate_text(cx, |text| {
6154 text.chars()
6155 .fold(String::with_capacity(text.len()), |mut t, c| {
6156 if c.is_uppercase() {
6157 t.extend(c.to_lowercase());
6158 } else {
6159 t.extend(c.to_uppercase());
6160 }
6161 t
6162 })
6163 })
6164 }
6165
6166 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6167 where
6168 Fn: FnMut(&str) -> String,
6169 {
6170 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6171 let buffer = self.buffer.read(cx).snapshot(cx);
6172
6173 let mut new_selections = Vec::new();
6174 let mut edits = Vec::new();
6175 let mut selection_adjustment = 0i32;
6176
6177 for selection in self.selections.all::<usize>(cx) {
6178 let selection_is_empty = selection.is_empty();
6179
6180 let (start, end) = if selection_is_empty {
6181 let word_range = movement::surrounding_word(
6182 &display_map,
6183 selection.start.to_display_point(&display_map),
6184 );
6185 let start = word_range.start.to_offset(&display_map, Bias::Left);
6186 let end = word_range.end.to_offset(&display_map, Bias::Left);
6187 (start, end)
6188 } else {
6189 (selection.start, selection.end)
6190 };
6191
6192 let text = buffer.text_for_range(start..end).collect::<String>();
6193 let old_length = text.len() as i32;
6194 let text = callback(&text);
6195
6196 new_selections.push(Selection {
6197 start: (start as i32 - selection_adjustment) as usize,
6198 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
6199 goal: SelectionGoal::None,
6200 ..selection
6201 });
6202
6203 selection_adjustment += old_length - text.len() as i32;
6204
6205 edits.push((start..end, text));
6206 }
6207
6208 self.transact(cx, |this, cx| {
6209 this.buffer.update(cx, |buffer, cx| {
6210 buffer.edit(edits, None, cx);
6211 });
6212
6213 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6214 s.select(new_selections);
6215 });
6216
6217 this.request_autoscroll(Autoscroll::fit(), cx);
6218 });
6219 }
6220
6221 pub fn duplicate_line(&mut self, upwards: bool, cx: &mut ViewContext<Self>) {
6222 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6223 let buffer = &display_map.buffer_snapshot;
6224 let selections = self.selections.all::<Point>(cx);
6225
6226 let mut edits = Vec::new();
6227 let mut selections_iter = selections.iter().peekable();
6228 while let Some(selection) = selections_iter.next() {
6229 // Avoid duplicating the same lines twice.
6230 let mut rows = selection.spanned_rows(false, &display_map);
6231
6232 while let Some(next_selection) = selections_iter.peek() {
6233 let next_rows = next_selection.spanned_rows(false, &display_map);
6234 if next_rows.start < rows.end {
6235 rows.end = next_rows.end;
6236 selections_iter.next().unwrap();
6237 } else {
6238 break;
6239 }
6240 }
6241
6242 // Copy the text from the selected row region and splice it either at the start
6243 // or end of the region.
6244 let start = Point::new(rows.start.0, 0);
6245 let end = Point::new(
6246 rows.end.previous_row().0,
6247 buffer.line_len(rows.end.previous_row()),
6248 );
6249 let text = buffer
6250 .text_for_range(start..end)
6251 .chain(Some("\n"))
6252 .collect::<String>();
6253 let insert_location = if upwards {
6254 Point::new(rows.end.0, 0)
6255 } else {
6256 start
6257 };
6258 edits.push((insert_location..insert_location, text));
6259 }
6260
6261 self.transact(cx, |this, cx| {
6262 this.buffer.update(cx, |buffer, cx| {
6263 buffer.edit(edits, None, cx);
6264 });
6265
6266 this.request_autoscroll(Autoscroll::fit(), cx);
6267 });
6268 }
6269
6270 pub fn duplicate_line_up(&mut self, _: &DuplicateLineUp, cx: &mut ViewContext<Self>) {
6271 self.duplicate_line(true, cx);
6272 }
6273
6274 pub fn duplicate_line_down(&mut self, _: &DuplicateLineDown, cx: &mut ViewContext<Self>) {
6275 self.duplicate_line(false, cx);
6276 }
6277
6278 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
6279 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6280 let buffer = self.buffer.read(cx).snapshot(cx);
6281
6282 let mut edits = Vec::new();
6283 let mut unfold_ranges = Vec::new();
6284 let mut refold_ranges = Vec::new();
6285
6286 let selections = self.selections.all::<Point>(cx);
6287 let mut selections = selections.iter().peekable();
6288 let mut contiguous_row_selections = Vec::new();
6289 let mut new_selections = Vec::new();
6290
6291 while let Some(selection) = selections.next() {
6292 // Find all the selections that span a contiguous row range
6293 let (start_row, end_row) = consume_contiguous_rows(
6294 &mut contiguous_row_selections,
6295 selection,
6296 &display_map,
6297 &mut selections,
6298 );
6299
6300 // Move the text spanned by the row range to be before the line preceding the row range
6301 if start_row.0 > 0 {
6302 let range_to_move = Point::new(
6303 start_row.previous_row().0,
6304 buffer.line_len(start_row.previous_row()),
6305 )
6306 ..Point::new(
6307 end_row.previous_row().0,
6308 buffer.line_len(end_row.previous_row()),
6309 );
6310 let insertion_point = display_map
6311 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
6312 .0;
6313
6314 // Don't move lines across excerpts
6315 if buffer
6316 .excerpt_boundaries_in_range((
6317 Bound::Excluded(insertion_point),
6318 Bound::Included(range_to_move.end),
6319 ))
6320 .next()
6321 .is_none()
6322 {
6323 let text = buffer
6324 .text_for_range(range_to_move.clone())
6325 .flat_map(|s| s.chars())
6326 .skip(1)
6327 .chain(['\n'])
6328 .collect::<String>();
6329
6330 edits.push((
6331 buffer.anchor_after(range_to_move.start)
6332 ..buffer.anchor_before(range_to_move.end),
6333 String::new(),
6334 ));
6335 let insertion_anchor = buffer.anchor_after(insertion_point);
6336 edits.push((insertion_anchor..insertion_anchor, text));
6337
6338 let row_delta = range_to_move.start.row - insertion_point.row + 1;
6339
6340 // Move selections up
6341 new_selections.extend(contiguous_row_selections.drain(..).map(
6342 |mut selection| {
6343 selection.start.row -= row_delta;
6344 selection.end.row -= row_delta;
6345 selection
6346 },
6347 ));
6348
6349 // Move folds up
6350 unfold_ranges.push(range_to_move.clone());
6351 for fold in display_map.folds_in_range(
6352 buffer.anchor_before(range_to_move.start)
6353 ..buffer.anchor_after(range_to_move.end),
6354 ) {
6355 let mut start = fold.range.start.to_point(&buffer);
6356 let mut end = fold.range.end.to_point(&buffer);
6357 start.row -= row_delta;
6358 end.row -= row_delta;
6359 refold_ranges.push((start..end, fold.placeholder.clone()));
6360 }
6361 }
6362 }
6363
6364 // If we didn't move line(s), preserve the existing selections
6365 new_selections.append(&mut contiguous_row_selections);
6366 }
6367
6368 self.transact(cx, |this, cx| {
6369 this.unfold_ranges(unfold_ranges, true, true, cx);
6370 this.buffer.update(cx, |buffer, cx| {
6371 for (range, text) in edits {
6372 buffer.edit([(range, text)], None, cx);
6373 }
6374 });
6375 this.fold_ranges(refold_ranges, true, cx);
6376 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6377 s.select(new_selections);
6378 })
6379 });
6380 }
6381
6382 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
6383 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6384 let buffer = self.buffer.read(cx).snapshot(cx);
6385
6386 let mut edits = Vec::new();
6387 let mut unfold_ranges = Vec::new();
6388 let mut refold_ranges = Vec::new();
6389
6390 let selections = self.selections.all::<Point>(cx);
6391 let mut selections = selections.iter().peekable();
6392 let mut contiguous_row_selections = Vec::new();
6393 let mut new_selections = Vec::new();
6394
6395 while let Some(selection) = selections.next() {
6396 // Find all the selections that span a contiguous row range
6397 let (start_row, end_row) = consume_contiguous_rows(
6398 &mut contiguous_row_selections,
6399 selection,
6400 &display_map,
6401 &mut selections,
6402 );
6403
6404 // Move the text spanned by the row range to be after the last line of the row range
6405 if end_row.0 <= buffer.max_point().row {
6406 let range_to_move =
6407 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
6408 let insertion_point = display_map
6409 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
6410 .0;
6411
6412 // Don't move lines across excerpt boundaries
6413 if buffer
6414 .excerpt_boundaries_in_range((
6415 Bound::Excluded(range_to_move.start),
6416 Bound::Included(insertion_point),
6417 ))
6418 .next()
6419 .is_none()
6420 {
6421 let mut text = String::from("\n");
6422 text.extend(buffer.text_for_range(range_to_move.clone()));
6423 text.pop(); // Drop trailing newline
6424 edits.push((
6425 buffer.anchor_after(range_to_move.start)
6426 ..buffer.anchor_before(range_to_move.end),
6427 String::new(),
6428 ));
6429 let insertion_anchor = buffer.anchor_after(insertion_point);
6430 edits.push((insertion_anchor..insertion_anchor, text));
6431
6432 let row_delta = insertion_point.row - range_to_move.end.row + 1;
6433
6434 // Move selections down
6435 new_selections.extend(contiguous_row_selections.drain(..).map(
6436 |mut selection| {
6437 selection.start.row += row_delta;
6438 selection.end.row += row_delta;
6439 selection
6440 },
6441 ));
6442
6443 // Move folds down
6444 unfold_ranges.push(range_to_move.clone());
6445 for fold in display_map.folds_in_range(
6446 buffer.anchor_before(range_to_move.start)
6447 ..buffer.anchor_after(range_to_move.end),
6448 ) {
6449 let mut start = fold.range.start.to_point(&buffer);
6450 let mut end = fold.range.end.to_point(&buffer);
6451 start.row += row_delta;
6452 end.row += row_delta;
6453 refold_ranges.push((start..end, fold.placeholder.clone()));
6454 }
6455 }
6456 }
6457
6458 // If we didn't move line(s), preserve the existing selections
6459 new_selections.append(&mut contiguous_row_selections);
6460 }
6461
6462 self.transact(cx, |this, cx| {
6463 this.unfold_ranges(unfold_ranges, true, true, cx);
6464 this.buffer.update(cx, |buffer, cx| {
6465 for (range, text) in edits {
6466 buffer.edit([(range, text)], None, cx);
6467 }
6468 });
6469 this.fold_ranges(refold_ranges, true, cx);
6470 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
6471 });
6472 }
6473
6474 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
6475 let text_layout_details = &self.text_layout_details(cx);
6476 self.transact(cx, |this, cx| {
6477 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6478 let mut edits: Vec<(Range<usize>, String)> = Default::default();
6479 let line_mode = s.line_mode;
6480 s.move_with(|display_map, selection| {
6481 if !selection.is_empty() || line_mode {
6482 return;
6483 }
6484
6485 let mut head = selection.head();
6486 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
6487 if head.column() == display_map.line_len(head.row()) {
6488 transpose_offset = display_map
6489 .buffer_snapshot
6490 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6491 }
6492
6493 if transpose_offset == 0 {
6494 return;
6495 }
6496
6497 *head.column_mut() += 1;
6498 head = display_map.clip_point(head, Bias::Right);
6499 let goal = SelectionGoal::HorizontalPosition(
6500 display_map
6501 .x_for_display_point(head, &text_layout_details)
6502 .into(),
6503 );
6504 selection.collapse_to(head, goal);
6505
6506 let transpose_start = display_map
6507 .buffer_snapshot
6508 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6509 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
6510 let transpose_end = display_map
6511 .buffer_snapshot
6512 .clip_offset(transpose_offset + 1, Bias::Right);
6513 if let Some(ch) =
6514 display_map.buffer_snapshot.chars_at(transpose_start).next()
6515 {
6516 edits.push((transpose_start..transpose_offset, String::new()));
6517 edits.push((transpose_end..transpose_end, ch.to_string()));
6518 }
6519 }
6520 });
6521 edits
6522 });
6523 this.buffer
6524 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
6525 let selections = this.selections.all::<usize>(cx);
6526 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6527 s.select(selections);
6528 });
6529 });
6530 }
6531
6532 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
6533 let mut text = String::new();
6534 let buffer = self.buffer.read(cx).snapshot(cx);
6535 let mut selections = self.selections.all::<Point>(cx);
6536 let mut clipboard_selections = Vec::with_capacity(selections.len());
6537 {
6538 let max_point = buffer.max_point();
6539 let mut is_first = true;
6540 for selection in &mut selections {
6541 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6542 if is_entire_line {
6543 selection.start = Point::new(selection.start.row, 0);
6544 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
6545 selection.goal = SelectionGoal::None;
6546 }
6547 if is_first {
6548 is_first = false;
6549 } else {
6550 text += "\n";
6551 }
6552 let mut len = 0;
6553 for chunk in buffer.text_for_range(selection.start..selection.end) {
6554 text.push_str(chunk);
6555 len += chunk.len();
6556 }
6557 clipboard_selections.push(ClipboardSelection {
6558 len,
6559 is_entire_line,
6560 first_line_indent: buffer
6561 .indent_size_for_line(MultiBufferRow(selection.start.row))
6562 .len,
6563 });
6564 }
6565 }
6566
6567 self.transact(cx, |this, cx| {
6568 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6569 s.select(selections);
6570 });
6571 this.insert("", cx);
6572 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
6573 });
6574 }
6575
6576 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
6577 let selections = self.selections.all::<Point>(cx);
6578 let buffer = self.buffer.read(cx).read(cx);
6579 let mut text = String::new();
6580
6581 let mut clipboard_selections = Vec::with_capacity(selections.len());
6582 {
6583 let max_point = buffer.max_point();
6584 let mut is_first = true;
6585 for selection in selections.iter() {
6586 let mut start = selection.start;
6587 let mut end = selection.end;
6588 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6589 if is_entire_line {
6590 start = Point::new(start.row, 0);
6591 end = cmp::min(max_point, Point::new(end.row + 1, 0));
6592 }
6593 if is_first {
6594 is_first = false;
6595 } else {
6596 text += "\n";
6597 }
6598 let mut len = 0;
6599 for chunk in buffer.text_for_range(start..end) {
6600 text.push_str(chunk);
6601 len += chunk.len();
6602 }
6603 clipboard_selections.push(ClipboardSelection {
6604 len,
6605 is_entire_line,
6606 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
6607 });
6608 }
6609 }
6610
6611 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
6612 }
6613
6614 pub fn do_paste(
6615 &mut self,
6616 text: &String,
6617 clipboard_selections: Option<Vec<ClipboardSelection>>,
6618 handle_entire_lines: bool,
6619 cx: &mut ViewContext<Self>,
6620 ) {
6621 if self.read_only(cx) {
6622 return;
6623 }
6624
6625 let clipboard_text = Cow::Borrowed(text);
6626
6627 self.transact(cx, |this, cx| {
6628 if let Some(mut clipboard_selections) = clipboard_selections {
6629 let old_selections = this.selections.all::<usize>(cx);
6630 let all_selections_were_entire_line =
6631 clipboard_selections.iter().all(|s| s.is_entire_line);
6632 let first_selection_indent_column =
6633 clipboard_selections.first().map(|s| s.first_line_indent);
6634 if clipboard_selections.len() != old_selections.len() {
6635 clipboard_selections.drain(..);
6636 }
6637
6638 this.buffer.update(cx, |buffer, cx| {
6639 let snapshot = buffer.read(cx);
6640 let mut start_offset = 0;
6641 let mut edits = Vec::new();
6642 let mut original_indent_columns = Vec::new();
6643 for (ix, selection) in old_selections.iter().enumerate() {
6644 let to_insert;
6645 let entire_line;
6646 let original_indent_column;
6647 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
6648 let end_offset = start_offset + clipboard_selection.len;
6649 to_insert = &clipboard_text[start_offset..end_offset];
6650 entire_line = clipboard_selection.is_entire_line;
6651 start_offset = end_offset + 1;
6652 original_indent_column = Some(clipboard_selection.first_line_indent);
6653 } else {
6654 to_insert = clipboard_text.as_str();
6655 entire_line = all_selections_were_entire_line;
6656 original_indent_column = first_selection_indent_column
6657 }
6658
6659 // If the corresponding selection was empty when this slice of the
6660 // clipboard text was written, then the entire line containing the
6661 // selection was copied. If this selection is also currently empty,
6662 // then paste the line before the current line of the buffer.
6663 let range = if selection.is_empty() && handle_entire_lines && entire_line {
6664 let column = selection.start.to_point(&snapshot).column as usize;
6665 let line_start = selection.start - column;
6666 line_start..line_start
6667 } else {
6668 selection.range()
6669 };
6670
6671 edits.push((range, to_insert));
6672 original_indent_columns.extend(original_indent_column);
6673 }
6674 drop(snapshot);
6675
6676 buffer.edit(
6677 edits,
6678 Some(AutoindentMode::Block {
6679 original_indent_columns,
6680 }),
6681 cx,
6682 );
6683 });
6684
6685 let selections = this.selections.all::<usize>(cx);
6686 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6687 } else {
6688 this.insert(&clipboard_text, cx);
6689 }
6690 });
6691 }
6692
6693 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
6694 if let Some(item) = cx.read_from_clipboard() {
6695 self.do_paste(
6696 item.text(),
6697 item.metadata::<Vec<ClipboardSelection>>(),
6698 true,
6699 cx,
6700 )
6701 };
6702 }
6703
6704 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
6705 if self.read_only(cx) {
6706 return;
6707 }
6708
6709 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
6710 if let Some((selections, _)) =
6711 self.selection_history.transaction(transaction_id).cloned()
6712 {
6713 self.change_selections(None, cx, |s| {
6714 s.select_anchors(selections.to_vec());
6715 });
6716 }
6717 self.request_autoscroll(Autoscroll::fit(), cx);
6718 self.unmark_text(cx);
6719 self.refresh_inline_completion(true, cx);
6720 cx.emit(EditorEvent::Edited { transaction_id });
6721 cx.emit(EditorEvent::TransactionUndone { transaction_id });
6722 }
6723 }
6724
6725 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
6726 if self.read_only(cx) {
6727 return;
6728 }
6729
6730 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
6731 if let Some((_, Some(selections))) =
6732 self.selection_history.transaction(transaction_id).cloned()
6733 {
6734 self.change_selections(None, cx, |s| {
6735 s.select_anchors(selections.to_vec());
6736 });
6737 }
6738 self.request_autoscroll(Autoscroll::fit(), cx);
6739 self.unmark_text(cx);
6740 self.refresh_inline_completion(true, cx);
6741 cx.emit(EditorEvent::Edited { transaction_id });
6742 }
6743 }
6744
6745 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
6746 self.buffer
6747 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
6748 }
6749
6750 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut ViewContext<Self>) {
6751 self.buffer
6752 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
6753 }
6754
6755 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
6756 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6757 let line_mode = s.line_mode;
6758 s.move_with(|map, selection| {
6759 let cursor = if selection.is_empty() && !line_mode {
6760 movement::left(map, selection.start)
6761 } else {
6762 selection.start
6763 };
6764 selection.collapse_to(cursor, SelectionGoal::None);
6765 });
6766 })
6767 }
6768
6769 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
6770 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6771 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
6772 })
6773 }
6774
6775 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
6776 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6777 let line_mode = s.line_mode;
6778 s.move_with(|map, selection| {
6779 let cursor = if selection.is_empty() && !line_mode {
6780 movement::right(map, selection.end)
6781 } else {
6782 selection.end
6783 };
6784 selection.collapse_to(cursor, SelectionGoal::None)
6785 });
6786 })
6787 }
6788
6789 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
6790 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6791 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
6792 })
6793 }
6794
6795 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
6796 if self.take_rename(true, cx).is_some() {
6797 return;
6798 }
6799
6800 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6801 cx.propagate();
6802 return;
6803 }
6804
6805 let text_layout_details = &self.text_layout_details(cx);
6806 let selection_count = self.selections.count();
6807 let first_selection = self.selections.first_anchor();
6808
6809 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6810 let line_mode = s.line_mode;
6811 s.move_with(|map, selection| {
6812 if !selection.is_empty() && !line_mode {
6813 selection.goal = SelectionGoal::None;
6814 }
6815 let (cursor, goal) = movement::up(
6816 map,
6817 selection.start,
6818 selection.goal,
6819 false,
6820 &text_layout_details,
6821 );
6822 selection.collapse_to(cursor, goal);
6823 });
6824 });
6825
6826 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
6827 {
6828 cx.propagate();
6829 }
6830 }
6831
6832 pub fn move_up_by_lines(&mut self, action: &MoveUpByLines, cx: &mut ViewContext<Self>) {
6833 if self.take_rename(true, cx).is_some() {
6834 return;
6835 }
6836
6837 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6838 cx.propagate();
6839 return;
6840 }
6841
6842 let text_layout_details = &self.text_layout_details(cx);
6843
6844 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6845 let line_mode = s.line_mode;
6846 s.move_with(|map, selection| {
6847 if !selection.is_empty() && !line_mode {
6848 selection.goal = SelectionGoal::None;
6849 }
6850 let (cursor, goal) = movement::up_by_rows(
6851 map,
6852 selection.start,
6853 action.lines,
6854 selection.goal,
6855 false,
6856 &text_layout_details,
6857 );
6858 selection.collapse_to(cursor, goal);
6859 });
6860 })
6861 }
6862
6863 pub fn move_down_by_lines(&mut self, action: &MoveDownByLines, cx: &mut ViewContext<Self>) {
6864 if self.take_rename(true, cx).is_some() {
6865 return;
6866 }
6867
6868 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6869 cx.propagate();
6870 return;
6871 }
6872
6873 let text_layout_details = &self.text_layout_details(cx);
6874
6875 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6876 let line_mode = s.line_mode;
6877 s.move_with(|map, selection| {
6878 if !selection.is_empty() && !line_mode {
6879 selection.goal = SelectionGoal::None;
6880 }
6881 let (cursor, goal) = movement::down_by_rows(
6882 map,
6883 selection.start,
6884 action.lines,
6885 selection.goal,
6886 false,
6887 &text_layout_details,
6888 );
6889 selection.collapse_to(cursor, goal);
6890 });
6891 })
6892 }
6893
6894 pub fn select_down_by_lines(&mut self, action: &SelectDownByLines, cx: &mut ViewContext<Self>) {
6895 let text_layout_details = &self.text_layout_details(cx);
6896 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6897 s.move_heads_with(|map, head, goal| {
6898 movement::down_by_rows(map, head, action.lines, goal, false, &text_layout_details)
6899 })
6900 })
6901 }
6902
6903 pub fn select_up_by_lines(&mut self, action: &SelectUpByLines, cx: &mut ViewContext<Self>) {
6904 let text_layout_details = &self.text_layout_details(cx);
6905 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6906 s.move_heads_with(|map, head, goal| {
6907 movement::up_by_rows(map, head, action.lines, goal, false, &text_layout_details)
6908 })
6909 })
6910 }
6911
6912 pub fn select_page_up(&mut self, _: &SelectPageUp, cx: &mut ViewContext<Self>) {
6913 let Some(row_count) = self.visible_row_count() else {
6914 return;
6915 };
6916
6917 let text_layout_details = &self.text_layout_details(cx);
6918
6919 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6920 s.move_heads_with(|map, head, goal| {
6921 movement::up_by_rows(map, head, row_count, goal, false, &text_layout_details)
6922 })
6923 })
6924 }
6925
6926 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
6927 if self.take_rename(true, cx).is_some() {
6928 return;
6929 }
6930
6931 if self
6932 .context_menu
6933 .write()
6934 .as_mut()
6935 .map(|menu| menu.select_first(self.project.as_ref(), cx))
6936 .unwrap_or(false)
6937 {
6938 return;
6939 }
6940
6941 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6942 cx.propagate();
6943 return;
6944 }
6945
6946 let Some(row_count) = self.visible_row_count() else {
6947 return;
6948 };
6949
6950 let autoscroll = if action.center_cursor {
6951 Autoscroll::center()
6952 } else {
6953 Autoscroll::fit()
6954 };
6955
6956 let text_layout_details = &self.text_layout_details(cx);
6957
6958 self.change_selections(Some(autoscroll), cx, |s| {
6959 let line_mode = s.line_mode;
6960 s.move_with(|map, selection| {
6961 if !selection.is_empty() && !line_mode {
6962 selection.goal = SelectionGoal::None;
6963 }
6964 let (cursor, goal) = movement::up_by_rows(
6965 map,
6966 selection.end,
6967 row_count,
6968 selection.goal,
6969 false,
6970 &text_layout_details,
6971 );
6972 selection.collapse_to(cursor, goal);
6973 });
6974 });
6975 }
6976
6977 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
6978 let text_layout_details = &self.text_layout_details(cx);
6979 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6980 s.move_heads_with(|map, head, goal| {
6981 movement::up(map, head, goal, false, &text_layout_details)
6982 })
6983 })
6984 }
6985
6986 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
6987 self.take_rename(true, cx);
6988
6989 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6990 cx.propagate();
6991 return;
6992 }
6993
6994 let text_layout_details = &self.text_layout_details(cx);
6995 let selection_count = self.selections.count();
6996 let first_selection = self.selections.first_anchor();
6997
6998 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6999 let line_mode = s.line_mode;
7000 s.move_with(|map, selection| {
7001 if !selection.is_empty() && !line_mode {
7002 selection.goal = SelectionGoal::None;
7003 }
7004 let (cursor, goal) = movement::down(
7005 map,
7006 selection.end,
7007 selection.goal,
7008 false,
7009 &text_layout_details,
7010 );
7011 selection.collapse_to(cursor, goal);
7012 });
7013 });
7014
7015 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7016 {
7017 cx.propagate();
7018 }
7019 }
7020
7021 pub fn select_page_down(&mut self, _: &SelectPageDown, cx: &mut ViewContext<Self>) {
7022 let Some(row_count) = self.visible_row_count() else {
7023 return;
7024 };
7025
7026 let text_layout_details = &self.text_layout_details(cx);
7027
7028 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7029 s.move_heads_with(|map, head, goal| {
7030 movement::down_by_rows(map, head, row_count, goal, false, &text_layout_details)
7031 })
7032 })
7033 }
7034
7035 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
7036 if self.take_rename(true, cx).is_some() {
7037 return;
7038 }
7039
7040 if self
7041 .context_menu
7042 .write()
7043 .as_mut()
7044 .map(|menu| menu.select_last(self.project.as_ref(), cx))
7045 .unwrap_or(false)
7046 {
7047 return;
7048 }
7049
7050 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7051 cx.propagate();
7052 return;
7053 }
7054
7055 let Some(row_count) = self.visible_row_count() else {
7056 return;
7057 };
7058
7059 let autoscroll = if action.center_cursor {
7060 Autoscroll::center()
7061 } else {
7062 Autoscroll::fit()
7063 };
7064
7065 let text_layout_details = &self.text_layout_details(cx);
7066 self.change_selections(Some(autoscroll), cx, |s| {
7067 let line_mode = s.line_mode;
7068 s.move_with(|map, selection| {
7069 if !selection.is_empty() && !line_mode {
7070 selection.goal = SelectionGoal::None;
7071 }
7072 let (cursor, goal) = movement::down_by_rows(
7073 map,
7074 selection.end,
7075 row_count,
7076 selection.goal,
7077 false,
7078 &text_layout_details,
7079 );
7080 selection.collapse_to(cursor, goal);
7081 });
7082 });
7083 }
7084
7085 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
7086 let text_layout_details = &self.text_layout_details(cx);
7087 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7088 s.move_heads_with(|map, head, goal| {
7089 movement::down(map, head, goal, false, &text_layout_details)
7090 })
7091 });
7092 }
7093
7094 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
7095 if let Some(context_menu) = self.context_menu.write().as_mut() {
7096 context_menu.select_first(self.project.as_ref(), cx);
7097 }
7098 }
7099
7100 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
7101 if let Some(context_menu) = self.context_menu.write().as_mut() {
7102 context_menu.select_prev(self.project.as_ref(), cx);
7103 }
7104 }
7105
7106 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
7107 if let Some(context_menu) = self.context_menu.write().as_mut() {
7108 context_menu.select_next(self.project.as_ref(), cx);
7109 }
7110 }
7111
7112 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
7113 if let Some(context_menu) = self.context_menu.write().as_mut() {
7114 context_menu.select_last(self.project.as_ref(), cx);
7115 }
7116 }
7117
7118 pub fn move_to_previous_word_start(
7119 &mut self,
7120 _: &MoveToPreviousWordStart,
7121 cx: &mut ViewContext<Self>,
7122 ) {
7123 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7124 s.move_cursors_with(|map, head, _| {
7125 (
7126 movement::previous_word_start(map, head),
7127 SelectionGoal::None,
7128 )
7129 });
7130 })
7131 }
7132
7133 pub fn move_to_previous_subword_start(
7134 &mut self,
7135 _: &MoveToPreviousSubwordStart,
7136 cx: &mut ViewContext<Self>,
7137 ) {
7138 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7139 s.move_cursors_with(|map, head, _| {
7140 (
7141 movement::previous_subword_start(map, head),
7142 SelectionGoal::None,
7143 )
7144 });
7145 })
7146 }
7147
7148 pub fn select_to_previous_word_start(
7149 &mut self,
7150 _: &SelectToPreviousWordStart,
7151 cx: &mut ViewContext<Self>,
7152 ) {
7153 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7154 s.move_heads_with(|map, head, _| {
7155 (
7156 movement::previous_word_start(map, head),
7157 SelectionGoal::None,
7158 )
7159 });
7160 })
7161 }
7162
7163 pub fn select_to_previous_subword_start(
7164 &mut self,
7165 _: &SelectToPreviousSubwordStart,
7166 cx: &mut ViewContext<Self>,
7167 ) {
7168 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7169 s.move_heads_with(|map, head, _| {
7170 (
7171 movement::previous_subword_start(map, head),
7172 SelectionGoal::None,
7173 )
7174 });
7175 })
7176 }
7177
7178 pub fn delete_to_previous_word_start(
7179 &mut self,
7180 _: &DeleteToPreviousWordStart,
7181 cx: &mut ViewContext<Self>,
7182 ) {
7183 self.transact(cx, |this, cx| {
7184 this.select_autoclose_pair(cx);
7185 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7186 let line_mode = s.line_mode;
7187 s.move_with(|map, selection| {
7188 if selection.is_empty() && !line_mode {
7189 let cursor = movement::previous_word_start(map, selection.head());
7190 selection.set_head(cursor, SelectionGoal::None);
7191 }
7192 });
7193 });
7194 this.insert("", cx);
7195 });
7196 }
7197
7198 pub fn delete_to_previous_subword_start(
7199 &mut self,
7200 _: &DeleteToPreviousSubwordStart,
7201 cx: &mut ViewContext<Self>,
7202 ) {
7203 self.transact(cx, |this, cx| {
7204 this.select_autoclose_pair(cx);
7205 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7206 let line_mode = s.line_mode;
7207 s.move_with(|map, selection| {
7208 if selection.is_empty() && !line_mode {
7209 let cursor = movement::previous_subword_start(map, selection.head());
7210 selection.set_head(cursor, SelectionGoal::None);
7211 }
7212 });
7213 });
7214 this.insert("", cx);
7215 });
7216 }
7217
7218 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
7219 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7220 s.move_cursors_with(|map, head, _| {
7221 (movement::next_word_end(map, head), SelectionGoal::None)
7222 });
7223 })
7224 }
7225
7226 pub fn move_to_next_subword_end(
7227 &mut self,
7228 _: &MoveToNextSubwordEnd,
7229 cx: &mut ViewContext<Self>,
7230 ) {
7231 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7232 s.move_cursors_with(|map, head, _| {
7233 (movement::next_subword_end(map, head), SelectionGoal::None)
7234 });
7235 })
7236 }
7237
7238 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
7239 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7240 s.move_heads_with(|map, head, _| {
7241 (movement::next_word_end(map, head), SelectionGoal::None)
7242 });
7243 })
7244 }
7245
7246 pub fn select_to_next_subword_end(
7247 &mut self,
7248 _: &SelectToNextSubwordEnd,
7249 cx: &mut ViewContext<Self>,
7250 ) {
7251 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7252 s.move_heads_with(|map, head, _| {
7253 (movement::next_subword_end(map, head), SelectionGoal::None)
7254 });
7255 })
7256 }
7257
7258 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
7259 self.transact(cx, |this, cx| {
7260 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7261 let line_mode = s.line_mode;
7262 s.move_with(|map, selection| {
7263 if selection.is_empty() && !line_mode {
7264 let cursor = movement::next_word_end(map, selection.head());
7265 selection.set_head(cursor, SelectionGoal::None);
7266 }
7267 });
7268 });
7269 this.insert("", cx);
7270 });
7271 }
7272
7273 pub fn delete_to_next_subword_end(
7274 &mut self,
7275 _: &DeleteToNextSubwordEnd,
7276 cx: &mut ViewContext<Self>,
7277 ) {
7278 self.transact(cx, |this, cx| {
7279 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7280 s.move_with(|map, selection| {
7281 if selection.is_empty() {
7282 let cursor = movement::next_subword_end(map, selection.head());
7283 selection.set_head(cursor, SelectionGoal::None);
7284 }
7285 });
7286 });
7287 this.insert("", cx);
7288 });
7289 }
7290
7291 pub fn move_to_beginning_of_line(
7292 &mut self,
7293 action: &MoveToBeginningOfLine,
7294 cx: &mut ViewContext<Self>,
7295 ) {
7296 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7297 s.move_cursors_with(|map, head, _| {
7298 (
7299 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7300 SelectionGoal::None,
7301 )
7302 });
7303 })
7304 }
7305
7306 pub fn select_to_beginning_of_line(
7307 &mut self,
7308 action: &SelectToBeginningOfLine,
7309 cx: &mut ViewContext<Self>,
7310 ) {
7311 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7312 s.move_heads_with(|map, head, _| {
7313 (
7314 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7315 SelectionGoal::None,
7316 )
7317 });
7318 });
7319 }
7320
7321 pub fn delete_to_beginning_of_line(
7322 &mut self,
7323 _: &DeleteToBeginningOfLine,
7324 cx: &mut ViewContext<Self>,
7325 ) {
7326 self.transact(cx, |this, cx| {
7327 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7328 s.move_with(|_, selection| {
7329 selection.reversed = true;
7330 });
7331 });
7332
7333 this.select_to_beginning_of_line(
7334 &SelectToBeginningOfLine {
7335 stop_at_soft_wraps: false,
7336 },
7337 cx,
7338 );
7339 this.backspace(&Backspace, cx);
7340 });
7341 }
7342
7343 pub fn move_to_end_of_line(&mut self, action: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
7344 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7345 s.move_cursors_with(|map, head, _| {
7346 (
7347 movement::line_end(map, head, action.stop_at_soft_wraps),
7348 SelectionGoal::None,
7349 )
7350 });
7351 })
7352 }
7353
7354 pub fn select_to_end_of_line(
7355 &mut self,
7356 action: &SelectToEndOfLine,
7357 cx: &mut ViewContext<Self>,
7358 ) {
7359 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7360 s.move_heads_with(|map, head, _| {
7361 (
7362 movement::line_end(map, head, action.stop_at_soft_wraps),
7363 SelectionGoal::None,
7364 )
7365 });
7366 })
7367 }
7368
7369 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
7370 self.transact(cx, |this, cx| {
7371 this.select_to_end_of_line(
7372 &SelectToEndOfLine {
7373 stop_at_soft_wraps: false,
7374 },
7375 cx,
7376 );
7377 this.delete(&Delete, cx);
7378 });
7379 }
7380
7381 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
7382 self.transact(cx, |this, cx| {
7383 this.select_to_end_of_line(
7384 &SelectToEndOfLine {
7385 stop_at_soft_wraps: false,
7386 },
7387 cx,
7388 );
7389 this.cut(&Cut, cx);
7390 });
7391 }
7392
7393 pub fn move_to_start_of_paragraph(
7394 &mut self,
7395 _: &MoveToStartOfParagraph,
7396 cx: &mut ViewContext<Self>,
7397 ) {
7398 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7399 cx.propagate();
7400 return;
7401 }
7402
7403 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7404 s.move_with(|map, selection| {
7405 selection.collapse_to(
7406 movement::start_of_paragraph(map, selection.head(), 1),
7407 SelectionGoal::None,
7408 )
7409 });
7410 })
7411 }
7412
7413 pub fn move_to_end_of_paragraph(
7414 &mut self,
7415 _: &MoveToEndOfParagraph,
7416 cx: &mut ViewContext<Self>,
7417 ) {
7418 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7419 cx.propagate();
7420 return;
7421 }
7422
7423 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7424 s.move_with(|map, selection| {
7425 selection.collapse_to(
7426 movement::end_of_paragraph(map, selection.head(), 1),
7427 SelectionGoal::None,
7428 )
7429 });
7430 })
7431 }
7432
7433 pub fn select_to_start_of_paragraph(
7434 &mut self,
7435 _: &SelectToStartOfParagraph,
7436 cx: &mut ViewContext<Self>,
7437 ) {
7438 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7439 cx.propagate();
7440 return;
7441 }
7442
7443 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7444 s.move_heads_with(|map, head, _| {
7445 (
7446 movement::start_of_paragraph(map, head, 1),
7447 SelectionGoal::None,
7448 )
7449 });
7450 })
7451 }
7452
7453 pub fn select_to_end_of_paragraph(
7454 &mut self,
7455 _: &SelectToEndOfParagraph,
7456 cx: &mut ViewContext<Self>,
7457 ) {
7458 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7459 cx.propagate();
7460 return;
7461 }
7462
7463 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7464 s.move_heads_with(|map, head, _| {
7465 (
7466 movement::end_of_paragraph(map, head, 1),
7467 SelectionGoal::None,
7468 )
7469 });
7470 })
7471 }
7472
7473 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
7474 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7475 cx.propagate();
7476 return;
7477 }
7478
7479 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7480 s.select_ranges(vec![0..0]);
7481 });
7482 }
7483
7484 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
7485 let mut selection = self.selections.last::<Point>(cx);
7486 selection.set_head(Point::zero(), SelectionGoal::None);
7487
7488 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7489 s.select(vec![selection]);
7490 });
7491 }
7492
7493 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
7494 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7495 cx.propagate();
7496 return;
7497 }
7498
7499 let cursor = self.buffer.read(cx).read(cx).len();
7500 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7501 s.select_ranges(vec![cursor..cursor])
7502 });
7503 }
7504
7505 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
7506 self.nav_history = nav_history;
7507 }
7508
7509 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
7510 self.nav_history.as_ref()
7511 }
7512
7513 fn push_to_nav_history(
7514 &mut self,
7515 cursor_anchor: Anchor,
7516 new_position: Option<Point>,
7517 cx: &mut ViewContext<Self>,
7518 ) {
7519 if let Some(nav_history) = self.nav_history.as_mut() {
7520 let buffer = self.buffer.read(cx).read(cx);
7521 let cursor_position = cursor_anchor.to_point(&buffer);
7522 let scroll_state = self.scroll_manager.anchor();
7523 let scroll_top_row = scroll_state.top_row(&buffer);
7524 drop(buffer);
7525
7526 if let Some(new_position) = new_position {
7527 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
7528 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
7529 return;
7530 }
7531 }
7532
7533 nav_history.push(
7534 Some(NavigationData {
7535 cursor_anchor,
7536 cursor_position,
7537 scroll_anchor: scroll_state,
7538 scroll_top_row,
7539 }),
7540 cx,
7541 );
7542 }
7543 }
7544
7545 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
7546 let buffer = self.buffer.read(cx).snapshot(cx);
7547 let mut selection = self.selections.first::<usize>(cx);
7548 selection.set_head(buffer.len(), SelectionGoal::None);
7549 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7550 s.select(vec![selection]);
7551 });
7552 }
7553
7554 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
7555 let end = self.buffer.read(cx).read(cx).len();
7556 self.change_selections(None, cx, |s| {
7557 s.select_ranges(vec![0..end]);
7558 });
7559 }
7560
7561 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
7562 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7563 let mut selections = self.selections.all::<Point>(cx);
7564 let max_point = display_map.buffer_snapshot.max_point();
7565 for selection in &mut selections {
7566 let rows = selection.spanned_rows(true, &display_map);
7567 selection.start = Point::new(rows.start.0, 0);
7568 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
7569 selection.reversed = false;
7570 }
7571 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7572 s.select(selections);
7573 });
7574 }
7575
7576 pub fn split_selection_into_lines(
7577 &mut self,
7578 _: &SplitSelectionIntoLines,
7579 cx: &mut ViewContext<Self>,
7580 ) {
7581 let mut to_unfold = Vec::new();
7582 let mut new_selection_ranges = Vec::new();
7583 {
7584 let selections = self.selections.all::<Point>(cx);
7585 let buffer = self.buffer.read(cx).read(cx);
7586 for selection in selections {
7587 for row in selection.start.row..selection.end.row {
7588 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
7589 new_selection_ranges.push(cursor..cursor);
7590 }
7591 new_selection_ranges.push(selection.end..selection.end);
7592 to_unfold.push(selection.start..selection.end);
7593 }
7594 }
7595 self.unfold_ranges(to_unfold, true, true, cx);
7596 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7597 s.select_ranges(new_selection_ranges);
7598 });
7599 }
7600
7601 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
7602 self.add_selection(true, cx);
7603 }
7604
7605 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
7606 self.add_selection(false, cx);
7607 }
7608
7609 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
7610 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7611 let mut selections = self.selections.all::<Point>(cx);
7612 let text_layout_details = self.text_layout_details(cx);
7613 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
7614 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
7615 let range = oldest_selection.display_range(&display_map).sorted();
7616
7617 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
7618 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
7619 let positions = start_x.min(end_x)..start_x.max(end_x);
7620
7621 selections.clear();
7622 let mut stack = Vec::new();
7623 for row in range.start.row().0..=range.end.row().0 {
7624 if let Some(selection) = self.selections.build_columnar_selection(
7625 &display_map,
7626 DisplayRow(row),
7627 &positions,
7628 oldest_selection.reversed,
7629 &text_layout_details,
7630 ) {
7631 stack.push(selection.id);
7632 selections.push(selection);
7633 }
7634 }
7635
7636 if above {
7637 stack.reverse();
7638 }
7639
7640 AddSelectionsState { above, stack }
7641 });
7642
7643 let last_added_selection = *state.stack.last().unwrap();
7644 let mut new_selections = Vec::new();
7645 if above == state.above {
7646 let end_row = if above {
7647 DisplayRow(0)
7648 } else {
7649 display_map.max_point().row()
7650 };
7651
7652 'outer: for selection in selections {
7653 if selection.id == last_added_selection {
7654 let range = selection.display_range(&display_map).sorted();
7655 debug_assert_eq!(range.start.row(), range.end.row());
7656 let mut row = range.start.row();
7657 let positions =
7658 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
7659 px(start)..px(end)
7660 } else {
7661 let start_x =
7662 display_map.x_for_display_point(range.start, &text_layout_details);
7663 let end_x =
7664 display_map.x_for_display_point(range.end, &text_layout_details);
7665 start_x.min(end_x)..start_x.max(end_x)
7666 };
7667
7668 while row != end_row {
7669 if above {
7670 row.0 -= 1;
7671 } else {
7672 row.0 += 1;
7673 }
7674
7675 if let Some(new_selection) = self.selections.build_columnar_selection(
7676 &display_map,
7677 row,
7678 &positions,
7679 selection.reversed,
7680 &text_layout_details,
7681 ) {
7682 state.stack.push(new_selection.id);
7683 if above {
7684 new_selections.push(new_selection);
7685 new_selections.push(selection);
7686 } else {
7687 new_selections.push(selection);
7688 new_selections.push(new_selection);
7689 }
7690
7691 continue 'outer;
7692 }
7693 }
7694 }
7695
7696 new_selections.push(selection);
7697 }
7698 } else {
7699 new_selections = selections;
7700 new_selections.retain(|s| s.id != last_added_selection);
7701 state.stack.pop();
7702 }
7703
7704 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7705 s.select(new_selections);
7706 });
7707 if state.stack.len() > 1 {
7708 self.add_selections_state = Some(state);
7709 }
7710 }
7711
7712 pub fn select_next_match_internal(
7713 &mut self,
7714 display_map: &DisplaySnapshot,
7715 replace_newest: bool,
7716 autoscroll: Option<Autoscroll>,
7717 cx: &mut ViewContext<Self>,
7718 ) -> Result<()> {
7719 fn select_next_match_ranges(
7720 this: &mut Editor,
7721 range: Range<usize>,
7722 replace_newest: bool,
7723 auto_scroll: Option<Autoscroll>,
7724 cx: &mut ViewContext<Editor>,
7725 ) {
7726 this.unfold_ranges([range.clone()], false, true, cx);
7727 this.change_selections(auto_scroll, cx, |s| {
7728 if replace_newest {
7729 s.delete(s.newest_anchor().id);
7730 }
7731 s.insert_range(range.clone());
7732 });
7733 }
7734
7735 let buffer = &display_map.buffer_snapshot;
7736 let mut selections = self.selections.all::<usize>(cx);
7737 if let Some(mut select_next_state) = self.select_next_state.take() {
7738 let query = &select_next_state.query;
7739 if !select_next_state.done {
7740 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
7741 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
7742 let mut next_selected_range = None;
7743
7744 let bytes_after_last_selection =
7745 buffer.bytes_in_range(last_selection.end..buffer.len());
7746 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
7747 let query_matches = query
7748 .stream_find_iter(bytes_after_last_selection)
7749 .map(|result| (last_selection.end, result))
7750 .chain(
7751 query
7752 .stream_find_iter(bytes_before_first_selection)
7753 .map(|result| (0, result)),
7754 );
7755
7756 for (start_offset, query_match) in query_matches {
7757 let query_match = query_match.unwrap(); // can only fail due to I/O
7758 let offset_range =
7759 start_offset + query_match.start()..start_offset + query_match.end();
7760 let display_range = offset_range.start.to_display_point(&display_map)
7761 ..offset_range.end.to_display_point(&display_map);
7762
7763 if !select_next_state.wordwise
7764 || (!movement::is_inside_word(&display_map, display_range.start)
7765 && !movement::is_inside_word(&display_map, display_range.end))
7766 {
7767 // TODO: This is n^2, because we might check all the selections
7768 if !selections
7769 .iter()
7770 .any(|selection| selection.range().overlaps(&offset_range))
7771 {
7772 next_selected_range = Some(offset_range);
7773 break;
7774 }
7775 }
7776 }
7777
7778 if let Some(next_selected_range) = next_selected_range {
7779 select_next_match_ranges(
7780 self,
7781 next_selected_range,
7782 replace_newest,
7783 autoscroll,
7784 cx,
7785 );
7786 } else {
7787 select_next_state.done = true;
7788 }
7789 }
7790
7791 self.select_next_state = Some(select_next_state);
7792 } else {
7793 let mut only_carets = true;
7794 let mut same_text_selected = true;
7795 let mut selected_text = None;
7796
7797 let mut selections_iter = selections.iter().peekable();
7798 while let Some(selection) = selections_iter.next() {
7799 if selection.start != selection.end {
7800 only_carets = false;
7801 }
7802
7803 if same_text_selected {
7804 if selected_text.is_none() {
7805 selected_text =
7806 Some(buffer.text_for_range(selection.range()).collect::<String>());
7807 }
7808
7809 if let Some(next_selection) = selections_iter.peek() {
7810 if next_selection.range().len() == selection.range().len() {
7811 let next_selected_text = buffer
7812 .text_for_range(next_selection.range())
7813 .collect::<String>();
7814 if Some(next_selected_text) != selected_text {
7815 same_text_selected = false;
7816 selected_text = None;
7817 }
7818 } else {
7819 same_text_selected = false;
7820 selected_text = None;
7821 }
7822 }
7823 }
7824 }
7825
7826 if only_carets {
7827 for selection in &mut selections {
7828 let word_range = movement::surrounding_word(
7829 &display_map,
7830 selection.start.to_display_point(&display_map),
7831 );
7832 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
7833 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
7834 selection.goal = SelectionGoal::None;
7835 selection.reversed = false;
7836 select_next_match_ranges(
7837 self,
7838 selection.start..selection.end,
7839 replace_newest,
7840 autoscroll,
7841 cx,
7842 );
7843 }
7844
7845 if selections.len() == 1 {
7846 let selection = selections
7847 .last()
7848 .expect("ensured that there's only one selection");
7849 let query = buffer
7850 .text_for_range(selection.start..selection.end)
7851 .collect::<String>();
7852 let is_empty = query.is_empty();
7853 let select_state = SelectNextState {
7854 query: AhoCorasick::new(&[query])?,
7855 wordwise: true,
7856 done: is_empty,
7857 };
7858 self.select_next_state = Some(select_state);
7859 } else {
7860 self.select_next_state = None;
7861 }
7862 } else if let Some(selected_text) = selected_text {
7863 self.select_next_state = Some(SelectNextState {
7864 query: AhoCorasick::new(&[selected_text])?,
7865 wordwise: false,
7866 done: false,
7867 });
7868 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
7869 }
7870 }
7871 Ok(())
7872 }
7873
7874 pub fn select_all_matches(
7875 &mut self,
7876 _action: &SelectAllMatches,
7877 cx: &mut ViewContext<Self>,
7878 ) -> Result<()> {
7879 self.push_to_selection_history();
7880 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7881
7882 self.select_next_match_internal(&display_map, false, None, cx)?;
7883 let Some(select_next_state) = self.select_next_state.as_mut() else {
7884 return Ok(());
7885 };
7886 if select_next_state.done {
7887 return Ok(());
7888 }
7889
7890 let mut new_selections = self.selections.all::<usize>(cx);
7891
7892 let buffer = &display_map.buffer_snapshot;
7893 let query_matches = select_next_state
7894 .query
7895 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
7896
7897 for query_match in query_matches {
7898 let query_match = query_match.unwrap(); // can only fail due to I/O
7899 let offset_range = query_match.start()..query_match.end();
7900 let display_range = offset_range.start.to_display_point(&display_map)
7901 ..offset_range.end.to_display_point(&display_map);
7902
7903 if !select_next_state.wordwise
7904 || (!movement::is_inside_word(&display_map, display_range.start)
7905 && !movement::is_inside_word(&display_map, display_range.end))
7906 {
7907 self.selections.change_with(cx, |selections| {
7908 new_selections.push(Selection {
7909 id: selections.new_selection_id(),
7910 start: offset_range.start,
7911 end: offset_range.end,
7912 reversed: false,
7913 goal: SelectionGoal::None,
7914 });
7915 });
7916 }
7917 }
7918
7919 new_selections.sort_by_key(|selection| selection.start);
7920 let mut ix = 0;
7921 while ix + 1 < new_selections.len() {
7922 let current_selection = &new_selections[ix];
7923 let next_selection = &new_selections[ix + 1];
7924 if current_selection.range().overlaps(&next_selection.range()) {
7925 if current_selection.id < next_selection.id {
7926 new_selections.remove(ix + 1);
7927 } else {
7928 new_selections.remove(ix);
7929 }
7930 } else {
7931 ix += 1;
7932 }
7933 }
7934
7935 select_next_state.done = true;
7936 self.unfold_ranges(
7937 new_selections.iter().map(|selection| selection.range()),
7938 false,
7939 false,
7940 cx,
7941 );
7942 self.change_selections(Some(Autoscroll::fit()), cx, |selections| {
7943 selections.select(new_selections)
7944 });
7945
7946 Ok(())
7947 }
7948
7949 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
7950 self.push_to_selection_history();
7951 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7952 self.select_next_match_internal(
7953 &display_map,
7954 action.replace_newest,
7955 Some(Autoscroll::newest()),
7956 cx,
7957 )?;
7958 Ok(())
7959 }
7960
7961 pub fn select_previous(
7962 &mut self,
7963 action: &SelectPrevious,
7964 cx: &mut ViewContext<Self>,
7965 ) -> Result<()> {
7966 self.push_to_selection_history();
7967 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7968 let buffer = &display_map.buffer_snapshot;
7969 let mut selections = self.selections.all::<usize>(cx);
7970 if let Some(mut select_prev_state) = self.select_prev_state.take() {
7971 let query = &select_prev_state.query;
7972 if !select_prev_state.done {
7973 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
7974 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
7975 let mut next_selected_range = None;
7976 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
7977 let bytes_before_last_selection =
7978 buffer.reversed_bytes_in_range(0..last_selection.start);
7979 let bytes_after_first_selection =
7980 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
7981 let query_matches = query
7982 .stream_find_iter(bytes_before_last_selection)
7983 .map(|result| (last_selection.start, result))
7984 .chain(
7985 query
7986 .stream_find_iter(bytes_after_first_selection)
7987 .map(|result| (buffer.len(), result)),
7988 );
7989 for (end_offset, query_match) in query_matches {
7990 let query_match = query_match.unwrap(); // can only fail due to I/O
7991 let offset_range =
7992 end_offset - query_match.end()..end_offset - query_match.start();
7993 let display_range = offset_range.start.to_display_point(&display_map)
7994 ..offset_range.end.to_display_point(&display_map);
7995
7996 if !select_prev_state.wordwise
7997 || (!movement::is_inside_word(&display_map, display_range.start)
7998 && !movement::is_inside_word(&display_map, display_range.end))
7999 {
8000 next_selected_range = Some(offset_range);
8001 break;
8002 }
8003 }
8004
8005 if let Some(next_selected_range) = next_selected_range {
8006 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
8007 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8008 if action.replace_newest {
8009 s.delete(s.newest_anchor().id);
8010 }
8011 s.insert_range(next_selected_range);
8012 });
8013 } else {
8014 select_prev_state.done = true;
8015 }
8016 }
8017
8018 self.select_prev_state = Some(select_prev_state);
8019 } else {
8020 let mut only_carets = true;
8021 let mut same_text_selected = true;
8022 let mut selected_text = None;
8023
8024 let mut selections_iter = selections.iter().peekable();
8025 while let Some(selection) = selections_iter.next() {
8026 if selection.start != selection.end {
8027 only_carets = false;
8028 }
8029
8030 if same_text_selected {
8031 if selected_text.is_none() {
8032 selected_text =
8033 Some(buffer.text_for_range(selection.range()).collect::<String>());
8034 }
8035
8036 if let Some(next_selection) = selections_iter.peek() {
8037 if next_selection.range().len() == selection.range().len() {
8038 let next_selected_text = buffer
8039 .text_for_range(next_selection.range())
8040 .collect::<String>();
8041 if Some(next_selected_text) != selected_text {
8042 same_text_selected = false;
8043 selected_text = None;
8044 }
8045 } else {
8046 same_text_selected = false;
8047 selected_text = None;
8048 }
8049 }
8050 }
8051 }
8052
8053 if only_carets {
8054 for selection in &mut selections {
8055 let word_range = movement::surrounding_word(
8056 &display_map,
8057 selection.start.to_display_point(&display_map),
8058 );
8059 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
8060 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
8061 selection.goal = SelectionGoal::None;
8062 selection.reversed = false;
8063 }
8064 if selections.len() == 1 {
8065 let selection = selections
8066 .last()
8067 .expect("ensured that there's only one selection");
8068 let query = buffer
8069 .text_for_range(selection.start..selection.end)
8070 .collect::<String>();
8071 let is_empty = query.is_empty();
8072 let select_state = SelectNextState {
8073 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
8074 wordwise: true,
8075 done: is_empty,
8076 };
8077 self.select_prev_state = Some(select_state);
8078 } else {
8079 self.select_prev_state = None;
8080 }
8081
8082 self.unfold_ranges(
8083 selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
8084 false,
8085 true,
8086 cx,
8087 );
8088 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8089 s.select(selections);
8090 });
8091 } else if let Some(selected_text) = selected_text {
8092 self.select_prev_state = Some(SelectNextState {
8093 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
8094 wordwise: false,
8095 done: false,
8096 });
8097 self.select_previous(action, cx)?;
8098 }
8099 }
8100 Ok(())
8101 }
8102
8103 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
8104 let text_layout_details = &self.text_layout_details(cx);
8105 self.transact(cx, |this, cx| {
8106 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
8107 let mut edits = Vec::new();
8108 let mut selection_edit_ranges = Vec::new();
8109 let mut last_toggled_row = None;
8110 let snapshot = this.buffer.read(cx).read(cx);
8111 let empty_str: Arc<str> = Arc::default();
8112 let mut suffixes_inserted = Vec::new();
8113
8114 fn comment_prefix_range(
8115 snapshot: &MultiBufferSnapshot,
8116 row: MultiBufferRow,
8117 comment_prefix: &str,
8118 comment_prefix_whitespace: &str,
8119 ) -> Range<Point> {
8120 let start = Point::new(row.0, snapshot.indent_size_for_line(row).len);
8121
8122 let mut line_bytes = snapshot
8123 .bytes_in_range(start..snapshot.max_point())
8124 .flatten()
8125 .copied();
8126
8127 // If this line currently begins with the line comment prefix, then record
8128 // the range containing the prefix.
8129 if line_bytes
8130 .by_ref()
8131 .take(comment_prefix.len())
8132 .eq(comment_prefix.bytes())
8133 {
8134 // Include any whitespace that matches the comment prefix.
8135 let matching_whitespace_len = line_bytes
8136 .zip(comment_prefix_whitespace.bytes())
8137 .take_while(|(a, b)| a == b)
8138 .count() as u32;
8139 let end = Point::new(
8140 start.row,
8141 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
8142 );
8143 start..end
8144 } else {
8145 start..start
8146 }
8147 }
8148
8149 fn comment_suffix_range(
8150 snapshot: &MultiBufferSnapshot,
8151 row: MultiBufferRow,
8152 comment_suffix: &str,
8153 comment_suffix_has_leading_space: bool,
8154 ) -> Range<Point> {
8155 let end = Point::new(row.0, snapshot.line_len(row));
8156 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
8157
8158 let mut line_end_bytes = snapshot
8159 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
8160 .flatten()
8161 .copied();
8162
8163 let leading_space_len = if suffix_start_column > 0
8164 && line_end_bytes.next() == Some(b' ')
8165 && comment_suffix_has_leading_space
8166 {
8167 1
8168 } else {
8169 0
8170 };
8171
8172 // If this line currently begins with the line comment prefix, then record
8173 // the range containing the prefix.
8174 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
8175 let start = Point::new(end.row, suffix_start_column - leading_space_len);
8176 start..end
8177 } else {
8178 end..end
8179 }
8180 }
8181
8182 // TODO: Handle selections that cross excerpts
8183 for selection in &mut selections {
8184 let start_column = snapshot
8185 .indent_size_for_line(MultiBufferRow(selection.start.row))
8186 .len;
8187 let language = if let Some(language) =
8188 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
8189 {
8190 language
8191 } else {
8192 continue;
8193 };
8194
8195 selection_edit_ranges.clear();
8196
8197 // If multiple selections contain a given row, avoid processing that
8198 // row more than once.
8199 let mut start_row = MultiBufferRow(selection.start.row);
8200 if last_toggled_row == Some(start_row) {
8201 start_row = start_row.next_row();
8202 }
8203 let end_row =
8204 if selection.end.row > selection.start.row && selection.end.column == 0 {
8205 MultiBufferRow(selection.end.row - 1)
8206 } else {
8207 MultiBufferRow(selection.end.row)
8208 };
8209 last_toggled_row = Some(end_row);
8210
8211 if start_row > end_row {
8212 continue;
8213 }
8214
8215 // If the language has line comments, toggle those.
8216 let full_comment_prefixes = language.line_comment_prefixes();
8217 if !full_comment_prefixes.is_empty() {
8218 let first_prefix = full_comment_prefixes
8219 .first()
8220 .expect("prefixes is non-empty");
8221 let prefix_trimmed_lengths = full_comment_prefixes
8222 .iter()
8223 .map(|p| p.trim_end_matches(' ').len())
8224 .collect::<SmallVec<[usize; 4]>>();
8225
8226 let mut all_selection_lines_are_comments = true;
8227
8228 for row in start_row.0..=end_row.0 {
8229 let row = MultiBufferRow(row);
8230 if start_row < end_row && snapshot.is_line_blank(row) {
8231 continue;
8232 }
8233
8234 let prefix_range = full_comment_prefixes
8235 .iter()
8236 .zip(prefix_trimmed_lengths.iter().copied())
8237 .map(|(prefix, trimmed_prefix_len)| {
8238 comment_prefix_range(
8239 snapshot.deref(),
8240 row,
8241 &prefix[..trimmed_prefix_len],
8242 &prefix[trimmed_prefix_len..],
8243 )
8244 })
8245 .max_by_key(|range| range.end.column - range.start.column)
8246 .expect("prefixes is non-empty");
8247
8248 if prefix_range.is_empty() {
8249 all_selection_lines_are_comments = false;
8250 }
8251
8252 selection_edit_ranges.push(prefix_range);
8253 }
8254
8255 if all_selection_lines_are_comments {
8256 edits.extend(
8257 selection_edit_ranges
8258 .iter()
8259 .cloned()
8260 .map(|range| (range, empty_str.clone())),
8261 );
8262 } else {
8263 let min_column = selection_edit_ranges
8264 .iter()
8265 .map(|range| range.start.column)
8266 .min()
8267 .unwrap_or(0);
8268 edits.extend(selection_edit_ranges.iter().map(|range| {
8269 let position = Point::new(range.start.row, min_column);
8270 (position..position, first_prefix.clone())
8271 }));
8272 }
8273 } else if let Some((full_comment_prefix, comment_suffix)) =
8274 language.block_comment_delimiters()
8275 {
8276 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
8277 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
8278 let prefix_range = comment_prefix_range(
8279 snapshot.deref(),
8280 start_row,
8281 comment_prefix,
8282 comment_prefix_whitespace,
8283 );
8284 let suffix_range = comment_suffix_range(
8285 snapshot.deref(),
8286 end_row,
8287 comment_suffix.trim_start_matches(' '),
8288 comment_suffix.starts_with(' '),
8289 );
8290
8291 if prefix_range.is_empty() || suffix_range.is_empty() {
8292 edits.push((
8293 prefix_range.start..prefix_range.start,
8294 full_comment_prefix.clone(),
8295 ));
8296 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
8297 suffixes_inserted.push((end_row, comment_suffix.len()));
8298 } else {
8299 edits.push((prefix_range, empty_str.clone()));
8300 edits.push((suffix_range, empty_str.clone()));
8301 }
8302 } else {
8303 continue;
8304 }
8305 }
8306
8307 drop(snapshot);
8308 this.buffer.update(cx, |buffer, cx| {
8309 buffer.edit(edits, None, cx);
8310 });
8311
8312 // Adjust selections so that they end before any comment suffixes that
8313 // were inserted.
8314 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
8315 let mut selections = this.selections.all::<Point>(cx);
8316 let snapshot = this.buffer.read(cx).read(cx);
8317 for selection in &mut selections {
8318 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
8319 match row.cmp(&MultiBufferRow(selection.end.row)) {
8320 Ordering::Less => {
8321 suffixes_inserted.next();
8322 continue;
8323 }
8324 Ordering::Greater => break,
8325 Ordering::Equal => {
8326 if selection.end.column == snapshot.line_len(row) {
8327 if selection.is_empty() {
8328 selection.start.column -= suffix_len as u32;
8329 }
8330 selection.end.column -= suffix_len as u32;
8331 }
8332 break;
8333 }
8334 }
8335 }
8336 }
8337
8338 drop(snapshot);
8339 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
8340
8341 let selections = this.selections.all::<Point>(cx);
8342 let selections_on_single_row = selections.windows(2).all(|selections| {
8343 selections[0].start.row == selections[1].start.row
8344 && selections[0].end.row == selections[1].end.row
8345 && selections[0].start.row == selections[0].end.row
8346 });
8347 let selections_selecting = selections
8348 .iter()
8349 .any(|selection| selection.start != selection.end);
8350 let advance_downwards = action.advance_downwards
8351 && selections_on_single_row
8352 && !selections_selecting
8353 && !matches!(this.mode, EditorMode::SingleLine { .. });
8354
8355 if advance_downwards {
8356 let snapshot = this.buffer.read(cx).snapshot(cx);
8357
8358 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8359 s.move_cursors_with(|display_snapshot, display_point, _| {
8360 let mut point = display_point.to_point(display_snapshot);
8361 point.row += 1;
8362 point = snapshot.clip_point(point, Bias::Left);
8363 let display_point = point.to_display_point(display_snapshot);
8364 let goal = SelectionGoal::HorizontalPosition(
8365 display_snapshot
8366 .x_for_display_point(display_point, &text_layout_details)
8367 .into(),
8368 );
8369 (display_point, goal)
8370 })
8371 });
8372 }
8373 });
8374 }
8375
8376 pub fn select_enclosing_symbol(
8377 &mut self,
8378 _: &SelectEnclosingSymbol,
8379 cx: &mut ViewContext<Self>,
8380 ) {
8381 let buffer = self.buffer.read(cx).snapshot(cx);
8382 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8383
8384 fn update_selection(
8385 selection: &Selection<usize>,
8386 buffer_snap: &MultiBufferSnapshot,
8387 ) -> Option<Selection<usize>> {
8388 let cursor = selection.head();
8389 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
8390 for symbol in symbols.iter().rev() {
8391 let start = symbol.range.start.to_offset(&buffer_snap);
8392 let end = symbol.range.end.to_offset(&buffer_snap);
8393 let new_range = start..end;
8394 if start < selection.start || end > selection.end {
8395 return Some(Selection {
8396 id: selection.id,
8397 start: new_range.start,
8398 end: new_range.end,
8399 goal: SelectionGoal::None,
8400 reversed: selection.reversed,
8401 });
8402 }
8403 }
8404 None
8405 }
8406
8407 let mut selected_larger_symbol = false;
8408 let new_selections = old_selections
8409 .iter()
8410 .map(|selection| match update_selection(selection, &buffer) {
8411 Some(new_selection) => {
8412 if new_selection.range() != selection.range() {
8413 selected_larger_symbol = true;
8414 }
8415 new_selection
8416 }
8417 None => selection.clone(),
8418 })
8419 .collect::<Vec<_>>();
8420
8421 if selected_larger_symbol {
8422 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8423 s.select(new_selections);
8424 });
8425 }
8426 }
8427
8428 pub fn select_larger_syntax_node(
8429 &mut self,
8430 _: &SelectLargerSyntaxNode,
8431 cx: &mut ViewContext<Self>,
8432 ) {
8433 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8434 let buffer = self.buffer.read(cx).snapshot(cx);
8435 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8436
8437 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8438 let mut selected_larger_node = false;
8439 let new_selections = old_selections
8440 .iter()
8441 .map(|selection| {
8442 let old_range = selection.start..selection.end;
8443 let mut new_range = old_range.clone();
8444 while let Some(containing_range) =
8445 buffer.range_for_syntax_ancestor(new_range.clone())
8446 {
8447 new_range = containing_range;
8448 if !display_map.intersects_fold(new_range.start)
8449 && !display_map.intersects_fold(new_range.end)
8450 {
8451 break;
8452 }
8453 }
8454
8455 selected_larger_node |= new_range != old_range;
8456 Selection {
8457 id: selection.id,
8458 start: new_range.start,
8459 end: new_range.end,
8460 goal: SelectionGoal::None,
8461 reversed: selection.reversed,
8462 }
8463 })
8464 .collect::<Vec<_>>();
8465
8466 if selected_larger_node {
8467 stack.push(old_selections);
8468 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8469 s.select(new_selections);
8470 });
8471 }
8472 self.select_larger_syntax_node_stack = stack;
8473 }
8474
8475 pub fn select_smaller_syntax_node(
8476 &mut self,
8477 _: &SelectSmallerSyntaxNode,
8478 cx: &mut ViewContext<Self>,
8479 ) {
8480 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8481 if let Some(selections) = stack.pop() {
8482 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8483 s.select(selections.to_vec());
8484 });
8485 }
8486 self.select_larger_syntax_node_stack = stack;
8487 }
8488
8489 fn refresh_runnables(&mut self, cx: &mut ViewContext<Self>) -> Task<()> {
8490 if !EditorSettings::get_global(cx).gutter.runnables {
8491 self.clear_tasks();
8492 return Task::ready(());
8493 }
8494 let project = self.project.clone();
8495 cx.spawn(|this, mut cx| async move {
8496 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
8497 this.display_map.update(cx, |map, cx| map.snapshot(cx))
8498 }) else {
8499 return;
8500 };
8501
8502 let Some(project) = project else {
8503 return;
8504 };
8505
8506 let hide_runnables = project
8507 .update(&mut cx, |project, cx| {
8508 // Do not display any test indicators in non-dev server remote projects.
8509 project.is_remote() && project.ssh_connection_string(cx).is_none()
8510 })
8511 .unwrap_or(true);
8512 if hide_runnables {
8513 return;
8514 }
8515 let new_rows =
8516 cx.background_executor()
8517 .spawn({
8518 let snapshot = display_snapshot.clone();
8519 async move {
8520 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
8521 }
8522 })
8523 .await;
8524 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
8525
8526 this.update(&mut cx, |this, _| {
8527 this.clear_tasks();
8528 for (key, value) in rows {
8529 this.insert_tasks(key, value);
8530 }
8531 })
8532 .ok();
8533 })
8534 }
8535 fn fetch_runnable_ranges(
8536 snapshot: &DisplaySnapshot,
8537 range: Range<Anchor>,
8538 ) -> Vec<language::RunnableRange> {
8539 snapshot.buffer_snapshot.runnable_ranges(range).collect()
8540 }
8541
8542 fn runnable_rows(
8543 project: Model<Project>,
8544 snapshot: DisplaySnapshot,
8545 runnable_ranges: Vec<RunnableRange>,
8546 mut cx: AsyncWindowContext,
8547 ) -> Vec<((BufferId, u32), RunnableTasks)> {
8548 runnable_ranges
8549 .into_iter()
8550 .filter_map(|mut runnable| {
8551 let tasks = cx
8552 .update(|cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
8553 .ok()?;
8554 if tasks.is_empty() {
8555 return None;
8556 }
8557
8558 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
8559
8560 let row = snapshot
8561 .buffer_snapshot
8562 .buffer_line_for_row(MultiBufferRow(point.row))?
8563 .1
8564 .start
8565 .row;
8566
8567 let context_range =
8568 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
8569 Some((
8570 (runnable.buffer_id, row),
8571 RunnableTasks {
8572 templates: tasks,
8573 offset: MultiBufferOffset(runnable.run_range.start),
8574 context_range,
8575 column: point.column,
8576 extra_variables: runnable.extra_captures,
8577 },
8578 ))
8579 })
8580 .collect()
8581 }
8582
8583 fn templates_with_tags(
8584 project: &Model<Project>,
8585 runnable: &mut Runnable,
8586 cx: &WindowContext<'_>,
8587 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
8588 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
8589 let (worktree_id, file) = project
8590 .buffer_for_id(runnable.buffer, cx)
8591 .and_then(|buffer| buffer.read(cx).file())
8592 .map(|file| (WorktreeId::from_usize(file.worktree_id()), file.clone()))
8593 .unzip();
8594
8595 (project.task_inventory().clone(), worktree_id, file)
8596 });
8597
8598 let inventory = inventory.read(cx);
8599 let tags = mem::take(&mut runnable.tags);
8600 let mut tags: Vec<_> = tags
8601 .into_iter()
8602 .flat_map(|tag| {
8603 let tag = tag.0.clone();
8604 inventory
8605 .list_tasks(
8606 file.clone(),
8607 Some(runnable.language.clone()),
8608 worktree_id,
8609 cx,
8610 )
8611 .into_iter()
8612 .filter(move |(_, template)| {
8613 template.tags.iter().any(|source_tag| source_tag == &tag)
8614 })
8615 })
8616 .sorted_by_key(|(kind, _)| kind.to_owned())
8617 .collect();
8618 if let Some((leading_tag_source, _)) = tags.first() {
8619 // Strongest source wins; if we have worktree tag binding, prefer that to
8620 // global and language bindings;
8621 // if we have a global binding, prefer that to language binding.
8622 let first_mismatch = tags
8623 .iter()
8624 .position(|(tag_source, _)| tag_source != leading_tag_source);
8625 if let Some(index) = first_mismatch {
8626 tags.truncate(index);
8627 }
8628 }
8629
8630 tags
8631 }
8632
8633 pub fn move_to_enclosing_bracket(
8634 &mut self,
8635 _: &MoveToEnclosingBracket,
8636 cx: &mut ViewContext<Self>,
8637 ) {
8638 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8639 s.move_offsets_with(|snapshot, selection| {
8640 let Some(enclosing_bracket_ranges) =
8641 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
8642 else {
8643 return;
8644 };
8645
8646 let mut best_length = usize::MAX;
8647 let mut best_inside = false;
8648 let mut best_in_bracket_range = false;
8649 let mut best_destination = None;
8650 for (open, close) in enclosing_bracket_ranges {
8651 let close = close.to_inclusive();
8652 let length = close.end() - open.start;
8653 let inside = selection.start >= open.end && selection.end <= *close.start();
8654 let in_bracket_range = open.to_inclusive().contains(&selection.head())
8655 || close.contains(&selection.head());
8656
8657 // If best is next to a bracket and current isn't, skip
8658 if !in_bracket_range && best_in_bracket_range {
8659 continue;
8660 }
8661
8662 // Prefer smaller lengths unless best is inside and current isn't
8663 if length > best_length && (best_inside || !inside) {
8664 continue;
8665 }
8666
8667 best_length = length;
8668 best_inside = inside;
8669 best_in_bracket_range = in_bracket_range;
8670 best_destination = Some(
8671 if close.contains(&selection.start) && close.contains(&selection.end) {
8672 if inside {
8673 open.end
8674 } else {
8675 open.start
8676 }
8677 } else {
8678 if inside {
8679 *close.start()
8680 } else {
8681 *close.end()
8682 }
8683 },
8684 );
8685 }
8686
8687 if let Some(destination) = best_destination {
8688 selection.collapse_to(destination, SelectionGoal::None);
8689 }
8690 })
8691 });
8692 }
8693
8694 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
8695 self.end_selection(cx);
8696 self.selection_history.mode = SelectionHistoryMode::Undoing;
8697 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
8698 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8699 self.select_next_state = entry.select_next_state;
8700 self.select_prev_state = entry.select_prev_state;
8701 self.add_selections_state = entry.add_selections_state;
8702 self.request_autoscroll(Autoscroll::newest(), cx);
8703 }
8704 self.selection_history.mode = SelectionHistoryMode::Normal;
8705 }
8706
8707 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
8708 self.end_selection(cx);
8709 self.selection_history.mode = SelectionHistoryMode::Redoing;
8710 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
8711 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8712 self.select_next_state = entry.select_next_state;
8713 self.select_prev_state = entry.select_prev_state;
8714 self.add_selections_state = entry.add_selections_state;
8715 self.request_autoscroll(Autoscroll::newest(), cx);
8716 }
8717 self.selection_history.mode = SelectionHistoryMode::Normal;
8718 }
8719
8720 pub fn expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
8721 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
8722 }
8723
8724 pub fn expand_excerpts_down(
8725 &mut self,
8726 action: &ExpandExcerptsDown,
8727 cx: &mut ViewContext<Self>,
8728 ) {
8729 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
8730 }
8731
8732 pub fn expand_excerpts_up(&mut self, action: &ExpandExcerptsUp, cx: &mut ViewContext<Self>) {
8733 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
8734 }
8735
8736 pub fn expand_excerpts_for_direction(
8737 &mut self,
8738 lines: u32,
8739 direction: ExpandExcerptDirection,
8740 cx: &mut ViewContext<Self>,
8741 ) {
8742 let selections = self.selections.disjoint_anchors();
8743
8744 let lines = if lines == 0 {
8745 EditorSettings::get_global(cx).expand_excerpt_lines
8746 } else {
8747 lines
8748 };
8749
8750 self.buffer.update(cx, |buffer, cx| {
8751 buffer.expand_excerpts(
8752 selections
8753 .into_iter()
8754 .map(|selection| selection.head().excerpt_id)
8755 .dedup(),
8756 lines,
8757 direction,
8758 cx,
8759 )
8760 })
8761 }
8762
8763 pub fn expand_excerpt(
8764 &mut self,
8765 excerpt: ExcerptId,
8766 direction: ExpandExcerptDirection,
8767 cx: &mut ViewContext<Self>,
8768 ) {
8769 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
8770 self.buffer.update(cx, |buffer, cx| {
8771 buffer.expand_excerpts([excerpt], lines, direction, cx)
8772 })
8773 }
8774
8775 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
8776 self.go_to_diagnostic_impl(Direction::Next, cx)
8777 }
8778
8779 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
8780 self.go_to_diagnostic_impl(Direction::Prev, cx)
8781 }
8782
8783 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
8784 let buffer = self.buffer.read(cx).snapshot(cx);
8785 let selection = self.selections.newest::<usize>(cx);
8786
8787 // If there is an active Diagnostic Popover jump to its diagnostic instead.
8788 if direction == Direction::Next {
8789 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
8790 let (group_id, jump_to) = popover.activation_info();
8791 if self.activate_diagnostics(group_id, cx) {
8792 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8793 let mut new_selection = s.newest_anchor().clone();
8794 new_selection.collapse_to(jump_to, SelectionGoal::None);
8795 s.select_anchors(vec![new_selection.clone()]);
8796 });
8797 }
8798 return;
8799 }
8800 }
8801
8802 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
8803 active_diagnostics
8804 .primary_range
8805 .to_offset(&buffer)
8806 .to_inclusive()
8807 });
8808 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
8809 if active_primary_range.contains(&selection.head()) {
8810 *active_primary_range.start()
8811 } else {
8812 selection.head()
8813 }
8814 } else {
8815 selection.head()
8816 };
8817 let snapshot = self.snapshot(cx);
8818 loop {
8819 let diagnostics = if direction == Direction::Prev {
8820 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
8821 } else {
8822 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
8823 }
8824 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start));
8825 let group = diagnostics
8826 // relies on diagnostics_in_range to return diagnostics with the same starting range to
8827 // be sorted in a stable way
8828 // skip until we are at current active diagnostic, if it exists
8829 .skip_while(|entry| {
8830 (match direction {
8831 Direction::Prev => entry.range.start >= search_start,
8832 Direction::Next => entry.range.start <= search_start,
8833 }) && self
8834 .active_diagnostics
8835 .as_ref()
8836 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
8837 })
8838 .find_map(|entry| {
8839 if entry.diagnostic.is_primary
8840 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
8841 && !entry.range.is_empty()
8842 // if we match with the active diagnostic, skip it
8843 && Some(entry.diagnostic.group_id)
8844 != self.active_diagnostics.as_ref().map(|d| d.group_id)
8845 {
8846 Some((entry.range, entry.diagnostic.group_id))
8847 } else {
8848 None
8849 }
8850 });
8851
8852 if let Some((primary_range, group_id)) = group {
8853 if self.activate_diagnostics(group_id, cx) {
8854 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8855 s.select(vec![Selection {
8856 id: selection.id,
8857 start: primary_range.start,
8858 end: primary_range.start,
8859 reversed: false,
8860 goal: SelectionGoal::None,
8861 }]);
8862 });
8863 }
8864 break;
8865 } else {
8866 // Cycle around to the start of the buffer, potentially moving back to the start of
8867 // the currently active diagnostic.
8868 active_primary_range.take();
8869 if direction == Direction::Prev {
8870 if search_start == buffer.len() {
8871 break;
8872 } else {
8873 search_start = buffer.len();
8874 }
8875 } else if search_start == 0 {
8876 break;
8877 } else {
8878 search_start = 0;
8879 }
8880 }
8881 }
8882 }
8883
8884 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
8885 let snapshot = self
8886 .display_map
8887 .update(cx, |display_map, cx| display_map.snapshot(cx));
8888 let selection = self.selections.newest::<Point>(cx);
8889
8890 if !self.seek_in_direction(
8891 &snapshot,
8892 selection.head(),
8893 false,
8894 snapshot.buffer_snapshot.git_diff_hunks_in_range(
8895 MultiBufferRow(selection.head().row + 1)..MultiBufferRow::MAX,
8896 ),
8897 cx,
8898 ) {
8899 let wrapped_point = Point::zero();
8900 self.seek_in_direction(
8901 &snapshot,
8902 wrapped_point,
8903 true,
8904 snapshot.buffer_snapshot.git_diff_hunks_in_range(
8905 MultiBufferRow(wrapped_point.row + 1)..MultiBufferRow::MAX,
8906 ),
8907 cx,
8908 );
8909 }
8910 }
8911
8912 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
8913 let snapshot = self
8914 .display_map
8915 .update(cx, |display_map, cx| display_map.snapshot(cx));
8916 let selection = self.selections.newest::<Point>(cx);
8917
8918 if !self.seek_in_direction(
8919 &snapshot,
8920 selection.head(),
8921 false,
8922 snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
8923 MultiBufferRow(0)..MultiBufferRow(selection.head().row),
8924 ),
8925 cx,
8926 ) {
8927 let wrapped_point = snapshot.buffer_snapshot.max_point();
8928 self.seek_in_direction(
8929 &snapshot,
8930 wrapped_point,
8931 true,
8932 snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
8933 MultiBufferRow(0)..MultiBufferRow(wrapped_point.row),
8934 ),
8935 cx,
8936 );
8937 }
8938 }
8939
8940 fn seek_in_direction(
8941 &mut self,
8942 snapshot: &DisplaySnapshot,
8943 initial_point: Point,
8944 is_wrapped: bool,
8945 hunks: impl Iterator<Item = DiffHunk<MultiBufferRow>>,
8946 cx: &mut ViewContext<Editor>,
8947 ) -> bool {
8948 let display_point = initial_point.to_display_point(snapshot);
8949 let mut hunks = hunks
8950 .map(|hunk| diff_hunk_to_display(&hunk, &snapshot))
8951 .filter(|hunk| is_wrapped || !hunk.contains_display_row(display_point.row()))
8952 .dedup();
8953
8954 if let Some(hunk) = hunks.next() {
8955 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8956 let row = hunk.start_display_row();
8957 let point = DisplayPoint::new(row, 0);
8958 s.select_display_ranges([point..point]);
8959 });
8960
8961 true
8962 } else {
8963 false
8964 }
8965 }
8966
8967 pub fn go_to_definition(
8968 &mut self,
8969 _: &GoToDefinition,
8970 cx: &mut ViewContext<Self>,
8971 ) -> Task<Result<bool>> {
8972 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx)
8973 }
8974
8975 pub fn go_to_declaration(
8976 &mut self,
8977 _: &GoToDeclaration,
8978 cx: &mut ViewContext<Self>,
8979 ) -> Task<Result<bool>> {
8980 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, cx)
8981 }
8982
8983 pub fn go_to_declaration_split(
8984 &mut self,
8985 _: &GoToDeclaration,
8986 cx: &mut ViewContext<Self>,
8987 ) -> Task<Result<bool>> {
8988 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, cx)
8989 }
8990
8991 pub fn go_to_implementation(
8992 &mut self,
8993 _: &GoToImplementation,
8994 cx: &mut ViewContext<Self>,
8995 ) -> Task<Result<bool>> {
8996 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx)
8997 }
8998
8999 pub fn go_to_implementation_split(
9000 &mut self,
9001 _: &GoToImplementationSplit,
9002 cx: &mut ViewContext<Self>,
9003 ) -> Task<Result<bool>> {
9004 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx)
9005 }
9006
9007 pub fn go_to_type_definition(
9008 &mut self,
9009 _: &GoToTypeDefinition,
9010 cx: &mut ViewContext<Self>,
9011 ) -> Task<Result<bool>> {
9012 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx)
9013 }
9014
9015 pub fn go_to_definition_split(
9016 &mut self,
9017 _: &GoToDefinitionSplit,
9018 cx: &mut ViewContext<Self>,
9019 ) -> Task<Result<bool>> {
9020 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx)
9021 }
9022
9023 pub fn go_to_type_definition_split(
9024 &mut self,
9025 _: &GoToTypeDefinitionSplit,
9026 cx: &mut ViewContext<Self>,
9027 ) -> Task<Result<bool>> {
9028 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx)
9029 }
9030
9031 fn go_to_definition_of_kind(
9032 &mut self,
9033 kind: GotoDefinitionKind,
9034 split: bool,
9035 cx: &mut ViewContext<Self>,
9036 ) -> Task<Result<bool>> {
9037 let Some(workspace) = self.workspace() else {
9038 return Task::ready(Ok(false));
9039 };
9040 let buffer = self.buffer.read(cx);
9041 let head = self.selections.newest::<usize>(cx).head();
9042 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
9043 text_anchor
9044 } else {
9045 return Task::ready(Ok(false));
9046 };
9047
9048 let project = workspace.read(cx).project().clone();
9049 let definitions = project.update(cx, |project, cx| match kind {
9050 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
9051 GotoDefinitionKind::Declaration => project.declaration(&buffer, head, cx),
9052 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
9053 GotoDefinitionKind::Implementation => project.implementation(&buffer, head, cx),
9054 });
9055
9056 cx.spawn(|editor, mut cx| async move {
9057 let definitions = definitions.await?;
9058 let navigated = editor
9059 .update(&mut cx, |editor, cx| {
9060 editor.navigate_to_hover_links(
9061 Some(kind),
9062 definitions
9063 .into_iter()
9064 .filter(|location| {
9065 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
9066 })
9067 .map(HoverLink::Text)
9068 .collect::<Vec<_>>(),
9069 split,
9070 cx,
9071 )
9072 })?
9073 .await?;
9074 anyhow::Ok(navigated)
9075 })
9076 }
9077
9078 pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext<Self>) {
9079 let position = self.selections.newest_anchor().head();
9080 let Some((buffer, buffer_position)) =
9081 self.buffer.read(cx).text_anchor_for_position(position, cx)
9082 else {
9083 return;
9084 };
9085
9086 cx.spawn(|editor, mut cx| async move {
9087 if let Some((_, url)) = find_url(&buffer, buffer_position, cx.clone()) {
9088 editor.update(&mut cx, |_, cx| {
9089 cx.open_url(&url);
9090 })
9091 } else {
9092 Ok(())
9093 }
9094 })
9095 .detach();
9096 }
9097
9098 pub(crate) fn navigate_to_hover_links(
9099 &mut self,
9100 kind: Option<GotoDefinitionKind>,
9101 mut definitions: Vec<HoverLink>,
9102 split: bool,
9103 cx: &mut ViewContext<Editor>,
9104 ) -> Task<Result<bool>> {
9105 // If there is one definition, just open it directly
9106 if definitions.len() == 1 {
9107 let definition = definitions.pop().unwrap();
9108 let target_task = match definition {
9109 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
9110 HoverLink::InlayHint(lsp_location, server_id) => {
9111 self.compute_target_location(lsp_location, server_id, cx)
9112 }
9113 HoverLink::Url(url) => {
9114 cx.open_url(&url);
9115 Task::ready(Ok(None))
9116 }
9117 };
9118 cx.spawn(|editor, mut cx| async move {
9119 let target = target_task.await.context("target resolution task")?;
9120 if let Some(target) = target {
9121 editor.update(&mut cx, |editor, cx| {
9122 let Some(workspace) = editor.workspace() else {
9123 return false;
9124 };
9125 let pane = workspace.read(cx).active_pane().clone();
9126
9127 let range = target.range.to_offset(target.buffer.read(cx));
9128 let range = editor.range_for_match(&range);
9129
9130 /// If select range has more than one line, we
9131 /// just point the cursor to range.start.
9132 fn check_multiline_range(
9133 buffer: &Buffer,
9134 range: Range<usize>,
9135 ) -> Range<usize> {
9136 if buffer.offset_to_point(range.start).row
9137 == buffer.offset_to_point(range.end).row
9138 {
9139 range
9140 } else {
9141 range.start..range.start
9142 }
9143 }
9144
9145 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
9146 let buffer = target.buffer.read(cx);
9147 let range = check_multiline_range(buffer, range);
9148 editor.change_selections(Some(Autoscroll::focused()), cx, |s| {
9149 s.select_ranges([range]);
9150 });
9151 } else {
9152 cx.window_context().defer(move |cx| {
9153 let target_editor: View<Self> =
9154 workspace.update(cx, |workspace, cx| {
9155 let pane = if split {
9156 workspace.adjacent_pane(cx)
9157 } else {
9158 workspace.active_pane().clone()
9159 };
9160
9161 workspace.open_project_item(
9162 pane,
9163 target.buffer.clone(),
9164 true,
9165 true,
9166 cx,
9167 )
9168 });
9169 target_editor.update(cx, |target_editor, cx| {
9170 // When selecting a definition in a different buffer, disable the nav history
9171 // to avoid creating a history entry at the previous cursor location.
9172 pane.update(cx, |pane, _| pane.disable_history());
9173 let buffer = target.buffer.read(cx);
9174 let range = check_multiline_range(buffer, range);
9175 target_editor.change_selections(
9176 Some(Autoscroll::focused()),
9177 cx,
9178 |s| {
9179 s.select_ranges([range]);
9180 },
9181 );
9182 pane.update(cx, |pane, _| pane.enable_history());
9183 });
9184 });
9185 }
9186 true
9187 })
9188 } else {
9189 Ok(false)
9190 }
9191 })
9192 } else if !definitions.is_empty() {
9193 let replica_id = self.replica_id(cx);
9194 cx.spawn(|editor, mut cx| async move {
9195 let (title, location_tasks, workspace) = editor
9196 .update(&mut cx, |editor, cx| {
9197 let tab_kind = match kind {
9198 Some(GotoDefinitionKind::Implementation) => "Implementations",
9199 _ => "Definitions",
9200 };
9201 let title = definitions
9202 .iter()
9203 .find_map(|definition| match definition {
9204 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
9205 let buffer = origin.buffer.read(cx);
9206 format!(
9207 "{} for {}",
9208 tab_kind,
9209 buffer
9210 .text_for_range(origin.range.clone())
9211 .collect::<String>()
9212 )
9213 }),
9214 HoverLink::InlayHint(_, _) => None,
9215 HoverLink::Url(_) => None,
9216 })
9217 .unwrap_or(tab_kind.to_string());
9218 let location_tasks = definitions
9219 .into_iter()
9220 .map(|definition| match definition {
9221 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
9222 HoverLink::InlayHint(lsp_location, server_id) => {
9223 editor.compute_target_location(lsp_location, server_id, cx)
9224 }
9225 HoverLink::Url(_) => Task::ready(Ok(None)),
9226 })
9227 .collect::<Vec<_>>();
9228 (title, location_tasks, editor.workspace().clone())
9229 })
9230 .context("location tasks preparation")?;
9231
9232 let locations = futures::future::join_all(location_tasks)
9233 .await
9234 .into_iter()
9235 .filter_map(|location| location.transpose())
9236 .collect::<Result<_>>()
9237 .context("location tasks")?;
9238
9239 let Some(workspace) = workspace else {
9240 return Ok(false);
9241 };
9242 let opened = workspace
9243 .update(&mut cx, |workspace, cx| {
9244 Self::open_locations_in_multibuffer(
9245 workspace, locations, replica_id, title, split, cx,
9246 )
9247 })
9248 .ok();
9249
9250 anyhow::Ok(opened.is_some())
9251 })
9252 } else {
9253 Task::ready(Ok(false))
9254 }
9255 }
9256
9257 fn compute_target_location(
9258 &self,
9259 lsp_location: lsp::Location,
9260 server_id: LanguageServerId,
9261 cx: &mut ViewContext<Editor>,
9262 ) -> Task<anyhow::Result<Option<Location>>> {
9263 let Some(project) = self.project.clone() else {
9264 return Task::Ready(Some(Ok(None)));
9265 };
9266
9267 cx.spawn(move |editor, mut cx| async move {
9268 let location_task = editor.update(&mut cx, |editor, cx| {
9269 project.update(cx, |project, cx| {
9270 let language_server_name =
9271 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
9272 project
9273 .language_server_for_buffer(buffer.read(cx), server_id, cx)
9274 .map(|(lsp_adapter, _)| lsp_adapter.name.clone())
9275 });
9276 language_server_name.map(|language_server_name| {
9277 project.open_local_buffer_via_lsp(
9278 lsp_location.uri.clone(),
9279 server_id,
9280 language_server_name,
9281 cx,
9282 )
9283 })
9284 })
9285 })?;
9286 let location = match location_task {
9287 Some(task) => Some({
9288 let target_buffer_handle = task.await.context("open local buffer")?;
9289 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
9290 let target_start = target_buffer
9291 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
9292 let target_end = target_buffer
9293 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
9294 target_buffer.anchor_after(target_start)
9295 ..target_buffer.anchor_before(target_end)
9296 })?;
9297 Location {
9298 buffer: target_buffer_handle,
9299 range,
9300 }
9301 }),
9302 None => None,
9303 };
9304 Ok(location)
9305 })
9306 }
9307
9308 pub fn find_all_references(
9309 &mut self,
9310 _: &FindAllReferences,
9311 cx: &mut ViewContext<Self>,
9312 ) -> Option<Task<Result<()>>> {
9313 let multi_buffer = self.buffer.read(cx);
9314 let selection = self.selections.newest::<usize>(cx);
9315 let head = selection.head();
9316
9317 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
9318 let head_anchor = multi_buffer_snapshot.anchor_at(
9319 head,
9320 if head < selection.tail() {
9321 Bias::Right
9322 } else {
9323 Bias::Left
9324 },
9325 );
9326
9327 match self
9328 .find_all_references_task_sources
9329 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
9330 {
9331 Ok(_) => {
9332 log::info!(
9333 "Ignoring repeated FindAllReferences invocation with the position of already running task"
9334 );
9335 return None;
9336 }
9337 Err(i) => {
9338 self.find_all_references_task_sources.insert(i, head_anchor);
9339 }
9340 }
9341
9342 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
9343 let replica_id = self.replica_id(cx);
9344 let workspace = self.workspace()?;
9345 let project = workspace.read(cx).project().clone();
9346 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
9347 Some(cx.spawn(|editor, mut cx| async move {
9348 let _cleanup = defer({
9349 let mut cx = cx.clone();
9350 move || {
9351 let _ = editor.update(&mut cx, |editor, _| {
9352 if let Ok(i) =
9353 editor
9354 .find_all_references_task_sources
9355 .binary_search_by(|anchor| {
9356 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
9357 })
9358 {
9359 editor.find_all_references_task_sources.remove(i);
9360 }
9361 });
9362 }
9363 });
9364
9365 let locations = references.await?;
9366 if locations.is_empty() {
9367 return anyhow::Ok(());
9368 }
9369
9370 workspace.update(&mut cx, |workspace, cx| {
9371 let title = locations
9372 .first()
9373 .as_ref()
9374 .map(|location| {
9375 let buffer = location.buffer.read(cx);
9376 format!(
9377 "References to `{}`",
9378 buffer
9379 .text_for_range(location.range.clone())
9380 .collect::<String>()
9381 )
9382 })
9383 .unwrap();
9384 Self::open_locations_in_multibuffer(
9385 workspace, locations, replica_id, title, false, cx,
9386 );
9387 })
9388 }))
9389 }
9390
9391 /// Opens a multibuffer with the given project locations in it
9392 pub fn open_locations_in_multibuffer(
9393 workspace: &mut Workspace,
9394 mut locations: Vec<Location>,
9395 replica_id: ReplicaId,
9396 title: String,
9397 split: bool,
9398 cx: &mut ViewContext<Workspace>,
9399 ) {
9400 // If there are multiple definitions, open them in a multibuffer
9401 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
9402 let mut locations = locations.into_iter().peekable();
9403 let mut ranges_to_highlight = Vec::new();
9404 let capability = workspace.project().read(cx).capability();
9405
9406 let excerpt_buffer = cx.new_model(|cx| {
9407 let mut multibuffer = MultiBuffer::new(replica_id, capability);
9408 while let Some(location) = locations.next() {
9409 let buffer = location.buffer.read(cx);
9410 let mut ranges_for_buffer = Vec::new();
9411 let range = location.range.to_offset(buffer);
9412 ranges_for_buffer.push(range.clone());
9413
9414 while let Some(next_location) = locations.peek() {
9415 if next_location.buffer == location.buffer {
9416 ranges_for_buffer.push(next_location.range.to_offset(buffer));
9417 locations.next();
9418 } else {
9419 break;
9420 }
9421 }
9422
9423 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
9424 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
9425 location.buffer.clone(),
9426 ranges_for_buffer,
9427 DEFAULT_MULTIBUFFER_CONTEXT,
9428 cx,
9429 ))
9430 }
9431
9432 multibuffer.with_title(title)
9433 });
9434
9435 let editor = cx.new_view(|cx| {
9436 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), true, cx)
9437 });
9438 editor.update(cx, |editor, cx| {
9439 if let Some(first_range) = ranges_to_highlight.first() {
9440 editor.change_selections(None, cx, |selections| {
9441 selections.clear_disjoint();
9442 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
9443 });
9444 }
9445 editor.highlight_background::<Self>(
9446 &ranges_to_highlight,
9447 |theme| theme.editor_highlighted_line_background,
9448 cx,
9449 );
9450 });
9451
9452 let item = Box::new(editor);
9453 let item_id = item.item_id();
9454
9455 if split {
9456 workspace.split_item(SplitDirection::Right, item.clone(), cx);
9457 } else {
9458 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
9459 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
9460 pane.close_current_preview_item(cx)
9461 } else {
9462 None
9463 }
9464 });
9465 workspace.add_item_to_active_pane(item.clone(), destination_index, true, cx);
9466 }
9467 workspace.active_pane().update(cx, |pane, cx| {
9468 pane.set_preview_item_id(Some(item_id), cx);
9469 });
9470 }
9471
9472 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9473 use language::ToOffset as _;
9474
9475 let project = self.project.clone()?;
9476 let selection = self.selections.newest_anchor().clone();
9477 let (cursor_buffer, cursor_buffer_position) = self
9478 .buffer
9479 .read(cx)
9480 .text_anchor_for_position(selection.head(), cx)?;
9481 let (tail_buffer, cursor_buffer_position_end) = self
9482 .buffer
9483 .read(cx)
9484 .text_anchor_for_position(selection.tail(), cx)?;
9485 if tail_buffer != cursor_buffer {
9486 return None;
9487 }
9488
9489 let snapshot = cursor_buffer.read(cx).snapshot();
9490 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
9491 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
9492 let prepare_rename = project.update(cx, |project, cx| {
9493 project.prepare_rename(cursor_buffer.clone(), cursor_buffer_offset, cx)
9494 });
9495 drop(snapshot);
9496
9497 Some(cx.spawn(|this, mut cx| async move {
9498 let rename_range = if let Some(range) = prepare_rename.await? {
9499 Some(range)
9500 } else {
9501 this.update(&mut cx, |this, cx| {
9502 let buffer = this.buffer.read(cx).snapshot(cx);
9503 let mut buffer_highlights = this
9504 .document_highlights_for_position(selection.head(), &buffer)
9505 .filter(|highlight| {
9506 highlight.start.excerpt_id == selection.head().excerpt_id
9507 && highlight.end.excerpt_id == selection.head().excerpt_id
9508 });
9509 buffer_highlights
9510 .next()
9511 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
9512 })?
9513 };
9514 if let Some(rename_range) = rename_range {
9515 this.update(&mut cx, |this, cx| {
9516 let snapshot = cursor_buffer.read(cx).snapshot();
9517 let rename_buffer_range = rename_range.to_offset(&snapshot);
9518 let cursor_offset_in_rename_range =
9519 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
9520 let cursor_offset_in_rename_range_end =
9521 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
9522
9523 this.take_rename(false, cx);
9524 let buffer = this.buffer.read(cx).read(cx);
9525 let cursor_offset = selection.head().to_offset(&buffer);
9526 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
9527 let rename_end = rename_start + rename_buffer_range.len();
9528 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
9529 let mut old_highlight_id = None;
9530 let old_name: Arc<str> = buffer
9531 .chunks(rename_start..rename_end, true)
9532 .map(|chunk| {
9533 if old_highlight_id.is_none() {
9534 old_highlight_id = chunk.syntax_highlight_id;
9535 }
9536 chunk.text
9537 })
9538 .collect::<String>()
9539 .into();
9540
9541 drop(buffer);
9542
9543 // Position the selection in the rename editor so that it matches the current selection.
9544 this.show_local_selections = false;
9545 let rename_editor = cx.new_view(|cx| {
9546 let mut editor = Editor::single_line(cx);
9547 editor.buffer.update(cx, |buffer, cx| {
9548 buffer.edit([(0..0, old_name.clone())], None, cx)
9549 });
9550 let rename_selection_range = match cursor_offset_in_rename_range
9551 .cmp(&cursor_offset_in_rename_range_end)
9552 {
9553 Ordering::Equal => {
9554 editor.select_all(&SelectAll, cx);
9555 return editor;
9556 }
9557 Ordering::Less => {
9558 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
9559 }
9560 Ordering::Greater => {
9561 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
9562 }
9563 };
9564 if rename_selection_range.end > old_name.len() {
9565 editor.select_all(&SelectAll, cx);
9566 } else {
9567 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
9568 s.select_ranges([rename_selection_range]);
9569 });
9570 }
9571 editor
9572 });
9573 cx.subscribe(&rename_editor, |_, _, e, cx| match e {
9574 EditorEvent::Focused => cx.emit(EditorEvent::FocusedIn),
9575 _ => {}
9576 })
9577 .detach();
9578
9579 let write_highlights =
9580 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
9581 let read_highlights =
9582 this.clear_background_highlights::<DocumentHighlightRead>(cx);
9583 let ranges = write_highlights
9584 .iter()
9585 .flat_map(|(_, ranges)| ranges.iter())
9586 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
9587 .cloned()
9588 .collect();
9589
9590 this.highlight_text::<Rename>(
9591 ranges,
9592 HighlightStyle {
9593 fade_out: Some(0.6),
9594 ..Default::default()
9595 },
9596 cx,
9597 );
9598 let rename_focus_handle = rename_editor.focus_handle(cx);
9599 cx.focus(&rename_focus_handle);
9600 let block_id = this.insert_blocks(
9601 [BlockProperties {
9602 style: BlockStyle::Flex,
9603 position: range.start,
9604 height: 1,
9605 render: Box::new({
9606 let rename_editor = rename_editor.clone();
9607 move |cx: &mut BlockContext| {
9608 let mut text_style = cx.editor_style.text.clone();
9609 if let Some(highlight_style) = old_highlight_id
9610 .and_then(|h| h.style(&cx.editor_style.syntax))
9611 {
9612 text_style = text_style.highlight(highlight_style);
9613 }
9614 div()
9615 .pl(cx.anchor_x)
9616 .child(EditorElement::new(
9617 &rename_editor,
9618 EditorStyle {
9619 background: cx.theme().system().transparent,
9620 local_player: cx.editor_style.local_player,
9621 text: text_style,
9622 scrollbar_width: cx.editor_style.scrollbar_width,
9623 syntax: cx.editor_style.syntax.clone(),
9624 status: cx.editor_style.status.clone(),
9625 inlay_hints_style: HighlightStyle {
9626 color: Some(cx.theme().status().hint),
9627 font_weight: Some(FontWeight::BOLD),
9628 ..HighlightStyle::default()
9629 },
9630 suggestions_style: HighlightStyle {
9631 color: Some(cx.theme().status().predictive),
9632 ..HighlightStyle::default()
9633 },
9634 },
9635 ))
9636 .into_any_element()
9637 }
9638 }),
9639 disposition: BlockDisposition::Below,
9640 priority: 0,
9641 }],
9642 Some(Autoscroll::fit()),
9643 cx,
9644 )[0];
9645 this.pending_rename = Some(RenameState {
9646 range,
9647 old_name,
9648 editor: rename_editor,
9649 block_id,
9650 });
9651 })?;
9652 }
9653
9654 Ok(())
9655 }))
9656 }
9657
9658 pub fn confirm_rename(
9659 &mut self,
9660 _: &ConfirmRename,
9661 cx: &mut ViewContext<Self>,
9662 ) -> Option<Task<Result<()>>> {
9663 let rename = self.take_rename(false, cx)?;
9664 let workspace = self.workspace()?;
9665 let (start_buffer, start) = self
9666 .buffer
9667 .read(cx)
9668 .text_anchor_for_position(rename.range.start, cx)?;
9669 let (end_buffer, end) = self
9670 .buffer
9671 .read(cx)
9672 .text_anchor_for_position(rename.range.end, cx)?;
9673 if start_buffer != end_buffer {
9674 return None;
9675 }
9676
9677 let buffer = start_buffer;
9678 let range = start..end;
9679 let old_name = rename.old_name;
9680 let new_name = rename.editor.read(cx).text(cx);
9681
9682 let rename = workspace
9683 .read(cx)
9684 .project()
9685 .clone()
9686 .update(cx, |project, cx| {
9687 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
9688 });
9689 let workspace = workspace.downgrade();
9690
9691 Some(cx.spawn(|editor, mut cx| async move {
9692 let project_transaction = rename.await?;
9693 Self::open_project_transaction(
9694 &editor,
9695 workspace,
9696 project_transaction,
9697 format!("Rename: {} → {}", old_name, new_name),
9698 cx.clone(),
9699 )
9700 .await?;
9701
9702 editor.update(&mut cx, |editor, cx| {
9703 editor.refresh_document_highlights(cx);
9704 })?;
9705 Ok(())
9706 }))
9707 }
9708
9709 fn take_rename(
9710 &mut self,
9711 moving_cursor: bool,
9712 cx: &mut ViewContext<Self>,
9713 ) -> Option<RenameState> {
9714 let rename = self.pending_rename.take()?;
9715 if rename.editor.focus_handle(cx).is_focused(cx) {
9716 cx.focus(&self.focus_handle);
9717 }
9718
9719 self.remove_blocks(
9720 [rename.block_id].into_iter().collect(),
9721 Some(Autoscroll::fit()),
9722 cx,
9723 );
9724 self.clear_highlights::<Rename>(cx);
9725 self.show_local_selections = true;
9726
9727 if moving_cursor {
9728 let rename_editor = rename.editor.read(cx);
9729 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
9730
9731 // Update the selection to match the position of the selection inside
9732 // the rename editor.
9733 let snapshot = self.buffer.read(cx).read(cx);
9734 let rename_range = rename.range.to_offset(&snapshot);
9735 let cursor_in_editor = snapshot
9736 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
9737 .min(rename_range.end);
9738 drop(snapshot);
9739
9740 self.change_selections(None, cx, |s| {
9741 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
9742 });
9743 } else {
9744 self.refresh_document_highlights(cx);
9745 }
9746
9747 Some(rename)
9748 }
9749
9750 pub fn pending_rename(&self) -> Option<&RenameState> {
9751 self.pending_rename.as_ref()
9752 }
9753
9754 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9755 let project = match &self.project {
9756 Some(project) => project.clone(),
9757 None => return None,
9758 };
9759
9760 Some(self.perform_format(project, FormatTrigger::Manual, cx))
9761 }
9762
9763 fn perform_format(
9764 &mut self,
9765 project: Model<Project>,
9766 trigger: FormatTrigger,
9767 cx: &mut ViewContext<Self>,
9768 ) -> Task<Result<()>> {
9769 let buffer = self.buffer().clone();
9770 let mut buffers = buffer.read(cx).all_buffers();
9771 if trigger == FormatTrigger::Save {
9772 buffers.retain(|buffer| buffer.read(cx).is_dirty());
9773 }
9774
9775 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
9776 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
9777
9778 cx.spawn(|_, mut cx| async move {
9779 let transaction = futures::select_biased! {
9780 () = timeout => {
9781 log::warn!("timed out waiting for formatting");
9782 None
9783 }
9784 transaction = format.log_err().fuse() => transaction,
9785 };
9786
9787 buffer
9788 .update(&mut cx, |buffer, cx| {
9789 if let Some(transaction) = transaction {
9790 if !buffer.is_singleton() {
9791 buffer.push_transaction(&transaction.0, cx);
9792 }
9793 }
9794
9795 cx.notify();
9796 })
9797 .ok();
9798
9799 Ok(())
9800 })
9801 }
9802
9803 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
9804 if let Some(project) = self.project.clone() {
9805 self.buffer.update(cx, |multi_buffer, cx| {
9806 project.update(cx, |project, cx| {
9807 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
9808 });
9809 })
9810 }
9811 }
9812
9813 fn cancel_language_server_work(
9814 &mut self,
9815 _: &CancelLanguageServerWork,
9816 cx: &mut ViewContext<Self>,
9817 ) {
9818 if let Some(project) = self.project.clone() {
9819 self.buffer.update(cx, |multi_buffer, cx| {
9820 project.update(cx, |project, cx| {
9821 project.cancel_language_server_work_for_buffers(multi_buffer.all_buffers(), cx);
9822 });
9823 })
9824 }
9825 }
9826
9827 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
9828 cx.show_character_palette();
9829 }
9830
9831 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
9832 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
9833 let buffer = self.buffer.read(cx).snapshot(cx);
9834 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
9835 let is_valid = buffer
9836 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
9837 .any(|entry| {
9838 entry.diagnostic.is_primary
9839 && !entry.range.is_empty()
9840 && entry.range.start == primary_range_start
9841 && entry.diagnostic.message == active_diagnostics.primary_message
9842 });
9843
9844 if is_valid != active_diagnostics.is_valid {
9845 active_diagnostics.is_valid = is_valid;
9846 let mut new_styles = HashMap::default();
9847 for (block_id, diagnostic) in &active_diagnostics.blocks {
9848 new_styles.insert(
9849 *block_id,
9850 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
9851 );
9852 }
9853 self.display_map.update(cx, |display_map, _cx| {
9854 display_map.replace_blocks(new_styles)
9855 });
9856 }
9857 }
9858 }
9859
9860 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
9861 self.dismiss_diagnostics(cx);
9862 let snapshot = self.snapshot(cx);
9863 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
9864 let buffer = self.buffer.read(cx).snapshot(cx);
9865
9866 let mut primary_range = None;
9867 let mut primary_message = None;
9868 let mut group_end = Point::zero();
9869 let diagnostic_group = buffer
9870 .diagnostic_group::<MultiBufferPoint>(group_id)
9871 .filter_map(|entry| {
9872 if snapshot.is_line_folded(MultiBufferRow(entry.range.start.row))
9873 && (entry.range.start.row == entry.range.end.row
9874 || snapshot.is_line_folded(MultiBufferRow(entry.range.end.row)))
9875 {
9876 return None;
9877 }
9878 if entry.range.end > group_end {
9879 group_end = entry.range.end;
9880 }
9881 if entry.diagnostic.is_primary {
9882 primary_range = Some(entry.range.clone());
9883 primary_message = Some(entry.diagnostic.message.clone());
9884 }
9885 Some(entry)
9886 })
9887 .collect::<Vec<_>>();
9888 let primary_range = primary_range?;
9889 let primary_message = primary_message?;
9890 let primary_range =
9891 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
9892
9893 let blocks = display_map
9894 .insert_blocks(
9895 diagnostic_group.iter().map(|entry| {
9896 let diagnostic = entry.diagnostic.clone();
9897 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
9898 BlockProperties {
9899 style: BlockStyle::Fixed,
9900 position: buffer.anchor_after(entry.range.start),
9901 height: message_height,
9902 render: diagnostic_block_renderer(diagnostic, None, true, true),
9903 disposition: BlockDisposition::Below,
9904 priority: 0,
9905 }
9906 }),
9907 cx,
9908 )
9909 .into_iter()
9910 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
9911 .collect();
9912
9913 Some(ActiveDiagnosticGroup {
9914 primary_range,
9915 primary_message,
9916 group_id,
9917 blocks,
9918 is_valid: true,
9919 })
9920 });
9921 self.active_diagnostics.is_some()
9922 }
9923
9924 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
9925 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
9926 self.display_map.update(cx, |display_map, cx| {
9927 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
9928 });
9929 cx.notify();
9930 }
9931 }
9932
9933 pub fn set_selections_from_remote(
9934 &mut self,
9935 selections: Vec<Selection<Anchor>>,
9936 pending_selection: Option<Selection<Anchor>>,
9937 cx: &mut ViewContext<Self>,
9938 ) {
9939 let old_cursor_position = self.selections.newest_anchor().head();
9940 self.selections.change_with(cx, |s| {
9941 s.select_anchors(selections);
9942 if let Some(pending_selection) = pending_selection {
9943 s.set_pending(pending_selection, SelectMode::Character);
9944 } else {
9945 s.clear_pending();
9946 }
9947 });
9948 self.selections_did_change(false, &old_cursor_position, true, cx);
9949 }
9950
9951 fn push_to_selection_history(&mut self) {
9952 self.selection_history.push(SelectionHistoryEntry {
9953 selections: self.selections.disjoint_anchors(),
9954 select_next_state: self.select_next_state.clone(),
9955 select_prev_state: self.select_prev_state.clone(),
9956 add_selections_state: self.add_selections_state.clone(),
9957 });
9958 }
9959
9960 pub fn transact(
9961 &mut self,
9962 cx: &mut ViewContext<Self>,
9963 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
9964 ) -> Option<TransactionId> {
9965 self.start_transaction_at(Instant::now(), cx);
9966 update(self, cx);
9967 self.end_transaction_at(Instant::now(), cx)
9968 }
9969
9970 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
9971 self.end_selection(cx);
9972 if let Some(tx_id) = self
9973 .buffer
9974 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
9975 {
9976 self.selection_history
9977 .insert_transaction(tx_id, self.selections.disjoint_anchors());
9978 cx.emit(EditorEvent::TransactionBegun {
9979 transaction_id: tx_id,
9980 })
9981 }
9982 }
9983
9984 fn end_transaction_at(
9985 &mut self,
9986 now: Instant,
9987 cx: &mut ViewContext<Self>,
9988 ) -> Option<TransactionId> {
9989 if let Some(transaction_id) = self
9990 .buffer
9991 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
9992 {
9993 if let Some((_, end_selections)) =
9994 self.selection_history.transaction_mut(transaction_id)
9995 {
9996 *end_selections = Some(self.selections.disjoint_anchors());
9997 } else {
9998 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
9999 }
10000
10001 cx.emit(EditorEvent::Edited { transaction_id });
10002 Some(transaction_id)
10003 } else {
10004 None
10005 }
10006 }
10007
10008 pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
10009 let mut fold_ranges = Vec::new();
10010
10011 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10012
10013 let selections = self.selections.all_adjusted(cx);
10014 for selection in selections {
10015 let range = selection.range().sorted();
10016 let buffer_start_row = range.start.row;
10017
10018 for row in (0..=range.end.row).rev() {
10019 if let Some((foldable_range, fold_text)) =
10020 display_map.foldable_range(MultiBufferRow(row))
10021 {
10022 if foldable_range.end.row >= buffer_start_row {
10023 fold_ranges.push((foldable_range, fold_text));
10024 if row <= range.start.row {
10025 break;
10026 }
10027 }
10028 }
10029 }
10030 }
10031
10032 self.fold_ranges(fold_ranges, true, cx);
10033 }
10034
10035 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
10036 let buffer_row = fold_at.buffer_row;
10037 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10038
10039 if let Some((fold_range, placeholder)) = display_map.foldable_range(buffer_row) {
10040 let autoscroll = self
10041 .selections
10042 .all::<Point>(cx)
10043 .iter()
10044 .any(|selection| fold_range.overlaps(&selection.range()));
10045
10046 self.fold_ranges([(fold_range, placeholder)], autoscroll, cx);
10047 }
10048 }
10049
10050 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
10051 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10052 let buffer = &display_map.buffer_snapshot;
10053 let selections = self.selections.all::<Point>(cx);
10054 let ranges = selections
10055 .iter()
10056 .map(|s| {
10057 let range = s.display_range(&display_map).sorted();
10058 let mut start = range.start.to_point(&display_map);
10059 let mut end = range.end.to_point(&display_map);
10060 start.column = 0;
10061 end.column = buffer.line_len(MultiBufferRow(end.row));
10062 start..end
10063 })
10064 .collect::<Vec<_>>();
10065
10066 self.unfold_ranges(ranges, true, true, cx);
10067 }
10068
10069 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
10070 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10071
10072 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
10073 ..Point::new(
10074 unfold_at.buffer_row.0,
10075 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
10076 );
10077
10078 let autoscroll = self
10079 .selections
10080 .all::<Point>(cx)
10081 .iter()
10082 .any(|selection| selection.range().overlaps(&intersection_range));
10083
10084 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
10085 }
10086
10087 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
10088 let selections = self.selections.all::<Point>(cx);
10089 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10090 let line_mode = self.selections.line_mode;
10091 let ranges = selections.into_iter().map(|s| {
10092 if line_mode {
10093 let start = Point::new(s.start.row, 0);
10094 let end = Point::new(
10095 s.end.row,
10096 display_map
10097 .buffer_snapshot
10098 .line_len(MultiBufferRow(s.end.row)),
10099 );
10100 (start..end, display_map.fold_placeholder.clone())
10101 } else {
10102 (s.start..s.end, display_map.fold_placeholder.clone())
10103 }
10104 });
10105 self.fold_ranges(ranges, true, cx);
10106 }
10107
10108 pub fn fold_ranges<T: ToOffset + Clone>(
10109 &mut self,
10110 ranges: impl IntoIterator<Item = (Range<T>, FoldPlaceholder)>,
10111 auto_scroll: bool,
10112 cx: &mut ViewContext<Self>,
10113 ) {
10114 let mut fold_ranges = Vec::new();
10115 let mut buffers_affected = HashMap::default();
10116 let multi_buffer = self.buffer().read(cx);
10117 for (fold_range, fold_text) in ranges {
10118 if let Some((_, buffer, _)) =
10119 multi_buffer.excerpt_containing(fold_range.start.clone(), cx)
10120 {
10121 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
10122 };
10123 fold_ranges.push((fold_range, fold_text));
10124 }
10125
10126 let mut ranges = fold_ranges.into_iter().peekable();
10127 if ranges.peek().is_some() {
10128 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
10129
10130 if auto_scroll {
10131 self.request_autoscroll(Autoscroll::fit(), cx);
10132 }
10133
10134 for buffer in buffers_affected.into_values() {
10135 self.sync_expanded_diff_hunks(buffer, cx);
10136 }
10137
10138 cx.notify();
10139
10140 if let Some(active_diagnostics) = self.active_diagnostics.take() {
10141 // Clear diagnostics block when folding a range that contains it.
10142 let snapshot = self.snapshot(cx);
10143 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
10144 drop(snapshot);
10145 self.active_diagnostics = Some(active_diagnostics);
10146 self.dismiss_diagnostics(cx);
10147 } else {
10148 self.active_diagnostics = Some(active_diagnostics);
10149 }
10150 }
10151
10152 self.scrollbar_marker_state.dirty = true;
10153 }
10154 }
10155
10156 pub fn unfold_ranges<T: ToOffset + Clone>(
10157 &mut self,
10158 ranges: impl IntoIterator<Item = Range<T>>,
10159 inclusive: bool,
10160 auto_scroll: bool,
10161 cx: &mut ViewContext<Self>,
10162 ) {
10163 let mut unfold_ranges = Vec::new();
10164 let mut buffers_affected = HashMap::default();
10165 let multi_buffer = self.buffer().read(cx);
10166 for range in ranges {
10167 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
10168 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
10169 };
10170 unfold_ranges.push(range);
10171 }
10172
10173 let mut ranges = unfold_ranges.into_iter().peekable();
10174 if ranges.peek().is_some() {
10175 self.display_map
10176 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
10177 if auto_scroll {
10178 self.request_autoscroll(Autoscroll::fit(), cx);
10179 }
10180
10181 for buffer in buffers_affected.into_values() {
10182 self.sync_expanded_diff_hunks(buffer, cx);
10183 }
10184
10185 cx.notify();
10186 self.scrollbar_marker_state.dirty = true;
10187 self.active_indent_guides_state.dirty = true;
10188 }
10189 }
10190
10191 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
10192 if hovered != self.gutter_hovered {
10193 self.gutter_hovered = hovered;
10194 cx.notify();
10195 }
10196 }
10197
10198 pub fn insert_blocks(
10199 &mut self,
10200 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
10201 autoscroll: Option<Autoscroll>,
10202 cx: &mut ViewContext<Self>,
10203 ) -> Vec<CustomBlockId> {
10204 let blocks = self
10205 .display_map
10206 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
10207 if let Some(autoscroll) = autoscroll {
10208 self.request_autoscroll(autoscroll, cx);
10209 }
10210 cx.notify();
10211 blocks
10212 }
10213
10214 pub fn resize_blocks(
10215 &mut self,
10216 heights: HashMap<CustomBlockId, u32>,
10217 autoscroll: Option<Autoscroll>,
10218 cx: &mut ViewContext<Self>,
10219 ) {
10220 self.display_map
10221 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
10222 if let Some(autoscroll) = autoscroll {
10223 self.request_autoscroll(autoscroll, cx);
10224 }
10225 cx.notify();
10226 }
10227
10228 pub fn replace_blocks(
10229 &mut self,
10230 renderers: HashMap<CustomBlockId, RenderBlock>,
10231 autoscroll: Option<Autoscroll>,
10232 cx: &mut ViewContext<Self>,
10233 ) {
10234 self.display_map
10235 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
10236 if let Some(autoscroll) = autoscroll {
10237 self.request_autoscroll(autoscroll, cx);
10238 }
10239 cx.notify();
10240 }
10241
10242 pub fn remove_blocks(
10243 &mut self,
10244 block_ids: HashSet<CustomBlockId>,
10245 autoscroll: Option<Autoscroll>,
10246 cx: &mut ViewContext<Self>,
10247 ) {
10248 self.display_map.update(cx, |display_map, cx| {
10249 display_map.remove_blocks(block_ids, cx)
10250 });
10251 if let Some(autoscroll) = autoscroll {
10252 self.request_autoscroll(autoscroll, cx);
10253 }
10254 cx.notify();
10255 }
10256
10257 pub fn row_for_block(
10258 &self,
10259 block_id: CustomBlockId,
10260 cx: &mut ViewContext<Self>,
10261 ) -> Option<DisplayRow> {
10262 self.display_map
10263 .update(cx, |map, cx| map.row_for_block(block_id, cx))
10264 }
10265
10266 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
10267 self.focused_block = Some(focused_block);
10268 }
10269
10270 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
10271 self.focused_block.take()
10272 }
10273
10274 pub fn insert_creases(
10275 &mut self,
10276 creases: impl IntoIterator<Item = Crease>,
10277 cx: &mut ViewContext<Self>,
10278 ) -> Vec<CreaseId> {
10279 self.display_map
10280 .update(cx, |map, cx| map.insert_creases(creases, cx))
10281 }
10282
10283 pub fn remove_creases(
10284 &mut self,
10285 ids: impl IntoIterator<Item = CreaseId>,
10286 cx: &mut ViewContext<Self>,
10287 ) {
10288 self.display_map
10289 .update(cx, |map, cx| map.remove_creases(ids, cx));
10290 }
10291
10292 pub fn longest_row(&self, cx: &mut AppContext) -> DisplayRow {
10293 self.display_map
10294 .update(cx, |map, cx| map.snapshot(cx))
10295 .longest_row()
10296 }
10297
10298 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
10299 self.display_map
10300 .update(cx, |map, cx| map.snapshot(cx))
10301 .max_point()
10302 }
10303
10304 pub fn text(&self, cx: &AppContext) -> String {
10305 self.buffer.read(cx).read(cx).text()
10306 }
10307
10308 pub fn text_option(&self, cx: &AppContext) -> Option<String> {
10309 let text = self.text(cx);
10310 let text = text.trim();
10311
10312 if text.is_empty() {
10313 return None;
10314 }
10315
10316 Some(text.to_string())
10317 }
10318
10319 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
10320 self.transact(cx, |this, cx| {
10321 this.buffer
10322 .read(cx)
10323 .as_singleton()
10324 .expect("you can only call set_text on editors for singleton buffers")
10325 .update(cx, |buffer, cx| buffer.set_text(text, cx));
10326 });
10327 }
10328
10329 pub fn display_text(&self, cx: &mut AppContext) -> String {
10330 self.display_map
10331 .update(cx, |map, cx| map.snapshot(cx))
10332 .text()
10333 }
10334
10335 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
10336 let mut wrap_guides = smallvec::smallvec![];
10337
10338 if self.show_wrap_guides == Some(false) {
10339 return wrap_guides;
10340 }
10341
10342 let settings = self.buffer.read(cx).settings_at(0, cx);
10343 if settings.show_wrap_guides {
10344 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
10345 wrap_guides.push((soft_wrap as usize, true));
10346 }
10347 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
10348 }
10349
10350 wrap_guides
10351 }
10352
10353 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
10354 let settings = self.buffer.read(cx).settings_at(0, cx);
10355 let mode = self
10356 .soft_wrap_mode_override
10357 .unwrap_or_else(|| settings.soft_wrap);
10358 match mode {
10359 language_settings::SoftWrap::None => SoftWrap::None,
10360 language_settings::SoftWrap::PreferLine => SoftWrap::PreferLine,
10361 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
10362 language_settings::SoftWrap::PreferredLineLength => {
10363 SoftWrap::Column(settings.preferred_line_length)
10364 }
10365 }
10366 }
10367
10368 pub fn set_soft_wrap_mode(
10369 &mut self,
10370 mode: language_settings::SoftWrap,
10371 cx: &mut ViewContext<Self>,
10372 ) {
10373 self.soft_wrap_mode_override = Some(mode);
10374 cx.notify();
10375 }
10376
10377 pub fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
10378 let rem_size = cx.rem_size();
10379 self.display_map.update(cx, |map, cx| {
10380 map.set_font(
10381 style.text.font(),
10382 style.text.font_size.to_pixels(rem_size),
10383 cx,
10384 )
10385 });
10386 self.style = Some(style);
10387 }
10388
10389 pub fn style(&self) -> Option<&EditorStyle> {
10390 self.style.as_ref()
10391 }
10392
10393 // Called by the element. This method is not designed to be called outside of the editor
10394 // element's layout code because it does not notify when rewrapping is computed synchronously.
10395 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
10396 self.display_map
10397 .update(cx, |map, cx| map.set_wrap_width(width, cx))
10398 }
10399
10400 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
10401 if self.soft_wrap_mode_override.is_some() {
10402 self.soft_wrap_mode_override.take();
10403 } else {
10404 let soft_wrap = match self.soft_wrap_mode(cx) {
10405 SoftWrap::None | SoftWrap::PreferLine => language_settings::SoftWrap::EditorWidth,
10406 SoftWrap::EditorWidth | SoftWrap::Column(_) => {
10407 language_settings::SoftWrap::PreferLine
10408 }
10409 };
10410 self.soft_wrap_mode_override = Some(soft_wrap);
10411 }
10412 cx.notify();
10413 }
10414
10415 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, cx: &mut ViewContext<Self>) {
10416 let Some(workspace) = self.workspace() else {
10417 return;
10418 };
10419 let fs = workspace.read(cx).app_state().fs.clone();
10420 let current_show = TabBarSettings::get_global(cx).show;
10421 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
10422 setting.show = Some(!current_show);
10423 });
10424 }
10425
10426 pub fn toggle_indent_guides(&mut self, _: &ToggleIndentGuides, cx: &mut ViewContext<Self>) {
10427 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
10428 self.buffer
10429 .read(cx)
10430 .settings_at(0, cx)
10431 .indent_guides
10432 .enabled
10433 });
10434 self.show_indent_guides = Some(!currently_enabled);
10435 cx.notify();
10436 }
10437
10438 fn should_show_indent_guides(&self) -> Option<bool> {
10439 self.show_indent_guides
10440 }
10441
10442 pub fn toggle_line_numbers(&mut self, _: &ToggleLineNumbers, cx: &mut ViewContext<Self>) {
10443 let mut editor_settings = EditorSettings::get_global(cx).clone();
10444 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
10445 EditorSettings::override_global(editor_settings, cx);
10446 }
10447
10448 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
10449 self.show_gutter = show_gutter;
10450 cx.notify();
10451 }
10452
10453 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut ViewContext<Self>) {
10454 self.show_line_numbers = Some(show_line_numbers);
10455 cx.notify();
10456 }
10457
10458 pub fn set_show_git_diff_gutter(
10459 &mut self,
10460 show_git_diff_gutter: bool,
10461 cx: &mut ViewContext<Self>,
10462 ) {
10463 self.show_git_diff_gutter = Some(show_git_diff_gutter);
10464 cx.notify();
10465 }
10466
10467 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut ViewContext<Self>) {
10468 self.show_code_actions = Some(show_code_actions);
10469 cx.notify();
10470 }
10471
10472 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut ViewContext<Self>) {
10473 self.show_runnables = Some(show_runnables);
10474 cx.notify();
10475 }
10476
10477 pub fn set_masked(&mut self, masked: bool, cx: &mut ViewContext<Self>) {
10478 if self.display_map.read(cx).masked != masked {
10479 self.display_map.update(cx, |map, _| map.masked = masked);
10480 }
10481 cx.notify()
10482 }
10483
10484 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut ViewContext<Self>) {
10485 self.show_wrap_guides = Some(show_wrap_guides);
10486 cx.notify();
10487 }
10488
10489 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut ViewContext<Self>) {
10490 self.show_indent_guides = Some(show_indent_guides);
10491 cx.notify();
10492 }
10493
10494 pub fn working_directory(&self, cx: &WindowContext) -> Option<PathBuf> {
10495 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10496 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10497 if let Some(dir) = file.abs_path(cx).parent() {
10498 return Some(dir.to_owned());
10499 }
10500 }
10501
10502 if let Some(project_path) = buffer.read(cx).project_path(cx) {
10503 return Some(project_path.path.to_path_buf());
10504 }
10505 }
10506
10507 None
10508 }
10509
10510 pub fn reveal_in_finder(&mut self, _: &RevealInFileManager, cx: &mut ViewContext<Self>) {
10511 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10512 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10513 cx.reveal_path(&file.abs_path(cx));
10514 }
10515 }
10516 }
10517
10518 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
10519 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10520 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10521 if let Some(path) = file.abs_path(cx).to_str() {
10522 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
10523 }
10524 }
10525 }
10526 }
10527
10528 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
10529 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10530 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10531 if let Some(path) = file.path().to_str() {
10532 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
10533 }
10534 }
10535 }
10536 }
10537
10538 pub fn toggle_git_blame(&mut self, _: &ToggleGitBlame, cx: &mut ViewContext<Self>) {
10539 self.show_git_blame_gutter = !self.show_git_blame_gutter;
10540
10541 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
10542 self.start_git_blame(true, cx);
10543 }
10544
10545 cx.notify();
10546 }
10547
10548 pub fn toggle_git_blame_inline(
10549 &mut self,
10550 _: &ToggleGitBlameInline,
10551 cx: &mut ViewContext<Self>,
10552 ) {
10553 self.toggle_git_blame_inline_internal(true, cx);
10554 cx.notify();
10555 }
10556
10557 pub fn git_blame_inline_enabled(&self) -> bool {
10558 self.git_blame_inline_enabled
10559 }
10560
10561 pub fn toggle_selection_menu(&mut self, _: &ToggleSelectionMenu, cx: &mut ViewContext<Self>) {
10562 self.show_selection_menu = self
10563 .show_selection_menu
10564 .map(|show_selections_menu| !show_selections_menu)
10565 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
10566
10567 cx.notify();
10568 }
10569
10570 pub fn selection_menu_enabled(&self, cx: &AppContext) -> bool {
10571 self.show_selection_menu
10572 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
10573 }
10574
10575 fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
10576 if let Some(project) = self.project.as_ref() {
10577 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
10578 return;
10579 };
10580
10581 if buffer.read(cx).file().is_none() {
10582 return;
10583 }
10584
10585 let focused = self.focus_handle(cx).contains_focused(cx);
10586
10587 let project = project.clone();
10588 let blame =
10589 cx.new_model(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
10590 self.blame_subscription = Some(cx.observe(&blame, |_, _, cx| cx.notify()));
10591 self.blame = Some(blame);
10592 }
10593 }
10594
10595 fn toggle_git_blame_inline_internal(
10596 &mut self,
10597 user_triggered: bool,
10598 cx: &mut ViewContext<Self>,
10599 ) {
10600 if self.git_blame_inline_enabled {
10601 self.git_blame_inline_enabled = false;
10602 self.show_git_blame_inline = false;
10603 self.show_git_blame_inline_delay_task.take();
10604 } else {
10605 self.git_blame_inline_enabled = true;
10606 self.start_git_blame_inline(user_triggered, cx);
10607 }
10608
10609 cx.notify();
10610 }
10611
10612 fn start_git_blame_inline(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
10613 self.start_git_blame(user_triggered, cx);
10614
10615 if ProjectSettings::get_global(cx)
10616 .git
10617 .inline_blame_delay()
10618 .is_some()
10619 {
10620 self.start_inline_blame_timer(cx);
10621 } else {
10622 self.show_git_blame_inline = true
10623 }
10624 }
10625
10626 pub fn blame(&self) -> Option<&Model<GitBlame>> {
10627 self.blame.as_ref()
10628 }
10629
10630 pub fn render_git_blame_gutter(&mut self, cx: &mut WindowContext) -> bool {
10631 self.show_git_blame_gutter && self.has_blame_entries(cx)
10632 }
10633
10634 pub fn render_git_blame_inline(&mut self, cx: &mut WindowContext) -> bool {
10635 self.show_git_blame_inline
10636 && self.focus_handle.is_focused(cx)
10637 && !self.newest_selection_head_on_empty_line(cx)
10638 && self.has_blame_entries(cx)
10639 }
10640
10641 fn has_blame_entries(&self, cx: &mut WindowContext) -> bool {
10642 self.blame()
10643 .map_or(false, |blame| blame.read(cx).has_generated_entries())
10644 }
10645
10646 fn newest_selection_head_on_empty_line(&mut self, cx: &mut WindowContext) -> bool {
10647 let cursor_anchor = self.selections.newest_anchor().head();
10648
10649 let snapshot = self.buffer.read(cx).snapshot(cx);
10650 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
10651
10652 snapshot.line_len(buffer_row) == 0
10653 }
10654
10655 fn get_permalink_to_line(&mut self, cx: &mut ViewContext<Self>) -> Result<url::Url> {
10656 let (path, selection, repo) = maybe!({
10657 let project_handle = self.project.as_ref()?.clone();
10658 let project = project_handle.read(cx);
10659
10660 let selection = self.selections.newest::<Point>(cx);
10661 let selection_range = selection.range();
10662
10663 let (buffer, selection) = if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10664 (buffer, selection_range.start.row..selection_range.end.row)
10665 } else {
10666 let buffer_ranges = self
10667 .buffer()
10668 .read(cx)
10669 .range_to_buffer_ranges(selection_range, cx);
10670
10671 let (buffer, range, _) = if selection.reversed {
10672 buffer_ranges.first()
10673 } else {
10674 buffer_ranges.last()
10675 }?;
10676
10677 let snapshot = buffer.read(cx).snapshot();
10678 let selection = text::ToPoint::to_point(&range.start, &snapshot).row
10679 ..text::ToPoint::to_point(&range.end, &snapshot).row;
10680 (buffer.clone(), selection)
10681 };
10682
10683 let path = buffer
10684 .read(cx)
10685 .file()?
10686 .as_local()?
10687 .path()
10688 .to_str()?
10689 .to_string();
10690 let repo = project.get_repo(&buffer.read(cx).project_path(cx)?, cx)?;
10691 Some((path, selection, repo))
10692 })
10693 .ok_or_else(|| anyhow!("unable to open git repository"))?;
10694
10695 const REMOTE_NAME: &str = "origin";
10696 let origin_url = repo
10697 .remote_url(REMOTE_NAME)
10698 .ok_or_else(|| anyhow!("remote \"{REMOTE_NAME}\" not found"))?;
10699 let sha = repo
10700 .head_sha()
10701 .ok_or_else(|| anyhow!("failed to read HEAD SHA"))?;
10702
10703 let (provider, remote) =
10704 parse_git_remote_url(GitHostingProviderRegistry::default_global(cx), &origin_url)
10705 .ok_or_else(|| anyhow!("failed to parse Git remote URL"))?;
10706
10707 Ok(provider.build_permalink(
10708 remote,
10709 BuildPermalinkParams {
10710 sha: &sha,
10711 path: &path,
10712 selection: Some(selection),
10713 },
10714 ))
10715 }
10716
10717 pub fn copy_permalink_to_line(&mut self, _: &CopyPermalinkToLine, cx: &mut ViewContext<Self>) {
10718 let permalink = self.get_permalink_to_line(cx);
10719
10720 match permalink {
10721 Ok(permalink) => {
10722 cx.write_to_clipboard(ClipboardItem::new(permalink.to_string()));
10723 }
10724 Err(err) => {
10725 let message = format!("Failed to copy permalink: {err}");
10726
10727 Err::<(), anyhow::Error>(err).log_err();
10728
10729 if let Some(workspace) = self.workspace() {
10730 workspace.update(cx, |workspace, cx| {
10731 struct CopyPermalinkToLine;
10732
10733 workspace.show_toast(
10734 Toast::new(NotificationId::unique::<CopyPermalinkToLine>(), message),
10735 cx,
10736 )
10737 })
10738 }
10739 }
10740 }
10741 }
10742
10743 pub fn open_permalink_to_line(&mut self, _: &OpenPermalinkToLine, cx: &mut ViewContext<Self>) {
10744 let permalink = self.get_permalink_to_line(cx);
10745
10746 match permalink {
10747 Ok(permalink) => {
10748 cx.open_url(permalink.as_ref());
10749 }
10750 Err(err) => {
10751 let message = format!("Failed to open permalink: {err}");
10752
10753 Err::<(), anyhow::Error>(err).log_err();
10754
10755 if let Some(workspace) = self.workspace() {
10756 workspace.update(cx, |workspace, cx| {
10757 struct OpenPermalinkToLine;
10758
10759 workspace.show_toast(
10760 Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
10761 cx,
10762 )
10763 })
10764 }
10765 }
10766 }
10767 }
10768
10769 /// Adds or removes (on `None` color) a highlight for the rows corresponding to the anchor range given.
10770 /// On matching anchor range, replaces the old highlight; does not clear the other existing highlights.
10771 /// If multiple anchor ranges will produce highlights for the same row, the last range added will be used.
10772 pub fn highlight_rows<T: 'static>(
10773 &mut self,
10774 rows: RangeInclusive<Anchor>,
10775 color: Option<Hsla>,
10776 should_autoscroll: bool,
10777 cx: &mut ViewContext<Self>,
10778 ) {
10779 let snapshot = self.buffer().read(cx).snapshot(cx);
10780 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
10781 let existing_highlight_index = row_highlights.binary_search_by(|highlight| {
10782 highlight
10783 .range
10784 .start()
10785 .cmp(&rows.start(), &snapshot)
10786 .then(highlight.range.end().cmp(&rows.end(), &snapshot))
10787 });
10788 match (color, existing_highlight_index) {
10789 (Some(_), Ok(ix)) | (_, Err(ix)) => row_highlights.insert(
10790 ix,
10791 RowHighlight {
10792 index: post_inc(&mut self.highlight_order),
10793 range: rows,
10794 should_autoscroll,
10795 color,
10796 },
10797 ),
10798 (None, Ok(i)) => {
10799 row_highlights.remove(i);
10800 }
10801 }
10802 }
10803
10804 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
10805 pub fn clear_row_highlights<T: 'static>(&mut self) {
10806 self.highlighted_rows.remove(&TypeId::of::<T>());
10807 }
10808
10809 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
10810 pub fn highlighted_rows<T: 'static>(
10811 &self,
10812 ) -> Option<impl Iterator<Item = (&RangeInclusive<Anchor>, Option<&Hsla>)>> {
10813 Some(
10814 self.highlighted_rows
10815 .get(&TypeId::of::<T>())?
10816 .iter()
10817 .map(|highlight| (&highlight.range, highlight.color.as_ref())),
10818 )
10819 }
10820
10821 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
10822 /// Rerturns a map of display rows that are highlighted and their corresponding highlight color.
10823 /// Allows to ignore certain kinds of highlights.
10824 pub fn highlighted_display_rows(
10825 &mut self,
10826 cx: &mut WindowContext,
10827 ) -> BTreeMap<DisplayRow, Hsla> {
10828 let snapshot = self.snapshot(cx);
10829 let mut used_highlight_orders = HashMap::default();
10830 self.highlighted_rows
10831 .iter()
10832 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
10833 .fold(
10834 BTreeMap::<DisplayRow, Hsla>::new(),
10835 |mut unique_rows, highlight| {
10836 let start_row = highlight.range.start().to_display_point(&snapshot).row();
10837 let end_row = highlight.range.end().to_display_point(&snapshot).row();
10838 for row in start_row.0..=end_row.0 {
10839 let used_index =
10840 used_highlight_orders.entry(row).or_insert(highlight.index);
10841 if highlight.index >= *used_index {
10842 *used_index = highlight.index;
10843 match highlight.color {
10844 Some(hsla) => unique_rows.insert(DisplayRow(row), hsla),
10845 None => unique_rows.remove(&DisplayRow(row)),
10846 };
10847 }
10848 }
10849 unique_rows
10850 },
10851 )
10852 }
10853
10854 pub fn highlighted_display_row_for_autoscroll(
10855 &self,
10856 snapshot: &DisplaySnapshot,
10857 ) -> Option<DisplayRow> {
10858 self.highlighted_rows
10859 .values()
10860 .flat_map(|highlighted_rows| highlighted_rows.iter())
10861 .filter_map(|highlight| {
10862 if highlight.color.is_none() || !highlight.should_autoscroll {
10863 return None;
10864 }
10865 Some(highlight.range.start().to_display_point(&snapshot).row())
10866 })
10867 .min()
10868 }
10869
10870 pub fn set_search_within_ranges(
10871 &mut self,
10872 ranges: &[Range<Anchor>],
10873 cx: &mut ViewContext<Self>,
10874 ) {
10875 self.highlight_background::<SearchWithinRange>(
10876 ranges,
10877 |colors| colors.editor_document_highlight_read_background,
10878 cx,
10879 )
10880 }
10881
10882 pub fn set_breadcrumb_header(&mut self, new_header: String) {
10883 self.breadcrumb_header = Some(new_header);
10884 }
10885
10886 pub fn clear_search_within_ranges(&mut self, cx: &mut ViewContext<Self>) {
10887 self.clear_background_highlights::<SearchWithinRange>(cx);
10888 }
10889
10890 pub fn highlight_background<T: 'static>(
10891 &mut self,
10892 ranges: &[Range<Anchor>],
10893 color_fetcher: fn(&ThemeColors) -> Hsla,
10894 cx: &mut ViewContext<Self>,
10895 ) {
10896 self.background_highlights
10897 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
10898 self.scrollbar_marker_state.dirty = true;
10899 cx.notify();
10900 }
10901
10902 pub fn clear_background_highlights<T: 'static>(
10903 &mut self,
10904 cx: &mut ViewContext<Self>,
10905 ) -> Option<BackgroundHighlight> {
10906 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
10907 if !text_highlights.1.is_empty() {
10908 self.scrollbar_marker_state.dirty = true;
10909 cx.notify();
10910 }
10911 Some(text_highlights)
10912 }
10913
10914 pub fn highlight_gutter<T: 'static>(
10915 &mut self,
10916 ranges: &[Range<Anchor>],
10917 color_fetcher: fn(&AppContext) -> Hsla,
10918 cx: &mut ViewContext<Self>,
10919 ) {
10920 self.gutter_highlights
10921 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
10922 cx.notify();
10923 }
10924
10925 pub fn clear_gutter_highlights<T: 'static>(
10926 &mut self,
10927 cx: &mut ViewContext<Self>,
10928 ) -> Option<GutterHighlight> {
10929 cx.notify();
10930 self.gutter_highlights.remove(&TypeId::of::<T>())
10931 }
10932
10933 #[cfg(feature = "test-support")]
10934 pub fn all_text_background_highlights(
10935 &mut self,
10936 cx: &mut ViewContext<Self>,
10937 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
10938 let snapshot = self.snapshot(cx);
10939 let buffer = &snapshot.buffer_snapshot;
10940 let start = buffer.anchor_before(0);
10941 let end = buffer.anchor_after(buffer.len());
10942 let theme = cx.theme().colors();
10943 self.background_highlights_in_range(start..end, &snapshot, theme)
10944 }
10945
10946 #[cfg(feature = "test-support")]
10947 pub fn search_background_highlights(
10948 &mut self,
10949 cx: &mut ViewContext<Self>,
10950 ) -> Vec<Range<Point>> {
10951 let snapshot = self.buffer().read(cx).snapshot(cx);
10952
10953 let highlights = self
10954 .background_highlights
10955 .get(&TypeId::of::<items::BufferSearchHighlights>());
10956
10957 if let Some((_color, ranges)) = highlights {
10958 ranges
10959 .iter()
10960 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
10961 .collect_vec()
10962 } else {
10963 vec![]
10964 }
10965 }
10966
10967 fn document_highlights_for_position<'a>(
10968 &'a self,
10969 position: Anchor,
10970 buffer: &'a MultiBufferSnapshot,
10971 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
10972 let read_highlights = self
10973 .background_highlights
10974 .get(&TypeId::of::<DocumentHighlightRead>())
10975 .map(|h| &h.1);
10976 let write_highlights = self
10977 .background_highlights
10978 .get(&TypeId::of::<DocumentHighlightWrite>())
10979 .map(|h| &h.1);
10980 let left_position = position.bias_left(buffer);
10981 let right_position = position.bias_right(buffer);
10982 read_highlights
10983 .into_iter()
10984 .chain(write_highlights)
10985 .flat_map(move |ranges| {
10986 let start_ix = match ranges.binary_search_by(|probe| {
10987 let cmp = probe.end.cmp(&left_position, buffer);
10988 if cmp.is_ge() {
10989 Ordering::Greater
10990 } else {
10991 Ordering::Less
10992 }
10993 }) {
10994 Ok(i) | Err(i) => i,
10995 };
10996
10997 ranges[start_ix..]
10998 .iter()
10999 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
11000 })
11001 }
11002
11003 pub fn has_background_highlights<T: 'static>(&self) -> bool {
11004 self.background_highlights
11005 .get(&TypeId::of::<T>())
11006 .map_or(false, |(_, highlights)| !highlights.is_empty())
11007 }
11008
11009 pub fn background_highlights_in_range(
11010 &self,
11011 search_range: Range<Anchor>,
11012 display_snapshot: &DisplaySnapshot,
11013 theme: &ThemeColors,
11014 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11015 let mut results = Vec::new();
11016 for (color_fetcher, ranges) in self.background_highlights.values() {
11017 let color = color_fetcher(theme);
11018 let start_ix = match ranges.binary_search_by(|probe| {
11019 let cmp = probe
11020 .end
11021 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11022 if cmp.is_gt() {
11023 Ordering::Greater
11024 } else {
11025 Ordering::Less
11026 }
11027 }) {
11028 Ok(i) | Err(i) => i,
11029 };
11030 for range in &ranges[start_ix..] {
11031 if range
11032 .start
11033 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11034 .is_ge()
11035 {
11036 break;
11037 }
11038
11039 let start = range.start.to_display_point(&display_snapshot);
11040 let end = range.end.to_display_point(&display_snapshot);
11041 results.push((start..end, color))
11042 }
11043 }
11044 results
11045 }
11046
11047 pub fn background_highlight_row_ranges<T: 'static>(
11048 &self,
11049 search_range: Range<Anchor>,
11050 display_snapshot: &DisplaySnapshot,
11051 count: usize,
11052 ) -> Vec<RangeInclusive<DisplayPoint>> {
11053 let mut results = Vec::new();
11054 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
11055 return vec![];
11056 };
11057
11058 let start_ix = match ranges.binary_search_by(|probe| {
11059 let cmp = probe
11060 .end
11061 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11062 if cmp.is_gt() {
11063 Ordering::Greater
11064 } else {
11065 Ordering::Less
11066 }
11067 }) {
11068 Ok(i) | Err(i) => i,
11069 };
11070 let mut push_region = |start: Option<Point>, end: Option<Point>| {
11071 if let (Some(start_display), Some(end_display)) = (start, end) {
11072 results.push(
11073 start_display.to_display_point(display_snapshot)
11074 ..=end_display.to_display_point(display_snapshot),
11075 );
11076 }
11077 };
11078 let mut start_row: Option<Point> = None;
11079 let mut end_row: Option<Point> = None;
11080 if ranges.len() > count {
11081 return Vec::new();
11082 }
11083 for range in &ranges[start_ix..] {
11084 if range
11085 .start
11086 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11087 .is_ge()
11088 {
11089 break;
11090 }
11091 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
11092 if let Some(current_row) = &end_row {
11093 if end.row == current_row.row {
11094 continue;
11095 }
11096 }
11097 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
11098 if start_row.is_none() {
11099 assert_eq!(end_row, None);
11100 start_row = Some(start);
11101 end_row = Some(end);
11102 continue;
11103 }
11104 if let Some(current_end) = end_row.as_mut() {
11105 if start.row > current_end.row + 1 {
11106 push_region(start_row, end_row);
11107 start_row = Some(start);
11108 end_row = Some(end);
11109 } else {
11110 // Merge two hunks.
11111 *current_end = end;
11112 }
11113 } else {
11114 unreachable!();
11115 }
11116 }
11117 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
11118 push_region(start_row, end_row);
11119 results
11120 }
11121
11122 pub fn gutter_highlights_in_range(
11123 &self,
11124 search_range: Range<Anchor>,
11125 display_snapshot: &DisplaySnapshot,
11126 cx: &AppContext,
11127 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11128 let mut results = Vec::new();
11129 for (color_fetcher, ranges) in self.gutter_highlights.values() {
11130 let color = color_fetcher(cx);
11131 let start_ix = match ranges.binary_search_by(|probe| {
11132 let cmp = probe
11133 .end
11134 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11135 if cmp.is_gt() {
11136 Ordering::Greater
11137 } else {
11138 Ordering::Less
11139 }
11140 }) {
11141 Ok(i) | Err(i) => i,
11142 };
11143 for range in &ranges[start_ix..] {
11144 if range
11145 .start
11146 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11147 .is_ge()
11148 {
11149 break;
11150 }
11151
11152 let start = range.start.to_display_point(&display_snapshot);
11153 let end = range.end.to_display_point(&display_snapshot);
11154 results.push((start..end, color))
11155 }
11156 }
11157 results
11158 }
11159
11160 /// Get the text ranges corresponding to the redaction query
11161 pub fn redacted_ranges(
11162 &self,
11163 search_range: Range<Anchor>,
11164 display_snapshot: &DisplaySnapshot,
11165 cx: &WindowContext,
11166 ) -> Vec<Range<DisplayPoint>> {
11167 display_snapshot
11168 .buffer_snapshot
11169 .redacted_ranges(search_range, |file| {
11170 if let Some(file) = file {
11171 file.is_private()
11172 && EditorSettings::get(Some(file.as_ref().into()), cx).redact_private_values
11173 } else {
11174 false
11175 }
11176 })
11177 .map(|range| {
11178 range.start.to_display_point(display_snapshot)
11179 ..range.end.to_display_point(display_snapshot)
11180 })
11181 .collect()
11182 }
11183
11184 pub fn highlight_text<T: 'static>(
11185 &mut self,
11186 ranges: Vec<Range<Anchor>>,
11187 style: HighlightStyle,
11188 cx: &mut ViewContext<Self>,
11189 ) {
11190 self.display_map.update(cx, |map, _| {
11191 map.highlight_text(TypeId::of::<T>(), ranges, style)
11192 });
11193 cx.notify();
11194 }
11195
11196 pub(crate) fn highlight_inlays<T: 'static>(
11197 &mut self,
11198 highlights: Vec<InlayHighlight>,
11199 style: HighlightStyle,
11200 cx: &mut ViewContext<Self>,
11201 ) {
11202 self.display_map.update(cx, |map, _| {
11203 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
11204 });
11205 cx.notify();
11206 }
11207
11208 pub fn text_highlights<'a, T: 'static>(
11209 &'a self,
11210 cx: &'a AppContext,
11211 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
11212 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
11213 }
11214
11215 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
11216 let cleared = self
11217 .display_map
11218 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
11219 if cleared {
11220 cx.notify();
11221 }
11222 }
11223
11224 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
11225 (self.read_only(cx) || self.blink_manager.read(cx).visible())
11226 && self.focus_handle.is_focused(cx)
11227 }
11228
11229 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut ViewContext<Self>) {
11230 self.show_cursor_when_unfocused = is_enabled;
11231 cx.notify();
11232 }
11233
11234 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
11235 cx.notify();
11236 }
11237
11238 fn on_buffer_event(
11239 &mut self,
11240 multibuffer: Model<MultiBuffer>,
11241 event: &multi_buffer::Event,
11242 cx: &mut ViewContext<Self>,
11243 ) {
11244 match event {
11245 multi_buffer::Event::Edited {
11246 singleton_buffer_edited,
11247 } => {
11248 self.scrollbar_marker_state.dirty = true;
11249 self.active_indent_guides_state.dirty = true;
11250 self.refresh_active_diagnostics(cx);
11251 self.refresh_code_actions(cx);
11252 if self.has_active_inline_completion(cx) {
11253 self.update_visible_inline_completion(cx);
11254 }
11255 cx.emit(EditorEvent::BufferEdited);
11256 cx.emit(SearchEvent::MatchesInvalidated);
11257 if *singleton_buffer_edited {
11258 if let Some(project) = &self.project {
11259 let project = project.read(cx);
11260 #[allow(clippy::mutable_key_type)]
11261 let languages_affected = multibuffer
11262 .read(cx)
11263 .all_buffers()
11264 .into_iter()
11265 .filter_map(|buffer| {
11266 let buffer = buffer.read(cx);
11267 let language = buffer.language()?;
11268 if project.is_local()
11269 && project.language_servers_for_buffer(buffer, cx).count() == 0
11270 {
11271 None
11272 } else {
11273 Some(language)
11274 }
11275 })
11276 .cloned()
11277 .collect::<HashSet<_>>();
11278 if !languages_affected.is_empty() {
11279 self.refresh_inlay_hints(
11280 InlayHintRefreshReason::BufferEdited(languages_affected),
11281 cx,
11282 );
11283 }
11284 }
11285 }
11286
11287 let Some(project) = &self.project else { return };
11288 let telemetry = project.read(cx).client().telemetry().clone();
11289 refresh_linked_ranges(self, cx);
11290 telemetry.log_edit_event("editor");
11291 }
11292 multi_buffer::Event::ExcerptsAdded {
11293 buffer,
11294 predecessor,
11295 excerpts,
11296 } => {
11297 self.tasks_update_task = Some(self.refresh_runnables(cx));
11298 cx.emit(EditorEvent::ExcerptsAdded {
11299 buffer: buffer.clone(),
11300 predecessor: *predecessor,
11301 excerpts: excerpts.clone(),
11302 });
11303 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
11304 }
11305 multi_buffer::Event::ExcerptsRemoved { ids } => {
11306 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
11307 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
11308 }
11309 multi_buffer::Event::ExcerptsEdited { ids } => {
11310 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
11311 }
11312 multi_buffer::Event::ExcerptsExpanded { ids } => {
11313 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
11314 }
11315 multi_buffer::Event::Reparsed(buffer_id) => {
11316 self.tasks_update_task = Some(self.refresh_runnables(cx));
11317
11318 cx.emit(EditorEvent::Reparsed(*buffer_id));
11319 }
11320 multi_buffer::Event::LanguageChanged(buffer_id) => {
11321 linked_editing_ranges::refresh_linked_ranges(self, cx);
11322 cx.emit(EditorEvent::Reparsed(*buffer_id));
11323 cx.notify();
11324 }
11325 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
11326 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
11327 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
11328 cx.emit(EditorEvent::TitleChanged)
11329 }
11330 multi_buffer::Event::DiffBaseChanged => {
11331 self.scrollbar_marker_state.dirty = true;
11332 cx.emit(EditorEvent::DiffBaseChanged);
11333 cx.notify();
11334 }
11335 multi_buffer::Event::DiffUpdated { buffer } => {
11336 self.sync_expanded_diff_hunks(buffer.clone(), cx);
11337 cx.notify();
11338 }
11339 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
11340 multi_buffer::Event::DiagnosticsUpdated => {
11341 self.refresh_active_diagnostics(cx);
11342 self.scrollbar_marker_state.dirty = true;
11343 cx.notify();
11344 }
11345 _ => {}
11346 };
11347 }
11348
11349 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
11350 cx.notify();
11351 }
11352
11353 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
11354 self.tasks_update_task = Some(self.refresh_runnables(cx));
11355 self.refresh_inline_completion(true, cx);
11356 self.refresh_inlay_hints(
11357 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
11358 self.selections.newest_anchor().head(),
11359 &self.buffer.read(cx).snapshot(cx),
11360 cx,
11361 )),
11362 cx,
11363 );
11364 let editor_settings = EditorSettings::get_global(cx);
11365 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
11366 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
11367
11368 let project_settings = ProjectSettings::get_global(cx);
11369 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
11370
11371 if self.mode == EditorMode::Full {
11372 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
11373 if self.git_blame_inline_enabled != inline_blame_enabled {
11374 self.toggle_git_blame_inline_internal(false, cx);
11375 }
11376 }
11377
11378 cx.notify();
11379 }
11380
11381 pub fn set_searchable(&mut self, searchable: bool) {
11382 self.searchable = searchable;
11383 }
11384
11385 pub fn searchable(&self) -> bool {
11386 self.searchable
11387 }
11388
11389 fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext<Self>) {
11390 self.open_excerpts_common(true, cx)
11391 }
11392
11393 fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
11394 self.open_excerpts_common(false, cx)
11395 }
11396
11397 fn open_excerpts_common(&mut self, split: bool, cx: &mut ViewContext<Self>) {
11398 let buffer = self.buffer.read(cx);
11399 if buffer.is_singleton() {
11400 cx.propagate();
11401 return;
11402 }
11403
11404 let Some(workspace) = self.workspace() else {
11405 cx.propagate();
11406 return;
11407 };
11408
11409 let mut new_selections_by_buffer = HashMap::default();
11410 for selection in self.selections.all::<usize>(cx) {
11411 for (buffer, mut range, _) in
11412 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
11413 {
11414 if selection.reversed {
11415 mem::swap(&mut range.start, &mut range.end);
11416 }
11417 new_selections_by_buffer
11418 .entry(buffer)
11419 .or_insert(Vec::new())
11420 .push(range)
11421 }
11422 }
11423
11424 // We defer the pane interaction because we ourselves are a workspace item
11425 // and activating a new item causes the pane to call a method on us reentrantly,
11426 // which panics if we're on the stack.
11427 cx.window_context().defer(move |cx| {
11428 workspace.update(cx, |workspace, cx| {
11429 let pane = if split {
11430 workspace.adjacent_pane(cx)
11431 } else {
11432 workspace.active_pane().clone()
11433 };
11434
11435 for (buffer, ranges) in new_selections_by_buffer {
11436 let editor =
11437 workspace.open_project_item::<Self>(pane.clone(), buffer, true, true, cx);
11438 editor.update(cx, |editor, cx| {
11439 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
11440 s.select_ranges(ranges);
11441 });
11442 });
11443 }
11444 })
11445 });
11446 }
11447
11448 fn jump(
11449 &mut self,
11450 path: ProjectPath,
11451 position: Point,
11452 anchor: language::Anchor,
11453 offset_from_top: u32,
11454 cx: &mut ViewContext<Self>,
11455 ) {
11456 let workspace = self.workspace();
11457 cx.spawn(|_, mut cx| async move {
11458 let workspace = workspace.ok_or_else(|| anyhow!("cannot jump without workspace"))?;
11459 let editor = workspace.update(&mut cx, |workspace, cx| {
11460 // Reset the preview item id before opening the new item
11461 workspace.active_pane().update(cx, |pane, cx| {
11462 pane.set_preview_item_id(None, cx);
11463 });
11464 workspace.open_path_preview(path, None, true, true, cx)
11465 })?;
11466 let editor = editor
11467 .await?
11468 .downcast::<Editor>()
11469 .ok_or_else(|| anyhow!("opened item was not an editor"))?
11470 .downgrade();
11471 editor.update(&mut cx, |editor, cx| {
11472 let buffer = editor
11473 .buffer()
11474 .read(cx)
11475 .as_singleton()
11476 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
11477 let buffer = buffer.read(cx);
11478 let cursor = if buffer.can_resolve(&anchor) {
11479 language::ToPoint::to_point(&anchor, buffer)
11480 } else {
11481 buffer.clip_point(position, Bias::Left)
11482 };
11483
11484 let nav_history = editor.nav_history.take();
11485 editor.change_selections(
11486 Some(Autoscroll::top_relative(offset_from_top as usize)),
11487 cx,
11488 |s| {
11489 s.select_ranges([cursor..cursor]);
11490 },
11491 );
11492 editor.nav_history = nav_history;
11493
11494 anyhow::Ok(())
11495 })??;
11496
11497 anyhow::Ok(())
11498 })
11499 .detach_and_log_err(cx);
11500 }
11501
11502 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
11503 let snapshot = self.buffer.read(cx).read(cx);
11504 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
11505 Some(
11506 ranges
11507 .iter()
11508 .map(move |range| {
11509 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
11510 })
11511 .collect(),
11512 )
11513 }
11514
11515 fn selection_replacement_ranges(
11516 &self,
11517 range: Range<OffsetUtf16>,
11518 cx: &AppContext,
11519 ) -> Vec<Range<OffsetUtf16>> {
11520 let selections = self.selections.all::<OffsetUtf16>(cx);
11521 let newest_selection = selections
11522 .iter()
11523 .max_by_key(|selection| selection.id)
11524 .unwrap();
11525 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
11526 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
11527 let snapshot = self.buffer.read(cx).read(cx);
11528 selections
11529 .into_iter()
11530 .map(|mut selection| {
11531 selection.start.0 =
11532 (selection.start.0 as isize).saturating_add(start_delta) as usize;
11533 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
11534 snapshot.clip_offset_utf16(selection.start, Bias::Left)
11535 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
11536 })
11537 .collect()
11538 }
11539
11540 fn report_editor_event(
11541 &self,
11542 operation: &'static str,
11543 file_extension: Option<String>,
11544 cx: &AppContext,
11545 ) {
11546 if cfg!(any(test, feature = "test-support")) {
11547 return;
11548 }
11549
11550 let Some(project) = &self.project else { return };
11551
11552 // If None, we are in a file without an extension
11553 let file = self
11554 .buffer
11555 .read(cx)
11556 .as_singleton()
11557 .and_then(|b| b.read(cx).file());
11558 let file_extension = file_extension.or(file
11559 .as_ref()
11560 .and_then(|file| Path::new(file.file_name(cx)).extension())
11561 .and_then(|e| e.to_str())
11562 .map(|a| a.to_string()));
11563
11564 let vim_mode = cx
11565 .global::<SettingsStore>()
11566 .raw_user_settings()
11567 .get("vim_mode")
11568 == Some(&serde_json::Value::Bool(true));
11569
11570 let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
11571 == language::language_settings::InlineCompletionProvider::Copilot;
11572 let copilot_enabled_for_language = self
11573 .buffer
11574 .read(cx)
11575 .settings_at(0, cx)
11576 .show_inline_completions;
11577
11578 let telemetry = project.read(cx).client().telemetry().clone();
11579 telemetry.report_editor_event(
11580 file_extension,
11581 vim_mode,
11582 operation,
11583 copilot_enabled,
11584 copilot_enabled_for_language,
11585 )
11586 }
11587
11588 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
11589 /// with each line being an array of {text, highlight} objects.
11590 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
11591 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
11592 return;
11593 };
11594
11595 #[derive(Serialize)]
11596 struct Chunk<'a> {
11597 text: String,
11598 highlight: Option<&'a str>,
11599 }
11600
11601 let snapshot = buffer.read(cx).snapshot();
11602 let range = self
11603 .selected_text_range(cx)
11604 .and_then(|selected_range| {
11605 if selected_range.is_empty() {
11606 None
11607 } else {
11608 Some(selected_range)
11609 }
11610 })
11611 .unwrap_or_else(|| 0..snapshot.len());
11612
11613 let chunks = snapshot.chunks(range, true);
11614 let mut lines = Vec::new();
11615 let mut line: VecDeque<Chunk> = VecDeque::new();
11616
11617 let Some(style) = self.style.as_ref() else {
11618 return;
11619 };
11620
11621 for chunk in chunks {
11622 let highlight = chunk
11623 .syntax_highlight_id
11624 .and_then(|id| id.name(&style.syntax));
11625 let mut chunk_lines = chunk.text.split('\n').peekable();
11626 while let Some(text) = chunk_lines.next() {
11627 let mut merged_with_last_token = false;
11628 if let Some(last_token) = line.back_mut() {
11629 if last_token.highlight == highlight {
11630 last_token.text.push_str(text);
11631 merged_with_last_token = true;
11632 }
11633 }
11634
11635 if !merged_with_last_token {
11636 line.push_back(Chunk {
11637 text: text.into(),
11638 highlight,
11639 });
11640 }
11641
11642 if chunk_lines.peek().is_some() {
11643 if line.len() > 1 && line.front().unwrap().text.is_empty() {
11644 line.pop_front();
11645 }
11646 if line.len() > 1 && line.back().unwrap().text.is_empty() {
11647 line.pop_back();
11648 }
11649
11650 lines.push(mem::take(&mut line));
11651 }
11652 }
11653 }
11654
11655 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
11656 return;
11657 };
11658 cx.write_to_clipboard(ClipboardItem::new(lines));
11659 }
11660
11661 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
11662 &self.inlay_hint_cache
11663 }
11664
11665 pub fn replay_insert_event(
11666 &mut self,
11667 text: &str,
11668 relative_utf16_range: Option<Range<isize>>,
11669 cx: &mut ViewContext<Self>,
11670 ) {
11671 if !self.input_enabled {
11672 cx.emit(EditorEvent::InputIgnored { text: text.into() });
11673 return;
11674 }
11675 if let Some(relative_utf16_range) = relative_utf16_range {
11676 let selections = self.selections.all::<OffsetUtf16>(cx);
11677 self.change_selections(None, cx, |s| {
11678 let new_ranges = selections.into_iter().map(|range| {
11679 let start = OffsetUtf16(
11680 range
11681 .head()
11682 .0
11683 .saturating_add_signed(relative_utf16_range.start),
11684 );
11685 let end = OffsetUtf16(
11686 range
11687 .head()
11688 .0
11689 .saturating_add_signed(relative_utf16_range.end),
11690 );
11691 start..end
11692 });
11693 s.select_ranges(new_ranges);
11694 });
11695 }
11696
11697 self.handle_input(text, cx);
11698 }
11699
11700 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
11701 let Some(project) = self.project.as_ref() else {
11702 return false;
11703 };
11704 let project = project.read(cx);
11705
11706 let mut supports = false;
11707 self.buffer().read(cx).for_each_buffer(|buffer| {
11708 if !supports {
11709 supports = project
11710 .language_servers_for_buffer(buffer.read(cx), cx)
11711 .any(
11712 |(_, server)| match server.capabilities().inlay_hint_provider {
11713 Some(lsp::OneOf::Left(enabled)) => enabled,
11714 Some(lsp::OneOf::Right(_)) => true,
11715 None => false,
11716 },
11717 )
11718 }
11719 });
11720 supports
11721 }
11722
11723 pub fn focus(&self, cx: &mut WindowContext) {
11724 cx.focus(&self.focus_handle)
11725 }
11726
11727 pub fn is_focused(&self, cx: &WindowContext) -> bool {
11728 self.focus_handle.is_focused(cx)
11729 }
11730
11731 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
11732 cx.emit(EditorEvent::Focused);
11733
11734 if let Some(descendant) = self
11735 .last_focused_descendant
11736 .take()
11737 .and_then(|descendant| descendant.upgrade())
11738 {
11739 cx.focus(&descendant);
11740 } else {
11741 if let Some(blame) = self.blame.as_ref() {
11742 blame.update(cx, GitBlame::focus)
11743 }
11744
11745 self.blink_manager.update(cx, BlinkManager::enable);
11746 self.show_cursor_names(cx);
11747 self.buffer.update(cx, |buffer, cx| {
11748 buffer.finalize_last_transaction(cx);
11749 if self.leader_peer_id.is_none() {
11750 buffer.set_active_selections(
11751 &self.selections.disjoint_anchors(),
11752 self.selections.line_mode,
11753 self.cursor_shape,
11754 cx,
11755 );
11756 }
11757 });
11758 }
11759 }
11760
11761 fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
11762 cx.emit(EditorEvent::FocusedIn)
11763 }
11764
11765 fn handle_focus_out(&mut self, event: FocusOutEvent, _cx: &mut ViewContext<Self>) {
11766 if event.blurred != self.focus_handle {
11767 self.last_focused_descendant = Some(event.blurred);
11768 }
11769 }
11770
11771 pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
11772 self.blink_manager.update(cx, BlinkManager::disable);
11773 self.buffer
11774 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
11775
11776 if let Some(blame) = self.blame.as_ref() {
11777 blame.update(cx, GitBlame::blur)
11778 }
11779 if !self.hover_state.focused(cx) {
11780 hide_hover(self, cx);
11781 }
11782
11783 self.hide_context_menu(cx);
11784 cx.emit(EditorEvent::Blurred);
11785 cx.notify();
11786 }
11787
11788 pub fn register_action<A: Action>(
11789 &mut self,
11790 listener: impl Fn(&A, &mut WindowContext) + 'static,
11791 ) -> Subscription {
11792 let id = self.next_editor_action_id.post_inc();
11793 let listener = Arc::new(listener);
11794 self.editor_actions.borrow_mut().insert(
11795 id,
11796 Box::new(move |cx| {
11797 let _view = cx.view().clone();
11798 let cx = cx.window_context();
11799 let listener = listener.clone();
11800 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
11801 let action = action.downcast_ref().unwrap();
11802 if phase == DispatchPhase::Bubble {
11803 listener(action, cx)
11804 }
11805 })
11806 }),
11807 );
11808
11809 let editor_actions = self.editor_actions.clone();
11810 Subscription::new(move || {
11811 editor_actions.borrow_mut().remove(&id);
11812 })
11813 }
11814
11815 pub fn file_header_size(&self) -> u32 {
11816 self.file_header_size
11817 }
11818
11819 pub fn revert(
11820 &mut self,
11821 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
11822 cx: &mut ViewContext<Self>,
11823 ) {
11824 self.buffer().update(cx, |multi_buffer, cx| {
11825 for (buffer_id, changes) in revert_changes {
11826 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
11827 buffer.update(cx, |buffer, cx| {
11828 buffer.edit(
11829 changes.into_iter().map(|(range, text)| {
11830 (range, text.to_string().map(Arc::<str>::from))
11831 }),
11832 None,
11833 cx,
11834 );
11835 });
11836 }
11837 }
11838 });
11839 self.change_selections(None, cx, |selections| selections.refresh());
11840 }
11841
11842 pub fn to_pixel_point(
11843 &mut self,
11844 source: multi_buffer::Anchor,
11845 editor_snapshot: &EditorSnapshot,
11846 cx: &mut ViewContext<Self>,
11847 ) -> Option<gpui::Point<Pixels>> {
11848 let source_point = source.to_display_point(editor_snapshot);
11849 self.display_to_pixel_point(source_point, editor_snapshot, cx)
11850 }
11851
11852 pub fn display_to_pixel_point(
11853 &mut self,
11854 source: DisplayPoint,
11855 editor_snapshot: &EditorSnapshot,
11856 cx: &mut ViewContext<Self>,
11857 ) -> Option<gpui::Point<Pixels>> {
11858 let line_height = self.style()?.text.line_height_in_pixels(cx.rem_size());
11859 let text_layout_details = self.text_layout_details(cx);
11860 let scroll_top = text_layout_details
11861 .scroll_anchor
11862 .scroll_position(editor_snapshot)
11863 .y;
11864
11865 if source.row().as_f32() < scroll_top.floor() {
11866 return None;
11867 }
11868 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
11869 let source_y = line_height * (source.row().as_f32() - scroll_top);
11870 Some(gpui::Point::new(source_x, source_y))
11871 }
11872
11873 fn gutter_bounds(&self) -> Option<Bounds<Pixels>> {
11874 let bounds = self.last_bounds?;
11875 Some(element::gutter_bounds(bounds, self.gutter_dimensions))
11876 }
11877}
11878
11879fn hunks_for_selections(
11880 multi_buffer_snapshot: &MultiBufferSnapshot,
11881 selections: &[Selection<Anchor>],
11882) -> Vec<DiffHunk<MultiBufferRow>> {
11883 let buffer_rows_for_selections = selections.iter().map(|selection| {
11884 let head = selection.head();
11885 let tail = selection.tail();
11886 let start = MultiBufferRow(tail.to_point(&multi_buffer_snapshot).row);
11887 let end = MultiBufferRow(head.to_point(&multi_buffer_snapshot).row);
11888 if start > end {
11889 end..start
11890 } else {
11891 start..end
11892 }
11893 });
11894
11895 hunks_for_rows(buffer_rows_for_selections, multi_buffer_snapshot)
11896}
11897
11898pub fn hunks_for_rows(
11899 rows: impl Iterator<Item = Range<MultiBufferRow>>,
11900 multi_buffer_snapshot: &MultiBufferSnapshot,
11901) -> Vec<DiffHunk<MultiBufferRow>> {
11902 let mut hunks = Vec::new();
11903 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
11904 HashMap::default();
11905 for selected_multi_buffer_rows in rows {
11906 let query_rows =
11907 selected_multi_buffer_rows.start..selected_multi_buffer_rows.end.next_row();
11908 for hunk in multi_buffer_snapshot.git_diff_hunks_in_range(query_rows.clone()) {
11909 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
11910 // when the caret is just above or just below the deleted hunk.
11911 let allow_adjacent = hunk_status(&hunk) == DiffHunkStatus::Removed;
11912 let related_to_selection = if allow_adjacent {
11913 hunk.associated_range.overlaps(&query_rows)
11914 || hunk.associated_range.start == query_rows.end
11915 || hunk.associated_range.end == query_rows.start
11916 } else {
11917 // `selected_multi_buffer_rows` are inclusive (e.g. [2..2] means 2nd row is selected)
11918 // `hunk.associated_range` is exclusive (e.g. [2..3] means 2nd row is selected)
11919 hunk.associated_range.overlaps(&selected_multi_buffer_rows)
11920 || selected_multi_buffer_rows.end == hunk.associated_range.start
11921 };
11922 if related_to_selection {
11923 if !processed_buffer_rows
11924 .entry(hunk.buffer_id)
11925 .or_default()
11926 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
11927 {
11928 continue;
11929 }
11930 hunks.push(hunk);
11931 }
11932 }
11933 }
11934
11935 hunks
11936}
11937
11938pub trait CollaborationHub {
11939 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
11940 fn user_participant_indices<'a>(
11941 &self,
11942 cx: &'a AppContext,
11943 ) -> &'a HashMap<u64, ParticipantIndex>;
11944 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
11945}
11946
11947impl CollaborationHub for Model<Project> {
11948 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
11949 self.read(cx).collaborators()
11950 }
11951
11952 fn user_participant_indices<'a>(
11953 &self,
11954 cx: &'a AppContext,
11955 ) -> &'a HashMap<u64, ParticipantIndex> {
11956 self.read(cx).user_store().read(cx).participant_indices()
11957 }
11958
11959 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
11960 let this = self.read(cx);
11961 let user_ids = this.collaborators().values().map(|c| c.user_id);
11962 this.user_store().read_with(cx, |user_store, cx| {
11963 user_store.participant_names(user_ids, cx)
11964 })
11965 }
11966}
11967
11968pub trait CompletionProvider {
11969 fn completions(
11970 &self,
11971 buffer: &Model<Buffer>,
11972 buffer_position: text::Anchor,
11973 trigger: CompletionContext,
11974 cx: &mut ViewContext<Editor>,
11975 ) -> Task<Result<Vec<Completion>>>;
11976
11977 fn resolve_completions(
11978 &self,
11979 buffer: Model<Buffer>,
11980 completion_indices: Vec<usize>,
11981 completions: Arc<RwLock<Box<[Completion]>>>,
11982 cx: &mut ViewContext<Editor>,
11983 ) -> Task<Result<bool>>;
11984
11985 fn apply_additional_edits_for_completion(
11986 &self,
11987 buffer: Model<Buffer>,
11988 completion: Completion,
11989 push_to_history: bool,
11990 cx: &mut ViewContext<Editor>,
11991 ) -> Task<Result<Option<language::Transaction>>>;
11992
11993 fn is_completion_trigger(
11994 &self,
11995 buffer: &Model<Buffer>,
11996 position: language::Anchor,
11997 text: &str,
11998 trigger_in_words: bool,
11999 cx: &mut ViewContext<Editor>,
12000 ) -> bool;
12001}
12002
12003fn snippet_completions(
12004 project: &Project,
12005 buffer: &Model<Buffer>,
12006 buffer_position: text::Anchor,
12007 cx: &mut AppContext,
12008) -> Vec<Completion> {
12009 let language = buffer.read(cx).language_at(buffer_position);
12010 let language_name = language.as_ref().map(|language| language.lsp_id());
12011 let snippet_store = project.snippets().read(cx);
12012 let snippets = snippet_store.snippets_for(language_name, cx);
12013
12014 if snippets.is_empty() {
12015 return vec![];
12016 }
12017 let snapshot = buffer.read(cx).text_snapshot();
12018 let chunks = snapshot.reversed_chunks_in_range(text::Anchor::MIN..buffer_position);
12019
12020 let mut lines = chunks.lines();
12021 let Some(line_at) = lines.next().filter(|line| !line.is_empty()) else {
12022 return vec![];
12023 };
12024
12025 let scope = language.map(|language| language.default_scope());
12026 let mut last_word = line_at
12027 .chars()
12028 .rev()
12029 .take_while(|c| char_kind(&scope, *c) == CharKind::Word)
12030 .collect::<String>();
12031 last_word = last_word.chars().rev().collect();
12032 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
12033 let to_lsp = |point: &text::Anchor| {
12034 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
12035 point_to_lsp(end)
12036 };
12037 let lsp_end = to_lsp(&buffer_position);
12038 snippets
12039 .into_iter()
12040 .filter_map(|snippet| {
12041 let matching_prefix = snippet
12042 .prefix
12043 .iter()
12044 .find(|prefix| prefix.starts_with(&last_word))?;
12045 let start = as_offset - last_word.len();
12046 let start = snapshot.anchor_before(start);
12047 let range = start..buffer_position;
12048 let lsp_start = to_lsp(&start);
12049 let lsp_range = lsp::Range {
12050 start: lsp_start,
12051 end: lsp_end,
12052 };
12053 Some(Completion {
12054 old_range: range,
12055 new_text: snippet.body.clone(),
12056 label: CodeLabel {
12057 text: matching_prefix.clone(),
12058 runs: vec![],
12059 filter_range: 0..matching_prefix.len(),
12060 },
12061 server_id: LanguageServerId(usize::MAX),
12062 documentation: snippet
12063 .description
12064 .clone()
12065 .map(|description| Documentation::SingleLine(description)),
12066 lsp_completion: lsp::CompletionItem {
12067 label: snippet.prefix.first().unwrap().clone(),
12068 kind: Some(CompletionItemKind::SNIPPET),
12069 label_details: snippet.description.as_ref().map(|description| {
12070 lsp::CompletionItemLabelDetails {
12071 detail: Some(description.clone()),
12072 description: None,
12073 }
12074 }),
12075 insert_text_format: Some(InsertTextFormat::SNIPPET),
12076 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
12077 lsp::InsertReplaceEdit {
12078 new_text: snippet.body.clone(),
12079 insert: lsp_range,
12080 replace: lsp_range,
12081 },
12082 )),
12083 filter_text: Some(snippet.body.clone()),
12084 sort_text: Some(char::MAX.to_string()),
12085 ..Default::default()
12086 },
12087 confirm: None,
12088 show_new_completions_on_confirm: false,
12089 })
12090 })
12091 .collect()
12092}
12093
12094impl CompletionProvider for Model<Project> {
12095 fn completions(
12096 &self,
12097 buffer: &Model<Buffer>,
12098 buffer_position: text::Anchor,
12099 options: CompletionContext,
12100 cx: &mut ViewContext<Editor>,
12101 ) -> Task<Result<Vec<Completion>>> {
12102 self.update(cx, |project, cx| {
12103 let snippets = snippet_completions(project, buffer, buffer_position, cx);
12104 let project_completions = project.completions(&buffer, buffer_position, options, cx);
12105 cx.background_executor().spawn(async move {
12106 let mut completions = project_completions.await?;
12107 //let snippets = snippets.into_iter().;
12108 completions.extend(snippets);
12109 Ok(completions)
12110 })
12111 })
12112 }
12113
12114 fn resolve_completions(
12115 &self,
12116 buffer: Model<Buffer>,
12117 completion_indices: Vec<usize>,
12118 completions: Arc<RwLock<Box<[Completion]>>>,
12119 cx: &mut ViewContext<Editor>,
12120 ) -> Task<Result<bool>> {
12121 self.update(cx, |project, cx| {
12122 project.resolve_completions(buffer, completion_indices, completions, cx)
12123 })
12124 }
12125
12126 fn apply_additional_edits_for_completion(
12127 &self,
12128 buffer: Model<Buffer>,
12129 completion: Completion,
12130 push_to_history: bool,
12131 cx: &mut ViewContext<Editor>,
12132 ) -> Task<Result<Option<language::Transaction>>> {
12133 self.update(cx, |project, cx| {
12134 project.apply_additional_edits_for_completion(buffer, completion, push_to_history, cx)
12135 })
12136 }
12137
12138 fn is_completion_trigger(
12139 &self,
12140 buffer: &Model<Buffer>,
12141 position: language::Anchor,
12142 text: &str,
12143 trigger_in_words: bool,
12144 cx: &mut ViewContext<Editor>,
12145 ) -> bool {
12146 if !EditorSettings::get_global(cx).show_completions_on_input {
12147 return false;
12148 }
12149
12150 let mut chars = text.chars();
12151 let char = if let Some(char) = chars.next() {
12152 char
12153 } else {
12154 return false;
12155 };
12156 if chars.next().is_some() {
12157 return false;
12158 }
12159
12160 let buffer = buffer.read(cx);
12161 let scope = buffer.snapshot().language_scope_at(position);
12162 if trigger_in_words && char_kind(&scope, char) == CharKind::Word {
12163 return true;
12164 }
12165
12166 buffer
12167 .completion_triggers()
12168 .iter()
12169 .any(|string| string == text)
12170 }
12171}
12172
12173fn inlay_hint_settings(
12174 location: Anchor,
12175 snapshot: &MultiBufferSnapshot,
12176 cx: &mut ViewContext<'_, Editor>,
12177) -> InlayHintSettings {
12178 let file = snapshot.file_at(location);
12179 let language = snapshot.language_at(location);
12180 let settings = all_language_settings(file, cx);
12181 settings
12182 .language(language.map(|l| l.name()).as_deref())
12183 .inlay_hints
12184}
12185
12186fn consume_contiguous_rows(
12187 contiguous_row_selections: &mut Vec<Selection<Point>>,
12188 selection: &Selection<Point>,
12189 display_map: &DisplaySnapshot,
12190 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
12191) -> (MultiBufferRow, MultiBufferRow) {
12192 contiguous_row_selections.push(selection.clone());
12193 let start_row = MultiBufferRow(selection.start.row);
12194 let mut end_row = ending_row(selection, display_map);
12195
12196 while let Some(next_selection) = selections.peek() {
12197 if next_selection.start.row <= end_row.0 {
12198 end_row = ending_row(next_selection, display_map);
12199 contiguous_row_selections.push(selections.next().unwrap().clone());
12200 } else {
12201 break;
12202 }
12203 }
12204 (start_row, end_row)
12205}
12206
12207fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
12208 if next_selection.end.column > 0 || next_selection.is_empty() {
12209 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
12210 } else {
12211 MultiBufferRow(next_selection.end.row)
12212 }
12213}
12214
12215impl EditorSnapshot {
12216 pub fn remote_selections_in_range<'a>(
12217 &'a self,
12218 range: &'a Range<Anchor>,
12219 collaboration_hub: &dyn CollaborationHub,
12220 cx: &'a AppContext,
12221 ) -> impl 'a + Iterator<Item = RemoteSelection> {
12222 let participant_names = collaboration_hub.user_names(cx);
12223 let participant_indices = collaboration_hub.user_participant_indices(cx);
12224 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
12225 let collaborators_by_replica_id = collaborators_by_peer_id
12226 .iter()
12227 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
12228 .collect::<HashMap<_, _>>();
12229 self.buffer_snapshot
12230 .selections_in_range(range, false)
12231 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
12232 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
12233 let participant_index = participant_indices.get(&collaborator.user_id).copied();
12234 let user_name = participant_names.get(&collaborator.user_id).cloned();
12235 Some(RemoteSelection {
12236 replica_id,
12237 selection,
12238 cursor_shape,
12239 line_mode,
12240 participant_index,
12241 peer_id: collaborator.peer_id,
12242 user_name,
12243 })
12244 })
12245 }
12246
12247 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
12248 self.display_snapshot.buffer_snapshot.language_at(position)
12249 }
12250
12251 pub fn is_focused(&self) -> bool {
12252 self.is_focused
12253 }
12254
12255 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
12256 self.placeholder_text.as_ref()
12257 }
12258
12259 pub fn scroll_position(&self) -> gpui::Point<f32> {
12260 self.scroll_anchor.scroll_position(&self.display_snapshot)
12261 }
12262
12263 fn gutter_dimensions(
12264 &self,
12265 font_id: FontId,
12266 font_size: Pixels,
12267 em_width: Pixels,
12268 max_line_number_width: Pixels,
12269 cx: &AppContext,
12270 ) -> GutterDimensions {
12271 if !self.show_gutter {
12272 return GutterDimensions::default();
12273 }
12274 let descent = cx.text_system().descent(font_id, font_size);
12275
12276 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
12277 matches!(
12278 ProjectSettings::get_global(cx).git.git_gutter,
12279 Some(GitGutterSetting::TrackedFiles)
12280 )
12281 });
12282 let gutter_settings = EditorSettings::get_global(cx).gutter;
12283 let show_line_numbers = self
12284 .show_line_numbers
12285 .unwrap_or(gutter_settings.line_numbers);
12286 let line_gutter_width = if show_line_numbers {
12287 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
12288 let min_width_for_number_on_gutter = em_width * 4.0;
12289 max_line_number_width.max(min_width_for_number_on_gutter)
12290 } else {
12291 0.0.into()
12292 };
12293
12294 let show_code_actions = self
12295 .show_code_actions
12296 .unwrap_or(gutter_settings.code_actions);
12297
12298 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
12299
12300 let git_blame_entries_width = self
12301 .render_git_blame_gutter
12302 .then_some(em_width * GIT_BLAME_GUTTER_WIDTH_CHARS);
12303
12304 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
12305 left_padding += if show_code_actions || show_runnables {
12306 em_width * 3.0
12307 } else if show_git_gutter && show_line_numbers {
12308 em_width * 2.0
12309 } else if show_git_gutter || show_line_numbers {
12310 em_width
12311 } else {
12312 px(0.)
12313 };
12314
12315 let right_padding = if gutter_settings.folds && show_line_numbers {
12316 em_width * 4.0
12317 } else if gutter_settings.folds {
12318 em_width * 3.0
12319 } else if show_line_numbers {
12320 em_width
12321 } else {
12322 px(0.)
12323 };
12324
12325 GutterDimensions {
12326 left_padding,
12327 right_padding,
12328 width: line_gutter_width + left_padding + right_padding,
12329 margin: -descent,
12330 git_blame_entries_width,
12331 }
12332 }
12333
12334 pub fn render_fold_toggle(
12335 &self,
12336 buffer_row: MultiBufferRow,
12337 row_contains_cursor: bool,
12338 editor: View<Editor>,
12339 cx: &mut WindowContext,
12340 ) -> Option<AnyElement> {
12341 let folded = self.is_line_folded(buffer_row);
12342
12343 if let Some(crease) = self
12344 .crease_snapshot
12345 .query_row(buffer_row, &self.buffer_snapshot)
12346 {
12347 let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
12348 if folded {
12349 editor.update(cx, |editor, cx| {
12350 editor.fold_at(&crate::FoldAt { buffer_row }, cx)
12351 });
12352 } else {
12353 editor.update(cx, |editor, cx| {
12354 editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
12355 });
12356 }
12357 });
12358
12359 Some((crease.render_toggle)(
12360 buffer_row,
12361 folded,
12362 toggle_callback,
12363 cx,
12364 ))
12365 } else if folded
12366 || (self.starts_indent(buffer_row) && (row_contains_cursor || self.gutter_hovered))
12367 {
12368 Some(
12369 Disclosure::new(("indent-fold-indicator", buffer_row.0), !folded)
12370 .selected(folded)
12371 .on_click(cx.listener_for(&editor, move |this, _e, cx| {
12372 if folded {
12373 this.unfold_at(&UnfoldAt { buffer_row }, cx);
12374 } else {
12375 this.fold_at(&FoldAt { buffer_row }, cx);
12376 }
12377 }))
12378 .into_any_element(),
12379 )
12380 } else {
12381 None
12382 }
12383 }
12384
12385 pub fn render_crease_trailer(
12386 &self,
12387 buffer_row: MultiBufferRow,
12388 cx: &mut WindowContext,
12389 ) -> Option<AnyElement> {
12390 let folded = self.is_line_folded(buffer_row);
12391 let crease = self
12392 .crease_snapshot
12393 .query_row(buffer_row, &self.buffer_snapshot)?;
12394 Some((crease.render_trailer)(buffer_row, folded, cx))
12395 }
12396}
12397
12398impl Deref for EditorSnapshot {
12399 type Target = DisplaySnapshot;
12400
12401 fn deref(&self) -> &Self::Target {
12402 &self.display_snapshot
12403 }
12404}
12405
12406#[derive(Clone, Debug, PartialEq, Eq)]
12407pub enum EditorEvent {
12408 InputIgnored {
12409 text: Arc<str>,
12410 },
12411 InputHandled {
12412 utf16_range_to_replace: Option<Range<isize>>,
12413 text: Arc<str>,
12414 },
12415 ExcerptsAdded {
12416 buffer: Model<Buffer>,
12417 predecessor: ExcerptId,
12418 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
12419 },
12420 ExcerptsRemoved {
12421 ids: Vec<ExcerptId>,
12422 },
12423 ExcerptsEdited {
12424 ids: Vec<ExcerptId>,
12425 },
12426 ExcerptsExpanded {
12427 ids: Vec<ExcerptId>,
12428 },
12429 BufferEdited,
12430 Edited {
12431 transaction_id: clock::Lamport,
12432 },
12433 Reparsed(BufferId),
12434 Focused,
12435 FocusedIn,
12436 Blurred,
12437 DirtyChanged,
12438 Saved,
12439 TitleChanged,
12440 DiffBaseChanged,
12441 SelectionsChanged {
12442 local: bool,
12443 },
12444 ScrollPositionChanged {
12445 local: bool,
12446 autoscroll: bool,
12447 },
12448 Closed,
12449 TransactionUndone {
12450 transaction_id: clock::Lamport,
12451 },
12452 TransactionBegun {
12453 transaction_id: clock::Lamport,
12454 },
12455}
12456
12457impl EventEmitter<EditorEvent> for Editor {}
12458
12459impl FocusableView for Editor {
12460 fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
12461 self.focus_handle.clone()
12462 }
12463}
12464
12465impl Render for Editor {
12466 fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
12467 let settings = ThemeSettings::get_global(cx);
12468
12469 let text_style = match self.mode {
12470 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
12471 color: cx.theme().colors().editor_foreground,
12472 font_family: settings.ui_font.family.clone(),
12473 font_features: settings.ui_font.features.clone(),
12474 font_fallbacks: settings.ui_font.fallbacks.clone(),
12475 font_size: rems(0.875).into(),
12476 font_weight: settings.ui_font.weight,
12477 line_height: relative(settings.buffer_line_height.value()),
12478 ..Default::default()
12479 },
12480 EditorMode::Full => TextStyle {
12481 color: cx.theme().colors().editor_foreground,
12482 font_family: settings.buffer_font.family.clone(),
12483 font_features: settings.buffer_font.features.clone(),
12484 font_fallbacks: settings.buffer_font.fallbacks.clone(),
12485 font_size: settings.buffer_font_size(cx).into(),
12486 font_weight: settings.buffer_font.weight,
12487 line_height: relative(settings.buffer_line_height.value()),
12488 ..Default::default()
12489 },
12490 };
12491
12492 let background = match self.mode {
12493 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
12494 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
12495 EditorMode::Full => cx.theme().colors().editor_background,
12496 };
12497
12498 EditorElement::new(
12499 cx.view(),
12500 EditorStyle {
12501 background,
12502 local_player: cx.theme().players().local(),
12503 text: text_style,
12504 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
12505 syntax: cx.theme().syntax().clone(),
12506 status: cx.theme().status().clone(),
12507 inlay_hints_style: HighlightStyle {
12508 color: Some(cx.theme().status().hint),
12509 ..HighlightStyle::default()
12510 },
12511 suggestions_style: HighlightStyle {
12512 color: Some(cx.theme().status().predictive),
12513 ..HighlightStyle::default()
12514 },
12515 },
12516 )
12517 }
12518}
12519
12520impl ViewInputHandler for Editor {
12521 fn text_for_range(
12522 &mut self,
12523 range_utf16: Range<usize>,
12524 cx: &mut ViewContext<Self>,
12525 ) -> Option<String> {
12526 Some(
12527 self.buffer
12528 .read(cx)
12529 .read(cx)
12530 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
12531 .collect(),
12532 )
12533 }
12534
12535 fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
12536 // Prevent the IME menu from appearing when holding down an alphabetic key
12537 // while input is disabled.
12538 if !self.input_enabled {
12539 return None;
12540 }
12541
12542 let range = self.selections.newest::<OffsetUtf16>(cx).range();
12543 Some(range.start.0..range.end.0)
12544 }
12545
12546 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
12547 let snapshot = self.buffer.read(cx).read(cx);
12548 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
12549 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
12550 }
12551
12552 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
12553 self.clear_highlights::<InputComposition>(cx);
12554 self.ime_transaction.take();
12555 }
12556
12557 fn replace_text_in_range(
12558 &mut self,
12559 range_utf16: Option<Range<usize>>,
12560 text: &str,
12561 cx: &mut ViewContext<Self>,
12562 ) {
12563 if !self.input_enabled {
12564 cx.emit(EditorEvent::InputIgnored { text: text.into() });
12565 return;
12566 }
12567
12568 self.transact(cx, |this, cx| {
12569 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
12570 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
12571 Some(this.selection_replacement_ranges(range_utf16, cx))
12572 } else {
12573 this.marked_text_ranges(cx)
12574 };
12575
12576 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
12577 let newest_selection_id = this.selections.newest_anchor().id;
12578 this.selections
12579 .all::<OffsetUtf16>(cx)
12580 .iter()
12581 .zip(ranges_to_replace.iter())
12582 .find_map(|(selection, range)| {
12583 if selection.id == newest_selection_id {
12584 Some(
12585 (range.start.0 as isize - selection.head().0 as isize)
12586 ..(range.end.0 as isize - selection.head().0 as isize),
12587 )
12588 } else {
12589 None
12590 }
12591 })
12592 });
12593
12594 cx.emit(EditorEvent::InputHandled {
12595 utf16_range_to_replace: range_to_replace,
12596 text: text.into(),
12597 });
12598
12599 if let Some(new_selected_ranges) = new_selected_ranges {
12600 this.change_selections(None, cx, |selections| {
12601 selections.select_ranges(new_selected_ranges)
12602 });
12603 this.backspace(&Default::default(), cx);
12604 }
12605
12606 this.handle_input(text, cx);
12607 });
12608
12609 if let Some(transaction) = self.ime_transaction {
12610 self.buffer.update(cx, |buffer, cx| {
12611 buffer.group_until_transaction(transaction, cx);
12612 });
12613 }
12614
12615 self.unmark_text(cx);
12616 }
12617
12618 fn replace_and_mark_text_in_range(
12619 &mut self,
12620 range_utf16: Option<Range<usize>>,
12621 text: &str,
12622 new_selected_range_utf16: Option<Range<usize>>,
12623 cx: &mut ViewContext<Self>,
12624 ) {
12625 if !self.input_enabled {
12626 return;
12627 }
12628
12629 let transaction = self.transact(cx, |this, cx| {
12630 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
12631 let snapshot = this.buffer.read(cx).read(cx);
12632 if let Some(relative_range_utf16) = range_utf16.as_ref() {
12633 for marked_range in &mut marked_ranges {
12634 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
12635 marked_range.start.0 += relative_range_utf16.start;
12636 marked_range.start =
12637 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
12638 marked_range.end =
12639 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
12640 }
12641 }
12642 Some(marked_ranges)
12643 } else if let Some(range_utf16) = range_utf16 {
12644 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
12645 Some(this.selection_replacement_ranges(range_utf16, cx))
12646 } else {
12647 None
12648 };
12649
12650 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
12651 let newest_selection_id = this.selections.newest_anchor().id;
12652 this.selections
12653 .all::<OffsetUtf16>(cx)
12654 .iter()
12655 .zip(ranges_to_replace.iter())
12656 .find_map(|(selection, range)| {
12657 if selection.id == newest_selection_id {
12658 Some(
12659 (range.start.0 as isize - selection.head().0 as isize)
12660 ..(range.end.0 as isize - selection.head().0 as isize),
12661 )
12662 } else {
12663 None
12664 }
12665 })
12666 });
12667
12668 cx.emit(EditorEvent::InputHandled {
12669 utf16_range_to_replace: range_to_replace,
12670 text: text.into(),
12671 });
12672
12673 if let Some(ranges) = ranges_to_replace {
12674 this.change_selections(None, cx, |s| s.select_ranges(ranges));
12675 }
12676
12677 let marked_ranges = {
12678 let snapshot = this.buffer.read(cx).read(cx);
12679 this.selections
12680 .disjoint_anchors()
12681 .iter()
12682 .map(|selection| {
12683 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
12684 })
12685 .collect::<Vec<_>>()
12686 };
12687
12688 if text.is_empty() {
12689 this.unmark_text(cx);
12690 } else {
12691 this.highlight_text::<InputComposition>(
12692 marked_ranges.clone(),
12693 HighlightStyle {
12694 underline: Some(UnderlineStyle {
12695 thickness: px(1.),
12696 color: None,
12697 wavy: false,
12698 }),
12699 ..Default::default()
12700 },
12701 cx,
12702 );
12703 }
12704
12705 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
12706 let use_autoclose = this.use_autoclose;
12707 let use_auto_surround = this.use_auto_surround;
12708 this.set_use_autoclose(false);
12709 this.set_use_auto_surround(false);
12710 this.handle_input(text, cx);
12711 this.set_use_autoclose(use_autoclose);
12712 this.set_use_auto_surround(use_auto_surround);
12713
12714 if let Some(new_selected_range) = new_selected_range_utf16 {
12715 let snapshot = this.buffer.read(cx).read(cx);
12716 let new_selected_ranges = marked_ranges
12717 .into_iter()
12718 .map(|marked_range| {
12719 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
12720 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
12721 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
12722 snapshot.clip_offset_utf16(new_start, Bias::Left)
12723 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
12724 })
12725 .collect::<Vec<_>>();
12726
12727 drop(snapshot);
12728 this.change_selections(None, cx, |selections| {
12729 selections.select_ranges(new_selected_ranges)
12730 });
12731 }
12732 });
12733
12734 self.ime_transaction = self.ime_transaction.or(transaction);
12735 if let Some(transaction) = self.ime_transaction {
12736 self.buffer.update(cx, |buffer, cx| {
12737 buffer.group_until_transaction(transaction, cx);
12738 });
12739 }
12740
12741 if self.text_highlights::<InputComposition>(cx).is_none() {
12742 self.ime_transaction.take();
12743 }
12744 }
12745
12746 fn bounds_for_range(
12747 &mut self,
12748 range_utf16: Range<usize>,
12749 element_bounds: gpui::Bounds<Pixels>,
12750 cx: &mut ViewContext<Self>,
12751 ) -> Option<gpui::Bounds<Pixels>> {
12752 let text_layout_details = self.text_layout_details(cx);
12753 let style = &text_layout_details.editor_style;
12754 let font_id = cx.text_system().resolve_font(&style.text.font());
12755 let font_size = style.text.font_size.to_pixels(cx.rem_size());
12756 let line_height = style.text.line_height_in_pixels(cx.rem_size());
12757
12758 let em_width = cx
12759 .text_system()
12760 .typographic_bounds(font_id, font_size, 'm')
12761 .unwrap()
12762 .size
12763 .width;
12764
12765 let snapshot = self.snapshot(cx);
12766 let scroll_position = snapshot.scroll_position();
12767 let scroll_left = scroll_position.x * em_width;
12768
12769 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
12770 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
12771 + self.gutter_dimensions.width;
12772 let y = line_height * (start.row().as_f32() - scroll_position.y);
12773
12774 Some(Bounds {
12775 origin: element_bounds.origin + point(x, y),
12776 size: size(em_width, line_height),
12777 })
12778 }
12779}
12780
12781trait SelectionExt {
12782 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
12783 fn spanned_rows(
12784 &self,
12785 include_end_if_at_line_start: bool,
12786 map: &DisplaySnapshot,
12787 ) -> Range<MultiBufferRow>;
12788}
12789
12790impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
12791 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
12792 let start = self
12793 .start
12794 .to_point(&map.buffer_snapshot)
12795 .to_display_point(map);
12796 let end = self
12797 .end
12798 .to_point(&map.buffer_snapshot)
12799 .to_display_point(map);
12800 if self.reversed {
12801 end..start
12802 } else {
12803 start..end
12804 }
12805 }
12806
12807 fn spanned_rows(
12808 &self,
12809 include_end_if_at_line_start: bool,
12810 map: &DisplaySnapshot,
12811 ) -> Range<MultiBufferRow> {
12812 let start = self.start.to_point(&map.buffer_snapshot);
12813 let mut end = self.end.to_point(&map.buffer_snapshot);
12814 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
12815 end.row -= 1;
12816 }
12817
12818 let buffer_start = map.prev_line_boundary(start).0;
12819 let buffer_end = map.next_line_boundary(end).0;
12820 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
12821 }
12822}
12823
12824impl<T: InvalidationRegion> InvalidationStack<T> {
12825 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
12826 where
12827 S: Clone + ToOffset,
12828 {
12829 while let Some(region) = self.last() {
12830 let all_selections_inside_invalidation_ranges =
12831 if selections.len() == region.ranges().len() {
12832 selections
12833 .iter()
12834 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
12835 .all(|(selection, invalidation_range)| {
12836 let head = selection.head().to_offset(buffer);
12837 invalidation_range.start <= head && invalidation_range.end >= head
12838 })
12839 } else {
12840 false
12841 };
12842
12843 if all_selections_inside_invalidation_ranges {
12844 break;
12845 } else {
12846 self.pop();
12847 }
12848 }
12849 }
12850}
12851
12852impl<T> Default for InvalidationStack<T> {
12853 fn default() -> Self {
12854 Self(Default::default())
12855 }
12856}
12857
12858impl<T> Deref for InvalidationStack<T> {
12859 type Target = Vec<T>;
12860
12861 fn deref(&self) -> &Self::Target {
12862 &self.0
12863 }
12864}
12865
12866impl<T> DerefMut for InvalidationStack<T> {
12867 fn deref_mut(&mut self) -> &mut Self::Target {
12868 &mut self.0
12869 }
12870}
12871
12872impl InvalidationRegion for SnippetState {
12873 fn ranges(&self) -> &[Range<Anchor>] {
12874 &self.ranges[self.active_index]
12875 }
12876}
12877
12878pub fn diagnostic_block_renderer(
12879 diagnostic: Diagnostic,
12880 max_message_rows: Option<u8>,
12881 allow_closing: bool,
12882 _is_valid: bool,
12883) -> RenderBlock {
12884 let (text_without_backticks, code_ranges) =
12885 highlight_diagnostic_message(&diagnostic, max_message_rows);
12886
12887 Box::new(move |cx: &mut BlockContext| {
12888 let group_id: SharedString = cx.block_id.to_string().into();
12889
12890 let mut text_style = cx.text_style().clone();
12891 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
12892 let theme_settings = ThemeSettings::get_global(cx);
12893 text_style.font_family = theme_settings.buffer_font.family.clone();
12894 text_style.font_style = theme_settings.buffer_font.style;
12895 text_style.font_features = theme_settings.buffer_font.features.clone();
12896 text_style.font_weight = theme_settings.buffer_font.weight;
12897
12898 let multi_line_diagnostic = diagnostic.message.contains('\n');
12899
12900 let buttons = |diagnostic: &Diagnostic, block_id: BlockId| {
12901 if multi_line_diagnostic {
12902 v_flex()
12903 } else {
12904 h_flex()
12905 }
12906 .when(allow_closing, |div| {
12907 div.children(diagnostic.is_primary.then(|| {
12908 IconButton::new(("close-block", EntityId::from(block_id)), IconName::XCircle)
12909 .icon_color(Color::Muted)
12910 .size(ButtonSize::Compact)
12911 .style(ButtonStyle::Transparent)
12912 .visible_on_hover(group_id.clone())
12913 .on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
12914 .tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
12915 }))
12916 })
12917 .child(
12918 IconButton::new(("copy-block", EntityId::from(block_id)), IconName::Copy)
12919 .icon_color(Color::Muted)
12920 .size(ButtonSize::Compact)
12921 .style(ButtonStyle::Transparent)
12922 .visible_on_hover(group_id.clone())
12923 .on_click({
12924 let message = diagnostic.message.clone();
12925 move |_click, cx| cx.write_to_clipboard(ClipboardItem::new(message.clone()))
12926 })
12927 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
12928 )
12929 };
12930
12931 let icon_size = buttons(&diagnostic, cx.block_id)
12932 .into_any_element()
12933 .layout_as_root(AvailableSpace::min_size(), cx);
12934
12935 h_flex()
12936 .id(cx.block_id)
12937 .group(group_id.clone())
12938 .relative()
12939 .size_full()
12940 .pl(cx.gutter_dimensions.width)
12941 .w(cx.max_width + cx.gutter_dimensions.width)
12942 .child(
12943 div()
12944 .flex()
12945 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
12946 .flex_shrink(),
12947 )
12948 .child(buttons(&diagnostic, cx.block_id))
12949 .child(div().flex().flex_shrink_0().child(
12950 StyledText::new(text_without_backticks.clone()).with_highlights(
12951 &text_style,
12952 code_ranges.iter().map(|range| {
12953 (
12954 range.clone(),
12955 HighlightStyle {
12956 font_weight: Some(FontWeight::BOLD),
12957 ..Default::default()
12958 },
12959 )
12960 }),
12961 ),
12962 ))
12963 .into_any_element()
12964 })
12965}
12966
12967pub fn highlight_diagnostic_message(
12968 diagnostic: &Diagnostic,
12969 mut max_message_rows: Option<u8>,
12970) -> (SharedString, Vec<Range<usize>>) {
12971 let mut text_without_backticks = String::new();
12972 let mut code_ranges = Vec::new();
12973
12974 if let Some(source) = &diagnostic.source {
12975 text_without_backticks.push_str(&source);
12976 code_ranges.push(0..source.len());
12977 text_without_backticks.push_str(": ");
12978 }
12979
12980 let mut prev_offset = 0;
12981 let mut in_code_block = false;
12982 let has_row_limit = max_message_rows.is_some();
12983 let mut newline_indices = diagnostic
12984 .message
12985 .match_indices('\n')
12986 .filter(|_| has_row_limit)
12987 .map(|(ix, _)| ix)
12988 .fuse()
12989 .peekable();
12990
12991 for (quote_ix, _) in diagnostic
12992 .message
12993 .match_indices('`')
12994 .chain([(diagnostic.message.len(), "")])
12995 {
12996 let mut first_newline_ix = None;
12997 let mut last_newline_ix = None;
12998 while let Some(newline_ix) = newline_indices.peek() {
12999 if *newline_ix < quote_ix {
13000 if first_newline_ix.is_none() {
13001 first_newline_ix = Some(*newline_ix);
13002 }
13003 last_newline_ix = Some(*newline_ix);
13004
13005 if let Some(rows_left) = &mut max_message_rows {
13006 if *rows_left == 0 {
13007 break;
13008 } else {
13009 *rows_left -= 1;
13010 }
13011 }
13012 let _ = newline_indices.next();
13013 } else {
13014 break;
13015 }
13016 }
13017 let prev_len = text_without_backticks.len();
13018 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
13019 text_without_backticks.push_str(new_text);
13020 if in_code_block {
13021 code_ranges.push(prev_len..text_without_backticks.len());
13022 }
13023 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
13024 in_code_block = !in_code_block;
13025 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
13026 text_without_backticks.push_str("...");
13027 break;
13028 }
13029 }
13030
13031 (text_without_backticks.into(), code_ranges)
13032}
13033
13034fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
13035 match severity {
13036 DiagnosticSeverity::ERROR => colors.error,
13037 DiagnosticSeverity::WARNING => colors.warning,
13038 DiagnosticSeverity::INFORMATION => colors.info,
13039 DiagnosticSeverity::HINT => colors.info,
13040 _ => colors.ignored,
13041 }
13042}
13043
13044pub fn styled_runs_for_code_label<'a>(
13045 label: &'a CodeLabel,
13046 syntax_theme: &'a theme::SyntaxTheme,
13047) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
13048 let fade_out = HighlightStyle {
13049 fade_out: Some(0.35),
13050 ..Default::default()
13051 };
13052
13053 let mut prev_end = label.filter_range.end;
13054 label
13055 .runs
13056 .iter()
13057 .enumerate()
13058 .flat_map(move |(ix, (range, highlight_id))| {
13059 let style = if let Some(style) = highlight_id.style(syntax_theme) {
13060 style
13061 } else {
13062 return Default::default();
13063 };
13064 let mut muted_style = style;
13065 muted_style.highlight(fade_out);
13066
13067 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
13068 if range.start >= label.filter_range.end {
13069 if range.start > prev_end {
13070 runs.push((prev_end..range.start, fade_out));
13071 }
13072 runs.push((range.clone(), muted_style));
13073 } else if range.end <= label.filter_range.end {
13074 runs.push((range.clone(), style));
13075 } else {
13076 runs.push((range.start..label.filter_range.end, style));
13077 runs.push((label.filter_range.end..range.end, muted_style));
13078 }
13079 prev_end = cmp::max(prev_end, range.end);
13080
13081 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
13082 runs.push((prev_end..label.text.len(), fade_out));
13083 }
13084
13085 runs
13086 })
13087}
13088
13089pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
13090 let mut prev_index = 0;
13091 let mut prev_codepoint: Option<char> = None;
13092 text.char_indices()
13093 .chain([(text.len(), '\0')])
13094 .filter_map(move |(index, codepoint)| {
13095 let prev_codepoint = prev_codepoint.replace(codepoint)?;
13096 let is_boundary = index == text.len()
13097 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
13098 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
13099 if is_boundary {
13100 let chunk = &text[prev_index..index];
13101 prev_index = index;
13102 Some(chunk)
13103 } else {
13104 None
13105 }
13106 })
13107}
13108
13109pub trait RangeToAnchorExt: Sized {
13110 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
13111
13112 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
13113 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
13114 anchor_range.start.to_display_point(&snapshot)..anchor_range.end.to_display_point(&snapshot)
13115 }
13116}
13117
13118impl<T: ToOffset> RangeToAnchorExt for Range<T> {
13119 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
13120 let start_offset = self.start.to_offset(snapshot);
13121 let end_offset = self.end.to_offset(snapshot);
13122 if start_offset == end_offset {
13123 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
13124 } else {
13125 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
13126 }
13127 }
13128}
13129
13130pub trait RowExt {
13131 fn as_f32(&self) -> f32;
13132
13133 fn next_row(&self) -> Self;
13134
13135 fn previous_row(&self) -> Self;
13136
13137 fn minus(&self, other: Self) -> u32;
13138}
13139
13140impl RowExt for DisplayRow {
13141 fn as_f32(&self) -> f32 {
13142 self.0 as f32
13143 }
13144
13145 fn next_row(&self) -> Self {
13146 Self(self.0 + 1)
13147 }
13148
13149 fn previous_row(&self) -> Self {
13150 Self(self.0.saturating_sub(1))
13151 }
13152
13153 fn minus(&self, other: Self) -> u32 {
13154 self.0 - other.0
13155 }
13156}
13157
13158impl RowExt for MultiBufferRow {
13159 fn as_f32(&self) -> f32 {
13160 self.0 as f32
13161 }
13162
13163 fn next_row(&self) -> Self {
13164 Self(self.0 + 1)
13165 }
13166
13167 fn previous_row(&self) -> Self {
13168 Self(self.0.saturating_sub(1))
13169 }
13170
13171 fn minus(&self, other: Self) -> u32 {
13172 self.0 - other.0
13173 }
13174}
13175
13176trait RowRangeExt {
13177 type Row;
13178
13179 fn len(&self) -> usize;
13180
13181 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
13182}
13183
13184impl RowRangeExt for Range<MultiBufferRow> {
13185 type Row = MultiBufferRow;
13186
13187 fn len(&self) -> usize {
13188 (self.end.0 - self.start.0) as usize
13189 }
13190
13191 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
13192 (self.start.0..self.end.0).map(MultiBufferRow)
13193 }
13194}
13195
13196impl RowRangeExt for Range<DisplayRow> {
13197 type Row = DisplayRow;
13198
13199 fn len(&self) -> usize {
13200 (self.end.0 - self.start.0) as usize
13201 }
13202
13203 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
13204 (self.start.0..self.end.0).map(DisplayRow)
13205 }
13206}
13207
13208fn hunk_status(hunk: &DiffHunk<MultiBufferRow>) -> DiffHunkStatus {
13209 if hunk.diff_base_byte_range.is_empty() {
13210 DiffHunkStatus::Added
13211 } else if hunk.associated_range.is_empty() {
13212 DiffHunkStatus::Removed
13213 } else {
13214 DiffHunkStatus::Modified
13215 }
13216}