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