1#![allow(rustdoc::private_intra_doc_links)]
2//! This is the place where everything editor-related is stored (data-wise) and displayed (ui-wise).
3//! The main point of interest in this crate is [`Editor`] type, which is used in every other Zed part as a user input element.
4//! It comes in different flavors: single line, multiline and a fixed height one.
5//!
6//! Editor contains of multiple large submodules:
7//! * [`element`] — the place where all rendering happens
8//! * [`display_map`] - chunks up text in the editor into the logical blocks, establishes coordinates and mapping between each of them.
9//! Contains all metadata related to text transformations (folds, fake inlay text insertions, soft wraps, tab markup, etc.).
10//! * [`inlay_hint_cache`] - is a storage of inlay hints out of LSP requests, responsible for querying LSP and updating `display_map`'s state accordingly.
11//!
12//! All other submodules and structs are mostly concerned with holding editor data about the way it displays current buffer region(s).
13//!
14//! If you're looking to improve Vim mode, you should check out Vim crate that wraps Editor and overrides its behavior.
15pub mod actions;
16mod blame_entry_tooltip;
17mod blink_manager;
18mod debounced_delay;
19pub mod display_map;
20mod editor_settings;
21mod editor_settings_controls;
22mod element;
23mod git;
24mod highlight_matching_bracket;
25mod hover_links;
26mod hover_popover;
27mod hunk_diff;
28mod indent_guides;
29mod inlay_hint_cache;
30mod inline_completion_provider;
31pub mod items;
32mod linked_editing_ranges;
33mod mouse_context_menu;
34pub mod movement;
35mod persistence;
36mod rust_analyzer_ext;
37pub mod scroll;
38mod selections_collection;
39pub mod tasks;
40
41#[cfg(test)]
42mod editor_tests;
43mod signature_help;
44#[cfg(any(test, feature = "test-support"))]
45pub mod test;
46
47use ::git::diff::{DiffHunk, DiffHunkStatus};
48use ::git::{parse_git_remote_url, BuildPermalinkParams, GitHostingProviderRegistry};
49pub(crate) use actions::*;
50use aho_corasick::AhoCorasick;
51use anyhow::{anyhow, Context as _, Result};
52use blink_manager::BlinkManager;
53use client::{Collaborator, ParticipantIndex};
54use clock::ReplicaId;
55use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
56use convert_case::{Case, Casing};
57use debounced_delay::DebouncedDelay;
58use display_map::*;
59pub use display_map::{DisplayPoint, FoldPlaceholder};
60pub use editor_settings::{CurrentLineHighlight, EditorSettings};
61pub use editor_settings_controls::*;
62use element::LineWithInvisibles;
63pub use element::{
64 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
65};
66use futures::FutureExt;
67use fuzzy::{StringMatch, StringMatchCandidate};
68use git::blame::GitBlame;
69use git::diff_hunk_to_display;
70use gpui::{
71 div, impl_actions, point, prelude::*, px, relative, size, uniform_list, Action, AnyElement,
72 AppContext, AsyncWindowContext, AvailableSpace, BackgroundExecutor, Bounds, ClipboardEntry,
73 ClipboardItem, Context, DispatchPhase, ElementId, EntityId, EventEmitter, FocusHandle,
74 FocusOutEvent, FocusableView, FontId, FontWeight, HighlightStyle, Hsla, InteractiveText,
75 KeyContext, ListSizingBehavior, Model, MouseButton, PaintQuad, ParentElement, Pixels, Render,
76 SharedString, Size, StrikethroughStyle, Styled, StyledText, Subscription, Task, TextStyle,
77 UnderlineStyle, UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext,
78 WeakFocusHandle, WeakView, WindowContext,
79};
80use highlight_matching_bracket::refresh_matching_bracket_highlights;
81use hover_popover::{hide_hover, HoverState};
82use hunk_diff::ExpandedHunks;
83pub(crate) use hunk_diff::HoveredHunk;
84use indent_guides::ActiveIndentGuidesState;
85use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
86pub use inline_completion_provider::*;
87pub use items::MAX_TAB_TITLE_LEN;
88use itertools::Itertools;
89use language::{
90 char_kind,
91 language_settings::{self, all_language_settings, InlayHintSettings},
92 markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
93 CursorShape, Diagnostic, Documentation, IndentKind, IndentSize, Language, OffsetRangeExt,
94 Point, Selection, SelectionGoal, TransactionId,
95};
96use language::{point_to_lsp, BufferRow, Runnable, RunnableRange};
97use linked_editing_ranges::refresh_linked_ranges;
98use task::{ResolvedTask, TaskTemplate, TaskVariables};
99
100use hover_links::{HoverLink, HoveredLinkState, InlayHighlight};
101pub use lsp::CompletionContext;
102use lsp::{
103 CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity, InsertTextFormat,
104 LanguageServerId,
105};
106use mouse_context_menu::MouseContextMenu;
107use movement::TextLayoutDetails;
108pub use multi_buffer::{
109 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
110 ToPoint,
111};
112use multi_buffer::{ExpandExcerptDirection, MultiBufferPoint, MultiBufferRow, ToOffsetUtf16};
113use ordered_float::OrderedFloat;
114use parking_lot::{Mutex, RwLock};
115use project::project_settings::{GitGutterSetting, ProjectSettings};
116use project::{
117 CodeAction, Completion, 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_string(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_file(&mut self, _: &RevertFile, cx: &mut ViewContext<Self>) {
5937 let mut revert_changes = HashMap::default();
5938 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
5939 for hunk in hunks_for_rows(
5940 Some(MultiBufferRow(0)..multi_buffer_snapshot.max_buffer_row()).into_iter(),
5941 &multi_buffer_snapshot,
5942 ) {
5943 Self::prepare_revert_change(&mut revert_changes, &self.buffer(), &hunk, cx);
5944 }
5945 if !revert_changes.is_empty() {
5946 self.transact(cx, |editor, cx| {
5947 editor.revert(revert_changes, cx);
5948 });
5949 }
5950 }
5951
5952 pub fn revert_selected_hunks(&mut self, _: &RevertSelectedHunks, cx: &mut ViewContext<Self>) {
5953 let revert_changes = self.gather_revert_changes(&self.selections.disjoint_anchors(), cx);
5954 if !revert_changes.is_empty() {
5955 self.transact(cx, |editor, cx| {
5956 editor.revert(revert_changes, cx);
5957 });
5958 }
5959 }
5960
5961 pub fn open_active_item_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
5962 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
5963 let project_path = buffer.read(cx).project_path(cx)?;
5964 let project = self.project.as_ref()?.read(cx);
5965 let entry = project.entry_for_path(&project_path, cx)?;
5966 let abs_path = project.absolute_path(&project_path, cx)?;
5967 let parent = if entry.is_symlink {
5968 abs_path.canonicalize().ok()?
5969 } else {
5970 abs_path
5971 }
5972 .parent()?
5973 .to_path_buf();
5974 Some(parent)
5975 }) {
5976 cx.dispatch_action(OpenTerminal { working_directory }.boxed_clone());
5977 }
5978 }
5979
5980 fn gather_revert_changes(
5981 &mut self,
5982 selections: &[Selection<Anchor>],
5983 cx: &mut ViewContext<'_, Editor>,
5984 ) -> HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>> {
5985 let mut revert_changes = HashMap::default();
5986 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
5987 for hunk in hunks_for_selections(&multi_buffer_snapshot, selections) {
5988 Self::prepare_revert_change(&mut revert_changes, self.buffer(), &hunk, cx);
5989 }
5990 revert_changes
5991 }
5992
5993 pub fn prepare_revert_change(
5994 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
5995 multi_buffer: &Model<MultiBuffer>,
5996 hunk: &DiffHunk<MultiBufferRow>,
5997 cx: &AppContext,
5998 ) -> Option<()> {
5999 let buffer = multi_buffer.read(cx).buffer(hunk.buffer_id)?;
6000 let buffer = buffer.read(cx);
6001 let original_text = buffer.diff_base()?.slice(hunk.diff_base_byte_range.clone());
6002 let buffer_snapshot = buffer.snapshot();
6003 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
6004 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
6005 probe
6006 .0
6007 .start
6008 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
6009 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
6010 }) {
6011 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
6012 Some(())
6013 } else {
6014 None
6015 }
6016 }
6017
6018 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
6019 self.manipulate_lines(cx, |lines| lines.reverse())
6020 }
6021
6022 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
6023 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
6024 }
6025
6026 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6027 where
6028 Fn: FnMut(&mut Vec<&str>),
6029 {
6030 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6031 let buffer = self.buffer.read(cx).snapshot(cx);
6032
6033 let mut edits = Vec::new();
6034
6035 let selections = self.selections.all::<Point>(cx);
6036 let mut selections = selections.iter().peekable();
6037 let mut contiguous_row_selections = Vec::new();
6038 let mut new_selections = Vec::new();
6039 let mut added_lines = 0;
6040 let mut removed_lines = 0;
6041
6042 while let Some(selection) = selections.next() {
6043 let (start_row, end_row) = consume_contiguous_rows(
6044 &mut contiguous_row_selections,
6045 selection,
6046 &display_map,
6047 &mut selections,
6048 );
6049
6050 let start_point = Point::new(start_row.0, 0);
6051 let end_point = Point::new(
6052 end_row.previous_row().0,
6053 buffer.line_len(end_row.previous_row()),
6054 );
6055 let text = buffer
6056 .text_for_range(start_point..end_point)
6057 .collect::<String>();
6058
6059 let mut lines = text.split('\n').collect_vec();
6060
6061 let lines_before = lines.len();
6062 callback(&mut lines);
6063 let lines_after = lines.len();
6064
6065 edits.push((start_point..end_point, lines.join("\n")));
6066
6067 // Selections must change based on added and removed line count
6068 let start_row =
6069 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
6070 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
6071 new_selections.push(Selection {
6072 id: selection.id,
6073 start: start_row,
6074 end: end_row,
6075 goal: SelectionGoal::None,
6076 reversed: selection.reversed,
6077 });
6078
6079 if lines_after > lines_before {
6080 added_lines += lines_after - lines_before;
6081 } else if lines_before > lines_after {
6082 removed_lines += lines_before - lines_after;
6083 }
6084 }
6085
6086 self.transact(cx, |this, cx| {
6087 let buffer = this.buffer.update(cx, |buffer, cx| {
6088 buffer.edit(edits, None, cx);
6089 buffer.snapshot(cx)
6090 });
6091
6092 // Recalculate offsets on newly edited buffer
6093 let new_selections = new_selections
6094 .iter()
6095 .map(|s| {
6096 let start_point = Point::new(s.start.0, 0);
6097 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
6098 Selection {
6099 id: s.id,
6100 start: buffer.point_to_offset(start_point),
6101 end: buffer.point_to_offset(end_point),
6102 goal: s.goal,
6103 reversed: s.reversed,
6104 }
6105 })
6106 .collect();
6107
6108 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6109 s.select(new_selections);
6110 });
6111
6112 this.request_autoscroll(Autoscroll::fit(), cx);
6113 });
6114 }
6115
6116 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
6117 self.manipulate_text(cx, |text| text.to_uppercase())
6118 }
6119
6120 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
6121 self.manipulate_text(cx, |text| text.to_lowercase())
6122 }
6123
6124 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
6125 self.manipulate_text(cx, |text| {
6126 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6127 // https://github.com/rutrum/convert-case/issues/16
6128 text.split('\n')
6129 .map(|line| line.to_case(Case::Title))
6130 .join("\n")
6131 })
6132 }
6133
6134 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
6135 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
6136 }
6137
6138 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
6139 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
6140 }
6141
6142 pub fn convert_to_upper_camel_case(
6143 &mut self,
6144 _: &ConvertToUpperCamelCase,
6145 cx: &mut ViewContext<Self>,
6146 ) {
6147 self.manipulate_text(cx, |text| {
6148 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6149 // https://github.com/rutrum/convert-case/issues/16
6150 text.split('\n')
6151 .map(|line| line.to_case(Case::UpperCamel))
6152 .join("\n")
6153 })
6154 }
6155
6156 pub fn convert_to_lower_camel_case(
6157 &mut self,
6158 _: &ConvertToLowerCamelCase,
6159 cx: &mut ViewContext<Self>,
6160 ) {
6161 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
6162 }
6163
6164 pub fn convert_to_opposite_case(
6165 &mut self,
6166 _: &ConvertToOppositeCase,
6167 cx: &mut ViewContext<Self>,
6168 ) {
6169 self.manipulate_text(cx, |text| {
6170 text.chars()
6171 .fold(String::with_capacity(text.len()), |mut t, c| {
6172 if c.is_uppercase() {
6173 t.extend(c.to_lowercase());
6174 } else {
6175 t.extend(c.to_uppercase());
6176 }
6177 t
6178 })
6179 })
6180 }
6181
6182 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6183 where
6184 Fn: FnMut(&str) -> String,
6185 {
6186 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6187 let buffer = self.buffer.read(cx).snapshot(cx);
6188
6189 let mut new_selections = Vec::new();
6190 let mut edits = Vec::new();
6191 let mut selection_adjustment = 0i32;
6192
6193 for selection in self.selections.all::<usize>(cx) {
6194 let selection_is_empty = selection.is_empty();
6195
6196 let (start, end) = if selection_is_empty {
6197 let word_range = movement::surrounding_word(
6198 &display_map,
6199 selection.start.to_display_point(&display_map),
6200 );
6201 let start = word_range.start.to_offset(&display_map, Bias::Left);
6202 let end = word_range.end.to_offset(&display_map, Bias::Left);
6203 (start, end)
6204 } else {
6205 (selection.start, selection.end)
6206 };
6207
6208 let text = buffer.text_for_range(start..end).collect::<String>();
6209 let old_length = text.len() as i32;
6210 let text = callback(&text);
6211
6212 new_selections.push(Selection {
6213 start: (start as i32 - selection_adjustment) as usize,
6214 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
6215 goal: SelectionGoal::None,
6216 ..selection
6217 });
6218
6219 selection_adjustment += old_length - text.len() as i32;
6220
6221 edits.push((start..end, text));
6222 }
6223
6224 self.transact(cx, |this, cx| {
6225 this.buffer.update(cx, |buffer, cx| {
6226 buffer.edit(edits, None, cx);
6227 });
6228
6229 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6230 s.select(new_selections);
6231 });
6232
6233 this.request_autoscroll(Autoscroll::fit(), cx);
6234 });
6235 }
6236
6237 pub fn duplicate_line(&mut self, upwards: bool, cx: &mut ViewContext<Self>) {
6238 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6239 let buffer = &display_map.buffer_snapshot;
6240 let selections = self.selections.all::<Point>(cx);
6241
6242 let mut edits = Vec::new();
6243 let mut selections_iter = selections.iter().peekable();
6244 while let Some(selection) = selections_iter.next() {
6245 // Avoid duplicating the same lines twice.
6246 let mut rows = selection.spanned_rows(false, &display_map);
6247
6248 while let Some(next_selection) = selections_iter.peek() {
6249 let next_rows = next_selection.spanned_rows(false, &display_map);
6250 if next_rows.start < rows.end {
6251 rows.end = next_rows.end;
6252 selections_iter.next().unwrap();
6253 } else {
6254 break;
6255 }
6256 }
6257
6258 // Copy the text from the selected row region and splice it either at the start
6259 // or end of the region.
6260 let start = Point::new(rows.start.0, 0);
6261 let end = Point::new(
6262 rows.end.previous_row().0,
6263 buffer.line_len(rows.end.previous_row()),
6264 );
6265 let text = buffer
6266 .text_for_range(start..end)
6267 .chain(Some("\n"))
6268 .collect::<String>();
6269 let insert_location = if upwards {
6270 Point::new(rows.end.0, 0)
6271 } else {
6272 start
6273 };
6274 edits.push((insert_location..insert_location, text));
6275 }
6276
6277 self.transact(cx, |this, cx| {
6278 this.buffer.update(cx, |buffer, cx| {
6279 buffer.edit(edits, None, cx);
6280 });
6281
6282 this.request_autoscroll(Autoscroll::fit(), cx);
6283 });
6284 }
6285
6286 pub fn duplicate_line_up(&mut self, _: &DuplicateLineUp, cx: &mut ViewContext<Self>) {
6287 self.duplicate_line(true, cx);
6288 }
6289
6290 pub fn duplicate_line_down(&mut self, _: &DuplicateLineDown, cx: &mut ViewContext<Self>) {
6291 self.duplicate_line(false, cx);
6292 }
6293
6294 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
6295 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6296 let buffer = self.buffer.read(cx).snapshot(cx);
6297
6298 let mut edits = Vec::new();
6299 let mut unfold_ranges = Vec::new();
6300 let mut refold_ranges = Vec::new();
6301
6302 let selections = self.selections.all::<Point>(cx);
6303 let mut selections = selections.iter().peekable();
6304 let mut contiguous_row_selections = Vec::new();
6305 let mut new_selections = Vec::new();
6306
6307 while let Some(selection) = selections.next() {
6308 // Find all the selections that span a contiguous row range
6309 let (start_row, end_row) = consume_contiguous_rows(
6310 &mut contiguous_row_selections,
6311 selection,
6312 &display_map,
6313 &mut selections,
6314 );
6315
6316 // Move the text spanned by the row range to be before the line preceding the row range
6317 if start_row.0 > 0 {
6318 let range_to_move = Point::new(
6319 start_row.previous_row().0,
6320 buffer.line_len(start_row.previous_row()),
6321 )
6322 ..Point::new(
6323 end_row.previous_row().0,
6324 buffer.line_len(end_row.previous_row()),
6325 );
6326 let insertion_point = display_map
6327 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
6328 .0;
6329
6330 // Don't move lines across excerpts
6331 if buffer
6332 .excerpt_boundaries_in_range((
6333 Bound::Excluded(insertion_point),
6334 Bound::Included(range_to_move.end),
6335 ))
6336 .next()
6337 .is_none()
6338 {
6339 let text = buffer
6340 .text_for_range(range_to_move.clone())
6341 .flat_map(|s| s.chars())
6342 .skip(1)
6343 .chain(['\n'])
6344 .collect::<String>();
6345
6346 edits.push((
6347 buffer.anchor_after(range_to_move.start)
6348 ..buffer.anchor_before(range_to_move.end),
6349 String::new(),
6350 ));
6351 let insertion_anchor = buffer.anchor_after(insertion_point);
6352 edits.push((insertion_anchor..insertion_anchor, text));
6353
6354 let row_delta = range_to_move.start.row - insertion_point.row + 1;
6355
6356 // Move selections up
6357 new_selections.extend(contiguous_row_selections.drain(..).map(
6358 |mut selection| {
6359 selection.start.row -= row_delta;
6360 selection.end.row -= row_delta;
6361 selection
6362 },
6363 ));
6364
6365 // Move folds up
6366 unfold_ranges.push(range_to_move.clone());
6367 for fold in display_map.folds_in_range(
6368 buffer.anchor_before(range_to_move.start)
6369 ..buffer.anchor_after(range_to_move.end),
6370 ) {
6371 let mut start = fold.range.start.to_point(&buffer);
6372 let mut end = fold.range.end.to_point(&buffer);
6373 start.row -= row_delta;
6374 end.row -= row_delta;
6375 refold_ranges.push((start..end, fold.placeholder.clone()));
6376 }
6377 }
6378 }
6379
6380 // If we didn't move line(s), preserve the existing selections
6381 new_selections.append(&mut contiguous_row_selections);
6382 }
6383
6384 self.transact(cx, |this, cx| {
6385 this.unfold_ranges(unfold_ranges, true, true, cx);
6386 this.buffer.update(cx, |buffer, cx| {
6387 for (range, text) in edits {
6388 buffer.edit([(range, text)], None, cx);
6389 }
6390 });
6391 this.fold_ranges(refold_ranges, true, cx);
6392 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6393 s.select(new_selections);
6394 })
6395 });
6396 }
6397
6398 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
6399 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6400 let buffer = self.buffer.read(cx).snapshot(cx);
6401
6402 let mut edits = Vec::new();
6403 let mut unfold_ranges = Vec::new();
6404 let mut refold_ranges = Vec::new();
6405
6406 let selections = self.selections.all::<Point>(cx);
6407 let mut selections = selections.iter().peekable();
6408 let mut contiguous_row_selections = Vec::new();
6409 let mut new_selections = Vec::new();
6410
6411 while let Some(selection) = selections.next() {
6412 // Find all the selections that span a contiguous row range
6413 let (start_row, end_row) = consume_contiguous_rows(
6414 &mut contiguous_row_selections,
6415 selection,
6416 &display_map,
6417 &mut selections,
6418 );
6419
6420 // Move the text spanned by the row range to be after the last line of the row range
6421 if end_row.0 <= buffer.max_point().row {
6422 let range_to_move =
6423 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
6424 let insertion_point = display_map
6425 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
6426 .0;
6427
6428 // Don't move lines across excerpt boundaries
6429 if buffer
6430 .excerpt_boundaries_in_range((
6431 Bound::Excluded(range_to_move.start),
6432 Bound::Included(insertion_point),
6433 ))
6434 .next()
6435 .is_none()
6436 {
6437 let mut text = String::from("\n");
6438 text.extend(buffer.text_for_range(range_to_move.clone()));
6439 text.pop(); // Drop trailing newline
6440 edits.push((
6441 buffer.anchor_after(range_to_move.start)
6442 ..buffer.anchor_before(range_to_move.end),
6443 String::new(),
6444 ));
6445 let insertion_anchor = buffer.anchor_after(insertion_point);
6446 edits.push((insertion_anchor..insertion_anchor, text));
6447
6448 let row_delta = insertion_point.row - range_to_move.end.row + 1;
6449
6450 // Move selections down
6451 new_selections.extend(contiguous_row_selections.drain(..).map(
6452 |mut selection| {
6453 selection.start.row += row_delta;
6454 selection.end.row += row_delta;
6455 selection
6456 },
6457 ));
6458
6459 // Move folds down
6460 unfold_ranges.push(range_to_move.clone());
6461 for fold in display_map.folds_in_range(
6462 buffer.anchor_before(range_to_move.start)
6463 ..buffer.anchor_after(range_to_move.end),
6464 ) {
6465 let mut start = fold.range.start.to_point(&buffer);
6466 let mut end = fold.range.end.to_point(&buffer);
6467 start.row += row_delta;
6468 end.row += row_delta;
6469 refold_ranges.push((start..end, fold.placeholder.clone()));
6470 }
6471 }
6472 }
6473
6474 // If we didn't move line(s), preserve the existing selections
6475 new_selections.append(&mut contiguous_row_selections);
6476 }
6477
6478 self.transact(cx, |this, cx| {
6479 this.unfold_ranges(unfold_ranges, true, true, cx);
6480 this.buffer.update(cx, |buffer, cx| {
6481 for (range, text) in edits {
6482 buffer.edit([(range, text)], None, cx);
6483 }
6484 });
6485 this.fold_ranges(refold_ranges, true, cx);
6486 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
6487 });
6488 }
6489
6490 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
6491 let text_layout_details = &self.text_layout_details(cx);
6492 self.transact(cx, |this, cx| {
6493 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6494 let mut edits: Vec<(Range<usize>, String)> = Default::default();
6495 let line_mode = s.line_mode;
6496 s.move_with(|display_map, selection| {
6497 if !selection.is_empty() || line_mode {
6498 return;
6499 }
6500
6501 let mut head = selection.head();
6502 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
6503 if head.column() == display_map.line_len(head.row()) {
6504 transpose_offset = display_map
6505 .buffer_snapshot
6506 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6507 }
6508
6509 if transpose_offset == 0 {
6510 return;
6511 }
6512
6513 *head.column_mut() += 1;
6514 head = display_map.clip_point(head, Bias::Right);
6515 let goal = SelectionGoal::HorizontalPosition(
6516 display_map
6517 .x_for_display_point(head, &text_layout_details)
6518 .into(),
6519 );
6520 selection.collapse_to(head, goal);
6521
6522 let transpose_start = display_map
6523 .buffer_snapshot
6524 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6525 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
6526 let transpose_end = display_map
6527 .buffer_snapshot
6528 .clip_offset(transpose_offset + 1, Bias::Right);
6529 if let Some(ch) =
6530 display_map.buffer_snapshot.chars_at(transpose_start).next()
6531 {
6532 edits.push((transpose_start..transpose_offset, String::new()));
6533 edits.push((transpose_end..transpose_end, ch.to_string()));
6534 }
6535 }
6536 });
6537 edits
6538 });
6539 this.buffer
6540 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
6541 let selections = this.selections.all::<usize>(cx);
6542 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6543 s.select(selections);
6544 });
6545 });
6546 }
6547
6548 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
6549 let mut text = String::new();
6550 let buffer = self.buffer.read(cx).snapshot(cx);
6551 let mut selections = self.selections.all::<Point>(cx);
6552 let mut clipboard_selections = Vec::with_capacity(selections.len());
6553 {
6554 let max_point = buffer.max_point();
6555 let mut is_first = true;
6556 for selection in &mut selections {
6557 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6558 if is_entire_line {
6559 selection.start = Point::new(selection.start.row, 0);
6560 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
6561 selection.goal = SelectionGoal::None;
6562 }
6563 if is_first {
6564 is_first = false;
6565 } else {
6566 text += "\n";
6567 }
6568 let mut len = 0;
6569 for chunk in buffer.text_for_range(selection.start..selection.end) {
6570 text.push_str(chunk);
6571 len += chunk.len();
6572 }
6573 clipboard_selections.push(ClipboardSelection {
6574 len,
6575 is_entire_line,
6576 first_line_indent: buffer
6577 .indent_size_for_line(MultiBufferRow(selection.start.row))
6578 .len,
6579 });
6580 }
6581 }
6582
6583 self.transact(cx, |this, cx| {
6584 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6585 s.select(selections);
6586 });
6587 this.insert("", cx);
6588 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
6589 text,
6590 clipboard_selections,
6591 ));
6592 });
6593 }
6594
6595 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
6596 let selections = self.selections.all::<Point>(cx);
6597 let buffer = self.buffer.read(cx).read(cx);
6598 let mut text = String::new();
6599
6600 let mut clipboard_selections = Vec::with_capacity(selections.len());
6601 {
6602 let max_point = buffer.max_point();
6603 let mut is_first = true;
6604 for selection in selections.iter() {
6605 let mut start = selection.start;
6606 let mut end = selection.end;
6607 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6608 if is_entire_line {
6609 start = Point::new(start.row, 0);
6610 end = cmp::min(max_point, Point::new(end.row + 1, 0));
6611 }
6612 if is_first {
6613 is_first = false;
6614 } else {
6615 text += "\n";
6616 }
6617 let mut len = 0;
6618 for chunk in buffer.text_for_range(start..end) {
6619 text.push_str(chunk);
6620 len += chunk.len();
6621 }
6622 clipboard_selections.push(ClipboardSelection {
6623 len,
6624 is_entire_line,
6625 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
6626 });
6627 }
6628 }
6629
6630 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
6631 text,
6632 clipboard_selections,
6633 ));
6634 }
6635
6636 pub fn do_paste(
6637 &mut self,
6638 text: &String,
6639 clipboard_selections: Option<Vec<ClipboardSelection>>,
6640 handle_entire_lines: bool,
6641 cx: &mut ViewContext<Self>,
6642 ) {
6643 if self.read_only(cx) {
6644 return;
6645 }
6646
6647 let clipboard_text = Cow::Borrowed(text);
6648
6649 self.transact(cx, |this, cx| {
6650 if let Some(mut clipboard_selections) = clipboard_selections {
6651 let old_selections = this.selections.all::<usize>(cx);
6652 let all_selections_were_entire_line =
6653 clipboard_selections.iter().all(|s| s.is_entire_line);
6654 let first_selection_indent_column =
6655 clipboard_selections.first().map(|s| s.first_line_indent);
6656 if clipboard_selections.len() != old_selections.len() {
6657 clipboard_selections.drain(..);
6658 }
6659
6660 this.buffer.update(cx, |buffer, cx| {
6661 let snapshot = buffer.read(cx);
6662 let mut start_offset = 0;
6663 let mut edits = Vec::new();
6664 let mut original_indent_columns = Vec::new();
6665 for (ix, selection) in old_selections.iter().enumerate() {
6666 let to_insert;
6667 let entire_line;
6668 let original_indent_column;
6669 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
6670 let end_offset = start_offset + clipboard_selection.len;
6671 to_insert = &clipboard_text[start_offset..end_offset];
6672 entire_line = clipboard_selection.is_entire_line;
6673 start_offset = end_offset + 1;
6674 original_indent_column = Some(clipboard_selection.first_line_indent);
6675 } else {
6676 to_insert = clipboard_text.as_str();
6677 entire_line = all_selections_were_entire_line;
6678 original_indent_column = first_selection_indent_column
6679 }
6680
6681 // If the corresponding selection was empty when this slice of the
6682 // clipboard text was written, then the entire line containing the
6683 // selection was copied. If this selection is also currently empty,
6684 // then paste the line before the current line of the buffer.
6685 let range = if selection.is_empty() && handle_entire_lines && entire_line {
6686 let column = selection.start.to_point(&snapshot).column as usize;
6687 let line_start = selection.start - column;
6688 line_start..line_start
6689 } else {
6690 selection.range()
6691 };
6692
6693 edits.push((range, to_insert));
6694 original_indent_columns.extend(original_indent_column);
6695 }
6696 drop(snapshot);
6697
6698 buffer.edit(
6699 edits,
6700 Some(AutoindentMode::Block {
6701 original_indent_columns,
6702 }),
6703 cx,
6704 );
6705 });
6706
6707 let selections = this.selections.all::<usize>(cx);
6708 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6709 } else {
6710 this.insert(&clipboard_text, cx);
6711 }
6712 });
6713 }
6714
6715 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
6716 if let Some(item) = cx.read_from_clipboard() {
6717 let entries = item.entries();
6718
6719 match entries.first() {
6720 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
6721 // of all the pasted entries.
6722 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
6723 .do_paste(
6724 clipboard_string.text(),
6725 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
6726 true,
6727 cx,
6728 ),
6729 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, cx),
6730 }
6731 }
6732 }
6733
6734 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
6735 if self.read_only(cx) {
6736 return;
6737 }
6738
6739 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
6740 if let Some((selections, _)) =
6741 self.selection_history.transaction(transaction_id).cloned()
6742 {
6743 self.change_selections(None, cx, |s| {
6744 s.select_anchors(selections.to_vec());
6745 });
6746 }
6747 self.request_autoscroll(Autoscroll::fit(), cx);
6748 self.unmark_text(cx);
6749 self.refresh_inline_completion(true, cx);
6750 cx.emit(EditorEvent::Edited { transaction_id });
6751 cx.emit(EditorEvent::TransactionUndone { transaction_id });
6752 }
6753 }
6754
6755 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
6756 if self.read_only(cx) {
6757 return;
6758 }
6759
6760 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
6761 if let Some((_, Some(selections))) =
6762 self.selection_history.transaction(transaction_id).cloned()
6763 {
6764 self.change_selections(None, cx, |s| {
6765 s.select_anchors(selections.to_vec());
6766 });
6767 }
6768 self.request_autoscroll(Autoscroll::fit(), cx);
6769 self.unmark_text(cx);
6770 self.refresh_inline_completion(true, cx);
6771 cx.emit(EditorEvent::Edited { transaction_id });
6772 }
6773 }
6774
6775 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
6776 self.buffer
6777 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
6778 }
6779
6780 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut ViewContext<Self>) {
6781 self.buffer
6782 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
6783 }
6784
6785 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
6786 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6787 let line_mode = s.line_mode;
6788 s.move_with(|map, selection| {
6789 let cursor = if selection.is_empty() && !line_mode {
6790 movement::left(map, selection.start)
6791 } else {
6792 selection.start
6793 };
6794 selection.collapse_to(cursor, SelectionGoal::None);
6795 });
6796 })
6797 }
6798
6799 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
6800 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6801 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
6802 })
6803 }
6804
6805 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
6806 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6807 let line_mode = s.line_mode;
6808 s.move_with(|map, selection| {
6809 let cursor = if selection.is_empty() && !line_mode {
6810 movement::right(map, selection.end)
6811 } else {
6812 selection.end
6813 };
6814 selection.collapse_to(cursor, SelectionGoal::None)
6815 });
6816 })
6817 }
6818
6819 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
6820 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6821 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
6822 })
6823 }
6824
6825 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
6826 if self.take_rename(true, cx).is_some() {
6827 return;
6828 }
6829
6830 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6831 cx.propagate();
6832 return;
6833 }
6834
6835 let text_layout_details = &self.text_layout_details(cx);
6836 let selection_count = self.selections.count();
6837 let first_selection = self.selections.first_anchor();
6838
6839 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6840 let line_mode = s.line_mode;
6841 s.move_with(|map, selection| {
6842 if !selection.is_empty() && !line_mode {
6843 selection.goal = SelectionGoal::None;
6844 }
6845 let (cursor, goal) = movement::up(
6846 map,
6847 selection.start,
6848 selection.goal,
6849 false,
6850 &text_layout_details,
6851 );
6852 selection.collapse_to(cursor, goal);
6853 });
6854 });
6855
6856 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
6857 {
6858 cx.propagate();
6859 }
6860 }
6861
6862 pub fn move_up_by_lines(&mut self, action: &MoveUpByLines, cx: &mut ViewContext<Self>) {
6863 if self.take_rename(true, cx).is_some() {
6864 return;
6865 }
6866
6867 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6868 cx.propagate();
6869 return;
6870 }
6871
6872 let text_layout_details = &self.text_layout_details(cx);
6873
6874 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6875 let line_mode = s.line_mode;
6876 s.move_with(|map, selection| {
6877 if !selection.is_empty() && !line_mode {
6878 selection.goal = SelectionGoal::None;
6879 }
6880 let (cursor, goal) = movement::up_by_rows(
6881 map,
6882 selection.start,
6883 action.lines,
6884 selection.goal,
6885 false,
6886 &text_layout_details,
6887 );
6888 selection.collapse_to(cursor, goal);
6889 });
6890 })
6891 }
6892
6893 pub fn move_down_by_lines(&mut self, action: &MoveDownByLines, cx: &mut ViewContext<Self>) {
6894 if self.take_rename(true, cx).is_some() {
6895 return;
6896 }
6897
6898 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6899 cx.propagate();
6900 return;
6901 }
6902
6903 let text_layout_details = &self.text_layout_details(cx);
6904
6905 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6906 let line_mode = s.line_mode;
6907 s.move_with(|map, selection| {
6908 if !selection.is_empty() && !line_mode {
6909 selection.goal = SelectionGoal::None;
6910 }
6911 let (cursor, goal) = movement::down_by_rows(
6912 map,
6913 selection.start,
6914 action.lines,
6915 selection.goal,
6916 false,
6917 &text_layout_details,
6918 );
6919 selection.collapse_to(cursor, goal);
6920 });
6921 })
6922 }
6923
6924 pub fn select_down_by_lines(&mut self, action: &SelectDownByLines, cx: &mut ViewContext<Self>) {
6925 let text_layout_details = &self.text_layout_details(cx);
6926 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6927 s.move_heads_with(|map, head, goal| {
6928 movement::down_by_rows(map, head, action.lines, goal, false, &text_layout_details)
6929 })
6930 })
6931 }
6932
6933 pub fn select_up_by_lines(&mut self, action: &SelectUpByLines, cx: &mut ViewContext<Self>) {
6934 let text_layout_details = &self.text_layout_details(cx);
6935 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6936 s.move_heads_with(|map, head, goal| {
6937 movement::up_by_rows(map, head, action.lines, goal, false, &text_layout_details)
6938 })
6939 })
6940 }
6941
6942 pub fn select_page_up(&mut self, _: &SelectPageUp, cx: &mut ViewContext<Self>) {
6943 let Some(row_count) = self.visible_row_count() else {
6944 return;
6945 };
6946
6947 let text_layout_details = &self.text_layout_details(cx);
6948
6949 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6950 s.move_heads_with(|map, head, goal| {
6951 movement::up_by_rows(map, head, row_count, goal, false, &text_layout_details)
6952 })
6953 })
6954 }
6955
6956 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
6957 if self.take_rename(true, cx).is_some() {
6958 return;
6959 }
6960
6961 if self
6962 .context_menu
6963 .write()
6964 .as_mut()
6965 .map(|menu| menu.select_first(self.project.as_ref(), cx))
6966 .unwrap_or(false)
6967 {
6968 return;
6969 }
6970
6971 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6972 cx.propagate();
6973 return;
6974 }
6975
6976 let Some(row_count) = self.visible_row_count() else {
6977 return;
6978 };
6979
6980 let autoscroll = if action.center_cursor {
6981 Autoscroll::center()
6982 } else {
6983 Autoscroll::fit()
6984 };
6985
6986 let text_layout_details = &self.text_layout_details(cx);
6987
6988 self.change_selections(Some(autoscroll), cx, |s| {
6989 let line_mode = s.line_mode;
6990 s.move_with(|map, selection| {
6991 if !selection.is_empty() && !line_mode {
6992 selection.goal = SelectionGoal::None;
6993 }
6994 let (cursor, goal) = movement::up_by_rows(
6995 map,
6996 selection.end,
6997 row_count,
6998 selection.goal,
6999 false,
7000 &text_layout_details,
7001 );
7002 selection.collapse_to(cursor, goal);
7003 });
7004 });
7005 }
7006
7007 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
7008 let text_layout_details = &self.text_layout_details(cx);
7009 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7010 s.move_heads_with(|map, head, goal| {
7011 movement::up(map, head, goal, false, &text_layout_details)
7012 })
7013 })
7014 }
7015
7016 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
7017 self.take_rename(true, cx);
7018
7019 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7020 cx.propagate();
7021 return;
7022 }
7023
7024 let text_layout_details = &self.text_layout_details(cx);
7025 let selection_count = self.selections.count();
7026 let first_selection = self.selections.first_anchor();
7027
7028 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7029 let line_mode = s.line_mode;
7030 s.move_with(|map, selection| {
7031 if !selection.is_empty() && !line_mode {
7032 selection.goal = SelectionGoal::None;
7033 }
7034 let (cursor, goal) = movement::down(
7035 map,
7036 selection.end,
7037 selection.goal,
7038 false,
7039 &text_layout_details,
7040 );
7041 selection.collapse_to(cursor, goal);
7042 });
7043 });
7044
7045 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7046 {
7047 cx.propagate();
7048 }
7049 }
7050
7051 pub fn select_page_down(&mut self, _: &SelectPageDown, cx: &mut ViewContext<Self>) {
7052 let Some(row_count) = self.visible_row_count() else {
7053 return;
7054 };
7055
7056 let text_layout_details = &self.text_layout_details(cx);
7057
7058 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7059 s.move_heads_with(|map, head, goal| {
7060 movement::down_by_rows(map, head, row_count, goal, false, &text_layout_details)
7061 })
7062 })
7063 }
7064
7065 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
7066 if self.take_rename(true, cx).is_some() {
7067 return;
7068 }
7069
7070 if self
7071 .context_menu
7072 .write()
7073 .as_mut()
7074 .map(|menu| menu.select_last(self.project.as_ref(), cx))
7075 .unwrap_or(false)
7076 {
7077 return;
7078 }
7079
7080 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7081 cx.propagate();
7082 return;
7083 }
7084
7085 let Some(row_count) = self.visible_row_count() else {
7086 return;
7087 };
7088
7089 let autoscroll = if action.center_cursor {
7090 Autoscroll::center()
7091 } else {
7092 Autoscroll::fit()
7093 };
7094
7095 let text_layout_details = &self.text_layout_details(cx);
7096 self.change_selections(Some(autoscroll), cx, |s| {
7097 let line_mode = s.line_mode;
7098 s.move_with(|map, selection| {
7099 if !selection.is_empty() && !line_mode {
7100 selection.goal = SelectionGoal::None;
7101 }
7102 let (cursor, goal) = movement::down_by_rows(
7103 map,
7104 selection.end,
7105 row_count,
7106 selection.goal,
7107 false,
7108 &text_layout_details,
7109 );
7110 selection.collapse_to(cursor, goal);
7111 });
7112 });
7113 }
7114
7115 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
7116 let text_layout_details = &self.text_layout_details(cx);
7117 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7118 s.move_heads_with(|map, head, goal| {
7119 movement::down(map, head, goal, false, &text_layout_details)
7120 })
7121 });
7122 }
7123
7124 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
7125 if let Some(context_menu) = self.context_menu.write().as_mut() {
7126 context_menu.select_first(self.project.as_ref(), cx);
7127 }
7128 }
7129
7130 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
7131 if let Some(context_menu) = self.context_menu.write().as_mut() {
7132 context_menu.select_prev(self.project.as_ref(), cx);
7133 }
7134 }
7135
7136 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
7137 if let Some(context_menu) = self.context_menu.write().as_mut() {
7138 context_menu.select_next(self.project.as_ref(), cx);
7139 }
7140 }
7141
7142 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
7143 if let Some(context_menu) = self.context_menu.write().as_mut() {
7144 context_menu.select_last(self.project.as_ref(), cx);
7145 }
7146 }
7147
7148 pub fn move_to_previous_word_start(
7149 &mut self,
7150 _: &MoveToPreviousWordStart,
7151 cx: &mut ViewContext<Self>,
7152 ) {
7153 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7154 s.move_cursors_with(|map, head, _| {
7155 (
7156 movement::previous_word_start(map, head),
7157 SelectionGoal::None,
7158 )
7159 });
7160 })
7161 }
7162
7163 pub fn move_to_previous_subword_start(
7164 &mut self,
7165 _: &MoveToPreviousSubwordStart,
7166 cx: &mut ViewContext<Self>,
7167 ) {
7168 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7169 s.move_cursors_with(|map, head, _| {
7170 (
7171 movement::previous_subword_start(map, head),
7172 SelectionGoal::None,
7173 )
7174 });
7175 })
7176 }
7177
7178 pub fn select_to_previous_word_start(
7179 &mut self,
7180 _: &SelectToPreviousWordStart,
7181 cx: &mut ViewContext<Self>,
7182 ) {
7183 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7184 s.move_heads_with(|map, head, _| {
7185 (
7186 movement::previous_word_start(map, head),
7187 SelectionGoal::None,
7188 )
7189 });
7190 })
7191 }
7192
7193 pub fn select_to_previous_subword_start(
7194 &mut self,
7195 _: &SelectToPreviousSubwordStart,
7196 cx: &mut ViewContext<Self>,
7197 ) {
7198 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7199 s.move_heads_with(|map, head, _| {
7200 (
7201 movement::previous_subword_start(map, head),
7202 SelectionGoal::None,
7203 )
7204 });
7205 })
7206 }
7207
7208 pub fn delete_to_previous_word_start(
7209 &mut self,
7210 _: &DeleteToPreviousWordStart,
7211 cx: &mut ViewContext<Self>,
7212 ) {
7213 self.transact(cx, |this, cx| {
7214 this.select_autoclose_pair(cx);
7215 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7216 let line_mode = s.line_mode;
7217 s.move_with(|map, selection| {
7218 if selection.is_empty() && !line_mode {
7219 let cursor = movement::previous_word_start(map, selection.head());
7220 selection.set_head(cursor, SelectionGoal::None);
7221 }
7222 });
7223 });
7224 this.insert("", cx);
7225 });
7226 }
7227
7228 pub fn delete_to_previous_subword_start(
7229 &mut self,
7230 _: &DeleteToPreviousSubwordStart,
7231 cx: &mut ViewContext<Self>,
7232 ) {
7233 self.transact(cx, |this, cx| {
7234 this.select_autoclose_pair(cx);
7235 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7236 let line_mode = s.line_mode;
7237 s.move_with(|map, selection| {
7238 if selection.is_empty() && !line_mode {
7239 let cursor = movement::previous_subword_start(map, selection.head());
7240 selection.set_head(cursor, SelectionGoal::None);
7241 }
7242 });
7243 });
7244 this.insert("", cx);
7245 });
7246 }
7247
7248 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
7249 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7250 s.move_cursors_with(|map, head, _| {
7251 (movement::next_word_end(map, head), SelectionGoal::None)
7252 });
7253 })
7254 }
7255
7256 pub fn move_to_next_subword_end(
7257 &mut self,
7258 _: &MoveToNextSubwordEnd,
7259 cx: &mut ViewContext<Self>,
7260 ) {
7261 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7262 s.move_cursors_with(|map, head, _| {
7263 (movement::next_subword_end(map, head), SelectionGoal::None)
7264 });
7265 })
7266 }
7267
7268 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
7269 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7270 s.move_heads_with(|map, head, _| {
7271 (movement::next_word_end(map, head), SelectionGoal::None)
7272 });
7273 })
7274 }
7275
7276 pub fn select_to_next_subword_end(
7277 &mut self,
7278 _: &SelectToNextSubwordEnd,
7279 cx: &mut ViewContext<Self>,
7280 ) {
7281 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7282 s.move_heads_with(|map, head, _| {
7283 (movement::next_subword_end(map, head), SelectionGoal::None)
7284 });
7285 })
7286 }
7287
7288 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
7289 self.transact(cx, |this, cx| {
7290 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7291 let line_mode = s.line_mode;
7292 s.move_with(|map, selection| {
7293 if selection.is_empty() && !line_mode {
7294 let cursor = movement::next_word_end(map, selection.head());
7295 selection.set_head(cursor, SelectionGoal::None);
7296 }
7297 });
7298 });
7299 this.insert("", cx);
7300 });
7301 }
7302
7303 pub fn delete_to_next_subword_end(
7304 &mut self,
7305 _: &DeleteToNextSubwordEnd,
7306 cx: &mut ViewContext<Self>,
7307 ) {
7308 self.transact(cx, |this, cx| {
7309 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7310 s.move_with(|map, selection| {
7311 if selection.is_empty() {
7312 let cursor = movement::next_subword_end(map, selection.head());
7313 selection.set_head(cursor, SelectionGoal::None);
7314 }
7315 });
7316 });
7317 this.insert("", cx);
7318 });
7319 }
7320
7321 pub fn move_to_beginning_of_line(
7322 &mut self,
7323 action: &MoveToBeginningOfLine,
7324 cx: &mut ViewContext<Self>,
7325 ) {
7326 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7327 s.move_cursors_with(|map, head, _| {
7328 (
7329 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7330 SelectionGoal::None,
7331 )
7332 });
7333 })
7334 }
7335
7336 pub fn select_to_beginning_of_line(
7337 &mut self,
7338 action: &SelectToBeginningOfLine,
7339 cx: &mut ViewContext<Self>,
7340 ) {
7341 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7342 s.move_heads_with(|map, head, _| {
7343 (
7344 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7345 SelectionGoal::None,
7346 )
7347 });
7348 });
7349 }
7350
7351 pub fn delete_to_beginning_of_line(
7352 &mut self,
7353 _: &DeleteToBeginningOfLine,
7354 cx: &mut ViewContext<Self>,
7355 ) {
7356 self.transact(cx, |this, cx| {
7357 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7358 s.move_with(|_, selection| {
7359 selection.reversed = true;
7360 });
7361 });
7362
7363 this.select_to_beginning_of_line(
7364 &SelectToBeginningOfLine {
7365 stop_at_soft_wraps: false,
7366 },
7367 cx,
7368 );
7369 this.backspace(&Backspace, cx);
7370 });
7371 }
7372
7373 pub fn move_to_end_of_line(&mut self, action: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
7374 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7375 s.move_cursors_with(|map, head, _| {
7376 (
7377 movement::line_end(map, head, action.stop_at_soft_wraps),
7378 SelectionGoal::None,
7379 )
7380 });
7381 })
7382 }
7383
7384 pub fn select_to_end_of_line(
7385 &mut self,
7386 action: &SelectToEndOfLine,
7387 cx: &mut ViewContext<Self>,
7388 ) {
7389 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7390 s.move_heads_with(|map, head, _| {
7391 (
7392 movement::line_end(map, head, action.stop_at_soft_wraps),
7393 SelectionGoal::None,
7394 )
7395 });
7396 })
7397 }
7398
7399 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
7400 self.transact(cx, |this, cx| {
7401 this.select_to_end_of_line(
7402 &SelectToEndOfLine {
7403 stop_at_soft_wraps: false,
7404 },
7405 cx,
7406 );
7407 this.delete(&Delete, cx);
7408 });
7409 }
7410
7411 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
7412 self.transact(cx, |this, cx| {
7413 this.select_to_end_of_line(
7414 &SelectToEndOfLine {
7415 stop_at_soft_wraps: false,
7416 },
7417 cx,
7418 );
7419 this.cut(&Cut, cx);
7420 });
7421 }
7422
7423 pub fn move_to_start_of_paragraph(
7424 &mut self,
7425 _: &MoveToStartOfParagraph,
7426 cx: &mut ViewContext<Self>,
7427 ) {
7428 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7429 cx.propagate();
7430 return;
7431 }
7432
7433 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7434 s.move_with(|map, selection| {
7435 selection.collapse_to(
7436 movement::start_of_paragraph(map, selection.head(), 1),
7437 SelectionGoal::None,
7438 )
7439 });
7440 })
7441 }
7442
7443 pub fn move_to_end_of_paragraph(
7444 &mut self,
7445 _: &MoveToEndOfParagraph,
7446 cx: &mut ViewContext<Self>,
7447 ) {
7448 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7449 cx.propagate();
7450 return;
7451 }
7452
7453 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7454 s.move_with(|map, selection| {
7455 selection.collapse_to(
7456 movement::end_of_paragraph(map, selection.head(), 1),
7457 SelectionGoal::None,
7458 )
7459 });
7460 })
7461 }
7462
7463 pub fn select_to_start_of_paragraph(
7464 &mut self,
7465 _: &SelectToStartOfParagraph,
7466 cx: &mut ViewContext<Self>,
7467 ) {
7468 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7469 cx.propagate();
7470 return;
7471 }
7472
7473 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7474 s.move_heads_with(|map, head, _| {
7475 (
7476 movement::start_of_paragraph(map, head, 1),
7477 SelectionGoal::None,
7478 )
7479 });
7480 })
7481 }
7482
7483 pub fn select_to_end_of_paragraph(
7484 &mut self,
7485 _: &SelectToEndOfParagraph,
7486 cx: &mut ViewContext<Self>,
7487 ) {
7488 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7489 cx.propagate();
7490 return;
7491 }
7492
7493 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7494 s.move_heads_with(|map, head, _| {
7495 (
7496 movement::end_of_paragraph(map, head, 1),
7497 SelectionGoal::None,
7498 )
7499 });
7500 })
7501 }
7502
7503 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
7504 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7505 cx.propagate();
7506 return;
7507 }
7508
7509 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7510 s.select_ranges(vec![0..0]);
7511 });
7512 }
7513
7514 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
7515 let mut selection = self.selections.last::<Point>(cx);
7516 selection.set_head(Point::zero(), SelectionGoal::None);
7517
7518 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7519 s.select(vec![selection]);
7520 });
7521 }
7522
7523 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
7524 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7525 cx.propagate();
7526 return;
7527 }
7528
7529 let cursor = self.buffer.read(cx).read(cx).len();
7530 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7531 s.select_ranges(vec![cursor..cursor])
7532 });
7533 }
7534
7535 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
7536 self.nav_history = nav_history;
7537 }
7538
7539 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
7540 self.nav_history.as_ref()
7541 }
7542
7543 fn push_to_nav_history(
7544 &mut self,
7545 cursor_anchor: Anchor,
7546 new_position: Option<Point>,
7547 cx: &mut ViewContext<Self>,
7548 ) {
7549 if let Some(nav_history) = self.nav_history.as_mut() {
7550 let buffer = self.buffer.read(cx).read(cx);
7551 let cursor_position = cursor_anchor.to_point(&buffer);
7552 let scroll_state = self.scroll_manager.anchor();
7553 let scroll_top_row = scroll_state.top_row(&buffer);
7554 drop(buffer);
7555
7556 if let Some(new_position) = new_position {
7557 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
7558 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
7559 return;
7560 }
7561 }
7562
7563 nav_history.push(
7564 Some(NavigationData {
7565 cursor_anchor,
7566 cursor_position,
7567 scroll_anchor: scroll_state,
7568 scroll_top_row,
7569 }),
7570 cx,
7571 );
7572 }
7573 }
7574
7575 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
7576 let buffer = self.buffer.read(cx).snapshot(cx);
7577 let mut selection = self.selections.first::<usize>(cx);
7578 selection.set_head(buffer.len(), SelectionGoal::None);
7579 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7580 s.select(vec![selection]);
7581 });
7582 }
7583
7584 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
7585 let end = self.buffer.read(cx).read(cx).len();
7586 self.change_selections(None, cx, |s| {
7587 s.select_ranges(vec![0..end]);
7588 });
7589 }
7590
7591 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
7592 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7593 let mut selections = self.selections.all::<Point>(cx);
7594 let max_point = display_map.buffer_snapshot.max_point();
7595 for selection in &mut selections {
7596 let rows = selection.spanned_rows(true, &display_map);
7597 selection.start = Point::new(rows.start.0, 0);
7598 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
7599 selection.reversed = false;
7600 }
7601 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7602 s.select(selections);
7603 });
7604 }
7605
7606 pub fn split_selection_into_lines(
7607 &mut self,
7608 _: &SplitSelectionIntoLines,
7609 cx: &mut ViewContext<Self>,
7610 ) {
7611 let mut to_unfold = Vec::new();
7612 let mut new_selection_ranges = Vec::new();
7613 {
7614 let selections = self.selections.all::<Point>(cx);
7615 let buffer = self.buffer.read(cx).read(cx);
7616 for selection in selections {
7617 for row in selection.start.row..selection.end.row {
7618 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
7619 new_selection_ranges.push(cursor..cursor);
7620 }
7621 new_selection_ranges.push(selection.end..selection.end);
7622 to_unfold.push(selection.start..selection.end);
7623 }
7624 }
7625 self.unfold_ranges(to_unfold, true, true, cx);
7626 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7627 s.select_ranges(new_selection_ranges);
7628 });
7629 }
7630
7631 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
7632 self.add_selection(true, cx);
7633 }
7634
7635 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
7636 self.add_selection(false, cx);
7637 }
7638
7639 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
7640 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7641 let mut selections = self.selections.all::<Point>(cx);
7642 let text_layout_details = self.text_layout_details(cx);
7643 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
7644 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
7645 let range = oldest_selection.display_range(&display_map).sorted();
7646
7647 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
7648 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
7649 let positions = start_x.min(end_x)..start_x.max(end_x);
7650
7651 selections.clear();
7652 let mut stack = Vec::new();
7653 for row in range.start.row().0..=range.end.row().0 {
7654 if let Some(selection) = self.selections.build_columnar_selection(
7655 &display_map,
7656 DisplayRow(row),
7657 &positions,
7658 oldest_selection.reversed,
7659 &text_layout_details,
7660 ) {
7661 stack.push(selection.id);
7662 selections.push(selection);
7663 }
7664 }
7665
7666 if above {
7667 stack.reverse();
7668 }
7669
7670 AddSelectionsState { above, stack }
7671 });
7672
7673 let last_added_selection = *state.stack.last().unwrap();
7674 let mut new_selections = Vec::new();
7675 if above == state.above {
7676 let end_row = if above {
7677 DisplayRow(0)
7678 } else {
7679 display_map.max_point().row()
7680 };
7681
7682 'outer: for selection in selections {
7683 if selection.id == last_added_selection {
7684 let range = selection.display_range(&display_map).sorted();
7685 debug_assert_eq!(range.start.row(), range.end.row());
7686 let mut row = range.start.row();
7687 let positions =
7688 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
7689 px(start)..px(end)
7690 } else {
7691 let start_x =
7692 display_map.x_for_display_point(range.start, &text_layout_details);
7693 let end_x =
7694 display_map.x_for_display_point(range.end, &text_layout_details);
7695 start_x.min(end_x)..start_x.max(end_x)
7696 };
7697
7698 while row != end_row {
7699 if above {
7700 row.0 -= 1;
7701 } else {
7702 row.0 += 1;
7703 }
7704
7705 if let Some(new_selection) = self.selections.build_columnar_selection(
7706 &display_map,
7707 row,
7708 &positions,
7709 selection.reversed,
7710 &text_layout_details,
7711 ) {
7712 state.stack.push(new_selection.id);
7713 if above {
7714 new_selections.push(new_selection);
7715 new_selections.push(selection);
7716 } else {
7717 new_selections.push(selection);
7718 new_selections.push(new_selection);
7719 }
7720
7721 continue 'outer;
7722 }
7723 }
7724 }
7725
7726 new_selections.push(selection);
7727 }
7728 } else {
7729 new_selections = selections;
7730 new_selections.retain(|s| s.id != last_added_selection);
7731 state.stack.pop();
7732 }
7733
7734 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7735 s.select(new_selections);
7736 });
7737 if state.stack.len() > 1 {
7738 self.add_selections_state = Some(state);
7739 }
7740 }
7741
7742 pub fn select_next_match_internal(
7743 &mut self,
7744 display_map: &DisplaySnapshot,
7745 replace_newest: bool,
7746 autoscroll: Option<Autoscroll>,
7747 cx: &mut ViewContext<Self>,
7748 ) -> Result<()> {
7749 fn select_next_match_ranges(
7750 this: &mut Editor,
7751 range: Range<usize>,
7752 replace_newest: bool,
7753 auto_scroll: Option<Autoscroll>,
7754 cx: &mut ViewContext<Editor>,
7755 ) {
7756 this.unfold_ranges([range.clone()], false, true, cx);
7757 this.change_selections(auto_scroll, cx, |s| {
7758 if replace_newest {
7759 s.delete(s.newest_anchor().id);
7760 }
7761 s.insert_range(range.clone());
7762 });
7763 }
7764
7765 let buffer = &display_map.buffer_snapshot;
7766 let mut selections = self.selections.all::<usize>(cx);
7767 if let Some(mut select_next_state) = self.select_next_state.take() {
7768 let query = &select_next_state.query;
7769 if !select_next_state.done {
7770 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
7771 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
7772 let mut next_selected_range = None;
7773
7774 let bytes_after_last_selection =
7775 buffer.bytes_in_range(last_selection.end..buffer.len());
7776 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
7777 let query_matches = query
7778 .stream_find_iter(bytes_after_last_selection)
7779 .map(|result| (last_selection.end, result))
7780 .chain(
7781 query
7782 .stream_find_iter(bytes_before_first_selection)
7783 .map(|result| (0, result)),
7784 );
7785
7786 for (start_offset, query_match) in query_matches {
7787 let query_match = query_match.unwrap(); // can only fail due to I/O
7788 let offset_range =
7789 start_offset + query_match.start()..start_offset + query_match.end();
7790 let display_range = offset_range.start.to_display_point(&display_map)
7791 ..offset_range.end.to_display_point(&display_map);
7792
7793 if !select_next_state.wordwise
7794 || (!movement::is_inside_word(&display_map, display_range.start)
7795 && !movement::is_inside_word(&display_map, display_range.end))
7796 {
7797 // TODO: This is n^2, because we might check all the selections
7798 if !selections
7799 .iter()
7800 .any(|selection| selection.range().overlaps(&offset_range))
7801 {
7802 next_selected_range = Some(offset_range);
7803 break;
7804 }
7805 }
7806 }
7807
7808 if let Some(next_selected_range) = next_selected_range {
7809 select_next_match_ranges(
7810 self,
7811 next_selected_range,
7812 replace_newest,
7813 autoscroll,
7814 cx,
7815 );
7816 } else {
7817 select_next_state.done = true;
7818 }
7819 }
7820
7821 self.select_next_state = Some(select_next_state);
7822 } else {
7823 let mut only_carets = true;
7824 let mut same_text_selected = true;
7825 let mut selected_text = None;
7826
7827 let mut selections_iter = selections.iter().peekable();
7828 while let Some(selection) = selections_iter.next() {
7829 if selection.start != selection.end {
7830 only_carets = false;
7831 }
7832
7833 if same_text_selected {
7834 if selected_text.is_none() {
7835 selected_text =
7836 Some(buffer.text_for_range(selection.range()).collect::<String>());
7837 }
7838
7839 if let Some(next_selection) = selections_iter.peek() {
7840 if next_selection.range().len() == selection.range().len() {
7841 let next_selected_text = buffer
7842 .text_for_range(next_selection.range())
7843 .collect::<String>();
7844 if Some(next_selected_text) != selected_text {
7845 same_text_selected = false;
7846 selected_text = None;
7847 }
7848 } else {
7849 same_text_selected = false;
7850 selected_text = None;
7851 }
7852 }
7853 }
7854 }
7855
7856 if only_carets {
7857 for selection in &mut selections {
7858 let word_range = movement::surrounding_word(
7859 &display_map,
7860 selection.start.to_display_point(&display_map),
7861 );
7862 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
7863 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
7864 selection.goal = SelectionGoal::None;
7865 selection.reversed = false;
7866 select_next_match_ranges(
7867 self,
7868 selection.start..selection.end,
7869 replace_newest,
7870 autoscroll,
7871 cx,
7872 );
7873 }
7874
7875 if selections.len() == 1 {
7876 let selection = selections
7877 .last()
7878 .expect("ensured that there's only one selection");
7879 let query = buffer
7880 .text_for_range(selection.start..selection.end)
7881 .collect::<String>();
7882 let is_empty = query.is_empty();
7883 let select_state = SelectNextState {
7884 query: AhoCorasick::new(&[query])?,
7885 wordwise: true,
7886 done: is_empty,
7887 };
7888 self.select_next_state = Some(select_state);
7889 } else {
7890 self.select_next_state = None;
7891 }
7892 } else if let Some(selected_text) = selected_text {
7893 self.select_next_state = Some(SelectNextState {
7894 query: AhoCorasick::new(&[selected_text])?,
7895 wordwise: false,
7896 done: false,
7897 });
7898 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
7899 }
7900 }
7901 Ok(())
7902 }
7903
7904 pub fn select_all_matches(
7905 &mut self,
7906 _action: &SelectAllMatches,
7907 cx: &mut ViewContext<Self>,
7908 ) -> Result<()> {
7909 self.push_to_selection_history();
7910 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7911
7912 self.select_next_match_internal(&display_map, false, None, cx)?;
7913 let Some(select_next_state) = self.select_next_state.as_mut() else {
7914 return Ok(());
7915 };
7916 if select_next_state.done {
7917 return Ok(());
7918 }
7919
7920 let mut new_selections = self.selections.all::<usize>(cx);
7921
7922 let buffer = &display_map.buffer_snapshot;
7923 let query_matches = select_next_state
7924 .query
7925 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
7926
7927 for query_match in query_matches {
7928 let query_match = query_match.unwrap(); // can only fail due to I/O
7929 let offset_range = query_match.start()..query_match.end();
7930 let display_range = offset_range.start.to_display_point(&display_map)
7931 ..offset_range.end.to_display_point(&display_map);
7932
7933 if !select_next_state.wordwise
7934 || (!movement::is_inside_word(&display_map, display_range.start)
7935 && !movement::is_inside_word(&display_map, display_range.end))
7936 {
7937 self.selections.change_with(cx, |selections| {
7938 new_selections.push(Selection {
7939 id: selections.new_selection_id(),
7940 start: offset_range.start,
7941 end: offset_range.end,
7942 reversed: false,
7943 goal: SelectionGoal::None,
7944 });
7945 });
7946 }
7947 }
7948
7949 new_selections.sort_by_key(|selection| selection.start);
7950 let mut ix = 0;
7951 while ix + 1 < new_selections.len() {
7952 let current_selection = &new_selections[ix];
7953 let next_selection = &new_selections[ix + 1];
7954 if current_selection.range().overlaps(&next_selection.range()) {
7955 if current_selection.id < next_selection.id {
7956 new_selections.remove(ix + 1);
7957 } else {
7958 new_selections.remove(ix);
7959 }
7960 } else {
7961 ix += 1;
7962 }
7963 }
7964
7965 select_next_state.done = true;
7966 self.unfold_ranges(
7967 new_selections.iter().map(|selection| selection.range()),
7968 false,
7969 false,
7970 cx,
7971 );
7972 self.change_selections(Some(Autoscroll::fit()), cx, |selections| {
7973 selections.select(new_selections)
7974 });
7975
7976 Ok(())
7977 }
7978
7979 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
7980 self.push_to_selection_history();
7981 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7982 self.select_next_match_internal(
7983 &display_map,
7984 action.replace_newest,
7985 Some(Autoscroll::newest()),
7986 cx,
7987 )?;
7988 Ok(())
7989 }
7990
7991 pub fn select_previous(
7992 &mut self,
7993 action: &SelectPrevious,
7994 cx: &mut ViewContext<Self>,
7995 ) -> Result<()> {
7996 self.push_to_selection_history();
7997 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7998 let buffer = &display_map.buffer_snapshot;
7999 let mut selections = self.selections.all::<usize>(cx);
8000 if let Some(mut select_prev_state) = self.select_prev_state.take() {
8001 let query = &select_prev_state.query;
8002 if !select_prev_state.done {
8003 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
8004 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
8005 let mut next_selected_range = None;
8006 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
8007 let bytes_before_last_selection =
8008 buffer.reversed_bytes_in_range(0..last_selection.start);
8009 let bytes_after_first_selection =
8010 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
8011 let query_matches = query
8012 .stream_find_iter(bytes_before_last_selection)
8013 .map(|result| (last_selection.start, result))
8014 .chain(
8015 query
8016 .stream_find_iter(bytes_after_first_selection)
8017 .map(|result| (buffer.len(), result)),
8018 );
8019 for (end_offset, query_match) in query_matches {
8020 let query_match = query_match.unwrap(); // can only fail due to I/O
8021 let offset_range =
8022 end_offset - query_match.end()..end_offset - query_match.start();
8023 let display_range = offset_range.start.to_display_point(&display_map)
8024 ..offset_range.end.to_display_point(&display_map);
8025
8026 if !select_prev_state.wordwise
8027 || (!movement::is_inside_word(&display_map, display_range.start)
8028 && !movement::is_inside_word(&display_map, display_range.end))
8029 {
8030 next_selected_range = Some(offset_range);
8031 break;
8032 }
8033 }
8034
8035 if let Some(next_selected_range) = next_selected_range {
8036 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
8037 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8038 if action.replace_newest {
8039 s.delete(s.newest_anchor().id);
8040 }
8041 s.insert_range(next_selected_range);
8042 });
8043 } else {
8044 select_prev_state.done = true;
8045 }
8046 }
8047
8048 self.select_prev_state = Some(select_prev_state);
8049 } else {
8050 let mut only_carets = true;
8051 let mut same_text_selected = true;
8052 let mut selected_text = None;
8053
8054 let mut selections_iter = selections.iter().peekable();
8055 while let Some(selection) = selections_iter.next() {
8056 if selection.start != selection.end {
8057 only_carets = false;
8058 }
8059
8060 if same_text_selected {
8061 if selected_text.is_none() {
8062 selected_text =
8063 Some(buffer.text_for_range(selection.range()).collect::<String>());
8064 }
8065
8066 if let Some(next_selection) = selections_iter.peek() {
8067 if next_selection.range().len() == selection.range().len() {
8068 let next_selected_text = buffer
8069 .text_for_range(next_selection.range())
8070 .collect::<String>();
8071 if Some(next_selected_text) != selected_text {
8072 same_text_selected = false;
8073 selected_text = None;
8074 }
8075 } else {
8076 same_text_selected = false;
8077 selected_text = None;
8078 }
8079 }
8080 }
8081 }
8082
8083 if only_carets {
8084 for selection in &mut selections {
8085 let word_range = movement::surrounding_word(
8086 &display_map,
8087 selection.start.to_display_point(&display_map),
8088 );
8089 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
8090 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
8091 selection.goal = SelectionGoal::None;
8092 selection.reversed = false;
8093 }
8094 if selections.len() == 1 {
8095 let selection = selections
8096 .last()
8097 .expect("ensured that there's only one selection");
8098 let query = buffer
8099 .text_for_range(selection.start..selection.end)
8100 .collect::<String>();
8101 let is_empty = query.is_empty();
8102 let select_state = SelectNextState {
8103 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
8104 wordwise: true,
8105 done: is_empty,
8106 };
8107 self.select_prev_state = Some(select_state);
8108 } else {
8109 self.select_prev_state = None;
8110 }
8111
8112 self.unfold_ranges(
8113 selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
8114 false,
8115 true,
8116 cx,
8117 );
8118 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8119 s.select(selections);
8120 });
8121 } else if let Some(selected_text) = selected_text {
8122 self.select_prev_state = Some(SelectNextState {
8123 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
8124 wordwise: false,
8125 done: false,
8126 });
8127 self.select_previous(action, cx)?;
8128 }
8129 }
8130 Ok(())
8131 }
8132
8133 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
8134 let text_layout_details = &self.text_layout_details(cx);
8135 self.transact(cx, |this, cx| {
8136 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
8137 let mut edits = Vec::new();
8138 let mut selection_edit_ranges = Vec::new();
8139 let mut last_toggled_row = None;
8140 let snapshot = this.buffer.read(cx).read(cx);
8141 let empty_str: Arc<str> = Arc::default();
8142 let mut suffixes_inserted = Vec::new();
8143
8144 fn comment_prefix_range(
8145 snapshot: &MultiBufferSnapshot,
8146 row: MultiBufferRow,
8147 comment_prefix: &str,
8148 comment_prefix_whitespace: &str,
8149 ) -> Range<Point> {
8150 let start = Point::new(row.0, snapshot.indent_size_for_line(row).len);
8151
8152 let mut line_bytes = snapshot
8153 .bytes_in_range(start..snapshot.max_point())
8154 .flatten()
8155 .copied();
8156
8157 // If this line currently begins with the line comment prefix, then record
8158 // the range containing the prefix.
8159 if line_bytes
8160 .by_ref()
8161 .take(comment_prefix.len())
8162 .eq(comment_prefix.bytes())
8163 {
8164 // Include any whitespace that matches the comment prefix.
8165 let matching_whitespace_len = line_bytes
8166 .zip(comment_prefix_whitespace.bytes())
8167 .take_while(|(a, b)| a == b)
8168 .count() as u32;
8169 let end = Point::new(
8170 start.row,
8171 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
8172 );
8173 start..end
8174 } else {
8175 start..start
8176 }
8177 }
8178
8179 fn comment_suffix_range(
8180 snapshot: &MultiBufferSnapshot,
8181 row: MultiBufferRow,
8182 comment_suffix: &str,
8183 comment_suffix_has_leading_space: bool,
8184 ) -> Range<Point> {
8185 let end = Point::new(row.0, snapshot.line_len(row));
8186 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
8187
8188 let mut line_end_bytes = snapshot
8189 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
8190 .flatten()
8191 .copied();
8192
8193 let leading_space_len = if suffix_start_column > 0
8194 && line_end_bytes.next() == Some(b' ')
8195 && comment_suffix_has_leading_space
8196 {
8197 1
8198 } else {
8199 0
8200 };
8201
8202 // If this line currently begins with the line comment prefix, then record
8203 // the range containing the prefix.
8204 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
8205 let start = Point::new(end.row, suffix_start_column - leading_space_len);
8206 start..end
8207 } else {
8208 end..end
8209 }
8210 }
8211
8212 // TODO: Handle selections that cross excerpts
8213 for selection in &mut selections {
8214 let start_column = snapshot
8215 .indent_size_for_line(MultiBufferRow(selection.start.row))
8216 .len;
8217 let language = if let Some(language) =
8218 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
8219 {
8220 language
8221 } else {
8222 continue;
8223 };
8224
8225 selection_edit_ranges.clear();
8226
8227 // If multiple selections contain a given row, avoid processing that
8228 // row more than once.
8229 let mut start_row = MultiBufferRow(selection.start.row);
8230 if last_toggled_row == Some(start_row) {
8231 start_row = start_row.next_row();
8232 }
8233 let end_row =
8234 if selection.end.row > selection.start.row && selection.end.column == 0 {
8235 MultiBufferRow(selection.end.row - 1)
8236 } else {
8237 MultiBufferRow(selection.end.row)
8238 };
8239 last_toggled_row = Some(end_row);
8240
8241 if start_row > end_row {
8242 continue;
8243 }
8244
8245 // If the language has line comments, toggle those.
8246 let full_comment_prefixes = language.line_comment_prefixes();
8247 if !full_comment_prefixes.is_empty() {
8248 let first_prefix = full_comment_prefixes
8249 .first()
8250 .expect("prefixes is non-empty");
8251 let prefix_trimmed_lengths = full_comment_prefixes
8252 .iter()
8253 .map(|p| p.trim_end_matches(' ').len())
8254 .collect::<SmallVec<[usize; 4]>>();
8255
8256 let mut all_selection_lines_are_comments = true;
8257
8258 for row in start_row.0..=end_row.0 {
8259 let row = MultiBufferRow(row);
8260 if start_row < end_row && snapshot.is_line_blank(row) {
8261 continue;
8262 }
8263
8264 let prefix_range = full_comment_prefixes
8265 .iter()
8266 .zip(prefix_trimmed_lengths.iter().copied())
8267 .map(|(prefix, trimmed_prefix_len)| {
8268 comment_prefix_range(
8269 snapshot.deref(),
8270 row,
8271 &prefix[..trimmed_prefix_len],
8272 &prefix[trimmed_prefix_len..],
8273 )
8274 })
8275 .max_by_key(|range| range.end.column - range.start.column)
8276 .expect("prefixes is non-empty");
8277
8278 if prefix_range.is_empty() {
8279 all_selection_lines_are_comments = false;
8280 }
8281
8282 selection_edit_ranges.push(prefix_range);
8283 }
8284
8285 if all_selection_lines_are_comments {
8286 edits.extend(
8287 selection_edit_ranges
8288 .iter()
8289 .cloned()
8290 .map(|range| (range, empty_str.clone())),
8291 );
8292 } else {
8293 let min_column = selection_edit_ranges
8294 .iter()
8295 .map(|range| range.start.column)
8296 .min()
8297 .unwrap_or(0);
8298 edits.extend(selection_edit_ranges.iter().map(|range| {
8299 let position = Point::new(range.start.row, min_column);
8300 (position..position, first_prefix.clone())
8301 }));
8302 }
8303 } else if let Some((full_comment_prefix, comment_suffix)) =
8304 language.block_comment_delimiters()
8305 {
8306 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
8307 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
8308 let prefix_range = comment_prefix_range(
8309 snapshot.deref(),
8310 start_row,
8311 comment_prefix,
8312 comment_prefix_whitespace,
8313 );
8314 let suffix_range = comment_suffix_range(
8315 snapshot.deref(),
8316 end_row,
8317 comment_suffix.trim_start_matches(' '),
8318 comment_suffix.starts_with(' '),
8319 );
8320
8321 if prefix_range.is_empty() || suffix_range.is_empty() {
8322 edits.push((
8323 prefix_range.start..prefix_range.start,
8324 full_comment_prefix.clone(),
8325 ));
8326 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
8327 suffixes_inserted.push((end_row, comment_suffix.len()));
8328 } else {
8329 edits.push((prefix_range, empty_str.clone()));
8330 edits.push((suffix_range, empty_str.clone()));
8331 }
8332 } else {
8333 continue;
8334 }
8335 }
8336
8337 drop(snapshot);
8338 this.buffer.update(cx, |buffer, cx| {
8339 buffer.edit(edits, None, cx);
8340 });
8341
8342 // Adjust selections so that they end before any comment suffixes that
8343 // were inserted.
8344 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
8345 let mut selections = this.selections.all::<Point>(cx);
8346 let snapshot = this.buffer.read(cx).read(cx);
8347 for selection in &mut selections {
8348 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
8349 match row.cmp(&MultiBufferRow(selection.end.row)) {
8350 Ordering::Less => {
8351 suffixes_inserted.next();
8352 continue;
8353 }
8354 Ordering::Greater => break,
8355 Ordering::Equal => {
8356 if selection.end.column == snapshot.line_len(row) {
8357 if selection.is_empty() {
8358 selection.start.column -= suffix_len as u32;
8359 }
8360 selection.end.column -= suffix_len as u32;
8361 }
8362 break;
8363 }
8364 }
8365 }
8366 }
8367
8368 drop(snapshot);
8369 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
8370
8371 let selections = this.selections.all::<Point>(cx);
8372 let selections_on_single_row = selections.windows(2).all(|selections| {
8373 selections[0].start.row == selections[1].start.row
8374 && selections[0].end.row == selections[1].end.row
8375 && selections[0].start.row == selections[0].end.row
8376 });
8377 let selections_selecting = selections
8378 .iter()
8379 .any(|selection| selection.start != selection.end);
8380 let advance_downwards = action.advance_downwards
8381 && selections_on_single_row
8382 && !selections_selecting
8383 && !matches!(this.mode, EditorMode::SingleLine { .. });
8384
8385 if advance_downwards {
8386 let snapshot = this.buffer.read(cx).snapshot(cx);
8387
8388 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8389 s.move_cursors_with(|display_snapshot, display_point, _| {
8390 let mut point = display_point.to_point(display_snapshot);
8391 point.row += 1;
8392 point = snapshot.clip_point(point, Bias::Left);
8393 let display_point = point.to_display_point(display_snapshot);
8394 let goal = SelectionGoal::HorizontalPosition(
8395 display_snapshot
8396 .x_for_display_point(display_point, &text_layout_details)
8397 .into(),
8398 );
8399 (display_point, goal)
8400 })
8401 });
8402 }
8403 });
8404 }
8405
8406 pub fn select_enclosing_symbol(
8407 &mut self,
8408 _: &SelectEnclosingSymbol,
8409 cx: &mut ViewContext<Self>,
8410 ) {
8411 let buffer = self.buffer.read(cx).snapshot(cx);
8412 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8413
8414 fn update_selection(
8415 selection: &Selection<usize>,
8416 buffer_snap: &MultiBufferSnapshot,
8417 ) -> Option<Selection<usize>> {
8418 let cursor = selection.head();
8419 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
8420 for symbol in symbols.iter().rev() {
8421 let start = symbol.range.start.to_offset(&buffer_snap);
8422 let end = symbol.range.end.to_offset(&buffer_snap);
8423 let new_range = start..end;
8424 if start < selection.start || end > selection.end {
8425 return Some(Selection {
8426 id: selection.id,
8427 start: new_range.start,
8428 end: new_range.end,
8429 goal: SelectionGoal::None,
8430 reversed: selection.reversed,
8431 });
8432 }
8433 }
8434 None
8435 }
8436
8437 let mut selected_larger_symbol = false;
8438 let new_selections = old_selections
8439 .iter()
8440 .map(|selection| match update_selection(selection, &buffer) {
8441 Some(new_selection) => {
8442 if new_selection.range() != selection.range() {
8443 selected_larger_symbol = true;
8444 }
8445 new_selection
8446 }
8447 None => selection.clone(),
8448 })
8449 .collect::<Vec<_>>();
8450
8451 if selected_larger_symbol {
8452 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8453 s.select(new_selections);
8454 });
8455 }
8456 }
8457
8458 pub fn select_larger_syntax_node(
8459 &mut self,
8460 _: &SelectLargerSyntaxNode,
8461 cx: &mut ViewContext<Self>,
8462 ) {
8463 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8464 let buffer = self.buffer.read(cx).snapshot(cx);
8465 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8466
8467 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8468 let mut selected_larger_node = false;
8469 let new_selections = old_selections
8470 .iter()
8471 .map(|selection| {
8472 let old_range = selection.start..selection.end;
8473 let mut new_range = old_range.clone();
8474 while let Some(containing_range) =
8475 buffer.range_for_syntax_ancestor(new_range.clone())
8476 {
8477 new_range = containing_range;
8478 if !display_map.intersects_fold(new_range.start)
8479 && !display_map.intersects_fold(new_range.end)
8480 {
8481 break;
8482 }
8483 }
8484
8485 selected_larger_node |= new_range != old_range;
8486 Selection {
8487 id: selection.id,
8488 start: new_range.start,
8489 end: new_range.end,
8490 goal: SelectionGoal::None,
8491 reversed: selection.reversed,
8492 }
8493 })
8494 .collect::<Vec<_>>();
8495
8496 if selected_larger_node {
8497 stack.push(old_selections);
8498 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8499 s.select(new_selections);
8500 });
8501 }
8502 self.select_larger_syntax_node_stack = stack;
8503 }
8504
8505 pub fn select_smaller_syntax_node(
8506 &mut self,
8507 _: &SelectSmallerSyntaxNode,
8508 cx: &mut ViewContext<Self>,
8509 ) {
8510 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8511 if let Some(selections) = stack.pop() {
8512 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8513 s.select(selections.to_vec());
8514 });
8515 }
8516 self.select_larger_syntax_node_stack = stack;
8517 }
8518
8519 fn refresh_runnables(&mut self, cx: &mut ViewContext<Self>) -> Task<()> {
8520 if !EditorSettings::get_global(cx).gutter.runnables {
8521 self.clear_tasks();
8522 return Task::ready(());
8523 }
8524 let project = self.project.clone();
8525 cx.spawn(|this, mut cx| async move {
8526 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
8527 this.display_map.update(cx, |map, cx| map.snapshot(cx))
8528 }) else {
8529 return;
8530 };
8531
8532 let Some(project) = project else {
8533 return;
8534 };
8535
8536 let hide_runnables = project
8537 .update(&mut cx, |project, cx| {
8538 // Do not display any test indicators in non-dev server remote projects.
8539 project.is_remote() && project.ssh_connection_string(cx).is_none()
8540 })
8541 .unwrap_or(true);
8542 if hide_runnables {
8543 return;
8544 }
8545 let new_rows =
8546 cx.background_executor()
8547 .spawn({
8548 let snapshot = display_snapshot.clone();
8549 async move {
8550 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
8551 }
8552 })
8553 .await;
8554 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
8555
8556 this.update(&mut cx, |this, _| {
8557 this.clear_tasks();
8558 for (key, value) in rows {
8559 this.insert_tasks(key, value);
8560 }
8561 })
8562 .ok();
8563 })
8564 }
8565 fn fetch_runnable_ranges(
8566 snapshot: &DisplaySnapshot,
8567 range: Range<Anchor>,
8568 ) -> Vec<language::RunnableRange> {
8569 snapshot.buffer_snapshot.runnable_ranges(range).collect()
8570 }
8571
8572 fn runnable_rows(
8573 project: Model<Project>,
8574 snapshot: DisplaySnapshot,
8575 runnable_ranges: Vec<RunnableRange>,
8576 mut cx: AsyncWindowContext,
8577 ) -> Vec<((BufferId, u32), RunnableTasks)> {
8578 runnable_ranges
8579 .into_iter()
8580 .filter_map(|mut runnable| {
8581 let tasks = cx
8582 .update(|cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
8583 .ok()?;
8584 if tasks.is_empty() {
8585 return None;
8586 }
8587
8588 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
8589
8590 let row = snapshot
8591 .buffer_snapshot
8592 .buffer_line_for_row(MultiBufferRow(point.row))?
8593 .1
8594 .start
8595 .row;
8596
8597 let context_range =
8598 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
8599 Some((
8600 (runnable.buffer_id, row),
8601 RunnableTasks {
8602 templates: tasks,
8603 offset: MultiBufferOffset(runnable.run_range.start),
8604 context_range,
8605 column: point.column,
8606 extra_variables: runnable.extra_captures,
8607 },
8608 ))
8609 })
8610 .collect()
8611 }
8612
8613 fn templates_with_tags(
8614 project: &Model<Project>,
8615 runnable: &mut Runnable,
8616 cx: &WindowContext<'_>,
8617 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
8618 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
8619 let (worktree_id, file) = project
8620 .buffer_for_id(runnable.buffer, cx)
8621 .and_then(|buffer| buffer.read(cx).file())
8622 .map(|file| (WorktreeId::from_usize(file.worktree_id()), file.clone()))
8623 .unzip();
8624
8625 (project.task_inventory().clone(), worktree_id, file)
8626 });
8627
8628 let inventory = inventory.read(cx);
8629 let tags = mem::take(&mut runnable.tags);
8630 let mut tags: Vec<_> = tags
8631 .into_iter()
8632 .flat_map(|tag| {
8633 let tag = tag.0.clone();
8634 inventory
8635 .list_tasks(
8636 file.clone(),
8637 Some(runnable.language.clone()),
8638 worktree_id,
8639 cx,
8640 )
8641 .into_iter()
8642 .filter(move |(_, template)| {
8643 template.tags.iter().any(|source_tag| source_tag == &tag)
8644 })
8645 })
8646 .sorted_by_key(|(kind, _)| kind.to_owned())
8647 .collect();
8648 if let Some((leading_tag_source, _)) = tags.first() {
8649 // Strongest source wins; if we have worktree tag binding, prefer that to
8650 // global and language bindings;
8651 // if we have a global binding, prefer that to language binding.
8652 let first_mismatch = tags
8653 .iter()
8654 .position(|(tag_source, _)| tag_source != leading_tag_source);
8655 if let Some(index) = first_mismatch {
8656 tags.truncate(index);
8657 }
8658 }
8659
8660 tags
8661 }
8662
8663 pub fn move_to_enclosing_bracket(
8664 &mut self,
8665 _: &MoveToEnclosingBracket,
8666 cx: &mut ViewContext<Self>,
8667 ) {
8668 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8669 s.move_offsets_with(|snapshot, selection| {
8670 let Some(enclosing_bracket_ranges) =
8671 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
8672 else {
8673 return;
8674 };
8675
8676 let mut best_length = usize::MAX;
8677 let mut best_inside = false;
8678 let mut best_in_bracket_range = false;
8679 let mut best_destination = None;
8680 for (open, close) in enclosing_bracket_ranges {
8681 let close = close.to_inclusive();
8682 let length = close.end() - open.start;
8683 let inside = selection.start >= open.end && selection.end <= *close.start();
8684 let in_bracket_range = open.to_inclusive().contains(&selection.head())
8685 || close.contains(&selection.head());
8686
8687 // If best is next to a bracket and current isn't, skip
8688 if !in_bracket_range && best_in_bracket_range {
8689 continue;
8690 }
8691
8692 // Prefer smaller lengths unless best is inside and current isn't
8693 if length > best_length && (best_inside || !inside) {
8694 continue;
8695 }
8696
8697 best_length = length;
8698 best_inside = inside;
8699 best_in_bracket_range = in_bracket_range;
8700 best_destination = Some(
8701 if close.contains(&selection.start) && close.contains(&selection.end) {
8702 if inside {
8703 open.end
8704 } else {
8705 open.start
8706 }
8707 } else {
8708 if inside {
8709 *close.start()
8710 } else {
8711 *close.end()
8712 }
8713 },
8714 );
8715 }
8716
8717 if let Some(destination) = best_destination {
8718 selection.collapse_to(destination, SelectionGoal::None);
8719 }
8720 })
8721 });
8722 }
8723
8724 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
8725 self.end_selection(cx);
8726 self.selection_history.mode = SelectionHistoryMode::Undoing;
8727 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
8728 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8729 self.select_next_state = entry.select_next_state;
8730 self.select_prev_state = entry.select_prev_state;
8731 self.add_selections_state = entry.add_selections_state;
8732 self.request_autoscroll(Autoscroll::newest(), cx);
8733 }
8734 self.selection_history.mode = SelectionHistoryMode::Normal;
8735 }
8736
8737 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
8738 self.end_selection(cx);
8739 self.selection_history.mode = SelectionHistoryMode::Redoing;
8740 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
8741 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8742 self.select_next_state = entry.select_next_state;
8743 self.select_prev_state = entry.select_prev_state;
8744 self.add_selections_state = entry.add_selections_state;
8745 self.request_autoscroll(Autoscroll::newest(), cx);
8746 }
8747 self.selection_history.mode = SelectionHistoryMode::Normal;
8748 }
8749
8750 pub fn expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
8751 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
8752 }
8753
8754 pub fn expand_excerpts_down(
8755 &mut self,
8756 action: &ExpandExcerptsDown,
8757 cx: &mut ViewContext<Self>,
8758 ) {
8759 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
8760 }
8761
8762 pub fn expand_excerpts_up(&mut self, action: &ExpandExcerptsUp, cx: &mut ViewContext<Self>) {
8763 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
8764 }
8765
8766 pub fn expand_excerpts_for_direction(
8767 &mut self,
8768 lines: u32,
8769 direction: ExpandExcerptDirection,
8770 cx: &mut ViewContext<Self>,
8771 ) {
8772 let selections = self.selections.disjoint_anchors();
8773
8774 let lines = if lines == 0 {
8775 EditorSettings::get_global(cx).expand_excerpt_lines
8776 } else {
8777 lines
8778 };
8779
8780 self.buffer.update(cx, |buffer, cx| {
8781 buffer.expand_excerpts(
8782 selections
8783 .into_iter()
8784 .map(|selection| selection.head().excerpt_id)
8785 .dedup(),
8786 lines,
8787 direction,
8788 cx,
8789 )
8790 })
8791 }
8792
8793 pub fn expand_excerpt(
8794 &mut self,
8795 excerpt: ExcerptId,
8796 direction: ExpandExcerptDirection,
8797 cx: &mut ViewContext<Self>,
8798 ) {
8799 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
8800 self.buffer.update(cx, |buffer, cx| {
8801 buffer.expand_excerpts([excerpt], lines, direction, cx)
8802 })
8803 }
8804
8805 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
8806 self.go_to_diagnostic_impl(Direction::Next, cx)
8807 }
8808
8809 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
8810 self.go_to_diagnostic_impl(Direction::Prev, cx)
8811 }
8812
8813 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
8814 let buffer = self.buffer.read(cx).snapshot(cx);
8815 let selection = self.selections.newest::<usize>(cx);
8816
8817 // If there is an active Diagnostic Popover jump to its diagnostic instead.
8818 if direction == Direction::Next {
8819 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
8820 let (group_id, jump_to) = popover.activation_info();
8821 if self.activate_diagnostics(group_id, cx) {
8822 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8823 let mut new_selection = s.newest_anchor().clone();
8824 new_selection.collapse_to(jump_to, SelectionGoal::None);
8825 s.select_anchors(vec![new_selection.clone()]);
8826 });
8827 }
8828 return;
8829 }
8830 }
8831
8832 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
8833 active_diagnostics
8834 .primary_range
8835 .to_offset(&buffer)
8836 .to_inclusive()
8837 });
8838 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
8839 if active_primary_range.contains(&selection.head()) {
8840 *active_primary_range.start()
8841 } else {
8842 selection.head()
8843 }
8844 } else {
8845 selection.head()
8846 };
8847 let snapshot = self.snapshot(cx);
8848 loop {
8849 let diagnostics = if direction == Direction::Prev {
8850 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
8851 } else {
8852 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
8853 }
8854 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start));
8855 let group = diagnostics
8856 // relies on diagnostics_in_range to return diagnostics with the same starting range to
8857 // be sorted in a stable way
8858 // skip until we are at current active diagnostic, if it exists
8859 .skip_while(|entry| {
8860 (match direction {
8861 Direction::Prev => entry.range.start >= search_start,
8862 Direction::Next => entry.range.start <= search_start,
8863 }) && self
8864 .active_diagnostics
8865 .as_ref()
8866 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
8867 })
8868 .find_map(|entry| {
8869 if entry.diagnostic.is_primary
8870 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
8871 && !entry.range.is_empty()
8872 // if we match with the active diagnostic, skip it
8873 && Some(entry.diagnostic.group_id)
8874 != self.active_diagnostics.as_ref().map(|d| d.group_id)
8875 {
8876 Some((entry.range, entry.diagnostic.group_id))
8877 } else {
8878 None
8879 }
8880 });
8881
8882 if let Some((primary_range, group_id)) = group {
8883 if self.activate_diagnostics(group_id, cx) {
8884 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8885 s.select(vec![Selection {
8886 id: selection.id,
8887 start: primary_range.start,
8888 end: primary_range.start,
8889 reversed: false,
8890 goal: SelectionGoal::None,
8891 }]);
8892 });
8893 }
8894 break;
8895 } else {
8896 // Cycle around to the start of the buffer, potentially moving back to the start of
8897 // the currently active diagnostic.
8898 active_primary_range.take();
8899 if direction == Direction::Prev {
8900 if search_start == buffer.len() {
8901 break;
8902 } else {
8903 search_start = buffer.len();
8904 }
8905 } else if search_start == 0 {
8906 break;
8907 } else {
8908 search_start = 0;
8909 }
8910 }
8911 }
8912 }
8913
8914 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
8915 let snapshot = self
8916 .display_map
8917 .update(cx, |display_map, cx| display_map.snapshot(cx));
8918 let selection = self.selections.newest::<Point>(cx);
8919
8920 if !self.seek_in_direction(
8921 &snapshot,
8922 selection.head(),
8923 false,
8924 snapshot.buffer_snapshot.git_diff_hunks_in_range(
8925 MultiBufferRow(selection.head().row + 1)..MultiBufferRow::MAX,
8926 ),
8927 cx,
8928 ) {
8929 let wrapped_point = Point::zero();
8930 self.seek_in_direction(
8931 &snapshot,
8932 wrapped_point,
8933 true,
8934 snapshot.buffer_snapshot.git_diff_hunks_in_range(
8935 MultiBufferRow(wrapped_point.row + 1)..MultiBufferRow::MAX,
8936 ),
8937 cx,
8938 );
8939 }
8940 }
8941
8942 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
8943 let snapshot = self
8944 .display_map
8945 .update(cx, |display_map, cx| display_map.snapshot(cx));
8946 let selection = self.selections.newest::<Point>(cx);
8947
8948 if !self.seek_in_direction(
8949 &snapshot,
8950 selection.head(),
8951 false,
8952 snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
8953 MultiBufferRow(0)..MultiBufferRow(selection.head().row),
8954 ),
8955 cx,
8956 ) {
8957 let wrapped_point = snapshot.buffer_snapshot.max_point();
8958 self.seek_in_direction(
8959 &snapshot,
8960 wrapped_point,
8961 true,
8962 snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
8963 MultiBufferRow(0)..MultiBufferRow(wrapped_point.row),
8964 ),
8965 cx,
8966 );
8967 }
8968 }
8969
8970 fn seek_in_direction(
8971 &mut self,
8972 snapshot: &DisplaySnapshot,
8973 initial_point: Point,
8974 is_wrapped: bool,
8975 hunks: impl Iterator<Item = DiffHunk<MultiBufferRow>>,
8976 cx: &mut ViewContext<Editor>,
8977 ) -> bool {
8978 let display_point = initial_point.to_display_point(snapshot);
8979 let mut hunks = hunks
8980 .map(|hunk| diff_hunk_to_display(&hunk, &snapshot))
8981 .filter(|hunk| is_wrapped || !hunk.contains_display_row(display_point.row()))
8982 .dedup();
8983
8984 if let Some(hunk) = hunks.next() {
8985 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8986 let row = hunk.start_display_row();
8987 let point = DisplayPoint::new(row, 0);
8988 s.select_display_ranges([point..point]);
8989 });
8990
8991 true
8992 } else {
8993 false
8994 }
8995 }
8996
8997 pub fn go_to_definition(
8998 &mut self,
8999 _: &GoToDefinition,
9000 cx: &mut ViewContext<Self>,
9001 ) -> Task<Result<bool>> {
9002 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx)
9003 }
9004
9005 pub fn go_to_declaration(
9006 &mut self,
9007 _: &GoToDeclaration,
9008 cx: &mut ViewContext<Self>,
9009 ) -> Task<Result<bool>> {
9010 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, cx)
9011 }
9012
9013 pub fn go_to_declaration_split(
9014 &mut self,
9015 _: &GoToDeclaration,
9016 cx: &mut ViewContext<Self>,
9017 ) -> Task<Result<bool>> {
9018 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, cx)
9019 }
9020
9021 pub fn go_to_implementation(
9022 &mut self,
9023 _: &GoToImplementation,
9024 cx: &mut ViewContext<Self>,
9025 ) -> Task<Result<bool>> {
9026 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx)
9027 }
9028
9029 pub fn go_to_implementation_split(
9030 &mut self,
9031 _: &GoToImplementationSplit,
9032 cx: &mut ViewContext<Self>,
9033 ) -> Task<Result<bool>> {
9034 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx)
9035 }
9036
9037 pub fn go_to_type_definition(
9038 &mut self,
9039 _: &GoToTypeDefinition,
9040 cx: &mut ViewContext<Self>,
9041 ) -> Task<Result<bool>> {
9042 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx)
9043 }
9044
9045 pub fn go_to_definition_split(
9046 &mut self,
9047 _: &GoToDefinitionSplit,
9048 cx: &mut ViewContext<Self>,
9049 ) -> Task<Result<bool>> {
9050 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx)
9051 }
9052
9053 pub fn go_to_type_definition_split(
9054 &mut self,
9055 _: &GoToTypeDefinitionSplit,
9056 cx: &mut ViewContext<Self>,
9057 ) -> Task<Result<bool>> {
9058 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx)
9059 }
9060
9061 fn go_to_definition_of_kind(
9062 &mut self,
9063 kind: GotoDefinitionKind,
9064 split: bool,
9065 cx: &mut ViewContext<Self>,
9066 ) -> Task<Result<bool>> {
9067 let Some(workspace) = self.workspace() else {
9068 return Task::ready(Ok(false));
9069 };
9070 let buffer = self.buffer.read(cx);
9071 let head = self.selections.newest::<usize>(cx).head();
9072 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
9073 text_anchor
9074 } else {
9075 return Task::ready(Ok(false));
9076 };
9077
9078 let project = workspace.read(cx).project().clone();
9079 let definitions = project.update(cx, |project, cx| match kind {
9080 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
9081 GotoDefinitionKind::Declaration => project.declaration(&buffer, head, cx),
9082 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
9083 GotoDefinitionKind::Implementation => project.implementation(&buffer, head, cx),
9084 });
9085
9086 cx.spawn(|editor, mut cx| async move {
9087 let definitions = definitions.await?;
9088 let navigated = editor
9089 .update(&mut cx, |editor, cx| {
9090 editor.navigate_to_hover_links(
9091 Some(kind),
9092 definitions
9093 .into_iter()
9094 .filter(|location| {
9095 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
9096 })
9097 .map(HoverLink::Text)
9098 .collect::<Vec<_>>(),
9099 split,
9100 cx,
9101 )
9102 })?
9103 .await?;
9104 anyhow::Ok(navigated)
9105 })
9106 }
9107
9108 pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext<Self>) {
9109 let position = self.selections.newest_anchor().head();
9110 let Some((buffer, buffer_position)) =
9111 self.buffer.read(cx).text_anchor_for_position(position, cx)
9112 else {
9113 return;
9114 };
9115
9116 cx.spawn(|editor, mut cx| async move {
9117 if let Some((_, url)) = find_url(&buffer, buffer_position, cx.clone()) {
9118 editor.update(&mut cx, |_, cx| {
9119 cx.open_url(&url);
9120 })
9121 } else {
9122 Ok(())
9123 }
9124 })
9125 .detach();
9126 }
9127
9128 pub(crate) fn navigate_to_hover_links(
9129 &mut self,
9130 kind: Option<GotoDefinitionKind>,
9131 mut definitions: Vec<HoverLink>,
9132 split: bool,
9133 cx: &mut ViewContext<Editor>,
9134 ) -> Task<Result<bool>> {
9135 // If there is one definition, just open it directly
9136 if definitions.len() == 1 {
9137 let definition = definitions.pop().unwrap();
9138 let target_task = match definition {
9139 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
9140 HoverLink::InlayHint(lsp_location, server_id) => {
9141 self.compute_target_location(lsp_location, server_id, cx)
9142 }
9143 HoverLink::Url(url) => {
9144 cx.open_url(&url);
9145 Task::ready(Ok(None))
9146 }
9147 };
9148 cx.spawn(|editor, mut cx| async move {
9149 let target = target_task.await.context("target resolution task")?;
9150 if let Some(target) = target {
9151 editor.update(&mut cx, |editor, cx| {
9152 let Some(workspace) = editor.workspace() else {
9153 return false;
9154 };
9155 let pane = workspace.read(cx).active_pane().clone();
9156
9157 let range = target.range.to_offset(target.buffer.read(cx));
9158 let range = editor.range_for_match(&range);
9159
9160 /// If select range has more than one line, we
9161 /// just point the cursor to range.start.
9162 fn check_multiline_range(
9163 buffer: &Buffer,
9164 range: Range<usize>,
9165 ) -> Range<usize> {
9166 if buffer.offset_to_point(range.start).row
9167 == buffer.offset_to_point(range.end).row
9168 {
9169 range
9170 } else {
9171 range.start..range.start
9172 }
9173 }
9174
9175 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
9176 let buffer = target.buffer.read(cx);
9177 let range = check_multiline_range(buffer, range);
9178 editor.change_selections(Some(Autoscroll::focused()), cx, |s| {
9179 s.select_ranges([range]);
9180 });
9181 } else {
9182 cx.window_context().defer(move |cx| {
9183 let target_editor: View<Self> =
9184 workspace.update(cx, |workspace, cx| {
9185 let pane = if split {
9186 workspace.adjacent_pane(cx)
9187 } else {
9188 workspace.active_pane().clone()
9189 };
9190
9191 workspace.open_project_item(
9192 pane,
9193 target.buffer.clone(),
9194 true,
9195 true,
9196 cx,
9197 )
9198 });
9199 target_editor.update(cx, |target_editor, cx| {
9200 // When selecting a definition in a different buffer, disable the nav history
9201 // to avoid creating a history entry at the previous cursor location.
9202 pane.update(cx, |pane, _| pane.disable_history());
9203 let buffer = target.buffer.read(cx);
9204 let range = check_multiline_range(buffer, range);
9205 target_editor.change_selections(
9206 Some(Autoscroll::focused()),
9207 cx,
9208 |s| {
9209 s.select_ranges([range]);
9210 },
9211 );
9212 pane.update(cx, |pane, _| pane.enable_history());
9213 });
9214 });
9215 }
9216 true
9217 })
9218 } else {
9219 Ok(false)
9220 }
9221 })
9222 } else if !definitions.is_empty() {
9223 let replica_id = self.replica_id(cx);
9224 cx.spawn(|editor, mut cx| async move {
9225 let (title, location_tasks, workspace) = editor
9226 .update(&mut cx, |editor, cx| {
9227 let tab_kind = match kind {
9228 Some(GotoDefinitionKind::Implementation) => "Implementations",
9229 _ => "Definitions",
9230 };
9231 let title = definitions
9232 .iter()
9233 .find_map(|definition| match definition {
9234 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
9235 let buffer = origin.buffer.read(cx);
9236 format!(
9237 "{} for {}",
9238 tab_kind,
9239 buffer
9240 .text_for_range(origin.range.clone())
9241 .collect::<String>()
9242 )
9243 }),
9244 HoverLink::InlayHint(_, _) => None,
9245 HoverLink::Url(_) => None,
9246 })
9247 .unwrap_or(tab_kind.to_string());
9248 let location_tasks = definitions
9249 .into_iter()
9250 .map(|definition| match definition {
9251 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
9252 HoverLink::InlayHint(lsp_location, server_id) => {
9253 editor.compute_target_location(lsp_location, server_id, cx)
9254 }
9255 HoverLink::Url(_) => Task::ready(Ok(None)),
9256 })
9257 .collect::<Vec<_>>();
9258 (title, location_tasks, editor.workspace().clone())
9259 })
9260 .context("location tasks preparation")?;
9261
9262 let locations = futures::future::join_all(location_tasks)
9263 .await
9264 .into_iter()
9265 .filter_map(|location| location.transpose())
9266 .collect::<Result<_>>()
9267 .context("location tasks")?;
9268
9269 let Some(workspace) = workspace else {
9270 return Ok(false);
9271 };
9272 let opened = workspace
9273 .update(&mut cx, |workspace, cx| {
9274 Self::open_locations_in_multibuffer(
9275 workspace, locations, replica_id, title, split, cx,
9276 )
9277 })
9278 .ok();
9279
9280 anyhow::Ok(opened.is_some())
9281 })
9282 } else {
9283 Task::ready(Ok(false))
9284 }
9285 }
9286
9287 fn compute_target_location(
9288 &self,
9289 lsp_location: lsp::Location,
9290 server_id: LanguageServerId,
9291 cx: &mut ViewContext<Editor>,
9292 ) -> Task<anyhow::Result<Option<Location>>> {
9293 let Some(project) = self.project.clone() else {
9294 return Task::Ready(Some(Ok(None)));
9295 };
9296
9297 cx.spawn(move |editor, mut cx| async move {
9298 let location_task = editor.update(&mut cx, |editor, cx| {
9299 project.update(cx, |project, cx| {
9300 let language_server_name =
9301 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
9302 project
9303 .language_server_for_buffer(buffer.read(cx), server_id, cx)
9304 .map(|(lsp_adapter, _)| lsp_adapter.name.clone())
9305 });
9306 language_server_name.map(|language_server_name| {
9307 project.open_local_buffer_via_lsp(
9308 lsp_location.uri.clone(),
9309 server_id,
9310 language_server_name,
9311 cx,
9312 )
9313 })
9314 })
9315 })?;
9316 let location = match location_task {
9317 Some(task) => Some({
9318 let target_buffer_handle = task.await.context("open local buffer")?;
9319 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
9320 let target_start = target_buffer
9321 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
9322 let target_end = target_buffer
9323 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
9324 target_buffer.anchor_after(target_start)
9325 ..target_buffer.anchor_before(target_end)
9326 })?;
9327 Location {
9328 buffer: target_buffer_handle,
9329 range,
9330 }
9331 }),
9332 None => None,
9333 };
9334 Ok(location)
9335 })
9336 }
9337
9338 pub fn find_all_references(
9339 &mut self,
9340 _: &FindAllReferences,
9341 cx: &mut ViewContext<Self>,
9342 ) -> Option<Task<Result<()>>> {
9343 let multi_buffer = self.buffer.read(cx);
9344 let selection = self.selections.newest::<usize>(cx);
9345 let head = selection.head();
9346
9347 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
9348 let head_anchor = multi_buffer_snapshot.anchor_at(
9349 head,
9350 if head < selection.tail() {
9351 Bias::Right
9352 } else {
9353 Bias::Left
9354 },
9355 );
9356
9357 match self
9358 .find_all_references_task_sources
9359 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
9360 {
9361 Ok(_) => {
9362 log::info!(
9363 "Ignoring repeated FindAllReferences invocation with the position of already running task"
9364 );
9365 return None;
9366 }
9367 Err(i) => {
9368 self.find_all_references_task_sources.insert(i, head_anchor);
9369 }
9370 }
9371
9372 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
9373 let replica_id = self.replica_id(cx);
9374 let workspace = self.workspace()?;
9375 let project = workspace.read(cx).project().clone();
9376 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
9377 Some(cx.spawn(|editor, mut cx| async move {
9378 let _cleanup = defer({
9379 let mut cx = cx.clone();
9380 move || {
9381 let _ = editor.update(&mut cx, |editor, _| {
9382 if let Ok(i) =
9383 editor
9384 .find_all_references_task_sources
9385 .binary_search_by(|anchor| {
9386 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
9387 })
9388 {
9389 editor.find_all_references_task_sources.remove(i);
9390 }
9391 });
9392 }
9393 });
9394
9395 let locations = references.await?;
9396 if locations.is_empty() {
9397 return anyhow::Ok(());
9398 }
9399
9400 workspace.update(&mut cx, |workspace, cx| {
9401 let title = locations
9402 .first()
9403 .as_ref()
9404 .map(|location| {
9405 let buffer = location.buffer.read(cx);
9406 format!(
9407 "References to `{}`",
9408 buffer
9409 .text_for_range(location.range.clone())
9410 .collect::<String>()
9411 )
9412 })
9413 .unwrap();
9414 Self::open_locations_in_multibuffer(
9415 workspace, locations, replica_id, title, false, cx,
9416 );
9417 })
9418 }))
9419 }
9420
9421 /// Opens a multibuffer with the given project locations in it
9422 pub fn open_locations_in_multibuffer(
9423 workspace: &mut Workspace,
9424 mut locations: Vec<Location>,
9425 replica_id: ReplicaId,
9426 title: String,
9427 split: bool,
9428 cx: &mut ViewContext<Workspace>,
9429 ) {
9430 // If there are multiple definitions, open them in a multibuffer
9431 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
9432 let mut locations = locations.into_iter().peekable();
9433 let mut ranges_to_highlight = Vec::new();
9434 let capability = workspace.project().read(cx).capability();
9435
9436 let excerpt_buffer = cx.new_model(|cx| {
9437 let mut multibuffer = MultiBuffer::new(replica_id, capability);
9438 while let Some(location) = locations.next() {
9439 let buffer = location.buffer.read(cx);
9440 let mut ranges_for_buffer = Vec::new();
9441 let range = location.range.to_offset(buffer);
9442 ranges_for_buffer.push(range.clone());
9443
9444 while let Some(next_location) = locations.peek() {
9445 if next_location.buffer == location.buffer {
9446 ranges_for_buffer.push(next_location.range.to_offset(buffer));
9447 locations.next();
9448 } else {
9449 break;
9450 }
9451 }
9452
9453 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
9454 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
9455 location.buffer.clone(),
9456 ranges_for_buffer,
9457 DEFAULT_MULTIBUFFER_CONTEXT,
9458 cx,
9459 ))
9460 }
9461
9462 multibuffer.with_title(title)
9463 });
9464
9465 let editor = cx.new_view(|cx| {
9466 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), true, cx)
9467 });
9468 editor.update(cx, |editor, cx| {
9469 if let Some(first_range) = ranges_to_highlight.first() {
9470 editor.change_selections(None, cx, |selections| {
9471 selections.clear_disjoint();
9472 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
9473 });
9474 }
9475 editor.highlight_background::<Self>(
9476 &ranges_to_highlight,
9477 |theme| theme.editor_highlighted_line_background,
9478 cx,
9479 );
9480 });
9481
9482 let item = Box::new(editor);
9483 let item_id = item.item_id();
9484
9485 if split {
9486 workspace.split_item(SplitDirection::Right, item.clone(), cx);
9487 } else {
9488 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
9489 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
9490 pane.close_current_preview_item(cx)
9491 } else {
9492 None
9493 }
9494 });
9495 workspace.add_item_to_active_pane(item.clone(), destination_index, true, cx);
9496 }
9497 workspace.active_pane().update(cx, |pane, cx| {
9498 pane.set_preview_item_id(Some(item_id), cx);
9499 });
9500 }
9501
9502 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9503 use language::ToOffset as _;
9504
9505 let project = self.project.clone()?;
9506 let selection = self.selections.newest_anchor().clone();
9507 let (cursor_buffer, cursor_buffer_position) = self
9508 .buffer
9509 .read(cx)
9510 .text_anchor_for_position(selection.head(), cx)?;
9511 let (tail_buffer, cursor_buffer_position_end) = self
9512 .buffer
9513 .read(cx)
9514 .text_anchor_for_position(selection.tail(), cx)?;
9515 if tail_buffer != cursor_buffer {
9516 return None;
9517 }
9518
9519 let snapshot = cursor_buffer.read(cx).snapshot();
9520 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
9521 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
9522 let prepare_rename = project.update(cx, |project, cx| {
9523 project.prepare_rename(cursor_buffer.clone(), cursor_buffer_offset, cx)
9524 });
9525 drop(snapshot);
9526
9527 Some(cx.spawn(|this, mut cx| async move {
9528 let rename_range = if let Some(range) = prepare_rename.await? {
9529 Some(range)
9530 } else {
9531 this.update(&mut cx, |this, cx| {
9532 let buffer = this.buffer.read(cx).snapshot(cx);
9533 let mut buffer_highlights = this
9534 .document_highlights_for_position(selection.head(), &buffer)
9535 .filter(|highlight| {
9536 highlight.start.excerpt_id == selection.head().excerpt_id
9537 && highlight.end.excerpt_id == selection.head().excerpt_id
9538 });
9539 buffer_highlights
9540 .next()
9541 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
9542 })?
9543 };
9544 if let Some(rename_range) = rename_range {
9545 this.update(&mut cx, |this, cx| {
9546 let snapshot = cursor_buffer.read(cx).snapshot();
9547 let rename_buffer_range = rename_range.to_offset(&snapshot);
9548 let cursor_offset_in_rename_range =
9549 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
9550 let cursor_offset_in_rename_range_end =
9551 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
9552
9553 this.take_rename(false, cx);
9554 let buffer = this.buffer.read(cx).read(cx);
9555 let cursor_offset = selection.head().to_offset(&buffer);
9556 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
9557 let rename_end = rename_start + rename_buffer_range.len();
9558 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
9559 let mut old_highlight_id = None;
9560 let old_name: Arc<str> = buffer
9561 .chunks(rename_start..rename_end, true)
9562 .map(|chunk| {
9563 if old_highlight_id.is_none() {
9564 old_highlight_id = chunk.syntax_highlight_id;
9565 }
9566 chunk.text
9567 })
9568 .collect::<String>()
9569 .into();
9570
9571 drop(buffer);
9572
9573 // Position the selection in the rename editor so that it matches the current selection.
9574 this.show_local_selections = false;
9575 let rename_editor = cx.new_view(|cx| {
9576 let mut editor = Editor::single_line(cx);
9577 editor.buffer.update(cx, |buffer, cx| {
9578 buffer.edit([(0..0, old_name.clone())], None, cx)
9579 });
9580 let rename_selection_range = match cursor_offset_in_rename_range
9581 .cmp(&cursor_offset_in_rename_range_end)
9582 {
9583 Ordering::Equal => {
9584 editor.select_all(&SelectAll, cx);
9585 return editor;
9586 }
9587 Ordering::Less => {
9588 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
9589 }
9590 Ordering::Greater => {
9591 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
9592 }
9593 };
9594 if rename_selection_range.end > old_name.len() {
9595 editor.select_all(&SelectAll, cx);
9596 } else {
9597 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
9598 s.select_ranges([rename_selection_range]);
9599 });
9600 }
9601 editor
9602 });
9603 cx.subscribe(&rename_editor, |_, _, e, cx| match e {
9604 EditorEvent::Focused => cx.emit(EditorEvent::FocusedIn),
9605 _ => {}
9606 })
9607 .detach();
9608
9609 let write_highlights =
9610 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
9611 let read_highlights =
9612 this.clear_background_highlights::<DocumentHighlightRead>(cx);
9613 let ranges = write_highlights
9614 .iter()
9615 .flat_map(|(_, ranges)| ranges.iter())
9616 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
9617 .cloned()
9618 .collect();
9619
9620 this.highlight_text::<Rename>(
9621 ranges,
9622 HighlightStyle {
9623 fade_out: Some(0.6),
9624 ..Default::default()
9625 },
9626 cx,
9627 );
9628 let rename_focus_handle = rename_editor.focus_handle(cx);
9629 cx.focus(&rename_focus_handle);
9630 let block_id = this.insert_blocks(
9631 [BlockProperties {
9632 style: BlockStyle::Flex,
9633 position: range.start,
9634 height: 1,
9635 render: Box::new({
9636 let rename_editor = rename_editor.clone();
9637 move |cx: &mut BlockContext| {
9638 let mut text_style = cx.editor_style.text.clone();
9639 if let Some(highlight_style) = old_highlight_id
9640 .and_then(|h| h.style(&cx.editor_style.syntax))
9641 {
9642 text_style = text_style.highlight(highlight_style);
9643 }
9644 div()
9645 .pl(cx.anchor_x)
9646 .child(EditorElement::new(
9647 &rename_editor,
9648 EditorStyle {
9649 background: cx.theme().system().transparent,
9650 local_player: cx.editor_style.local_player,
9651 text: text_style,
9652 scrollbar_width: cx.editor_style.scrollbar_width,
9653 syntax: cx.editor_style.syntax.clone(),
9654 status: cx.editor_style.status.clone(),
9655 inlay_hints_style: HighlightStyle {
9656 color: Some(cx.theme().status().hint),
9657 font_weight: Some(FontWeight::BOLD),
9658 ..HighlightStyle::default()
9659 },
9660 suggestions_style: HighlightStyle {
9661 color: Some(cx.theme().status().predictive),
9662 ..HighlightStyle::default()
9663 },
9664 },
9665 ))
9666 .into_any_element()
9667 }
9668 }),
9669 disposition: BlockDisposition::Below,
9670 priority: 0,
9671 }],
9672 Some(Autoscroll::fit()),
9673 cx,
9674 )[0];
9675 this.pending_rename = Some(RenameState {
9676 range,
9677 old_name,
9678 editor: rename_editor,
9679 block_id,
9680 });
9681 })?;
9682 }
9683
9684 Ok(())
9685 }))
9686 }
9687
9688 pub fn confirm_rename(
9689 &mut self,
9690 _: &ConfirmRename,
9691 cx: &mut ViewContext<Self>,
9692 ) -> Option<Task<Result<()>>> {
9693 let rename = self.take_rename(false, cx)?;
9694 let workspace = self.workspace()?;
9695 let (start_buffer, start) = self
9696 .buffer
9697 .read(cx)
9698 .text_anchor_for_position(rename.range.start, cx)?;
9699 let (end_buffer, end) = self
9700 .buffer
9701 .read(cx)
9702 .text_anchor_for_position(rename.range.end, cx)?;
9703 if start_buffer != end_buffer {
9704 return None;
9705 }
9706
9707 let buffer = start_buffer;
9708 let range = start..end;
9709 let old_name = rename.old_name;
9710 let new_name = rename.editor.read(cx).text(cx);
9711
9712 let rename = workspace
9713 .read(cx)
9714 .project()
9715 .clone()
9716 .update(cx, |project, cx| {
9717 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
9718 });
9719 let workspace = workspace.downgrade();
9720
9721 Some(cx.spawn(|editor, mut cx| async move {
9722 let project_transaction = rename.await?;
9723 Self::open_project_transaction(
9724 &editor,
9725 workspace,
9726 project_transaction,
9727 format!("Rename: {} → {}", old_name, new_name),
9728 cx.clone(),
9729 )
9730 .await?;
9731
9732 editor.update(&mut cx, |editor, cx| {
9733 editor.refresh_document_highlights(cx);
9734 })?;
9735 Ok(())
9736 }))
9737 }
9738
9739 fn take_rename(
9740 &mut self,
9741 moving_cursor: bool,
9742 cx: &mut ViewContext<Self>,
9743 ) -> Option<RenameState> {
9744 let rename = self.pending_rename.take()?;
9745 if rename.editor.focus_handle(cx).is_focused(cx) {
9746 cx.focus(&self.focus_handle);
9747 }
9748
9749 self.remove_blocks(
9750 [rename.block_id].into_iter().collect(),
9751 Some(Autoscroll::fit()),
9752 cx,
9753 );
9754 self.clear_highlights::<Rename>(cx);
9755 self.show_local_selections = true;
9756
9757 if moving_cursor {
9758 let rename_editor = rename.editor.read(cx);
9759 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
9760
9761 // Update the selection to match the position of the selection inside
9762 // the rename editor.
9763 let snapshot = self.buffer.read(cx).read(cx);
9764 let rename_range = rename.range.to_offset(&snapshot);
9765 let cursor_in_editor = snapshot
9766 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
9767 .min(rename_range.end);
9768 drop(snapshot);
9769
9770 self.change_selections(None, cx, |s| {
9771 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
9772 });
9773 } else {
9774 self.refresh_document_highlights(cx);
9775 }
9776
9777 Some(rename)
9778 }
9779
9780 pub fn pending_rename(&self) -> Option<&RenameState> {
9781 self.pending_rename.as_ref()
9782 }
9783
9784 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9785 let project = match &self.project {
9786 Some(project) => project.clone(),
9787 None => return None,
9788 };
9789
9790 Some(self.perform_format(project, FormatTrigger::Manual, cx))
9791 }
9792
9793 fn perform_format(
9794 &mut self,
9795 project: Model<Project>,
9796 trigger: FormatTrigger,
9797 cx: &mut ViewContext<Self>,
9798 ) -> Task<Result<()>> {
9799 let buffer = self.buffer().clone();
9800 let mut buffers = buffer.read(cx).all_buffers();
9801 if trigger == FormatTrigger::Save {
9802 buffers.retain(|buffer| buffer.read(cx).is_dirty());
9803 }
9804
9805 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
9806 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
9807
9808 cx.spawn(|_, mut cx| async move {
9809 let transaction = futures::select_biased! {
9810 () = timeout => {
9811 log::warn!("timed out waiting for formatting");
9812 None
9813 }
9814 transaction = format.log_err().fuse() => transaction,
9815 };
9816
9817 buffer
9818 .update(&mut cx, |buffer, cx| {
9819 if let Some(transaction) = transaction {
9820 if !buffer.is_singleton() {
9821 buffer.push_transaction(&transaction.0, cx);
9822 }
9823 }
9824
9825 cx.notify();
9826 })
9827 .ok();
9828
9829 Ok(())
9830 })
9831 }
9832
9833 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
9834 if let Some(project) = self.project.clone() {
9835 self.buffer.update(cx, |multi_buffer, cx| {
9836 project.update(cx, |project, cx| {
9837 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
9838 });
9839 })
9840 }
9841 }
9842
9843 fn cancel_language_server_work(
9844 &mut self,
9845 _: &CancelLanguageServerWork,
9846 cx: &mut ViewContext<Self>,
9847 ) {
9848 if let Some(project) = self.project.clone() {
9849 self.buffer.update(cx, |multi_buffer, cx| {
9850 project.update(cx, |project, cx| {
9851 project.cancel_language_server_work_for_buffers(multi_buffer.all_buffers(), cx);
9852 });
9853 })
9854 }
9855 }
9856
9857 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
9858 cx.show_character_palette();
9859 }
9860
9861 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
9862 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
9863 let buffer = self.buffer.read(cx).snapshot(cx);
9864 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
9865 let is_valid = buffer
9866 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
9867 .any(|entry| {
9868 entry.diagnostic.is_primary
9869 && !entry.range.is_empty()
9870 && entry.range.start == primary_range_start
9871 && entry.diagnostic.message == active_diagnostics.primary_message
9872 });
9873
9874 if is_valid != active_diagnostics.is_valid {
9875 active_diagnostics.is_valid = is_valid;
9876 let mut new_styles = HashMap::default();
9877 for (block_id, diagnostic) in &active_diagnostics.blocks {
9878 new_styles.insert(
9879 *block_id,
9880 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
9881 );
9882 }
9883 self.display_map.update(cx, |display_map, _cx| {
9884 display_map.replace_blocks(new_styles)
9885 });
9886 }
9887 }
9888 }
9889
9890 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
9891 self.dismiss_diagnostics(cx);
9892 let snapshot = self.snapshot(cx);
9893 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
9894 let buffer = self.buffer.read(cx).snapshot(cx);
9895
9896 let mut primary_range = None;
9897 let mut primary_message = None;
9898 let mut group_end = Point::zero();
9899 let diagnostic_group = buffer
9900 .diagnostic_group::<MultiBufferPoint>(group_id)
9901 .filter_map(|entry| {
9902 if snapshot.is_line_folded(MultiBufferRow(entry.range.start.row))
9903 && (entry.range.start.row == entry.range.end.row
9904 || snapshot.is_line_folded(MultiBufferRow(entry.range.end.row)))
9905 {
9906 return None;
9907 }
9908 if entry.range.end > group_end {
9909 group_end = entry.range.end;
9910 }
9911 if entry.diagnostic.is_primary {
9912 primary_range = Some(entry.range.clone());
9913 primary_message = Some(entry.diagnostic.message.clone());
9914 }
9915 Some(entry)
9916 })
9917 .collect::<Vec<_>>();
9918 let primary_range = primary_range?;
9919 let primary_message = primary_message?;
9920 let primary_range =
9921 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
9922
9923 let blocks = display_map
9924 .insert_blocks(
9925 diagnostic_group.iter().map(|entry| {
9926 let diagnostic = entry.diagnostic.clone();
9927 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
9928 BlockProperties {
9929 style: BlockStyle::Fixed,
9930 position: buffer.anchor_after(entry.range.start),
9931 height: message_height,
9932 render: diagnostic_block_renderer(diagnostic, None, true, true),
9933 disposition: BlockDisposition::Below,
9934 priority: 0,
9935 }
9936 }),
9937 cx,
9938 )
9939 .into_iter()
9940 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
9941 .collect();
9942
9943 Some(ActiveDiagnosticGroup {
9944 primary_range,
9945 primary_message,
9946 group_id,
9947 blocks,
9948 is_valid: true,
9949 })
9950 });
9951 self.active_diagnostics.is_some()
9952 }
9953
9954 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
9955 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
9956 self.display_map.update(cx, |display_map, cx| {
9957 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
9958 });
9959 cx.notify();
9960 }
9961 }
9962
9963 pub fn set_selections_from_remote(
9964 &mut self,
9965 selections: Vec<Selection<Anchor>>,
9966 pending_selection: Option<Selection<Anchor>>,
9967 cx: &mut ViewContext<Self>,
9968 ) {
9969 let old_cursor_position = self.selections.newest_anchor().head();
9970 self.selections.change_with(cx, |s| {
9971 s.select_anchors(selections);
9972 if let Some(pending_selection) = pending_selection {
9973 s.set_pending(pending_selection, SelectMode::Character);
9974 } else {
9975 s.clear_pending();
9976 }
9977 });
9978 self.selections_did_change(false, &old_cursor_position, true, cx);
9979 }
9980
9981 fn push_to_selection_history(&mut self) {
9982 self.selection_history.push(SelectionHistoryEntry {
9983 selections: self.selections.disjoint_anchors(),
9984 select_next_state: self.select_next_state.clone(),
9985 select_prev_state: self.select_prev_state.clone(),
9986 add_selections_state: self.add_selections_state.clone(),
9987 });
9988 }
9989
9990 pub fn transact(
9991 &mut self,
9992 cx: &mut ViewContext<Self>,
9993 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
9994 ) -> Option<TransactionId> {
9995 self.start_transaction_at(Instant::now(), cx);
9996 update(self, cx);
9997 self.end_transaction_at(Instant::now(), cx)
9998 }
9999
10000 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
10001 self.end_selection(cx);
10002 if let Some(tx_id) = self
10003 .buffer
10004 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
10005 {
10006 self.selection_history
10007 .insert_transaction(tx_id, self.selections.disjoint_anchors());
10008 cx.emit(EditorEvent::TransactionBegun {
10009 transaction_id: tx_id,
10010 })
10011 }
10012 }
10013
10014 fn end_transaction_at(
10015 &mut self,
10016 now: Instant,
10017 cx: &mut ViewContext<Self>,
10018 ) -> Option<TransactionId> {
10019 if let Some(transaction_id) = self
10020 .buffer
10021 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
10022 {
10023 if let Some((_, end_selections)) =
10024 self.selection_history.transaction_mut(transaction_id)
10025 {
10026 *end_selections = Some(self.selections.disjoint_anchors());
10027 } else {
10028 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
10029 }
10030
10031 cx.emit(EditorEvent::Edited { transaction_id });
10032 Some(transaction_id)
10033 } else {
10034 None
10035 }
10036 }
10037
10038 pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
10039 let mut fold_ranges = Vec::new();
10040
10041 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10042
10043 let selections = self.selections.all_adjusted(cx);
10044 for selection in selections {
10045 let range = selection.range().sorted();
10046 let buffer_start_row = range.start.row;
10047
10048 for row in (0..=range.end.row).rev() {
10049 if let Some((foldable_range, fold_text)) =
10050 display_map.foldable_range(MultiBufferRow(row))
10051 {
10052 if foldable_range.end.row >= buffer_start_row {
10053 fold_ranges.push((foldable_range, fold_text));
10054 if row <= range.start.row {
10055 break;
10056 }
10057 }
10058 }
10059 }
10060 }
10061
10062 self.fold_ranges(fold_ranges, true, cx);
10063 }
10064
10065 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
10066 let buffer_row = fold_at.buffer_row;
10067 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10068
10069 if let Some((fold_range, placeholder)) = display_map.foldable_range(buffer_row) {
10070 let autoscroll = self
10071 .selections
10072 .all::<Point>(cx)
10073 .iter()
10074 .any(|selection| fold_range.overlaps(&selection.range()));
10075
10076 self.fold_ranges([(fold_range, placeholder)], autoscroll, cx);
10077 }
10078 }
10079
10080 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
10081 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10082 let buffer = &display_map.buffer_snapshot;
10083 let selections = self.selections.all::<Point>(cx);
10084 let ranges = selections
10085 .iter()
10086 .map(|s| {
10087 let range = s.display_range(&display_map).sorted();
10088 let mut start = range.start.to_point(&display_map);
10089 let mut end = range.end.to_point(&display_map);
10090 start.column = 0;
10091 end.column = buffer.line_len(MultiBufferRow(end.row));
10092 start..end
10093 })
10094 .collect::<Vec<_>>();
10095
10096 self.unfold_ranges(ranges, true, true, cx);
10097 }
10098
10099 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
10100 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10101
10102 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
10103 ..Point::new(
10104 unfold_at.buffer_row.0,
10105 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
10106 );
10107
10108 let autoscroll = self
10109 .selections
10110 .all::<Point>(cx)
10111 .iter()
10112 .any(|selection| selection.range().overlaps(&intersection_range));
10113
10114 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
10115 }
10116
10117 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
10118 let selections = self.selections.all::<Point>(cx);
10119 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10120 let line_mode = self.selections.line_mode;
10121 let ranges = selections.into_iter().map(|s| {
10122 if line_mode {
10123 let start = Point::new(s.start.row, 0);
10124 let end = Point::new(
10125 s.end.row,
10126 display_map
10127 .buffer_snapshot
10128 .line_len(MultiBufferRow(s.end.row)),
10129 );
10130 (start..end, display_map.fold_placeholder.clone())
10131 } else {
10132 (s.start..s.end, display_map.fold_placeholder.clone())
10133 }
10134 });
10135 self.fold_ranges(ranges, true, cx);
10136 }
10137
10138 pub fn fold_ranges<T: ToOffset + Clone>(
10139 &mut self,
10140 ranges: impl IntoIterator<Item = (Range<T>, FoldPlaceholder)>,
10141 auto_scroll: bool,
10142 cx: &mut ViewContext<Self>,
10143 ) {
10144 let mut fold_ranges = Vec::new();
10145 let mut buffers_affected = HashMap::default();
10146 let multi_buffer = self.buffer().read(cx);
10147 for (fold_range, fold_text) in ranges {
10148 if let Some((_, buffer, _)) =
10149 multi_buffer.excerpt_containing(fold_range.start.clone(), cx)
10150 {
10151 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
10152 };
10153 fold_ranges.push((fold_range, fold_text));
10154 }
10155
10156 let mut ranges = fold_ranges.into_iter().peekable();
10157 if ranges.peek().is_some() {
10158 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
10159
10160 if auto_scroll {
10161 self.request_autoscroll(Autoscroll::fit(), cx);
10162 }
10163
10164 for buffer in buffers_affected.into_values() {
10165 self.sync_expanded_diff_hunks(buffer, cx);
10166 }
10167
10168 cx.notify();
10169
10170 if let Some(active_diagnostics) = self.active_diagnostics.take() {
10171 // Clear diagnostics block when folding a range that contains it.
10172 let snapshot = self.snapshot(cx);
10173 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
10174 drop(snapshot);
10175 self.active_diagnostics = Some(active_diagnostics);
10176 self.dismiss_diagnostics(cx);
10177 } else {
10178 self.active_diagnostics = Some(active_diagnostics);
10179 }
10180 }
10181
10182 self.scrollbar_marker_state.dirty = true;
10183 }
10184 }
10185
10186 pub fn unfold_ranges<T: ToOffset + Clone>(
10187 &mut self,
10188 ranges: impl IntoIterator<Item = Range<T>>,
10189 inclusive: bool,
10190 auto_scroll: bool,
10191 cx: &mut ViewContext<Self>,
10192 ) {
10193 let mut unfold_ranges = Vec::new();
10194 let mut buffers_affected = HashMap::default();
10195 let multi_buffer = self.buffer().read(cx);
10196 for range in ranges {
10197 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
10198 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
10199 };
10200 unfold_ranges.push(range);
10201 }
10202
10203 let mut ranges = unfold_ranges.into_iter().peekable();
10204 if ranges.peek().is_some() {
10205 self.display_map
10206 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
10207 if auto_scroll {
10208 self.request_autoscroll(Autoscroll::fit(), cx);
10209 }
10210
10211 for buffer in buffers_affected.into_values() {
10212 self.sync_expanded_diff_hunks(buffer, cx);
10213 }
10214
10215 cx.notify();
10216 self.scrollbar_marker_state.dirty = true;
10217 self.active_indent_guides_state.dirty = true;
10218 }
10219 }
10220
10221 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
10222 if hovered != self.gutter_hovered {
10223 self.gutter_hovered = hovered;
10224 cx.notify();
10225 }
10226 }
10227
10228 pub fn insert_blocks(
10229 &mut self,
10230 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
10231 autoscroll: Option<Autoscroll>,
10232 cx: &mut ViewContext<Self>,
10233 ) -> Vec<CustomBlockId> {
10234 let blocks = self
10235 .display_map
10236 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
10237 if let Some(autoscroll) = autoscroll {
10238 self.request_autoscroll(autoscroll, cx);
10239 }
10240 cx.notify();
10241 blocks
10242 }
10243
10244 pub fn resize_blocks(
10245 &mut self,
10246 heights: HashMap<CustomBlockId, u32>,
10247 autoscroll: Option<Autoscroll>,
10248 cx: &mut ViewContext<Self>,
10249 ) {
10250 self.display_map
10251 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
10252 if let Some(autoscroll) = autoscroll {
10253 self.request_autoscroll(autoscroll, cx);
10254 }
10255 cx.notify();
10256 }
10257
10258 pub fn replace_blocks(
10259 &mut self,
10260 renderers: HashMap<CustomBlockId, RenderBlock>,
10261 autoscroll: Option<Autoscroll>,
10262 cx: &mut ViewContext<Self>,
10263 ) {
10264 self.display_map
10265 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
10266 if let Some(autoscroll) = autoscroll {
10267 self.request_autoscroll(autoscroll, cx);
10268 }
10269 cx.notify();
10270 }
10271
10272 pub fn remove_blocks(
10273 &mut self,
10274 block_ids: HashSet<CustomBlockId>,
10275 autoscroll: Option<Autoscroll>,
10276 cx: &mut ViewContext<Self>,
10277 ) {
10278 self.display_map.update(cx, |display_map, cx| {
10279 display_map.remove_blocks(block_ids, cx)
10280 });
10281 if let Some(autoscroll) = autoscroll {
10282 self.request_autoscroll(autoscroll, cx);
10283 }
10284 cx.notify();
10285 }
10286
10287 pub fn row_for_block(
10288 &self,
10289 block_id: CustomBlockId,
10290 cx: &mut ViewContext<Self>,
10291 ) -> Option<DisplayRow> {
10292 self.display_map
10293 .update(cx, |map, cx| map.row_for_block(block_id, cx))
10294 }
10295
10296 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
10297 self.focused_block = Some(focused_block);
10298 }
10299
10300 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
10301 self.focused_block.take()
10302 }
10303
10304 pub fn insert_creases(
10305 &mut self,
10306 creases: impl IntoIterator<Item = Crease>,
10307 cx: &mut ViewContext<Self>,
10308 ) -> Vec<CreaseId> {
10309 self.display_map
10310 .update(cx, |map, cx| map.insert_creases(creases, cx))
10311 }
10312
10313 pub fn remove_creases(
10314 &mut self,
10315 ids: impl IntoIterator<Item = CreaseId>,
10316 cx: &mut ViewContext<Self>,
10317 ) {
10318 self.display_map
10319 .update(cx, |map, cx| map.remove_creases(ids, cx));
10320 }
10321
10322 pub fn longest_row(&self, cx: &mut AppContext) -> DisplayRow {
10323 self.display_map
10324 .update(cx, |map, cx| map.snapshot(cx))
10325 .longest_row()
10326 }
10327
10328 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
10329 self.display_map
10330 .update(cx, |map, cx| map.snapshot(cx))
10331 .max_point()
10332 }
10333
10334 pub fn text(&self, cx: &AppContext) -> String {
10335 self.buffer.read(cx).read(cx).text()
10336 }
10337
10338 pub fn text_option(&self, cx: &AppContext) -> Option<String> {
10339 let text = self.text(cx);
10340 let text = text.trim();
10341
10342 if text.is_empty() {
10343 return None;
10344 }
10345
10346 Some(text.to_string())
10347 }
10348
10349 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
10350 self.transact(cx, |this, cx| {
10351 this.buffer
10352 .read(cx)
10353 .as_singleton()
10354 .expect("you can only call set_text on editors for singleton buffers")
10355 .update(cx, |buffer, cx| buffer.set_text(text, cx));
10356 });
10357 }
10358
10359 pub fn display_text(&self, cx: &mut AppContext) -> String {
10360 self.display_map
10361 .update(cx, |map, cx| map.snapshot(cx))
10362 .text()
10363 }
10364
10365 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
10366 let mut wrap_guides = smallvec::smallvec![];
10367
10368 if self.show_wrap_guides == Some(false) {
10369 return wrap_guides;
10370 }
10371
10372 let settings = self.buffer.read(cx).settings_at(0, cx);
10373 if settings.show_wrap_guides {
10374 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
10375 wrap_guides.push((soft_wrap as usize, true));
10376 }
10377 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
10378 }
10379
10380 wrap_guides
10381 }
10382
10383 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
10384 let settings = self.buffer.read(cx).settings_at(0, cx);
10385 let mode = self
10386 .soft_wrap_mode_override
10387 .unwrap_or_else(|| settings.soft_wrap);
10388 match mode {
10389 language_settings::SoftWrap::None => SoftWrap::None,
10390 language_settings::SoftWrap::PreferLine => SoftWrap::PreferLine,
10391 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
10392 language_settings::SoftWrap::PreferredLineLength => {
10393 SoftWrap::Column(settings.preferred_line_length)
10394 }
10395 }
10396 }
10397
10398 pub fn set_soft_wrap_mode(
10399 &mut self,
10400 mode: language_settings::SoftWrap,
10401 cx: &mut ViewContext<Self>,
10402 ) {
10403 self.soft_wrap_mode_override = Some(mode);
10404 cx.notify();
10405 }
10406
10407 pub fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
10408 let rem_size = cx.rem_size();
10409 self.display_map.update(cx, |map, cx| {
10410 map.set_font(
10411 style.text.font(),
10412 style.text.font_size.to_pixels(rem_size),
10413 cx,
10414 )
10415 });
10416 self.style = Some(style);
10417 }
10418
10419 pub fn style(&self) -> Option<&EditorStyle> {
10420 self.style.as_ref()
10421 }
10422
10423 // Called by the element. This method is not designed to be called outside of the editor
10424 // element's layout code because it does not notify when rewrapping is computed synchronously.
10425 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
10426 self.display_map
10427 .update(cx, |map, cx| map.set_wrap_width(width, cx))
10428 }
10429
10430 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
10431 if self.soft_wrap_mode_override.is_some() {
10432 self.soft_wrap_mode_override.take();
10433 } else {
10434 let soft_wrap = match self.soft_wrap_mode(cx) {
10435 SoftWrap::None | SoftWrap::PreferLine => language_settings::SoftWrap::EditorWidth,
10436 SoftWrap::EditorWidth | SoftWrap::Column(_) => {
10437 language_settings::SoftWrap::PreferLine
10438 }
10439 };
10440 self.soft_wrap_mode_override = Some(soft_wrap);
10441 }
10442 cx.notify();
10443 }
10444
10445 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, cx: &mut ViewContext<Self>) {
10446 let Some(workspace) = self.workspace() else {
10447 return;
10448 };
10449 let fs = workspace.read(cx).app_state().fs.clone();
10450 let current_show = TabBarSettings::get_global(cx).show;
10451 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
10452 setting.show = Some(!current_show);
10453 });
10454 }
10455
10456 pub fn toggle_indent_guides(&mut self, _: &ToggleIndentGuides, cx: &mut ViewContext<Self>) {
10457 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
10458 self.buffer
10459 .read(cx)
10460 .settings_at(0, cx)
10461 .indent_guides
10462 .enabled
10463 });
10464 self.show_indent_guides = Some(!currently_enabled);
10465 cx.notify();
10466 }
10467
10468 fn should_show_indent_guides(&self) -> Option<bool> {
10469 self.show_indent_guides
10470 }
10471
10472 pub fn toggle_line_numbers(&mut self, _: &ToggleLineNumbers, cx: &mut ViewContext<Self>) {
10473 let mut editor_settings = EditorSettings::get_global(cx).clone();
10474 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
10475 EditorSettings::override_global(editor_settings, cx);
10476 }
10477
10478 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
10479 self.show_gutter = show_gutter;
10480 cx.notify();
10481 }
10482
10483 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut ViewContext<Self>) {
10484 self.show_line_numbers = Some(show_line_numbers);
10485 cx.notify();
10486 }
10487
10488 pub fn set_show_git_diff_gutter(
10489 &mut self,
10490 show_git_diff_gutter: bool,
10491 cx: &mut ViewContext<Self>,
10492 ) {
10493 self.show_git_diff_gutter = Some(show_git_diff_gutter);
10494 cx.notify();
10495 }
10496
10497 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut ViewContext<Self>) {
10498 self.show_code_actions = Some(show_code_actions);
10499 cx.notify();
10500 }
10501
10502 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut ViewContext<Self>) {
10503 self.show_runnables = Some(show_runnables);
10504 cx.notify();
10505 }
10506
10507 pub fn set_masked(&mut self, masked: bool, cx: &mut ViewContext<Self>) {
10508 if self.display_map.read(cx).masked != masked {
10509 self.display_map.update(cx, |map, _| map.masked = masked);
10510 }
10511 cx.notify()
10512 }
10513
10514 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut ViewContext<Self>) {
10515 self.show_wrap_guides = Some(show_wrap_guides);
10516 cx.notify();
10517 }
10518
10519 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut ViewContext<Self>) {
10520 self.show_indent_guides = Some(show_indent_guides);
10521 cx.notify();
10522 }
10523
10524 pub fn working_directory(&self, cx: &WindowContext) -> Option<PathBuf> {
10525 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10526 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10527 if let Some(dir) = file.abs_path(cx).parent() {
10528 return Some(dir.to_owned());
10529 }
10530 }
10531
10532 if let Some(project_path) = buffer.read(cx).project_path(cx) {
10533 return Some(project_path.path.to_path_buf());
10534 }
10535 }
10536
10537 None
10538 }
10539
10540 pub fn reveal_in_finder(&mut self, _: &RevealInFileManager, cx: &mut ViewContext<Self>) {
10541 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10542 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10543 cx.reveal_path(&file.abs_path(cx));
10544 }
10545 }
10546 }
10547
10548 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
10549 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10550 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10551 if let Some(path) = file.abs_path(cx).to_str() {
10552 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
10553 }
10554 }
10555 }
10556 }
10557
10558 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
10559 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10560 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10561 if let Some(path) = file.path().to_str() {
10562 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
10563 }
10564 }
10565 }
10566 }
10567
10568 pub fn toggle_git_blame(&mut self, _: &ToggleGitBlame, cx: &mut ViewContext<Self>) {
10569 self.show_git_blame_gutter = !self.show_git_blame_gutter;
10570
10571 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
10572 self.start_git_blame(true, cx);
10573 }
10574
10575 cx.notify();
10576 }
10577
10578 pub fn toggle_git_blame_inline(
10579 &mut self,
10580 _: &ToggleGitBlameInline,
10581 cx: &mut ViewContext<Self>,
10582 ) {
10583 self.toggle_git_blame_inline_internal(true, cx);
10584 cx.notify();
10585 }
10586
10587 pub fn git_blame_inline_enabled(&self) -> bool {
10588 self.git_blame_inline_enabled
10589 }
10590
10591 pub fn toggle_selection_menu(&mut self, _: &ToggleSelectionMenu, cx: &mut ViewContext<Self>) {
10592 self.show_selection_menu = self
10593 .show_selection_menu
10594 .map(|show_selections_menu| !show_selections_menu)
10595 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
10596
10597 cx.notify();
10598 }
10599
10600 pub fn selection_menu_enabled(&self, cx: &AppContext) -> bool {
10601 self.show_selection_menu
10602 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
10603 }
10604
10605 fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
10606 if let Some(project) = self.project.as_ref() {
10607 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
10608 return;
10609 };
10610
10611 if buffer.read(cx).file().is_none() {
10612 return;
10613 }
10614
10615 let focused = self.focus_handle(cx).contains_focused(cx);
10616
10617 let project = project.clone();
10618 let blame =
10619 cx.new_model(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
10620 self.blame_subscription = Some(cx.observe(&blame, |_, _, cx| cx.notify()));
10621 self.blame = Some(blame);
10622 }
10623 }
10624
10625 fn toggle_git_blame_inline_internal(
10626 &mut self,
10627 user_triggered: bool,
10628 cx: &mut ViewContext<Self>,
10629 ) {
10630 if self.git_blame_inline_enabled {
10631 self.git_blame_inline_enabled = false;
10632 self.show_git_blame_inline = false;
10633 self.show_git_blame_inline_delay_task.take();
10634 } else {
10635 self.git_blame_inline_enabled = true;
10636 self.start_git_blame_inline(user_triggered, cx);
10637 }
10638
10639 cx.notify();
10640 }
10641
10642 fn start_git_blame_inline(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
10643 self.start_git_blame(user_triggered, cx);
10644
10645 if ProjectSettings::get_global(cx)
10646 .git
10647 .inline_blame_delay()
10648 .is_some()
10649 {
10650 self.start_inline_blame_timer(cx);
10651 } else {
10652 self.show_git_blame_inline = true
10653 }
10654 }
10655
10656 pub fn blame(&self) -> Option<&Model<GitBlame>> {
10657 self.blame.as_ref()
10658 }
10659
10660 pub fn render_git_blame_gutter(&mut self, cx: &mut WindowContext) -> bool {
10661 self.show_git_blame_gutter && self.has_blame_entries(cx)
10662 }
10663
10664 pub fn render_git_blame_inline(&mut self, cx: &mut WindowContext) -> bool {
10665 self.show_git_blame_inline
10666 && self.focus_handle.is_focused(cx)
10667 && !self.newest_selection_head_on_empty_line(cx)
10668 && self.has_blame_entries(cx)
10669 }
10670
10671 fn has_blame_entries(&self, cx: &mut WindowContext) -> bool {
10672 self.blame()
10673 .map_or(false, |blame| blame.read(cx).has_generated_entries())
10674 }
10675
10676 fn newest_selection_head_on_empty_line(&mut self, cx: &mut WindowContext) -> bool {
10677 let cursor_anchor = self.selections.newest_anchor().head();
10678
10679 let snapshot = self.buffer.read(cx).snapshot(cx);
10680 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
10681
10682 snapshot.line_len(buffer_row) == 0
10683 }
10684
10685 fn get_permalink_to_line(&mut self, cx: &mut ViewContext<Self>) -> Result<url::Url> {
10686 let (path, selection, repo) = maybe!({
10687 let project_handle = self.project.as_ref()?.clone();
10688 let project = project_handle.read(cx);
10689
10690 let selection = self.selections.newest::<Point>(cx);
10691 let selection_range = selection.range();
10692
10693 let (buffer, selection) = if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10694 (buffer, selection_range.start.row..selection_range.end.row)
10695 } else {
10696 let buffer_ranges = self
10697 .buffer()
10698 .read(cx)
10699 .range_to_buffer_ranges(selection_range, cx);
10700
10701 let (buffer, range, _) = if selection.reversed {
10702 buffer_ranges.first()
10703 } else {
10704 buffer_ranges.last()
10705 }?;
10706
10707 let snapshot = buffer.read(cx).snapshot();
10708 let selection = text::ToPoint::to_point(&range.start, &snapshot).row
10709 ..text::ToPoint::to_point(&range.end, &snapshot).row;
10710 (buffer.clone(), selection)
10711 };
10712
10713 let path = buffer
10714 .read(cx)
10715 .file()?
10716 .as_local()?
10717 .path()
10718 .to_str()?
10719 .to_string();
10720 let repo = project.get_repo(&buffer.read(cx).project_path(cx)?, cx)?;
10721 Some((path, selection, repo))
10722 })
10723 .ok_or_else(|| anyhow!("unable to open git repository"))?;
10724
10725 const REMOTE_NAME: &str = "origin";
10726 let origin_url = repo
10727 .remote_url(REMOTE_NAME)
10728 .ok_or_else(|| anyhow!("remote \"{REMOTE_NAME}\" not found"))?;
10729 let sha = repo
10730 .head_sha()
10731 .ok_or_else(|| anyhow!("failed to read HEAD SHA"))?;
10732
10733 let (provider, remote) =
10734 parse_git_remote_url(GitHostingProviderRegistry::default_global(cx), &origin_url)
10735 .ok_or_else(|| anyhow!("failed to parse Git remote URL"))?;
10736
10737 Ok(provider.build_permalink(
10738 remote,
10739 BuildPermalinkParams {
10740 sha: &sha,
10741 path: &path,
10742 selection: Some(selection),
10743 },
10744 ))
10745 }
10746
10747 pub fn copy_permalink_to_line(&mut self, _: &CopyPermalinkToLine, cx: &mut ViewContext<Self>) {
10748 let permalink = self.get_permalink_to_line(cx);
10749
10750 match permalink {
10751 Ok(permalink) => {
10752 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
10753 }
10754 Err(err) => {
10755 let message = format!("Failed to copy permalink: {err}");
10756
10757 Err::<(), anyhow::Error>(err).log_err();
10758
10759 if let Some(workspace) = self.workspace() {
10760 workspace.update(cx, |workspace, cx| {
10761 struct CopyPermalinkToLine;
10762
10763 workspace.show_toast(
10764 Toast::new(NotificationId::unique::<CopyPermalinkToLine>(), message),
10765 cx,
10766 )
10767 })
10768 }
10769 }
10770 }
10771 }
10772
10773 pub fn open_permalink_to_line(&mut self, _: &OpenPermalinkToLine, cx: &mut ViewContext<Self>) {
10774 let permalink = self.get_permalink_to_line(cx);
10775
10776 match permalink {
10777 Ok(permalink) => {
10778 cx.open_url(permalink.as_ref());
10779 }
10780 Err(err) => {
10781 let message = format!("Failed to open permalink: {err}");
10782
10783 Err::<(), anyhow::Error>(err).log_err();
10784
10785 if let Some(workspace) = self.workspace() {
10786 workspace.update(cx, |workspace, cx| {
10787 struct OpenPermalinkToLine;
10788
10789 workspace.show_toast(
10790 Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
10791 cx,
10792 )
10793 })
10794 }
10795 }
10796 }
10797 }
10798
10799 /// Adds or removes (on `None` color) a highlight for the rows corresponding to the anchor range given.
10800 /// On matching anchor range, replaces the old highlight; does not clear the other existing highlights.
10801 /// If multiple anchor ranges will produce highlights for the same row, the last range added will be used.
10802 pub fn highlight_rows<T: 'static>(
10803 &mut self,
10804 rows: RangeInclusive<Anchor>,
10805 color: Option<Hsla>,
10806 should_autoscroll: bool,
10807 cx: &mut ViewContext<Self>,
10808 ) {
10809 let snapshot = self.buffer().read(cx).snapshot(cx);
10810 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
10811 let existing_highlight_index = row_highlights.binary_search_by(|highlight| {
10812 highlight
10813 .range
10814 .start()
10815 .cmp(&rows.start(), &snapshot)
10816 .then(highlight.range.end().cmp(&rows.end(), &snapshot))
10817 });
10818 match (color, existing_highlight_index) {
10819 (Some(_), Ok(ix)) | (_, Err(ix)) => row_highlights.insert(
10820 ix,
10821 RowHighlight {
10822 index: post_inc(&mut self.highlight_order),
10823 range: rows,
10824 should_autoscroll,
10825 color,
10826 },
10827 ),
10828 (None, Ok(i)) => {
10829 row_highlights.remove(i);
10830 }
10831 }
10832 }
10833
10834 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
10835 pub fn clear_row_highlights<T: 'static>(&mut self) {
10836 self.highlighted_rows.remove(&TypeId::of::<T>());
10837 }
10838
10839 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
10840 pub fn highlighted_rows<T: 'static>(
10841 &self,
10842 ) -> Option<impl Iterator<Item = (&RangeInclusive<Anchor>, Option<&Hsla>)>> {
10843 Some(
10844 self.highlighted_rows
10845 .get(&TypeId::of::<T>())?
10846 .iter()
10847 .map(|highlight| (&highlight.range, highlight.color.as_ref())),
10848 )
10849 }
10850
10851 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
10852 /// Rerturns a map of display rows that are highlighted and their corresponding highlight color.
10853 /// Allows to ignore certain kinds of highlights.
10854 pub fn highlighted_display_rows(
10855 &mut self,
10856 cx: &mut WindowContext,
10857 ) -> BTreeMap<DisplayRow, Hsla> {
10858 let snapshot = self.snapshot(cx);
10859 let mut used_highlight_orders = HashMap::default();
10860 self.highlighted_rows
10861 .iter()
10862 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
10863 .fold(
10864 BTreeMap::<DisplayRow, Hsla>::new(),
10865 |mut unique_rows, highlight| {
10866 let start_row = highlight.range.start().to_display_point(&snapshot).row();
10867 let end_row = highlight.range.end().to_display_point(&snapshot).row();
10868 for row in start_row.0..=end_row.0 {
10869 let used_index =
10870 used_highlight_orders.entry(row).or_insert(highlight.index);
10871 if highlight.index >= *used_index {
10872 *used_index = highlight.index;
10873 match highlight.color {
10874 Some(hsla) => unique_rows.insert(DisplayRow(row), hsla),
10875 None => unique_rows.remove(&DisplayRow(row)),
10876 };
10877 }
10878 }
10879 unique_rows
10880 },
10881 )
10882 }
10883
10884 pub fn highlighted_display_row_for_autoscroll(
10885 &self,
10886 snapshot: &DisplaySnapshot,
10887 ) -> Option<DisplayRow> {
10888 self.highlighted_rows
10889 .values()
10890 .flat_map(|highlighted_rows| highlighted_rows.iter())
10891 .filter_map(|highlight| {
10892 if highlight.color.is_none() || !highlight.should_autoscroll {
10893 return None;
10894 }
10895 Some(highlight.range.start().to_display_point(&snapshot).row())
10896 })
10897 .min()
10898 }
10899
10900 pub fn set_search_within_ranges(
10901 &mut self,
10902 ranges: &[Range<Anchor>],
10903 cx: &mut ViewContext<Self>,
10904 ) {
10905 self.highlight_background::<SearchWithinRange>(
10906 ranges,
10907 |colors| colors.editor_document_highlight_read_background,
10908 cx,
10909 )
10910 }
10911
10912 pub fn set_breadcrumb_header(&mut self, new_header: String) {
10913 self.breadcrumb_header = Some(new_header);
10914 }
10915
10916 pub fn clear_search_within_ranges(&mut self, cx: &mut ViewContext<Self>) {
10917 self.clear_background_highlights::<SearchWithinRange>(cx);
10918 }
10919
10920 pub fn highlight_background<T: 'static>(
10921 &mut self,
10922 ranges: &[Range<Anchor>],
10923 color_fetcher: fn(&ThemeColors) -> Hsla,
10924 cx: &mut ViewContext<Self>,
10925 ) {
10926 self.background_highlights
10927 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
10928 self.scrollbar_marker_state.dirty = true;
10929 cx.notify();
10930 }
10931
10932 pub fn clear_background_highlights<T: 'static>(
10933 &mut self,
10934 cx: &mut ViewContext<Self>,
10935 ) -> Option<BackgroundHighlight> {
10936 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
10937 if !text_highlights.1.is_empty() {
10938 self.scrollbar_marker_state.dirty = true;
10939 cx.notify();
10940 }
10941 Some(text_highlights)
10942 }
10943
10944 pub fn highlight_gutter<T: 'static>(
10945 &mut self,
10946 ranges: &[Range<Anchor>],
10947 color_fetcher: fn(&AppContext) -> Hsla,
10948 cx: &mut ViewContext<Self>,
10949 ) {
10950 self.gutter_highlights
10951 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
10952 cx.notify();
10953 }
10954
10955 pub fn clear_gutter_highlights<T: 'static>(
10956 &mut self,
10957 cx: &mut ViewContext<Self>,
10958 ) -> Option<GutterHighlight> {
10959 cx.notify();
10960 self.gutter_highlights.remove(&TypeId::of::<T>())
10961 }
10962
10963 #[cfg(feature = "test-support")]
10964 pub fn all_text_background_highlights(
10965 &mut self,
10966 cx: &mut ViewContext<Self>,
10967 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
10968 let snapshot = self.snapshot(cx);
10969 let buffer = &snapshot.buffer_snapshot;
10970 let start = buffer.anchor_before(0);
10971 let end = buffer.anchor_after(buffer.len());
10972 let theme = cx.theme().colors();
10973 self.background_highlights_in_range(start..end, &snapshot, theme)
10974 }
10975
10976 #[cfg(feature = "test-support")]
10977 pub fn search_background_highlights(
10978 &mut self,
10979 cx: &mut ViewContext<Self>,
10980 ) -> Vec<Range<Point>> {
10981 let snapshot = self.buffer().read(cx).snapshot(cx);
10982
10983 let highlights = self
10984 .background_highlights
10985 .get(&TypeId::of::<items::BufferSearchHighlights>());
10986
10987 if let Some((_color, ranges)) = highlights {
10988 ranges
10989 .iter()
10990 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
10991 .collect_vec()
10992 } else {
10993 vec![]
10994 }
10995 }
10996
10997 fn document_highlights_for_position<'a>(
10998 &'a self,
10999 position: Anchor,
11000 buffer: &'a MultiBufferSnapshot,
11001 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
11002 let read_highlights = self
11003 .background_highlights
11004 .get(&TypeId::of::<DocumentHighlightRead>())
11005 .map(|h| &h.1);
11006 let write_highlights = self
11007 .background_highlights
11008 .get(&TypeId::of::<DocumentHighlightWrite>())
11009 .map(|h| &h.1);
11010 let left_position = position.bias_left(buffer);
11011 let right_position = position.bias_right(buffer);
11012 read_highlights
11013 .into_iter()
11014 .chain(write_highlights)
11015 .flat_map(move |ranges| {
11016 let start_ix = match ranges.binary_search_by(|probe| {
11017 let cmp = probe.end.cmp(&left_position, buffer);
11018 if cmp.is_ge() {
11019 Ordering::Greater
11020 } else {
11021 Ordering::Less
11022 }
11023 }) {
11024 Ok(i) | Err(i) => i,
11025 };
11026
11027 ranges[start_ix..]
11028 .iter()
11029 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
11030 })
11031 }
11032
11033 pub fn has_background_highlights<T: 'static>(&self) -> bool {
11034 self.background_highlights
11035 .get(&TypeId::of::<T>())
11036 .map_or(false, |(_, highlights)| !highlights.is_empty())
11037 }
11038
11039 pub fn background_highlights_in_range(
11040 &self,
11041 search_range: Range<Anchor>,
11042 display_snapshot: &DisplaySnapshot,
11043 theme: &ThemeColors,
11044 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11045 let mut results = Vec::new();
11046 for (color_fetcher, ranges) in self.background_highlights.values() {
11047 let color = color_fetcher(theme);
11048 let start_ix = match ranges.binary_search_by(|probe| {
11049 let cmp = probe
11050 .end
11051 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11052 if cmp.is_gt() {
11053 Ordering::Greater
11054 } else {
11055 Ordering::Less
11056 }
11057 }) {
11058 Ok(i) | Err(i) => i,
11059 };
11060 for range in &ranges[start_ix..] {
11061 if range
11062 .start
11063 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11064 .is_ge()
11065 {
11066 break;
11067 }
11068
11069 let start = range.start.to_display_point(&display_snapshot);
11070 let end = range.end.to_display_point(&display_snapshot);
11071 results.push((start..end, color))
11072 }
11073 }
11074 results
11075 }
11076
11077 pub fn background_highlight_row_ranges<T: 'static>(
11078 &self,
11079 search_range: Range<Anchor>,
11080 display_snapshot: &DisplaySnapshot,
11081 count: usize,
11082 ) -> Vec<RangeInclusive<DisplayPoint>> {
11083 let mut results = Vec::new();
11084 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
11085 return vec![];
11086 };
11087
11088 let start_ix = match ranges.binary_search_by(|probe| {
11089 let cmp = probe
11090 .end
11091 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11092 if cmp.is_gt() {
11093 Ordering::Greater
11094 } else {
11095 Ordering::Less
11096 }
11097 }) {
11098 Ok(i) | Err(i) => i,
11099 };
11100 let mut push_region = |start: Option<Point>, end: Option<Point>| {
11101 if let (Some(start_display), Some(end_display)) = (start, end) {
11102 results.push(
11103 start_display.to_display_point(display_snapshot)
11104 ..=end_display.to_display_point(display_snapshot),
11105 );
11106 }
11107 };
11108 let mut start_row: Option<Point> = None;
11109 let mut end_row: Option<Point> = None;
11110 if ranges.len() > count {
11111 return Vec::new();
11112 }
11113 for range in &ranges[start_ix..] {
11114 if range
11115 .start
11116 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11117 .is_ge()
11118 {
11119 break;
11120 }
11121 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
11122 if let Some(current_row) = &end_row {
11123 if end.row == current_row.row {
11124 continue;
11125 }
11126 }
11127 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
11128 if start_row.is_none() {
11129 assert_eq!(end_row, None);
11130 start_row = Some(start);
11131 end_row = Some(end);
11132 continue;
11133 }
11134 if let Some(current_end) = end_row.as_mut() {
11135 if start.row > current_end.row + 1 {
11136 push_region(start_row, end_row);
11137 start_row = Some(start);
11138 end_row = Some(end);
11139 } else {
11140 // Merge two hunks.
11141 *current_end = end;
11142 }
11143 } else {
11144 unreachable!();
11145 }
11146 }
11147 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
11148 push_region(start_row, end_row);
11149 results
11150 }
11151
11152 pub fn gutter_highlights_in_range(
11153 &self,
11154 search_range: Range<Anchor>,
11155 display_snapshot: &DisplaySnapshot,
11156 cx: &AppContext,
11157 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11158 let mut results = Vec::new();
11159 for (color_fetcher, ranges) in self.gutter_highlights.values() {
11160 let color = color_fetcher(cx);
11161 let start_ix = match ranges.binary_search_by(|probe| {
11162 let cmp = probe
11163 .end
11164 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11165 if cmp.is_gt() {
11166 Ordering::Greater
11167 } else {
11168 Ordering::Less
11169 }
11170 }) {
11171 Ok(i) | Err(i) => i,
11172 };
11173 for range in &ranges[start_ix..] {
11174 if range
11175 .start
11176 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11177 .is_ge()
11178 {
11179 break;
11180 }
11181
11182 let start = range.start.to_display_point(&display_snapshot);
11183 let end = range.end.to_display_point(&display_snapshot);
11184 results.push((start..end, color))
11185 }
11186 }
11187 results
11188 }
11189
11190 /// Get the text ranges corresponding to the redaction query
11191 pub fn redacted_ranges(
11192 &self,
11193 search_range: Range<Anchor>,
11194 display_snapshot: &DisplaySnapshot,
11195 cx: &WindowContext,
11196 ) -> Vec<Range<DisplayPoint>> {
11197 display_snapshot
11198 .buffer_snapshot
11199 .redacted_ranges(search_range, |file| {
11200 if let Some(file) = file {
11201 file.is_private()
11202 && EditorSettings::get(Some(file.as_ref().into()), cx).redact_private_values
11203 } else {
11204 false
11205 }
11206 })
11207 .map(|range| {
11208 range.start.to_display_point(display_snapshot)
11209 ..range.end.to_display_point(display_snapshot)
11210 })
11211 .collect()
11212 }
11213
11214 pub fn highlight_text<T: 'static>(
11215 &mut self,
11216 ranges: Vec<Range<Anchor>>,
11217 style: HighlightStyle,
11218 cx: &mut ViewContext<Self>,
11219 ) {
11220 self.display_map.update(cx, |map, _| {
11221 map.highlight_text(TypeId::of::<T>(), ranges, style)
11222 });
11223 cx.notify();
11224 }
11225
11226 pub(crate) fn highlight_inlays<T: 'static>(
11227 &mut self,
11228 highlights: Vec<InlayHighlight>,
11229 style: HighlightStyle,
11230 cx: &mut ViewContext<Self>,
11231 ) {
11232 self.display_map.update(cx, |map, _| {
11233 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
11234 });
11235 cx.notify();
11236 }
11237
11238 pub fn text_highlights<'a, T: 'static>(
11239 &'a self,
11240 cx: &'a AppContext,
11241 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
11242 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
11243 }
11244
11245 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
11246 let cleared = self
11247 .display_map
11248 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
11249 if cleared {
11250 cx.notify();
11251 }
11252 }
11253
11254 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
11255 (self.read_only(cx) || self.blink_manager.read(cx).visible())
11256 && self.focus_handle.is_focused(cx)
11257 }
11258
11259 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut ViewContext<Self>) {
11260 self.show_cursor_when_unfocused = is_enabled;
11261 cx.notify();
11262 }
11263
11264 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
11265 cx.notify();
11266 }
11267
11268 fn on_buffer_event(
11269 &mut self,
11270 multibuffer: Model<MultiBuffer>,
11271 event: &multi_buffer::Event,
11272 cx: &mut ViewContext<Self>,
11273 ) {
11274 match event {
11275 multi_buffer::Event::Edited {
11276 singleton_buffer_edited,
11277 } => {
11278 self.scrollbar_marker_state.dirty = true;
11279 self.active_indent_guides_state.dirty = true;
11280 self.refresh_active_diagnostics(cx);
11281 self.refresh_code_actions(cx);
11282 if self.has_active_inline_completion(cx) {
11283 self.update_visible_inline_completion(cx);
11284 }
11285 cx.emit(EditorEvent::BufferEdited);
11286 cx.emit(SearchEvent::MatchesInvalidated);
11287 if *singleton_buffer_edited {
11288 if let Some(project) = &self.project {
11289 let project = project.read(cx);
11290 #[allow(clippy::mutable_key_type)]
11291 let languages_affected = multibuffer
11292 .read(cx)
11293 .all_buffers()
11294 .into_iter()
11295 .filter_map(|buffer| {
11296 let buffer = buffer.read(cx);
11297 let language = buffer.language()?;
11298 if project.is_local()
11299 && project.language_servers_for_buffer(buffer, cx).count() == 0
11300 {
11301 None
11302 } else {
11303 Some(language)
11304 }
11305 })
11306 .cloned()
11307 .collect::<HashSet<_>>();
11308 if !languages_affected.is_empty() {
11309 self.refresh_inlay_hints(
11310 InlayHintRefreshReason::BufferEdited(languages_affected),
11311 cx,
11312 );
11313 }
11314 }
11315 }
11316
11317 let Some(project) = &self.project else { return };
11318 let telemetry = project.read(cx).client().telemetry().clone();
11319 refresh_linked_ranges(self, cx);
11320 telemetry.log_edit_event("editor");
11321 }
11322 multi_buffer::Event::ExcerptsAdded {
11323 buffer,
11324 predecessor,
11325 excerpts,
11326 } => {
11327 self.tasks_update_task = Some(self.refresh_runnables(cx));
11328 cx.emit(EditorEvent::ExcerptsAdded {
11329 buffer: buffer.clone(),
11330 predecessor: *predecessor,
11331 excerpts: excerpts.clone(),
11332 });
11333 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
11334 }
11335 multi_buffer::Event::ExcerptsRemoved { ids } => {
11336 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
11337 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
11338 }
11339 multi_buffer::Event::ExcerptsEdited { ids } => {
11340 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
11341 }
11342 multi_buffer::Event::ExcerptsExpanded { ids } => {
11343 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
11344 }
11345 multi_buffer::Event::Reparsed(buffer_id) => {
11346 self.tasks_update_task = Some(self.refresh_runnables(cx));
11347
11348 cx.emit(EditorEvent::Reparsed(*buffer_id));
11349 }
11350 multi_buffer::Event::LanguageChanged(buffer_id) => {
11351 linked_editing_ranges::refresh_linked_ranges(self, cx);
11352 cx.emit(EditorEvent::Reparsed(*buffer_id));
11353 cx.notify();
11354 }
11355 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
11356 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
11357 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
11358 cx.emit(EditorEvent::TitleChanged)
11359 }
11360 multi_buffer::Event::DiffBaseChanged => {
11361 self.scrollbar_marker_state.dirty = true;
11362 cx.emit(EditorEvent::DiffBaseChanged);
11363 cx.notify();
11364 }
11365 multi_buffer::Event::DiffUpdated { buffer } => {
11366 self.sync_expanded_diff_hunks(buffer.clone(), cx);
11367 cx.notify();
11368 }
11369 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
11370 multi_buffer::Event::DiagnosticsUpdated => {
11371 self.refresh_active_diagnostics(cx);
11372 self.scrollbar_marker_state.dirty = true;
11373 cx.notify();
11374 }
11375 _ => {}
11376 };
11377 }
11378
11379 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
11380 cx.notify();
11381 }
11382
11383 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
11384 self.tasks_update_task = Some(self.refresh_runnables(cx));
11385 self.refresh_inline_completion(true, cx);
11386 self.refresh_inlay_hints(
11387 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
11388 self.selections.newest_anchor().head(),
11389 &self.buffer.read(cx).snapshot(cx),
11390 cx,
11391 )),
11392 cx,
11393 );
11394 let editor_settings = EditorSettings::get_global(cx);
11395 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
11396 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
11397
11398 let project_settings = ProjectSettings::get_global(cx);
11399 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
11400
11401 if self.mode == EditorMode::Full {
11402 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
11403 if self.git_blame_inline_enabled != inline_blame_enabled {
11404 self.toggle_git_blame_inline_internal(false, cx);
11405 }
11406 }
11407
11408 cx.notify();
11409 }
11410
11411 pub fn set_searchable(&mut self, searchable: bool) {
11412 self.searchable = searchable;
11413 }
11414
11415 pub fn searchable(&self) -> bool {
11416 self.searchable
11417 }
11418
11419 fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext<Self>) {
11420 self.open_excerpts_common(true, cx)
11421 }
11422
11423 fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
11424 self.open_excerpts_common(false, cx)
11425 }
11426
11427 fn open_excerpts_common(&mut self, split: bool, cx: &mut ViewContext<Self>) {
11428 let buffer = self.buffer.read(cx);
11429 if buffer.is_singleton() {
11430 cx.propagate();
11431 return;
11432 }
11433
11434 let Some(workspace) = self.workspace() else {
11435 cx.propagate();
11436 return;
11437 };
11438
11439 let mut new_selections_by_buffer = HashMap::default();
11440 for selection in self.selections.all::<usize>(cx) {
11441 for (buffer, mut range, _) in
11442 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
11443 {
11444 if selection.reversed {
11445 mem::swap(&mut range.start, &mut range.end);
11446 }
11447 new_selections_by_buffer
11448 .entry(buffer)
11449 .or_insert(Vec::new())
11450 .push(range)
11451 }
11452 }
11453
11454 // We defer the pane interaction because we ourselves are a workspace item
11455 // and activating a new item causes the pane to call a method on us reentrantly,
11456 // which panics if we're on the stack.
11457 cx.window_context().defer(move |cx| {
11458 workspace.update(cx, |workspace, cx| {
11459 let pane = if split {
11460 workspace.adjacent_pane(cx)
11461 } else {
11462 workspace.active_pane().clone()
11463 };
11464
11465 for (buffer, ranges) in new_selections_by_buffer {
11466 let editor =
11467 workspace.open_project_item::<Self>(pane.clone(), buffer, true, true, cx);
11468 editor.update(cx, |editor, cx| {
11469 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
11470 s.select_ranges(ranges);
11471 });
11472 });
11473 }
11474 })
11475 });
11476 }
11477
11478 fn jump(
11479 &mut self,
11480 path: ProjectPath,
11481 position: Point,
11482 anchor: language::Anchor,
11483 offset_from_top: u32,
11484 cx: &mut ViewContext<Self>,
11485 ) {
11486 let workspace = self.workspace();
11487 cx.spawn(|_, mut cx| async move {
11488 let workspace = workspace.ok_or_else(|| anyhow!("cannot jump without workspace"))?;
11489 let editor = workspace.update(&mut cx, |workspace, cx| {
11490 // Reset the preview item id before opening the new item
11491 workspace.active_pane().update(cx, |pane, cx| {
11492 pane.set_preview_item_id(None, cx);
11493 });
11494 workspace.open_path_preview(path, None, true, true, cx)
11495 })?;
11496 let editor = editor
11497 .await?
11498 .downcast::<Editor>()
11499 .ok_or_else(|| anyhow!("opened item was not an editor"))?
11500 .downgrade();
11501 editor.update(&mut cx, |editor, cx| {
11502 let buffer = editor
11503 .buffer()
11504 .read(cx)
11505 .as_singleton()
11506 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
11507 let buffer = buffer.read(cx);
11508 let cursor = if buffer.can_resolve(&anchor) {
11509 language::ToPoint::to_point(&anchor, buffer)
11510 } else {
11511 buffer.clip_point(position, Bias::Left)
11512 };
11513
11514 let nav_history = editor.nav_history.take();
11515 editor.change_selections(
11516 Some(Autoscroll::top_relative(offset_from_top as usize)),
11517 cx,
11518 |s| {
11519 s.select_ranges([cursor..cursor]);
11520 },
11521 );
11522 editor.nav_history = nav_history;
11523
11524 anyhow::Ok(())
11525 })??;
11526
11527 anyhow::Ok(())
11528 })
11529 .detach_and_log_err(cx);
11530 }
11531
11532 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
11533 let snapshot = self.buffer.read(cx).read(cx);
11534 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
11535 Some(
11536 ranges
11537 .iter()
11538 .map(move |range| {
11539 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
11540 })
11541 .collect(),
11542 )
11543 }
11544
11545 fn selection_replacement_ranges(
11546 &self,
11547 range: Range<OffsetUtf16>,
11548 cx: &AppContext,
11549 ) -> Vec<Range<OffsetUtf16>> {
11550 let selections = self.selections.all::<OffsetUtf16>(cx);
11551 let newest_selection = selections
11552 .iter()
11553 .max_by_key(|selection| selection.id)
11554 .unwrap();
11555 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
11556 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
11557 let snapshot = self.buffer.read(cx).read(cx);
11558 selections
11559 .into_iter()
11560 .map(|mut selection| {
11561 selection.start.0 =
11562 (selection.start.0 as isize).saturating_add(start_delta) as usize;
11563 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
11564 snapshot.clip_offset_utf16(selection.start, Bias::Left)
11565 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
11566 })
11567 .collect()
11568 }
11569
11570 fn report_editor_event(
11571 &self,
11572 operation: &'static str,
11573 file_extension: Option<String>,
11574 cx: &AppContext,
11575 ) {
11576 if cfg!(any(test, feature = "test-support")) {
11577 return;
11578 }
11579
11580 let Some(project) = &self.project else { return };
11581
11582 // If None, we are in a file without an extension
11583 let file = self
11584 .buffer
11585 .read(cx)
11586 .as_singleton()
11587 .and_then(|b| b.read(cx).file());
11588 let file_extension = file_extension.or(file
11589 .as_ref()
11590 .and_then(|file| Path::new(file.file_name(cx)).extension())
11591 .and_then(|e| e.to_str())
11592 .map(|a| a.to_string()));
11593
11594 let vim_mode = cx
11595 .global::<SettingsStore>()
11596 .raw_user_settings()
11597 .get("vim_mode")
11598 == Some(&serde_json::Value::Bool(true));
11599
11600 let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
11601 == language::language_settings::InlineCompletionProvider::Copilot;
11602 let copilot_enabled_for_language = self
11603 .buffer
11604 .read(cx)
11605 .settings_at(0, cx)
11606 .show_inline_completions;
11607
11608 let telemetry = project.read(cx).client().telemetry().clone();
11609 telemetry.report_editor_event(
11610 file_extension,
11611 vim_mode,
11612 operation,
11613 copilot_enabled,
11614 copilot_enabled_for_language,
11615 )
11616 }
11617
11618 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
11619 /// with each line being an array of {text, highlight} objects.
11620 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
11621 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
11622 return;
11623 };
11624
11625 #[derive(Serialize)]
11626 struct Chunk<'a> {
11627 text: String,
11628 highlight: Option<&'a str>,
11629 }
11630
11631 let snapshot = buffer.read(cx).snapshot();
11632 let range = self
11633 .selected_text_range(cx)
11634 .and_then(|selected_range| {
11635 if selected_range.is_empty() {
11636 None
11637 } else {
11638 Some(selected_range)
11639 }
11640 })
11641 .unwrap_or_else(|| 0..snapshot.len());
11642
11643 let chunks = snapshot.chunks(range, true);
11644 let mut lines = Vec::new();
11645 let mut line: VecDeque<Chunk> = VecDeque::new();
11646
11647 let Some(style) = self.style.as_ref() else {
11648 return;
11649 };
11650
11651 for chunk in chunks {
11652 let highlight = chunk
11653 .syntax_highlight_id
11654 .and_then(|id| id.name(&style.syntax));
11655 let mut chunk_lines = chunk.text.split('\n').peekable();
11656 while let Some(text) = chunk_lines.next() {
11657 let mut merged_with_last_token = false;
11658 if let Some(last_token) = line.back_mut() {
11659 if last_token.highlight == highlight {
11660 last_token.text.push_str(text);
11661 merged_with_last_token = true;
11662 }
11663 }
11664
11665 if !merged_with_last_token {
11666 line.push_back(Chunk {
11667 text: text.into(),
11668 highlight,
11669 });
11670 }
11671
11672 if chunk_lines.peek().is_some() {
11673 if line.len() > 1 && line.front().unwrap().text.is_empty() {
11674 line.pop_front();
11675 }
11676 if line.len() > 1 && line.back().unwrap().text.is_empty() {
11677 line.pop_back();
11678 }
11679
11680 lines.push(mem::take(&mut line));
11681 }
11682 }
11683 }
11684
11685 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
11686 return;
11687 };
11688 cx.write_to_clipboard(ClipboardItem::new_string(lines));
11689 }
11690
11691 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
11692 &self.inlay_hint_cache
11693 }
11694
11695 pub fn replay_insert_event(
11696 &mut self,
11697 text: &str,
11698 relative_utf16_range: Option<Range<isize>>,
11699 cx: &mut ViewContext<Self>,
11700 ) {
11701 if !self.input_enabled {
11702 cx.emit(EditorEvent::InputIgnored { text: text.into() });
11703 return;
11704 }
11705 if let Some(relative_utf16_range) = relative_utf16_range {
11706 let selections = self.selections.all::<OffsetUtf16>(cx);
11707 self.change_selections(None, cx, |s| {
11708 let new_ranges = selections.into_iter().map(|range| {
11709 let start = OffsetUtf16(
11710 range
11711 .head()
11712 .0
11713 .saturating_add_signed(relative_utf16_range.start),
11714 );
11715 let end = OffsetUtf16(
11716 range
11717 .head()
11718 .0
11719 .saturating_add_signed(relative_utf16_range.end),
11720 );
11721 start..end
11722 });
11723 s.select_ranges(new_ranges);
11724 });
11725 }
11726
11727 self.handle_input(text, cx);
11728 }
11729
11730 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
11731 let Some(project) = self.project.as_ref() else {
11732 return false;
11733 };
11734 let project = project.read(cx);
11735
11736 let mut supports = false;
11737 self.buffer().read(cx).for_each_buffer(|buffer| {
11738 if !supports {
11739 supports = project
11740 .language_servers_for_buffer(buffer.read(cx), cx)
11741 .any(
11742 |(_, server)| match server.capabilities().inlay_hint_provider {
11743 Some(lsp::OneOf::Left(enabled)) => enabled,
11744 Some(lsp::OneOf::Right(_)) => true,
11745 None => false,
11746 },
11747 )
11748 }
11749 });
11750 supports
11751 }
11752
11753 pub fn focus(&self, cx: &mut WindowContext) {
11754 cx.focus(&self.focus_handle)
11755 }
11756
11757 pub fn is_focused(&self, cx: &WindowContext) -> bool {
11758 self.focus_handle.is_focused(cx)
11759 }
11760
11761 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
11762 cx.emit(EditorEvent::Focused);
11763
11764 if let Some(descendant) = self
11765 .last_focused_descendant
11766 .take()
11767 .and_then(|descendant| descendant.upgrade())
11768 {
11769 cx.focus(&descendant);
11770 } else {
11771 if let Some(blame) = self.blame.as_ref() {
11772 blame.update(cx, GitBlame::focus)
11773 }
11774
11775 self.blink_manager.update(cx, BlinkManager::enable);
11776 self.show_cursor_names(cx);
11777 self.buffer.update(cx, |buffer, cx| {
11778 buffer.finalize_last_transaction(cx);
11779 if self.leader_peer_id.is_none() {
11780 buffer.set_active_selections(
11781 &self.selections.disjoint_anchors(),
11782 self.selections.line_mode,
11783 self.cursor_shape,
11784 cx,
11785 );
11786 }
11787 });
11788 }
11789 }
11790
11791 fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
11792 cx.emit(EditorEvent::FocusedIn)
11793 }
11794
11795 fn handle_focus_out(&mut self, event: FocusOutEvent, _cx: &mut ViewContext<Self>) {
11796 if event.blurred != self.focus_handle {
11797 self.last_focused_descendant = Some(event.blurred);
11798 }
11799 }
11800
11801 pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
11802 self.blink_manager.update(cx, BlinkManager::disable);
11803 self.buffer
11804 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
11805
11806 if let Some(blame) = self.blame.as_ref() {
11807 blame.update(cx, GitBlame::blur)
11808 }
11809 if !self.hover_state.focused(cx) {
11810 hide_hover(self, cx);
11811 }
11812
11813 self.hide_context_menu(cx);
11814 cx.emit(EditorEvent::Blurred);
11815 cx.notify();
11816 }
11817
11818 pub fn register_action<A: Action>(
11819 &mut self,
11820 listener: impl Fn(&A, &mut WindowContext) + 'static,
11821 ) -> Subscription {
11822 let id = self.next_editor_action_id.post_inc();
11823 let listener = Arc::new(listener);
11824 self.editor_actions.borrow_mut().insert(
11825 id,
11826 Box::new(move |cx| {
11827 let _view = cx.view().clone();
11828 let cx = cx.window_context();
11829 let listener = listener.clone();
11830 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
11831 let action = action.downcast_ref().unwrap();
11832 if phase == DispatchPhase::Bubble {
11833 listener(action, cx)
11834 }
11835 })
11836 }),
11837 );
11838
11839 let editor_actions = self.editor_actions.clone();
11840 Subscription::new(move || {
11841 editor_actions.borrow_mut().remove(&id);
11842 })
11843 }
11844
11845 pub fn file_header_size(&self) -> u32 {
11846 self.file_header_size
11847 }
11848
11849 pub fn revert(
11850 &mut self,
11851 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
11852 cx: &mut ViewContext<Self>,
11853 ) {
11854 self.buffer().update(cx, |multi_buffer, cx| {
11855 for (buffer_id, changes) in revert_changes {
11856 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
11857 buffer.update(cx, |buffer, cx| {
11858 buffer.edit(
11859 changes.into_iter().map(|(range, text)| {
11860 (range, text.to_string().map(Arc::<str>::from))
11861 }),
11862 None,
11863 cx,
11864 );
11865 });
11866 }
11867 }
11868 });
11869 self.change_selections(None, cx, |selections| selections.refresh());
11870 }
11871
11872 pub fn to_pixel_point(
11873 &mut self,
11874 source: multi_buffer::Anchor,
11875 editor_snapshot: &EditorSnapshot,
11876 cx: &mut ViewContext<Self>,
11877 ) -> Option<gpui::Point<Pixels>> {
11878 let source_point = source.to_display_point(editor_snapshot);
11879 self.display_to_pixel_point(source_point, editor_snapshot, cx)
11880 }
11881
11882 pub fn display_to_pixel_point(
11883 &mut self,
11884 source: DisplayPoint,
11885 editor_snapshot: &EditorSnapshot,
11886 cx: &mut ViewContext<Self>,
11887 ) -> Option<gpui::Point<Pixels>> {
11888 let line_height = self.style()?.text.line_height_in_pixels(cx.rem_size());
11889 let text_layout_details = self.text_layout_details(cx);
11890 let scroll_top = text_layout_details
11891 .scroll_anchor
11892 .scroll_position(editor_snapshot)
11893 .y;
11894
11895 if source.row().as_f32() < scroll_top.floor() {
11896 return None;
11897 }
11898 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
11899 let source_y = line_height * (source.row().as_f32() - scroll_top);
11900 Some(gpui::Point::new(source_x, source_y))
11901 }
11902
11903 fn gutter_bounds(&self) -> Option<Bounds<Pixels>> {
11904 let bounds = self.last_bounds?;
11905 Some(element::gutter_bounds(bounds, self.gutter_dimensions))
11906 }
11907}
11908
11909fn hunks_for_selections(
11910 multi_buffer_snapshot: &MultiBufferSnapshot,
11911 selections: &[Selection<Anchor>],
11912) -> Vec<DiffHunk<MultiBufferRow>> {
11913 let buffer_rows_for_selections = selections.iter().map(|selection| {
11914 let head = selection.head();
11915 let tail = selection.tail();
11916 let start = MultiBufferRow(tail.to_point(&multi_buffer_snapshot).row);
11917 let end = MultiBufferRow(head.to_point(&multi_buffer_snapshot).row);
11918 if start > end {
11919 end..start
11920 } else {
11921 start..end
11922 }
11923 });
11924
11925 hunks_for_rows(buffer_rows_for_selections, multi_buffer_snapshot)
11926}
11927
11928pub fn hunks_for_rows(
11929 rows: impl Iterator<Item = Range<MultiBufferRow>>,
11930 multi_buffer_snapshot: &MultiBufferSnapshot,
11931) -> Vec<DiffHunk<MultiBufferRow>> {
11932 let mut hunks = Vec::new();
11933 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
11934 HashMap::default();
11935 for selected_multi_buffer_rows in rows {
11936 let query_rows =
11937 selected_multi_buffer_rows.start..selected_multi_buffer_rows.end.next_row();
11938 for hunk in multi_buffer_snapshot.git_diff_hunks_in_range(query_rows.clone()) {
11939 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
11940 // when the caret is just above or just below the deleted hunk.
11941 let allow_adjacent = hunk_status(&hunk) == DiffHunkStatus::Removed;
11942 let related_to_selection = if allow_adjacent {
11943 hunk.associated_range.overlaps(&query_rows)
11944 || hunk.associated_range.start == query_rows.end
11945 || hunk.associated_range.end == query_rows.start
11946 } else {
11947 // `selected_multi_buffer_rows` are inclusive (e.g. [2..2] means 2nd row is selected)
11948 // `hunk.associated_range` is exclusive (e.g. [2..3] means 2nd row is selected)
11949 hunk.associated_range.overlaps(&selected_multi_buffer_rows)
11950 || selected_multi_buffer_rows.end == hunk.associated_range.start
11951 };
11952 if related_to_selection {
11953 if !processed_buffer_rows
11954 .entry(hunk.buffer_id)
11955 .or_default()
11956 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
11957 {
11958 continue;
11959 }
11960 hunks.push(hunk);
11961 }
11962 }
11963 }
11964
11965 hunks
11966}
11967
11968pub trait CollaborationHub {
11969 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
11970 fn user_participant_indices<'a>(
11971 &self,
11972 cx: &'a AppContext,
11973 ) -> &'a HashMap<u64, ParticipantIndex>;
11974 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
11975}
11976
11977impl CollaborationHub for Model<Project> {
11978 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
11979 self.read(cx).collaborators()
11980 }
11981
11982 fn user_participant_indices<'a>(
11983 &self,
11984 cx: &'a AppContext,
11985 ) -> &'a HashMap<u64, ParticipantIndex> {
11986 self.read(cx).user_store().read(cx).participant_indices()
11987 }
11988
11989 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
11990 let this = self.read(cx);
11991 let user_ids = this.collaborators().values().map(|c| c.user_id);
11992 this.user_store().read_with(cx, |user_store, cx| {
11993 user_store.participant_names(user_ids, cx)
11994 })
11995 }
11996}
11997
11998pub trait CompletionProvider {
11999 fn completions(
12000 &self,
12001 buffer: &Model<Buffer>,
12002 buffer_position: text::Anchor,
12003 trigger: CompletionContext,
12004 cx: &mut ViewContext<Editor>,
12005 ) -> Task<Result<Vec<Completion>>>;
12006
12007 fn resolve_completions(
12008 &self,
12009 buffer: Model<Buffer>,
12010 completion_indices: Vec<usize>,
12011 completions: Arc<RwLock<Box<[Completion]>>>,
12012 cx: &mut ViewContext<Editor>,
12013 ) -> Task<Result<bool>>;
12014
12015 fn apply_additional_edits_for_completion(
12016 &self,
12017 buffer: Model<Buffer>,
12018 completion: Completion,
12019 push_to_history: bool,
12020 cx: &mut ViewContext<Editor>,
12021 ) -> Task<Result<Option<language::Transaction>>>;
12022
12023 fn is_completion_trigger(
12024 &self,
12025 buffer: &Model<Buffer>,
12026 position: language::Anchor,
12027 text: &str,
12028 trigger_in_words: bool,
12029 cx: &mut ViewContext<Editor>,
12030 ) -> bool;
12031}
12032
12033fn snippet_completions(
12034 project: &Project,
12035 buffer: &Model<Buffer>,
12036 buffer_position: text::Anchor,
12037 cx: &mut AppContext,
12038) -> Vec<Completion> {
12039 let language = buffer.read(cx).language_at(buffer_position);
12040 let language_name = language.as_ref().map(|language| language.lsp_id());
12041 let snippet_store = project.snippets().read(cx);
12042 let snippets = snippet_store.snippets_for(language_name, cx);
12043
12044 if snippets.is_empty() {
12045 return vec![];
12046 }
12047 let snapshot = buffer.read(cx).text_snapshot();
12048 let chunks = snapshot.reversed_chunks_in_range(text::Anchor::MIN..buffer_position);
12049
12050 let mut lines = chunks.lines();
12051 let Some(line_at) = lines.next().filter(|line| !line.is_empty()) else {
12052 return vec![];
12053 };
12054
12055 let scope = language.map(|language| language.default_scope());
12056 let mut last_word = line_at
12057 .chars()
12058 .rev()
12059 .take_while(|c| char_kind(&scope, *c) == CharKind::Word)
12060 .collect::<String>();
12061 last_word = last_word.chars().rev().collect();
12062 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
12063 let to_lsp = |point: &text::Anchor| {
12064 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
12065 point_to_lsp(end)
12066 };
12067 let lsp_end = to_lsp(&buffer_position);
12068 snippets
12069 .into_iter()
12070 .filter_map(|snippet| {
12071 let matching_prefix = snippet
12072 .prefix
12073 .iter()
12074 .find(|prefix| prefix.starts_with(&last_word))?;
12075 let start = as_offset - last_word.len();
12076 let start = snapshot.anchor_before(start);
12077 let range = start..buffer_position;
12078 let lsp_start = to_lsp(&start);
12079 let lsp_range = lsp::Range {
12080 start: lsp_start,
12081 end: lsp_end,
12082 };
12083 Some(Completion {
12084 old_range: range,
12085 new_text: snippet.body.clone(),
12086 label: CodeLabel {
12087 text: matching_prefix.clone(),
12088 runs: vec![],
12089 filter_range: 0..matching_prefix.len(),
12090 },
12091 server_id: LanguageServerId(usize::MAX),
12092 documentation: snippet
12093 .description
12094 .clone()
12095 .map(|description| Documentation::SingleLine(description)),
12096 lsp_completion: lsp::CompletionItem {
12097 label: snippet.prefix.first().unwrap().clone(),
12098 kind: Some(CompletionItemKind::SNIPPET),
12099 label_details: snippet.description.as_ref().map(|description| {
12100 lsp::CompletionItemLabelDetails {
12101 detail: Some(description.clone()),
12102 description: None,
12103 }
12104 }),
12105 insert_text_format: Some(InsertTextFormat::SNIPPET),
12106 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
12107 lsp::InsertReplaceEdit {
12108 new_text: snippet.body.clone(),
12109 insert: lsp_range,
12110 replace: lsp_range,
12111 },
12112 )),
12113 filter_text: Some(snippet.body.clone()),
12114 sort_text: Some(char::MAX.to_string()),
12115 ..Default::default()
12116 },
12117 confirm: None,
12118 show_new_completions_on_confirm: false,
12119 })
12120 })
12121 .collect()
12122}
12123
12124impl CompletionProvider for Model<Project> {
12125 fn completions(
12126 &self,
12127 buffer: &Model<Buffer>,
12128 buffer_position: text::Anchor,
12129 options: CompletionContext,
12130 cx: &mut ViewContext<Editor>,
12131 ) -> Task<Result<Vec<Completion>>> {
12132 self.update(cx, |project, cx| {
12133 let snippets = snippet_completions(project, buffer, buffer_position, cx);
12134 let project_completions = project.completions(&buffer, buffer_position, options, cx);
12135 cx.background_executor().spawn(async move {
12136 let mut completions = project_completions.await?;
12137 //let snippets = snippets.into_iter().;
12138 completions.extend(snippets);
12139 Ok(completions)
12140 })
12141 })
12142 }
12143
12144 fn resolve_completions(
12145 &self,
12146 buffer: Model<Buffer>,
12147 completion_indices: Vec<usize>,
12148 completions: Arc<RwLock<Box<[Completion]>>>,
12149 cx: &mut ViewContext<Editor>,
12150 ) -> Task<Result<bool>> {
12151 self.update(cx, |project, cx| {
12152 project.resolve_completions(buffer, completion_indices, completions, cx)
12153 })
12154 }
12155
12156 fn apply_additional_edits_for_completion(
12157 &self,
12158 buffer: Model<Buffer>,
12159 completion: Completion,
12160 push_to_history: bool,
12161 cx: &mut ViewContext<Editor>,
12162 ) -> Task<Result<Option<language::Transaction>>> {
12163 self.update(cx, |project, cx| {
12164 project.apply_additional_edits_for_completion(buffer, completion, push_to_history, cx)
12165 })
12166 }
12167
12168 fn is_completion_trigger(
12169 &self,
12170 buffer: &Model<Buffer>,
12171 position: language::Anchor,
12172 text: &str,
12173 trigger_in_words: bool,
12174 cx: &mut ViewContext<Editor>,
12175 ) -> bool {
12176 if !EditorSettings::get_global(cx).show_completions_on_input {
12177 return false;
12178 }
12179
12180 let mut chars = text.chars();
12181 let char = if let Some(char) = chars.next() {
12182 char
12183 } else {
12184 return false;
12185 };
12186 if chars.next().is_some() {
12187 return false;
12188 }
12189
12190 let buffer = buffer.read(cx);
12191 let scope = buffer.snapshot().language_scope_at(position);
12192 if trigger_in_words && char_kind(&scope, char) == CharKind::Word {
12193 return true;
12194 }
12195
12196 buffer
12197 .completion_triggers()
12198 .iter()
12199 .any(|string| string == text)
12200 }
12201}
12202
12203fn inlay_hint_settings(
12204 location: Anchor,
12205 snapshot: &MultiBufferSnapshot,
12206 cx: &mut ViewContext<'_, Editor>,
12207) -> InlayHintSettings {
12208 let file = snapshot.file_at(location);
12209 let language = snapshot.language_at(location);
12210 let settings = all_language_settings(file, cx);
12211 settings
12212 .language(language.map(|l| l.name()).as_deref())
12213 .inlay_hints
12214}
12215
12216fn consume_contiguous_rows(
12217 contiguous_row_selections: &mut Vec<Selection<Point>>,
12218 selection: &Selection<Point>,
12219 display_map: &DisplaySnapshot,
12220 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
12221) -> (MultiBufferRow, MultiBufferRow) {
12222 contiguous_row_selections.push(selection.clone());
12223 let start_row = MultiBufferRow(selection.start.row);
12224 let mut end_row = ending_row(selection, display_map);
12225
12226 while let Some(next_selection) = selections.peek() {
12227 if next_selection.start.row <= end_row.0 {
12228 end_row = ending_row(next_selection, display_map);
12229 contiguous_row_selections.push(selections.next().unwrap().clone());
12230 } else {
12231 break;
12232 }
12233 }
12234 (start_row, end_row)
12235}
12236
12237fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
12238 if next_selection.end.column > 0 || next_selection.is_empty() {
12239 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
12240 } else {
12241 MultiBufferRow(next_selection.end.row)
12242 }
12243}
12244
12245impl EditorSnapshot {
12246 pub fn remote_selections_in_range<'a>(
12247 &'a self,
12248 range: &'a Range<Anchor>,
12249 collaboration_hub: &dyn CollaborationHub,
12250 cx: &'a AppContext,
12251 ) -> impl 'a + Iterator<Item = RemoteSelection> {
12252 let participant_names = collaboration_hub.user_names(cx);
12253 let participant_indices = collaboration_hub.user_participant_indices(cx);
12254 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
12255 let collaborators_by_replica_id = collaborators_by_peer_id
12256 .iter()
12257 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
12258 .collect::<HashMap<_, _>>();
12259 self.buffer_snapshot
12260 .selections_in_range(range, false)
12261 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
12262 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
12263 let participant_index = participant_indices.get(&collaborator.user_id).copied();
12264 let user_name = participant_names.get(&collaborator.user_id).cloned();
12265 Some(RemoteSelection {
12266 replica_id,
12267 selection,
12268 cursor_shape,
12269 line_mode,
12270 participant_index,
12271 peer_id: collaborator.peer_id,
12272 user_name,
12273 })
12274 })
12275 }
12276
12277 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
12278 self.display_snapshot.buffer_snapshot.language_at(position)
12279 }
12280
12281 pub fn is_focused(&self) -> bool {
12282 self.is_focused
12283 }
12284
12285 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
12286 self.placeholder_text.as_ref()
12287 }
12288
12289 pub fn scroll_position(&self) -> gpui::Point<f32> {
12290 self.scroll_anchor.scroll_position(&self.display_snapshot)
12291 }
12292
12293 fn gutter_dimensions(
12294 &self,
12295 font_id: FontId,
12296 font_size: Pixels,
12297 em_width: Pixels,
12298 max_line_number_width: Pixels,
12299 cx: &AppContext,
12300 ) -> GutterDimensions {
12301 if !self.show_gutter {
12302 return GutterDimensions::default();
12303 }
12304 let descent = cx.text_system().descent(font_id, font_size);
12305
12306 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
12307 matches!(
12308 ProjectSettings::get_global(cx).git.git_gutter,
12309 Some(GitGutterSetting::TrackedFiles)
12310 )
12311 });
12312 let gutter_settings = EditorSettings::get_global(cx).gutter;
12313 let show_line_numbers = self
12314 .show_line_numbers
12315 .unwrap_or(gutter_settings.line_numbers);
12316 let line_gutter_width = if show_line_numbers {
12317 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
12318 let min_width_for_number_on_gutter = em_width * 4.0;
12319 max_line_number_width.max(min_width_for_number_on_gutter)
12320 } else {
12321 0.0.into()
12322 };
12323
12324 let show_code_actions = self
12325 .show_code_actions
12326 .unwrap_or(gutter_settings.code_actions);
12327
12328 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
12329
12330 let git_blame_entries_width = self
12331 .render_git_blame_gutter
12332 .then_some(em_width * GIT_BLAME_GUTTER_WIDTH_CHARS);
12333
12334 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
12335 left_padding += if show_code_actions || show_runnables {
12336 em_width * 3.0
12337 } else if show_git_gutter && show_line_numbers {
12338 em_width * 2.0
12339 } else if show_git_gutter || show_line_numbers {
12340 em_width
12341 } else {
12342 px(0.)
12343 };
12344
12345 let right_padding = if gutter_settings.folds && show_line_numbers {
12346 em_width * 4.0
12347 } else if gutter_settings.folds {
12348 em_width * 3.0
12349 } else if show_line_numbers {
12350 em_width
12351 } else {
12352 px(0.)
12353 };
12354
12355 GutterDimensions {
12356 left_padding,
12357 right_padding,
12358 width: line_gutter_width + left_padding + right_padding,
12359 margin: -descent,
12360 git_blame_entries_width,
12361 }
12362 }
12363
12364 pub fn render_fold_toggle(
12365 &self,
12366 buffer_row: MultiBufferRow,
12367 row_contains_cursor: bool,
12368 editor: View<Editor>,
12369 cx: &mut WindowContext,
12370 ) -> Option<AnyElement> {
12371 let folded = self.is_line_folded(buffer_row);
12372
12373 if let Some(crease) = self
12374 .crease_snapshot
12375 .query_row(buffer_row, &self.buffer_snapshot)
12376 {
12377 let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
12378 if folded {
12379 editor.update(cx, |editor, cx| {
12380 editor.fold_at(&crate::FoldAt { buffer_row }, cx)
12381 });
12382 } else {
12383 editor.update(cx, |editor, cx| {
12384 editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
12385 });
12386 }
12387 });
12388
12389 Some((crease.render_toggle)(
12390 buffer_row,
12391 folded,
12392 toggle_callback,
12393 cx,
12394 ))
12395 } else if folded
12396 || (self.starts_indent(buffer_row) && (row_contains_cursor || self.gutter_hovered))
12397 {
12398 Some(
12399 Disclosure::new(("indent-fold-indicator", buffer_row.0), !folded)
12400 .selected(folded)
12401 .on_click(cx.listener_for(&editor, move |this, _e, cx| {
12402 if folded {
12403 this.unfold_at(&UnfoldAt { buffer_row }, cx);
12404 } else {
12405 this.fold_at(&FoldAt { buffer_row }, cx);
12406 }
12407 }))
12408 .into_any_element(),
12409 )
12410 } else {
12411 None
12412 }
12413 }
12414
12415 pub fn render_crease_trailer(
12416 &self,
12417 buffer_row: MultiBufferRow,
12418 cx: &mut WindowContext,
12419 ) -> Option<AnyElement> {
12420 let folded = self.is_line_folded(buffer_row);
12421 let crease = self
12422 .crease_snapshot
12423 .query_row(buffer_row, &self.buffer_snapshot)?;
12424 Some((crease.render_trailer)(buffer_row, folded, cx))
12425 }
12426}
12427
12428impl Deref for EditorSnapshot {
12429 type Target = DisplaySnapshot;
12430
12431 fn deref(&self) -> &Self::Target {
12432 &self.display_snapshot
12433 }
12434}
12435
12436#[derive(Clone, Debug, PartialEq, Eq)]
12437pub enum EditorEvent {
12438 InputIgnored {
12439 text: Arc<str>,
12440 },
12441 InputHandled {
12442 utf16_range_to_replace: Option<Range<isize>>,
12443 text: Arc<str>,
12444 },
12445 ExcerptsAdded {
12446 buffer: Model<Buffer>,
12447 predecessor: ExcerptId,
12448 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
12449 },
12450 ExcerptsRemoved {
12451 ids: Vec<ExcerptId>,
12452 },
12453 ExcerptsEdited {
12454 ids: Vec<ExcerptId>,
12455 },
12456 ExcerptsExpanded {
12457 ids: Vec<ExcerptId>,
12458 },
12459 BufferEdited,
12460 Edited {
12461 transaction_id: clock::Lamport,
12462 },
12463 Reparsed(BufferId),
12464 Focused,
12465 FocusedIn,
12466 Blurred,
12467 DirtyChanged,
12468 Saved,
12469 TitleChanged,
12470 DiffBaseChanged,
12471 SelectionsChanged {
12472 local: bool,
12473 },
12474 ScrollPositionChanged {
12475 local: bool,
12476 autoscroll: bool,
12477 },
12478 Closed,
12479 TransactionUndone {
12480 transaction_id: clock::Lamport,
12481 },
12482 TransactionBegun {
12483 transaction_id: clock::Lamport,
12484 },
12485}
12486
12487impl EventEmitter<EditorEvent> for Editor {}
12488
12489impl FocusableView for Editor {
12490 fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
12491 self.focus_handle.clone()
12492 }
12493}
12494
12495impl Render for Editor {
12496 fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
12497 let settings = ThemeSettings::get_global(cx);
12498
12499 let text_style = match self.mode {
12500 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
12501 color: cx.theme().colors().editor_foreground,
12502 font_family: settings.ui_font.family.clone(),
12503 font_features: settings.ui_font.features.clone(),
12504 font_fallbacks: settings.ui_font.fallbacks.clone(),
12505 font_size: rems(0.875).into(),
12506 font_weight: settings.ui_font.weight,
12507 line_height: relative(settings.buffer_line_height.value()),
12508 ..Default::default()
12509 },
12510 EditorMode::Full => TextStyle {
12511 color: cx.theme().colors().editor_foreground,
12512 font_family: settings.buffer_font.family.clone(),
12513 font_features: settings.buffer_font.features.clone(),
12514 font_fallbacks: settings.buffer_font.fallbacks.clone(),
12515 font_size: settings.buffer_font_size(cx).into(),
12516 font_weight: settings.buffer_font.weight,
12517 line_height: relative(settings.buffer_line_height.value()),
12518 ..Default::default()
12519 },
12520 };
12521
12522 let background = match self.mode {
12523 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
12524 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
12525 EditorMode::Full => cx.theme().colors().editor_background,
12526 };
12527
12528 EditorElement::new(
12529 cx.view(),
12530 EditorStyle {
12531 background,
12532 local_player: cx.theme().players().local(),
12533 text: text_style,
12534 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
12535 syntax: cx.theme().syntax().clone(),
12536 status: cx.theme().status().clone(),
12537 inlay_hints_style: HighlightStyle {
12538 color: Some(cx.theme().status().hint),
12539 ..HighlightStyle::default()
12540 },
12541 suggestions_style: HighlightStyle {
12542 color: Some(cx.theme().status().predictive),
12543 ..HighlightStyle::default()
12544 },
12545 },
12546 )
12547 }
12548}
12549
12550impl ViewInputHandler for Editor {
12551 fn text_for_range(
12552 &mut self,
12553 range_utf16: Range<usize>,
12554 cx: &mut ViewContext<Self>,
12555 ) -> Option<String> {
12556 Some(
12557 self.buffer
12558 .read(cx)
12559 .read(cx)
12560 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
12561 .collect(),
12562 )
12563 }
12564
12565 fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
12566 // Prevent the IME menu from appearing when holding down an alphabetic key
12567 // while input is disabled.
12568 if !self.input_enabled {
12569 return None;
12570 }
12571
12572 let range = self.selections.newest::<OffsetUtf16>(cx).range();
12573 Some(range.start.0..range.end.0)
12574 }
12575
12576 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
12577 let snapshot = self.buffer.read(cx).read(cx);
12578 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
12579 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
12580 }
12581
12582 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
12583 self.clear_highlights::<InputComposition>(cx);
12584 self.ime_transaction.take();
12585 }
12586
12587 fn replace_text_in_range(
12588 &mut self,
12589 range_utf16: Option<Range<usize>>,
12590 text: &str,
12591 cx: &mut ViewContext<Self>,
12592 ) {
12593 if !self.input_enabled {
12594 cx.emit(EditorEvent::InputIgnored { text: text.into() });
12595 return;
12596 }
12597
12598 self.transact(cx, |this, cx| {
12599 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
12600 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
12601 Some(this.selection_replacement_ranges(range_utf16, cx))
12602 } else {
12603 this.marked_text_ranges(cx)
12604 };
12605
12606 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
12607 let newest_selection_id = this.selections.newest_anchor().id;
12608 this.selections
12609 .all::<OffsetUtf16>(cx)
12610 .iter()
12611 .zip(ranges_to_replace.iter())
12612 .find_map(|(selection, range)| {
12613 if selection.id == newest_selection_id {
12614 Some(
12615 (range.start.0 as isize - selection.head().0 as isize)
12616 ..(range.end.0 as isize - selection.head().0 as isize),
12617 )
12618 } else {
12619 None
12620 }
12621 })
12622 });
12623
12624 cx.emit(EditorEvent::InputHandled {
12625 utf16_range_to_replace: range_to_replace,
12626 text: text.into(),
12627 });
12628
12629 if let Some(new_selected_ranges) = new_selected_ranges {
12630 this.change_selections(None, cx, |selections| {
12631 selections.select_ranges(new_selected_ranges)
12632 });
12633 this.backspace(&Default::default(), cx);
12634 }
12635
12636 this.handle_input(text, cx);
12637 });
12638
12639 if let Some(transaction) = self.ime_transaction {
12640 self.buffer.update(cx, |buffer, cx| {
12641 buffer.group_until_transaction(transaction, cx);
12642 });
12643 }
12644
12645 self.unmark_text(cx);
12646 }
12647
12648 fn replace_and_mark_text_in_range(
12649 &mut self,
12650 range_utf16: Option<Range<usize>>,
12651 text: &str,
12652 new_selected_range_utf16: Option<Range<usize>>,
12653 cx: &mut ViewContext<Self>,
12654 ) {
12655 if !self.input_enabled {
12656 return;
12657 }
12658
12659 let transaction = self.transact(cx, |this, cx| {
12660 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
12661 let snapshot = this.buffer.read(cx).read(cx);
12662 if let Some(relative_range_utf16) = range_utf16.as_ref() {
12663 for marked_range in &mut marked_ranges {
12664 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
12665 marked_range.start.0 += relative_range_utf16.start;
12666 marked_range.start =
12667 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
12668 marked_range.end =
12669 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
12670 }
12671 }
12672 Some(marked_ranges)
12673 } else if let Some(range_utf16) = range_utf16 {
12674 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
12675 Some(this.selection_replacement_ranges(range_utf16, cx))
12676 } else {
12677 None
12678 };
12679
12680 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
12681 let newest_selection_id = this.selections.newest_anchor().id;
12682 this.selections
12683 .all::<OffsetUtf16>(cx)
12684 .iter()
12685 .zip(ranges_to_replace.iter())
12686 .find_map(|(selection, range)| {
12687 if selection.id == newest_selection_id {
12688 Some(
12689 (range.start.0 as isize - selection.head().0 as isize)
12690 ..(range.end.0 as isize - selection.head().0 as isize),
12691 )
12692 } else {
12693 None
12694 }
12695 })
12696 });
12697
12698 cx.emit(EditorEvent::InputHandled {
12699 utf16_range_to_replace: range_to_replace,
12700 text: text.into(),
12701 });
12702
12703 if let Some(ranges) = ranges_to_replace {
12704 this.change_selections(None, cx, |s| s.select_ranges(ranges));
12705 }
12706
12707 let marked_ranges = {
12708 let snapshot = this.buffer.read(cx).read(cx);
12709 this.selections
12710 .disjoint_anchors()
12711 .iter()
12712 .map(|selection| {
12713 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
12714 })
12715 .collect::<Vec<_>>()
12716 };
12717
12718 if text.is_empty() {
12719 this.unmark_text(cx);
12720 } else {
12721 this.highlight_text::<InputComposition>(
12722 marked_ranges.clone(),
12723 HighlightStyle {
12724 underline: Some(UnderlineStyle {
12725 thickness: px(1.),
12726 color: None,
12727 wavy: false,
12728 }),
12729 ..Default::default()
12730 },
12731 cx,
12732 );
12733 }
12734
12735 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
12736 let use_autoclose = this.use_autoclose;
12737 let use_auto_surround = this.use_auto_surround;
12738 this.set_use_autoclose(false);
12739 this.set_use_auto_surround(false);
12740 this.handle_input(text, cx);
12741 this.set_use_autoclose(use_autoclose);
12742 this.set_use_auto_surround(use_auto_surround);
12743
12744 if let Some(new_selected_range) = new_selected_range_utf16 {
12745 let snapshot = this.buffer.read(cx).read(cx);
12746 let new_selected_ranges = marked_ranges
12747 .into_iter()
12748 .map(|marked_range| {
12749 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
12750 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
12751 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
12752 snapshot.clip_offset_utf16(new_start, Bias::Left)
12753 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
12754 })
12755 .collect::<Vec<_>>();
12756
12757 drop(snapshot);
12758 this.change_selections(None, cx, |selections| {
12759 selections.select_ranges(new_selected_ranges)
12760 });
12761 }
12762 });
12763
12764 self.ime_transaction = self.ime_transaction.or(transaction);
12765 if let Some(transaction) = self.ime_transaction {
12766 self.buffer.update(cx, |buffer, cx| {
12767 buffer.group_until_transaction(transaction, cx);
12768 });
12769 }
12770
12771 if self.text_highlights::<InputComposition>(cx).is_none() {
12772 self.ime_transaction.take();
12773 }
12774 }
12775
12776 fn bounds_for_range(
12777 &mut self,
12778 range_utf16: Range<usize>,
12779 element_bounds: gpui::Bounds<Pixels>,
12780 cx: &mut ViewContext<Self>,
12781 ) -> Option<gpui::Bounds<Pixels>> {
12782 let text_layout_details = self.text_layout_details(cx);
12783 let style = &text_layout_details.editor_style;
12784 let font_id = cx.text_system().resolve_font(&style.text.font());
12785 let font_size = style.text.font_size.to_pixels(cx.rem_size());
12786 let line_height = style.text.line_height_in_pixels(cx.rem_size());
12787
12788 let em_width = cx
12789 .text_system()
12790 .typographic_bounds(font_id, font_size, 'm')
12791 .unwrap()
12792 .size
12793 .width;
12794
12795 let snapshot = self.snapshot(cx);
12796 let scroll_position = snapshot.scroll_position();
12797 let scroll_left = scroll_position.x * em_width;
12798
12799 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
12800 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
12801 + self.gutter_dimensions.width;
12802 let y = line_height * (start.row().as_f32() - scroll_position.y);
12803
12804 Some(Bounds {
12805 origin: element_bounds.origin + point(x, y),
12806 size: size(em_width, line_height),
12807 })
12808 }
12809}
12810
12811trait SelectionExt {
12812 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
12813 fn spanned_rows(
12814 &self,
12815 include_end_if_at_line_start: bool,
12816 map: &DisplaySnapshot,
12817 ) -> Range<MultiBufferRow>;
12818}
12819
12820impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
12821 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
12822 let start = self
12823 .start
12824 .to_point(&map.buffer_snapshot)
12825 .to_display_point(map);
12826 let end = self
12827 .end
12828 .to_point(&map.buffer_snapshot)
12829 .to_display_point(map);
12830 if self.reversed {
12831 end..start
12832 } else {
12833 start..end
12834 }
12835 }
12836
12837 fn spanned_rows(
12838 &self,
12839 include_end_if_at_line_start: bool,
12840 map: &DisplaySnapshot,
12841 ) -> Range<MultiBufferRow> {
12842 let start = self.start.to_point(&map.buffer_snapshot);
12843 let mut end = self.end.to_point(&map.buffer_snapshot);
12844 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
12845 end.row -= 1;
12846 }
12847
12848 let buffer_start = map.prev_line_boundary(start).0;
12849 let buffer_end = map.next_line_boundary(end).0;
12850 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
12851 }
12852}
12853
12854impl<T: InvalidationRegion> InvalidationStack<T> {
12855 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
12856 where
12857 S: Clone + ToOffset,
12858 {
12859 while let Some(region) = self.last() {
12860 let all_selections_inside_invalidation_ranges =
12861 if selections.len() == region.ranges().len() {
12862 selections
12863 .iter()
12864 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
12865 .all(|(selection, invalidation_range)| {
12866 let head = selection.head().to_offset(buffer);
12867 invalidation_range.start <= head && invalidation_range.end >= head
12868 })
12869 } else {
12870 false
12871 };
12872
12873 if all_selections_inside_invalidation_ranges {
12874 break;
12875 } else {
12876 self.pop();
12877 }
12878 }
12879 }
12880}
12881
12882impl<T> Default for InvalidationStack<T> {
12883 fn default() -> Self {
12884 Self(Default::default())
12885 }
12886}
12887
12888impl<T> Deref for InvalidationStack<T> {
12889 type Target = Vec<T>;
12890
12891 fn deref(&self) -> &Self::Target {
12892 &self.0
12893 }
12894}
12895
12896impl<T> DerefMut for InvalidationStack<T> {
12897 fn deref_mut(&mut self) -> &mut Self::Target {
12898 &mut self.0
12899 }
12900}
12901
12902impl InvalidationRegion for SnippetState {
12903 fn ranges(&self) -> &[Range<Anchor>] {
12904 &self.ranges[self.active_index]
12905 }
12906}
12907
12908pub fn diagnostic_block_renderer(
12909 diagnostic: Diagnostic,
12910 max_message_rows: Option<u8>,
12911 allow_closing: bool,
12912 _is_valid: bool,
12913) -> RenderBlock {
12914 let (text_without_backticks, code_ranges) =
12915 highlight_diagnostic_message(&diagnostic, max_message_rows);
12916
12917 Box::new(move |cx: &mut BlockContext| {
12918 let group_id: SharedString = cx.block_id.to_string().into();
12919
12920 let mut text_style = cx.text_style().clone();
12921 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
12922 let theme_settings = ThemeSettings::get_global(cx);
12923 text_style.font_family = theme_settings.buffer_font.family.clone();
12924 text_style.font_style = theme_settings.buffer_font.style;
12925 text_style.font_features = theme_settings.buffer_font.features.clone();
12926 text_style.font_weight = theme_settings.buffer_font.weight;
12927
12928 let multi_line_diagnostic = diagnostic.message.contains('\n');
12929
12930 let buttons = |diagnostic: &Diagnostic, block_id: BlockId| {
12931 if multi_line_diagnostic {
12932 v_flex()
12933 } else {
12934 h_flex()
12935 }
12936 .when(allow_closing, |div| {
12937 div.children(diagnostic.is_primary.then(|| {
12938 IconButton::new(("close-block", EntityId::from(block_id)), IconName::XCircle)
12939 .icon_color(Color::Muted)
12940 .size(ButtonSize::Compact)
12941 .style(ButtonStyle::Transparent)
12942 .visible_on_hover(group_id.clone())
12943 .on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
12944 .tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
12945 }))
12946 })
12947 .child(
12948 IconButton::new(("copy-block", EntityId::from(block_id)), IconName::Copy)
12949 .icon_color(Color::Muted)
12950 .size(ButtonSize::Compact)
12951 .style(ButtonStyle::Transparent)
12952 .visible_on_hover(group_id.clone())
12953 .on_click({
12954 let message = diagnostic.message.clone();
12955 move |_click, cx| {
12956 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
12957 }
12958 })
12959 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
12960 )
12961 };
12962
12963 let icon_size = buttons(&diagnostic, cx.block_id)
12964 .into_any_element()
12965 .layout_as_root(AvailableSpace::min_size(), cx);
12966
12967 h_flex()
12968 .id(cx.block_id)
12969 .group(group_id.clone())
12970 .relative()
12971 .size_full()
12972 .pl(cx.gutter_dimensions.width)
12973 .w(cx.max_width + cx.gutter_dimensions.width)
12974 .child(
12975 div()
12976 .flex()
12977 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
12978 .flex_shrink(),
12979 )
12980 .child(buttons(&diagnostic, cx.block_id))
12981 .child(div().flex().flex_shrink_0().child(
12982 StyledText::new(text_without_backticks.clone()).with_highlights(
12983 &text_style,
12984 code_ranges.iter().map(|range| {
12985 (
12986 range.clone(),
12987 HighlightStyle {
12988 font_weight: Some(FontWeight::BOLD),
12989 ..Default::default()
12990 },
12991 )
12992 }),
12993 ),
12994 ))
12995 .into_any_element()
12996 })
12997}
12998
12999pub fn highlight_diagnostic_message(
13000 diagnostic: &Diagnostic,
13001 mut max_message_rows: Option<u8>,
13002) -> (SharedString, Vec<Range<usize>>) {
13003 let mut text_without_backticks = String::new();
13004 let mut code_ranges = Vec::new();
13005
13006 if let Some(source) = &diagnostic.source {
13007 text_without_backticks.push_str(&source);
13008 code_ranges.push(0..source.len());
13009 text_without_backticks.push_str(": ");
13010 }
13011
13012 let mut prev_offset = 0;
13013 let mut in_code_block = false;
13014 let has_row_limit = max_message_rows.is_some();
13015 let mut newline_indices = diagnostic
13016 .message
13017 .match_indices('\n')
13018 .filter(|_| has_row_limit)
13019 .map(|(ix, _)| ix)
13020 .fuse()
13021 .peekable();
13022
13023 for (quote_ix, _) in diagnostic
13024 .message
13025 .match_indices('`')
13026 .chain([(diagnostic.message.len(), "")])
13027 {
13028 let mut first_newline_ix = None;
13029 let mut last_newline_ix = None;
13030 while let Some(newline_ix) = newline_indices.peek() {
13031 if *newline_ix < quote_ix {
13032 if first_newline_ix.is_none() {
13033 first_newline_ix = Some(*newline_ix);
13034 }
13035 last_newline_ix = Some(*newline_ix);
13036
13037 if let Some(rows_left) = &mut max_message_rows {
13038 if *rows_left == 0 {
13039 break;
13040 } else {
13041 *rows_left -= 1;
13042 }
13043 }
13044 let _ = newline_indices.next();
13045 } else {
13046 break;
13047 }
13048 }
13049 let prev_len = text_without_backticks.len();
13050 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
13051 text_without_backticks.push_str(new_text);
13052 if in_code_block {
13053 code_ranges.push(prev_len..text_without_backticks.len());
13054 }
13055 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
13056 in_code_block = !in_code_block;
13057 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
13058 text_without_backticks.push_str("...");
13059 break;
13060 }
13061 }
13062
13063 (text_without_backticks.into(), code_ranges)
13064}
13065
13066fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
13067 match severity {
13068 DiagnosticSeverity::ERROR => colors.error,
13069 DiagnosticSeverity::WARNING => colors.warning,
13070 DiagnosticSeverity::INFORMATION => colors.info,
13071 DiagnosticSeverity::HINT => colors.info,
13072 _ => colors.ignored,
13073 }
13074}
13075
13076pub fn styled_runs_for_code_label<'a>(
13077 label: &'a CodeLabel,
13078 syntax_theme: &'a theme::SyntaxTheme,
13079) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
13080 let fade_out = HighlightStyle {
13081 fade_out: Some(0.35),
13082 ..Default::default()
13083 };
13084
13085 let mut prev_end = label.filter_range.end;
13086 label
13087 .runs
13088 .iter()
13089 .enumerate()
13090 .flat_map(move |(ix, (range, highlight_id))| {
13091 let style = if let Some(style) = highlight_id.style(syntax_theme) {
13092 style
13093 } else {
13094 return Default::default();
13095 };
13096 let mut muted_style = style;
13097 muted_style.highlight(fade_out);
13098
13099 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
13100 if range.start >= label.filter_range.end {
13101 if range.start > prev_end {
13102 runs.push((prev_end..range.start, fade_out));
13103 }
13104 runs.push((range.clone(), muted_style));
13105 } else if range.end <= label.filter_range.end {
13106 runs.push((range.clone(), style));
13107 } else {
13108 runs.push((range.start..label.filter_range.end, style));
13109 runs.push((label.filter_range.end..range.end, muted_style));
13110 }
13111 prev_end = cmp::max(prev_end, range.end);
13112
13113 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
13114 runs.push((prev_end..label.text.len(), fade_out));
13115 }
13116
13117 runs
13118 })
13119}
13120
13121pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
13122 let mut prev_index = 0;
13123 let mut prev_codepoint: Option<char> = None;
13124 text.char_indices()
13125 .chain([(text.len(), '\0')])
13126 .filter_map(move |(index, codepoint)| {
13127 let prev_codepoint = prev_codepoint.replace(codepoint)?;
13128 let is_boundary = index == text.len()
13129 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
13130 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
13131 if is_boundary {
13132 let chunk = &text[prev_index..index];
13133 prev_index = index;
13134 Some(chunk)
13135 } else {
13136 None
13137 }
13138 })
13139}
13140
13141pub trait RangeToAnchorExt: Sized {
13142 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
13143
13144 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
13145 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
13146 anchor_range.start.to_display_point(&snapshot)..anchor_range.end.to_display_point(&snapshot)
13147 }
13148}
13149
13150impl<T: ToOffset> RangeToAnchorExt for Range<T> {
13151 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
13152 let start_offset = self.start.to_offset(snapshot);
13153 let end_offset = self.end.to_offset(snapshot);
13154 if start_offset == end_offset {
13155 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
13156 } else {
13157 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
13158 }
13159 }
13160}
13161
13162pub trait RowExt {
13163 fn as_f32(&self) -> f32;
13164
13165 fn next_row(&self) -> Self;
13166
13167 fn previous_row(&self) -> Self;
13168
13169 fn minus(&self, other: Self) -> u32;
13170}
13171
13172impl RowExt for DisplayRow {
13173 fn as_f32(&self) -> f32 {
13174 self.0 as f32
13175 }
13176
13177 fn next_row(&self) -> Self {
13178 Self(self.0 + 1)
13179 }
13180
13181 fn previous_row(&self) -> Self {
13182 Self(self.0.saturating_sub(1))
13183 }
13184
13185 fn minus(&self, other: Self) -> u32 {
13186 self.0 - other.0
13187 }
13188}
13189
13190impl RowExt for MultiBufferRow {
13191 fn as_f32(&self) -> f32 {
13192 self.0 as f32
13193 }
13194
13195 fn next_row(&self) -> Self {
13196 Self(self.0 + 1)
13197 }
13198
13199 fn previous_row(&self) -> Self {
13200 Self(self.0.saturating_sub(1))
13201 }
13202
13203 fn minus(&self, other: Self) -> u32 {
13204 self.0 - other.0
13205 }
13206}
13207
13208trait RowRangeExt {
13209 type Row;
13210
13211 fn len(&self) -> usize;
13212
13213 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
13214}
13215
13216impl RowRangeExt for Range<MultiBufferRow> {
13217 type Row = MultiBufferRow;
13218
13219 fn len(&self) -> usize {
13220 (self.end.0 - self.start.0) as usize
13221 }
13222
13223 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
13224 (self.start.0..self.end.0).map(MultiBufferRow)
13225 }
13226}
13227
13228impl RowRangeExt for Range<DisplayRow> {
13229 type Row = DisplayRow;
13230
13231 fn len(&self) -> usize {
13232 (self.end.0 - self.start.0) as usize
13233 }
13234
13235 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
13236 (self.start.0..self.end.0).map(DisplayRow)
13237 }
13238}
13239
13240fn hunk_status(hunk: &DiffHunk<MultiBufferRow>) -> DiffHunkStatus {
13241 if hunk.diff_base_byte_range.is_empty() {
13242 DiffHunkStatus::Added
13243 } else if hunk.associated_range.is_empty() {
13244 DiffHunkStatus::Removed
13245 } else {
13246 DiffHunkStatus::Modified
13247 }
13248}