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 clangd_ext;
19mod code_context_menus;
20pub mod display_map;
21mod editor_settings;
22mod editor_settings_controls;
23mod element;
24mod git;
25mod highlight_matching_bracket;
26mod hover_links;
27mod hover_popover;
28mod hunk_diff;
29mod indent_guides;
30mod inlay_hint_cache;
31pub mod items;
32mod linked_editing_ranges;
33mod lsp_ext;
34mod mouse_context_menu;
35pub mod movement;
36mod persistence;
37mod proposed_changes_editor;
38mod rust_analyzer_ext;
39pub mod scroll;
40mod selections_collection;
41pub mod tasks;
42
43#[cfg(test)]
44mod editor_tests;
45#[cfg(test)]
46mod inline_completion_tests;
47mod signature_help;
48#[cfg(any(test, feature = "test-support"))]
49pub mod test;
50
51use ::git::diff::DiffHunkStatus;
52pub(crate) use actions::*;
53pub use actions::{OpenExcerpts, OpenExcerptsSplit};
54use aho_corasick::AhoCorasick;
55use anyhow::{anyhow, Context as _, Result};
56use blink_manager::BlinkManager;
57use client::{Collaborator, ParticipantIndex};
58use clock::ReplicaId;
59use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
60use convert_case::{Case, Casing};
61use display_map::*;
62pub use display_map::{DisplayPoint, FoldPlaceholder};
63pub use editor_settings::{
64 CurrentLineHighlight, EditorSettings, ScrollBeyondLastLine, SearchSettings, ShowScrollbar,
65};
66pub use editor_settings_controls::*;
67use element::LineWithInvisibles;
68pub use element::{
69 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
70};
71use futures::{future, FutureExt};
72use fuzzy::StringMatchCandidate;
73
74use code_context_menus::{
75 AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
76 CompletionsMenu, ContextMenuOrigin,
77};
78use git::blame::GitBlame;
79use gpui::{
80 div, impl_actions, point, prelude::*, px, relative, size, Action, AnyElement, AppContext,
81 AsyncWindowContext, AvailableSpace, Bounds, ClipboardEntry, ClipboardItem, Context,
82 DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusOutEvent, FocusableView, FontId,
83 FontWeight, Global, HighlightStyle, Hsla, InteractiveText, KeyContext, Model, ModelContext,
84 MouseButton, PaintQuad, ParentElement, Pixels, Render, SharedString, Size, Styled, StyledText,
85 Subscription, Task, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle,
86 UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext, WeakFocusHandle,
87 WeakView, WindowContext,
88};
89use highlight_matching_bracket::refresh_matching_bracket_highlights;
90use hover_popover::{hide_hover, HoverState};
91pub(crate) use hunk_diff::HoveredHunk;
92use hunk_diff::{diff_hunk_to_display, DiffMap, DiffMapSnapshot};
93use indent_guides::ActiveIndentGuidesState;
94use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
95pub use inline_completion::Direction;
96use inline_completion::{InlineCompletionProvider, InlineCompletionProviderHandle};
97pub use items::MAX_TAB_TITLE_LEN;
98use itertools::Itertools;
99use language::{
100 language_settings::{self, all_language_settings, language_settings, InlayHintSettings},
101 markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
102 CursorShape, Diagnostic, Documentation, IndentKind, IndentSize, Language, OffsetRangeExt,
103 Point, Selection, SelectionGoal, TransactionId,
104};
105use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
106use linked_editing_ranges::refresh_linked_ranges;
107use mouse_context_menu::MouseContextMenu;
108pub use proposed_changes_editor::{
109 ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar,
110};
111use similar::{ChangeTag, TextDiff};
112use std::iter::Peekable;
113use task::{ResolvedTask, TaskTemplate, TaskVariables};
114
115use hover_links::{find_file, HoverLink, HoveredLinkState, InlayHighlight};
116pub use lsp::CompletionContext;
117use lsp::{
118 CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity, InsertTextFormat,
119 LanguageServerId, LanguageServerName,
120};
121
122use movement::TextLayoutDetails;
123pub use multi_buffer::{
124 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
125 ToPoint,
126};
127use multi_buffer::{
128 ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow, ToOffsetUtf16,
129};
130use project::{
131 lsp_store::{FormatTarget, FormatTrigger, OpenLspBufferHandle},
132 project_settings::{GitGutterSetting, ProjectSettings},
133 CodeAction, Completion, CompletionIntent, DocumentHighlight, InlayHint, Location, LocationLink,
134 LspStore, Project, ProjectItem, ProjectTransaction, TaskSourceKind,
135};
136use rand::prelude::*;
137use rpc::{proto::*, ErrorExt};
138use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
139use selections_collection::{
140 resolve_selections, MutableSelectionsCollection, SelectionsCollection,
141};
142use serde::{Deserialize, Serialize};
143use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
144use smallvec::SmallVec;
145use snippet::Snippet;
146use std::{
147 any::TypeId,
148 borrow::Cow,
149 cell::RefCell,
150 cmp::{self, Ordering, Reverse},
151 mem,
152 num::NonZeroU32,
153 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
154 path::{Path, PathBuf},
155 rc::Rc,
156 sync::Arc,
157 time::{Duration, Instant},
158};
159pub use sum_tree::Bias;
160use sum_tree::TreeMap;
161use text::{BufferId, OffsetUtf16, Rope};
162use theme::{
163 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
164 ThemeColors, ThemeSettings,
165};
166use ui::{
167 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize,
168 PopoverMenuHandle, Tooltip,
169};
170use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
171use workspace::item::{ItemHandle, PreviewTabsSettings};
172use workspace::notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt};
173use workspace::{
174 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
175};
176use workspace::{Item as WorkspaceItem, OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
177
178use crate::hover_links::{find_url, find_url_from_range};
179use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
180
181pub const FILE_HEADER_HEIGHT: u32 = 2;
182pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
183pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u32 = 1;
184pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
185const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
186const MAX_LINE_LEN: usize = 1024;
187const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
188const MAX_SELECTION_HISTORY_LEN: usize = 1024;
189pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
190#[doc(hidden)]
191pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
192
193pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
194pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
195
196pub fn render_parsed_markdown(
197 element_id: impl Into<ElementId>,
198 parsed: &language::ParsedMarkdown,
199 editor_style: &EditorStyle,
200 workspace: Option<WeakView<Workspace>>,
201 cx: &mut WindowContext,
202) -> InteractiveText {
203 let code_span_background_color = cx
204 .theme()
205 .colors()
206 .editor_document_highlight_read_background;
207
208 let highlights = gpui::combine_highlights(
209 parsed.highlights.iter().filter_map(|(range, highlight)| {
210 let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
211 Some((range.clone(), highlight))
212 }),
213 parsed
214 .regions
215 .iter()
216 .zip(&parsed.region_ranges)
217 .filter_map(|(region, range)| {
218 if region.code {
219 Some((
220 range.clone(),
221 HighlightStyle {
222 background_color: Some(code_span_background_color),
223 ..Default::default()
224 },
225 ))
226 } else {
227 None
228 }
229 }),
230 );
231
232 let mut links = Vec::new();
233 let mut link_ranges = Vec::new();
234 for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
235 if let Some(link) = region.link.clone() {
236 links.push(link);
237 link_ranges.push(range.clone());
238 }
239 }
240
241 InteractiveText::new(
242 element_id,
243 StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
244 )
245 .on_click(link_ranges, move |clicked_range_ix, cx| {
246 match &links[clicked_range_ix] {
247 markdown::Link::Web { url } => cx.open_url(url),
248 markdown::Link::Path { path } => {
249 if let Some(workspace) = &workspace {
250 _ = workspace.update(cx, |workspace, cx| {
251 workspace.open_abs_path(path.clone(), false, cx).detach();
252 });
253 }
254 }
255 }
256 })
257}
258
259#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
260pub(crate) enum InlayId {
261 InlineCompletion(usize),
262 Hint(usize),
263}
264
265impl InlayId {
266 fn id(&self) -> usize {
267 match self {
268 Self::InlineCompletion(id) => *id,
269 Self::Hint(id) => *id,
270 }
271 }
272}
273
274enum DiffRowHighlight {}
275enum DocumentHighlightRead {}
276enum DocumentHighlightWrite {}
277enum InputComposition {}
278
279#[derive(Debug, Copy, Clone, PartialEq, Eq)]
280pub enum Navigated {
281 Yes,
282 No,
283}
284
285impl Navigated {
286 pub fn from_bool(yes: bool) -> Navigated {
287 if yes {
288 Navigated::Yes
289 } else {
290 Navigated::No
291 }
292 }
293}
294
295pub fn init_settings(cx: &mut AppContext) {
296 EditorSettings::register(cx);
297}
298
299pub fn init(cx: &mut AppContext) {
300 init_settings(cx);
301
302 workspace::register_project_item::<Editor>(cx);
303 workspace::FollowableViewRegistry::register::<Editor>(cx);
304 workspace::register_serializable_item::<Editor>(cx);
305
306 cx.observe_new_views(
307 |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
308 workspace.register_action(Editor::new_file);
309 workspace.register_action(Editor::new_file_vertical);
310 workspace.register_action(Editor::new_file_horizontal);
311 },
312 )
313 .detach();
314
315 cx.on_action(move |_: &workspace::NewFile, cx| {
316 let app_state = workspace::AppState::global(cx);
317 if let Some(app_state) = app_state.upgrade() {
318 workspace::open_new(Default::default(), app_state, cx, |workspace, cx| {
319 Editor::new_file(workspace, &Default::default(), cx)
320 })
321 .detach();
322 }
323 });
324 cx.on_action(move |_: &workspace::NewWindow, cx| {
325 let app_state = workspace::AppState::global(cx);
326 if let Some(app_state) = app_state.upgrade() {
327 workspace::open_new(Default::default(), app_state, cx, |workspace, cx| {
328 Editor::new_file(workspace, &Default::default(), cx)
329 })
330 .detach();
331 }
332 });
333 git::project_diff::init(cx);
334}
335
336pub struct SearchWithinRange;
337
338trait InvalidationRegion {
339 fn ranges(&self) -> &[Range<Anchor>];
340}
341
342#[derive(Clone, Debug, PartialEq)]
343pub enum SelectPhase {
344 Begin {
345 position: DisplayPoint,
346 add: bool,
347 click_count: usize,
348 },
349 BeginColumnar {
350 position: DisplayPoint,
351 reset: bool,
352 goal_column: u32,
353 },
354 Extend {
355 position: DisplayPoint,
356 click_count: usize,
357 },
358 Update {
359 position: DisplayPoint,
360 goal_column: u32,
361 scroll_delta: gpui::Point<f32>,
362 },
363 End,
364}
365
366#[derive(Clone, Debug)]
367pub enum SelectMode {
368 Character,
369 Word(Range<Anchor>),
370 Line(Range<Anchor>),
371 All,
372}
373
374#[derive(Copy, Clone, PartialEq, Eq, Debug)]
375pub enum EditorMode {
376 SingleLine { auto_width: bool },
377 AutoHeight { max_lines: usize },
378 Full,
379}
380
381#[derive(Copy, Clone, Debug)]
382pub enum SoftWrap {
383 /// Prefer not to wrap at all.
384 ///
385 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
386 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
387 GitDiff,
388 /// Prefer a single line generally, unless an overly long line is encountered.
389 None,
390 /// Soft wrap lines that exceed the editor width.
391 EditorWidth,
392 /// Soft wrap lines at the preferred line length.
393 Column(u32),
394 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
395 Bounded(u32),
396}
397
398#[derive(Clone)]
399pub struct EditorStyle {
400 pub background: Hsla,
401 pub local_player: PlayerColor,
402 pub text: TextStyle,
403 pub scrollbar_width: Pixels,
404 pub syntax: Arc<SyntaxTheme>,
405 pub status: StatusColors,
406 pub inlay_hints_style: HighlightStyle,
407 pub inline_completion_styles: InlineCompletionStyles,
408 pub unnecessary_code_fade: f32,
409}
410
411impl Default for EditorStyle {
412 fn default() -> Self {
413 Self {
414 background: Hsla::default(),
415 local_player: PlayerColor::default(),
416 text: TextStyle::default(),
417 scrollbar_width: Pixels::default(),
418 syntax: Default::default(),
419 // HACK: Status colors don't have a real default.
420 // We should look into removing the status colors from the editor
421 // style and retrieve them directly from the theme.
422 status: StatusColors::dark(),
423 inlay_hints_style: HighlightStyle::default(),
424 inline_completion_styles: InlineCompletionStyles {
425 insertion: HighlightStyle::default(),
426 whitespace: HighlightStyle::default(),
427 },
428 unnecessary_code_fade: Default::default(),
429 }
430 }
431}
432
433pub fn make_inlay_hints_style(cx: &WindowContext) -> HighlightStyle {
434 let show_background = language_settings::language_settings(None, None, cx)
435 .inlay_hints
436 .show_background;
437
438 HighlightStyle {
439 color: Some(cx.theme().status().hint),
440 background_color: show_background.then(|| cx.theme().status().hint_background),
441 ..HighlightStyle::default()
442 }
443}
444
445pub fn make_suggestion_styles(cx: &WindowContext) -> InlineCompletionStyles {
446 InlineCompletionStyles {
447 insertion: HighlightStyle {
448 color: Some(cx.theme().status().predictive),
449 ..HighlightStyle::default()
450 },
451 whitespace: HighlightStyle {
452 background_color: Some(cx.theme().status().created_background),
453 ..HighlightStyle::default()
454 },
455 }
456}
457
458type CompletionId = usize;
459
460enum InlineCompletion {
461 Edit(Vec<(Range<Anchor>, String)>),
462 Move(Anchor),
463}
464
465struct InlineCompletionState {
466 inlay_ids: Vec<InlayId>,
467 completion: InlineCompletion,
468 invalidation_range: Range<Anchor>,
469}
470
471enum InlineCompletionHighlight {}
472
473#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
474struct EditorActionId(usize);
475
476impl EditorActionId {
477 pub fn post_inc(&mut self) -> Self {
478 let answer = self.0;
479
480 *self = Self(answer + 1);
481
482 Self(answer)
483 }
484}
485
486// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
487// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
488
489type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
490type GutterHighlight = (fn(&AppContext) -> Hsla, Arc<[Range<Anchor>]>);
491
492#[derive(Default)]
493struct ScrollbarMarkerState {
494 scrollbar_size: Size<Pixels>,
495 dirty: bool,
496 markers: Arc<[PaintQuad]>,
497 pending_refresh: Option<Task<Result<()>>>,
498}
499
500impl ScrollbarMarkerState {
501 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
502 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
503 }
504}
505
506#[derive(Clone, Debug)]
507struct RunnableTasks {
508 templates: Vec<(TaskSourceKind, TaskTemplate)>,
509 offset: MultiBufferOffset,
510 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
511 column: u32,
512 // Values of all named captures, including those starting with '_'
513 extra_variables: HashMap<String, String>,
514 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
515 context_range: Range<BufferOffset>,
516}
517
518impl RunnableTasks {
519 fn resolve<'a>(
520 &'a self,
521 cx: &'a task::TaskContext,
522 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
523 self.templates.iter().filter_map(|(kind, template)| {
524 template
525 .resolve_task(&kind.to_id_base(), cx)
526 .map(|task| (kind.clone(), task))
527 })
528 }
529}
530
531#[derive(Clone)]
532struct ResolvedTasks {
533 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
534 position: Anchor,
535}
536#[derive(Copy, Clone, Debug)]
537struct MultiBufferOffset(usize);
538#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
539struct BufferOffset(usize);
540
541// Addons allow storing per-editor state in other crates (e.g. Vim)
542pub trait Addon: 'static {
543 fn extend_key_context(&self, _: &mut KeyContext, _: &AppContext) {}
544
545 fn to_any(&self) -> &dyn std::any::Any;
546}
547
548#[derive(Debug, Copy, Clone, PartialEq, Eq)]
549pub enum IsVimMode {
550 Yes,
551 No,
552}
553
554/// Zed's primary text input `View`, allowing users to edit a [`MultiBuffer`]
555///
556/// See the [module level documentation](self) for more information.
557pub struct Editor {
558 focus_handle: FocusHandle,
559 last_focused_descendant: Option<WeakFocusHandle>,
560 /// The text buffer being edited
561 buffer: Model<MultiBuffer>,
562 /// Map of how text in the buffer should be displayed.
563 /// Handles soft wraps, folds, fake inlay text insertions, etc.
564 pub display_map: Model<DisplayMap>,
565 pub selections: SelectionsCollection,
566 pub scroll_manager: ScrollManager,
567 /// When inline assist editors are linked, they all render cursors because
568 /// typing enters text into each of them, even the ones that aren't focused.
569 pub(crate) show_cursor_when_unfocused: bool,
570 columnar_selection_tail: Option<Anchor>,
571 add_selections_state: Option<AddSelectionsState>,
572 select_next_state: Option<SelectNextState>,
573 select_prev_state: Option<SelectNextState>,
574 selection_history: SelectionHistory,
575 autoclose_regions: Vec<AutocloseRegion>,
576 snippet_stack: InvalidationStack<SnippetState>,
577 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
578 ime_transaction: Option<TransactionId>,
579 active_diagnostics: Option<ActiveDiagnosticGroup>,
580 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
581
582 project: Option<Model<Project>>,
583 semantics_provider: Option<Rc<dyn SemanticsProvider>>,
584 completion_provider: Option<Box<dyn CompletionProvider>>,
585 collaboration_hub: Option<Box<dyn CollaborationHub>>,
586 blink_manager: Model<BlinkManager>,
587 show_cursor_names: bool,
588 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
589 pub show_local_selections: bool,
590 mode: EditorMode,
591 show_breadcrumbs: bool,
592 show_gutter: bool,
593 show_line_numbers: Option<bool>,
594 use_relative_line_numbers: Option<bool>,
595 show_git_diff_gutter: Option<bool>,
596 show_code_actions: Option<bool>,
597 show_runnables: Option<bool>,
598 show_wrap_guides: Option<bool>,
599 show_indent_guides: Option<bool>,
600 placeholder_text: Option<Arc<str>>,
601 highlight_order: usize,
602 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
603 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
604 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
605 scrollbar_marker_state: ScrollbarMarkerState,
606 active_indent_guides_state: ActiveIndentGuidesState,
607 nav_history: Option<ItemNavHistory>,
608 context_menu: RefCell<Option<CodeContextMenu>>,
609 mouse_context_menu: Option<MouseContextMenu>,
610 hunk_controls_menu_handle: PopoverMenuHandle<ui::ContextMenu>,
611 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
612 signature_help_state: SignatureHelpState,
613 auto_signature_help: Option<bool>,
614 find_all_references_task_sources: Vec<Anchor>,
615 next_completion_id: CompletionId,
616 available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
617 code_actions_task: Option<Task<Result<()>>>,
618 document_highlights_task: Option<Task<()>>,
619 linked_editing_range_task: Option<Task<Option<()>>>,
620 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
621 pending_rename: Option<RenameState>,
622 searchable: bool,
623 cursor_shape: CursorShape,
624 current_line_highlight: Option<CurrentLineHighlight>,
625 collapse_matches: bool,
626 autoindent_mode: Option<AutoindentMode>,
627 workspace: Option<(WeakView<Workspace>, Option<WorkspaceId>)>,
628 input_enabled: bool,
629 use_modal_editing: bool,
630 read_only: bool,
631 leader_peer_id: Option<PeerId>,
632 remote_id: Option<ViewId>,
633 hover_state: HoverState,
634 gutter_hovered: bool,
635 hovered_link_state: Option<HoveredLinkState>,
636 inline_completion_provider: Option<RegisteredInlineCompletionProvider>,
637 code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
638 active_inline_completion: Option<InlineCompletionState>,
639 // enable_inline_completions is a switch that Vim can use to disable
640 // inline completions based on its mode.
641 enable_inline_completions: bool,
642 show_inline_completions_override: Option<bool>,
643 inlay_hint_cache: InlayHintCache,
644 diff_map: DiffMap,
645 next_inlay_id: usize,
646 _subscriptions: Vec<Subscription>,
647 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
648 gutter_dimensions: GutterDimensions,
649 style: Option<EditorStyle>,
650 text_style_refinement: Option<TextStyleRefinement>,
651 next_editor_action_id: EditorActionId,
652 editor_actions: Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut ViewContext<Self>)>>>>,
653 use_autoclose: bool,
654 use_auto_surround: bool,
655 auto_replace_emoji_shortcode: bool,
656 show_git_blame_gutter: bool,
657 show_git_blame_inline: bool,
658 show_git_blame_inline_delay_task: Option<Task<()>>,
659 git_blame_inline_enabled: bool,
660 serialize_dirty_buffers: bool,
661 show_selection_menu: Option<bool>,
662 blame: Option<Model<GitBlame>>,
663 blame_subscription: Option<Subscription>,
664 custom_context_menu: Option<
665 Box<
666 dyn 'static
667 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
668 >,
669 >,
670 last_bounds: Option<Bounds<Pixels>>,
671 expect_bounds_change: Option<Bounds<Pixels>>,
672 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
673 tasks_update_task: Option<Task<()>>,
674 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
675 breadcrumb_header: Option<String>,
676 focused_block: Option<FocusedBlock>,
677 next_scroll_position: NextScrollCursorCenterTopBottom,
678 addons: HashMap<TypeId, Box<dyn Addon>>,
679 registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
680 toggle_fold_multiple_buffers: Task<()>,
681 _scroll_cursor_center_top_bottom_task: Task<()>,
682}
683
684#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
685enum NextScrollCursorCenterTopBottom {
686 #[default]
687 Center,
688 Top,
689 Bottom,
690}
691
692impl NextScrollCursorCenterTopBottom {
693 fn next(&self) -> Self {
694 match self {
695 Self::Center => Self::Top,
696 Self::Top => Self::Bottom,
697 Self::Bottom => Self::Center,
698 }
699 }
700}
701
702#[derive(Clone)]
703pub struct EditorSnapshot {
704 pub mode: EditorMode,
705 show_gutter: bool,
706 show_line_numbers: Option<bool>,
707 show_git_diff_gutter: Option<bool>,
708 show_code_actions: Option<bool>,
709 show_runnables: Option<bool>,
710 git_blame_gutter_max_author_length: Option<usize>,
711 pub display_snapshot: DisplaySnapshot,
712 pub placeholder_text: Option<Arc<str>>,
713 diff_map: DiffMapSnapshot,
714 is_focused: bool,
715 scroll_anchor: ScrollAnchor,
716 ongoing_scroll: OngoingScroll,
717 current_line_highlight: CurrentLineHighlight,
718 gutter_hovered: bool,
719}
720
721const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
722
723#[derive(Default, Debug, Clone, Copy)]
724pub struct GutterDimensions {
725 pub left_padding: Pixels,
726 pub right_padding: Pixels,
727 pub width: Pixels,
728 pub margin: Pixels,
729 pub git_blame_entries_width: Option<Pixels>,
730}
731
732impl GutterDimensions {
733 /// The full width of the space taken up by the gutter.
734 pub fn full_width(&self) -> Pixels {
735 self.margin + self.width
736 }
737
738 /// The width of the space reserved for the fold indicators,
739 /// use alongside 'justify_end' and `gutter_width` to
740 /// right align content with the line numbers
741 pub fn fold_area_width(&self) -> Pixels {
742 self.margin + self.right_padding
743 }
744}
745
746#[derive(Debug)]
747pub struct RemoteSelection {
748 pub replica_id: ReplicaId,
749 pub selection: Selection<Anchor>,
750 pub cursor_shape: CursorShape,
751 pub peer_id: PeerId,
752 pub line_mode: bool,
753 pub participant_index: Option<ParticipantIndex>,
754 pub user_name: Option<SharedString>,
755}
756
757#[derive(Clone, Debug)]
758struct SelectionHistoryEntry {
759 selections: Arc<[Selection<Anchor>]>,
760 select_next_state: Option<SelectNextState>,
761 select_prev_state: Option<SelectNextState>,
762 add_selections_state: Option<AddSelectionsState>,
763}
764
765enum SelectionHistoryMode {
766 Normal,
767 Undoing,
768 Redoing,
769}
770
771#[derive(Clone, PartialEq, Eq, Hash)]
772struct HoveredCursor {
773 replica_id: u16,
774 selection_id: usize,
775}
776
777impl Default for SelectionHistoryMode {
778 fn default() -> Self {
779 Self::Normal
780 }
781}
782
783#[derive(Default)]
784struct SelectionHistory {
785 #[allow(clippy::type_complexity)]
786 selections_by_transaction:
787 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
788 mode: SelectionHistoryMode,
789 undo_stack: VecDeque<SelectionHistoryEntry>,
790 redo_stack: VecDeque<SelectionHistoryEntry>,
791}
792
793impl SelectionHistory {
794 fn insert_transaction(
795 &mut self,
796 transaction_id: TransactionId,
797 selections: Arc<[Selection<Anchor>]>,
798 ) {
799 self.selections_by_transaction
800 .insert(transaction_id, (selections, None));
801 }
802
803 #[allow(clippy::type_complexity)]
804 fn transaction(
805 &self,
806 transaction_id: TransactionId,
807 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
808 self.selections_by_transaction.get(&transaction_id)
809 }
810
811 #[allow(clippy::type_complexity)]
812 fn transaction_mut(
813 &mut self,
814 transaction_id: TransactionId,
815 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
816 self.selections_by_transaction.get_mut(&transaction_id)
817 }
818
819 fn push(&mut self, entry: SelectionHistoryEntry) {
820 if !entry.selections.is_empty() {
821 match self.mode {
822 SelectionHistoryMode::Normal => {
823 self.push_undo(entry);
824 self.redo_stack.clear();
825 }
826 SelectionHistoryMode::Undoing => self.push_redo(entry),
827 SelectionHistoryMode::Redoing => self.push_undo(entry),
828 }
829 }
830 }
831
832 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
833 if self
834 .undo_stack
835 .back()
836 .map_or(true, |e| e.selections != entry.selections)
837 {
838 self.undo_stack.push_back(entry);
839 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
840 self.undo_stack.pop_front();
841 }
842 }
843 }
844
845 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
846 if self
847 .redo_stack
848 .back()
849 .map_or(true, |e| e.selections != entry.selections)
850 {
851 self.redo_stack.push_back(entry);
852 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
853 self.redo_stack.pop_front();
854 }
855 }
856 }
857}
858
859struct RowHighlight {
860 index: usize,
861 range: Range<Anchor>,
862 color: Hsla,
863 should_autoscroll: bool,
864}
865
866#[derive(Clone, Debug)]
867struct AddSelectionsState {
868 above: bool,
869 stack: Vec<usize>,
870}
871
872#[derive(Clone)]
873struct SelectNextState {
874 query: AhoCorasick,
875 wordwise: bool,
876 done: bool,
877}
878
879impl std::fmt::Debug for SelectNextState {
880 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
881 f.debug_struct(std::any::type_name::<Self>())
882 .field("wordwise", &self.wordwise)
883 .field("done", &self.done)
884 .finish()
885 }
886}
887
888#[derive(Debug)]
889struct AutocloseRegion {
890 selection_id: usize,
891 range: Range<Anchor>,
892 pair: BracketPair,
893}
894
895#[derive(Debug)]
896struct SnippetState {
897 ranges: Vec<Vec<Range<Anchor>>>,
898 active_index: usize,
899 choices: Vec<Option<Vec<String>>>,
900}
901
902#[doc(hidden)]
903pub struct RenameState {
904 pub range: Range<Anchor>,
905 pub old_name: Arc<str>,
906 pub editor: View<Editor>,
907 block_id: CustomBlockId,
908}
909
910struct InvalidationStack<T>(Vec<T>);
911
912struct RegisteredInlineCompletionProvider {
913 provider: Arc<dyn InlineCompletionProviderHandle>,
914 _subscription: Subscription,
915}
916
917#[derive(Debug)]
918struct ActiveDiagnosticGroup {
919 primary_range: Range<Anchor>,
920 primary_message: String,
921 group_id: usize,
922 blocks: HashMap<CustomBlockId, Diagnostic>,
923 is_valid: bool,
924}
925
926#[derive(Serialize, Deserialize, Clone, Debug)]
927pub struct ClipboardSelection {
928 pub len: usize,
929 pub is_entire_line: bool,
930 pub first_line_indent: u32,
931}
932
933#[derive(Debug)]
934pub(crate) struct NavigationData {
935 cursor_anchor: Anchor,
936 cursor_position: Point,
937 scroll_anchor: ScrollAnchor,
938 scroll_top_row: u32,
939}
940
941#[derive(Debug, Clone, Copy, PartialEq, Eq)]
942pub enum GotoDefinitionKind {
943 Symbol,
944 Declaration,
945 Type,
946 Implementation,
947}
948
949#[derive(Debug, Clone)]
950enum InlayHintRefreshReason {
951 Toggle(bool),
952 SettingsChange(InlayHintSettings),
953 NewLinesShown,
954 BufferEdited(HashSet<Arc<Language>>),
955 RefreshRequested,
956 ExcerptsRemoved(Vec<ExcerptId>),
957}
958
959impl InlayHintRefreshReason {
960 fn description(&self) -> &'static str {
961 match self {
962 Self::Toggle(_) => "toggle",
963 Self::SettingsChange(_) => "settings change",
964 Self::NewLinesShown => "new lines shown",
965 Self::BufferEdited(_) => "buffer edited",
966 Self::RefreshRequested => "refresh requested",
967 Self::ExcerptsRemoved(_) => "excerpts removed",
968 }
969 }
970}
971
972pub(crate) struct FocusedBlock {
973 id: BlockId,
974 focus_handle: WeakFocusHandle,
975}
976
977#[derive(Clone)]
978struct JumpData {
979 excerpt_id: ExcerptId,
980 position: Point,
981 anchor: text::Anchor,
982 path: Option<project::ProjectPath>,
983 line_offset_from_top: u32,
984}
985
986impl Editor {
987 pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
988 let buffer = cx.new_model(|cx| Buffer::local("", cx));
989 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
990 Self::new(
991 EditorMode::SingleLine { auto_width: false },
992 buffer,
993 None,
994 false,
995 cx,
996 )
997 }
998
999 pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
1000 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1001 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1002 Self::new(EditorMode::Full, buffer, None, false, cx)
1003 }
1004
1005 pub fn auto_width(cx: &mut ViewContext<Self>) -> Self {
1006 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1007 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1008 Self::new(
1009 EditorMode::SingleLine { auto_width: true },
1010 buffer,
1011 None,
1012 false,
1013 cx,
1014 )
1015 }
1016
1017 pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
1018 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1019 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1020 Self::new(
1021 EditorMode::AutoHeight { max_lines },
1022 buffer,
1023 None,
1024 false,
1025 cx,
1026 )
1027 }
1028
1029 pub fn for_buffer(
1030 buffer: Model<Buffer>,
1031 project: Option<Model<Project>>,
1032 cx: &mut ViewContext<Self>,
1033 ) -> Self {
1034 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1035 Self::new(EditorMode::Full, buffer, project, false, cx)
1036 }
1037
1038 pub fn for_multibuffer(
1039 buffer: Model<MultiBuffer>,
1040 project: Option<Model<Project>>,
1041 show_excerpt_controls: bool,
1042 cx: &mut ViewContext<Self>,
1043 ) -> Self {
1044 Self::new(EditorMode::Full, buffer, project, show_excerpt_controls, cx)
1045 }
1046
1047 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1048 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1049 let mut clone = Self::new(
1050 self.mode,
1051 self.buffer.clone(),
1052 self.project.clone(),
1053 show_excerpt_controls,
1054 cx,
1055 );
1056 self.display_map.update(cx, |display_map, cx| {
1057 let snapshot = display_map.snapshot(cx);
1058 clone.display_map.update(cx, |display_map, cx| {
1059 display_map.set_state(&snapshot, cx);
1060 });
1061 });
1062 clone.selections.clone_state(&self.selections);
1063 clone.scroll_manager.clone_state(&self.scroll_manager);
1064 clone.searchable = self.searchable;
1065 clone
1066 }
1067
1068 pub fn new(
1069 mode: EditorMode,
1070 buffer: Model<MultiBuffer>,
1071 project: Option<Model<Project>>,
1072 show_excerpt_controls: bool,
1073 cx: &mut ViewContext<Self>,
1074 ) -> Self {
1075 let style = cx.text_style();
1076 let font_size = style.font_size.to_pixels(cx.rem_size());
1077 let editor = cx.view().downgrade();
1078 let fold_placeholder = FoldPlaceholder {
1079 constrain_width: true,
1080 render: Arc::new(move |fold_id, fold_range, cx| {
1081 let editor = editor.clone();
1082 div()
1083 .id(fold_id)
1084 .bg(cx.theme().colors().ghost_element_background)
1085 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1086 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1087 .rounded_sm()
1088 .size_full()
1089 .cursor_pointer()
1090 .child("⋯")
1091 .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
1092 .on_click(move |_, cx| {
1093 editor
1094 .update(cx, |editor, cx| {
1095 editor.unfold_ranges(
1096 &[fold_range.start..fold_range.end],
1097 true,
1098 false,
1099 cx,
1100 );
1101 cx.stop_propagation();
1102 })
1103 .ok();
1104 })
1105 .into_any()
1106 }),
1107 merge_adjacent: true,
1108 ..Default::default()
1109 };
1110 let display_map = cx.new_model(|cx| {
1111 DisplayMap::new(
1112 buffer.clone(),
1113 style.font(),
1114 font_size,
1115 None,
1116 show_excerpt_controls,
1117 FILE_HEADER_HEIGHT,
1118 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1119 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1120 fold_placeholder,
1121 cx,
1122 )
1123 });
1124
1125 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1126
1127 let blink_manager = cx.new_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1128
1129 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1130 .then(|| language_settings::SoftWrap::None);
1131
1132 let mut project_subscriptions = Vec::new();
1133 if mode == EditorMode::Full {
1134 if let Some(project) = project.as_ref() {
1135 if buffer.read(cx).is_singleton() {
1136 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1137 cx.emit(EditorEvent::TitleChanged);
1138 }));
1139 }
1140 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1141 if let project::Event::RefreshInlayHints = event {
1142 editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1143 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1144 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1145 let focus_handle = editor.focus_handle(cx);
1146 if focus_handle.is_focused(cx) {
1147 let snapshot = buffer.read(cx).snapshot();
1148 for (range, snippet) in snippet_edits {
1149 let editor_range =
1150 language::range_from_lsp(*range).to_offset(&snapshot);
1151 editor
1152 .insert_snippet(&[editor_range], snippet.clone(), cx)
1153 .ok();
1154 }
1155 }
1156 }
1157 }
1158 }));
1159 if let Some(task_inventory) = project
1160 .read(cx)
1161 .task_store()
1162 .read(cx)
1163 .task_inventory()
1164 .cloned()
1165 {
1166 project_subscriptions.push(cx.observe(&task_inventory, |editor, _, cx| {
1167 editor.tasks_update_task = Some(editor.refresh_runnables(cx));
1168 }));
1169 }
1170 }
1171 }
1172
1173 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1174
1175 let inlay_hint_settings =
1176 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1177 let focus_handle = cx.focus_handle();
1178 cx.on_focus(&focus_handle, Self::handle_focus).detach();
1179 cx.on_focus_in(&focus_handle, Self::handle_focus_in)
1180 .detach();
1181 cx.on_focus_out(&focus_handle, Self::handle_focus_out)
1182 .detach();
1183 cx.on_blur(&focus_handle, Self::handle_blur).detach();
1184
1185 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1186 Some(false)
1187 } else {
1188 None
1189 };
1190
1191 let mut code_action_providers = Vec::new();
1192 if let Some(project) = project.clone() {
1193 get_unstaged_changes_for_buffers(&project, buffer.read(cx).all_buffers(), cx);
1194 code_action_providers.push(Rc::new(project) as Rc<_>);
1195 }
1196
1197 let mut this = Self {
1198 focus_handle,
1199 show_cursor_when_unfocused: false,
1200 last_focused_descendant: None,
1201 buffer: buffer.clone(),
1202 display_map: display_map.clone(),
1203 selections,
1204 scroll_manager: ScrollManager::new(cx),
1205 columnar_selection_tail: None,
1206 add_selections_state: None,
1207 select_next_state: None,
1208 select_prev_state: None,
1209 selection_history: Default::default(),
1210 autoclose_regions: Default::default(),
1211 snippet_stack: Default::default(),
1212 select_larger_syntax_node_stack: Vec::new(),
1213 ime_transaction: Default::default(),
1214 active_diagnostics: None,
1215 soft_wrap_mode_override,
1216 completion_provider: project.clone().map(|project| Box::new(project) as _),
1217 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
1218 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1219 project,
1220 blink_manager: blink_manager.clone(),
1221 show_local_selections: true,
1222 mode,
1223 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1224 show_gutter: mode == EditorMode::Full,
1225 show_line_numbers: None,
1226 use_relative_line_numbers: None,
1227 show_git_diff_gutter: None,
1228 show_code_actions: None,
1229 show_runnables: None,
1230 show_wrap_guides: None,
1231 show_indent_guides,
1232 placeholder_text: None,
1233 highlight_order: 0,
1234 highlighted_rows: HashMap::default(),
1235 background_highlights: Default::default(),
1236 gutter_highlights: TreeMap::default(),
1237 scrollbar_marker_state: ScrollbarMarkerState::default(),
1238 active_indent_guides_state: ActiveIndentGuidesState::default(),
1239 nav_history: None,
1240 context_menu: RefCell::new(None),
1241 mouse_context_menu: None,
1242 hunk_controls_menu_handle: PopoverMenuHandle::default(),
1243 completion_tasks: Default::default(),
1244 signature_help_state: SignatureHelpState::default(),
1245 auto_signature_help: None,
1246 find_all_references_task_sources: Vec::new(),
1247 next_completion_id: 0,
1248 next_inlay_id: 0,
1249 code_action_providers,
1250 available_code_actions: Default::default(),
1251 code_actions_task: Default::default(),
1252 document_highlights_task: Default::default(),
1253 linked_editing_range_task: Default::default(),
1254 pending_rename: Default::default(),
1255 searchable: true,
1256 cursor_shape: EditorSettings::get_global(cx)
1257 .cursor_shape
1258 .unwrap_or_default(),
1259 current_line_highlight: None,
1260 autoindent_mode: Some(AutoindentMode::EachLine),
1261 collapse_matches: false,
1262 workspace: None,
1263 input_enabled: true,
1264 use_modal_editing: mode == EditorMode::Full,
1265 read_only: false,
1266 use_autoclose: true,
1267 use_auto_surround: true,
1268 auto_replace_emoji_shortcode: false,
1269 leader_peer_id: None,
1270 remote_id: None,
1271 hover_state: Default::default(),
1272 hovered_link_state: Default::default(),
1273 inline_completion_provider: None,
1274 active_inline_completion: None,
1275 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1276 diff_map: DiffMap::default(),
1277 gutter_hovered: false,
1278 pixel_position_of_newest_cursor: None,
1279 last_bounds: None,
1280 expect_bounds_change: None,
1281 gutter_dimensions: GutterDimensions::default(),
1282 style: None,
1283 show_cursor_names: false,
1284 hovered_cursors: Default::default(),
1285 next_editor_action_id: EditorActionId::default(),
1286 editor_actions: Rc::default(),
1287 show_inline_completions_override: None,
1288 enable_inline_completions: true,
1289 custom_context_menu: None,
1290 show_git_blame_gutter: false,
1291 show_git_blame_inline: false,
1292 show_selection_menu: None,
1293 show_git_blame_inline_delay_task: None,
1294 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1295 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1296 .session
1297 .restore_unsaved_buffers,
1298 blame: None,
1299 blame_subscription: None,
1300 tasks: Default::default(),
1301 _subscriptions: vec![
1302 cx.observe(&buffer, Self::on_buffer_changed),
1303 cx.subscribe(&buffer, Self::on_buffer_event),
1304 cx.observe(&display_map, Self::on_display_map_changed),
1305 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1306 cx.observe_global::<SettingsStore>(Self::settings_changed),
1307 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1308 cx.observe_window_activation(|editor, cx| {
1309 let active = cx.is_window_active();
1310 editor.blink_manager.update(cx, |blink_manager, cx| {
1311 if active {
1312 blink_manager.enable(cx);
1313 } else {
1314 blink_manager.disable(cx);
1315 }
1316 });
1317 }),
1318 ],
1319 tasks_update_task: None,
1320 linked_edit_ranges: Default::default(),
1321 previous_search_ranges: None,
1322 breadcrumb_header: None,
1323 focused_block: None,
1324 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1325 addons: HashMap::default(),
1326 registered_buffers: HashMap::default(),
1327 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1328 toggle_fold_multiple_buffers: Task::ready(()),
1329 text_style_refinement: None,
1330 };
1331 this.tasks_update_task = Some(this.refresh_runnables(cx));
1332 this._subscriptions.extend(project_subscriptions);
1333
1334 this.end_selection(cx);
1335 this.scroll_manager.show_scrollbar(cx);
1336
1337 if mode == EditorMode::Full {
1338 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1339 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1340
1341 if this.git_blame_inline_enabled {
1342 this.git_blame_inline_enabled = true;
1343 this.start_git_blame_inline(false, cx);
1344 }
1345
1346 if let Some(buffer) = buffer.read(cx).as_singleton() {
1347 if let Some(project) = this.project.as_ref() {
1348 let lsp_store = project.read(cx).lsp_store();
1349 let handle = lsp_store.update(cx, |lsp_store, cx| {
1350 lsp_store.register_buffer_with_language_servers(&buffer, cx)
1351 });
1352 this.registered_buffers
1353 .insert(buffer.read(cx).remote_id(), handle);
1354 }
1355 }
1356 }
1357
1358 this.report_editor_event("open", None, cx);
1359 this
1360 }
1361
1362 pub fn mouse_menu_is_focused(&self, cx: &WindowContext) -> bool {
1363 self.mouse_context_menu
1364 .as_ref()
1365 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(cx))
1366 }
1367
1368 fn key_context(&self, cx: &ViewContext<Self>) -> KeyContext {
1369 let mut key_context = KeyContext::new_with_defaults();
1370 key_context.add("Editor");
1371 let mode = match self.mode {
1372 EditorMode::SingleLine { .. } => "single_line",
1373 EditorMode::AutoHeight { .. } => "auto_height",
1374 EditorMode::Full => "full",
1375 };
1376
1377 if EditorSettings::jupyter_enabled(cx) {
1378 key_context.add("jupyter");
1379 }
1380
1381 key_context.set("mode", mode);
1382 if self.pending_rename.is_some() {
1383 key_context.add("renaming");
1384 }
1385 match self.context_menu.borrow().as_ref() {
1386 Some(CodeContextMenu::Completions(_)) => {
1387 key_context.add("menu");
1388 key_context.add("showing_completions")
1389 }
1390 Some(CodeContextMenu::CodeActions(_)) => {
1391 key_context.add("menu");
1392 key_context.add("showing_code_actions")
1393 }
1394 None => {}
1395 }
1396
1397 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
1398 if !self.focus_handle(cx).contains_focused(cx)
1399 || (self.is_focused(cx) || self.mouse_menu_is_focused(cx))
1400 {
1401 for addon in self.addons.values() {
1402 addon.extend_key_context(&mut key_context, cx)
1403 }
1404 }
1405
1406 if let Some(extension) = self
1407 .buffer
1408 .read(cx)
1409 .as_singleton()
1410 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
1411 {
1412 key_context.set("extension", extension.to_string());
1413 }
1414
1415 if self.has_active_inline_completion() {
1416 key_context.add("copilot_suggestion");
1417 key_context.add("inline_completion");
1418 }
1419
1420 if !self
1421 .selections
1422 .disjoint
1423 .iter()
1424 .all(|selection| selection.start == selection.end)
1425 {
1426 key_context.add("selection");
1427 }
1428
1429 key_context
1430 }
1431
1432 pub fn new_file(
1433 workspace: &mut Workspace,
1434 _: &workspace::NewFile,
1435 cx: &mut ViewContext<Workspace>,
1436 ) {
1437 Self::new_in_workspace(workspace, cx).detach_and_prompt_err(
1438 "Failed to create buffer",
1439 cx,
1440 |e, _| match e.error_code() {
1441 ErrorCode::RemoteUpgradeRequired => Some(format!(
1442 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1443 e.error_tag("required").unwrap_or("the latest version")
1444 )),
1445 _ => None,
1446 },
1447 );
1448 }
1449
1450 pub fn new_in_workspace(
1451 workspace: &mut Workspace,
1452 cx: &mut ViewContext<Workspace>,
1453 ) -> Task<Result<View<Editor>>> {
1454 let project = workspace.project().clone();
1455 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1456
1457 cx.spawn(|workspace, mut cx| async move {
1458 let buffer = create.await?;
1459 workspace.update(&mut cx, |workspace, cx| {
1460 let editor =
1461 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx));
1462 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
1463 editor
1464 })
1465 })
1466 }
1467
1468 fn new_file_vertical(
1469 workspace: &mut Workspace,
1470 _: &workspace::NewFileSplitVertical,
1471 cx: &mut ViewContext<Workspace>,
1472 ) {
1473 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), cx)
1474 }
1475
1476 fn new_file_horizontal(
1477 workspace: &mut Workspace,
1478 _: &workspace::NewFileSplitHorizontal,
1479 cx: &mut ViewContext<Workspace>,
1480 ) {
1481 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), cx)
1482 }
1483
1484 fn new_file_in_direction(
1485 workspace: &mut Workspace,
1486 direction: SplitDirection,
1487 cx: &mut ViewContext<Workspace>,
1488 ) {
1489 let project = workspace.project().clone();
1490 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1491
1492 cx.spawn(|workspace, mut cx| async move {
1493 let buffer = create.await?;
1494 workspace.update(&mut cx, move |workspace, cx| {
1495 workspace.split_item(
1496 direction,
1497 Box::new(
1498 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)),
1499 ),
1500 cx,
1501 )
1502 })?;
1503 anyhow::Ok(())
1504 })
1505 .detach_and_prompt_err("Failed to create buffer", cx, |e, _| match e.error_code() {
1506 ErrorCode::RemoteUpgradeRequired => Some(format!(
1507 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1508 e.error_tag("required").unwrap_or("the latest version")
1509 )),
1510 _ => None,
1511 });
1512 }
1513
1514 pub fn leader_peer_id(&self) -> Option<PeerId> {
1515 self.leader_peer_id
1516 }
1517
1518 pub fn buffer(&self) -> &Model<MultiBuffer> {
1519 &self.buffer
1520 }
1521
1522 pub fn workspace(&self) -> Option<View<Workspace>> {
1523 self.workspace.as_ref()?.0.upgrade()
1524 }
1525
1526 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
1527 self.buffer().read(cx).title(cx)
1528 }
1529
1530 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
1531 let git_blame_gutter_max_author_length = self
1532 .render_git_blame_gutter(cx)
1533 .then(|| {
1534 if let Some(blame) = self.blame.as_ref() {
1535 let max_author_length =
1536 blame.update(cx, |blame, cx| blame.max_author_length(cx));
1537 Some(max_author_length)
1538 } else {
1539 None
1540 }
1541 })
1542 .flatten();
1543
1544 EditorSnapshot {
1545 mode: self.mode,
1546 show_gutter: self.show_gutter,
1547 show_line_numbers: self.show_line_numbers,
1548 show_git_diff_gutter: self.show_git_diff_gutter,
1549 show_code_actions: self.show_code_actions,
1550 show_runnables: self.show_runnables,
1551 git_blame_gutter_max_author_length,
1552 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1553 scroll_anchor: self.scroll_manager.anchor(),
1554 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1555 placeholder_text: self.placeholder_text.clone(),
1556 diff_map: self.diff_map.snapshot(),
1557 is_focused: self.focus_handle.is_focused(cx),
1558 current_line_highlight: self
1559 .current_line_highlight
1560 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
1561 gutter_hovered: self.gutter_hovered,
1562 }
1563 }
1564
1565 pub fn language_at<T: ToOffset>(&self, point: T, cx: &AppContext) -> Option<Arc<Language>> {
1566 self.buffer.read(cx).language_at(point, cx)
1567 }
1568
1569 pub fn file_at<T: ToOffset>(
1570 &self,
1571 point: T,
1572 cx: &AppContext,
1573 ) -> Option<Arc<dyn language::File>> {
1574 self.buffer.read(cx).read(cx).file_at(point).cloned()
1575 }
1576
1577 pub fn active_excerpt(
1578 &self,
1579 cx: &AppContext,
1580 ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
1581 self.buffer
1582 .read(cx)
1583 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1584 }
1585
1586 pub fn mode(&self) -> EditorMode {
1587 self.mode
1588 }
1589
1590 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1591 self.collaboration_hub.as_deref()
1592 }
1593
1594 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1595 self.collaboration_hub = Some(hub);
1596 }
1597
1598 pub fn set_custom_context_menu(
1599 &mut self,
1600 f: impl 'static
1601 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
1602 ) {
1603 self.custom_context_menu = Some(Box::new(f))
1604 }
1605
1606 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
1607 self.completion_provider = provider;
1608 }
1609
1610 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
1611 self.semantics_provider.clone()
1612 }
1613
1614 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
1615 self.semantics_provider = provider;
1616 }
1617
1618 pub fn set_inline_completion_provider<T>(
1619 &mut self,
1620 provider: Option<Model<T>>,
1621 cx: &mut ViewContext<Self>,
1622 ) where
1623 T: InlineCompletionProvider,
1624 {
1625 self.inline_completion_provider =
1626 provider.map(|provider| RegisteredInlineCompletionProvider {
1627 _subscription: cx.observe(&provider, |this, _, cx| {
1628 if this.focus_handle.is_focused(cx) {
1629 this.update_visible_inline_completion(cx);
1630 }
1631 }),
1632 provider: Arc::new(provider),
1633 });
1634 self.refresh_inline_completion(false, false, cx);
1635 }
1636
1637 pub fn placeholder_text(&self, _cx: &WindowContext) -> Option<&str> {
1638 self.placeholder_text.as_deref()
1639 }
1640
1641 pub fn set_placeholder_text(
1642 &mut self,
1643 placeholder_text: impl Into<Arc<str>>,
1644 cx: &mut ViewContext<Self>,
1645 ) {
1646 let placeholder_text = Some(placeholder_text.into());
1647 if self.placeholder_text != placeholder_text {
1648 self.placeholder_text = placeholder_text;
1649 cx.notify();
1650 }
1651 }
1652
1653 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
1654 self.cursor_shape = cursor_shape;
1655
1656 // Disrupt blink for immediate user feedback that the cursor shape has changed
1657 self.blink_manager.update(cx, BlinkManager::show_cursor);
1658
1659 cx.notify();
1660 }
1661
1662 pub fn set_current_line_highlight(
1663 &mut self,
1664 current_line_highlight: Option<CurrentLineHighlight>,
1665 ) {
1666 self.current_line_highlight = current_line_highlight;
1667 }
1668
1669 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1670 self.collapse_matches = collapse_matches;
1671 }
1672
1673 pub fn register_buffers_with_language_servers(&mut self, cx: &mut ViewContext<Self>) {
1674 let buffers = self.buffer.read(cx).all_buffers();
1675 let Some(lsp_store) = self.lsp_store(cx) else {
1676 return;
1677 };
1678 lsp_store.update(cx, |lsp_store, cx| {
1679 for buffer in buffers {
1680 self.registered_buffers
1681 .entry(buffer.read(cx).remote_id())
1682 .or_insert_with(|| {
1683 lsp_store.register_buffer_with_language_servers(&buffer, cx)
1684 });
1685 }
1686 })
1687 }
1688
1689 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1690 if self.collapse_matches {
1691 return range.start..range.start;
1692 }
1693 range.clone()
1694 }
1695
1696 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
1697 if self.display_map.read(cx).clip_at_line_ends != clip {
1698 self.display_map
1699 .update(cx, |map, _| map.clip_at_line_ends = clip);
1700 }
1701 }
1702
1703 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1704 self.input_enabled = input_enabled;
1705 }
1706
1707 pub fn set_inline_completions_enabled(&mut self, enabled: bool) {
1708 self.enable_inline_completions = enabled;
1709 }
1710
1711 pub fn set_autoindent(&mut self, autoindent: bool) {
1712 if autoindent {
1713 self.autoindent_mode = Some(AutoindentMode::EachLine);
1714 } else {
1715 self.autoindent_mode = None;
1716 }
1717 }
1718
1719 pub fn read_only(&self, cx: &AppContext) -> bool {
1720 self.read_only || self.buffer.read(cx).read_only()
1721 }
1722
1723 pub fn set_read_only(&mut self, read_only: bool) {
1724 self.read_only = read_only;
1725 }
1726
1727 pub fn set_use_autoclose(&mut self, autoclose: bool) {
1728 self.use_autoclose = autoclose;
1729 }
1730
1731 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
1732 self.use_auto_surround = auto_surround;
1733 }
1734
1735 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
1736 self.auto_replace_emoji_shortcode = auto_replace;
1737 }
1738
1739 pub fn toggle_inline_completions(
1740 &mut self,
1741 _: &ToggleInlineCompletions,
1742 cx: &mut ViewContext<Self>,
1743 ) {
1744 if self.show_inline_completions_override.is_some() {
1745 self.set_show_inline_completions(None, cx);
1746 } else {
1747 let cursor = self.selections.newest_anchor().head();
1748 if let Some((buffer, cursor_buffer_position)) =
1749 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
1750 {
1751 let show_inline_completions =
1752 !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx);
1753 self.set_show_inline_completions(Some(show_inline_completions), cx);
1754 }
1755 }
1756 }
1757
1758 pub fn set_show_inline_completions(
1759 &mut self,
1760 show_inline_completions: Option<bool>,
1761 cx: &mut ViewContext<Self>,
1762 ) {
1763 self.show_inline_completions_override = show_inline_completions;
1764 self.refresh_inline_completion(false, true, cx);
1765 }
1766
1767 fn should_show_inline_completions(
1768 &self,
1769 buffer: &Model<Buffer>,
1770 buffer_position: language::Anchor,
1771 cx: &AppContext,
1772 ) -> bool {
1773 if !self.snippet_stack.is_empty() {
1774 return false;
1775 }
1776
1777 if self.inline_completions_disabled_in_scope(buffer, buffer_position, cx) {
1778 return false;
1779 }
1780
1781 if let Some(provider) = self.inline_completion_provider() {
1782 if let Some(show_inline_completions) = self.show_inline_completions_override {
1783 show_inline_completions
1784 } else {
1785 self.mode == EditorMode::Full && provider.is_enabled(buffer, buffer_position, cx)
1786 }
1787 } else {
1788 false
1789 }
1790 }
1791
1792 fn inline_completions_disabled_in_scope(
1793 &self,
1794 buffer: &Model<Buffer>,
1795 buffer_position: language::Anchor,
1796 cx: &AppContext,
1797 ) -> bool {
1798 let snapshot = buffer.read(cx).snapshot();
1799 let settings = snapshot.settings_at(buffer_position, cx);
1800
1801 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
1802 return false;
1803 };
1804
1805 scope.override_name().map_or(false, |scope_name| {
1806 settings
1807 .inline_completions_disabled_in
1808 .iter()
1809 .any(|s| s == scope_name)
1810 })
1811 }
1812
1813 pub fn set_use_modal_editing(&mut self, to: bool) {
1814 self.use_modal_editing = to;
1815 }
1816
1817 pub fn use_modal_editing(&self) -> bool {
1818 self.use_modal_editing
1819 }
1820
1821 fn selections_did_change(
1822 &mut self,
1823 local: bool,
1824 old_cursor_position: &Anchor,
1825 show_completions: bool,
1826 cx: &mut ViewContext<Self>,
1827 ) {
1828 cx.invalidate_character_coordinates();
1829
1830 // Copy selections to primary selection buffer
1831 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
1832 if local {
1833 let selections = self.selections.all::<usize>(cx);
1834 let buffer_handle = self.buffer.read(cx).read(cx);
1835
1836 let mut text = String::new();
1837 for (index, selection) in selections.iter().enumerate() {
1838 let text_for_selection = buffer_handle
1839 .text_for_range(selection.start..selection.end)
1840 .collect::<String>();
1841
1842 text.push_str(&text_for_selection);
1843 if index != selections.len() - 1 {
1844 text.push('\n');
1845 }
1846 }
1847
1848 if !text.is_empty() {
1849 cx.write_to_primary(ClipboardItem::new_string(text));
1850 }
1851 }
1852
1853 if self.focus_handle.is_focused(cx) && self.leader_peer_id.is_none() {
1854 self.buffer.update(cx, |buffer, cx| {
1855 buffer.set_active_selections(
1856 &self.selections.disjoint_anchors(),
1857 self.selections.line_mode,
1858 self.cursor_shape,
1859 cx,
1860 )
1861 });
1862 }
1863 let display_map = self
1864 .display_map
1865 .update(cx, |display_map, cx| display_map.snapshot(cx));
1866 let buffer = &display_map.buffer_snapshot;
1867 self.add_selections_state = None;
1868 self.select_next_state = None;
1869 self.select_prev_state = None;
1870 self.select_larger_syntax_node_stack.clear();
1871 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
1872 self.snippet_stack
1873 .invalidate(&self.selections.disjoint_anchors(), buffer);
1874 self.take_rename(false, cx);
1875
1876 let new_cursor_position = self.selections.newest_anchor().head();
1877
1878 self.push_to_nav_history(
1879 *old_cursor_position,
1880 Some(new_cursor_position.to_point(buffer)),
1881 cx,
1882 );
1883
1884 if local {
1885 let new_cursor_position = self.selections.newest_anchor().head();
1886 let mut context_menu = self.context_menu.borrow_mut();
1887 let completion_menu = match context_menu.as_ref() {
1888 Some(CodeContextMenu::Completions(menu)) => Some(menu),
1889 _ => {
1890 *context_menu = None;
1891 None
1892 }
1893 };
1894
1895 if let Some(completion_menu) = completion_menu {
1896 let cursor_position = new_cursor_position.to_offset(buffer);
1897 let (word_range, kind) =
1898 buffer.surrounding_word(completion_menu.initial_position, true);
1899 if kind == Some(CharKind::Word)
1900 && word_range.to_inclusive().contains(&cursor_position)
1901 {
1902 let mut completion_menu = completion_menu.clone();
1903 drop(context_menu);
1904
1905 let query = Self::completion_query(buffer, cursor_position);
1906 cx.spawn(move |this, mut cx| async move {
1907 completion_menu
1908 .filter(query.as_deref(), cx.background_executor().clone())
1909 .await;
1910
1911 this.update(&mut cx, |this, cx| {
1912 let mut context_menu = this.context_menu.borrow_mut();
1913 let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
1914 else {
1915 return;
1916 };
1917
1918 if menu.id > completion_menu.id {
1919 return;
1920 }
1921
1922 *context_menu = Some(CodeContextMenu::Completions(completion_menu));
1923 drop(context_menu);
1924 cx.notify();
1925 })
1926 })
1927 .detach();
1928
1929 if show_completions {
1930 self.show_completions(&ShowCompletions { trigger: None }, cx);
1931 }
1932 } else {
1933 drop(context_menu);
1934 self.hide_context_menu(cx);
1935 }
1936 } else {
1937 drop(context_menu);
1938 }
1939
1940 hide_hover(self, cx);
1941
1942 if old_cursor_position.to_display_point(&display_map).row()
1943 != new_cursor_position.to_display_point(&display_map).row()
1944 {
1945 self.available_code_actions.take();
1946 }
1947 self.refresh_code_actions(cx);
1948 self.refresh_document_highlights(cx);
1949 refresh_matching_bracket_highlights(self, cx);
1950 self.update_visible_inline_completion(cx);
1951 linked_editing_ranges::refresh_linked_ranges(self, cx);
1952 if self.git_blame_inline_enabled {
1953 self.start_inline_blame_timer(cx);
1954 }
1955 }
1956
1957 self.blink_manager.update(cx, BlinkManager::pause_blinking);
1958 cx.emit(EditorEvent::SelectionsChanged { local });
1959
1960 if self.selections.disjoint_anchors().len() == 1 {
1961 cx.emit(SearchEvent::ActiveMatchChanged)
1962 }
1963 cx.notify();
1964 }
1965
1966 pub fn change_selections<R>(
1967 &mut self,
1968 autoscroll: Option<Autoscroll>,
1969 cx: &mut ViewContext<Self>,
1970 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
1971 ) -> R {
1972 self.change_selections_inner(autoscroll, true, cx, change)
1973 }
1974
1975 pub fn change_selections_inner<R>(
1976 &mut self,
1977 autoscroll: Option<Autoscroll>,
1978 request_completions: bool,
1979 cx: &mut ViewContext<Self>,
1980 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
1981 ) -> R {
1982 let old_cursor_position = self.selections.newest_anchor().head();
1983 self.push_to_selection_history();
1984
1985 let (changed, result) = self.selections.change_with(cx, change);
1986
1987 if changed {
1988 if let Some(autoscroll) = autoscroll {
1989 self.request_autoscroll(autoscroll, cx);
1990 }
1991 self.selections_did_change(true, &old_cursor_position, request_completions, cx);
1992
1993 if self.should_open_signature_help_automatically(
1994 &old_cursor_position,
1995 self.signature_help_state.backspace_pressed(),
1996 cx,
1997 ) {
1998 self.show_signature_help(&ShowSignatureHelp, cx);
1999 }
2000 self.signature_help_state.set_backspace_pressed(false);
2001 }
2002
2003 result
2004 }
2005
2006 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2007 where
2008 I: IntoIterator<Item = (Range<S>, T)>,
2009 S: ToOffset,
2010 T: Into<Arc<str>>,
2011 {
2012 if self.read_only(cx) {
2013 return;
2014 }
2015
2016 self.buffer
2017 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2018 }
2019
2020 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2021 where
2022 I: IntoIterator<Item = (Range<S>, T)>,
2023 S: ToOffset,
2024 T: Into<Arc<str>>,
2025 {
2026 if self.read_only(cx) {
2027 return;
2028 }
2029
2030 self.buffer.update(cx, |buffer, cx| {
2031 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2032 });
2033 }
2034
2035 pub fn edit_with_block_indent<I, S, T>(
2036 &mut self,
2037 edits: I,
2038 original_indent_columns: Vec<u32>,
2039 cx: &mut ViewContext<Self>,
2040 ) where
2041 I: IntoIterator<Item = (Range<S>, T)>,
2042 S: ToOffset,
2043 T: Into<Arc<str>>,
2044 {
2045 if self.read_only(cx) {
2046 return;
2047 }
2048
2049 self.buffer.update(cx, |buffer, cx| {
2050 buffer.edit(
2051 edits,
2052 Some(AutoindentMode::Block {
2053 original_indent_columns,
2054 }),
2055 cx,
2056 )
2057 });
2058 }
2059
2060 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
2061 self.hide_context_menu(cx);
2062
2063 match phase {
2064 SelectPhase::Begin {
2065 position,
2066 add,
2067 click_count,
2068 } => self.begin_selection(position, add, click_count, cx),
2069 SelectPhase::BeginColumnar {
2070 position,
2071 goal_column,
2072 reset,
2073 } => self.begin_columnar_selection(position, goal_column, reset, cx),
2074 SelectPhase::Extend {
2075 position,
2076 click_count,
2077 } => self.extend_selection(position, click_count, cx),
2078 SelectPhase::Update {
2079 position,
2080 goal_column,
2081 scroll_delta,
2082 } => self.update_selection(position, goal_column, scroll_delta, cx),
2083 SelectPhase::End => self.end_selection(cx),
2084 }
2085 }
2086
2087 fn extend_selection(
2088 &mut self,
2089 position: DisplayPoint,
2090 click_count: usize,
2091 cx: &mut ViewContext<Self>,
2092 ) {
2093 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2094 let tail = self.selections.newest::<usize>(cx).tail();
2095 self.begin_selection(position, false, click_count, cx);
2096
2097 let position = position.to_offset(&display_map, Bias::Left);
2098 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2099
2100 let mut pending_selection = self
2101 .selections
2102 .pending_anchor()
2103 .expect("extend_selection not called with pending selection");
2104 if position >= tail {
2105 pending_selection.start = tail_anchor;
2106 } else {
2107 pending_selection.end = tail_anchor;
2108 pending_selection.reversed = true;
2109 }
2110
2111 let mut pending_mode = self.selections.pending_mode().unwrap();
2112 match &mut pending_mode {
2113 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2114 _ => {}
2115 }
2116
2117 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
2118 s.set_pending(pending_selection, pending_mode)
2119 });
2120 }
2121
2122 fn begin_selection(
2123 &mut self,
2124 position: DisplayPoint,
2125 add: bool,
2126 click_count: usize,
2127 cx: &mut ViewContext<Self>,
2128 ) {
2129 if !self.focus_handle.is_focused(cx) {
2130 self.last_focused_descendant = None;
2131 cx.focus(&self.focus_handle);
2132 }
2133
2134 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2135 let buffer = &display_map.buffer_snapshot;
2136 let newest_selection = self.selections.newest_anchor().clone();
2137 let position = display_map.clip_point(position, Bias::Left);
2138
2139 let start;
2140 let end;
2141 let mode;
2142 let mut auto_scroll;
2143 match click_count {
2144 1 => {
2145 start = buffer.anchor_before(position.to_point(&display_map));
2146 end = start;
2147 mode = SelectMode::Character;
2148 auto_scroll = true;
2149 }
2150 2 => {
2151 let range = movement::surrounding_word(&display_map, position);
2152 start = buffer.anchor_before(range.start.to_point(&display_map));
2153 end = buffer.anchor_before(range.end.to_point(&display_map));
2154 mode = SelectMode::Word(start..end);
2155 auto_scroll = true;
2156 }
2157 3 => {
2158 let position = display_map
2159 .clip_point(position, Bias::Left)
2160 .to_point(&display_map);
2161 let line_start = display_map.prev_line_boundary(position).0;
2162 let next_line_start = buffer.clip_point(
2163 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2164 Bias::Left,
2165 );
2166 start = buffer.anchor_before(line_start);
2167 end = buffer.anchor_before(next_line_start);
2168 mode = SelectMode::Line(start..end);
2169 auto_scroll = true;
2170 }
2171 _ => {
2172 start = buffer.anchor_before(0);
2173 end = buffer.anchor_before(buffer.len());
2174 mode = SelectMode::All;
2175 auto_scroll = false;
2176 }
2177 }
2178 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2179
2180 let point_to_delete: Option<usize> = {
2181 let selected_points: Vec<Selection<Point>> =
2182 self.selections.disjoint_in_range(start..end, cx);
2183
2184 if !add || click_count > 1 {
2185 None
2186 } else if !selected_points.is_empty() {
2187 Some(selected_points[0].id)
2188 } else {
2189 let clicked_point_already_selected =
2190 self.selections.disjoint.iter().find(|selection| {
2191 selection.start.to_point(buffer) == start.to_point(buffer)
2192 || selection.end.to_point(buffer) == end.to_point(buffer)
2193 });
2194
2195 clicked_point_already_selected.map(|selection| selection.id)
2196 }
2197 };
2198
2199 let selections_count = self.selections.count();
2200
2201 self.change_selections(auto_scroll.then(Autoscroll::newest), cx, |s| {
2202 if let Some(point_to_delete) = point_to_delete {
2203 s.delete(point_to_delete);
2204
2205 if selections_count == 1 {
2206 s.set_pending_anchor_range(start..end, mode);
2207 }
2208 } else {
2209 if !add {
2210 s.clear_disjoint();
2211 } else if click_count > 1 {
2212 s.delete(newest_selection.id)
2213 }
2214
2215 s.set_pending_anchor_range(start..end, mode);
2216 }
2217 });
2218 }
2219
2220 fn begin_columnar_selection(
2221 &mut self,
2222 position: DisplayPoint,
2223 goal_column: u32,
2224 reset: bool,
2225 cx: &mut ViewContext<Self>,
2226 ) {
2227 if !self.focus_handle.is_focused(cx) {
2228 self.last_focused_descendant = None;
2229 cx.focus(&self.focus_handle);
2230 }
2231
2232 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2233
2234 if reset {
2235 let pointer_position = display_map
2236 .buffer_snapshot
2237 .anchor_before(position.to_point(&display_map));
2238
2239 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
2240 s.clear_disjoint();
2241 s.set_pending_anchor_range(
2242 pointer_position..pointer_position,
2243 SelectMode::Character,
2244 );
2245 });
2246 }
2247
2248 let tail = self.selections.newest::<Point>(cx).tail();
2249 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2250
2251 if !reset {
2252 self.select_columns(
2253 tail.to_display_point(&display_map),
2254 position,
2255 goal_column,
2256 &display_map,
2257 cx,
2258 );
2259 }
2260 }
2261
2262 fn update_selection(
2263 &mut self,
2264 position: DisplayPoint,
2265 goal_column: u32,
2266 scroll_delta: gpui::Point<f32>,
2267 cx: &mut ViewContext<Self>,
2268 ) {
2269 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2270
2271 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2272 let tail = tail.to_display_point(&display_map);
2273 self.select_columns(tail, position, goal_column, &display_map, cx);
2274 } else if let Some(mut pending) = self.selections.pending_anchor() {
2275 let buffer = self.buffer.read(cx).snapshot(cx);
2276 let head;
2277 let tail;
2278 let mode = self.selections.pending_mode().unwrap();
2279 match &mode {
2280 SelectMode::Character => {
2281 head = position.to_point(&display_map);
2282 tail = pending.tail().to_point(&buffer);
2283 }
2284 SelectMode::Word(original_range) => {
2285 let original_display_range = original_range.start.to_display_point(&display_map)
2286 ..original_range.end.to_display_point(&display_map);
2287 let original_buffer_range = original_display_range.start.to_point(&display_map)
2288 ..original_display_range.end.to_point(&display_map);
2289 if movement::is_inside_word(&display_map, position)
2290 || original_display_range.contains(&position)
2291 {
2292 let word_range = movement::surrounding_word(&display_map, position);
2293 if word_range.start < original_display_range.start {
2294 head = word_range.start.to_point(&display_map);
2295 } else {
2296 head = word_range.end.to_point(&display_map);
2297 }
2298 } else {
2299 head = position.to_point(&display_map);
2300 }
2301
2302 if head <= original_buffer_range.start {
2303 tail = original_buffer_range.end;
2304 } else {
2305 tail = original_buffer_range.start;
2306 }
2307 }
2308 SelectMode::Line(original_range) => {
2309 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2310
2311 let position = display_map
2312 .clip_point(position, Bias::Left)
2313 .to_point(&display_map);
2314 let line_start = display_map.prev_line_boundary(position).0;
2315 let next_line_start = buffer.clip_point(
2316 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2317 Bias::Left,
2318 );
2319
2320 if line_start < original_range.start {
2321 head = line_start
2322 } else {
2323 head = next_line_start
2324 }
2325
2326 if head <= original_range.start {
2327 tail = original_range.end;
2328 } else {
2329 tail = original_range.start;
2330 }
2331 }
2332 SelectMode::All => {
2333 return;
2334 }
2335 };
2336
2337 if head < tail {
2338 pending.start = buffer.anchor_before(head);
2339 pending.end = buffer.anchor_before(tail);
2340 pending.reversed = true;
2341 } else {
2342 pending.start = buffer.anchor_before(tail);
2343 pending.end = buffer.anchor_before(head);
2344 pending.reversed = false;
2345 }
2346
2347 self.change_selections(None, cx, |s| {
2348 s.set_pending(pending, mode);
2349 });
2350 } else {
2351 log::error!("update_selection dispatched with no pending selection");
2352 return;
2353 }
2354
2355 self.apply_scroll_delta(scroll_delta, cx);
2356 cx.notify();
2357 }
2358
2359 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
2360 self.columnar_selection_tail.take();
2361 if self.selections.pending_anchor().is_some() {
2362 let selections = self.selections.all::<usize>(cx);
2363 self.change_selections(None, cx, |s| {
2364 s.select(selections);
2365 s.clear_pending();
2366 });
2367 }
2368 }
2369
2370 fn select_columns(
2371 &mut self,
2372 tail: DisplayPoint,
2373 head: DisplayPoint,
2374 goal_column: u32,
2375 display_map: &DisplaySnapshot,
2376 cx: &mut ViewContext<Self>,
2377 ) {
2378 let start_row = cmp::min(tail.row(), head.row());
2379 let end_row = cmp::max(tail.row(), head.row());
2380 let start_column = cmp::min(tail.column(), goal_column);
2381 let end_column = cmp::max(tail.column(), goal_column);
2382 let reversed = start_column < tail.column();
2383
2384 let selection_ranges = (start_row.0..=end_row.0)
2385 .map(DisplayRow)
2386 .filter_map(|row| {
2387 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2388 let start = display_map
2389 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2390 .to_point(display_map);
2391 let end = display_map
2392 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2393 .to_point(display_map);
2394 if reversed {
2395 Some(end..start)
2396 } else {
2397 Some(start..end)
2398 }
2399 } else {
2400 None
2401 }
2402 })
2403 .collect::<Vec<_>>();
2404
2405 self.change_selections(None, cx, |s| {
2406 s.select_ranges(selection_ranges);
2407 });
2408 cx.notify();
2409 }
2410
2411 pub fn has_pending_nonempty_selection(&self) -> bool {
2412 let pending_nonempty_selection = match self.selections.pending_anchor() {
2413 Some(Selection { start, end, .. }) => start != end,
2414 None => false,
2415 };
2416
2417 pending_nonempty_selection
2418 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2419 }
2420
2421 pub fn has_pending_selection(&self) -> bool {
2422 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2423 }
2424
2425 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
2426 if self.clear_expanded_diff_hunks(cx) {
2427 cx.notify();
2428 return;
2429 }
2430 if self.dismiss_menus_and_popups(false, true, cx) {
2431 return;
2432 }
2433
2434 if self.mode == EditorMode::Full
2435 && self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel())
2436 {
2437 return;
2438 }
2439
2440 cx.propagate();
2441 }
2442
2443 pub fn dismiss_menus_and_popups(
2444 &mut self,
2445 keep_inline_completion: bool,
2446 should_report_inline_completion_event: bool,
2447 cx: &mut ViewContext<Self>,
2448 ) -> bool {
2449 if self.take_rename(false, cx).is_some() {
2450 return true;
2451 }
2452
2453 if hide_hover(self, cx) {
2454 return true;
2455 }
2456
2457 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2458 return true;
2459 }
2460
2461 if self.hide_context_menu(cx).is_some() {
2462 return true;
2463 }
2464
2465 if self.mouse_context_menu.take().is_some() {
2466 return true;
2467 }
2468
2469 if !keep_inline_completion
2470 && self.discard_inline_completion(should_report_inline_completion_event, cx)
2471 {
2472 return true;
2473 }
2474
2475 if self.snippet_stack.pop().is_some() {
2476 return true;
2477 }
2478
2479 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
2480 self.dismiss_diagnostics(cx);
2481 return true;
2482 }
2483
2484 false
2485 }
2486
2487 fn linked_editing_ranges_for(
2488 &self,
2489 selection: Range<text::Anchor>,
2490 cx: &AppContext,
2491 ) -> Option<HashMap<Model<Buffer>, Vec<Range<text::Anchor>>>> {
2492 if self.linked_edit_ranges.is_empty() {
2493 return None;
2494 }
2495 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2496 selection.end.buffer_id.and_then(|end_buffer_id| {
2497 if selection.start.buffer_id != Some(end_buffer_id) {
2498 return None;
2499 }
2500 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2501 let snapshot = buffer.read(cx).snapshot();
2502 self.linked_edit_ranges
2503 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2504 .map(|ranges| (ranges, snapshot, buffer))
2505 })?;
2506 use text::ToOffset as TO;
2507 // find offset from the start of current range to current cursor position
2508 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2509
2510 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2511 let start_difference = start_offset - start_byte_offset;
2512 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2513 let end_difference = end_offset - start_byte_offset;
2514 // Current range has associated linked ranges.
2515 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2516 for range in linked_ranges.iter() {
2517 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2518 let end_offset = start_offset + end_difference;
2519 let start_offset = start_offset + start_difference;
2520 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2521 continue;
2522 }
2523 if self.selections.disjoint_anchor_ranges().iter().any(|s| {
2524 if s.start.buffer_id != selection.start.buffer_id
2525 || s.end.buffer_id != selection.end.buffer_id
2526 {
2527 return false;
2528 }
2529 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
2530 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
2531 }) {
2532 continue;
2533 }
2534 let start = buffer_snapshot.anchor_after(start_offset);
2535 let end = buffer_snapshot.anchor_after(end_offset);
2536 linked_edits
2537 .entry(buffer.clone())
2538 .or_default()
2539 .push(start..end);
2540 }
2541 Some(linked_edits)
2542 }
2543
2544 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2545 let text: Arc<str> = text.into();
2546
2547 if self.read_only(cx) {
2548 return;
2549 }
2550
2551 let selections = self.selections.all_adjusted(cx);
2552 let mut bracket_inserted = false;
2553 let mut edits = Vec::new();
2554 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2555 let mut new_selections = Vec::with_capacity(selections.len());
2556 let mut new_autoclose_regions = Vec::new();
2557 let snapshot = self.buffer.read(cx).read(cx);
2558
2559 for (selection, autoclose_region) in
2560 self.selections_with_autoclose_regions(selections, &snapshot)
2561 {
2562 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2563 // Determine if the inserted text matches the opening or closing
2564 // bracket of any of this language's bracket pairs.
2565 let mut bracket_pair = None;
2566 let mut is_bracket_pair_start = false;
2567 let mut is_bracket_pair_end = false;
2568 if !text.is_empty() {
2569 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
2570 // and they are removing the character that triggered IME popup.
2571 for (pair, enabled) in scope.brackets() {
2572 if !pair.close && !pair.surround {
2573 continue;
2574 }
2575
2576 if enabled && pair.start.ends_with(text.as_ref()) {
2577 let prefix_len = pair.start.len() - text.len();
2578 let preceding_text_matches_prefix = prefix_len == 0
2579 || (selection.start.column >= (prefix_len as u32)
2580 && snapshot.contains_str_at(
2581 Point::new(
2582 selection.start.row,
2583 selection.start.column - (prefix_len as u32),
2584 ),
2585 &pair.start[..prefix_len],
2586 ));
2587 if preceding_text_matches_prefix {
2588 bracket_pair = Some(pair.clone());
2589 is_bracket_pair_start = true;
2590 break;
2591 }
2592 }
2593 if pair.end.as_str() == text.as_ref() {
2594 bracket_pair = Some(pair.clone());
2595 is_bracket_pair_end = true;
2596 break;
2597 }
2598 }
2599 }
2600
2601 if let Some(bracket_pair) = bracket_pair {
2602 let snapshot_settings = snapshot.settings_at(selection.start, cx);
2603 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
2604 let auto_surround =
2605 self.use_auto_surround && snapshot_settings.use_auto_surround;
2606 if selection.is_empty() {
2607 if is_bracket_pair_start {
2608 // If the inserted text is a suffix of an opening bracket and the
2609 // selection is preceded by the rest of the opening bracket, then
2610 // insert the closing bracket.
2611 let following_text_allows_autoclose = snapshot
2612 .chars_at(selection.start)
2613 .next()
2614 .map_or(true, |c| scope.should_autoclose_before(c));
2615
2616 let is_closing_quote = if bracket_pair.end == bracket_pair.start
2617 && bracket_pair.start.len() == 1
2618 {
2619 let target = bracket_pair.start.chars().next().unwrap();
2620 let current_line_count = snapshot
2621 .reversed_chars_at(selection.start)
2622 .take_while(|&c| c != '\n')
2623 .filter(|&c| c == target)
2624 .count();
2625 current_line_count % 2 == 1
2626 } else {
2627 false
2628 };
2629
2630 if autoclose
2631 && bracket_pair.close
2632 && following_text_allows_autoclose
2633 && !is_closing_quote
2634 {
2635 let anchor = snapshot.anchor_before(selection.end);
2636 new_selections.push((selection.map(|_| anchor), text.len()));
2637 new_autoclose_regions.push((
2638 anchor,
2639 text.len(),
2640 selection.id,
2641 bracket_pair.clone(),
2642 ));
2643 edits.push((
2644 selection.range(),
2645 format!("{}{}", text, bracket_pair.end).into(),
2646 ));
2647 bracket_inserted = true;
2648 continue;
2649 }
2650 }
2651
2652 if let Some(region) = autoclose_region {
2653 // If the selection is followed by an auto-inserted closing bracket,
2654 // then don't insert that closing bracket again; just move the selection
2655 // past the closing bracket.
2656 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2657 && text.as_ref() == region.pair.end.as_str();
2658 if should_skip {
2659 let anchor = snapshot.anchor_after(selection.end);
2660 new_selections
2661 .push((selection.map(|_| anchor), region.pair.end.len()));
2662 continue;
2663 }
2664 }
2665
2666 let always_treat_brackets_as_autoclosed = snapshot
2667 .settings_at(selection.start, cx)
2668 .always_treat_brackets_as_autoclosed;
2669 if always_treat_brackets_as_autoclosed
2670 && is_bracket_pair_end
2671 && snapshot.contains_str_at(selection.end, text.as_ref())
2672 {
2673 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
2674 // and the inserted text is a closing bracket and the selection is followed
2675 // by the closing bracket then move the selection past the closing bracket.
2676 let anchor = snapshot.anchor_after(selection.end);
2677 new_selections.push((selection.map(|_| anchor), text.len()));
2678 continue;
2679 }
2680 }
2681 // If an opening bracket is 1 character long and is typed while
2682 // text is selected, then surround that text with the bracket pair.
2683 else if auto_surround
2684 && bracket_pair.surround
2685 && is_bracket_pair_start
2686 && bracket_pair.start.chars().count() == 1
2687 {
2688 edits.push((selection.start..selection.start, text.clone()));
2689 edits.push((
2690 selection.end..selection.end,
2691 bracket_pair.end.as_str().into(),
2692 ));
2693 bracket_inserted = true;
2694 new_selections.push((
2695 Selection {
2696 id: selection.id,
2697 start: snapshot.anchor_after(selection.start),
2698 end: snapshot.anchor_before(selection.end),
2699 reversed: selection.reversed,
2700 goal: selection.goal,
2701 },
2702 0,
2703 ));
2704 continue;
2705 }
2706 }
2707 }
2708
2709 if self.auto_replace_emoji_shortcode
2710 && selection.is_empty()
2711 && text.as_ref().ends_with(':')
2712 {
2713 if let Some(possible_emoji_short_code) =
2714 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
2715 {
2716 if !possible_emoji_short_code.is_empty() {
2717 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
2718 let emoji_shortcode_start = Point::new(
2719 selection.start.row,
2720 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
2721 );
2722
2723 // Remove shortcode from buffer
2724 edits.push((
2725 emoji_shortcode_start..selection.start,
2726 "".to_string().into(),
2727 ));
2728 new_selections.push((
2729 Selection {
2730 id: selection.id,
2731 start: snapshot.anchor_after(emoji_shortcode_start),
2732 end: snapshot.anchor_before(selection.start),
2733 reversed: selection.reversed,
2734 goal: selection.goal,
2735 },
2736 0,
2737 ));
2738
2739 // Insert emoji
2740 let selection_start_anchor = snapshot.anchor_after(selection.start);
2741 new_selections.push((selection.map(|_| selection_start_anchor), 0));
2742 edits.push((selection.start..selection.end, emoji.to_string().into()));
2743
2744 continue;
2745 }
2746 }
2747 }
2748 }
2749
2750 // If not handling any auto-close operation, then just replace the selected
2751 // text with the given input and move the selection to the end of the
2752 // newly inserted text.
2753 let anchor = snapshot.anchor_after(selection.end);
2754 if !self.linked_edit_ranges.is_empty() {
2755 let start_anchor = snapshot.anchor_before(selection.start);
2756
2757 let is_word_char = text.chars().next().map_or(true, |char| {
2758 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
2759 classifier.is_word(char)
2760 });
2761
2762 if is_word_char {
2763 if let Some(ranges) = self
2764 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
2765 {
2766 for (buffer, edits) in ranges {
2767 linked_edits
2768 .entry(buffer.clone())
2769 .or_default()
2770 .extend(edits.into_iter().map(|range| (range, text.clone())));
2771 }
2772 }
2773 }
2774 }
2775
2776 new_selections.push((selection.map(|_| anchor), 0));
2777 edits.push((selection.start..selection.end, text.clone()));
2778 }
2779
2780 drop(snapshot);
2781
2782 self.transact(cx, |this, cx| {
2783 this.buffer.update(cx, |buffer, cx| {
2784 buffer.edit(edits, this.autoindent_mode.clone(), cx);
2785 });
2786 for (buffer, edits) in linked_edits {
2787 buffer.update(cx, |buffer, cx| {
2788 let snapshot = buffer.snapshot();
2789 let edits = edits
2790 .into_iter()
2791 .map(|(range, text)| {
2792 use text::ToPoint as TP;
2793 let end_point = TP::to_point(&range.end, &snapshot);
2794 let start_point = TP::to_point(&range.start, &snapshot);
2795 (start_point..end_point, text)
2796 })
2797 .sorted_by_key(|(range, _)| range.start)
2798 .collect::<Vec<_>>();
2799 buffer.edit(edits, None, cx);
2800 })
2801 }
2802 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
2803 let new_selection_deltas = new_selections.iter().map(|e| e.1);
2804 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
2805 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
2806 .zip(new_selection_deltas)
2807 .map(|(selection, delta)| Selection {
2808 id: selection.id,
2809 start: selection.start + delta,
2810 end: selection.end + delta,
2811 reversed: selection.reversed,
2812 goal: SelectionGoal::None,
2813 })
2814 .collect::<Vec<_>>();
2815
2816 let mut i = 0;
2817 for (position, delta, selection_id, pair) in new_autoclose_regions {
2818 let position = position.to_offset(&map.buffer_snapshot) + delta;
2819 let start = map.buffer_snapshot.anchor_before(position);
2820 let end = map.buffer_snapshot.anchor_after(position);
2821 while let Some(existing_state) = this.autoclose_regions.get(i) {
2822 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
2823 Ordering::Less => i += 1,
2824 Ordering::Greater => break,
2825 Ordering::Equal => {
2826 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
2827 Ordering::Less => i += 1,
2828 Ordering::Equal => break,
2829 Ordering::Greater => break,
2830 }
2831 }
2832 }
2833 }
2834 this.autoclose_regions.insert(
2835 i,
2836 AutocloseRegion {
2837 selection_id,
2838 range: start..end,
2839 pair,
2840 },
2841 );
2842 }
2843
2844 this.change_selections_inner(Some(Autoscroll::fit()), false, cx, |s| {
2845 s.select(new_selections)
2846 });
2847
2848 if !bracket_inserted {
2849 if let Some(on_type_format_task) =
2850 this.trigger_on_type_formatting(text.to_string(), cx)
2851 {
2852 on_type_format_task.detach_and_log_err(cx);
2853 }
2854 }
2855
2856 let editor_settings = EditorSettings::get_global(cx);
2857 if bracket_inserted
2858 && (editor_settings.auto_signature_help
2859 || editor_settings.show_signature_help_after_edits)
2860 {
2861 this.show_signature_help(&ShowSignatureHelp, cx);
2862 }
2863
2864 this.trigger_completion_on_input(&text, true, cx);
2865 linked_editing_ranges::refresh_linked_ranges(this, cx);
2866 this.refresh_inline_completion(true, false, cx);
2867 });
2868 }
2869
2870 fn find_possible_emoji_shortcode_at_position(
2871 snapshot: &MultiBufferSnapshot,
2872 position: Point,
2873 ) -> Option<String> {
2874 let mut chars = Vec::new();
2875 let mut found_colon = false;
2876 for char in snapshot.reversed_chars_at(position).take(100) {
2877 // Found a possible emoji shortcode in the middle of the buffer
2878 if found_colon {
2879 if char.is_whitespace() {
2880 chars.reverse();
2881 return Some(chars.iter().collect());
2882 }
2883 // If the previous character is not a whitespace, we are in the middle of a word
2884 // and we only want to complete the shortcode if the word is made up of other emojis
2885 let mut containing_word = String::new();
2886 for ch in snapshot
2887 .reversed_chars_at(position)
2888 .skip(chars.len() + 1)
2889 .take(100)
2890 {
2891 if ch.is_whitespace() {
2892 break;
2893 }
2894 containing_word.push(ch);
2895 }
2896 let containing_word = containing_word.chars().rev().collect::<String>();
2897 if util::word_consists_of_emojis(containing_word.as_str()) {
2898 chars.reverse();
2899 return Some(chars.iter().collect());
2900 }
2901 }
2902
2903 if char.is_whitespace() || !char.is_ascii() {
2904 return None;
2905 }
2906 if char == ':' {
2907 found_colon = true;
2908 } else {
2909 chars.push(char);
2910 }
2911 }
2912 // Found a possible emoji shortcode at the beginning of the buffer
2913 chars.reverse();
2914 Some(chars.iter().collect())
2915 }
2916
2917 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
2918 self.transact(cx, |this, cx| {
2919 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
2920 let selections = this.selections.all::<usize>(cx);
2921 let multi_buffer = this.buffer.read(cx);
2922 let buffer = multi_buffer.snapshot(cx);
2923 selections
2924 .iter()
2925 .map(|selection| {
2926 let start_point = selection.start.to_point(&buffer);
2927 let mut indent =
2928 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
2929 indent.len = cmp::min(indent.len, start_point.column);
2930 let start = selection.start;
2931 let end = selection.end;
2932 let selection_is_empty = start == end;
2933 let language_scope = buffer.language_scope_at(start);
2934 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
2935 &language_scope
2936 {
2937 let leading_whitespace_len = buffer
2938 .reversed_chars_at(start)
2939 .take_while(|c| c.is_whitespace() && *c != '\n')
2940 .map(|c| c.len_utf8())
2941 .sum::<usize>();
2942
2943 let trailing_whitespace_len = buffer
2944 .chars_at(end)
2945 .take_while(|c| c.is_whitespace() && *c != '\n')
2946 .map(|c| c.len_utf8())
2947 .sum::<usize>();
2948
2949 let insert_extra_newline =
2950 language.brackets().any(|(pair, enabled)| {
2951 let pair_start = pair.start.trim_end();
2952 let pair_end = pair.end.trim_start();
2953
2954 enabled
2955 && pair.newline
2956 && buffer.contains_str_at(
2957 end + trailing_whitespace_len,
2958 pair_end,
2959 )
2960 && buffer.contains_str_at(
2961 (start - leading_whitespace_len)
2962 .saturating_sub(pair_start.len()),
2963 pair_start,
2964 )
2965 });
2966
2967 // Comment extension on newline is allowed only for cursor selections
2968 let comment_delimiter = maybe!({
2969 if !selection_is_empty {
2970 return None;
2971 }
2972
2973 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
2974 return None;
2975 }
2976
2977 let delimiters = language.line_comment_prefixes();
2978 let max_len_of_delimiter =
2979 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
2980 let (snapshot, range) =
2981 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
2982
2983 let mut index_of_first_non_whitespace = 0;
2984 let comment_candidate = snapshot
2985 .chars_for_range(range)
2986 .skip_while(|c| {
2987 let should_skip = c.is_whitespace();
2988 if should_skip {
2989 index_of_first_non_whitespace += 1;
2990 }
2991 should_skip
2992 })
2993 .take(max_len_of_delimiter)
2994 .collect::<String>();
2995 let comment_prefix = delimiters.iter().find(|comment_prefix| {
2996 comment_candidate.starts_with(comment_prefix.as_ref())
2997 })?;
2998 let cursor_is_placed_after_comment_marker =
2999 index_of_first_non_whitespace + comment_prefix.len()
3000 <= start_point.column as usize;
3001 if cursor_is_placed_after_comment_marker {
3002 Some(comment_prefix.clone())
3003 } else {
3004 None
3005 }
3006 });
3007 (comment_delimiter, insert_extra_newline)
3008 } else {
3009 (None, false)
3010 };
3011
3012 let capacity_for_delimiter = comment_delimiter
3013 .as_deref()
3014 .map(str::len)
3015 .unwrap_or_default();
3016 let mut new_text =
3017 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3018 new_text.push('\n');
3019 new_text.extend(indent.chars());
3020 if let Some(delimiter) = &comment_delimiter {
3021 new_text.push_str(delimiter);
3022 }
3023 if insert_extra_newline {
3024 new_text = new_text.repeat(2);
3025 }
3026
3027 let anchor = buffer.anchor_after(end);
3028 let new_selection = selection.map(|_| anchor);
3029 (
3030 (start..end, new_text),
3031 (insert_extra_newline, new_selection),
3032 )
3033 })
3034 .unzip()
3035 };
3036
3037 this.edit_with_autoindent(edits, cx);
3038 let buffer = this.buffer.read(cx).snapshot(cx);
3039 let new_selections = selection_fixup_info
3040 .into_iter()
3041 .map(|(extra_newline_inserted, new_selection)| {
3042 let mut cursor = new_selection.end.to_point(&buffer);
3043 if extra_newline_inserted {
3044 cursor.row -= 1;
3045 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3046 }
3047 new_selection.map(|_| cursor)
3048 })
3049 .collect();
3050
3051 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
3052 this.refresh_inline_completion(true, false, cx);
3053 });
3054 }
3055
3056 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
3057 let buffer = self.buffer.read(cx);
3058 let snapshot = buffer.snapshot(cx);
3059
3060 let mut edits = Vec::new();
3061 let mut rows = Vec::new();
3062
3063 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3064 let cursor = selection.head();
3065 let row = cursor.row;
3066
3067 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3068
3069 let newline = "\n".to_string();
3070 edits.push((start_of_line..start_of_line, newline));
3071
3072 rows.push(row + rows_inserted as u32);
3073 }
3074
3075 self.transact(cx, |editor, cx| {
3076 editor.edit(edits, cx);
3077
3078 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3079 let mut index = 0;
3080 s.move_cursors_with(|map, _, _| {
3081 let row = rows[index];
3082 index += 1;
3083
3084 let point = Point::new(row, 0);
3085 let boundary = map.next_line_boundary(point).1;
3086 let clipped = map.clip_point(boundary, Bias::Left);
3087
3088 (clipped, SelectionGoal::None)
3089 });
3090 });
3091
3092 let mut indent_edits = Vec::new();
3093 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3094 for row in rows {
3095 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3096 for (row, indent) in indents {
3097 if indent.len == 0 {
3098 continue;
3099 }
3100
3101 let text = match indent.kind {
3102 IndentKind::Space => " ".repeat(indent.len as usize),
3103 IndentKind::Tab => "\t".repeat(indent.len as usize),
3104 };
3105 let point = Point::new(row.0, 0);
3106 indent_edits.push((point..point, text));
3107 }
3108 }
3109 editor.edit(indent_edits, cx);
3110 });
3111 }
3112
3113 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
3114 let buffer = self.buffer.read(cx);
3115 let snapshot = buffer.snapshot(cx);
3116
3117 let mut edits = Vec::new();
3118 let mut rows = Vec::new();
3119 let mut rows_inserted = 0;
3120
3121 for selection in self.selections.all_adjusted(cx) {
3122 let cursor = selection.head();
3123 let row = cursor.row;
3124
3125 let point = Point::new(row + 1, 0);
3126 let start_of_line = snapshot.clip_point(point, Bias::Left);
3127
3128 let newline = "\n".to_string();
3129 edits.push((start_of_line..start_of_line, newline));
3130
3131 rows_inserted += 1;
3132 rows.push(row + rows_inserted);
3133 }
3134
3135 self.transact(cx, |editor, cx| {
3136 editor.edit(edits, cx);
3137
3138 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3139 let mut index = 0;
3140 s.move_cursors_with(|map, _, _| {
3141 let row = rows[index];
3142 index += 1;
3143
3144 let point = Point::new(row, 0);
3145 let boundary = map.next_line_boundary(point).1;
3146 let clipped = map.clip_point(boundary, Bias::Left);
3147
3148 (clipped, SelectionGoal::None)
3149 });
3150 });
3151
3152 let mut indent_edits = Vec::new();
3153 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3154 for row in rows {
3155 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3156 for (row, indent) in indents {
3157 if indent.len == 0 {
3158 continue;
3159 }
3160
3161 let text = match indent.kind {
3162 IndentKind::Space => " ".repeat(indent.len as usize),
3163 IndentKind::Tab => "\t".repeat(indent.len as usize),
3164 };
3165 let point = Point::new(row.0, 0);
3166 indent_edits.push((point..point, text));
3167 }
3168 }
3169 editor.edit(indent_edits, cx);
3170 });
3171 }
3172
3173 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3174 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3175 original_indent_columns: Vec::new(),
3176 });
3177 self.insert_with_autoindent_mode(text, autoindent, cx);
3178 }
3179
3180 fn insert_with_autoindent_mode(
3181 &mut self,
3182 text: &str,
3183 autoindent_mode: Option<AutoindentMode>,
3184 cx: &mut ViewContext<Self>,
3185 ) {
3186 if self.read_only(cx) {
3187 return;
3188 }
3189
3190 let text: Arc<str> = text.into();
3191 self.transact(cx, |this, cx| {
3192 let old_selections = this.selections.all_adjusted(cx);
3193 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3194 let anchors = {
3195 let snapshot = buffer.read(cx);
3196 old_selections
3197 .iter()
3198 .map(|s| {
3199 let anchor = snapshot.anchor_after(s.head());
3200 s.map(|_| anchor)
3201 })
3202 .collect::<Vec<_>>()
3203 };
3204 buffer.edit(
3205 old_selections
3206 .iter()
3207 .map(|s| (s.start..s.end, text.clone())),
3208 autoindent_mode,
3209 cx,
3210 );
3211 anchors
3212 });
3213
3214 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3215 s.select_anchors(selection_anchors);
3216 })
3217 });
3218 }
3219
3220 fn trigger_completion_on_input(
3221 &mut self,
3222 text: &str,
3223 trigger_in_words: bool,
3224 cx: &mut ViewContext<Self>,
3225 ) {
3226 if self.is_completion_trigger(text, trigger_in_words, cx) {
3227 self.show_completions(
3228 &ShowCompletions {
3229 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3230 },
3231 cx,
3232 );
3233 } else {
3234 self.hide_context_menu(cx);
3235 }
3236 }
3237
3238 fn is_completion_trigger(
3239 &self,
3240 text: &str,
3241 trigger_in_words: bool,
3242 cx: &mut ViewContext<Self>,
3243 ) -> bool {
3244 let position = self.selections.newest_anchor().head();
3245 let multibuffer = self.buffer.read(cx);
3246 let Some(buffer) = position
3247 .buffer_id
3248 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3249 else {
3250 return false;
3251 };
3252
3253 if let Some(completion_provider) = &self.completion_provider {
3254 completion_provider.is_completion_trigger(
3255 &buffer,
3256 position.text_anchor,
3257 text,
3258 trigger_in_words,
3259 cx,
3260 )
3261 } else {
3262 false
3263 }
3264 }
3265
3266 /// If any empty selections is touching the start of its innermost containing autoclose
3267 /// region, expand it to select the brackets.
3268 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
3269 let selections = self.selections.all::<usize>(cx);
3270 let buffer = self.buffer.read(cx).read(cx);
3271 let new_selections = self
3272 .selections_with_autoclose_regions(selections, &buffer)
3273 .map(|(mut selection, region)| {
3274 if !selection.is_empty() {
3275 return selection;
3276 }
3277
3278 if let Some(region) = region {
3279 let mut range = region.range.to_offset(&buffer);
3280 if selection.start == range.start && range.start >= region.pair.start.len() {
3281 range.start -= region.pair.start.len();
3282 if buffer.contains_str_at(range.start, ®ion.pair.start)
3283 && buffer.contains_str_at(range.end, ®ion.pair.end)
3284 {
3285 range.end += region.pair.end.len();
3286 selection.start = range.start;
3287 selection.end = range.end;
3288
3289 return selection;
3290 }
3291 }
3292 }
3293
3294 let always_treat_brackets_as_autoclosed = buffer
3295 .settings_at(selection.start, cx)
3296 .always_treat_brackets_as_autoclosed;
3297
3298 if !always_treat_brackets_as_autoclosed {
3299 return selection;
3300 }
3301
3302 if let Some(scope) = buffer.language_scope_at(selection.start) {
3303 for (pair, enabled) in scope.brackets() {
3304 if !enabled || !pair.close {
3305 continue;
3306 }
3307
3308 if buffer.contains_str_at(selection.start, &pair.end) {
3309 let pair_start_len = pair.start.len();
3310 if buffer.contains_str_at(
3311 selection.start.saturating_sub(pair_start_len),
3312 &pair.start,
3313 ) {
3314 selection.start -= pair_start_len;
3315 selection.end += pair.end.len();
3316
3317 return selection;
3318 }
3319 }
3320 }
3321 }
3322
3323 selection
3324 })
3325 .collect();
3326
3327 drop(buffer);
3328 self.change_selections(None, cx, |selections| selections.select(new_selections));
3329 }
3330
3331 /// Iterate the given selections, and for each one, find the smallest surrounding
3332 /// autoclose region. This uses the ordering of the selections and the autoclose
3333 /// regions to avoid repeated comparisons.
3334 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3335 &'a self,
3336 selections: impl IntoIterator<Item = Selection<D>>,
3337 buffer: &'a MultiBufferSnapshot,
3338 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3339 let mut i = 0;
3340 let mut regions = self.autoclose_regions.as_slice();
3341 selections.into_iter().map(move |selection| {
3342 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3343
3344 let mut enclosing = None;
3345 while let Some(pair_state) = regions.get(i) {
3346 if pair_state.range.end.to_offset(buffer) < range.start {
3347 regions = ®ions[i + 1..];
3348 i = 0;
3349 } else if pair_state.range.start.to_offset(buffer) > range.end {
3350 break;
3351 } else {
3352 if pair_state.selection_id == selection.id {
3353 enclosing = Some(pair_state);
3354 }
3355 i += 1;
3356 }
3357 }
3358
3359 (selection, enclosing)
3360 })
3361 }
3362
3363 /// Remove any autoclose regions that no longer contain their selection.
3364 fn invalidate_autoclose_regions(
3365 &mut self,
3366 mut selections: &[Selection<Anchor>],
3367 buffer: &MultiBufferSnapshot,
3368 ) {
3369 self.autoclose_regions.retain(|state| {
3370 let mut i = 0;
3371 while let Some(selection) = selections.get(i) {
3372 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3373 selections = &selections[1..];
3374 continue;
3375 }
3376 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3377 break;
3378 }
3379 if selection.id == state.selection_id {
3380 return true;
3381 } else {
3382 i += 1;
3383 }
3384 }
3385 false
3386 });
3387 }
3388
3389 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3390 let offset = position.to_offset(buffer);
3391 let (word_range, kind) = buffer.surrounding_word(offset, true);
3392 if offset > word_range.start && kind == Some(CharKind::Word) {
3393 Some(
3394 buffer
3395 .text_for_range(word_range.start..offset)
3396 .collect::<String>(),
3397 )
3398 } else {
3399 None
3400 }
3401 }
3402
3403 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
3404 self.refresh_inlay_hints(
3405 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
3406 cx,
3407 );
3408 }
3409
3410 pub fn inlay_hints_enabled(&self) -> bool {
3411 self.inlay_hint_cache.enabled
3412 }
3413
3414 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
3415 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
3416 return;
3417 }
3418
3419 let reason_description = reason.description();
3420 let ignore_debounce = matches!(
3421 reason,
3422 InlayHintRefreshReason::SettingsChange(_)
3423 | InlayHintRefreshReason::Toggle(_)
3424 | InlayHintRefreshReason::ExcerptsRemoved(_)
3425 );
3426 let (invalidate_cache, required_languages) = match reason {
3427 InlayHintRefreshReason::Toggle(enabled) => {
3428 self.inlay_hint_cache.enabled = enabled;
3429 if enabled {
3430 (InvalidationStrategy::RefreshRequested, None)
3431 } else {
3432 self.inlay_hint_cache.clear();
3433 self.splice_inlays(
3434 self.visible_inlay_hints(cx)
3435 .iter()
3436 .map(|inlay| inlay.id)
3437 .collect(),
3438 Vec::new(),
3439 cx,
3440 );
3441 return;
3442 }
3443 }
3444 InlayHintRefreshReason::SettingsChange(new_settings) => {
3445 match self.inlay_hint_cache.update_settings(
3446 &self.buffer,
3447 new_settings,
3448 self.visible_inlay_hints(cx),
3449 cx,
3450 ) {
3451 ControlFlow::Break(Some(InlaySplice {
3452 to_remove,
3453 to_insert,
3454 })) => {
3455 self.splice_inlays(to_remove, to_insert, cx);
3456 return;
3457 }
3458 ControlFlow::Break(None) => return,
3459 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3460 }
3461 }
3462 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3463 if let Some(InlaySplice {
3464 to_remove,
3465 to_insert,
3466 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3467 {
3468 self.splice_inlays(to_remove, to_insert, cx);
3469 }
3470 return;
3471 }
3472 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3473 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3474 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3475 }
3476 InlayHintRefreshReason::RefreshRequested => {
3477 (InvalidationStrategy::RefreshRequested, None)
3478 }
3479 };
3480
3481 if let Some(InlaySplice {
3482 to_remove,
3483 to_insert,
3484 }) = self.inlay_hint_cache.spawn_hint_refresh(
3485 reason_description,
3486 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3487 invalidate_cache,
3488 ignore_debounce,
3489 cx,
3490 ) {
3491 self.splice_inlays(to_remove, to_insert, cx);
3492 }
3493 }
3494
3495 fn visible_inlay_hints(&self, cx: &ViewContext<'_, Editor>) -> Vec<Inlay> {
3496 self.display_map
3497 .read(cx)
3498 .current_inlays()
3499 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3500 .cloned()
3501 .collect()
3502 }
3503
3504 pub fn excerpts_for_inlay_hints_query(
3505 &self,
3506 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3507 cx: &mut ViewContext<Editor>,
3508 ) -> HashMap<ExcerptId, (Model<Buffer>, clock::Global, Range<usize>)> {
3509 let Some(project) = self.project.as_ref() else {
3510 return HashMap::default();
3511 };
3512 let project = project.read(cx);
3513 let multi_buffer = self.buffer().read(cx);
3514 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3515 let multi_buffer_visible_start = self
3516 .scroll_manager
3517 .anchor()
3518 .anchor
3519 .to_point(&multi_buffer_snapshot);
3520 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3521 multi_buffer_visible_start
3522 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3523 Bias::Left,
3524 );
3525 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3526 multi_buffer
3527 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
3528 .into_iter()
3529 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3530 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
3531 let buffer = buffer_handle.read(cx);
3532 let buffer_file = project::File::from_dyn(buffer.file())?;
3533 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3534 let worktree_entry = buffer_worktree
3535 .read(cx)
3536 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3537 if worktree_entry.is_ignored {
3538 return None;
3539 }
3540
3541 let language = buffer.language()?;
3542 if let Some(restrict_to_languages) = restrict_to_languages {
3543 if !restrict_to_languages.contains(language) {
3544 return None;
3545 }
3546 }
3547 Some((
3548 excerpt_id,
3549 (
3550 buffer_handle,
3551 buffer.version().clone(),
3552 excerpt_visible_range,
3553 ),
3554 ))
3555 })
3556 .collect()
3557 }
3558
3559 pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
3560 TextLayoutDetails {
3561 text_system: cx.text_system().clone(),
3562 editor_style: self.style.clone().unwrap(),
3563 rem_size: cx.rem_size(),
3564 scroll_anchor: self.scroll_manager.anchor(),
3565 visible_rows: self.visible_line_count(),
3566 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
3567 }
3568 }
3569
3570 fn splice_inlays(
3571 &self,
3572 to_remove: Vec<InlayId>,
3573 to_insert: Vec<Inlay>,
3574 cx: &mut ViewContext<Self>,
3575 ) {
3576 self.display_map.update(cx, |display_map, cx| {
3577 display_map.splice_inlays(to_remove, to_insert, cx)
3578 });
3579 cx.notify();
3580 }
3581
3582 fn trigger_on_type_formatting(
3583 &self,
3584 input: String,
3585 cx: &mut ViewContext<Self>,
3586 ) -> Option<Task<Result<()>>> {
3587 if input.len() != 1 {
3588 return None;
3589 }
3590
3591 let project = self.project.as_ref()?;
3592 let position = self.selections.newest_anchor().head();
3593 let (buffer, buffer_position) = self
3594 .buffer
3595 .read(cx)
3596 .text_anchor_for_position(position, cx)?;
3597
3598 let settings = language_settings::language_settings(
3599 buffer
3600 .read(cx)
3601 .language_at(buffer_position)
3602 .map(|l| l.name()),
3603 buffer.read(cx).file(),
3604 cx,
3605 );
3606 if !settings.use_on_type_format {
3607 return None;
3608 }
3609
3610 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3611 // hence we do LSP request & edit on host side only — add formats to host's history.
3612 let push_to_lsp_host_history = true;
3613 // If this is not the host, append its history with new edits.
3614 let push_to_client_history = project.read(cx).is_via_collab();
3615
3616 let on_type_formatting = project.update(cx, |project, cx| {
3617 project.on_type_format(
3618 buffer.clone(),
3619 buffer_position,
3620 input,
3621 push_to_lsp_host_history,
3622 cx,
3623 )
3624 });
3625 Some(cx.spawn(|editor, mut cx| async move {
3626 if let Some(transaction) = on_type_formatting.await? {
3627 if push_to_client_history {
3628 buffer
3629 .update(&mut cx, |buffer, _| {
3630 buffer.push_transaction(transaction, Instant::now());
3631 })
3632 .ok();
3633 }
3634 editor.update(&mut cx, |editor, cx| {
3635 editor.refresh_document_highlights(cx);
3636 })?;
3637 }
3638 Ok(())
3639 }))
3640 }
3641
3642 pub fn show_completions(&mut self, options: &ShowCompletions, cx: &mut ViewContext<Self>) {
3643 if self.pending_rename.is_some() {
3644 return;
3645 }
3646
3647 let Some(provider) = self.completion_provider.as_ref() else {
3648 return;
3649 };
3650
3651 if !self.snippet_stack.is_empty() && self.context_menu.borrow().as_ref().is_some() {
3652 return;
3653 }
3654
3655 let position = self.selections.newest_anchor().head();
3656 let (buffer, buffer_position) =
3657 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
3658 output
3659 } else {
3660 return;
3661 };
3662 let show_completion_documentation = buffer
3663 .read(cx)
3664 .snapshot()
3665 .settings_at(buffer_position, cx)
3666 .show_completion_documentation;
3667
3668 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
3669
3670 let aside_was_displayed = match self.context_menu.borrow().deref() {
3671 Some(CodeContextMenu::Completions(menu)) => menu.aside_was_displayed.get(),
3672 _ => false,
3673 };
3674 let trigger_kind = match &options.trigger {
3675 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
3676 CompletionTriggerKind::TRIGGER_CHARACTER
3677 }
3678 _ => CompletionTriggerKind::INVOKED,
3679 };
3680 let completion_context = CompletionContext {
3681 trigger_character: options.trigger.as_ref().and_then(|trigger| {
3682 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
3683 Some(String::from(trigger))
3684 } else {
3685 None
3686 }
3687 }),
3688 trigger_kind,
3689 };
3690 let completions = provider.completions(&buffer, buffer_position, completion_context, cx);
3691 let sort_completions = provider.sort_completions();
3692
3693 let id = post_inc(&mut self.next_completion_id);
3694 let task = cx.spawn(|editor, mut cx| {
3695 async move {
3696 editor.update(&mut cx, |this, _| {
3697 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
3698 })?;
3699 let completions = completions.await.log_err();
3700 let menu = if let Some(completions) = completions {
3701 let mut menu = CompletionsMenu::new(
3702 id,
3703 sort_completions,
3704 show_completion_documentation,
3705 position,
3706 buffer.clone(),
3707 completions.into(),
3708 aside_was_displayed,
3709 );
3710 menu.filter(query.as_deref(), cx.background_executor().clone())
3711 .await;
3712
3713 if menu.matches.is_empty() {
3714 None
3715 } else {
3716 Some(menu)
3717 }
3718 } else {
3719 None
3720 };
3721
3722 editor.update(&mut cx, |editor, cx| {
3723 let mut context_menu = editor.context_menu.borrow_mut();
3724 match context_menu.as_ref() {
3725 None => {}
3726 Some(CodeContextMenu::Completions(prev_menu)) => {
3727 if prev_menu.id > id {
3728 return;
3729 }
3730 }
3731 _ => return,
3732 }
3733
3734 if editor.focus_handle.is_focused(cx) && menu.is_some() {
3735 let mut menu = menu.unwrap();
3736 menu.resolve_selected_completion(editor.completion_provider.as_deref(), cx);
3737 *context_menu = Some(CodeContextMenu::Completions(menu));
3738 drop(context_menu);
3739 cx.notify();
3740 } else if editor.completion_tasks.len() <= 1 {
3741 // If there are no more completion tasks and the last menu was
3742 // empty, we should hide it. If it was already hidden, we should
3743 // also show the copilot completion when available.
3744 drop(context_menu);
3745 editor.hide_context_menu(cx);
3746 }
3747 })?;
3748
3749 Ok::<_, anyhow::Error>(())
3750 }
3751 .log_err()
3752 });
3753
3754 self.completion_tasks.push((id, task));
3755 }
3756
3757 pub fn confirm_completion(
3758 &mut self,
3759 action: &ConfirmCompletion,
3760 cx: &mut ViewContext<Self>,
3761 ) -> Option<Task<Result<()>>> {
3762 self.do_completion(action.item_ix, CompletionIntent::Complete, cx)
3763 }
3764
3765 pub fn compose_completion(
3766 &mut self,
3767 action: &ComposeCompletion,
3768 cx: &mut ViewContext<Self>,
3769 ) -> Option<Task<Result<()>>> {
3770 self.do_completion(action.item_ix, CompletionIntent::Compose, cx)
3771 }
3772
3773 fn do_completion(
3774 &mut self,
3775 item_ix: Option<usize>,
3776 intent: CompletionIntent,
3777 cx: &mut ViewContext<Editor>,
3778 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
3779 use language::ToOffset as _;
3780
3781 self.discard_inline_completion(true, cx);
3782 let completions_menu =
3783 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
3784 menu
3785 } else {
3786 return None;
3787 };
3788
3789 let mat = completions_menu
3790 .matches
3791 .get(item_ix.unwrap_or(completions_menu.selected_item))?;
3792 let buffer_handle = completions_menu.buffer;
3793 let completions = completions_menu.completions.borrow_mut();
3794 let completion = completions.get(mat.candidate_id)?;
3795 cx.stop_propagation();
3796
3797 let snippet;
3798 let text;
3799
3800 if completion.is_snippet() {
3801 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
3802 text = snippet.as_ref().unwrap().text.clone();
3803 } else {
3804 snippet = None;
3805 text = completion.new_text.clone();
3806 };
3807 let selections = self.selections.all::<usize>(cx);
3808 let buffer = buffer_handle.read(cx);
3809 let old_range = completion.old_range.to_offset(buffer);
3810 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
3811
3812 let newest_selection = self.selections.newest_anchor();
3813 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
3814 return None;
3815 }
3816
3817 let lookbehind = newest_selection
3818 .start
3819 .text_anchor
3820 .to_offset(buffer)
3821 .saturating_sub(old_range.start);
3822 let lookahead = old_range
3823 .end
3824 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
3825 let mut common_prefix_len = old_text
3826 .bytes()
3827 .zip(text.bytes())
3828 .take_while(|(a, b)| a == b)
3829 .count();
3830
3831 let snapshot = self.buffer.read(cx).snapshot(cx);
3832 let mut range_to_replace: Option<Range<isize>> = None;
3833 let mut ranges = Vec::new();
3834 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3835 for selection in &selections {
3836 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
3837 let start = selection.start.saturating_sub(lookbehind);
3838 let end = selection.end + lookahead;
3839 if selection.id == newest_selection.id {
3840 range_to_replace = Some(
3841 ((start + common_prefix_len) as isize - selection.start as isize)
3842 ..(end as isize - selection.start as isize),
3843 );
3844 }
3845 ranges.push(start + common_prefix_len..end);
3846 } else {
3847 common_prefix_len = 0;
3848 ranges.clear();
3849 ranges.extend(selections.iter().map(|s| {
3850 if s.id == newest_selection.id {
3851 range_to_replace = Some(
3852 old_range.start.to_offset_utf16(&snapshot).0 as isize
3853 - selection.start as isize
3854 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
3855 - selection.start as isize,
3856 );
3857 old_range.clone()
3858 } else {
3859 s.start..s.end
3860 }
3861 }));
3862 break;
3863 }
3864 if !self.linked_edit_ranges.is_empty() {
3865 let start_anchor = snapshot.anchor_before(selection.head());
3866 let end_anchor = snapshot.anchor_after(selection.tail());
3867 if let Some(ranges) = self
3868 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
3869 {
3870 for (buffer, edits) in ranges {
3871 linked_edits.entry(buffer.clone()).or_default().extend(
3872 edits
3873 .into_iter()
3874 .map(|range| (range, text[common_prefix_len..].to_owned())),
3875 );
3876 }
3877 }
3878 }
3879 }
3880 let text = &text[common_prefix_len..];
3881
3882 cx.emit(EditorEvent::InputHandled {
3883 utf16_range_to_replace: range_to_replace,
3884 text: text.into(),
3885 });
3886
3887 self.transact(cx, |this, cx| {
3888 if let Some(mut snippet) = snippet {
3889 snippet.text = text.to_string();
3890 for tabstop in snippet
3891 .tabstops
3892 .iter_mut()
3893 .flat_map(|tabstop| tabstop.ranges.iter_mut())
3894 {
3895 tabstop.start -= common_prefix_len as isize;
3896 tabstop.end -= common_prefix_len as isize;
3897 }
3898
3899 this.insert_snippet(&ranges, snippet, cx).log_err();
3900 } else {
3901 this.buffer.update(cx, |buffer, cx| {
3902 buffer.edit(
3903 ranges.iter().map(|range| (range.clone(), text)),
3904 this.autoindent_mode.clone(),
3905 cx,
3906 );
3907 });
3908 }
3909 for (buffer, edits) in linked_edits {
3910 buffer.update(cx, |buffer, cx| {
3911 let snapshot = buffer.snapshot();
3912 let edits = edits
3913 .into_iter()
3914 .map(|(range, text)| {
3915 use text::ToPoint as TP;
3916 let end_point = TP::to_point(&range.end, &snapshot);
3917 let start_point = TP::to_point(&range.start, &snapshot);
3918 (start_point..end_point, text)
3919 })
3920 .sorted_by_key(|(range, _)| range.start)
3921 .collect::<Vec<_>>();
3922 buffer.edit(edits, None, cx);
3923 })
3924 }
3925
3926 this.refresh_inline_completion(true, false, cx);
3927 });
3928
3929 let show_new_completions_on_confirm = completion
3930 .confirm
3931 .as_ref()
3932 .map_or(false, |confirm| confirm(intent, cx));
3933 if show_new_completions_on_confirm {
3934 self.show_completions(&ShowCompletions { trigger: None }, cx);
3935 }
3936
3937 let provider = self.completion_provider.as_ref()?;
3938 let apply_edits = provider.apply_additional_edits_for_completion(
3939 buffer_handle,
3940 completion.clone(),
3941 true,
3942 cx,
3943 );
3944
3945 let editor_settings = EditorSettings::get_global(cx);
3946 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
3947 // After the code completion is finished, users often want to know what signatures are needed.
3948 // so we should automatically call signature_help
3949 self.show_signature_help(&ShowSignatureHelp, cx);
3950 }
3951
3952 Some(cx.foreground_executor().spawn(async move {
3953 apply_edits.await?;
3954 Ok(())
3955 }))
3956 }
3957
3958 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
3959 let mut context_menu = self.context_menu.borrow_mut();
3960 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
3961 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
3962 // Toggle if we're selecting the same one
3963 *context_menu = None;
3964 cx.notify();
3965 return;
3966 } else {
3967 // Otherwise, clear it and start a new one
3968 *context_menu = None;
3969 cx.notify();
3970 }
3971 }
3972 drop(context_menu);
3973 let snapshot = self.snapshot(cx);
3974 let deployed_from_indicator = action.deployed_from_indicator;
3975 let mut task = self.code_actions_task.take();
3976 let action = action.clone();
3977 cx.spawn(|editor, mut cx| async move {
3978 while let Some(prev_task) = task {
3979 prev_task.await.log_err();
3980 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
3981 }
3982
3983 let spawned_test_task = editor.update(&mut cx, |editor, cx| {
3984 if editor.focus_handle.is_focused(cx) {
3985 let multibuffer_point = action
3986 .deployed_from_indicator
3987 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
3988 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
3989 let (buffer, buffer_row) = snapshot
3990 .buffer_snapshot
3991 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
3992 .and_then(|(buffer_snapshot, range)| {
3993 editor
3994 .buffer
3995 .read(cx)
3996 .buffer(buffer_snapshot.remote_id())
3997 .map(|buffer| (buffer, range.start.row))
3998 })?;
3999 let (_, code_actions) = editor
4000 .available_code_actions
4001 .clone()
4002 .and_then(|(location, code_actions)| {
4003 let snapshot = location.buffer.read(cx).snapshot();
4004 let point_range = location.range.to_point(&snapshot);
4005 let point_range = point_range.start.row..=point_range.end.row;
4006 if point_range.contains(&buffer_row) {
4007 Some((location, code_actions))
4008 } else {
4009 None
4010 }
4011 })
4012 .unzip();
4013 let buffer_id = buffer.read(cx).remote_id();
4014 let tasks = editor
4015 .tasks
4016 .get(&(buffer_id, buffer_row))
4017 .map(|t| Arc::new(t.to_owned()));
4018 if tasks.is_none() && code_actions.is_none() {
4019 return None;
4020 }
4021
4022 editor.completion_tasks.clear();
4023 editor.discard_inline_completion(false, cx);
4024 let task_context =
4025 tasks
4026 .as_ref()
4027 .zip(editor.project.clone())
4028 .map(|(tasks, project)| {
4029 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4030 });
4031
4032 Some(cx.spawn(|editor, mut cx| async move {
4033 let task_context = match task_context {
4034 Some(task_context) => task_context.await,
4035 None => None,
4036 };
4037 let resolved_tasks =
4038 tasks.zip(task_context).map(|(tasks, task_context)| {
4039 Rc::new(ResolvedTasks {
4040 templates: tasks.resolve(&task_context).collect(),
4041 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4042 multibuffer_point.row,
4043 tasks.column,
4044 )),
4045 })
4046 });
4047 let spawn_straight_away = resolved_tasks
4048 .as_ref()
4049 .map_or(false, |tasks| tasks.templates.len() == 1)
4050 && code_actions
4051 .as_ref()
4052 .map_or(true, |actions| actions.is_empty());
4053 if let Ok(task) = editor.update(&mut cx, |editor, cx| {
4054 *editor.context_menu.borrow_mut() =
4055 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4056 buffer,
4057 actions: CodeActionContents {
4058 tasks: resolved_tasks,
4059 actions: code_actions,
4060 },
4061 selected_item: Default::default(),
4062 scroll_handle: UniformListScrollHandle::default(),
4063 deployed_from_indicator,
4064 }));
4065 if spawn_straight_away {
4066 if let Some(task) = editor.confirm_code_action(
4067 &ConfirmCodeAction { item_ix: Some(0) },
4068 cx,
4069 ) {
4070 cx.notify();
4071 return task;
4072 }
4073 }
4074 cx.notify();
4075 Task::ready(Ok(()))
4076 }) {
4077 task.await
4078 } else {
4079 Ok(())
4080 }
4081 }))
4082 } else {
4083 Some(Task::ready(Ok(())))
4084 }
4085 })?;
4086 if let Some(task) = spawned_test_task {
4087 task.await?;
4088 }
4089
4090 Ok::<_, anyhow::Error>(())
4091 })
4092 .detach_and_log_err(cx);
4093 }
4094
4095 pub fn confirm_code_action(
4096 &mut self,
4097 action: &ConfirmCodeAction,
4098 cx: &mut ViewContext<Self>,
4099 ) -> Option<Task<Result<()>>> {
4100 let actions_menu = if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
4101 menu
4102 } else {
4103 return None;
4104 };
4105 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4106 let action = actions_menu.actions.get(action_ix)?;
4107 let title = action.label();
4108 let buffer = actions_menu.buffer;
4109 let workspace = self.workspace()?;
4110
4111 match action {
4112 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4113 workspace.update(cx, |workspace, cx| {
4114 workspace::tasks::schedule_resolved_task(
4115 workspace,
4116 task_source_kind,
4117 resolved_task,
4118 false,
4119 cx,
4120 );
4121
4122 Some(Task::ready(Ok(())))
4123 })
4124 }
4125 CodeActionsItem::CodeAction {
4126 excerpt_id,
4127 action,
4128 provider,
4129 } => {
4130 let apply_code_action =
4131 provider.apply_code_action(buffer, action, excerpt_id, true, cx);
4132 let workspace = workspace.downgrade();
4133 Some(cx.spawn(|editor, cx| async move {
4134 let project_transaction = apply_code_action.await?;
4135 Self::open_project_transaction(
4136 &editor,
4137 workspace,
4138 project_transaction,
4139 title,
4140 cx,
4141 )
4142 .await
4143 }))
4144 }
4145 }
4146 }
4147
4148 pub async fn open_project_transaction(
4149 this: &WeakView<Editor>,
4150 workspace: WeakView<Workspace>,
4151 transaction: ProjectTransaction,
4152 title: String,
4153 mut cx: AsyncWindowContext,
4154 ) -> Result<()> {
4155 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4156 cx.update(|cx| {
4157 entries.sort_unstable_by_key(|(buffer, _)| {
4158 buffer.read(cx).file().map(|f| f.path().clone())
4159 });
4160 })?;
4161
4162 // If the project transaction's edits are all contained within this editor, then
4163 // avoid opening a new editor to display them.
4164
4165 if let Some((buffer, transaction)) = entries.first() {
4166 if entries.len() == 1 {
4167 let excerpt = this.update(&mut cx, |editor, cx| {
4168 editor
4169 .buffer()
4170 .read(cx)
4171 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4172 })?;
4173 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4174 if excerpted_buffer == *buffer {
4175 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4176 let excerpt_range = excerpt_range.to_offset(buffer);
4177 buffer
4178 .edited_ranges_for_transaction::<usize>(transaction)
4179 .all(|range| {
4180 excerpt_range.start <= range.start
4181 && excerpt_range.end >= range.end
4182 })
4183 })?;
4184
4185 if all_edits_within_excerpt {
4186 return Ok(());
4187 }
4188 }
4189 }
4190 }
4191 } else {
4192 return Ok(());
4193 }
4194
4195 let mut ranges_to_highlight = Vec::new();
4196 let excerpt_buffer = cx.new_model(|cx| {
4197 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4198 for (buffer_handle, transaction) in &entries {
4199 let buffer = buffer_handle.read(cx);
4200 ranges_to_highlight.extend(
4201 multibuffer.push_excerpts_with_context_lines(
4202 buffer_handle.clone(),
4203 buffer
4204 .edited_ranges_for_transaction::<usize>(transaction)
4205 .collect(),
4206 DEFAULT_MULTIBUFFER_CONTEXT,
4207 cx,
4208 ),
4209 );
4210 }
4211 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4212 multibuffer
4213 })?;
4214
4215 workspace.update(&mut cx, |workspace, cx| {
4216 let project = workspace.project().clone();
4217 let editor =
4218 cx.new_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, cx));
4219 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
4220 editor.update(cx, |editor, cx| {
4221 editor.highlight_background::<Self>(
4222 &ranges_to_highlight,
4223 |theme| theme.editor_highlighted_line_background,
4224 cx,
4225 );
4226 });
4227 })?;
4228
4229 Ok(())
4230 }
4231
4232 pub fn clear_code_action_providers(&mut self) {
4233 self.code_action_providers.clear();
4234 self.available_code_actions.take();
4235 }
4236
4237 pub fn push_code_action_provider(
4238 &mut self,
4239 provider: Rc<dyn CodeActionProvider>,
4240 cx: &mut ViewContext<Self>,
4241 ) {
4242 self.code_action_providers.push(provider);
4243 self.refresh_code_actions(cx);
4244 }
4245
4246 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4247 let buffer = self.buffer.read(cx);
4248 let newest_selection = self.selections.newest_anchor().clone();
4249 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4250 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4251 if start_buffer != end_buffer {
4252 return None;
4253 }
4254
4255 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
4256 cx.background_executor()
4257 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4258 .await;
4259
4260 let (providers, tasks) = this.update(&mut cx, |this, cx| {
4261 let providers = this.code_action_providers.clone();
4262 let tasks = this
4263 .code_action_providers
4264 .iter()
4265 .map(|provider| provider.code_actions(&start_buffer, start..end, cx))
4266 .collect::<Vec<_>>();
4267 (providers, tasks)
4268 })?;
4269
4270 let mut actions = Vec::new();
4271 for (provider, provider_actions) in
4272 providers.into_iter().zip(future::join_all(tasks).await)
4273 {
4274 if let Some(provider_actions) = provider_actions.log_err() {
4275 actions.extend(provider_actions.into_iter().map(|action| {
4276 AvailableCodeAction {
4277 excerpt_id: newest_selection.start.excerpt_id,
4278 action,
4279 provider: provider.clone(),
4280 }
4281 }));
4282 }
4283 }
4284
4285 this.update(&mut cx, |this, cx| {
4286 this.available_code_actions = if actions.is_empty() {
4287 None
4288 } else {
4289 Some((
4290 Location {
4291 buffer: start_buffer,
4292 range: start..end,
4293 },
4294 actions.into(),
4295 ))
4296 };
4297 cx.notify();
4298 })
4299 }));
4300 None
4301 }
4302
4303 fn start_inline_blame_timer(&mut self, cx: &mut ViewContext<Self>) {
4304 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4305 self.show_git_blame_inline = false;
4306
4307 self.show_git_blame_inline_delay_task = Some(cx.spawn(|this, mut cx| async move {
4308 cx.background_executor().timer(delay).await;
4309
4310 this.update(&mut cx, |this, cx| {
4311 this.show_git_blame_inline = true;
4312 cx.notify();
4313 })
4314 .log_err();
4315 }));
4316 }
4317 }
4318
4319 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4320 if self.pending_rename.is_some() {
4321 return None;
4322 }
4323
4324 let provider = self.semantics_provider.clone()?;
4325 let buffer = self.buffer.read(cx);
4326 let newest_selection = self.selections.newest_anchor().clone();
4327 let cursor_position = newest_selection.head();
4328 let (cursor_buffer, cursor_buffer_position) =
4329 buffer.text_anchor_for_position(cursor_position, cx)?;
4330 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4331 if cursor_buffer != tail_buffer {
4332 return None;
4333 }
4334 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
4335 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4336 cx.background_executor()
4337 .timer(Duration::from_millis(debounce))
4338 .await;
4339
4340 let highlights = if let Some(highlights) = cx
4341 .update(|cx| {
4342 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4343 })
4344 .ok()
4345 .flatten()
4346 {
4347 highlights.await.log_err()
4348 } else {
4349 None
4350 };
4351
4352 if let Some(highlights) = highlights {
4353 this.update(&mut cx, |this, cx| {
4354 if this.pending_rename.is_some() {
4355 return;
4356 }
4357
4358 let buffer_id = cursor_position.buffer_id;
4359 let buffer = this.buffer.read(cx);
4360 if !buffer
4361 .text_anchor_for_position(cursor_position, cx)
4362 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4363 {
4364 return;
4365 }
4366
4367 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4368 let mut write_ranges = Vec::new();
4369 let mut read_ranges = Vec::new();
4370 for highlight in highlights {
4371 for (excerpt_id, excerpt_range) in
4372 buffer.excerpts_for_buffer(&cursor_buffer, cx)
4373 {
4374 let start = highlight
4375 .range
4376 .start
4377 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4378 let end = highlight
4379 .range
4380 .end
4381 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4382 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4383 continue;
4384 }
4385
4386 let range = Anchor {
4387 buffer_id,
4388 excerpt_id,
4389 text_anchor: start,
4390 }..Anchor {
4391 buffer_id,
4392 excerpt_id,
4393 text_anchor: end,
4394 };
4395 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4396 write_ranges.push(range);
4397 } else {
4398 read_ranges.push(range);
4399 }
4400 }
4401 }
4402
4403 this.highlight_background::<DocumentHighlightRead>(
4404 &read_ranges,
4405 |theme| theme.editor_document_highlight_read_background,
4406 cx,
4407 );
4408 this.highlight_background::<DocumentHighlightWrite>(
4409 &write_ranges,
4410 |theme| theme.editor_document_highlight_write_background,
4411 cx,
4412 );
4413 cx.notify();
4414 })
4415 .log_err();
4416 }
4417 }));
4418 None
4419 }
4420
4421 pub fn refresh_inline_completion(
4422 &mut self,
4423 debounce: bool,
4424 user_requested: bool,
4425 cx: &mut ViewContext<Self>,
4426 ) -> Option<()> {
4427 let provider = self.inline_completion_provider()?;
4428 let cursor = self.selections.newest_anchor().head();
4429 let (buffer, cursor_buffer_position) =
4430 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4431
4432 if !user_requested
4433 && (!self.enable_inline_completions
4434 || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
4435 || !self.is_focused(cx))
4436 {
4437 self.discard_inline_completion(false, cx);
4438 return None;
4439 }
4440
4441 self.update_visible_inline_completion(cx);
4442 provider.refresh(buffer, cursor_buffer_position, debounce, cx);
4443 Some(())
4444 }
4445
4446 fn cycle_inline_completion(
4447 &mut self,
4448 direction: Direction,
4449 cx: &mut ViewContext<Self>,
4450 ) -> Option<()> {
4451 let provider = self.inline_completion_provider()?;
4452 let cursor = self.selections.newest_anchor().head();
4453 let (buffer, cursor_buffer_position) =
4454 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4455 if !self.enable_inline_completions
4456 || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
4457 {
4458 return None;
4459 }
4460
4461 provider.cycle(buffer, cursor_buffer_position, direction, cx);
4462 self.update_visible_inline_completion(cx);
4463
4464 Some(())
4465 }
4466
4467 pub fn show_inline_completion(&mut self, _: &ShowInlineCompletion, cx: &mut ViewContext<Self>) {
4468 if !self.has_active_inline_completion() {
4469 self.refresh_inline_completion(false, true, cx);
4470 return;
4471 }
4472
4473 self.update_visible_inline_completion(cx);
4474 }
4475
4476 pub fn display_cursor_names(&mut self, _: &DisplayCursorNames, cx: &mut ViewContext<Self>) {
4477 self.show_cursor_names(cx);
4478 }
4479
4480 fn show_cursor_names(&mut self, cx: &mut ViewContext<Self>) {
4481 self.show_cursor_names = true;
4482 cx.notify();
4483 cx.spawn(|this, mut cx| async move {
4484 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
4485 this.update(&mut cx, |this, cx| {
4486 this.show_cursor_names = false;
4487 cx.notify()
4488 })
4489 .ok()
4490 })
4491 .detach();
4492 }
4493
4494 pub fn next_inline_completion(&mut self, _: &NextInlineCompletion, cx: &mut ViewContext<Self>) {
4495 if self.has_active_inline_completion() {
4496 self.cycle_inline_completion(Direction::Next, cx);
4497 } else {
4498 let is_copilot_disabled = self.refresh_inline_completion(false, true, cx).is_none();
4499 if is_copilot_disabled {
4500 cx.propagate();
4501 }
4502 }
4503 }
4504
4505 pub fn previous_inline_completion(
4506 &mut self,
4507 _: &PreviousInlineCompletion,
4508 cx: &mut ViewContext<Self>,
4509 ) {
4510 if self.has_active_inline_completion() {
4511 self.cycle_inline_completion(Direction::Prev, cx);
4512 } else {
4513 let is_copilot_disabled = self.refresh_inline_completion(false, true, cx).is_none();
4514 if is_copilot_disabled {
4515 cx.propagate();
4516 }
4517 }
4518 }
4519
4520 pub fn accept_inline_completion(
4521 &mut self,
4522 _: &AcceptInlineCompletion,
4523 cx: &mut ViewContext<Self>,
4524 ) {
4525 self.hide_context_menu(cx);
4526
4527 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
4528 return;
4529 };
4530
4531 self.report_inline_completion_event(true, cx);
4532
4533 match &active_inline_completion.completion {
4534 InlineCompletion::Move(position) => {
4535 let position = *position;
4536 self.change_selections(Some(Autoscroll::newest()), cx, |selections| {
4537 selections.select_anchor_ranges([position..position]);
4538 });
4539 }
4540 InlineCompletion::Edit(edits) => {
4541 if let Some(provider) = self.inline_completion_provider() {
4542 provider.accept(cx);
4543 }
4544
4545 let snapshot = self.buffer.read(cx).snapshot(cx);
4546 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
4547
4548 self.buffer.update(cx, |buffer, cx| {
4549 buffer.edit(edits.iter().cloned(), None, cx)
4550 });
4551
4552 self.change_selections(None, cx, |s| {
4553 s.select_anchor_ranges([last_edit_end..last_edit_end])
4554 });
4555
4556 self.update_visible_inline_completion(cx);
4557 if self.active_inline_completion.is_none() {
4558 self.refresh_inline_completion(true, true, cx);
4559 }
4560
4561 cx.notify();
4562 }
4563 }
4564 }
4565
4566 pub fn accept_partial_inline_completion(
4567 &mut self,
4568 _: &AcceptPartialInlineCompletion,
4569 cx: &mut ViewContext<Self>,
4570 ) {
4571 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
4572 return;
4573 };
4574 if self.selections.count() != 1 {
4575 return;
4576 }
4577
4578 self.report_inline_completion_event(true, cx);
4579
4580 match &active_inline_completion.completion {
4581 InlineCompletion::Move(position) => {
4582 let position = *position;
4583 self.change_selections(Some(Autoscroll::newest()), cx, |selections| {
4584 selections.select_anchor_ranges([position..position]);
4585 });
4586 }
4587 InlineCompletion::Edit(edits) => {
4588 if edits.len() == 1 && edits[0].0.start == edits[0].0.end {
4589 let text = edits[0].1.as_str();
4590 let mut partial_completion = text
4591 .chars()
4592 .by_ref()
4593 .take_while(|c| c.is_alphabetic())
4594 .collect::<String>();
4595 if partial_completion.is_empty() {
4596 partial_completion = text
4597 .chars()
4598 .by_ref()
4599 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
4600 .collect::<String>();
4601 }
4602
4603 cx.emit(EditorEvent::InputHandled {
4604 utf16_range_to_replace: None,
4605 text: partial_completion.clone().into(),
4606 });
4607
4608 self.insert_with_autoindent_mode(&partial_completion, None, cx);
4609
4610 self.refresh_inline_completion(true, true, cx);
4611 cx.notify();
4612 }
4613 }
4614 }
4615 }
4616
4617 fn discard_inline_completion(
4618 &mut self,
4619 should_report_inline_completion_event: bool,
4620 cx: &mut ViewContext<Self>,
4621 ) -> bool {
4622 if should_report_inline_completion_event {
4623 self.report_inline_completion_event(false, cx);
4624 }
4625
4626 if let Some(provider) = self.inline_completion_provider() {
4627 provider.discard(cx);
4628 }
4629
4630 self.take_active_inline_completion(cx).is_some()
4631 }
4632
4633 fn report_inline_completion_event(&self, accepted: bool, cx: &AppContext) {
4634 let Some(provider) = self.inline_completion_provider() else {
4635 return;
4636 };
4637 let Some(project) = self.project.as_ref() else {
4638 return;
4639 };
4640 let Some((_, buffer, _)) = self
4641 .buffer
4642 .read(cx)
4643 .excerpt_containing(self.selections.newest_anchor().head(), cx)
4644 else {
4645 return;
4646 };
4647
4648 let project = project.read(cx);
4649 let extension = buffer
4650 .read(cx)
4651 .file()
4652 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
4653 project.client().telemetry().report_inline_completion_event(
4654 provider.name().into(),
4655 accepted,
4656 extension,
4657 );
4658 }
4659
4660 pub fn has_active_inline_completion(&self) -> bool {
4661 self.active_inline_completion.is_some()
4662 }
4663
4664 fn take_active_inline_completion(
4665 &mut self,
4666 cx: &mut ViewContext<Self>,
4667 ) -> Option<InlineCompletion> {
4668 let active_inline_completion = self.active_inline_completion.take()?;
4669 self.splice_inlays(active_inline_completion.inlay_ids, Default::default(), cx);
4670 self.clear_highlights::<InlineCompletionHighlight>(cx);
4671 Some(active_inline_completion.completion)
4672 }
4673
4674 fn update_visible_inline_completion(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4675 let selection = self.selections.newest_anchor();
4676 let cursor = selection.head();
4677 let multibuffer = self.buffer.read(cx).snapshot(cx);
4678 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
4679 let excerpt_id = cursor.excerpt_id;
4680
4681 if !offset_selection.is_empty()
4682 || self
4683 .active_inline_completion
4684 .as_ref()
4685 .map_or(false, |completion| {
4686 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
4687 let invalidation_range = invalidation_range.start..=invalidation_range.end;
4688 !invalidation_range.contains(&offset_selection.head())
4689 })
4690 {
4691 self.discard_inline_completion(false, cx);
4692 return None;
4693 }
4694
4695 self.take_active_inline_completion(cx);
4696 let provider = self.inline_completion_provider()?;
4697
4698 let (buffer, cursor_buffer_position) =
4699 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4700
4701 let completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
4702 let edits = completion
4703 .edits
4704 .into_iter()
4705 .map(|(range, new_text)| {
4706 (
4707 multibuffer
4708 .anchor_in_excerpt(excerpt_id, range.start)
4709 .unwrap()
4710 ..multibuffer
4711 .anchor_in_excerpt(excerpt_id, range.end)
4712 .unwrap(),
4713 new_text,
4714 )
4715 })
4716 .collect::<Vec<_>>();
4717 if edits.is_empty() {
4718 return None;
4719 }
4720
4721 let first_edit_start = edits.first().unwrap().0.start;
4722 let edit_start_row = first_edit_start
4723 .to_point(&multibuffer)
4724 .row
4725 .saturating_sub(2);
4726
4727 let last_edit_end = edits.last().unwrap().0.end;
4728 let edit_end_row = cmp::min(
4729 multibuffer.max_point().row,
4730 last_edit_end.to_point(&multibuffer).row + 2,
4731 );
4732
4733 let cursor_row = cursor.to_point(&multibuffer).row;
4734
4735 let mut inlay_ids = Vec::new();
4736 let invalidation_row_range;
4737 let completion;
4738 if cursor_row < edit_start_row {
4739 invalidation_row_range = cursor_row..edit_end_row;
4740 completion = InlineCompletion::Move(first_edit_start);
4741 } else if cursor_row > edit_end_row {
4742 invalidation_row_range = edit_start_row..cursor_row;
4743 completion = InlineCompletion::Move(first_edit_start);
4744 } else {
4745 if edits
4746 .iter()
4747 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
4748 {
4749 let mut inlays = Vec::new();
4750 for (range, new_text) in &edits {
4751 let inlay = Inlay::inline_completion(
4752 post_inc(&mut self.next_inlay_id),
4753 range.start,
4754 new_text.as_str(),
4755 );
4756 inlay_ids.push(inlay.id);
4757 inlays.push(inlay);
4758 }
4759
4760 self.splice_inlays(vec![], inlays, cx);
4761 } else {
4762 let background_color = cx.theme().status().deleted_background;
4763 self.highlight_text::<InlineCompletionHighlight>(
4764 edits.iter().map(|(range, _)| range.clone()).collect(),
4765 HighlightStyle {
4766 background_color: Some(background_color),
4767 ..Default::default()
4768 },
4769 cx,
4770 );
4771 }
4772
4773 invalidation_row_range = edit_start_row..edit_end_row;
4774 completion = InlineCompletion::Edit(edits);
4775 };
4776
4777 let invalidation_range = multibuffer
4778 .anchor_before(Point::new(invalidation_row_range.start, 0))
4779 ..multibuffer.anchor_after(Point::new(
4780 invalidation_row_range.end,
4781 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
4782 ));
4783
4784 self.active_inline_completion = Some(InlineCompletionState {
4785 inlay_ids,
4786 completion,
4787 invalidation_range,
4788 });
4789 cx.notify();
4790
4791 Some(())
4792 }
4793
4794 fn inline_completion_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
4795 Some(self.inline_completion_provider.as_ref()?.provider.clone())
4796 }
4797
4798 fn render_code_actions_indicator(
4799 &self,
4800 _style: &EditorStyle,
4801 row: DisplayRow,
4802 is_active: bool,
4803 cx: &mut ViewContext<Self>,
4804 ) -> Option<IconButton> {
4805 if self.available_code_actions.is_some() {
4806 Some(
4807 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
4808 .shape(ui::IconButtonShape::Square)
4809 .icon_size(IconSize::XSmall)
4810 .icon_color(Color::Muted)
4811 .toggle_state(is_active)
4812 .tooltip({
4813 let focus_handle = self.focus_handle.clone();
4814 move |cx| {
4815 Tooltip::for_action_in(
4816 "Toggle Code Actions",
4817 &ToggleCodeActions {
4818 deployed_from_indicator: None,
4819 },
4820 &focus_handle,
4821 cx,
4822 )
4823 }
4824 })
4825 .on_click(cx.listener(move |editor, _e, cx| {
4826 editor.focus(cx);
4827 editor.toggle_code_actions(
4828 &ToggleCodeActions {
4829 deployed_from_indicator: Some(row),
4830 },
4831 cx,
4832 );
4833 })),
4834 )
4835 } else {
4836 None
4837 }
4838 }
4839
4840 fn clear_tasks(&mut self) {
4841 self.tasks.clear()
4842 }
4843
4844 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
4845 if self.tasks.insert(key, value).is_some() {
4846 // This case should hopefully be rare, but just in case...
4847 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
4848 }
4849 }
4850
4851 fn build_tasks_context(
4852 project: &Model<Project>,
4853 buffer: &Model<Buffer>,
4854 buffer_row: u32,
4855 tasks: &Arc<RunnableTasks>,
4856 cx: &mut ViewContext<Self>,
4857 ) -> Task<Option<task::TaskContext>> {
4858 let position = Point::new(buffer_row, tasks.column);
4859 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
4860 let location = Location {
4861 buffer: buffer.clone(),
4862 range: range_start..range_start,
4863 };
4864 // Fill in the environmental variables from the tree-sitter captures
4865 let mut captured_task_variables = TaskVariables::default();
4866 for (capture_name, value) in tasks.extra_variables.clone() {
4867 captured_task_variables.insert(
4868 task::VariableName::Custom(capture_name.into()),
4869 value.clone(),
4870 );
4871 }
4872 project.update(cx, |project, cx| {
4873 project.task_store().update(cx, |task_store, cx| {
4874 task_store.task_context_for_location(captured_task_variables, location, cx)
4875 })
4876 })
4877 }
4878
4879 pub fn spawn_nearest_task(&mut self, action: &SpawnNearestTask, cx: &mut ViewContext<Self>) {
4880 let Some((workspace, _)) = self.workspace.clone() else {
4881 return;
4882 };
4883 let Some(project) = self.project.clone() else {
4884 return;
4885 };
4886
4887 // Try to find a closest, enclosing node using tree-sitter that has a
4888 // task
4889 let Some((buffer, buffer_row, tasks)) = self
4890 .find_enclosing_node_task(cx)
4891 // Or find the task that's closest in row-distance.
4892 .or_else(|| self.find_closest_task(cx))
4893 else {
4894 return;
4895 };
4896
4897 let reveal_strategy = action.reveal;
4898 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
4899 cx.spawn(|_, mut cx| async move {
4900 let context = task_context.await?;
4901 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
4902
4903 let resolved = resolved_task.resolved.as_mut()?;
4904 resolved.reveal = reveal_strategy;
4905
4906 workspace
4907 .update(&mut cx, |workspace, cx| {
4908 workspace::tasks::schedule_resolved_task(
4909 workspace,
4910 task_source_kind,
4911 resolved_task,
4912 false,
4913 cx,
4914 );
4915 })
4916 .ok()
4917 })
4918 .detach();
4919 }
4920
4921 fn find_closest_task(
4922 &mut self,
4923 cx: &mut ViewContext<Self>,
4924 ) -> Option<(Model<Buffer>, u32, Arc<RunnableTasks>)> {
4925 let cursor_row = self.selections.newest_adjusted(cx).head().row;
4926
4927 let ((buffer_id, row), tasks) = self
4928 .tasks
4929 .iter()
4930 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
4931
4932 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
4933 let tasks = Arc::new(tasks.to_owned());
4934 Some((buffer, *row, tasks))
4935 }
4936
4937 fn find_enclosing_node_task(
4938 &mut self,
4939 cx: &mut ViewContext<Self>,
4940 ) -> Option<(Model<Buffer>, u32, Arc<RunnableTasks>)> {
4941 let snapshot = self.buffer.read(cx).snapshot(cx);
4942 let offset = self.selections.newest::<usize>(cx).head();
4943 let excerpt = snapshot.excerpt_containing(offset..offset)?;
4944 let buffer_id = excerpt.buffer().remote_id();
4945
4946 let layer = excerpt.buffer().syntax_layer_at(offset)?;
4947 let mut cursor = layer.node().walk();
4948
4949 while cursor.goto_first_child_for_byte(offset).is_some() {
4950 if cursor.node().end_byte() == offset {
4951 cursor.goto_next_sibling();
4952 }
4953 }
4954
4955 // Ascend to the smallest ancestor that contains the range and has a task.
4956 loop {
4957 let node = cursor.node();
4958 let node_range = node.byte_range();
4959 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
4960
4961 // Check if this node contains our offset
4962 if node_range.start <= offset && node_range.end >= offset {
4963 // If it contains offset, check for task
4964 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
4965 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
4966 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
4967 }
4968 }
4969
4970 if !cursor.goto_parent() {
4971 break;
4972 }
4973 }
4974 None
4975 }
4976
4977 fn render_run_indicator(
4978 &self,
4979 _style: &EditorStyle,
4980 is_active: bool,
4981 row: DisplayRow,
4982 cx: &mut ViewContext<Self>,
4983 ) -> IconButton {
4984 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
4985 .shape(ui::IconButtonShape::Square)
4986 .icon_size(IconSize::XSmall)
4987 .icon_color(Color::Muted)
4988 .toggle_state(is_active)
4989 .on_click(cx.listener(move |editor, _e, cx| {
4990 editor.focus(cx);
4991 editor.toggle_code_actions(
4992 &ToggleCodeActions {
4993 deployed_from_indicator: Some(row),
4994 },
4995 cx,
4996 );
4997 }))
4998 }
4999
5000 #[cfg(feature = "test-support")]
5001 pub fn context_menu_visible(&self) -> bool {
5002 self.context_menu
5003 .borrow()
5004 .as_ref()
5005 .map_or(false, |menu| menu.visible())
5006 }
5007
5008 fn context_menu_origin(&self, cursor_position: DisplayPoint) -> Option<ContextMenuOrigin> {
5009 self.context_menu
5010 .borrow()
5011 .as_ref()
5012 .map(|menu| menu.origin(cursor_position))
5013 }
5014
5015 fn render_context_menu(
5016 &self,
5017 style: &EditorStyle,
5018 max_height_in_lines: u32,
5019 cx: &mut ViewContext<Editor>,
5020 ) -> Option<AnyElement> {
5021 self.context_menu.borrow().as_ref().and_then(|menu| {
5022 if menu.visible() {
5023 Some(menu.render(
5024 style,
5025 max_height_in_lines,
5026 self.workspace.as_ref().map(|(w, _)| w.clone()),
5027 cx,
5028 ))
5029 } else {
5030 None
5031 }
5032 })
5033 }
5034
5035 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<CodeContextMenu> {
5036 cx.notify();
5037 self.completion_tasks.clear();
5038 self.context_menu.borrow_mut().take()
5039 }
5040
5041 fn show_snippet_choices(
5042 &mut self,
5043 choices: &Vec<String>,
5044 selection: Range<Anchor>,
5045 cx: &mut ViewContext<Self>,
5046 ) {
5047 if selection.start.buffer_id.is_none() {
5048 return;
5049 }
5050 let buffer_id = selection.start.buffer_id.unwrap();
5051 let buffer = self.buffer().read(cx).buffer(buffer_id);
5052 let id = post_inc(&mut self.next_completion_id);
5053
5054 if let Some(buffer) = buffer {
5055 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
5056 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
5057 ));
5058 }
5059 }
5060
5061 pub fn insert_snippet(
5062 &mut self,
5063 insertion_ranges: &[Range<usize>],
5064 snippet: Snippet,
5065 cx: &mut ViewContext<Self>,
5066 ) -> Result<()> {
5067 struct Tabstop<T> {
5068 is_end_tabstop: bool,
5069 ranges: Vec<Range<T>>,
5070 choices: Option<Vec<String>>,
5071 }
5072
5073 let tabstops = self.buffer.update(cx, |buffer, cx| {
5074 let snippet_text: Arc<str> = snippet.text.clone().into();
5075 buffer.edit(
5076 insertion_ranges
5077 .iter()
5078 .cloned()
5079 .map(|range| (range, snippet_text.clone())),
5080 Some(AutoindentMode::EachLine),
5081 cx,
5082 );
5083
5084 let snapshot = &*buffer.read(cx);
5085 let snippet = &snippet;
5086 snippet
5087 .tabstops
5088 .iter()
5089 .map(|tabstop| {
5090 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
5091 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
5092 });
5093 let mut tabstop_ranges = tabstop
5094 .ranges
5095 .iter()
5096 .flat_map(|tabstop_range| {
5097 let mut delta = 0_isize;
5098 insertion_ranges.iter().map(move |insertion_range| {
5099 let insertion_start = insertion_range.start as isize + delta;
5100 delta +=
5101 snippet.text.len() as isize - insertion_range.len() as isize;
5102
5103 let start = ((insertion_start + tabstop_range.start) as usize)
5104 .min(snapshot.len());
5105 let end = ((insertion_start + tabstop_range.end) as usize)
5106 .min(snapshot.len());
5107 snapshot.anchor_before(start)..snapshot.anchor_after(end)
5108 })
5109 })
5110 .collect::<Vec<_>>();
5111 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
5112
5113 Tabstop {
5114 is_end_tabstop,
5115 ranges: tabstop_ranges,
5116 choices: tabstop.choices.clone(),
5117 }
5118 })
5119 .collect::<Vec<_>>()
5120 });
5121 if let Some(tabstop) = tabstops.first() {
5122 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5123 s.select_ranges(tabstop.ranges.iter().cloned());
5124 });
5125
5126 if let Some(choices) = &tabstop.choices {
5127 if let Some(selection) = tabstop.ranges.first() {
5128 self.show_snippet_choices(choices, selection.clone(), cx)
5129 }
5130 }
5131
5132 // If we're already at the last tabstop and it's at the end of the snippet,
5133 // we're done, we don't need to keep the state around.
5134 if !tabstop.is_end_tabstop {
5135 let choices = tabstops
5136 .iter()
5137 .map(|tabstop| tabstop.choices.clone())
5138 .collect();
5139
5140 let ranges = tabstops
5141 .into_iter()
5142 .map(|tabstop| tabstop.ranges)
5143 .collect::<Vec<_>>();
5144
5145 self.snippet_stack.push(SnippetState {
5146 active_index: 0,
5147 ranges,
5148 choices,
5149 });
5150 }
5151
5152 // Check whether the just-entered snippet ends with an auto-closable bracket.
5153 if self.autoclose_regions.is_empty() {
5154 let snapshot = self.buffer.read(cx).snapshot(cx);
5155 for selection in &mut self.selections.all::<Point>(cx) {
5156 let selection_head = selection.head();
5157 let Some(scope) = snapshot.language_scope_at(selection_head) else {
5158 continue;
5159 };
5160
5161 let mut bracket_pair = None;
5162 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
5163 let prev_chars = snapshot
5164 .reversed_chars_at(selection_head)
5165 .collect::<String>();
5166 for (pair, enabled) in scope.brackets() {
5167 if enabled
5168 && pair.close
5169 && prev_chars.starts_with(pair.start.as_str())
5170 && next_chars.starts_with(pair.end.as_str())
5171 {
5172 bracket_pair = Some(pair.clone());
5173 break;
5174 }
5175 }
5176 if let Some(pair) = bracket_pair {
5177 let start = snapshot.anchor_after(selection_head);
5178 let end = snapshot.anchor_after(selection_head);
5179 self.autoclose_regions.push(AutocloseRegion {
5180 selection_id: selection.id,
5181 range: start..end,
5182 pair,
5183 });
5184 }
5185 }
5186 }
5187 }
5188 Ok(())
5189 }
5190
5191 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5192 self.move_to_snippet_tabstop(Bias::Right, cx)
5193 }
5194
5195 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5196 self.move_to_snippet_tabstop(Bias::Left, cx)
5197 }
5198
5199 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
5200 if let Some(mut snippet) = self.snippet_stack.pop() {
5201 match bias {
5202 Bias::Left => {
5203 if snippet.active_index > 0 {
5204 snippet.active_index -= 1;
5205 } else {
5206 self.snippet_stack.push(snippet);
5207 return false;
5208 }
5209 }
5210 Bias::Right => {
5211 if snippet.active_index + 1 < snippet.ranges.len() {
5212 snippet.active_index += 1;
5213 } else {
5214 self.snippet_stack.push(snippet);
5215 return false;
5216 }
5217 }
5218 }
5219 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
5220 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5221 s.select_anchor_ranges(current_ranges.iter().cloned())
5222 });
5223
5224 if let Some(choices) = &snippet.choices[snippet.active_index] {
5225 if let Some(selection) = current_ranges.first() {
5226 self.show_snippet_choices(&choices, selection.clone(), cx);
5227 }
5228 }
5229
5230 // If snippet state is not at the last tabstop, push it back on the stack
5231 if snippet.active_index + 1 < snippet.ranges.len() {
5232 self.snippet_stack.push(snippet);
5233 }
5234 return true;
5235 }
5236 }
5237
5238 false
5239 }
5240
5241 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
5242 self.transact(cx, |this, cx| {
5243 this.select_all(&SelectAll, cx);
5244 this.insert("", cx);
5245 });
5246 }
5247
5248 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
5249 self.transact(cx, |this, cx| {
5250 this.select_autoclose_pair(cx);
5251 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
5252 if !this.linked_edit_ranges.is_empty() {
5253 let selections = this.selections.all::<MultiBufferPoint>(cx);
5254 let snapshot = this.buffer.read(cx).snapshot(cx);
5255
5256 for selection in selections.iter() {
5257 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
5258 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
5259 if selection_start.buffer_id != selection_end.buffer_id {
5260 continue;
5261 }
5262 if let Some(ranges) =
5263 this.linked_editing_ranges_for(selection_start..selection_end, cx)
5264 {
5265 for (buffer, entries) in ranges {
5266 linked_ranges.entry(buffer).or_default().extend(entries);
5267 }
5268 }
5269 }
5270 }
5271
5272 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
5273 if !this.selections.line_mode {
5274 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
5275 for selection in &mut selections {
5276 if selection.is_empty() {
5277 let old_head = selection.head();
5278 let mut new_head =
5279 movement::left(&display_map, old_head.to_display_point(&display_map))
5280 .to_point(&display_map);
5281 if let Some((buffer, line_buffer_range)) = display_map
5282 .buffer_snapshot
5283 .buffer_line_for_row(MultiBufferRow(old_head.row))
5284 {
5285 let indent_size =
5286 buffer.indent_size_for_line(line_buffer_range.start.row);
5287 let indent_len = match indent_size.kind {
5288 IndentKind::Space => {
5289 buffer.settings_at(line_buffer_range.start, cx).tab_size
5290 }
5291 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
5292 };
5293 if old_head.column <= indent_size.len && old_head.column > 0 {
5294 let indent_len = indent_len.get();
5295 new_head = cmp::min(
5296 new_head,
5297 MultiBufferPoint::new(
5298 old_head.row,
5299 ((old_head.column - 1) / indent_len) * indent_len,
5300 ),
5301 );
5302 }
5303 }
5304
5305 selection.set_head(new_head, SelectionGoal::None);
5306 }
5307 }
5308 }
5309
5310 this.signature_help_state.set_backspace_pressed(true);
5311 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5312 this.insert("", cx);
5313 let empty_str: Arc<str> = Arc::from("");
5314 for (buffer, edits) in linked_ranges {
5315 let snapshot = buffer.read(cx).snapshot();
5316 use text::ToPoint as TP;
5317
5318 let edits = edits
5319 .into_iter()
5320 .map(|range| {
5321 let end_point = TP::to_point(&range.end, &snapshot);
5322 let mut start_point = TP::to_point(&range.start, &snapshot);
5323
5324 if end_point == start_point {
5325 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
5326 .saturating_sub(1);
5327 start_point = TP::to_point(&offset, &snapshot);
5328 };
5329
5330 (start_point..end_point, empty_str.clone())
5331 })
5332 .sorted_by_key(|(range, _)| range.start)
5333 .collect::<Vec<_>>();
5334 buffer.update(cx, |this, cx| {
5335 this.edit(edits, None, cx);
5336 })
5337 }
5338 this.refresh_inline_completion(true, false, cx);
5339 linked_editing_ranges::refresh_linked_ranges(this, cx);
5340 });
5341 }
5342
5343 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
5344 self.transact(cx, |this, cx| {
5345 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5346 let line_mode = s.line_mode;
5347 s.move_with(|map, selection| {
5348 if selection.is_empty() && !line_mode {
5349 let cursor = movement::right(map, selection.head());
5350 selection.end = cursor;
5351 selection.reversed = true;
5352 selection.goal = SelectionGoal::None;
5353 }
5354 })
5355 });
5356 this.insert("", cx);
5357 this.refresh_inline_completion(true, false, cx);
5358 });
5359 }
5360
5361 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
5362 if self.move_to_prev_snippet_tabstop(cx) {
5363 return;
5364 }
5365
5366 self.outdent(&Outdent, cx);
5367 }
5368
5369 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
5370 if self.move_to_next_snippet_tabstop(cx) || self.read_only(cx) {
5371 return;
5372 }
5373
5374 let mut selections = self.selections.all_adjusted(cx);
5375 let buffer = self.buffer.read(cx);
5376 let snapshot = buffer.snapshot(cx);
5377 let rows_iter = selections.iter().map(|s| s.head().row);
5378 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
5379
5380 let mut edits = Vec::new();
5381 let mut prev_edited_row = 0;
5382 let mut row_delta = 0;
5383 for selection in &mut selections {
5384 if selection.start.row != prev_edited_row {
5385 row_delta = 0;
5386 }
5387 prev_edited_row = selection.end.row;
5388
5389 // If the selection is non-empty, then increase the indentation of the selected lines.
5390 if !selection.is_empty() {
5391 row_delta =
5392 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5393 continue;
5394 }
5395
5396 // If the selection is empty and the cursor is in the leading whitespace before the
5397 // suggested indentation, then auto-indent the line.
5398 let cursor = selection.head();
5399 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
5400 if let Some(suggested_indent) =
5401 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
5402 {
5403 if cursor.column < suggested_indent.len
5404 && cursor.column <= current_indent.len
5405 && current_indent.len <= suggested_indent.len
5406 {
5407 selection.start = Point::new(cursor.row, suggested_indent.len);
5408 selection.end = selection.start;
5409 if row_delta == 0 {
5410 edits.extend(Buffer::edit_for_indent_size_adjustment(
5411 cursor.row,
5412 current_indent,
5413 suggested_indent,
5414 ));
5415 row_delta = suggested_indent.len - current_indent.len;
5416 }
5417 continue;
5418 }
5419 }
5420
5421 // Otherwise, insert a hard or soft tab.
5422 let settings = buffer.settings_at(cursor, cx);
5423 let tab_size = if settings.hard_tabs {
5424 IndentSize::tab()
5425 } else {
5426 let tab_size = settings.tab_size.get();
5427 let char_column = snapshot
5428 .text_for_range(Point::new(cursor.row, 0)..cursor)
5429 .flat_map(str::chars)
5430 .count()
5431 + row_delta as usize;
5432 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
5433 IndentSize::spaces(chars_to_next_tab_stop)
5434 };
5435 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
5436 selection.end = selection.start;
5437 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
5438 row_delta += tab_size.len;
5439 }
5440
5441 self.transact(cx, |this, cx| {
5442 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5443 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5444 this.refresh_inline_completion(true, false, cx);
5445 });
5446 }
5447
5448 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
5449 if self.read_only(cx) {
5450 return;
5451 }
5452 let mut selections = self.selections.all::<Point>(cx);
5453 let mut prev_edited_row = 0;
5454 let mut row_delta = 0;
5455 let mut edits = Vec::new();
5456 let buffer = self.buffer.read(cx);
5457 let snapshot = buffer.snapshot(cx);
5458 for selection in &mut selections {
5459 if selection.start.row != prev_edited_row {
5460 row_delta = 0;
5461 }
5462 prev_edited_row = selection.end.row;
5463
5464 row_delta =
5465 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5466 }
5467
5468 self.transact(cx, |this, cx| {
5469 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5470 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5471 });
5472 }
5473
5474 fn indent_selection(
5475 buffer: &MultiBuffer,
5476 snapshot: &MultiBufferSnapshot,
5477 selection: &mut Selection<Point>,
5478 edits: &mut Vec<(Range<Point>, String)>,
5479 delta_for_start_row: u32,
5480 cx: &AppContext,
5481 ) -> u32 {
5482 let settings = buffer.settings_at(selection.start, cx);
5483 let tab_size = settings.tab_size.get();
5484 let indent_kind = if settings.hard_tabs {
5485 IndentKind::Tab
5486 } else {
5487 IndentKind::Space
5488 };
5489 let mut start_row = selection.start.row;
5490 let mut end_row = selection.end.row + 1;
5491
5492 // If a selection ends at the beginning of a line, don't indent
5493 // that last line.
5494 if selection.end.column == 0 && selection.end.row > selection.start.row {
5495 end_row -= 1;
5496 }
5497
5498 // Avoid re-indenting a row that has already been indented by a
5499 // previous selection, but still update this selection's column
5500 // to reflect that indentation.
5501 if delta_for_start_row > 0 {
5502 start_row += 1;
5503 selection.start.column += delta_for_start_row;
5504 if selection.end.row == selection.start.row {
5505 selection.end.column += delta_for_start_row;
5506 }
5507 }
5508
5509 let mut delta_for_end_row = 0;
5510 let has_multiple_rows = start_row + 1 != end_row;
5511 for row in start_row..end_row {
5512 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
5513 let indent_delta = match (current_indent.kind, indent_kind) {
5514 (IndentKind::Space, IndentKind::Space) => {
5515 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
5516 IndentSize::spaces(columns_to_next_tab_stop)
5517 }
5518 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
5519 (_, IndentKind::Tab) => IndentSize::tab(),
5520 };
5521
5522 let start = if has_multiple_rows || current_indent.len < selection.start.column {
5523 0
5524 } else {
5525 selection.start.column
5526 };
5527 let row_start = Point::new(row, start);
5528 edits.push((
5529 row_start..row_start,
5530 indent_delta.chars().collect::<String>(),
5531 ));
5532
5533 // Update this selection's endpoints to reflect the indentation.
5534 if row == selection.start.row {
5535 selection.start.column += indent_delta.len;
5536 }
5537 if row == selection.end.row {
5538 selection.end.column += indent_delta.len;
5539 delta_for_end_row = indent_delta.len;
5540 }
5541 }
5542
5543 if selection.start.row == selection.end.row {
5544 delta_for_start_row + delta_for_end_row
5545 } else {
5546 delta_for_end_row
5547 }
5548 }
5549
5550 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
5551 if self.read_only(cx) {
5552 return;
5553 }
5554 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5555 let selections = self.selections.all::<Point>(cx);
5556 let mut deletion_ranges = Vec::new();
5557 let mut last_outdent = None;
5558 {
5559 let buffer = self.buffer.read(cx);
5560 let snapshot = buffer.snapshot(cx);
5561 for selection in &selections {
5562 let settings = buffer.settings_at(selection.start, cx);
5563 let tab_size = settings.tab_size.get();
5564 let mut rows = selection.spanned_rows(false, &display_map);
5565
5566 // Avoid re-outdenting a row that has already been outdented by a
5567 // previous selection.
5568 if let Some(last_row) = last_outdent {
5569 if last_row == rows.start {
5570 rows.start = rows.start.next_row();
5571 }
5572 }
5573 let has_multiple_rows = rows.len() > 1;
5574 for row in rows.iter_rows() {
5575 let indent_size = snapshot.indent_size_for_line(row);
5576 if indent_size.len > 0 {
5577 let deletion_len = match indent_size.kind {
5578 IndentKind::Space => {
5579 let columns_to_prev_tab_stop = indent_size.len % tab_size;
5580 if columns_to_prev_tab_stop == 0 {
5581 tab_size
5582 } else {
5583 columns_to_prev_tab_stop
5584 }
5585 }
5586 IndentKind::Tab => 1,
5587 };
5588 let start = if has_multiple_rows
5589 || deletion_len > selection.start.column
5590 || indent_size.len < selection.start.column
5591 {
5592 0
5593 } else {
5594 selection.start.column - deletion_len
5595 };
5596 deletion_ranges.push(
5597 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
5598 );
5599 last_outdent = Some(row);
5600 }
5601 }
5602 }
5603 }
5604
5605 self.transact(cx, |this, cx| {
5606 this.buffer.update(cx, |buffer, cx| {
5607 let empty_str: Arc<str> = Arc::default();
5608 buffer.edit(
5609 deletion_ranges
5610 .into_iter()
5611 .map(|range| (range, empty_str.clone())),
5612 None,
5613 cx,
5614 );
5615 });
5616 let selections = this.selections.all::<usize>(cx);
5617 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5618 });
5619 }
5620
5621 pub fn autoindent(&mut self, _: &AutoIndent, cx: &mut ViewContext<Self>) {
5622 if self.read_only(cx) {
5623 return;
5624 }
5625 let selections = self
5626 .selections
5627 .all::<usize>(cx)
5628 .into_iter()
5629 .map(|s| s.range());
5630
5631 self.transact(cx, |this, cx| {
5632 this.buffer.update(cx, |buffer, cx| {
5633 buffer.autoindent_ranges(selections, cx);
5634 });
5635 let selections = this.selections.all::<usize>(cx);
5636 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5637 });
5638 }
5639
5640 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
5641 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5642 let selections = self.selections.all::<Point>(cx);
5643
5644 let mut new_cursors = Vec::new();
5645 let mut edit_ranges = Vec::new();
5646 let mut selections = selections.iter().peekable();
5647 while let Some(selection) = selections.next() {
5648 let mut rows = selection.spanned_rows(false, &display_map);
5649 let goal_display_column = selection.head().to_display_point(&display_map).column();
5650
5651 // Accumulate contiguous regions of rows that we want to delete.
5652 while let Some(next_selection) = selections.peek() {
5653 let next_rows = next_selection.spanned_rows(false, &display_map);
5654 if next_rows.start <= rows.end {
5655 rows.end = next_rows.end;
5656 selections.next().unwrap();
5657 } else {
5658 break;
5659 }
5660 }
5661
5662 let buffer = &display_map.buffer_snapshot;
5663 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
5664 let edit_end;
5665 let cursor_buffer_row;
5666 if buffer.max_point().row >= rows.end.0 {
5667 // If there's a line after the range, delete the \n from the end of the row range
5668 // and position the cursor on the next line.
5669 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
5670 cursor_buffer_row = rows.end;
5671 } else {
5672 // If there isn't a line after the range, delete the \n from the line before the
5673 // start of the row range and position the cursor there.
5674 edit_start = edit_start.saturating_sub(1);
5675 edit_end = buffer.len();
5676 cursor_buffer_row = rows.start.previous_row();
5677 }
5678
5679 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
5680 *cursor.column_mut() =
5681 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
5682
5683 new_cursors.push((
5684 selection.id,
5685 buffer.anchor_after(cursor.to_point(&display_map)),
5686 ));
5687 edit_ranges.push(edit_start..edit_end);
5688 }
5689
5690 self.transact(cx, |this, cx| {
5691 let buffer = this.buffer.update(cx, |buffer, cx| {
5692 let empty_str: Arc<str> = Arc::default();
5693 buffer.edit(
5694 edit_ranges
5695 .into_iter()
5696 .map(|range| (range, empty_str.clone())),
5697 None,
5698 cx,
5699 );
5700 buffer.snapshot(cx)
5701 });
5702 let new_selections = new_cursors
5703 .into_iter()
5704 .map(|(id, cursor)| {
5705 let cursor = cursor.to_point(&buffer);
5706 Selection {
5707 id,
5708 start: cursor,
5709 end: cursor,
5710 reversed: false,
5711 goal: SelectionGoal::None,
5712 }
5713 })
5714 .collect();
5715
5716 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5717 s.select(new_selections);
5718 });
5719 });
5720 }
5721
5722 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
5723 if self.read_only(cx) {
5724 return;
5725 }
5726 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
5727 for selection in self.selections.all::<Point>(cx) {
5728 let start = MultiBufferRow(selection.start.row);
5729 // Treat single line selections as if they include the next line. Otherwise this action
5730 // would do nothing for single line selections individual cursors.
5731 let end = if selection.start.row == selection.end.row {
5732 MultiBufferRow(selection.start.row + 1)
5733 } else {
5734 MultiBufferRow(selection.end.row)
5735 };
5736
5737 if let Some(last_row_range) = row_ranges.last_mut() {
5738 if start <= last_row_range.end {
5739 last_row_range.end = end;
5740 continue;
5741 }
5742 }
5743 row_ranges.push(start..end);
5744 }
5745
5746 let snapshot = self.buffer.read(cx).snapshot(cx);
5747 let mut cursor_positions = Vec::new();
5748 for row_range in &row_ranges {
5749 let anchor = snapshot.anchor_before(Point::new(
5750 row_range.end.previous_row().0,
5751 snapshot.line_len(row_range.end.previous_row()),
5752 ));
5753 cursor_positions.push(anchor..anchor);
5754 }
5755
5756 self.transact(cx, |this, cx| {
5757 for row_range in row_ranges.into_iter().rev() {
5758 for row in row_range.iter_rows().rev() {
5759 let end_of_line = Point::new(row.0, snapshot.line_len(row));
5760 let next_line_row = row.next_row();
5761 let indent = snapshot.indent_size_for_line(next_line_row);
5762 let start_of_next_line = Point::new(next_line_row.0, indent.len);
5763
5764 let replace = if snapshot.line_len(next_line_row) > indent.len {
5765 " "
5766 } else {
5767 ""
5768 };
5769
5770 this.buffer.update(cx, |buffer, cx| {
5771 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
5772 });
5773 }
5774 }
5775
5776 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5777 s.select_anchor_ranges(cursor_positions)
5778 });
5779 });
5780 }
5781
5782 pub fn sort_lines_case_sensitive(
5783 &mut self,
5784 _: &SortLinesCaseSensitive,
5785 cx: &mut ViewContext<Self>,
5786 ) {
5787 self.manipulate_lines(cx, |lines| lines.sort())
5788 }
5789
5790 pub fn sort_lines_case_insensitive(
5791 &mut self,
5792 _: &SortLinesCaseInsensitive,
5793 cx: &mut ViewContext<Self>,
5794 ) {
5795 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
5796 }
5797
5798 pub fn unique_lines_case_insensitive(
5799 &mut self,
5800 _: &UniqueLinesCaseInsensitive,
5801 cx: &mut ViewContext<Self>,
5802 ) {
5803 self.manipulate_lines(cx, |lines| {
5804 let mut seen = HashSet::default();
5805 lines.retain(|line| seen.insert(line.to_lowercase()));
5806 })
5807 }
5808
5809 pub fn unique_lines_case_sensitive(
5810 &mut self,
5811 _: &UniqueLinesCaseSensitive,
5812 cx: &mut ViewContext<Self>,
5813 ) {
5814 self.manipulate_lines(cx, |lines| {
5815 let mut seen = HashSet::default();
5816 lines.retain(|line| seen.insert(*line));
5817 })
5818 }
5819
5820 pub fn revert_file(&mut self, _: &RevertFile, cx: &mut ViewContext<Self>) {
5821 let mut revert_changes = HashMap::default();
5822 let snapshot = self.snapshot(cx);
5823 for hunk in hunks_for_ranges(
5824 Some(Point::zero()..snapshot.buffer_snapshot.max_point()).into_iter(),
5825 &snapshot,
5826 ) {
5827 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
5828 }
5829 if !revert_changes.is_empty() {
5830 self.transact(cx, |editor, cx| {
5831 editor.revert(revert_changes, cx);
5832 });
5833 }
5834 }
5835
5836 pub fn reload_file(&mut self, _: &ReloadFile, cx: &mut ViewContext<Self>) {
5837 let Some(project) = self.project.clone() else {
5838 return;
5839 };
5840 self.reload(project, cx).detach_and_notify_err(cx);
5841 }
5842
5843 pub fn revert_selected_hunks(&mut self, _: &RevertSelectedHunks, cx: &mut ViewContext<Self>) {
5844 let revert_changes = self.gather_revert_changes(&self.selections.all(cx), cx);
5845 if !revert_changes.is_empty() {
5846 self.transact(cx, |editor, cx| {
5847 editor.revert(revert_changes, cx);
5848 });
5849 }
5850 }
5851
5852 fn revert_hunk(&mut self, hunk: HoveredHunk, cx: &mut ViewContext<Editor>) {
5853 let snapshot = self.buffer.read(cx).read(cx);
5854 if let Some(hunk) = crate::hunk_diff::to_diff_hunk(&hunk, &snapshot) {
5855 drop(snapshot);
5856 let mut revert_changes = HashMap::default();
5857 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
5858 if !revert_changes.is_empty() {
5859 self.revert(revert_changes, cx)
5860 }
5861 }
5862 }
5863
5864 pub fn open_active_item_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
5865 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
5866 let project_path = buffer.read(cx).project_path(cx)?;
5867 let project = self.project.as_ref()?.read(cx);
5868 let entry = project.entry_for_path(&project_path, cx)?;
5869 let parent = match &entry.canonical_path {
5870 Some(canonical_path) => canonical_path.to_path_buf(),
5871 None => project.absolute_path(&project_path, cx)?,
5872 }
5873 .parent()?
5874 .to_path_buf();
5875 Some(parent)
5876 }) {
5877 cx.dispatch_action(OpenTerminal { working_directory }.boxed_clone());
5878 }
5879 }
5880
5881 fn gather_revert_changes(
5882 &mut self,
5883 selections: &[Selection<Point>],
5884 cx: &mut ViewContext<'_, Editor>,
5885 ) -> HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>> {
5886 let mut revert_changes = HashMap::default();
5887 let snapshot = self.snapshot(cx);
5888 for hunk in hunks_for_selections(&snapshot, selections) {
5889 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
5890 }
5891 revert_changes
5892 }
5893
5894 pub fn prepare_revert_change(
5895 &mut self,
5896 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
5897 hunk: &MultiBufferDiffHunk,
5898 cx: &AppContext,
5899 ) -> Option<()> {
5900 let buffer = self.buffer.read(cx).buffer(hunk.buffer_id)?;
5901 let buffer = buffer.read(cx);
5902 let change_set = &self.diff_map.diff_bases.get(&hunk.buffer_id)?.change_set;
5903 let original_text = change_set
5904 .read(cx)
5905 .base_text
5906 .as_ref()?
5907 .read(cx)
5908 .as_rope()
5909 .slice(hunk.diff_base_byte_range.clone());
5910 let buffer_snapshot = buffer.snapshot();
5911 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
5912 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
5913 probe
5914 .0
5915 .start
5916 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
5917 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
5918 }) {
5919 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
5920 Some(())
5921 } else {
5922 None
5923 }
5924 }
5925
5926 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
5927 self.manipulate_lines(cx, |lines| lines.reverse())
5928 }
5929
5930 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
5931 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
5932 }
5933
5934 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
5935 where
5936 Fn: FnMut(&mut Vec<&str>),
5937 {
5938 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5939 let buffer = self.buffer.read(cx).snapshot(cx);
5940
5941 let mut edits = Vec::new();
5942
5943 let selections = self.selections.all::<Point>(cx);
5944 let mut selections = selections.iter().peekable();
5945 let mut contiguous_row_selections = Vec::new();
5946 let mut new_selections = Vec::new();
5947 let mut added_lines = 0;
5948 let mut removed_lines = 0;
5949
5950 while let Some(selection) = selections.next() {
5951 let (start_row, end_row) = consume_contiguous_rows(
5952 &mut contiguous_row_selections,
5953 selection,
5954 &display_map,
5955 &mut selections,
5956 );
5957
5958 let start_point = Point::new(start_row.0, 0);
5959 let end_point = Point::new(
5960 end_row.previous_row().0,
5961 buffer.line_len(end_row.previous_row()),
5962 );
5963 let text = buffer
5964 .text_for_range(start_point..end_point)
5965 .collect::<String>();
5966
5967 let mut lines = text.split('\n').collect_vec();
5968
5969 let lines_before = lines.len();
5970 callback(&mut lines);
5971 let lines_after = lines.len();
5972
5973 edits.push((start_point..end_point, lines.join("\n")));
5974
5975 // Selections must change based on added and removed line count
5976 let start_row =
5977 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
5978 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
5979 new_selections.push(Selection {
5980 id: selection.id,
5981 start: start_row,
5982 end: end_row,
5983 goal: SelectionGoal::None,
5984 reversed: selection.reversed,
5985 });
5986
5987 if lines_after > lines_before {
5988 added_lines += lines_after - lines_before;
5989 } else if lines_before > lines_after {
5990 removed_lines += lines_before - lines_after;
5991 }
5992 }
5993
5994 self.transact(cx, |this, cx| {
5995 let buffer = this.buffer.update(cx, |buffer, cx| {
5996 buffer.edit(edits, None, cx);
5997 buffer.snapshot(cx)
5998 });
5999
6000 // Recalculate offsets on newly edited buffer
6001 let new_selections = new_selections
6002 .iter()
6003 .map(|s| {
6004 let start_point = Point::new(s.start.0, 0);
6005 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
6006 Selection {
6007 id: s.id,
6008 start: buffer.point_to_offset(start_point),
6009 end: buffer.point_to_offset(end_point),
6010 goal: s.goal,
6011 reversed: s.reversed,
6012 }
6013 })
6014 .collect();
6015
6016 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6017 s.select(new_selections);
6018 });
6019
6020 this.request_autoscroll(Autoscroll::fit(), cx);
6021 });
6022 }
6023
6024 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
6025 self.manipulate_text(cx, |text| text.to_uppercase())
6026 }
6027
6028 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
6029 self.manipulate_text(cx, |text| text.to_lowercase())
6030 }
6031
6032 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
6033 self.manipulate_text(cx, |text| {
6034 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6035 // https://github.com/rutrum/convert-case/issues/16
6036 text.split('\n')
6037 .map(|line| line.to_case(Case::Title))
6038 .join("\n")
6039 })
6040 }
6041
6042 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
6043 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
6044 }
6045
6046 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
6047 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
6048 }
6049
6050 pub fn convert_to_upper_camel_case(
6051 &mut self,
6052 _: &ConvertToUpperCamelCase,
6053 cx: &mut ViewContext<Self>,
6054 ) {
6055 self.manipulate_text(cx, |text| {
6056 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6057 // https://github.com/rutrum/convert-case/issues/16
6058 text.split('\n')
6059 .map(|line| line.to_case(Case::UpperCamel))
6060 .join("\n")
6061 })
6062 }
6063
6064 pub fn convert_to_lower_camel_case(
6065 &mut self,
6066 _: &ConvertToLowerCamelCase,
6067 cx: &mut ViewContext<Self>,
6068 ) {
6069 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
6070 }
6071
6072 pub fn convert_to_opposite_case(
6073 &mut self,
6074 _: &ConvertToOppositeCase,
6075 cx: &mut ViewContext<Self>,
6076 ) {
6077 self.manipulate_text(cx, |text| {
6078 text.chars()
6079 .fold(String::with_capacity(text.len()), |mut t, c| {
6080 if c.is_uppercase() {
6081 t.extend(c.to_lowercase());
6082 } else {
6083 t.extend(c.to_uppercase());
6084 }
6085 t
6086 })
6087 })
6088 }
6089
6090 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6091 where
6092 Fn: FnMut(&str) -> String,
6093 {
6094 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6095 let buffer = self.buffer.read(cx).snapshot(cx);
6096
6097 let mut new_selections = Vec::new();
6098 let mut edits = Vec::new();
6099 let mut selection_adjustment = 0i32;
6100
6101 for selection in self.selections.all::<usize>(cx) {
6102 let selection_is_empty = selection.is_empty();
6103
6104 let (start, end) = if selection_is_empty {
6105 let word_range = movement::surrounding_word(
6106 &display_map,
6107 selection.start.to_display_point(&display_map),
6108 );
6109 let start = word_range.start.to_offset(&display_map, Bias::Left);
6110 let end = word_range.end.to_offset(&display_map, Bias::Left);
6111 (start, end)
6112 } else {
6113 (selection.start, selection.end)
6114 };
6115
6116 let text = buffer.text_for_range(start..end).collect::<String>();
6117 let old_length = text.len() as i32;
6118 let text = callback(&text);
6119
6120 new_selections.push(Selection {
6121 start: (start as i32 - selection_adjustment) as usize,
6122 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
6123 goal: SelectionGoal::None,
6124 ..selection
6125 });
6126
6127 selection_adjustment += old_length - text.len() as i32;
6128
6129 edits.push((start..end, text));
6130 }
6131
6132 self.transact(cx, |this, cx| {
6133 this.buffer.update(cx, |buffer, cx| {
6134 buffer.edit(edits, None, cx);
6135 });
6136
6137 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6138 s.select(new_selections);
6139 });
6140
6141 this.request_autoscroll(Autoscroll::fit(), cx);
6142 });
6143 }
6144
6145 pub fn duplicate(&mut self, upwards: bool, whole_lines: bool, cx: &mut ViewContext<Self>) {
6146 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6147 let buffer = &display_map.buffer_snapshot;
6148 let selections = self.selections.all::<Point>(cx);
6149
6150 let mut edits = Vec::new();
6151 let mut selections_iter = selections.iter().peekable();
6152 while let Some(selection) = selections_iter.next() {
6153 let mut rows = selection.spanned_rows(false, &display_map);
6154 // duplicate line-wise
6155 if whole_lines || selection.start == selection.end {
6156 // Avoid duplicating the same lines twice.
6157 while let Some(next_selection) = selections_iter.peek() {
6158 let next_rows = next_selection.spanned_rows(false, &display_map);
6159 if next_rows.start < rows.end {
6160 rows.end = next_rows.end;
6161 selections_iter.next().unwrap();
6162 } else {
6163 break;
6164 }
6165 }
6166
6167 // Copy the text from the selected row region and splice it either at the start
6168 // or end of the region.
6169 let start = Point::new(rows.start.0, 0);
6170 let end = Point::new(
6171 rows.end.previous_row().0,
6172 buffer.line_len(rows.end.previous_row()),
6173 );
6174 let text = buffer
6175 .text_for_range(start..end)
6176 .chain(Some("\n"))
6177 .collect::<String>();
6178 let insert_location = if upwards {
6179 Point::new(rows.end.0, 0)
6180 } else {
6181 start
6182 };
6183 edits.push((insert_location..insert_location, text));
6184 } else {
6185 // duplicate character-wise
6186 let start = selection.start;
6187 let end = selection.end;
6188 let text = buffer.text_for_range(start..end).collect::<String>();
6189 edits.push((selection.end..selection.end, text));
6190 }
6191 }
6192
6193 self.transact(cx, |this, cx| {
6194 this.buffer.update(cx, |buffer, cx| {
6195 buffer.edit(edits, None, cx);
6196 });
6197
6198 this.request_autoscroll(Autoscroll::fit(), cx);
6199 });
6200 }
6201
6202 pub fn duplicate_line_up(&mut self, _: &DuplicateLineUp, cx: &mut ViewContext<Self>) {
6203 self.duplicate(true, true, cx);
6204 }
6205
6206 pub fn duplicate_line_down(&mut self, _: &DuplicateLineDown, cx: &mut ViewContext<Self>) {
6207 self.duplicate(false, true, cx);
6208 }
6209
6210 pub fn duplicate_selection(&mut self, _: &DuplicateSelection, cx: &mut ViewContext<Self>) {
6211 self.duplicate(false, false, cx);
6212 }
6213
6214 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
6215 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6216 let buffer = self.buffer.read(cx).snapshot(cx);
6217
6218 let mut edits = Vec::new();
6219 let mut unfold_ranges = Vec::new();
6220 let mut refold_creases = Vec::new();
6221
6222 let selections = self.selections.all::<Point>(cx);
6223 let mut selections = selections.iter().peekable();
6224 let mut contiguous_row_selections = Vec::new();
6225 let mut new_selections = Vec::new();
6226
6227 while let Some(selection) = selections.next() {
6228 // Find all the selections that span a contiguous row range
6229 let (start_row, end_row) = consume_contiguous_rows(
6230 &mut contiguous_row_selections,
6231 selection,
6232 &display_map,
6233 &mut selections,
6234 );
6235
6236 // Move the text spanned by the row range to be before the line preceding the row range
6237 if start_row.0 > 0 {
6238 let range_to_move = Point::new(
6239 start_row.previous_row().0,
6240 buffer.line_len(start_row.previous_row()),
6241 )
6242 ..Point::new(
6243 end_row.previous_row().0,
6244 buffer.line_len(end_row.previous_row()),
6245 );
6246 let insertion_point = display_map
6247 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
6248 .0;
6249
6250 // Don't move lines across excerpts
6251 if buffer
6252 .excerpt_boundaries_in_range((
6253 Bound::Excluded(insertion_point),
6254 Bound::Included(range_to_move.end),
6255 ))
6256 .next()
6257 .is_none()
6258 {
6259 let text = buffer
6260 .text_for_range(range_to_move.clone())
6261 .flat_map(|s| s.chars())
6262 .skip(1)
6263 .chain(['\n'])
6264 .collect::<String>();
6265
6266 edits.push((
6267 buffer.anchor_after(range_to_move.start)
6268 ..buffer.anchor_before(range_to_move.end),
6269 String::new(),
6270 ));
6271 let insertion_anchor = buffer.anchor_after(insertion_point);
6272 edits.push((insertion_anchor..insertion_anchor, text));
6273
6274 let row_delta = range_to_move.start.row - insertion_point.row + 1;
6275
6276 // Move selections up
6277 new_selections.extend(contiguous_row_selections.drain(..).map(
6278 |mut selection| {
6279 selection.start.row -= row_delta;
6280 selection.end.row -= row_delta;
6281 selection
6282 },
6283 ));
6284
6285 // Move folds up
6286 unfold_ranges.push(range_to_move.clone());
6287 for fold in display_map.folds_in_range(
6288 buffer.anchor_before(range_to_move.start)
6289 ..buffer.anchor_after(range_to_move.end),
6290 ) {
6291 let mut start = fold.range.start.to_point(&buffer);
6292 let mut end = fold.range.end.to_point(&buffer);
6293 start.row -= row_delta;
6294 end.row -= row_delta;
6295 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
6296 }
6297 }
6298 }
6299
6300 // If we didn't move line(s), preserve the existing selections
6301 new_selections.append(&mut contiguous_row_selections);
6302 }
6303
6304 self.transact(cx, |this, cx| {
6305 this.unfold_ranges(&unfold_ranges, true, true, cx);
6306 this.buffer.update(cx, |buffer, cx| {
6307 for (range, text) in edits {
6308 buffer.edit([(range, text)], None, cx);
6309 }
6310 });
6311 this.fold_creases(refold_creases, true, cx);
6312 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6313 s.select(new_selections);
6314 })
6315 });
6316 }
6317
6318 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
6319 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6320 let buffer = self.buffer.read(cx).snapshot(cx);
6321
6322 let mut edits = Vec::new();
6323 let mut unfold_ranges = Vec::new();
6324 let mut refold_creases = Vec::new();
6325
6326 let selections = self.selections.all::<Point>(cx);
6327 let mut selections = selections.iter().peekable();
6328 let mut contiguous_row_selections = Vec::new();
6329 let mut new_selections = Vec::new();
6330
6331 while let Some(selection) = selections.next() {
6332 // Find all the selections that span a contiguous row range
6333 let (start_row, end_row) = consume_contiguous_rows(
6334 &mut contiguous_row_selections,
6335 selection,
6336 &display_map,
6337 &mut selections,
6338 );
6339
6340 // Move the text spanned by the row range to be after the last line of the row range
6341 if end_row.0 <= buffer.max_point().row {
6342 let range_to_move =
6343 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
6344 let insertion_point = display_map
6345 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
6346 .0;
6347
6348 // Don't move lines across excerpt boundaries
6349 if buffer
6350 .excerpt_boundaries_in_range((
6351 Bound::Excluded(range_to_move.start),
6352 Bound::Included(insertion_point),
6353 ))
6354 .next()
6355 .is_none()
6356 {
6357 let mut text = String::from("\n");
6358 text.extend(buffer.text_for_range(range_to_move.clone()));
6359 text.pop(); // Drop trailing newline
6360 edits.push((
6361 buffer.anchor_after(range_to_move.start)
6362 ..buffer.anchor_before(range_to_move.end),
6363 String::new(),
6364 ));
6365 let insertion_anchor = buffer.anchor_after(insertion_point);
6366 edits.push((insertion_anchor..insertion_anchor, text));
6367
6368 let row_delta = insertion_point.row - range_to_move.end.row + 1;
6369
6370 // Move selections down
6371 new_selections.extend(contiguous_row_selections.drain(..).map(
6372 |mut selection| {
6373 selection.start.row += row_delta;
6374 selection.end.row += row_delta;
6375 selection
6376 },
6377 ));
6378
6379 // Move folds down
6380 unfold_ranges.push(range_to_move.clone());
6381 for fold in display_map.folds_in_range(
6382 buffer.anchor_before(range_to_move.start)
6383 ..buffer.anchor_after(range_to_move.end),
6384 ) {
6385 let mut start = fold.range.start.to_point(&buffer);
6386 let mut end = fold.range.end.to_point(&buffer);
6387 start.row += row_delta;
6388 end.row += row_delta;
6389 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
6390 }
6391 }
6392 }
6393
6394 // If we didn't move line(s), preserve the existing selections
6395 new_selections.append(&mut contiguous_row_selections);
6396 }
6397
6398 self.transact(cx, |this, cx| {
6399 this.unfold_ranges(&unfold_ranges, true, true, cx);
6400 this.buffer.update(cx, |buffer, cx| {
6401 for (range, text) in edits {
6402 buffer.edit([(range, text)], None, cx);
6403 }
6404 });
6405 this.fold_creases(refold_creases, true, cx);
6406 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
6407 });
6408 }
6409
6410 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
6411 let text_layout_details = &self.text_layout_details(cx);
6412 self.transact(cx, |this, cx| {
6413 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6414 let mut edits: Vec<(Range<usize>, String)> = Default::default();
6415 let line_mode = s.line_mode;
6416 s.move_with(|display_map, selection| {
6417 if !selection.is_empty() || line_mode {
6418 return;
6419 }
6420
6421 let mut head = selection.head();
6422 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
6423 if head.column() == display_map.line_len(head.row()) {
6424 transpose_offset = display_map
6425 .buffer_snapshot
6426 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6427 }
6428
6429 if transpose_offset == 0 {
6430 return;
6431 }
6432
6433 *head.column_mut() += 1;
6434 head = display_map.clip_point(head, Bias::Right);
6435 let goal = SelectionGoal::HorizontalPosition(
6436 display_map
6437 .x_for_display_point(head, text_layout_details)
6438 .into(),
6439 );
6440 selection.collapse_to(head, goal);
6441
6442 let transpose_start = display_map
6443 .buffer_snapshot
6444 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6445 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
6446 let transpose_end = display_map
6447 .buffer_snapshot
6448 .clip_offset(transpose_offset + 1, Bias::Right);
6449 if let Some(ch) =
6450 display_map.buffer_snapshot.chars_at(transpose_start).next()
6451 {
6452 edits.push((transpose_start..transpose_offset, String::new()));
6453 edits.push((transpose_end..transpose_end, ch.to_string()));
6454 }
6455 }
6456 });
6457 edits
6458 });
6459 this.buffer
6460 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
6461 let selections = this.selections.all::<usize>(cx);
6462 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6463 s.select(selections);
6464 });
6465 });
6466 }
6467
6468 pub fn rewrap(&mut self, _: &Rewrap, cx: &mut ViewContext<Self>) {
6469 self.rewrap_impl(IsVimMode::No, cx)
6470 }
6471
6472 pub fn rewrap_impl(&mut self, is_vim_mode: IsVimMode, cx: &mut ViewContext<Self>) {
6473 let buffer = self.buffer.read(cx).snapshot(cx);
6474 let selections = self.selections.all::<Point>(cx);
6475 let mut selections = selections.iter().peekable();
6476
6477 let mut edits = Vec::new();
6478 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
6479
6480 while let Some(selection) = selections.next() {
6481 let mut start_row = selection.start.row;
6482 let mut end_row = selection.end.row;
6483
6484 // Skip selections that overlap with a range that has already been rewrapped.
6485 let selection_range = start_row..end_row;
6486 if rewrapped_row_ranges
6487 .iter()
6488 .any(|range| range.overlaps(&selection_range))
6489 {
6490 continue;
6491 }
6492
6493 let mut should_rewrap = is_vim_mode == IsVimMode::Yes;
6494
6495 if let Some(language_scope) = buffer.language_scope_at(selection.head()) {
6496 match language_scope.language_name().0.as_ref() {
6497 "Markdown" | "Plain Text" => {
6498 should_rewrap = true;
6499 }
6500 _ => {}
6501 }
6502 }
6503
6504 let tab_size = buffer.settings_at(selection.head(), cx).tab_size;
6505
6506 // Since not all lines in the selection may be at the same indent
6507 // level, choose the indent size that is the most common between all
6508 // of the lines.
6509 //
6510 // If there is a tie, we use the deepest indent.
6511 let (indent_size, indent_end) = {
6512 let mut indent_size_occurrences = HashMap::default();
6513 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
6514
6515 for row in start_row..=end_row {
6516 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
6517 rows_by_indent_size.entry(indent).or_default().push(row);
6518 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
6519 }
6520
6521 let indent_size = indent_size_occurrences
6522 .into_iter()
6523 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
6524 .map(|(indent, _)| indent)
6525 .unwrap_or_default();
6526 let row = rows_by_indent_size[&indent_size][0];
6527 let indent_end = Point::new(row, indent_size.len);
6528
6529 (indent_size, indent_end)
6530 };
6531
6532 let mut line_prefix = indent_size.chars().collect::<String>();
6533
6534 if let Some(comment_prefix) =
6535 buffer
6536 .language_scope_at(selection.head())
6537 .and_then(|language| {
6538 language
6539 .line_comment_prefixes()
6540 .iter()
6541 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
6542 .cloned()
6543 })
6544 {
6545 line_prefix.push_str(&comment_prefix);
6546 should_rewrap = true;
6547 }
6548
6549 if !should_rewrap {
6550 continue;
6551 }
6552
6553 if selection.is_empty() {
6554 'expand_upwards: while start_row > 0 {
6555 let prev_row = start_row - 1;
6556 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
6557 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
6558 {
6559 start_row = prev_row;
6560 } else {
6561 break 'expand_upwards;
6562 }
6563 }
6564
6565 'expand_downwards: while end_row < buffer.max_point().row {
6566 let next_row = end_row + 1;
6567 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
6568 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
6569 {
6570 end_row = next_row;
6571 } else {
6572 break 'expand_downwards;
6573 }
6574 }
6575 }
6576
6577 let start = Point::new(start_row, 0);
6578 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
6579 let selection_text = buffer.text_for_range(start..end).collect::<String>();
6580 let Some(lines_without_prefixes) = selection_text
6581 .lines()
6582 .map(|line| {
6583 line.strip_prefix(&line_prefix)
6584 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
6585 .ok_or_else(|| {
6586 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
6587 })
6588 })
6589 .collect::<Result<Vec<_>, _>>()
6590 .log_err()
6591 else {
6592 continue;
6593 };
6594
6595 let wrap_column = buffer
6596 .settings_at(Point::new(start_row, 0), cx)
6597 .preferred_line_length as usize;
6598 let wrapped_text = wrap_with_prefix(
6599 line_prefix,
6600 lines_without_prefixes.join(" "),
6601 wrap_column,
6602 tab_size,
6603 );
6604
6605 // TODO: should always use char-based diff while still supporting cursor behavior that
6606 // matches vim.
6607 let diff = match is_vim_mode {
6608 IsVimMode::Yes => TextDiff::from_lines(&selection_text, &wrapped_text),
6609 IsVimMode::No => TextDiff::from_chars(&selection_text, &wrapped_text),
6610 };
6611 let mut offset = start.to_offset(&buffer);
6612 let mut moved_since_edit = true;
6613
6614 for change in diff.iter_all_changes() {
6615 let value = change.value();
6616 match change.tag() {
6617 ChangeTag::Equal => {
6618 offset += value.len();
6619 moved_since_edit = true;
6620 }
6621 ChangeTag::Delete => {
6622 let start = buffer.anchor_after(offset);
6623 let end = buffer.anchor_before(offset + value.len());
6624
6625 if moved_since_edit {
6626 edits.push((start..end, String::new()));
6627 } else {
6628 edits.last_mut().unwrap().0.end = end;
6629 }
6630
6631 offset += value.len();
6632 moved_since_edit = false;
6633 }
6634 ChangeTag::Insert => {
6635 if moved_since_edit {
6636 let anchor = buffer.anchor_after(offset);
6637 edits.push((anchor..anchor, value.to_string()));
6638 } else {
6639 edits.last_mut().unwrap().1.push_str(value);
6640 }
6641
6642 moved_since_edit = false;
6643 }
6644 }
6645 }
6646
6647 rewrapped_row_ranges.push(start_row..=end_row);
6648 }
6649
6650 self.buffer
6651 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
6652 }
6653
6654 pub fn cut_common(&mut self, cx: &mut ViewContext<Self>) -> ClipboardItem {
6655 let mut text = String::new();
6656 let buffer = self.buffer.read(cx).snapshot(cx);
6657 let mut selections = self.selections.all::<Point>(cx);
6658 let mut clipboard_selections = Vec::with_capacity(selections.len());
6659 {
6660 let max_point = buffer.max_point();
6661 let mut is_first = true;
6662 for selection in &mut selections {
6663 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6664 if is_entire_line {
6665 selection.start = Point::new(selection.start.row, 0);
6666 if !selection.is_empty() && selection.end.column == 0 {
6667 selection.end = cmp::min(max_point, selection.end);
6668 } else {
6669 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
6670 }
6671 selection.goal = SelectionGoal::None;
6672 }
6673 if is_first {
6674 is_first = false;
6675 } else {
6676 text += "\n";
6677 }
6678 let mut len = 0;
6679 for chunk in buffer.text_for_range(selection.start..selection.end) {
6680 text.push_str(chunk);
6681 len += chunk.len();
6682 }
6683 clipboard_selections.push(ClipboardSelection {
6684 len,
6685 is_entire_line,
6686 first_line_indent: buffer
6687 .indent_size_for_line(MultiBufferRow(selection.start.row))
6688 .len,
6689 });
6690 }
6691 }
6692
6693 self.transact(cx, |this, cx| {
6694 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6695 s.select(selections);
6696 });
6697 this.insert("", cx);
6698 });
6699 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
6700 }
6701
6702 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
6703 let item = self.cut_common(cx);
6704 cx.write_to_clipboard(item);
6705 }
6706
6707 pub fn kill_ring_cut(&mut self, _: &KillRingCut, cx: &mut ViewContext<Self>) {
6708 self.change_selections(None, cx, |s| {
6709 s.move_with(|snapshot, sel| {
6710 if sel.is_empty() {
6711 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
6712 }
6713 });
6714 });
6715 let item = self.cut_common(cx);
6716 cx.set_global(KillRing(item))
6717 }
6718
6719 pub fn kill_ring_yank(&mut self, _: &KillRingYank, cx: &mut ViewContext<Self>) {
6720 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
6721 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
6722 (kill_ring.text().to_string(), kill_ring.metadata_json())
6723 } else {
6724 return;
6725 }
6726 } else {
6727 return;
6728 };
6729 self.do_paste(&text, metadata, false, cx);
6730 }
6731
6732 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
6733 let selections = self.selections.all::<Point>(cx);
6734 let buffer = self.buffer.read(cx).read(cx);
6735 let mut text = String::new();
6736
6737 let mut clipboard_selections = Vec::with_capacity(selections.len());
6738 {
6739 let max_point = buffer.max_point();
6740 let mut is_first = true;
6741 for selection in selections.iter() {
6742 let mut start = selection.start;
6743 let mut end = selection.end;
6744 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6745 if is_entire_line {
6746 start = Point::new(start.row, 0);
6747 end = cmp::min(max_point, Point::new(end.row + 1, 0));
6748 }
6749 if is_first {
6750 is_first = false;
6751 } else {
6752 text += "\n";
6753 }
6754 let mut len = 0;
6755 for chunk in buffer.text_for_range(start..end) {
6756 text.push_str(chunk);
6757 len += chunk.len();
6758 }
6759 clipboard_selections.push(ClipboardSelection {
6760 len,
6761 is_entire_line,
6762 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
6763 });
6764 }
6765 }
6766
6767 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
6768 text,
6769 clipboard_selections,
6770 ));
6771 }
6772
6773 pub fn do_paste(
6774 &mut self,
6775 text: &String,
6776 clipboard_selections: Option<Vec<ClipboardSelection>>,
6777 handle_entire_lines: bool,
6778 cx: &mut ViewContext<Self>,
6779 ) {
6780 if self.read_only(cx) {
6781 return;
6782 }
6783
6784 let clipboard_text = Cow::Borrowed(text);
6785
6786 self.transact(cx, |this, cx| {
6787 if let Some(mut clipboard_selections) = clipboard_selections {
6788 let old_selections = this.selections.all::<usize>(cx);
6789 let all_selections_were_entire_line =
6790 clipboard_selections.iter().all(|s| s.is_entire_line);
6791 let first_selection_indent_column =
6792 clipboard_selections.first().map(|s| s.first_line_indent);
6793 if clipboard_selections.len() != old_selections.len() {
6794 clipboard_selections.drain(..);
6795 }
6796 let cursor_offset = this.selections.last::<usize>(cx).head();
6797 let mut auto_indent_on_paste = true;
6798
6799 this.buffer.update(cx, |buffer, cx| {
6800 let snapshot = buffer.read(cx);
6801 auto_indent_on_paste =
6802 snapshot.settings_at(cursor_offset, cx).auto_indent_on_paste;
6803
6804 let mut start_offset = 0;
6805 let mut edits = Vec::new();
6806 let mut original_indent_columns = Vec::new();
6807 for (ix, selection) in old_selections.iter().enumerate() {
6808 let to_insert;
6809 let entire_line;
6810 let original_indent_column;
6811 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
6812 let end_offset = start_offset + clipboard_selection.len;
6813 to_insert = &clipboard_text[start_offset..end_offset];
6814 entire_line = clipboard_selection.is_entire_line;
6815 start_offset = end_offset + 1;
6816 original_indent_column = Some(clipboard_selection.first_line_indent);
6817 } else {
6818 to_insert = clipboard_text.as_str();
6819 entire_line = all_selections_were_entire_line;
6820 original_indent_column = first_selection_indent_column
6821 }
6822
6823 // If the corresponding selection was empty when this slice of the
6824 // clipboard text was written, then the entire line containing the
6825 // selection was copied. If this selection is also currently empty,
6826 // then paste the line before the current line of the buffer.
6827 let range = if selection.is_empty() && handle_entire_lines && entire_line {
6828 let column = selection.start.to_point(&snapshot).column as usize;
6829 let line_start = selection.start - column;
6830 line_start..line_start
6831 } else {
6832 selection.range()
6833 };
6834
6835 edits.push((range, to_insert));
6836 original_indent_columns.extend(original_indent_column);
6837 }
6838 drop(snapshot);
6839
6840 buffer.edit(
6841 edits,
6842 if auto_indent_on_paste {
6843 Some(AutoindentMode::Block {
6844 original_indent_columns,
6845 })
6846 } else {
6847 None
6848 },
6849 cx,
6850 );
6851 });
6852
6853 let selections = this.selections.all::<usize>(cx);
6854 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6855 } else {
6856 this.insert(&clipboard_text, cx);
6857 }
6858 });
6859 }
6860
6861 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
6862 if let Some(item) = cx.read_from_clipboard() {
6863 let entries = item.entries();
6864
6865 match entries.first() {
6866 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
6867 // of all the pasted entries.
6868 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
6869 .do_paste(
6870 clipboard_string.text(),
6871 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
6872 true,
6873 cx,
6874 ),
6875 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, cx),
6876 }
6877 }
6878 }
6879
6880 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
6881 if self.read_only(cx) {
6882 return;
6883 }
6884
6885 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
6886 if let Some((selections, _)) =
6887 self.selection_history.transaction(transaction_id).cloned()
6888 {
6889 self.change_selections(None, cx, |s| {
6890 s.select_anchors(selections.to_vec());
6891 });
6892 }
6893 self.request_autoscroll(Autoscroll::fit(), cx);
6894 self.unmark_text(cx);
6895 self.refresh_inline_completion(true, false, cx);
6896 cx.emit(EditorEvent::Edited { transaction_id });
6897 cx.emit(EditorEvent::TransactionUndone { transaction_id });
6898 }
6899 }
6900
6901 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
6902 if self.read_only(cx) {
6903 return;
6904 }
6905
6906 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
6907 if let Some((_, Some(selections))) =
6908 self.selection_history.transaction(transaction_id).cloned()
6909 {
6910 self.change_selections(None, cx, |s| {
6911 s.select_anchors(selections.to_vec());
6912 });
6913 }
6914 self.request_autoscroll(Autoscroll::fit(), cx);
6915 self.unmark_text(cx);
6916 self.refresh_inline_completion(true, false, cx);
6917 cx.emit(EditorEvent::Edited { transaction_id });
6918 }
6919 }
6920
6921 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
6922 self.buffer
6923 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
6924 }
6925
6926 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut ViewContext<Self>) {
6927 self.buffer
6928 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
6929 }
6930
6931 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
6932 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6933 let line_mode = s.line_mode;
6934 s.move_with(|map, selection| {
6935 let cursor = if selection.is_empty() && !line_mode {
6936 movement::left(map, selection.start)
6937 } else {
6938 selection.start
6939 };
6940 selection.collapse_to(cursor, SelectionGoal::None);
6941 });
6942 })
6943 }
6944
6945 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
6946 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6947 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
6948 })
6949 }
6950
6951 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
6952 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6953 let line_mode = s.line_mode;
6954 s.move_with(|map, selection| {
6955 let cursor = if selection.is_empty() && !line_mode {
6956 movement::right(map, selection.end)
6957 } else {
6958 selection.end
6959 };
6960 selection.collapse_to(cursor, SelectionGoal::None)
6961 });
6962 })
6963 }
6964
6965 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
6966 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6967 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
6968 })
6969 }
6970
6971 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
6972 if self.take_rename(true, cx).is_some() {
6973 return;
6974 }
6975
6976 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6977 cx.propagate();
6978 return;
6979 }
6980
6981 let text_layout_details = &self.text_layout_details(cx);
6982 let selection_count = self.selections.count();
6983 let first_selection = self.selections.first_anchor();
6984
6985 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6986 let line_mode = s.line_mode;
6987 s.move_with(|map, selection| {
6988 if !selection.is_empty() && !line_mode {
6989 selection.goal = SelectionGoal::None;
6990 }
6991 let (cursor, goal) = movement::up(
6992 map,
6993 selection.start,
6994 selection.goal,
6995 false,
6996 text_layout_details,
6997 );
6998 selection.collapse_to(cursor, goal);
6999 });
7000 });
7001
7002 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7003 {
7004 cx.propagate();
7005 }
7006 }
7007
7008 pub fn move_up_by_lines(&mut self, action: &MoveUpByLines, cx: &mut ViewContext<Self>) {
7009 if self.take_rename(true, cx).is_some() {
7010 return;
7011 }
7012
7013 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7014 cx.propagate();
7015 return;
7016 }
7017
7018 let text_layout_details = &self.text_layout_details(cx);
7019
7020 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7021 let line_mode = s.line_mode;
7022 s.move_with(|map, selection| {
7023 if !selection.is_empty() && !line_mode {
7024 selection.goal = SelectionGoal::None;
7025 }
7026 let (cursor, goal) = movement::up_by_rows(
7027 map,
7028 selection.start,
7029 action.lines,
7030 selection.goal,
7031 false,
7032 text_layout_details,
7033 );
7034 selection.collapse_to(cursor, goal);
7035 });
7036 })
7037 }
7038
7039 pub fn move_down_by_lines(&mut self, action: &MoveDownByLines, cx: &mut ViewContext<Self>) {
7040 if self.take_rename(true, cx).is_some() {
7041 return;
7042 }
7043
7044 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7045 cx.propagate();
7046 return;
7047 }
7048
7049 let text_layout_details = &self.text_layout_details(cx);
7050
7051 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7052 let line_mode = s.line_mode;
7053 s.move_with(|map, selection| {
7054 if !selection.is_empty() && !line_mode {
7055 selection.goal = SelectionGoal::None;
7056 }
7057 let (cursor, goal) = movement::down_by_rows(
7058 map,
7059 selection.start,
7060 action.lines,
7061 selection.goal,
7062 false,
7063 text_layout_details,
7064 );
7065 selection.collapse_to(cursor, goal);
7066 });
7067 })
7068 }
7069
7070 pub fn select_down_by_lines(&mut self, action: &SelectDownByLines, cx: &mut ViewContext<Self>) {
7071 let text_layout_details = &self.text_layout_details(cx);
7072 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7073 s.move_heads_with(|map, head, goal| {
7074 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
7075 })
7076 })
7077 }
7078
7079 pub fn select_up_by_lines(&mut self, action: &SelectUpByLines, cx: &mut ViewContext<Self>) {
7080 let text_layout_details = &self.text_layout_details(cx);
7081 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7082 s.move_heads_with(|map, head, goal| {
7083 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
7084 })
7085 })
7086 }
7087
7088 pub fn select_page_up(&mut self, _: &SelectPageUp, cx: &mut ViewContext<Self>) {
7089 let Some(row_count) = self.visible_row_count() else {
7090 return;
7091 };
7092
7093 let text_layout_details = &self.text_layout_details(cx);
7094
7095 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7096 s.move_heads_with(|map, head, goal| {
7097 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
7098 })
7099 })
7100 }
7101
7102 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
7103 if self.take_rename(true, cx).is_some() {
7104 return;
7105 }
7106
7107 if self
7108 .context_menu
7109 .borrow_mut()
7110 .as_mut()
7111 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
7112 .unwrap_or(false)
7113 {
7114 return;
7115 }
7116
7117 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7118 cx.propagate();
7119 return;
7120 }
7121
7122 let Some(row_count) = self.visible_row_count() else {
7123 return;
7124 };
7125
7126 let autoscroll = if action.center_cursor {
7127 Autoscroll::center()
7128 } else {
7129 Autoscroll::fit()
7130 };
7131
7132 let text_layout_details = &self.text_layout_details(cx);
7133
7134 self.change_selections(Some(autoscroll), cx, |s| {
7135 let line_mode = s.line_mode;
7136 s.move_with(|map, selection| {
7137 if !selection.is_empty() && !line_mode {
7138 selection.goal = SelectionGoal::None;
7139 }
7140 let (cursor, goal) = movement::up_by_rows(
7141 map,
7142 selection.end,
7143 row_count,
7144 selection.goal,
7145 false,
7146 text_layout_details,
7147 );
7148 selection.collapse_to(cursor, goal);
7149 });
7150 });
7151 }
7152
7153 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
7154 let text_layout_details = &self.text_layout_details(cx);
7155 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7156 s.move_heads_with(|map, head, goal| {
7157 movement::up(map, head, goal, false, text_layout_details)
7158 })
7159 })
7160 }
7161
7162 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
7163 self.take_rename(true, cx);
7164
7165 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7166 cx.propagate();
7167 return;
7168 }
7169
7170 let text_layout_details = &self.text_layout_details(cx);
7171 let selection_count = self.selections.count();
7172 let first_selection = self.selections.first_anchor();
7173
7174 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7175 let line_mode = s.line_mode;
7176 s.move_with(|map, selection| {
7177 if !selection.is_empty() && !line_mode {
7178 selection.goal = SelectionGoal::None;
7179 }
7180 let (cursor, goal) = movement::down(
7181 map,
7182 selection.end,
7183 selection.goal,
7184 false,
7185 text_layout_details,
7186 );
7187 selection.collapse_to(cursor, goal);
7188 });
7189 });
7190
7191 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7192 {
7193 cx.propagate();
7194 }
7195 }
7196
7197 pub fn select_page_down(&mut self, _: &SelectPageDown, cx: &mut ViewContext<Self>) {
7198 let Some(row_count) = self.visible_row_count() else {
7199 return;
7200 };
7201
7202 let text_layout_details = &self.text_layout_details(cx);
7203
7204 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7205 s.move_heads_with(|map, head, goal| {
7206 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
7207 })
7208 })
7209 }
7210
7211 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
7212 if self.take_rename(true, cx).is_some() {
7213 return;
7214 }
7215
7216 if self
7217 .context_menu
7218 .borrow_mut()
7219 .as_mut()
7220 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
7221 .unwrap_or(false)
7222 {
7223 return;
7224 }
7225
7226 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7227 cx.propagate();
7228 return;
7229 }
7230
7231 let Some(row_count) = self.visible_row_count() else {
7232 return;
7233 };
7234
7235 let autoscroll = if action.center_cursor {
7236 Autoscroll::center()
7237 } else {
7238 Autoscroll::fit()
7239 };
7240
7241 let text_layout_details = &self.text_layout_details(cx);
7242 self.change_selections(Some(autoscroll), cx, |s| {
7243 let line_mode = s.line_mode;
7244 s.move_with(|map, selection| {
7245 if !selection.is_empty() && !line_mode {
7246 selection.goal = SelectionGoal::None;
7247 }
7248 let (cursor, goal) = movement::down_by_rows(
7249 map,
7250 selection.end,
7251 row_count,
7252 selection.goal,
7253 false,
7254 text_layout_details,
7255 );
7256 selection.collapse_to(cursor, goal);
7257 });
7258 });
7259 }
7260
7261 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
7262 let text_layout_details = &self.text_layout_details(cx);
7263 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7264 s.move_heads_with(|map, head, goal| {
7265 movement::down(map, head, goal, false, text_layout_details)
7266 })
7267 });
7268 }
7269
7270 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
7271 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
7272 context_menu.select_first(self.completion_provider.as_deref(), cx);
7273 }
7274 }
7275
7276 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
7277 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
7278 context_menu.select_prev(self.completion_provider.as_deref(), cx);
7279 }
7280 }
7281
7282 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
7283 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
7284 context_menu.select_next(self.completion_provider.as_deref(), cx);
7285 }
7286 }
7287
7288 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
7289 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
7290 context_menu.select_last(self.completion_provider.as_deref(), cx);
7291 }
7292 }
7293
7294 pub fn move_to_previous_word_start(
7295 &mut self,
7296 _: &MoveToPreviousWordStart,
7297 cx: &mut ViewContext<Self>,
7298 ) {
7299 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7300 s.move_cursors_with(|map, head, _| {
7301 (
7302 movement::previous_word_start(map, head),
7303 SelectionGoal::None,
7304 )
7305 });
7306 })
7307 }
7308
7309 pub fn move_to_previous_subword_start(
7310 &mut self,
7311 _: &MoveToPreviousSubwordStart,
7312 cx: &mut ViewContext<Self>,
7313 ) {
7314 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7315 s.move_cursors_with(|map, head, _| {
7316 (
7317 movement::previous_subword_start(map, head),
7318 SelectionGoal::None,
7319 )
7320 });
7321 })
7322 }
7323
7324 pub fn select_to_previous_word_start(
7325 &mut self,
7326 _: &SelectToPreviousWordStart,
7327 cx: &mut ViewContext<Self>,
7328 ) {
7329 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7330 s.move_heads_with(|map, head, _| {
7331 (
7332 movement::previous_word_start(map, head),
7333 SelectionGoal::None,
7334 )
7335 });
7336 })
7337 }
7338
7339 pub fn select_to_previous_subword_start(
7340 &mut self,
7341 _: &SelectToPreviousSubwordStart,
7342 cx: &mut ViewContext<Self>,
7343 ) {
7344 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7345 s.move_heads_with(|map, head, _| {
7346 (
7347 movement::previous_subword_start(map, head),
7348 SelectionGoal::None,
7349 )
7350 });
7351 })
7352 }
7353
7354 pub fn delete_to_previous_word_start(
7355 &mut self,
7356 action: &DeleteToPreviousWordStart,
7357 cx: &mut ViewContext<Self>,
7358 ) {
7359 self.transact(cx, |this, cx| {
7360 this.select_autoclose_pair(cx);
7361 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7362 let line_mode = s.line_mode;
7363 s.move_with(|map, selection| {
7364 if selection.is_empty() && !line_mode {
7365 let cursor = if action.ignore_newlines {
7366 movement::previous_word_start(map, selection.head())
7367 } else {
7368 movement::previous_word_start_or_newline(map, selection.head())
7369 };
7370 selection.set_head(cursor, SelectionGoal::None);
7371 }
7372 });
7373 });
7374 this.insert("", cx);
7375 });
7376 }
7377
7378 pub fn delete_to_previous_subword_start(
7379 &mut self,
7380 _: &DeleteToPreviousSubwordStart,
7381 cx: &mut ViewContext<Self>,
7382 ) {
7383 self.transact(cx, |this, cx| {
7384 this.select_autoclose_pair(cx);
7385 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7386 let line_mode = s.line_mode;
7387 s.move_with(|map, selection| {
7388 if selection.is_empty() && !line_mode {
7389 let cursor = movement::previous_subword_start(map, selection.head());
7390 selection.set_head(cursor, SelectionGoal::None);
7391 }
7392 });
7393 });
7394 this.insert("", cx);
7395 });
7396 }
7397
7398 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
7399 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7400 s.move_cursors_with(|map, head, _| {
7401 (movement::next_word_end(map, head), SelectionGoal::None)
7402 });
7403 })
7404 }
7405
7406 pub fn move_to_next_subword_end(
7407 &mut self,
7408 _: &MoveToNextSubwordEnd,
7409 cx: &mut ViewContext<Self>,
7410 ) {
7411 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7412 s.move_cursors_with(|map, head, _| {
7413 (movement::next_subword_end(map, head), SelectionGoal::None)
7414 });
7415 })
7416 }
7417
7418 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
7419 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7420 s.move_heads_with(|map, head, _| {
7421 (movement::next_word_end(map, head), SelectionGoal::None)
7422 });
7423 })
7424 }
7425
7426 pub fn select_to_next_subword_end(
7427 &mut self,
7428 _: &SelectToNextSubwordEnd,
7429 cx: &mut ViewContext<Self>,
7430 ) {
7431 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7432 s.move_heads_with(|map, head, _| {
7433 (movement::next_subword_end(map, head), SelectionGoal::None)
7434 });
7435 })
7436 }
7437
7438 pub fn delete_to_next_word_end(
7439 &mut self,
7440 action: &DeleteToNextWordEnd,
7441 cx: &mut ViewContext<Self>,
7442 ) {
7443 self.transact(cx, |this, cx| {
7444 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7445 let line_mode = s.line_mode;
7446 s.move_with(|map, selection| {
7447 if selection.is_empty() && !line_mode {
7448 let cursor = if action.ignore_newlines {
7449 movement::next_word_end(map, selection.head())
7450 } else {
7451 movement::next_word_end_or_newline(map, selection.head())
7452 };
7453 selection.set_head(cursor, SelectionGoal::None);
7454 }
7455 });
7456 });
7457 this.insert("", cx);
7458 });
7459 }
7460
7461 pub fn delete_to_next_subword_end(
7462 &mut self,
7463 _: &DeleteToNextSubwordEnd,
7464 cx: &mut ViewContext<Self>,
7465 ) {
7466 self.transact(cx, |this, cx| {
7467 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7468 s.move_with(|map, selection| {
7469 if selection.is_empty() {
7470 let cursor = movement::next_subword_end(map, selection.head());
7471 selection.set_head(cursor, SelectionGoal::None);
7472 }
7473 });
7474 });
7475 this.insert("", cx);
7476 });
7477 }
7478
7479 pub fn move_to_beginning_of_line(
7480 &mut self,
7481 action: &MoveToBeginningOfLine,
7482 cx: &mut ViewContext<Self>,
7483 ) {
7484 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7485 s.move_cursors_with(|map, head, _| {
7486 (
7487 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7488 SelectionGoal::None,
7489 )
7490 });
7491 })
7492 }
7493
7494 pub fn select_to_beginning_of_line(
7495 &mut self,
7496 action: &SelectToBeginningOfLine,
7497 cx: &mut ViewContext<Self>,
7498 ) {
7499 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7500 s.move_heads_with(|map, head, _| {
7501 (
7502 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7503 SelectionGoal::None,
7504 )
7505 });
7506 });
7507 }
7508
7509 pub fn delete_to_beginning_of_line(
7510 &mut self,
7511 _: &DeleteToBeginningOfLine,
7512 cx: &mut ViewContext<Self>,
7513 ) {
7514 self.transact(cx, |this, cx| {
7515 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7516 s.move_with(|_, selection| {
7517 selection.reversed = true;
7518 });
7519 });
7520
7521 this.select_to_beginning_of_line(
7522 &SelectToBeginningOfLine {
7523 stop_at_soft_wraps: false,
7524 },
7525 cx,
7526 );
7527 this.backspace(&Backspace, cx);
7528 });
7529 }
7530
7531 pub fn move_to_end_of_line(&mut self, action: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
7532 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7533 s.move_cursors_with(|map, head, _| {
7534 (
7535 movement::line_end(map, head, action.stop_at_soft_wraps),
7536 SelectionGoal::None,
7537 )
7538 });
7539 })
7540 }
7541
7542 pub fn select_to_end_of_line(
7543 &mut self,
7544 action: &SelectToEndOfLine,
7545 cx: &mut ViewContext<Self>,
7546 ) {
7547 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7548 s.move_heads_with(|map, head, _| {
7549 (
7550 movement::line_end(map, head, action.stop_at_soft_wraps),
7551 SelectionGoal::None,
7552 )
7553 });
7554 })
7555 }
7556
7557 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
7558 self.transact(cx, |this, cx| {
7559 this.select_to_end_of_line(
7560 &SelectToEndOfLine {
7561 stop_at_soft_wraps: false,
7562 },
7563 cx,
7564 );
7565 this.delete(&Delete, cx);
7566 });
7567 }
7568
7569 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
7570 self.transact(cx, |this, cx| {
7571 this.select_to_end_of_line(
7572 &SelectToEndOfLine {
7573 stop_at_soft_wraps: false,
7574 },
7575 cx,
7576 );
7577 this.cut(&Cut, cx);
7578 });
7579 }
7580
7581 pub fn move_to_start_of_paragraph(
7582 &mut self,
7583 _: &MoveToStartOfParagraph,
7584 cx: &mut ViewContext<Self>,
7585 ) {
7586 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7587 cx.propagate();
7588 return;
7589 }
7590
7591 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7592 s.move_with(|map, selection| {
7593 selection.collapse_to(
7594 movement::start_of_paragraph(map, selection.head(), 1),
7595 SelectionGoal::None,
7596 )
7597 });
7598 })
7599 }
7600
7601 pub fn move_to_end_of_paragraph(
7602 &mut self,
7603 _: &MoveToEndOfParagraph,
7604 cx: &mut ViewContext<Self>,
7605 ) {
7606 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7607 cx.propagate();
7608 return;
7609 }
7610
7611 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7612 s.move_with(|map, selection| {
7613 selection.collapse_to(
7614 movement::end_of_paragraph(map, selection.head(), 1),
7615 SelectionGoal::None,
7616 )
7617 });
7618 })
7619 }
7620
7621 pub fn select_to_start_of_paragraph(
7622 &mut self,
7623 _: &SelectToStartOfParagraph,
7624 cx: &mut ViewContext<Self>,
7625 ) {
7626 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7627 cx.propagate();
7628 return;
7629 }
7630
7631 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7632 s.move_heads_with(|map, head, _| {
7633 (
7634 movement::start_of_paragraph(map, head, 1),
7635 SelectionGoal::None,
7636 )
7637 });
7638 })
7639 }
7640
7641 pub fn select_to_end_of_paragraph(
7642 &mut self,
7643 _: &SelectToEndOfParagraph,
7644 cx: &mut ViewContext<Self>,
7645 ) {
7646 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7647 cx.propagate();
7648 return;
7649 }
7650
7651 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7652 s.move_heads_with(|map, head, _| {
7653 (
7654 movement::end_of_paragraph(map, head, 1),
7655 SelectionGoal::None,
7656 )
7657 });
7658 })
7659 }
7660
7661 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
7662 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7663 cx.propagate();
7664 return;
7665 }
7666
7667 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7668 s.select_ranges(vec![0..0]);
7669 });
7670 }
7671
7672 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
7673 let mut selection = self.selections.last::<Point>(cx);
7674 selection.set_head(Point::zero(), SelectionGoal::None);
7675
7676 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7677 s.select(vec![selection]);
7678 });
7679 }
7680
7681 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
7682 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7683 cx.propagate();
7684 return;
7685 }
7686
7687 let cursor = self.buffer.read(cx).read(cx).len();
7688 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7689 s.select_ranges(vec![cursor..cursor])
7690 });
7691 }
7692
7693 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
7694 self.nav_history = nav_history;
7695 }
7696
7697 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
7698 self.nav_history.as_ref()
7699 }
7700
7701 fn push_to_nav_history(
7702 &mut self,
7703 cursor_anchor: Anchor,
7704 new_position: Option<Point>,
7705 cx: &mut ViewContext<Self>,
7706 ) {
7707 if let Some(nav_history) = self.nav_history.as_mut() {
7708 let buffer = self.buffer.read(cx).read(cx);
7709 let cursor_position = cursor_anchor.to_point(&buffer);
7710 let scroll_state = self.scroll_manager.anchor();
7711 let scroll_top_row = scroll_state.top_row(&buffer);
7712 drop(buffer);
7713
7714 if let Some(new_position) = new_position {
7715 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
7716 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
7717 return;
7718 }
7719 }
7720
7721 nav_history.push(
7722 Some(NavigationData {
7723 cursor_anchor,
7724 cursor_position,
7725 scroll_anchor: scroll_state,
7726 scroll_top_row,
7727 }),
7728 cx,
7729 );
7730 }
7731 }
7732
7733 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
7734 let buffer = self.buffer.read(cx).snapshot(cx);
7735 let mut selection = self.selections.first::<usize>(cx);
7736 selection.set_head(buffer.len(), SelectionGoal::None);
7737 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7738 s.select(vec![selection]);
7739 });
7740 }
7741
7742 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
7743 let end = self.buffer.read(cx).read(cx).len();
7744 self.change_selections(None, cx, |s| {
7745 s.select_ranges(vec![0..end]);
7746 });
7747 }
7748
7749 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
7750 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7751 let mut selections = self.selections.all::<Point>(cx);
7752 let max_point = display_map.buffer_snapshot.max_point();
7753 for selection in &mut selections {
7754 let rows = selection.spanned_rows(true, &display_map);
7755 selection.start = Point::new(rows.start.0, 0);
7756 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
7757 selection.reversed = false;
7758 }
7759 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7760 s.select(selections);
7761 });
7762 }
7763
7764 pub fn split_selection_into_lines(
7765 &mut self,
7766 _: &SplitSelectionIntoLines,
7767 cx: &mut ViewContext<Self>,
7768 ) {
7769 let mut to_unfold = Vec::new();
7770 let mut new_selection_ranges = Vec::new();
7771 {
7772 let selections = self.selections.all::<Point>(cx);
7773 let buffer = self.buffer.read(cx).read(cx);
7774 for selection in selections {
7775 for row in selection.start.row..selection.end.row {
7776 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
7777 new_selection_ranges.push(cursor..cursor);
7778 }
7779 new_selection_ranges.push(selection.end..selection.end);
7780 to_unfold.push(selection.start..selection.end);
7781 }
7782 }
7783 self.unfold_ranges(&to_unfold, true, true, cx);
7784 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7785 s.select_ranges(new_selection_ranges);
7786 });
7787 }
7788
7789 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
7790 self.add_selection(true, cx);
7791 }
7792
7793 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
7794 self.add_selection(false, cx);
7795 }
7796
7797 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
7798 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7799 let mut selections = self.selections.all::<Point>(cx);
7800 let text_layout_details = self.text_layout_details(cx);
7801 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
7802 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
7803 let range = oldest_selection.display_range(&display_map).sorted();
7804
7805 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
7806 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
7807 let positions = start_x.min(end_x)..start_x.max(end_x);
7808
7809 selections.clear();
7810 let mut stack = Vec::new();
7811 for row in range.start.row().0..=range.end.row().0 {
7812 if let Some(selection) = self.selections.build_columnar_selection(
7813 &display_map,
7814 DisplayRow(row),
7815 &positions,
7816 oldest_selection.reversed,
7817 &text_layout_details,
7818 ) {
7819 stack.push(selection.id);
7820 selections.push(selection);
7821 }
7822 }
7823
7824 if above {
7825 stack.reverse();
7826 }
7827
7828 AddSelectionsState { above, stack }
7829 });
7830
7831 let last_added_selection = *state.stack.last().unwrap();
7832 let mut new_selections = Vec::new();
7833 if above == state.above {
7834 let end_row = if above {
7835 DisplayRow(0)
7836 } else {
7837 display_map.max_point().row()
7838 };
7839
7840 'outer: for selection in selections {
7841 if selection.id == last_added_selection {
7842 let range = selection.display_range(&display_map).sorted();
7843 debug_assert_eq!(range.start.row(), range.end.row());
7844 let mut row = range.start.row();
7845 let positions =
7846 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
7847 px(start)..px(end)
7848 } else {
7849 let start_x =
7850 display_map.x_for_display_point(range.start, &text_layout_details);
7851 let end_x =
7852 display_map.x_for_display_point(range.end, &text_layout_details);
7853 start_x.min(end_x)..start_x.max(end_x)
7854 };
7855
7856 while row != end_row {
7857 if above {
7858 row.0 -= 1;
7859 } else {
7860 row.0 += 1;
7861 }
7862
7863 if let Some(new_selection) = self.selections.build_columnar_selection(
7864 &display_map,
7865 row,
7866 &positions,
7867 selection.reversed,
7868 &text_layout_details,
7869 ) {
7870 state.stack.push(new_selection.id);
7871 if above {
7872 new_selections.push(new_selection);
7873 new_selections.push(selection);
7874 } else {
7875 new_selections.push(selection);
7876 new_selections.push(new_selection);
7877 }
7878
7879 continue 'outer;
7880 }
7881 }
7882 }
7883
7884 new_selections.push(selection);
7885 }
7886 } else {
7887 new_selections = selections;
7888 new_selections.retain(|s| s.id != last_added_selection);
7889 state.stack.pop();
7890 }
7891
7892 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7893 s.select(new_selections);
7894 });
7895 if state.stack.len() > 1 {
7896 self.add_selections_state = Some(state);
7897 }
7898 }
7899
7900 pub fn select_next_match_internal(
7901 &mut self,
7902 display_map: &DisplaySnapshot,
7903 replace_newest: bool,
7904 autoscroll: Option<Autoscroll>,
7905 cx: &mut ViewContext<Self>,
7906 ) -> Result<()> {
7907 fn select_next_match_ranges(
7908 this: &mut Editor,
7909 range: Range<usize>,
7910 replace_newest: bool,
7911 auto_scroll: Option<Autoscroll>,
7912 cx: &mut ViewContext<Editor>,
7913 ) {
7914 this.unfold_ranges(&[range.clone()], false, true, cx);
7915 this.change_selections(auto_scroll, cx, |s| {
7916 if replace_newest {
7917 s.delete(s.newest_anchor().id);
7918 }
7919 s.insert_range(range.clone());
7920 });
7921 }
7922
7923 let buffer = &display_map.buffer_snapshot;
7924 let mut selections = self.selections.all::<usize>(cx);
7925 if let Some(mut select_next_state) = self.select_next_state.take() {
7926 let query = &select_next_state.query;
7927 if !select_next_state.done {
7928 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
7929 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
7930 let mut next_selected_range = None;
7931
7932 let bytes_after_last_selection =
7933 buffer.bytes_in_range(last_selection.end..buffer.len());
7934 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
7935 let query_matches = query
7936 .stream_find_iter(bytes_after_last_selection)
7937 .map(|result| (last_selection.end, result))
7938 .chain(
7939 query
7940 .stream_find_iter(bytes_before_first_selection)
7941 .map(|result| (0, result)),
7942 );
7943
7944 for (start_offset, query_match) in query_matches {
7945 let query_match = query_match.unwrap(); // can only fail due to I/O
7946 let offset_range =
7947 start_offset + query_match.start()..start_offset + query_match.end();
7948 let display_range = offset_range.start.to_display_point(display_map)
7949 ..offset_range.end.to_display_point(display_map);
7950
7951 if !select_next_state.wordwise
7952 || (!movement::is_inside_word(display_map, display_range.start)
7953 && !movement::is_inside_word(display_map, display_range.end))
7954 {
7955 // TODO: This is n^2, because we might check all the selections
7956 if !selections
7957 .iter()
7958 .any(|selection| selection.range().overlaps(&offset_range))
7959 {
7960 next_selected_range = Some(offset_range);
7961 break;
7962 }
7963 }
7964 }
7965
7966 if let Some(next_selected_range) = next_selected_range {
7967 select_next_match_ranges(
7968 self,
7969 next_selected_range,
7970 replace_newest,
7971 autoscroll,
7972 cx,
7973 );
7974 } else {
7975 select_next_state.done = true;
7976 }
7977 }
7978
7979 self.select_next_state = Some(select_next_state);
7980 } else {
7981 let mut only_carets = true;
7982 let mut same_text_selected = true;
7983 let mut selected_text = None;
7984
7985 let mut selections_iter = selections.iter().peekable();
7986 while let Some(selection) = selections_iter.next() {
7987 if selection.start != selection.end {
7988 only_carets = false;
7989 }
7990
7991 if same_text_selected {
7992 if selected_text.is_none() {
7993 selected_text =
7994 Some(buffer.text_for_range(selection.range()).collect::<String>());
7995 }
7996
7997 if let Some(next_selection) = selections_iter.peek() {
7998 if next_selection.range().len() == selection.range().len() {
7999 let next_selected_text = buffer
8000 .text_for_range(next_selection.range())
8001 .collect::<String>();
8002 if Some(next_selected_text) != selected_text {
8003 same_text_selected = false;
8004 selected_text = None;
8005 }
8006 } else {
8007 same_text_selected = false;
8008 selected_text = None;
8009 }
8010 }
8011 }
8012 }
8013
8014 if only_carets {
8015 for selection in &mut selections {
8016 let word_range = movement::surrounding_word(
8017 display_map,
8018 selection.start.to_display_point(display_map),
8019 );
8020 selection.start = word_range.start.to_offset(display_map, Bias::Left);
8021 selection.end = word_range.end.to_offset(display_map, Bias::Left);
8022 selection.goal = SelectionGoal::None;
8023 selection.reversed = false;
8024 select_next_match_ranges(
8025 self,
8026 selection.start..selection.end,
8027 replace_newest,
8028 autoscroll,
8029 cx,
8030 );
8031 }
8032
8033 if selections.len() == 1 {
8034 let selection = selections
8035 .last()
8036 .expect("ensured that there's only one selection");
8037 let query = buffer
8038 .text_for_range(selection.start..selection.end)
8039 .collect::<String>();
8040 let is_empty = query.is_empty();
8041 let select_state = SelectNextState {
8042 query: AhoCorasick::new(&[query])?,
8043 wordwise: true,
8044 done: is_empty,
8045 };
8046 self.select_next_state = Some(select_state);
8047 } else {
8048 self.select_next_state = None;
8049 }
8050 } else if let Some(selected_text) = selected_text {
8051 self.select_next_state = Some(SelectNextState {
8052 query: AhoCorasick::new(&[selected_text])?,
8053 wordwise: false,
8054 done: false,
8055 });
8056 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
8057 }
8058 }
8059 Ok(())
8060 }
8061
8062 pub fn select_all_matches(
8063 &mut self,
8064 _action: &SelectAllMatches,
8065 cx: &mut ViewContext<Self>,
8066 ) -> Result<()> {
8067 self.push_to_selection_history();
8068 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8069
8070 self.select_next_match_internal(&display_map, false, None, cx)?;
8071 let Some(select_next_state) = self.select_next_state.as_mut() else {
8072 return Ok(());
8073 };
8074 if select_next_state.done {
8075 return Ok(());
8076 }
8077
8078 let mut new_selections = self.selections.all::<usize>(cx);
8079
8080 let buffer = &display_map.buffer_snapshot;
8081 let query_matches = select_next_state
8082 .query
8083 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
8084
8085 for query_match in query_matches {
8086 let query_match = query_match.unwrap(); // can only fail due to I/O
8087 let offset_range = query_match.start()..query_match.end();
8088 let display_range = offset_range.start.to_display_point(&display_map)
8089 ..offset_range.end.to_display_point(&display_map);
8090
8091 if !select_next_state.wordwise
8092 || (!movement::is_inside_word(&display_map, display_range.start)
8093 && !movement::is_inside_word(&display_map, display_range.end))
8094 {
8095 self.selections.change_with(cx, |selections| {
8096 new_selections.push(Selection {
8097 id: selections.new_selection_id(),
8098 start: offset_range.start,
8099 end: offset_range.end,
8100 reversed: false,
8101 goal: SelectionGoal::None,
8102 });
8103 });
8104 }
8105 }
8106
8107 new_selections.sort_by_key(|selection| selection.start);
8108 let mut ix = 0;
8109 while ix + 1 < new_selections.len() {
8110 let current_selection = &new_selections[ix];
8111 let next_selection = &new_selections[ix + 1];
8112 if current_selection.range().overlaps(&next_selection.range()) {
8113 if current_selection.id < next_selection.id {
8114 new_selections.remove(ix + 1);
8115 } else {
8116 new_selections.remove(ix);
8117 }
8118 } else {
8119 ix += 1;
8120 }
8121 }
8122
8123 select_next_state.done = true;
8124 self.unfold_ranges(
8125 &new_selections
8126 .iter()
8127 .map(|selection| selection.range())
8128 .collect::<Vec<_>>(),
8129 false,
8130 false,
8131 cx,
8132 );
8133 self.change_selections(Some(Autoscroll::fit()), cx, |selections| {
8134 selections.select(new_selections)
8135 });
8136
8137 Ok(())
8138 }
8139
8140 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
8141 self.push_to_selection_history();
8142 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8143 self.select_next_match_internal(
8144 &display_map,
8145 action.replace_newest,
8146 Some(Autoscroll::newest()),
8147 cx,
8148 )?;
8149 Ok(())
8150 }
8151
8152 pub fn select_previous(
8153 &mut self,
8154 action: &SelectPrevious,
8155 cx: &mut ViewContext<Self>,
8156 ) -> Result<()> {
8157 self.push_to_selection_history();
8158 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8159 let buffer = &display_map.buffer_snapshot;
8160 let mut selections = self.selections.all::<usize>(cx);
8161 if let Some(mut select_prev_state) = self.select_prev_state.take() {
8162 let query = &select_prev_state.query;
8163 if !select_prev_state.done {
8164 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
8165 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
8166 let mut next_selected_range = None;
8167 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
8168 let bytes_before_last_selection =
8169 buffer.reversed_bytes_in_range(0..last_selection.start);
8170 let bytes_after_first_selection =
8171 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
8172 let query_matches = query
8173 .stream_find_iter(bytes_before_last_selection)
8174 .map(|result| (last_selection.start, result))
8175 .chain(
8176 query
8177 .stream_find_iter(bytes_after_first_selection)
8178 .map(|result| (buffer.len(), result)),
8179 );
8180 for (end_offset, query_match) in query_matches {
8181 let query_match = query_match.unwrap(); // can only fail due to I/O
8182 let offset_range =
8183 end_offset - query_match.end()..end_offset - query_match.start();
8184 let display_range = offset_range.start.to_display_point(&display_map)
8185 ..offset_range.end.to_display_point(&display_map);
8186
8187 if !select_prev_state.wordwise
8188 || (!movement::is_inside_word(&display_map, display_range.start)
8189 && !movement::is_inside_word(&display_map, display_range.end))
8190 {
8191 next_selected_range = Some(offset_range);
8192 break;
8193 }
8194 }
8195
8196 if let Some(next_selected_range) = next_selected_range {
8197 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
8198 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8199 if action.replace_newest {
8200 s.delete(s.newest_anchor().id);
8201 }
8202 s.insert_range(next_selected_range);
8203 });
8204 } else {
8205 select_prev_state.done = true;
8206 }
8207 }
8208
8209 self.select_prev_state = Some(select_prev_state);
8210 } else {
8211 let mut only_carets = true;
8212 let mut same_text_selected = true;
8213 let mut selected_text = None;
8214
8215 let mut selections_iter = selections.iter().peekable();
8216 while let Some(selection) = selections_iter.next() {
8217 if selection.start != selection.end {
8218 only_carets = false;
8219 }
8220
8221 if same_text_selected {
8222 if selected_text.is_none() {
8223 selected_text =
8224 Some(buffer.text_for_range(selection.range()).collect::<String>());
8225 }
8226
8227 if let Some(next_selection) = selections_iter.peek() {
8228 if next_selection.range().len() == selection.range().len() {
8229 let next_selected_text = buffer
8230 .text_for_range(next_selection.range())
8231 .collect::<String>();
8232 if Some(next_selected_text) != selected_text {
8233 same_text_selected = false;
8234 selected_text = None;
8235 }
8236 } else {
8237 same_text_selected = false;
8238 selected_text = None;
8239 }
8240 }
8241 }
8242 }
8243
8244 if only_carets {
8245 for selection in &mut selections {
8246 let word_range = movement::surrounding_word(
8247 &display_map,
8248 selection.start.to_display_point(&display_map),
8249 );
8250 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
8251 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
8252 selection.goal = SelectionGoal::None;
8253 selection.reversed = false;
8254 }
8255 if selections.len() == 1 {
8256 let selection = selections
8257 .last()
8258 .expect("ensured that there's only one selection");
8259 let query = buffer
8260 .text_for_range(selection.start..selection.end)
8261 .collect::<String>();
8262 let is_empty = query.is_empty();
8263 let select_state = SelectNextState {
8264 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
8265 wordwise: true,
8266 done: is_empty,
8267 };
8268 self.select_prev_state = Some(select_state);
8269 } else {
8270 self.select_prev_state = None;
8271 }
8272
8273 self.unfold_ranges(
8274 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
8275 false,
8276 true,
8277 cx,
8278 );
8279 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8280 s.select(selections);
8281 });
8282 } else if let Some(selected_text) = selected_text {
8283 self.select_prev_state = Some(SelectNextState {
8284 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
8285 wordwise: false,
8286 done: false,
8287 });
8288 self.select_previous(action, cx)?;
8289 }
8290 }
8291 Ok(())
8292 }
8293
8294 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
8295 if self.read_only(cx) {
8296 return;
8297 }
8298 let text_layout_details = &self.text_layout_details(cx);
8299 self.transact(cx, |this, cx| {
8300 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
8301 let mut edits = Vec::new();
8302 let mut selection_edit_ranges = Vec::new();
8303 let mut last_toggled_row = None;
8304 let snapshot = this.buffer.read(cx).read(cx);
8305 let empty_str: Arc<str> = Arc::default();
8306 let mut suffixes_inserted = Vec::new();
8307 let ignore_indent = action.ignore_indent;
8308
8309 fn comment_prefix_range(
8310 snapshot: &MultiBufferSnapshot,
8311 row: MultiBufferRow,
8312 comment_prefix: &str,
8313 comment_prefix_whitespace: &str,
8314 ignore_indent: bool,
8315 ) -> Range<Point> {
8316 let indent_size = if ignore_indent {
8317 0
8318 } else {
8319 snapshot.indent_size_for_line(row).len
8320 };
8321
8322 let start = Point::new(row.0, indent_size);
8323
8324 let mut line_bytes = snapshot
8325 .bytes_in_range(start..snapshot.max_point())
8326 .flatten()
8327 .copied();
8328
8329 // If this line currently begins with the line comment prefix, then record
8330 // the range containing the prefix.
8331 if line_bytes
8332 .by_ref()
8333 .take(comment_prefix.len())
8334 .eq(comment_prefix.bytes())
8335 {
8336 // Include any whitespace that matches the comment prefix.
8337 let matching_whitespace_len = line_bytes
8338 .zip(comment_prefix_whitespace.bytes())
8339 .take_while(|(a, b)| a == b)
8340 .count() as u32;
8341 let end = Point::new(
8342 start.row,
8343 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
8344 );
8345 start..end
8346 } else {
8347 start..start
8348 }
8349 }
8350
8351 fn comment_suffix_range(
8352 snapshot: &MultiBufferSnapshot,
8353 row: MultiBufferRow,
8354 comment_suffix: &str,
8355 comment_suffix_has_leading_space: bool,
8356 ) -> Range<Point> {
8357 let end = Point::new(row.0, snapshot.line_len(row));
8358 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
8359
8360 let mut line_end_bytes = snapshot
8361 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
8362 .flatten()
8363 .copied();
8364
8365 let leading_space_len = if suffix_start_column > 0
8366 && line_end_bytes.next() == Some(b' ')
8367 && comment_suffix_has_leading_space
8368 {
8369 1
8370 } else {
8371 0
8372 };
8373
8374 // If this line currently begins with the line comment prefix, then record
8375 // the range containing the prefix.
8376 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
8377 let start = Point::new(end.row, suffix_start_column - leading_space_len);
8378 start..end
8379 } else {
8380 end..end
8381 }
8382 }
8383
8384 // TODO: Handle selections that cross excerpts
8385 for selection in &mut selections {
8386 let start_column = snapshot
8387 .indent_size_for_line(MultiBufferRow(selection.start.row))
8388 .len;
8389 let language = if let Some(language) =
8390 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
8391 {
8392 language
8393 } else {
8394 continue;
8395 };
8396
8397 selection_edit_ranges.clear();
8398
8399 // If multiple selections contain a given row, avoid processing that
8400 // row more than once.
8401 let mut start_row = MultiBufferRow(selection.start.row);
8402 if last_toggled_row == Some(start_row) {
8403 start_row = start_row.next_row();
8404 }
8405 let end_row =
8406 if selection.end.row > selection.start.row && selection.end.column == 0 {
8407 MultiBufferRow(selection.end.row - 1)
8408 } else {
8409 MultiBufferRow(selection.end.row)
8410 };
8411 last_toggled_row = Some(end_row);
8412
8413 if start_row > end_row {
8414 continue;
8415 }
8416
8417 // If the language has line comments, toggle those.
8418 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
8419
8420 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
8421 if ignore_indent {
8422 full_comment_prefixes = full_comment_prefixes
8423 .into_iter()
8424 .map(|s| Arc::from(s.trim_end()))
8425 .collect();
8426 }
8427
8428 if !full_comment_prefixes.is_empty() {
8429 let first_prefix = full_comment_prefixes
8430 .first()
8431 .expect("prefixes is non-empty");
8432 let prefix_trimmed_lengths = full_comment_prefixes
8433 .iter()
8434 .map(|p| p.trim_end_matches(' ').len())
8435 .collect::<SmallVec<[usize; 4]>>();
8436
8437 let mut all_selection_lines_are_comments = true;
8438
8439 for row in start_row.0..=end_row.0 {
8440 let row = MultiBufferRow(row);
8441 if start_row < end_row && snapshot.is_line_blank(row) {
8442 continue;
8443 }
8444
8445 let prefix_range = full_comment_prefixes
8446 .iter()
8447 .zip(prefix_trimmed_lengths.iter().copied())
8448 .map(|(prefix, trimmed_prefix_len)| {
8449 comment_prefix_range(
8450 snapshot.deref(),
8451 row,
8452 &prefix[..trimmed_prefix_len],
8453 &prefix[trimmed_prefix_len..],
8454 ignore_indent,
8455 )
8456 })
8457 .max_by_key(|range| range.end.column - range.start.column)
8458 .expect("prefixes is non-empty");
8459
8460 if prefix_range.is_empty() {
8461 all_selection_lines_are_comments = false;
8462 }
8463
8464 selection_edit_ranges.push(prefix_range);
8465 }
8466
8467 if all_selection_lines_are_comments {
8468 edits.extend(
8469 selection_edit_ranges
8470 .iter()
8471 .cloned()
8472 .map(|range| (range, empty_str.clone())),
8473 );
8474 } else {
8475 let min_column = selection_edit_ranges
8476 .iter()
8477 .map(|range| range.start.column)
8478 .min()
8479 .unwrap_or(0);
8480 edits.extend(selection_edit_ranges.iter().map(|range| {
8481 let position = Point::new(range.start.row, min_column);
8482 (position..position, first_prefix.clone())
8483 }));
8484 }
8485 } else if let Some((full_comment_prefix, comment_suffix)) =
8486 language.block_comment_delimiters()
8487 {
8488 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
8489 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
8490 let prefix_range = comment_prefix_range(
8491 snapshot.deref(),
8492 start_row,
8493 comment_prefix,
8494 comment_prefix_whitespace,
8495 ignore_indent,
8496 );
8497 let suffix_range = comment_suffix_range(
8498 snapshot.deref(),
8499 end_row,
8500 comment_suffix.trim_start_matches(' '),
8501 comment_suffix.starts_with(' '),
8502 );
8503
8504 if prefix_range.is_empty() || suffix_range.is_empty() {
8505 edits.push((
8506 prefix_range.start..prefix_range.start,
8507 full_comment_prefix.clone(),
8508 ));
8509 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
8510 suffixes_inserted.push((end_row, comment_suffix.len()));
8511 } else {
8512 edits.push((prefix_range, empty_str.clone()));
8513 edits.push((suffix_range, empty_str.clone()));
8514 }
8515 } else {
8516 continue;
8517 }
8518 }
8519
8520 drop(snapshot);
8521 this.buffer.update(cx, |buffer, cx| {
8522 buffer.edit(edits, None, cx);
8523 });
8524
8525 // Adjust selections so that they end before any comment suffixes that
8526 // were inserted.
8527 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
8528 let mut selections = this.selections.all::<Point>(cx);
8529 let snapshot = this.buffer.read(cx).read(cx);
8530 for selection in &mut selections {
8531 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
8532 match row.cmp(&MultiBufferRow(selection.end.row)) {
8533 Ordering::Less => {
8534 suffixes_inserted.next();
8535 continue;
8536 }
8537 Ordering::Greater => break,
8538 Ordering::Equal => {
8539 if selection.end.column == snapshot.line_len(row) {
8540 if selection.is_empty() {
8541 selection.start.column -= suffix_len as u32;
8542 }
8543 selection.end.column -= suffix_len as u32;
8544 }
8545 break;
8546 }
8547 }
8548 }
8549 }
8550
8551 drop(snapshot);
8552 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
8553
8554 let selections = this.selections.all::<Point>(cx);
8555 let selections_on_single_row = selections.windows(2).all(|selections| {
8556 selections[0].start.row == selections[1].start.row
8557 && selections[0].end.row == selections[1].end.row
8558 && selections[0].start.row == selections[0].end.row
8559 });
8560 let selections_selecting = selections
8561 .iter()
8562 .any(|selection| selection.start != selection.end);
8563 let advance_downwards = action.advance_downwards
8564 && selections_on_single_row
8565 && !selections_selecting
8566 && !matches!(this.mode, EditorMode::SingleLine { .. });
8567
8568 if advance_downwards {
8569 let snapshot = this.buffer.read(cx).snapshot(cx);
8570
8571 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8572 s.move_cursors_with(|display_snapshot, display_point, _| {
8573 let mut point = display_point.to_point(display_snapshot);
8574 point.row += 1;
8575 point = snapshot.clip_point(point, Bias::Left);
8576 let display_point = point.to_display_point(display_snapshot);
8577 let goal = SelectionGoal::HorizontalPosition(
8578 display_snapshot
8579 .x_for_display_point(display_point, text_layout_details)
8580 .into(),
8581 );
8582 (display_point, goal)
8583 })
8584 });
8585 }
8586 });
8587 }
8588
8589 pub fn select_enclosing_symbol(
8590 &mut self,
8591 _: &SelectEnclosingSymbol,
8592 cx: &mut ViewContext<Self>,
8593 ) {
8594 let buffer = self.buffer.read(cx).snapshot(cx);
8595 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8596
8597 fn update_selection(
8598 selection: &Selection<usize>,
8599 buffer_snap: &MultiBufferSnapshot,
8600 ) -> Option<Selection<usize>> {
8601 let cursor = selection.head();
8602 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
8603 for symbol in symbols.iter().rev() {
8604 let start = symbol.range.start.to_offset(buffer_snap);
8605 let end = symbol.range.end.to_offset(buffer_snap);
8606 let new_range = start..end;
8607 if start < selection.start || end > selection.end {
8608 return Some(Selection {
8609 id: selection.id,
8610 start: new_range.start,
8611 end: new_range.end,
8612 goal: SelectionGoal::None,
8613 reversed: selection.reversed,
8614 });
8615 }
8616 }
8617 None
8618 }
8619
8620 let mut selected_larger_symbol = false;
8621 let new_selections = old_selections
8622 .iter()
8623 .map(|selection| match update_selection(selection, &buffer) {
8624 Some(new_selection) => {
8625 if new_selection.range() != selection.range() {
8626 selected_larger_symbol = true;
8627 }
8628 new_selection
8629 }
8630 None => selection.clone(),
8631 })
8632 .collect::<Vec<_>>();
8633
8634 if selected_larger_symbol {
8635 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8636 s.select(new_selections);
8637 });
8638 }
8639 }
8640
8641 pub fn select_larger_syntax_node(
8642 &mut self,
8643 _: &SelectLargerSyntaxNode,
8644 cx: &mut ViewContext<Self>,
8645 ) {
8646 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8647 let buffer = self.buffer.read(cx).snapshot(cx);
8648 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8649
8650 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8651 let mut selected_larger_node = false;
8652 let new_selections = old_selections
8653 .iter()
8654 .map(|selection| {
8655 let old_range = selection.start..selection.end;
8656 let mut new_range = old_range.clone();
8657 while let Some(containing_range) =
8658 buffer.range_for_syntax_ancestor(new_range.clone())
8659 {
8660 new_range = containing_range;
8661 if !display_map.intersects_fold(new_range.start)
8662 && !display_map.intersects_fold(new_range.end)
8663 {
8664 break;
8665 }
8666 }
8667
8668 selected_larger_node |= new_range != old_range;
8669 Selection {
8670 id: selection.id,
8671 start: new_range.start,
8672 end: new_range.end,
8673 goal: SelectionGoal::None,
8674 reversed: selection.reversed,
8675 }
8676 })
8677 .collect::<Vec<_>>();
8678
8679 if selected_larger_node {
8680 stack.push(old_selections);
8681 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8682 s.select(new_selections);
8683 });
8684 }
8685 self.select_larger_syntax_node_stack = stack;
8686 }
8687
8688 pub fn select_smaller_syntax_node(
8689 &mut self,
8690 _: &SelectSmallerSyntaxNode,
8691 cx: &mut ViewContext<Self>,
8692 ) {
8693 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8694 if let Some(selections) = stack.pop() {
8695 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8696 s.select(selections.to_vec());
8697 });
8698 }
8699 self.select_larger_syntax_node_stack = stack;
8700 }
8701
8702 fn refresh_runnables(&mut self, cx: &mut ViewContext<Self>) -> Task<()> {
8703 if !EditorSettings::get_global(cx).gutter.runnables {
8704 self.clear_tasks();
8705 return Task::ready(());
8706 }
8707 let project = self.project.as_ref().map(Model::downgrade);
8708 cx.spawn(|this, mut cx| async move {
8709 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
8710 let Some(project) = project.and_then(|p| p.upgrade()) else {
8711 return;
8712 };
8713 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
8714 this.display_map.update(cx, |map, cx| map.snapshot(cx))
8715 }) else {
8716 return;
8717 };
8718
8719 let hide_runnables = project
8720 .update(&mut cx, |project, cx| {
8721 // Do not display any test indicators in non-dev server remote projects.
8722 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
8723 })
8724 .unwrap_or(true);
8725 if hide_runnables {
8726 return;
8727 }
8728 let new_rows =
8729 cx.background_executor()
8730 .spawn({
8731 let snapshot = display_snapshot.clone();
8732 async move {
8733 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
8734 }
8735 })
8736 .await;
8737 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
8738
8739 this.update(&mut cx, |this, _| {
8740 this.clear_tasks();
8741 for (key, value) in rows {
8742 this.insert_tasks(key, value);
8743 }
8744 })
8745 .ok();
8746 })
8747 }
8748 fn fetch_runnable_ranges(
8749 snapshot: &DisplaySnapshot,
8750 range: Range<Anchor>,
8751 ) -> Vec<language::RunnableRange> {
8752 snapshot.buffer_snapshot.runnable_ranges(range).collect()
8753 }
8754
8755 fn runnable_rows(
8756 project: Model<Project>,
8757 snapshot: DisplaySnapshot,
8758 runnable_ranges: Vec<RunnableRange>,
8759 mut cx: AsyncWindowContext,
8760 ) -> Vec<((BufferId, u32), RunnableTasks)> {
8761 runnable_ranges
8762 .into_iter()
8763 .filter_map(|mut runnable| {
8764 let tasks = cx
8765 .update(|cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
8766 .ok()?;
8767 if tasks.is_empty() {
8768 return None;
8769 }
8770
8771 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
8772
8773 let row = snapshot
8774 .buffer_snapshot
8775 .buffer_line_for_row(MultiBufferRow(point.row))?
8776 .1
8777 .start
8778 .row;
8779
8780 let context_range =
8781 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
8782 Some((
8783 (runnable.buffer_id, row),
8784 RunnableTasks {
8785 templates: tasks,
8786 offset: MultiBufferOffset(runnable.run_range.start),
8787 context_range,
8788 column: point.column,
8789 extra_variables: runnable.extra_captures,
8790 },
8791 ))
8792 })
8793 .collect()
8794 }
8795
8796 fn templates_with_tags(
8797 project: &Model<Project>,
8798 runnable: &mut Runnable,
8799 cx: &WindowContext<'_>,
8800 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
8801 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
8802 let (worktree_id, file) = project
8803 .buffer_for_id(runnable.buffer, cx)
8804 .and_then(|buffer| buffer.read(cx).file())
8805 .map(|file| (file.worktree_id(cx), file.clone()))
8806 .unzip();
8807
8808 (
8809 project.task_store().read(cx).task_inventory().cloned(),
8810 worktree_id,
8811 file,
8812 )
8813 });
8814
8815 let tags = mem::take(&mut runnable.tags);
8816 let mut tags: Vec<_> = tags
8817 .into_iter()
8818 .flat_map(|tag| {
8819 let tag = tag.0.clone();
8820 inventory
8821 .as_ref()
8822 .into_iter()
8823 .flat_map(|inventory| {
8824 inventory.read(cx).list_tasks(
8825 file.clone(),
8826 Some(runnable.language.clone()),
8827 worktree_id,
8828 cx,
8829 )
8830 })
8831 .filter(move |(_, template)| {
8832 template.tags.iter().any(|source_tag| source_tag == &tag)
8833 })
8834 })
8835 .sorted_by_key(|(kind, _)| kind.to_owned())
8836 .collect();
8837 if let Some((leading_tag_source, _)) = tags.first() {
8838 // Strongest source wins; if we have worktree tag binding, prefer that to
8839 // global and language bindings;
8840 // if we have a global binding, prefer that to language binding.
8841 let first_mismatch = tags
8842 .iter()
8843 .position(|(tag_source, _)| tag_source != leading_tag_source);
8844 if let Some(index) = first_mismatch {
8845 tags.truncate(index);
8846 }
8847 }
8848
8849 tags
8850 }
8851
8852 pub fn move_to_enclosing_bracket(
8853 &mut self,
8854 _: &MoveToEnclosingBracket,
8855 cx: &mut ViewContext<Self>,
8856 ) {
8857 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8858 s.move_offsets_with(|snapshot, selection| {
8859 let Some(enclosing_bracket_ranges) =
8860 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
8861 else {
8862 return;
8863 };
8864
8865 let mut best_length = usize::MAX;
8866 let mut best_inside = false;
8867 let mut best_in_bracket_range = false;
8868 let mut best_destination = None;
8869 for (open, close) in enclosing_bracket_ranges {
8870 let close = close.to_inclusive();
8871 let length = close.end() - open.start;
8872 let inside = selection.start >= open.end && selection.end <= *close.start();
8873 let in_bracket_range = open.to_inclusive().contains(&selection.head())
8874 || close.contains(&selection.head());
8875
8876 // If best is next to a bracket and current isn't, skip
8877 if !in_bracket_range && best_in_bracket_range {
8878 continue;
8879 }
8880
8881 // Prefer smaller lengths unless best is inside and current isn't
8882 if length > best_length && (best_inside || !inside) {
8883 continue;
8884 }
8885
8886 best_length = length;
8887 best_inside = inside;
8888 best_in_bracket_range = in_bracket_range;
8889 best_destination = Some(
8890 if close.contains(&selection.start) && close.contains(&selection.end) {
8891 if inside {
8892 open.end
8893 } else {
8894 open.start
8895 }
8896 } else if inside {
8897 *close.start()
8898 } else {
8899 *close.end()
8900 },
8901 );
8902 }
8903
8904 if let Some(destination) = best_destination {
8905 selection.collapse_to(destination, SelectionGoal::None);
8906 }
8907 })
8908 });
8909 }
8910
8911 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
8912 self.end_selection(cx);
8913 self.selection_history.mode = SelectionHistoryMode::Undoing;
8914 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
8915 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8916 self.select_next_state = entry.select_next_state;
8917 self.select_prev_state = entry.select_prev_state;
8918 self.add_selections_state = entry.add_selections_state;
8919 self.request_autoscroll(Autoscroll::newest(), cx);
8920 }
8921 self.selection_history.mode = SelectionHistoryMode::Normal;
8922 }
8923
8924 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
8925 self.end_selection(cx);
8926 self.selection_history.mode = SelectionHistoryMode::Redoing;
8927 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
8928 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8929 self.select_next_state = entry.select_next_state;
8930 self.select_prev_state = entry.select_prev_state;
8931 self.add_selections_state = entry.add_selections_state;
8932 self.request_autoscroll(Autoscroll::newest(), cx);
8933 }
8934 self.selection_history.mode = SelectionHistoryMode::Normal;
8935 }
8936
8937 pub fn expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
8938 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
8939 }
8940
8941 pub fn expand_excerpts_down(
8942 &mut self,
8943 action: &ExpandExcerptsDown,
8944 cx: &mut ViewContext<Self>,
8945 ) {
8946 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
8947 }
8948
8949 pub fn expand_excerpts_up(&mut self, action: &ExpandExcerptsUp, cx: &mut ViewContext<Self>) {
8950 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
8951 }
8952
8953 pub fn expand_excerpts_for_direction(
8954 &mut self,
8955 lines: u32,
8956 direction: ExpandExcerptDirection,
8957 cx: &mut ViewContext<Self>,
8958 ) {
8959 let selections = self.selections.disjoint_anchors();
8960
8961 let lines = if lines == 0 {
8962 EditorSettings::get_global(cx).expand_excerpt_lines
8963 } else {
8964 lines
8965 };
8966
8967 self.buffer.update(cx, |buffer, cx| {
8968 buffer.expand_excerpts(
8969 selections
8970 .iter()
8971 .map(|selection| selection.head().excerpt_id)
8972 .dedup(),
8973 lines,
8974 direction,
8975 cx,
8976 )
8977 })
8978 }
8979
8980 pub fn expand_excerpt(
8981 &mut self,
8982 excerpt: ExcerptId,
8983 direction: ExpandExcerptDirection,
8984 cx: &mut ViewContext<Self>,
8985 ) {
8986 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
8987 self.buffer.update(cx, |buffer, cx| {
8988 buffer.expand_excerpts([excerpt], lines, direction, cx)
8989 })
8990 }
8991
8992 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
8993 self.go_to_diagnostic_impl(Direction::Next, cx)
8994 }
8995
8996 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
8997 self.go_to_diagnostic_impl(Direction::Prev, cx)
8998 }
8999
9000 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
9001 let buffer = self.buffer.read(cx).snapshot(cx);
9002 let selection = self.selections.newest::<usize>(cx);
9003
9004 // If there is an active Diagnostic Popover jump to its diagnostic instead.
9005 if direction == Direction::Next {
9006 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
9007 let (group_id, jump_to) = popover.activation_info();
9008 if self.activate_diagnostics(group_id, cx) {
9009 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9010 let mut new_selection = s.newest_anchor().clone();
9011 new_selection.collapse_to(jump_to, SelectionGoal::None);
9012 s.select_anchors(vec![new_selection.clone()]);
9013 });
9014 }
9015 return;
9016 }
9017 }
9018
9019 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
9020 active_diagnostics
9021 .primary_range
9022 .to_offset(&buffer)
9023 .to_inclusive()
9024 });
9025 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
9026 if active_primary_range.contains(&selection.head()) {
9027 *active_primary_range.start()
9028 } else {
9029 selection.head()
9030 }
9031 } else {
9032 selection.head()
9033 };
9034 let snapshot = self.snapshot(cx);
9035 loop {
9036 let diagnostics = if direction == Direction::Prev {
9037 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
9038 } else {
9039 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
9040 }
9041 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start));
9042 let group = diagnostics
9043 // relies on diagnostics_in_range to return diagnostics with the same starting range to
9044 // be sorted in a stable way
9045 // skip until we are at current active diagnostic, if it exists
9046 .skip_while(|entry| {
9047 (match direction {
9048 Direction::Prev => entry.range.start >= search_start,
9049 Direction::Next => entry.range.start <= search_start,
9050 }) && self
9051 .active_diagnostics
9052 .as_ref()
9053 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
9054 })
9055 .find_map(|entry| {
9056 if entry.diagnostic.is_primary
9057 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
9058 && !entry.range.is_empty()
9059 // if we match with the active diagnostic, skip it
9060 && Some(entry.diagnostic.group_id)
9061 != self.active_diagnostics.as_ref().map(|d| d.group_id)
9062 {
9063 Some((entry.range, entry.diagnostic.group_id))
9064 } else {
9065 None
9066 }
9067 });
9068
9069 if let Some((primary_range, group_id)) = group {
9070 if self.activate_diagnostics(group_id, cx) {
9071 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9072 s.select(vec![Selection {
9073 id: selection.id,
9074 start: primary_range.start,
9075 end: primary_range.start,
9076 reversed: false,
9077 goal: SelectionGoal::None,
9078 }]);
9079 });
9080 }
9081 break;
9082 } else {
9083 // Cycle around to the start of the buffer, potentially moving back to the start of
9084 // the currently active diagnostic.
9085 active_primary_range.take();
9086 if direction == Direction::Prev {
9087 if search_start == buffer.len() {
9088 break;
9089 } else {
9090 search_start = buffer.len();
9091 }
9092 } else if search_start == 0 {
9093 break;
9094 } else {
9095 search_start = 0;
9096 }
9097 }
9098 }
9099 }
9100
9101 fn go_to_next_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
9102 let snapshot = self.snapshot(cx);
9103 let selection = self.selections.newest::<Point>(cx);
9104 self.go_to_hunk_after_position(&snapshot, selection.head(), cx);
9105 }
9106
9107 fn go_to_hunk_after_position(
9108 &mut self,
9109 snapshot: &EditorSnapshot,
9110 position: Point,
9111 cx: &mut ViewContext<'_, Editor>,
9112 ) -> Option<MultiBufferDiffHunk> {
9113 for (ix, position) in [position, Point::zero()].into_iter().enumerate() {
9114 if let Some(hunk) = self.go_to_next_hunk_in_direction(
9115 snapshot,
9116 position,
9117 ix > 0,
9118 snapshot.diff_map.diff_hunks_in_range(
9119 position + Point::new(1, 0)..snapshot.buffer_snapshot.max_point(),
9120 &snapshot.buffer_snapshot,
9121 ),
9122 cx,
9123 ) {
9124 return Some(hunk);
9125 }
9126 }
9127 None
9128 }
9129
9130 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
9131 let snapshot = self.snapshot(cx);
9132 let selection = self.selections.newest::<Point>(cx);
9133 self.go_to_hunk_before_position(&snapshot, selection.head(), cx);
9134 }
9135
9136 fn go_to_hunk_before_position(
9137 &mut self,
9138 snapshot: &EditorSnapshot,
9139 position: Point,
9140 cx: &mut ViewContext<'_, Editor>,
9141 ) -> Option<MultiBufferDiffHunk> {
9142 for (ix, position) in [position, snapshot.buffer_snapshot.max_point()]
9143 .into_iter()
9144 .enumerate()
9145 {
9146 if let Some(hunk) = self.go_to_next_hunk_in_direction(
9147 snapshot,
9148 position,
9149 ix > 0,
9150 snapshot
9151 .diff_map
9152 .diff_hunks_in_range_rev(Point::zero()..position, &snapshot.buffer_snapshot),
9153 cx,
9154 ) {
9155 return Some(hunk);
9156 }
9157 }
9158 None
9159 }
9160
9161 fn go_to_next_hunk_in_direction(
9162 &mut self,
9163 snapshot: &DisplaySnapshot,
9164 initial_point: Point,
9165 is_wrapped: bool,
9166 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
9167 cx: &mut ViewContext<Editor>,
9168 ) -> Option<MultiBufferDiffHunk> {
9169 let display_point = initial_point.to_display_point(snapshot);
9170 let mut hunks = hunks
9171 .map(|hunk| (diff_hunk_to_display(&hunk, snapshot), hunk))
9172 .filter(|(display_hunk, _)| {
9173 is_wrapped || !display_hunk.contains_display_row(display_point.row())
9174 })
9175 .dedup();
9176
9177 if let Some((display_hunk, hunk)) = hunks.next() {
9178 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9179 let row = display_hunk.start_display_row();
9180 let point = DisplayPoint::new(row, 0);
9181 s.select_display_ranges([point..point]);
9182 });
9183
9184 Some(hunk)
9185 } else {
9186 None
9187 }
9188 }
9189
9190 pub fn go_to_definition(
9191 &mut self,
9192 _: &GoToDefinition,
9193 cx: &mut ViewContext<Self>,
9194 ) -> Task<Result<Navigated>> {
9195 let definition = self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
9196 cx.spawn(|editor, mut cx| async move {
9197 if definition.await? == Navigated::Yes {
9198 return Ok(Navigated::Yes);
9199 }
9200 match editor.update(&mut cx, |editor, cx| {
9201 editor.find_all_references(&FindAllReferences, cx)
9202 })? {
9203 Some(references) => references.await,
9204 None => Ok(Navigated::No),
9205 }
9206 })
9207 }
9208
9209 pub fn go_to_declaration(
9210 &mut self,
9211 _: &GoToDeclaration,
9212 cx: &mut ViewContext<Self>,
9213 ) -> Task<Result<Navigated>> {
9214 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, cx)
9215 }
9216
9217 pub fn go_to_declaration_split(
9218 &mut self,
9219 _: &GoToDeclaration,
9220 cx: &mut ViewContext<Self>,
9221 ) -> Task<Result<Navigated>> {
9222 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, cx)
9223 }
9224
9225 pub fn go_to_implementation(
9226 &mut self,
9227 _: &GoToImplementation,
9228 cx: &mut ViewContext<Self>,
9229 ) -> Task<Result<Navigated>> {
9230 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx)
9231 }
9232
9233 pub fn go_to_implementation_split(
9234 &mut self,
9235 _: &GoToImplementationSplit,
9236 cx: &mut ViewContext<Self>,
9237 ) -> Task<Result<Navigated>> {
9238 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx)
9239 }
9240
9241 pub fn go_to_type_definition(
9242 &mut self,
9243 _: &GoToTypeDefinition,
9244 cx: &mut ViewContext<Self>,
9245 ) -> Task<Result<Navigated>> {
9246 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx)
9247 }
9248
9249 pub fn go_to_definition_split(
9250 &mut self,
9251 _: &GoToDefinitionSplit,
9252 cx: &mut ViewContext<Self>,
9253 ) -> Task<Result<Navigated>> {
9254 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx)
9255 }
9256
9257 pub fn go_to_type_definition_split(
9258 &mut self,
9259 _: &GoToTypeDefinitionSplit,
9260 cx: &mut ViewContext<Self>,
9261 ) -> Task<Result<Navigated>> {
9262 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx)
9263 }
9264
9265 fn go_to_definition_of_kind(
9266 &mut self,
9267 kind: GotoDefinitionKind,
9268 split: bool,
9269 cx: &mut ViewContext<Self>,
9270 ) -> Task<Result<Navigated>> {
9271 let Some(provider) = self.semantics_provider.clone() else {
9272 return Task::ready(Ok(Navigated::No));
9273 };
9274 let head = self.selections.newest::<usize>(cx).head();
9275 let buffer = self.buffer.read(cx);
9276 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
9277 text_anchor
9278 } else {
9279 return Task::ready(Ok(Navigated::No));
9280 };
9281
9282 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
9283 return Task::ready(Ok(Navigated::No));
9284 };
9285
9286 cx.spawn(|editor, mut cx| async move {
9287 let definitions = definitions.await?;
9288 let navigated = editor
9289 .update(&mut cx, |editor, cx| {
9290 editor.navigate_to_hover_links(
9291 Some(kind),
9292 definitions
9293 .into_iter()
9294 .filter(|location| {
9295 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
9296 })
9297 .map(HoverLink::Text)
9298 .collect::<Vec<_>>(),
9299 split,
9300 cx,
9301 )
9302 })?
9303 .await?;
9304 anyhow::Ok(navigated)
9305 })
9306 }
9307
9308 pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext<Self>) {
9309 let selection = self.selections.newest_anchor();
9310 let head = selection.head();
9311 let tail = selection.tail();
9312
9313 let Some((buffer, start_position)) =
9314 self.buffer.read(cx).text_anchor_for_position(head, cx)
9315 else {
9316 return;
9317 };
9318
9319 let end_position = if head != tail {
9320 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
9321 return;
9322 };
9323 Some(pos)
9324 } else {
9325 None
9326 };
9327
9328 let url_finder = cx.spawn(|editor, mut cx| async move {
9329 let url = if let Some(end_pos) = end_position {
9330 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
9331 } else {
9332 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
9333 };
9334
9335 if let Some(url) = url {
9336 editor.update(&mut cx, |_, cx| {
9337 cx.open_url(&url);
9338 })
9339 } else {
9340 Ok(())
9341 }
9342 });
9343
9344 url_finder.detach();
9345 }
9346
9347 pub fn open_file(&mut self, _: &OpenFile, cx: &mut ViewContext<Self>) {
9348 let Some(workspace) = self.workspace() else {
9349 return;
9350 };
9351
9352 let position = self.selections.newest_anchor().head();
9353
9354 let Some((buffer, buffer_position)) =
9355 self.buffer.read(cx).text_anchor_for_position(position, cx)
9356 else {
9357 return;
9358 };
9359
9360 let project = self.project.clone();
9361
9362 cx.spawn(|_, mut cx| async move {
9363 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
9364
9365 if let Some((_, path)) = result {
9366 workspace
9367 .update(&mut cx, |workspace, cx| {
9368 workspace.open_resolved_path(path, cx)
9369 })?
9370 .await?;
9371 }
9372 anyhow::Ok(())
9373 })
9374 .detach();
9375 }
9376
9377 pub(crate) fn navigate_to_hover_links(
9378 &mut self,
9379 kind: Option<GotoDefinitionKind>,
9380 mut definitions: Vec<HoverLink>,
9381 split: bool,
9382 cx: &mut ViewContext<Editor>,
9383 ) -> Task<Result<Navigated>> {
9384 // If there is one definition, just open it directly
9385 if definitions.len() == 1 {
9386 let definition = definitions.pop().unwrap();
9387
9388 enum TargetTaskResult {
9389 Location(Option<Location>),
9390 AlreadyNavigated,
9391 }
9392
9393 let target_task = match definition {
9394 HoverLink::Text(link) => {
9395 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
9396 }
9397 HoverLink::InlayHint(lsp_location, server_id) => {
9398 let computation = self.compute_target_location(lsp_location, server_id, cx);
9399 cx.background_executor().spawn(async move {
9400 let location = computation.await?;
9401 Ok(TargetTaskResult::Location(location))
9402 })
9403 }
9404 HoverLink::Url(url) => {
9405 cx.open_url(&url);
9406 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
9407 }
9408 HoverLink::File(path) => {
9409 if let Some(workspace) = self.workspace() {
9410 cx.spawn(|_, mut cx| async move {
9411 workspace
9412 .update(&mut cx, |workspace, cx| {
9413 workspace.open_resolved_path(path, cx)
9414 })?
9415 .await
9416 .map(|_| TargetTaskResult::AlreadyNavigated)
9417 })
9418 } else {
9419 Task::ready(Ok(TargetTaskResult::Location(None)))
9420 }
9421 }
9422 };
9423 cx.spawn(|editor, mut cx| async move {
9424 let target = match target_task.await.context("target resolution task")? {
9425 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
9426 TargetTaskResult::Location(None) => return Ok(Navigated::No),
9427 TargetTaskResult::Location(Some(target)) => target,
9428 };
9429
9430 editor.update(&mut cx, |editor, cx| {
9431 let Some(workspace) = editor.workspace() else {
9432 return Navigated::No;
9433 };
9434 let pane = workspace.read(cx).active_pane().clone();
9435
9436 let range = target.range.to_offset(target.buffer.read(cx));
9437 let range = editor.range_for_match(&range);
9438
9439 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
9440 let buffer = target.buffer.read(cx);
9441 let range = check_multiline_range(buffer, range);
9442 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
9443 s.select_ranges([range]);
9444 });
9445 } else {
9446 cx.window_context().defer(move |cx| {
9447 let target_editor: View<Self> =
9448 workspace.update(cx, |workspace, cx| {
9449 let pane = if split {
9450 workspace.adjacent_pane(cx)
9451 } else {
9452 workspace.active_pane().clone()
9453 };
9454
9455 workspace.open_project_item(
9456 pane,
9457 target.buffer.clone(),
9458 true,
9459 true,
9460 cx,
9461 )
9462 });
9463 target_editor.update(cx, |target_editor, cx| {
9464 // When selecting a definition in a different buffer, disable the nav history
9465 // to avoid creating a history entry at the previous cursor location.
9466 pane.update(cx, |pane, _| pane.disable_history());
9467 let buffer = target.buffer.read(cx);
9468 let range = check_multiline_range(buffer, range);
9469 target_editor.change_selections(
9470 Some(Autoscroll::focused()),
9471 cx,
9472 |s| {
9473 s.select_ranges([range]);
9474 },
9475 );
9476 pane.update(cx, |pane, _| pane.enable_history());
9477 });
9478 });
9479 }
9480 Navigated::Yes
9481 })
9482 })
9483 } else if !definitions.is_empty() {
9484 cx.spawn(|editor, mut cx| async move {
9485 let (title, location_tasks, workspace) = editor
9486 .update(&mut cx, |editor, cx| {
9487 let tab_kind = match kind {
9488 Some(GotoDefinitionKind::Implementation) => "Implementations",
9489 _ => "Definitions",
9490 };
9491 let title = definitions
9492 .iter()
9493 .find_map(|definition| match definition {
9494 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
9495 let buffer = origin.buffer.read(cx);
9496 format!(
9497 "{} for {}",
9498 tab_kind,
9499 buffer
9500 .text_for_range(origin.range.clone())
9501 .collect::<String>()
9502 )
9503 }),
9504 HoverLink::InlayHint(_, _) => None,
9505 HoverLink::Url(_) => None,
9506 HoverLink::File(_) => None,
9507 })
9508 .unwrap_or(tab_kind.to_string());
9509 let location_tasks = definitions
9510 .into_iter()
9511 .map(|definition| match definition {
9512 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
9513 HoverLink::InlayHint(lsp_location, server_id) => {
9514 editor.compute_target_location(lsp_location, server_id, cx)
9515 }
9516 HoverLink::Url(_) => Task::ready(Ok(None)),
9517 HoverLink::File(_) => Task::ready(Ok(None)),
9518 })
9519 .collect::<Vec<_>>();
9520 (title, location_tasks, editor.workspace().clone())
9521 })
9522 .context("location tasks preparation")?;
9523
9524 let locations = future::join_all(location_tasks)
9525 .await
9526 .into_iter()
9527 .filter_map(|location| location.transpose())
9528 .collect::<Result<_>>()
9529 .context("location tasks")?;
9530
9531 let Some(workspace) = workspace else {
9532 return Ok(Navigated::No);
9533 };
9534 let opened = workspace
9535 .update(&mut cx, |workspace, cx| {
9536 Self::open_locations_in_multibuffer(workspace, locations, title, split, cx)
9537 })
9538 .ok();
9539
9540 anyhow::Ok(Navigated::from_bool(opened.is_some()))
9541 })
9542 } else {
9543 Task::ready(Ok(Navigated::No))
9544 }
9545 }
9546
9547 fn compute_target_location(
9548 &self,
9549 lsp_location: lsp::Location,
9550 server_id: LanguageServerId,
9551 cx: &mut ViewContext<Self>,
9552 ) -> Task<anyhow::Result<Option<Location>>> {
9553 let Some(project) = self.project.clone() else {
9554 return Task::ready(Ok(None));
9555 };
9556
9557 cx.spawn(move |editor, mut cx| async move {
9558 let location_task = editor.update(&mut cx, |_, cx| {
9559 project.update(cx, |project, cx| {
9560 let language_server_name = project
9561 .language_server_statuses(cx)
9562 .find(|(id, _)| server_id == *id)
9563 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
9564 language_server_name.map(|language_server_name| {
9565 project.open_local_buffer_via_lsp(
9566 lsp_location.uri.clone(),
9567 server_id,
9568 language_server_name,
9569 cx,
9570 )
9571 })
9572 })
9573 })?;
9574 let location = match location_task {
9575 Some(task) => Some({
9576 let target_buffer_handle = task.await.context("open local buffer")?;
9577 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
9578 let target_start = target_buffer
9579 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
9580 let target_end = target_buffer
9581 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
9582 target_buffer.anchor_after(target_start)
9583 ..target_buffer.anchor_before(target_end)
9584 })?;
9585 Location {
9586 buffer: target_buffer_handle,
9587 range,
9588 }
9589 }),
9590 None => None,
9591 };
9592 Ok(location)
9593 })
9594 }
9595
9596 pub fn find_all_references(
9597 &mut self,
9598 _: &FindAllReferences,
9599 cx: &mut ViewContext<Self>,
9600 ) -> Option<Task<Result<Navigated>>> {
9601 let selection = self.selections.newest::<usize>(cx);
9602 let multi_buffer = self.buffer.read(cx);
9603 let head = selection.head();
9604
9605 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
9606 let head_anchor = multi_buffer_snapshot.anchor_at(
9607 head,
9608 if head < selection.tail() {
9609 Bias::Right
9610 } else {
9611 Bias::Left
9612 },
9613 );
9614
9615 match self
9616 .find_all_references_task_sources
9617 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
9618 {
9619 Ok(_) => {
9620 log::info!(
9621 "Ignoring repeated FindAllReferences invocation with the position of already running task"
9622 );
9623 return None;
9624 }
9625 Err(i) => {
9626 self.find_all_references_task_sources.insert(i, head_anchor);
9627 }
9628 }
9629
9630 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
9631 let workspace = self.workspace()?;
9632 let project = workspace.read(cx).project().clone();
9633 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
9634 Some(cx.spawn(|editor, mut cx| async move {
9635 let _cleanup = defer({
9636 let mut cx = cx.clone();
9637 move || {
9638 let _ = editor.update(&mut cx, |editor, _| {
9639 if let Ok(i) =
9640 editor
9641 .find_all_references_task_sources
9642 .binary_search_by(|anchor| {
9643 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
9644 })
9645 {
9646 editor.find_all_references_task_sources.remove(i);
9647 }
9648 });
9649 }
9650 });
9651
9652 let locations = references.await?;
9653 if locations.is_empty() {
9654 return anyhow::Ok(Navigated::No);
9655 }
9656
9657 workspace.update(&mut cx, |workspace, cx| {
9658 let title = locations
9659 .first()
9660 .as_ref()
9661 .map(|location| {
9662 let buffer = location.buffer.read(cx);
9663 format!(
9664 "References to `{}`",
9665 buffer
9666 .text_for_range(location.range.clone())
9667 .collect::<String>()
9668 )
9669 })
9670 .unwrap();
9671 Self::open_locations_in_multibuffer(workspace, locations, title, false, cx);
9672 Navigated::Yes
9673 })
9674 }))
9675 }
9676
9677 /// Opens a multibuffer with the given project locations in it
9678 pub fn open_locations_in_multibuffer(
9679 workspace: &mut Workspace,
9680 mut locations: Vec<Location>,
9681 title: String,
9682 split: bool,
9683 cx: &mut ViewContext<Workspace>,
9684 ) {
9685 // If there are multiple definitions, open them in a multibuffer
9686 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
9687 let mut locations = locations.into_iter().peekable();
9688 let mut ranges_to_highlight = Vec::new();
9689 let capability = workspace.project().read(cx).capability();
9690
9691 let excerpt_buffer = cx.new_model(|cx| {
9692 let mut multibuffer = MultiBuffer::new(capability);
9693 while let Some(location) = locations.next() {
9694 let buffer = location.buffer.read(cx);
9695 let mut ranges_for_buffer = Vec::new();
9696 let range = location.range.to_offset(buffer);
9697 ranges_for_buffer.push(range.clone());
9698
9699 while let Some(next_location) = locations.peek() {
9700 if next_location.buffer == location.buffer {
9701 ranges_for_buffer.push(next_location.range.to_offset(buffer));
9702 locations.next();
9703 } else {
9704 break;
9705 }
9706 }
9707
9708 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
9709 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
9710 location.buffer.clone(),
9711 ranges_for_buffer,
9712 DEFAULT_MULTIBUFFER_CONTEXT,
9713 cx,
9714 ))
9715 }
9716
9717 multibuffer.with_title(title)
9718 });
9719
9720 let editor = cx.new_view(|cx| {
9721 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), true, cx)
9722 });
9723 editor.update(cx, |editor, cx| {
9724 if let Some(first_range) = ranges_to_highlight.first() {
9725 editor.change_selections(None, cx, |selections| {
9726 selections.clear_disjoint();
9727 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
9728 });
9729 }
9730 editor.highlight_background::<Self>(
9731 &ranges_to_highlight,
9732 |theme| theme.editor_highlighted_line_background,
9733 cx,
9734 );
9735 editor.register_buffers_with_language_servers(cx);
9736 });
9737
9738 let item = Box::new(editor);
9739 let item_id = item.item_id();
9740
9741 if split {
9742 workspace.split_item(SplitDirection::Right, item.clone(), cx);
9743 } else {
9744 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
9745 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
9746 pane.close_current_preview_item(cx)
9747 } else {
9748 None
9749 }
9750 });
9751 workspace.add_item_to_active_pane(item.clone(), destination_index, true, cx);
9752 }
9753 workspace.active_pane().update(cx, |pane, cx| {
9754 pane.set_preview_item_id(Some(item_id), cx);
9755 });
9756 }
9757
9758 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9759 use language::ToOffset as _;
9760
9761 let provider = self.semantics_provider.clone()?;
9762 let selection = self.selections.newest_anchor().clone();
9763 let (cursor_buffer, cursor_buffer_position) = self
9764 .buffer
9765 .read(cx)
9766 .text_anchor_for_position(selection.head(), cx)?;
9767 let (tail_buffer, cursor_buffer_position_end) = self
9768 .buffer
9769 .read(cx)
9770 .text_anchor_for_position(selection.tail(), cx)?;
9771 if tail_buffer != cursor_buffer {
9772 return None;
9773 }
9774
9775 let snapshot = cursor_buffer.read(cx).snapshot();
9776 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
9777 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
9778 let prepare_rename = provider
9779 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
9780 .unwrap_or_else(|| Task::ready(Ok(None)));
9781 drop(snapshot);
9782
9783 Some(cx.spawn(|this, mut cx| async move {
9784 let rename_range = if let Some(range) = prepare_rename.await? {
9785 Some(range)
9786 } else {
9787 this.update(&mut cx, |this, cx| {
9788 let buffer = this.buffer.read(cx).snapshot(cx);
9789 let mut buffer_highlights = this
9790 .document_highlights_for_position(selection.head(), &buffer)
9791 .filter(|highlight| {
9792 highlight.start.excerpt_id == selection.head().excerpt_id
9793 && highlight.end.excerpt_id == selection.head().excerpt_id
9794 });
9795 buffer_highlights
9796 .next()
9797 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
9798 })?
9799 };
9800 if let Some(rename_range) = rename_range {
9801 this.update(&mut cx, |this, cx| {
9802 let snapshot = cursor_buffer.read(cx).snapshot();
9803 let rename_buffer_range = rename_range.to_offset(&snapshot);
9804 let cursor_offset_in_rename_range =
9805 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
9806 let cursor_offset_in_rename_range_end =
9807 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
9808
9809 this.take_rename(false, cx);
9810 let buffer = this.buffer.read(cx).read(cx);
9811 let cursor_offset = selection.head().to_offset(&buffer);
9812 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
9813 let rename_end = rename_start + rename_buffer_range.len();
9814 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
9815 let mut old_highlight_id = None;
9816 let old_name: Arc<str> = buffer
9817 .chunks(rename_start..rename_end, true)
9818 .map(|chunk| {
9819 if old_highlight_id.is_none() {
9820 old_highlight_id = chunk.syntax_highlight_id;
9821 }
9822 chunk.text
9823 })
9824 .collect::<String>()
9825 .into();
9826
9827 drop(buffer);
9828
9829 // Position the selection in the rename editor so that it matches the current selection.
9830 this.show_local_selections = false;
9831 let rename_editor = cx.new_view(|cx| {
9832 let mut editor = Editor::single_line(cx);
9833 editor.buffer.update(cx, |buffer, cx| {
9834 buffer.edit([(0..0, old_name.clone())], None, cx)
9835 });
9836 let rename_selection_range = match cursor_offset_in_rename_range
9837 .cmp(&cursor_offset_in_rename_range_end)
9838 {
9839 Ordering::Equal => {
9840 editor.select_all(&SelectAll, cx);
9841 return editor;
9842 }
9843 Ordering::Less => {
9844 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
9845 }
9846 Ordering::Greater => {
9847 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
9848 }
9849 };
9850 if rename_selection_range.end > old_name.len() {
9851 editor.select_all(&SelectAll, cx);
9852 } else {
9853 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
9854 s.select_ranges([rename_selection_range]);
9855 });
9856 }
9857 editor
9858 });
9859 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
9860 if e == &EditorEvent::Focused {
9861 cx.emit(EditorEvent::FocusedIn)
9862 }
9863 })
9864 .detach();
9865
9866 let write_highlights =
9867 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
9868 let read_highlights =
9869 this.clear_background_highlights::<DocumentHighlightRead>(cx);
9870 let ranges = write_highlights
9871 .iter()
9872 .flat_map(|(_, ranges)| ranges.iter())
9873 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
9874 .cloned()
9875 .collect();
9876
9877 this.highlight_text::<Rename>(
9878 ranges,
9879 HighlightStyle {
9880 fade_out: Some(0.6),
9881 ..Default::default()
9882 },
9883 cx,
9884 );
9885 let rename_focus_handle = rename_editor.focus_handle(cx);
9886 cx.focus(&rename_focus_handle);
9887 let block_id = this.insert_blocks(
9888 [BlockProperties {
9889 style: BlockStyle::Flex,
9890 placement: BlockPlacement::Below(range.start),
9891 height: 1,
9892 render: Arc::new({
9893 let rename_editor = rename_editor.clone();
9894 move |cx: &mut BlockContext| {
9895 let mut text_style = cx.editor_style.text.clone();
9896 if let Some(highlight_style) = old_highlight_id
9897 .and_then(|h| h.style(&cx.editor_style.syntax))
9898 {
9899 text_style = text_style.highlight(highlight_style);
9900 }
9901 div()
9902 .block_mouse_down()
9903 .pl(cx.anchor_x)
9904 .child(EditorElement::new(
9905 &rename_editor,
9906 EditorStyle {
9907 background: cx.theme().system().transparent,
9908 local_player: cx.editor_style.local_player,
9909 text: text_style,
9910 scrollbar_width: cx.editor_style.scrollbar_width,
9911 syntax: cx.editor_style.syntax.clone(),
9912 status: cx.editor_style.status.clone(),
9913 inlay_hints_style: HighlightStyle {
9914 font_weight: Some(FontWeight::BOLD),
9915 ..make_inlay_hints_style(cx)
9916 },
9917 inline_completion_styles: make_suggestion_styles(
9918 cx,
9919 ),
9920 ..EditorStyle::default()
9921 },
9922 ))
9923 .into_any_element()
9924 }
9925 }),
9926 priority: 0,
9927 }],
9928 Some(Autoscroll::fit()),
9929 cx,
9930 )[0];
9931 this.pending_rename = Some(RenameState {
9932 range,
9933 old_name,
9934 editor: rename_editor,
9935 block_id,
9936 });
9937 })?;
9938 }
9939
9940 Ok(())
9941 }))
9942 }
9943
9944 pub fn confirm_rename(
9945 &mut self,
9946 _: &ConfirmRename,
9947 cx: &mut ViewContext<Self>,
9948 ) -> Option<Task<Result<()>>> {
9949 let rename = self.take_rename(false, cx)?;
9950 let workspace = self.workspace()?.downgrade();
9951 let (buffer, start) = self
9952 .buffer
9953 .read(cx)
9954 .text_anchor_for_position(rename.range.start, cx)?;
9955 let (end_buffer, _) = self
9956 .buffer
9957 .read(cx)
9958 .text_anchor_for_position(rename.range.end, cx)?;
9959 if buffer != end_buffer {
9960 return None;
9961 }
9962
9963 let old_name = rename.old_name;
9964 let new_name = rename.editor.read(cx).text(cx);
9965
9966 let rename = self.semantics_provider.as_ref()?.perform_rename(
9967 &buffer,
9968 start,
9969 new_name.clone(),
9970 cx,
9971 )?;
9972
9973 Some(cx.spawn(|editor, mut cx| async move {
9974 let project_transaction = rename.await?;
9975 Self::open_project_transaction(
9976 &editor,
9977 workspace,
9978 project_transaction,
9979 format!("Rename: {} → {}", old_name, new_name),
9980 cx.clone(),
9981 )
9982 .await?;
9983
9984 editor.update(&mut cx, |editor, cx| {
9985 editor.refresh_document_highlights(cx);
9986 })?;
9987 Ok(())
9988 }))
9989 }
9990
9991 fn take_rename(
9992 &mut self,
9993 moving_cursor: bool,
9994 cx: &mut ViewContext<Self>,
9995 ) -> Option<RenameState> {
9996 let rename = self.pending_rename.take()?;
9997 if rename.editor.focus_handle(cx).is_focused(cx) {
9998 cx.focus(&self.focus_handle);
9999 }
10000
10001 self.remove_blocks(
10002 [rename.block_id].into_iter().collect(),
10003 Some(Autoscroll::fit()),
10004 cx,
10005 );
10006 self.clear_highlights::<Rename>(cx);
10007 self.show_local_selections = true;
10008
10009 if moving_cursor {
10010 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
10011 editor.selections.newest::<usize>(cx).head()
10012 });
10013
10014 // Update the selection to match the position of the selection inside
10015 // the rename editor.
10016 let snapshot = self.buffer.read(cx).read(cx);
10017 let rename_range = rename.range.to_offset(&snapshot);
10018 let cursor_in_editor = snapshot
10019 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
10020 .min(rename_range.end);
10021 drop(snapshot);
10022
10023 self.change_selections(None, cx, |s| {
10024 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
10025 });
10026 } else {
10027 self.refresh_document_highlights(cx);
10028 }
10029
10030 Some(rename)
10031 }
10032
10033 pub fn pending_rename(&self) -> Option<&RenameState> {
10034 self.pending_rename.as_ref()
10035 }
10036
10037 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
10038 let project = match &self.project {
10039 Some(project) => project.clone(),
10040 None => return None,
10041 };
10042
10043 Some(self.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffer, cx))
10044 }
10045
10046 fn format_selections(
10047 &mut self,
10048 _: &FormatSelections,
10049 cx: &mut ViewContext<Self>,
10050 ) -> Option<Task<Result<()>>> {
10051 let project = match &self.project {
10052 Some(project) => project.clone(),
10053 None => return None,
10054 };
10055
10056 let selections = self
10057 .selections
10058 .all_adjusted(cx)
10059 .into_iter()
10060 .filter(|s| !s.is_empty())
10061 .collect_vec();
10062
10063 Some(self.perform_format(
10064 project,
10065 FormatTrigger::Manual,
10066 FormatTarget::Ranges(selections),
10067 cx,
10068 ))
10069 }
10070
10071 fn perform_format(
10072 &mut self,
10073 project: Model<Project>,
10074 trigger: FormatTrigger,
10075 target: FormatTarget,
10076 cx: &mut ViewContext<Self>,
10077 ) -> Task<Result<()>> {
10078 let buffer = self.buffer().clone();
10079 let mut buffers = buffer.read(cx).all_buffers();
10080 if trigger == FormatTrigger::Save {
10081 buffers.retain(|buffer| buffer.read(cx).is_dirty());
10082 }
10083
10084 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
10085 let format = project.update(cx, |project, cx| {
10086 project.format(buffers, true, trigger, target, cx)
10087 });
10088
10089 cx.spawn(|_, mut cx| async move {
10090 let transaction = futures::select_biased! {
10091 () = timeout => {
10092 log::warn!("timed out waiting for formatting");
10093 None
10094 }
10095 transaction = format.log_err().fuse() => transaction,
10096 };
10097
10098 buffer
10099 .update(&mut cx, |buffer, cx| {
10100 if let Some(transaction) = transaction {
10101 if !buffer.is_singleton() {
10102 buffer.push_transaction(&transaction.0, cx);
10103 }
10104 }
10105
10106 cx.notify();
10107 })
10108 .ok();
10109
10110 Ok(())
10111 })
10112 }
10113
10114 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
10115 if let Some(project) = self.project.clone() {
10116 self.buffer.update(cx, |multi_buffer, cx| {
10117 project.update(cx, |project, cx| {
10118 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
10119 });
10120 })
10121 }
10122 }
10123
10124 fn cancel_language_server_work(
10125 &mut self,
10126 _: &actions::CancelLanguageServerWork,
10127 cx: &mut ViewContext<Self>,
10128 ) {
10129 if let Some(project) = self.project.clone() {
10130 self.buffer.update(cx, |multi_buffer, cx| {
10131 project.update(cx, |project, cx| {
10132 project.cancel_language_server_work_for_buffers(multi_buffer.all_buffers(), cx);
10133 });
10134 })
10135 }
10136 }
10137
10138 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
10139 cx.show_character_palette();
10140 }
10141
10142 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
10143 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
10144 let buffer = self.buffer.read(cx).snapshot(cx);
10145 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
10146 let is_valid = buffer
10147 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
10148 .any(|entry| {
10149 entry.diagnostic.is_primary
10150 && !entry.range.is_empty()
10151 && entry.range.start == primary_range_start
10152 && entry.diagnostic.message == active_diagnostics.primary_message
10153 });
10154
10155 if is_valid != active_diagnostics.is_valid {
10156 active_diagnostics.is_valid = is_valid;
10157 let mut new_styles = HashMap::default();
10158 for (block_id, diagnostic) in &active_diagnostics.blocks {
10159 new_styles.insert(
10160 *block_id,
10161 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
10162 );
10163 }
10164 self.display_map.update(cx, |display_map, _cx| {
10165 display_map.replace_blocks(new_styles)
10166 });
10167 }
10168 }
10169 }
10170
10171 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
10172 self.dismiss_diagnostics(cx);
10173 let snapshot = self.snapshot(cx);
10174 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
10175 let buffer = self.buffer.read(cx).snapshot(cx);
10176
10177 let mut primary_range = None;
10178 let mut primary_message = None;
10179 let mut group_end = Point::zero();
10180 let diagnostic_group = buffer
10181 .diagnostic_group::<MultiBufferPoint>(group_id)
10182 .filter_map(|entry| {
10183 if snapshot.is_line_folded(MultiBufferRow(entry.range.start.row))
10184 && (entry.range.start.row == entry.range.end.row
10185 || snapshot.is_line_folded(MultiBufferRow(entry.range.end.row)))
10186 {
10187 return None;
10188 }
10189 if entry.range.end > group_end {
10190 group_end = entry.range.end;
10191 }
10192 if entry.diagnostic.is_primary {
10193 primary_range = Some(entry.range.clone());
10194 primary_message = Some(entry.diagnostic.message.clone());
10195 }
10196 Some(entry)
10197 })
10198 .collect::<Vec<_>>();
10199 let primary_range = primary_range?;
10200 let primary_message = primary_message?;
10201 let primary_range =
10202 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
10203
10204 let blocks = display_map
10205 .insert_blocks(
10206 diagnostic_group.iter().map(|entry| {
10207 let diagnostic = entry.diagnostic.clone();
10208 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
10209 BlockProperties {
10210 style: BlockStyle::Fixed,
10211 placement: BlockPlacement::Below(
10212 buffer.anchor_after(entry.range.start),
10213 ),
10214 height: message_height,
10215 render: diagnostic_block_renderer(diagnostic, None, true, true),
10216 priority: 0,
10217 }
10218 }),
10219 cx,
10220 )
10221 .into_iter()
10222 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
10223 .collect();
10224
10225 Some(ActiveDiagnosticGroup {
10226 primary_range,
10227 primary_message,
10228 group_id,
10229 blocks,
10230 is_valid: true,
10231 })
10232 });
10233 self.active_diagnostics.is_some()
10234 }
10235
10236 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
10237 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
10238 self.display_map.update(cx, |display_map, cx| {
10239 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
10240 });
10241 cx.notify();
10242 }
10243 }
10244
10245 pub fn set_selections_from_remote(
10246 &mut self,
10247 selections: Vec<Selection<Anchor>>,
10248 pending_selection: Option<Selection<Anchor>>,
10249 cx: &mut ViewContext<Self>,
10250 ) {
10251 let old_cursor_position = self.selections.newest_anchor().head();
10252 self.selections.change_with(cx, |s| {
10253 s.select_anchors(selections);
10254 if let Some(pending_selection) = pending_selection {
10255 s.set_pending(pending_selection, SelectMode::Character);
10256 } else {
10257 s.clear_pending();
10258 }
10259 });
10260 self.selections_did_change(false, &old_cursor_position, true, cx);
10261 }
10262
10263 fn push_to_selection_history(&mut self) {
10264 self.selection_history.push(SelectionHistoryEntry {
10265 selections: self.selections.disjoint_anchors(),
10266 select_next_state: self.select_next_state.clone(),
10267 select_prev_state: self.select_prev_state.clone(),
10268 add_selections_state: self.add_selections_state.clone(),
10269 });
10270 }
10271
10272 pub fn transact(
10273 &mut self,
10274 cx: &mut ViewContext<Self>,
10275 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
10276 ) -> Option<TransactionId> {
10277 self.start_transaction_at(Instant::now(), cx);
10278 update(self, cx);
10279 self.end_transaction_at(Instant::now(), cx)
10280 }
10281
10282 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
10283 self.end_selection(cx);
10284 if let Some(tx_id) = self
10285 .buffer
10286 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
10287 {
10288 self.selection_history
10289 .insert_transaction(tx_id, self.selections.disjoint_anchors());
10290 cx.emit(EditorEvent::TransactionBegun {
10291 transaction_id: tx_id,
10292 })
10293 }
10294 }
10295
10296 fn end_transaction_at(
10297 &mut self,
10298 now: Instant,
10299 cx: &mut ViewContext<Self>,
10300 ) -> Option<TransactionId> {
10301 if let Some(transaction_id) = self
10302 .buffer
10303 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
10304 {
10305 if let Some((_, end_selections)) =
10306 self.selection_history.transaction_mut(transaction_id)
10307 {
10308 *end_selections = Some(self.selections.disjoint_anchors());
10309 } else {
10310 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
10311 }
10312
10313 cx.emit(EditorEvent::Edited { transaction_id });
10314 Some(transaction_id)
10315 } else {
10316 None
10317 }
10318 }
10319
10320 pub fn toggle_fold(&mut self, _: &actions::ToggleFold, cx: &mut ViewContext<Self>) {
10321 if self.is_singleton(cx) {
10322 let selection = self.selections.newest::<Point>(cx);
10323
10324 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10325 let range = if selection.is_empty() {
10326 let point = selection.head().to_display_point(&display_map);
10327 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
10328 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
10329 .to_point(&display_map);
10330 start..end
10331 } else {
10332 selection.range()
10333 };
10334 if display_map.folds_in_range(range).next().is_some() {
10335 self.unfold_lines(&Default::default(), cx)
10336 } else {
10337 self.fold(&Default::default(), cx)
10338 }
10339 } else {
10340 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
10341 let mut toggled_buffers = HashSet::default();
10342 for (_, buffer_snapshot, _) in multi_buffer_snapshot.excerpts_in_ranges(
10343 self.selections
10344 .disjoint_anchors()
10345 .into_iter()
10346 .map(|selection| selection.range()),
10347 ) {
10348 let buffer_id = buffer_snapshot.remote_id();
10349 if toggled_buffers.insert(buffer_id) {
10350 if self.buffer_folded(buffer_id, cx) {
10351 self.unfold_buffer(buffer_id, cx);
10352 } else {
10353 self.fold_buffer(buffer_id, cx);
10354 }
10355 }
10356 }
10357 }
10358 }
10359
10360 pub fn toggle_fold_recursive(
10361 &mut self,
10362 _: &actions::ToggleFoldRecursive,
10363 cx: &mut ViewContext<Self>,
10364 ) {
10365 let selection = self.selections.newest::<Point>(cx);
10366
10367 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10368 let range = if selection.is_empty() {
10369 let point = selection.head().to_display_point(&display_map);
10370 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
10371 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
10372 .to_point(&display_map);
10373 start..end
10374 } else {
10375 selection.range()
10376 };
10377 if display_map.folds_in_range(range).next().is_some() {
10378 self.unfold_recursive(&Default::default(), cx)
10379 } else {
10380 self.fold_recursive(&Default::default(), cx)
10381 }
10382 }
10383
10384 pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
10385 if self.is_singleton(cx) {
10386 let mut to_fold = Vec::new();
10387 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10388 let selections = self.selections.all_adjusted(cx);
10389
10390 for selection in selections {
10391 let range = selection.range().sorted();
10392 let buffer_start_row = range.start.row;
10393
10394 if range.start.row != range.end.row {
10395 let mut found = false;
10396 let mut row = range.start.row;
10397 while row <= range.end.row {
10398 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
10399 {
10400 found = true;
10401 row = crease.range().end.row + 1;
10402 to_fold.push(crease);
10403 } else {
10404 row += 1
10405 }
10406 }
10407 if found {
10408 continue;
10409 }
10410 }
10411
10412 for row in (0..=range.start.row).rev() {
10413 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
10414 if crease.range().end.row >= buffer_start_row {
10415 to_fold.push(crease);
10416 if row <= range.start.row {
10417 break;
10418 }
10419 }
10420 }
10421 }
10422 }
10423
10424 self.fold_creases(to_fold, true, cx);
10425 } else {
10426 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
10427 let mut folded_buffers = HashSet::default();
10428 for (_, buffer_snapshot, _) in multi_buffer_snapshot.excerpts_in_ranges(
10429 self.selections
10430 .disjoint_anchors()
10431 .into_iter()
10432 .map(|selection| selection.range()),
10433 ) {
10434 let buffer_id = buffer_snapshot.remote_id();
10435 if folded_buffers.insert(buffer_id) {
10436 self.fold_buffer(buffer_id, cx);
10437 }
10438 }
10439 }
10440 }
10441
10442 fn fold_at_level(&mut self, fold_at: &FoldAtLevel, cx: &mut ViewContext<Self>) {
10443 if !self.buffer.read(cx).is_singleton() {
10444 return;
10445 }
10446
10447 let fold_at_level = fold_at.level;
10448 let snapshot = self.buffer.read(cx).snapshot(cx);
10449 let mut to_fold = Vec::new();
10450 let mut stack = vec![(0, snapshot.max_row().0, 1)];
10451
10452 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
10453 while start_row < end_row {
10454 match self
10455 .snapshot(cx)
10456 .crease_for_buffer_row(MultiBufferRow(start_row))
10457 {
10458 Some(crease) => {
10459 let nested_start_row = crease.range().start.row + 1;
10460 let nested_end_row = crease.range().end.row;
10461
10462 if current_level < fold_at_level {
10463 stack.push((nested_start_row, nested_end_row, current_level + 1));
10464 } else if current_level == fold_at_level {
10465 to_fold.push(crease);
10466 }
10467
10468 start_row = nested_end_row + 1;
10469 }
10470 None => start_row += 1,
10471 }
10472 }
10473 }
10474
10475 self.fold_creases(to_fold, true, cx);
10476 }
10477
10478 pub fn fold_all(&mut self, _: &actions::FoldAll, cx: &mut ViewContext<Self>) {
10479 if self.buffer.read(cx).is_singleton() {
10480 let mut fold_ranges = Vec::new();
10481 let snapshot = self.buffer.read(cx).snapshot(cx);
10482
10483 for row in 0..snapshot.max_row().0 {
10484 if let Some(foldable_range) =
10485 self.snapshot(cx).crease_for_buffer_row(MultiBufferRow(row))
10486 {
10487 fold_ranges.push(foldable_range);
10488 }
10489 }
10490
10491 self.fold_creases(fold_ranges, true, cx);
10492 } else {
10493 self.toggle_fold_multiple_buffers = cx.spawn(|editor, mut cx| async move {
10494 editor
10495 .update(&mut cx, |editor, cx| {
10496 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
10497 editor.fold_buffer(buffer_id, cx);
10498 }
10499 })
10500 .ok();
10501 });
10502 }
10503 }
10504
10505 pub fn fold_function_bodies(
10506 &mut self,
10507 _: &actions::FoldFunctionBodies,
10508 cx: &mut ViewContext<Self>,
10509 ) {
10510 let snapshot = self.buffer.read(cx).snapshot(cx);
10511 let Some((_, _, buffer)) = snapshot.as_singleton() else {
10512 return;
10513 };
10514 let creases = buffer
10515 .function_body_fold_ranges(0..buffer.len())
10516 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
10517 .collect();
10518
10519 self.fold_creases(creases, true, cx);
10520 }
10521
10522 pub fn fold_recursive(&mut self, _: &actions::FoldRecursive, cx: &mut ViewContext<Self>) {
10523 let mut to_fold = Vec::new();
10524 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10525 let selections = self.selections.all_adjusted(cx);
10526
10527 for selection in selections {
10528 let range = selection.range().sorted();
10529 let buffer_start_row = range.start.row;
10530
10531 if range.start.row != range.end.row {
10532 let mut found = false;
10533 for row in range.start.row..=range.end.row {
10534 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
10535 found = true;
10536 to_fold.push(crease);
10537 }
10538 }
10539 if found {
10540 continue;
10541 }
10542 }
10543
10544 for row in (0..=range.start.row).rev() {
10545 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
10546 if crease.range().end.row >= buffer_start_row {
10547 to_fold.push(crease);
10548 } else {
10549 break;
10550 }
10551 }
10552 }
10553 }
10554
10555 self.fold_creases(to_fold, true, cx);
10556 }
10557
10558 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
10559 let buffer_row = fold_at.buffer_row;
10560 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10561
10562 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
10563 let autoscroll = self
10564 .selections
10565 .all::<Point>(cx)
10566 .iter()
10567 .any(|selection| crease.range().overlaps(&selection.range()));
10568
10569 self.fold_creases(vec![crease], autoscroll, cx);
10570 }
10571 }
10572
10573 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
10574 if self.is_singleton(cx) {
10575 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10576 let buffer = &display_map.buffer_snapshot;
10577 let selections = self.selections.all::<Point>(cx);
10578 let ranges = selections
10579 .iter()
10580 .map(|s| {
10581 let range = s.display_range(&display_map).sorted();
10582 let mut start = range.start.to_point(&display_map);
10583 let mut end = range.end.to_point(&display_map);
10584 start.column = 0;
10585 end.column = buffer.line_len(MultiBufferRow(end.row));
10586 start..end
10587 })
10588 .collect::<Vec<_>>();
10589
10590 self.unfold_ranges(&ranges, true, true, cx);
10591 } else {
10592 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
10593 let mut unfolded_buffers = HashSet::default();
10594 for (_, buffer_snapshot, _) in multi_buffer_snapshot.excerpts_in_ranges(
10595 self.selections
10596 .disjoint_anchors()
10597 .into_iter()
10598 .map(|selection| selection.range()),
10599 ) {
10600 let buffer_id = buffer_snapshot.remote_id();
10601 if unfolded_buffers.insert(buffer_id) {
10602 self.unfold_buffer(buffer_id, cx);
10603 }
10604 }
10605 }
10606 }
10607
10608 pub fn unfold_recursive(&mut self, _: &UnfoldRecursive, cx: &mut ViewContext<Self>) {
10609 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10610 let selections = self.selections.all::<Point>(cx);
10611 let ranges = selections
10612 .iter()
10613 .map(|s| {
10614 let mut range = s.display_range(&display_map).sorted();
10615 *range.start.column_mut() = 0;
10616 *range.end.column_mut() = display_map.line_len(range.end.row());
10617 let start = range.start.to_point(&display_map);
10618 let end = range.end.to_point(&display_map);
10619 start..end
10620 })
10621 .collect::<Vec<_>>();
10622
10623 self.unfold_ranges(&ranges, true, true, cx);
10624 }
10625
10626 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
10627 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10628
10629 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
10630 ..Point::new(
10631 unfold_at.buffer_row.0,
10632 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
10633 );
10634
10635 let autoscroll = self
10636 .selections
10637 .all::<Point>(cx)
10638 .iter()
10639 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
10640
10641 self.unfold_ranges(&[intersection_range], true, autoscroll, cx)
10642 }
10643
10644 pub fn unfold_all(&mut self, _: &actions::UnfoldAll, cx: &mut ViewContext<Self>) {
10645 if self.buffer.read(cx).is_singleton() {
10646 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10647 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
10648 } else {
10649 self.toggle_fold_multiple_buffers = cx.spawn(|editor, mut cx| async move {
10650 editor
10651 .update(&mut cx, |editor, cx| {
10652 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
10653 editor.unfold_buffer(buffer_id, cx);
10654 }
10655 })
10656 .ok();
10657 });
10658 }
10659 }
10660
10661 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
10662 let selections = self.selections.all::<Point>(cx);
10663 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10664 let line_mode = self.selections.line_mode;
10665 let ranges = selections
10666 .into_iter()
10667 .map(|s| {
10668 if line_mode {
10669 let start = Point::new(s.start.row, 0);
10670 let end = Point::new(
10671 s.end.row,
10672 display_map
10673 .buffer_snapshot
10674 .line_len(MultiBufferRow(s.end.row)),
10675 );
10676 Crease::simple(start..end, display_map.fold_placeholder.clone())
10677 } else {
10678 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
10679 }
10680 })
10681 .collect::<Vec<_>>();
10682 self.fold_creases(ranges, true, cx);
10683 }
10684
10685 pub fn fold_creases<T: ToOffset + Clone>(
10686 &mut self,
10687 creases: Vec<Crease<T>>,
10688 auto_scroll: bool,
10689 cx: &mut ViewContext<Self>,
10690 ) {
10691 if creases.is_empty() {
10692 return;
10693 }
10694
10695 let mut buffers_affected = HashSet::default();
10696 let multi_buffer = self.buffer().read(cx);
10697 for crease in &creases {
10698 if let Some((_, buffer, _)) =
10699 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
10700 {
10701 buffers_affected.insert(buffer.read(cx).remote_id());
10702 };
10703 }
10704
10705 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
10706
10707 if auto_scroll {
10708 self.request_autoscroll(Autoscroll::fit(), cx);
10709 }
10710
10711 for buffer_id in buffers_affected {
10712 Self::sync_expanded_diff_hunks(&mut self.diff_map, buffer_id, cx);
10713 }
10714
10715 cx.notify();
10716
10717 if let Some(active_diagnostics) = self.active_diagnostics.take() {
10718 // Clear diagnostics block when folding a range that contains it.
10719 let snapshot = self.snapshot(cx);
10720 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
10721 drop(snapshot);
10722 self.active_diagnostics = Some(active_diagnostics);
10723 self.dismiss_diagnostics(cx);
10724 } else {
10725 self.active_diagnostics = Some(active_diagnostics);
10726 }
10727 }
10728
10729 self.scrollbar_marker_state.dirty = true;
10730 }
10731
10732 /// Removes any folds whose ranges intersect any of the given ranges.
10733 pub fn unfold_ranges<T: ToOffset + Clone>(
10734 &mut self,
10735 ranges: &[Range<T>],
10736 inclusive: bool,
10737 auto_scroll: bool,
10738 cx: &mut ViewContext<Self>,
10739 ) {
10740 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
10741 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
10742 });
10743 }
10744
10745 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut ViewContext<Self>) {
10746 if self.buffer().read(cx).is_singleton() || self.buffer_folded(buffer_id, cx) {
10747 return;
10748 }
10749 let Some(buffer) = self.buffer().read(cx).buffer(buffer_id) else {
10750 return;
10751 };
10752 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(&buffer, cx);
10753 self.display_map
10754 .update(cx, |display_map, cx| display_map.fold_buffer(buffer_id, cx));
10755 cx.emit(EditorEvent::BufferFoldToggled {
10756 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
10757 folded: true,
10758 });
10759 cx.notify();
10760 }
10761
10762 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut ViewContext<Self>) {
10763 if self.buffer().read(cx).is_singleton() || !self.buffer_folded(buffer_id, cx) {
10764 return;
10765 }
10766 let Some(buffer) = self.buffer().read(cx).buffer(buffer_id) else {
10767 return;
10768 };
10769 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(&buffer, cx);
10770 self.display_map.update(cx, |display_map, cx| {
10771 display_map.unfold_buffer(buffer_id, cx);
10772 });
10773 cx.emit(EditorEvent::BufferFoldToggled {
10774 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
10775 folded: false,
10776 });
10777 cx.notify();
10778 }
10779
10780 pub fn buffer_folded(&self, buffer: BufferId, cx: &AppContext) -> bool {
10781 self.display_map.read(cx).buffer_folded(buffer)
10782 }
10783
10784 /// Removes any folds with the given ranges.
10785 pub fn remove_folds_with_type<T: ToOffset + Clone>(
10786 &mut self,
10787 ranges: &[Range<T>],
10788 type_id: TypeId,
10789 auto_scroll: bool,
10790 cx: &mut ViewContext<Self>,
10791 ) {
10792 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
10793 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
10794 });
10795 }
10796
10797 fn remove_folds_with<T: ToOffset + Clone>(
10798 &mut self,
10799 ranges: &[Range<T>],
10800 auto_scroll: bool,
10801 cx: &mut ViewContext<Self>,
10802 update: impl FnOnce(&mut DisplayMap, &mut ModelContext<DisplayMap>),
10803 ) {
10804 if ranges.is_empty() {
10805 return;
10806 }
10807
10808 let mut buffers_affected = HashSet::default();
10809 let multi_buffer = self.buffer().read(cx);
10810 for range in ranges {
10811 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
10812 buffers_affected.insert(buffer.read(cx).remote_id());
10813 };
10814 }
10815
10816 self.display_map.update(cx, update);
10817
10818 if auto_scroll {
10819 self.request_autoscroll(Autoscroll::fit(), cx);
10820 }
10821
10822 for buffer_id in buffers_affected {
10823 Self::sync_expanded_diff_hunks(&mut self.diff_map, buffer_id, cx);
10824 }
10825
10826 cx.notify();
10827 self.scrollbar_marker_state.dirty = true;
10828 self.active_indent_guides_state.dirty = true;
10829 }
10830
10831 pub fn default_fold_placeholder(&self, cx: &AppContext) -> FoldPlaceholder {
10832 self.display_map.read(cx).fold_placeholder.clone()
10833 }
10834
10835 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
10836 if hovered != self.gutter_hovered {
10837 self.gutter_hovered = hovered;
10838 cx.notify();
10839 }
10840 }
10841
10842 pub fn insert_blocks(
10843 &mut self,
10844 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
10845 autoscroll: Option<Autoscroll>,
10846 cx: &mut ViewContext<Self>,
10847 ) -> Vec<CustomBlockId> {
10848 let blocks = self
10849 .display_map
10850 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
10851 if let Some(autoscroll) = autoscroll {
10852 self.request_autoscroll(autoscroll, cx);
10853 }
10854 cx.notify();
10855 blocks
10856 }
10857
10858 pub fn resize_blocks(
10859 &mut self,
10860 heights: HashMap<CustomBlockId, u32>,
10861 autoscroll: Option<Autoscroll>,
10862 cx: &mut ViewContext<Self>,
10863 ) {
10864 self.display_map
10865 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
10866 if let Some(autoscroll) = autoscroll {
10867 self.request_autoscroll(autoscroll, cx);
10868 }
10869 cx.notify();
10870 }
10871
10872 pub fn replace_blocks(
10873 &mut self,
10874 renderers: HashMap<CustomBlockId, RenderBlock>,
10875 autoscroll: Option<Autoscroll>,
10876 cx: &mut ViewContext<Self>,
10877 ) {
10878 self.display_map
10879 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
10880 if let Some(autoscroll) = autoscroll {
10881 self.request_autoscroll(autoscroll, cx);
10882 }
10883 cx.notify();
10884 }
10885
10886 pub fn remove_blocks(
10887 &mut self,
10888 block_ids: HashSet<CustomBlockId>,
10889 autoscroll: Option<Autoscroll>,
10890 cx: &mut ViewContext<Self>,
10891 ) {
10892 self.display_map.update(cx, |display_map, cx| {
10893 display_map.remove_blocks(block_ids, cx)
10894 });
10895 if let Some(autoscroll) = autoscroll {
10896 self.request_autoscroll(autoscroll, cx);
10897 }
10898 cx.notify();
10899 }
10900
10901 pub fn row_for_block(
10902 &self,
10903 block_id: CustomBlockId,
10904 cx: &mut ViewContext<Self>,
10905 ) -> Option<DisplayRow> {
10906 self.display_map
10907 .update(cx, |map, cx| map.row_for_block(block_id, cx))
10908 }
10909
10910 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
10911 self.focused_block = Some(focused_block);
10912 }
10913
10914 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
10915 self.focused_block.take()
10916 }
10917
10918 pub fn insert_creases(
10919 &mut self,
10920 creases: impl IntoIterator<Item = Crease<Anchor>>,
10921 cx: &mut ViewContext<Self>,
10922 ) -> Vec<CreaseId> {
10923 self.display_map
10924 .update(cx, |map, cx| map.insert_creases(creases, cx))
10925 }
10926
10927 pub fn remove_creases(
10928 &mut self,
10929 ids: impl IntoIterator<Item = CreaseId>,
10930 cx: &mut ViewContext<Self>,
10931 ) {
10932 self.display_map
10933 .update(cx, |map, cx| map.remove_creases(ids, cx));
10934 }
10935
10936 pub fn longest_row(&self, cx: &mut AppContext) -> DisplayRow {
10937 self.display_map
10938 .update(cx, |map, cx| map.snapshot(cx))
10939 .longest_row()
10940 }
10941
10942 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
10943 self.display_map
10944 .update(cx, |map, cx| map.snapshot(cx))
10945 .max_point()
10946 }
10947
10948 pub fn text(&self, cx: &AppContext) -> String {
10949 self.buffer.read(cx).read(cx).text()
10950 }
10951
10952 pub fn text_option(&self, cx: &AppContext) -> Option<String> {
10953 let text = self.text(cx);
10954 let text = text.trim();
10955
10956 if text.is_empty() {
10957 return None;
10958 }
10959
10960 Some(text.to_string())
10961 }
10962
10963 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
10964 self.transact(cx, |this, cx| {
10965 this.buffer
10966 .read(cx)
10967 .as_singleton()
10968 .expect("you can only call set_text on editors for singleton buffers")
10969 .update(cx, |buffer, cx| buffer.set_text(text, cx));
10970 });
10971 }
10972
10973 pub fn display_text(&self, cx: &mut AppContext) -> String {
10974 self.display_map
10975 .update(cx, |map, cx| map.snapshot(cx))
10976 .text()
10977 }
10978
10979 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
10980 let mut wrap_guides = smallvec::smallvec![];
10981
10982 if self.show_wrap_guides == Some(false) {
10983 return wrap_guides;
10984 }
10985
10986 let settings = self.buffer.read(cx).settings_at(0, cx);
10987 if settings.show_wrap_guides {
10988 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
10989 wrap_guides.push((soft_wrap as usize, true));
10990 } else if let SoftWrap::Bounded(soft_wrap) = self.soft_wrap_mode(cx) {
10991 wrap_guides.push((soft_wrap as usize, true));
10992 }
10993 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
10994 }
10995
10996 wrap_guides
10997 }
10998
10999 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
11000 let settings = self.buffer.read(cx).settings_at(0, cx);
11001 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
11002 match mode {
11003 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
11004 SoftWrap::None
11005 }
11006 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
11007 language_settings::SoftWrap::PreferredLineLength => {
11008 SoftWrap::Column(settings.preferred_line_length)
11009 }
11010 language_settings::SoftWrap::Bounded => {
11011 SoftWrap::Bounded(settings.preferred_line_length)
11012 }
11013 }
11014 }
11015
11016 pub fn set_soft_wrap_mode(
11017 &mut self,
11018 mode: language_settings::SoftWrap,
11019 cx: &mut ViewContext<Self>,
11020 ) {
11021 self.soft_wrap_mode_override = Some(mode);
11022 cx.notify();
11023 }
11024
11025 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
11026 self.text_style_refinement = Some(style);
11027 }
11028
11029 /// called by the Element so we know what style we were most recently rendered with.
11030 pub(crate) fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
11031 let rem_size = cx.rem_size();
11032 self.display_map.update(cx, |map, cx| {
11033 map.set_font(
11034 style.text.font(),
11035 style.text.font_size.to_pixels(rem_size),
11036 cx,
11037 )
11038 });
11039 self.style = Some(style);
11040 }
11041
11042 pub fn style(&self) -> Option<&EditorStyle> {
11043 self.style.as_ref()
11044 }
11045
11046 // Called by the element. This method is not designed to be called outside of the editor
11047 // element's layout code because it does not notify when rewrapping is computed synchronously.
11048 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
11049 self.display_map
11050 .update(cx, |map, cx| map.set_wrap_width(width, cx))
11051 }
11052
11053 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
11054 if self.soft_wrap_mode_override.is_some() {
11055 self.soft_wrap_mode_override.take();
11056 } else {
11057 let soft_wrap = match self.soft_wrap_mode(cx) {
11058 SoftWrap::GitDiff => return,
11059 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
11060 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
11061 language_settings::SoftWrap::None
11062 }
11063 };
11064 self.soft_wrap_mode_override = Some(soft_wrap);
11065 }
11066 cx.notify();
11067 }
11068
11069 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, cx: &mut ViewContext<Self>) {
11070 let Some(workspace) = self.workspace() else {
11071 return;
11072 };
11073 let fs = workspace.read(cx).app_state().fs.clone();
11074 let current_show = TabBarSettings::get_global(cx).show;
11075 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
11076 setting.show = Some(!current_show);
11077 });
11078 }
11079
11080 pub fn toggle_indent_guides(&mut self, _: &ToggleIndentGuides, cx: &mut ViewContext<Self>) {
11081 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
11082 self.buffer
11083 .read(cx)
11084 .settings_at(0, cx)
11085 .indent_guides
11086 .enabled
11087 });
11088 self.show_indent_guides = Some(!currently_enabled);
11089 cx.notify();
11090 }
11091
11092 fn should_show_indent_guides(&self) -> Option<bool> {
11093 self.show_indent_guides
11094 }
11095
11096 pub fn toggle_line_numbers(&mut self, _: &ToggleLineNumbers, cx: &mut ViewContext<Self>) {
11097 let mut editor_settings = EditorSettings::get_global(cx).clone();
11098 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
11099 EditorSettings::override_global(editor_settings, cx);
11100 }
11101
11102 pub fn should_use_relative_line_numbers(&self, cx: &WindowContext) -> bool {
11103 self.use_relative_line_numbers
11104 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
11105 }
11106
11107 pub fn toggle_relative_line_numbers(
11108 &mut self,
11109 _: &ToggleRelativeLineNumbers,
11110 cx: &mut ViewContext<Self>,
11111 ) {
11112 let is_relative = self.should_use_relative_line_numbers(cx);
11113 self.set_relative_line_number(Some(!is_relative), cx)
11114 }
11115
11116 pub fn set_relative_line_number(
11117 &mut self,
11118 is_relative: Option<bool>,
11119 cx: &mut ViewContext<Self>,
11120 ) {
11121 self.use_relative_line_numbers = is_relative;
11122 cx.notify();
11123 }
11124
11125 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
11126 self.show_gutter = show_gutter;
11127 cx.notify();
11128 }
11129
11130 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut ViewContext<Self>) {
11131 self.show_line_numbers = Some(show_line_numbers);
11132 cx.notify();
11133 }
11134
11135 pub fn set_show_git_diff_gutter(
11136 &mut self,
11137 show_git_diff_gutter: bool,
11138 cx: &mut ViewContext<Self>,
11139 ) {
11140 self.show_git_diff_gutter = Some(show_git_diff_gutter);
11141 cx.notify();
11142 }
11143
11144 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut ViewContext<Self>) {
11145 self.show_code_actions = Some(show_code_actions);
11146 cx.notify();
11147 }
11148
11149 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut ViewContext<Self>) {
11150 self.show_runnables = Some(show_runnables);
11151 cx.notify();
11152 }
11153
11154 pub fn set_masked(&mut self, masked: bool, cx: &mut ViewContext<Self>) {
11155 if self.display_map.read(cx).masked != masked {
11156 self.display_map.update(cx, |map, _| map.masked = masked);
11157 }
11158 cx.notify()
11159 }
11160
11161 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut ViewContext<Self>) {
11162 self.show_wrap_guides = Some(show_wrap_guides);
11163 cx.notify();
11164 }
11165
11166 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut ViewContext<Self>) {
11167 self.show_indent_guides = Some(show_indent_guides);
11168 cx.notify();
11169 }
11170
11171 pub fn working_directory(&self, cx: &WindowContext) -> Option<PathBuf> {
11172 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
11173 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
11174 if let Some(dir) = file.abs_path(cx).parent() {
11175 return Some(dir.to_owned());
11176 }
11177 }
11178
11179 if let Some(project_path) = buffer.read(cx).project_path(cx) {
11180 return Some(project_path.path.to_path_buf());
11181 }
11182 }
11183
11184 None
11185 }
11186
11187 fn target_file<'a>(&self, cx: &'a AppContext) -> Option<&'a dyn language::LocalFile> {
11188 self.active_excerpt(cx)?
11189 .1
11190 .read(cx)
11191 .file()
11192 .and_then(|f| f.as_local())
11193 }
11194
11195 pub fn reveal_in_finder(&mut self, _: &RevealInFileManager, cx: &mut ViewContext<Self>) {
11196 if let Some(target) = self.target_file(cx) {
11197 cx.reveal_path(&target.abs_path(cx));
11198 }
11199 }
11200
11201 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
11202 if let Some(file) = self.target_file(cx) {
11203 if let Some(path) = file.abs_path(cx).to_str() {
11204 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
11205 }
11206 }
11207 }
11208
11209 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
11210 if let Some(file) = self.target_file(cx) {
11211 if let Some(path) = file.path().to_str() {
11212 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
11213 }
11214 }
11215 }
11216
11217 pub fn toggle_git_blame(&mut self, _: &ToggleGitBlame, cx: &mut ViewContext<Self>) {
11218 self.show_git_blame_gutter = !self.show_git_blame_gutter;
11219
11220 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
11221 self.start_git_blame(true, cx);
11222 }
11223
11224 cx.notify();
11225 }
11226
11227 pub fn toggle_git_blame_inline(
11228 &mut self,
11229 _: &ToggleGitBlameInline,
11230 cx: &mut ViewContext<Self>,
11231 ) {
11232 self.toggle_git_blame_inline_internal(true, cx);
11233 cx.notify();
11234 }
11235
11236 pub fn git_blame_inline_enabled(&self) -> bool {
11237 self.git_blame_inline_enabled
11238 }
11239
11240 pub fn toggle_selection_menu(&mut self, _: &ToggleSelectionMenu, cx: &mut ViewContext<Self>) {
11241 self.show_selection_menu = self
11242 .show_selection_menu
11243 .map(|show_selections_menu| !show_selections_menu)
11244 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
11245
11246 cx.notify();
11247 }
11248
11249 pub fn selection_menu_enabled(&self, cx: &AppContext) -> bool {
11250 self.show_selection_menu
11251 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
11252 }
11253
11254 fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
11255 if let Some(project) = self.project.as_ref() {
11256 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
11257 return;
11258 };
11259
11260 if buffer.read(cx).file().is_none() {
11261 return;
11262 }
11263
11264 let focused = self.focus_handle(cx).contains_focused(cx);
11265
11266 let project = project.clone();
11267 let blame =
11268 cx.new_model(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
11269 self.blame_subscription = Some(cx.observe(&blame, |_, _, cx| cx.notify()));
11270 self.blame = Some(blame);
11271 }
11272 }
11273
11274 fn toggle_git_blame_inline_internal(
11275 &mut self,
11276 user_triggered: bool,
11277 cx: &mut ViewContext<Self>,
11278 ) {
11279 if self.git_blame_inline_enabled {
11280 self.git_blame_inline_enabled = false;
11281 self.show_git_blame_inline = false;
11282 self.show_git_blame_inline_delay_task.take();
11283 } else {
11284 self.git_blame_inline_enabled = true;
11285 self.start_git_blame_inline(user_triggered, cx);
11286 }
11287
11288 cx.notify();
11289 }
11290
11291 fn start_git_blame_inline(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
11292 self.start_git_blame(user_triggered, cx);
11293
11294 if ProjectSettings::get_global(cx)
11295 .git
11296 .inline_blame_delay()
11297 .is_some()
11298 {
11299 self.start_inline_blame_timer(cx);
11300 } else {
11301 self.show_git_blame_inline = true
11302 }
11303 }
11304
11305 pub fn blame(&self) -> Option<&Model<GitBlame>> {
11306 self.blame.as_ref()
11307 }
11308
11309 pub fn show_git_blame_gutter(&self) -> bool {
11310 self.show_git_blame_gutter
11311 }
11312
11313 pub fn render_git_blame_gutter(&mut self, cx: &mut WindowContext) -> bool {
11314 self.show_git_blame_gutter && self.has_blame_entries(cx)
11315 }
11316
11317 pub fn render_git_blame_inline(&mut self, cx: &mut WindowContext) -> bool {
11318 self.show_git_blame_inline
11319 && self.focus_handle.is_focused(cx)
11320 && !self.newest_selection_head_on_empty_line(cx)
11321 && self.has_blame_entries(cx)
11322 }
11323
11324 fn has_blame_entries(&self, cx: &mut WindowContext) -> bool {
11325 self.blame()
11326 .map_or(false, |blame| blame.read(cx).has_generated_entries())
11327 }
11328
11329 fn newest_selection_head_on_empty_line(&mut self, cx: &mut WindowContext) -> bool {
11330 let cursor_anchor = self.selections.newest_anchor().head();
11331
11332 let snapshot = self.buffer.read(cx).snapshot(cx);
11333 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
11334
11335 snapshot.line_len(buffer_row) == 0
11336 }
11337
11338 fn get_permalink_to_line(&mut self, cx: &mut ViewContext<Self>) -> Task<Result<url::Url>> {
11339 let buffer_and_selection = maybe!({
11340 let selection = self.selections.newest::<Point>(cx);
11341 let selection_range = selection.range();
11342
11343 let (buffer, selection) = if let Some(buffer) = self.buffer().read(cx).as_singleton() {
11344 (buffer, selection_range.start.row..selection_range.end.row)
11345 } else {
11346 let buffer_ranges = self
11347 .buffer()
11348 .read(cx)
11349 .range_to_buffer_ranges(selection_range, cx);
11350
11351 let (buffer, range, _) = if selection.reversed {
11352 buffer_ranges.first()
11353 } else {
11354 buffer_ranges.last()
11355 }?;
11356
11357 let snapshot = buffer.read(cx).snapshot();
11358 let selection = text::ToPoint::to_point(&range.start, &snapshot).row
11359 ..text::ToPoint::to_point(&range.end, &snapshot).row;
11360 (buffer.clone(), selection)
11361 };
11362
11363 Some((buffer, selection))
11364 });
11365
11366 let Some((buffer, selection)) = buffer_and_selection else {
11367 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
11368 };
11369
11370 let Some(project) = self.project.as_ref() else {
11371 return Task::ready(Err(anyhow!("editor does not have project")));
11372 };
11373
11374 project.update(cx, |project, cx| {
11375 project.get_permalink_to_line(&buffer, selection, cx)
11376 })
11377 }
11378
11379 pub fn copy_permalink_to_line(&mut self, _: &CopyPermalinkToLine, cx: &mut ViewContext<Self>) {
11380 let permalink_task = self.get_permalink_to_line(cx);
11381 let workspace = self.workspace();
11382
11383 cx.spawn(|_, mut cx| async move {
11384 match permalink_task.await {
11385 Ok(permalink) => {
11386 cx.update(|cx| {
11387 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
11388 })
11389 .ok();
11390 }
11391 Err(err) => {
11392 let message = format!("Failed to copy permalink: {err}");
11393
11394 Err::<(), anyhow::Error>(err).log_err();
11395
11396 if let Some(workspace) = workspace {
11397 workspace
11398 .update(&mut cx, |workspace, cx| {
11399 struct CopyPermalinkToLine;
11400
11401 workspace.show_toast(
11402 Toast::new(
11403 NotificationId::unique::<CopyPermalinkToLine>(),
11404 message,
11405 ),
11406 cx,
11407 )
11408 })
11409 .ok();
11410 }
11411 }
11412 }
11413 })
11414 .detach();
11415 }
11416
11417 pub fn copy_file_location(&mut self, _: &CopyFileLocation, cx: &mut ViewContext<Self>) {
11418 let selection = self.selections.newest::<Point>(cx).start.row + 1;
11419 if let Some(file) = self.target_file(cx) {
11420 if let Some(path) = file.path().to_str() {
11421 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
11422 }
11423 }
11424 }
11425
11426 pub fn open_permalink_to_line(&mut self, _: &OpenPermalinkToLine, cx: &mut ViewContext<Self>) {
11427 let permalink_task = self.get_permalink_to_line(cx);
11428 let workspace = self.workspace();
11429
11430 cx.spawn(|_, mut cx| async move {
11431 match permalink_task.await {
11432 Ok(permalink) => {
11433 cx.update(|cx| {
11434 cx.open_url(permalink.as_ref());
11435 })
11436 .ok();
11437 }
11438 Err(err) => {
11439 let message = format!("Failed to open permalink: {err}");
11440
11441 Err::<(), anyhow::Error>(err).log_err();
11442
11443 if let Some(workspace) = workspace {
11444 workspace
11445 .update(&mut cx, |workspace, cx| {
11446 struct OpenPermalinkToLine;
11447
11448 workspace.show_toast(
11449 Toast::new(
11450 NotificationId::unique::<OpenPermalinkToLine>(),
11451 message,
11452 ),
11453 cx,
11454 )
11455 })
11456 .ok();
11457 }
11458 }
11459 }
11460 })
11461 .detach();
11462 }
11463
11464 pub fn insert_uuid_v4(&mut self, _: &InsertUuidV4, cx: &mut ViewContext<Self>) {
11465 self.insert_uuid(UuidVersion::V4, cx);
11466 }
11467
11468 pub fn insert_uuid_v7(&mut self, _: &InsertUuidV7, cx: &mut ViewContext<Self>) {
11469 self.insert_uuid(UuidVersion::V7, cx);
11470 }
11471
11472 fn insert_uuid(&mut self, version: UuidVersion, cx: &mut ViewContext<Self>) {
11473 self.transact(cx, |this, cx| {
11474 let edits = this
11475 .selections
11476 .all::<Point>(cx)
11477 .into_iter()
11478 .map(|selection| {
11479 let uuid = match version {
11480 UuidVersion::V4 => uuid::Uuid::new_v4(),
11481 UuidVersion::V7 => uuid::Uuid::now_v7(),
11482 };
11483
11484 (selection.range(), uuid.to_string())
11485 });
11486 this.edit(edits, cx);
11487 this.refresh_inline_completion(true, false, cx);
11488 });
11489 }
11490
11491 /// Adds a row highlight for the given range. If a row has multiple highlights, the
11492 /// last highlight added will be used.
11493 ///
11494 /// If the range ends at the beginning of a line, then that line will not be highlighted.
11495 pub fn highlight_rows<T: 'static>(
11496 &mut self,
11497 range: Range<Anchor>,
11498 color: Hsla,
11499 should_autoscroll: bool,
11500 cx: &mut ViewContext<Self>,
11501 ) {
11502 let snapshot = self.buffer().read(cx).snapshot(cx);
11503 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
11504 let ix = row_highlights.binary_search_by(|highlight| {
11505 Ordering::Equal
11506 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
11507 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
11508 });
11509
11510 if let Err(mut ix) = ix {
11511 let index = post_inc(&mut self.highlight_order);
11512
11513 // If this range intersects with the preceding highlight, then merge it with
11514 // the preceding highlight. Otherwise insert a new highlight.
11515 let mut merged = false;
11516 if ix > 0 {
11517 let prev_highlight = &mut row_highlights[ix - 1];
11518 if prev_highlight
11519 .range
11520 .end
11521 .cmp(&range.start, &snapshot)
11522 .is_ge()
11523 {
11524 ix -= 1;
11525 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
11526 prev_highlight.range.end = range.end;
11527 }
11528 merged = true;
11529 prev_highlight.index = index;
11530 prev_highlight.color = color;
11531 prev_highlight.should_autoscroll = should_autoscroll;
11532 }
11533 }
11534
11535 if !merged {
11536 row_highlights.insert(
11537 ix,
11538 RowHighlight {
11539 range: range.clone(),
11540 index,
11541 color,
11542 should_autoscroll,
11543 },
11544 );
11545 }
11546
11547 // If any of the following highlights intersect with this one, merge them.
11548 while let Some(next_highlight) = row_highlights.get(ix + 1) {
11549 let highlight = &row_highlights[ix];
11550 if next_highlight
11551 .range
11552 .start
11553 .cmp(&highlight.range.end, &snapshot)
11554 .is_le()
11555 {
11556 if next_highlight
11557 .range
11558 .end
11559 .cmp(&highlight.range.end, &snapshot)
11560 .is_gt()
11561 {
11562 row_highlights[ix].range.end = next_highlight.range.end;
11563 }
11564 row_highlights.remove(ix + 1);
11565 } else {
11566 break;
11567 }
11568 }
11569 }
11570 }
11571
11572 /// Remove any highlighted row ranges of the given type that intersect the
11573 /// given ranges.
11574 pub fn remove_highlighted_rows<T: 'static>(
11575 &mut self,
11576 ranges_to_remove: Vec<Range<Anchor>>,
11577 cx: &mut ViewContext<Self>,
11578 ) {
11579 let snapshot = self.buffer().read(cx).snapshot(cx);
11580 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
11581 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
11582 row_highlights.retain(|highlight| {
11583 while let Some(range_to_remove) = ranges_to_remove.peek() {
11584 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
11585 Ordering::Less | Ordering::Equal => {
11586 ranges_to_remove.next();
11587 }
11588 Ordering::Greater => {
11589 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
11590 Ordering::Less | Ordering::Equal => {
11591 return false;
11592 }
11593 Ordering::Greater => break,
11594 }
11595 }
11596 }
11597 }
11598
11599 true
11600 })
11601 }
11602
11603 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
11604 pub fn clear_row_highlights<T: 'static>(&mut self) {
11605 self.highlighted_rows.remove(&TypeId::of::<T>());
11606 }
11607
11608 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
11609 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
11610 self.highlighted_rows
11611 .get(&TypeId::of::<T>())
11612 .map_or(&[] as &[_], |vec| vec.as_slice())
11613 .iter()
11614 .map(|highlight| (highlight.range.clone(), highlight.color))
11615 }
11616
11617 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
11618 /// Rerturns a map of display rows that are highlighted and their corresponding highlight color.
11619 /// Allows to ignore certain kinds of highlights.
11620 pub fn highlighted_display_rows(
11621 &mut self,
11622 cx: &mut WindowContext,
11623 ) -> BTreeMap<DisplayRow, Hsla> {
11624 let snapshot = self.snapshot(cx);
11625 let mut used_highlight_orders = HashMap::default();
11626 self.highlighted_rows
11627 .iter()
11628 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
11629 .fold(
11630 BTreeMap::<DisplayRow, Hsla>::new(),
11631 |mut unique_rows, highlight| {
11632 let start = highlight.range.start.to_display_point(&snapshot);
11633 let end = highlight.range.end.to_display_point(&snapshot);
11634 let start_row = start.row().0;
11635 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
11636 && end.column() == 0
11637 {
11638 end.row().0.saturating_sub(1)
11639 } else {
11640 end.row().0
11641 };
11642 for row in start_row..=end_row {
11643 let used_index =
11644 used_highlight_orders.entry(row).or_insert(highlight.index);
11645 if highlight.index >= *used_index {
11646 *used_index = highlight.index;
11647 unique_rows.insert(DisplayRow(row), highlight.color);
11648 }
11649 }
11650 unique_rows
11651 },
11652 )
11653 }
11654
11655 pub fn highlighted_display_row_for_autoscroll(
11656 &self,
11657 snapshot: &DisplaySnapshot,
11658 ) -> Option<DisplayRow> {
11659 self.highlighted_rows
11660 .values()
11661 .flat_map(|highlighted_rows| highlighted_rows.iter())
11662 .filter_map(|highlight| {
11663 if highlight.should_autoscroll {
11664 Some(highlight.range.start.to_display_point(snapshot).row())
11665 } else {
11666 None
11667 }
11668 })
11669 .min()
11670 }
11671
11672 pub fn set_search_within_ranges(
11673 &mut self,
11674 ranges: &[Range<Anchor>],
11675 cx: &mut ViewContext<Self>,
11676 ) {
11677 self.highlight_background::<SearchWithinRange>(
11678 ranges,
11679 |colors| colors.editor_document_highlight_read_background,
11680 cx,
11681 )
11682 }
11683
11684 pub fn set_breadcrumb_header(&mut self, new_header: String) {
11685 self.breadcrumb_header = Some(new_header);
11686 }
11687
11688 pub fn clear_search_within_ranges(&mut self, cx: &mut ViewContext<Self>) {
11689 self.clear_background_highlights::<SearchWithinRange>(cx);
11690 }
11691
11692 pub fn highlight_background<T: 'static>(
11693 &mut self,
11694 ranges: &[Range<Anchor>],
11695 color_fetcher: fn(&ThemeColors) -> Hsla,
11696 cx: &mut ViewContext<Self>,
11697 ) {
11698 self.background_highlights
11699 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
11700 self.scrollbar_marker_state.dirty = true;
11701 cx.notify();
11702 }
11703
11704 pub fn clear_background_highlights<T: 'static>(
11705 &mut self,
11706 cx: &mut ViewContext<Self>,
11707 ) -> Option<BackgroundHighlight> {
11708 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
11709 if !text_highlights.1.is_empty() {
11710 self.scrollbar_marker_state.dirty = true;
11711 cx.notify();
11712 }
11713 Some(text_highlights)
11714 }
11715
11716 pub fn highlight_gutter<T: 'static>(
11717 &mut self,
11718 ranges: &[Range<Anchor>],
11719 color_fetcher: fn(&AppContext) -> Hsla,
11720 cx: &mut ViewContext<Self>,
11721 ) {
11722 self.gutter_highlights
11723 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
11724 cx.notify();
11725 }
11726
11727 pub fn clear_gutter_highlights<T: 'static>(
11728 &mut self,
11729 cx: &mut ViewContext<Self>,
11730 ) -> Option<GutterHighlight> {
11731 cx.notify();
11732 self.gutter_highlights.remove(&TypeId::of::<T>())
11733 }
11734
11735 #[cfg(feature = "test-support")]
11736 pub fn all_text_background_highlights(
11737 &mut self,
11738 cx: &mut ViewContext<Self>,
11739 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11740 let snapshot = self.snapshot(cx);
11741 let buffer = &snapshot.buffer_snapshot;
11742 let start = buffer.anchor_before(0);
11743 let end = buffer.anchor_after(buffer.len());
11744 let theme = cx.theme().colors();
11745 self.background_highlights_in_range(start..end, &snapshot, theme)
11746 }
11747
11748 #[cfg(feature = "test-support")]
11749 pub fn search_background_highlights(
11750 &mut self,
11751 cx: &mut ViewContext<Self>,
11752 ) -> Vec<Range<Point>> {
11753 let snapshot = self.buffer().read(cx).snapshot(cx);
11754
11755 let highlights = self
11756 .background_highlights
11757 .get(&TypeId::of::<items::BufferSearchHighlights>());
11758
11759 if let Some((_color, ranges)) = highlights {
11760 ranges
11761 .iter()
11762 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
11763 .collect_vec()
11764 } else {
11765 vec![]
11766 }
11767 }
11768
11769 fn document_highlights_for_position<'a>(
11770 &'a self,
11771 position: Anchor,
11772 buffer: &'a MultiBufferSnapshot,
11773 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
11774 let read_highlights = self
11775 .background_highlights
11776 .get(&TypeId::of::<DocumentHighlightRead>())
11777 .map(|h| &h.1);
11778 let write_highlights = self
11779 .background_highlights
11780 .get(&TypeId::of::<DocumentHighlightWrite>())
11781 .map(|h| &h.1);
11782 let left_position = position.bias_left(buffer);
11783 let right_position = position.bias_right(buffer);
11784 read_highlights
11785 .into_iter()
11786 .chain(write_highlights)
11787 .flat_map(move |ranges| {
11788 let start_ix = match ranges.binary_search_by(|probe| {
11789 let cmp = probe.end.cmp(&left_position, buffer);
11790 if cmp.is_ge() {
11791 Ordering::Greater
11792 } else {
11793 Ordering::Less
11794 }
11795 }) {
11796 Ok(i) | Err(i) => i,
11797 };
11798
11799 ranges[start_ix..]
11800 .iter()
11801 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
11802 })
11803 }
11804
11805 pub fn has_background_highlights<T: 'static>(&self) -> bool {
11806 self.background_highlights
11807 .get(&TypeId::of::<T>())
11808 .map_or(false, |(_, highlights)| !highlights.is_empty())
11809 }
11810
11811 pub fn background_highlights_in_range(
11812 &self,
11813 search_range: Range<Anchor>,
11814 display_snapshot: &DisplaySnapshot,
11815 theme: &ThemeColors,
11816 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11817 let mut results = Vec::new();
11818 for (color_fetcher, ranges) in self.background_highlights.values() {
11819 let color = color_fetcher(theme);
11820 let start_ix = match ranges.binary_search_by(|probe| {
11821 let cmp = probe
11822 .end
11823 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11824 if cmp.is_gt() {
11825 Ordering::Greater
11826 } else {
11827 Ordering::Less
11828 }
11829 }) {
11830 Ok(i) | Err(i) => i,
11831 };
11832 for range in &ranges[start_ix..] {
11833 if range
11834 .start
11835 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11836 .is_ge()
11837 {
11838 break;
11839 }
11840
11841 let start = range.start.to_display_point(display_snapshot);
11842 let end = range.end.to_display_point(display_snapshot);
11843 results.push((start..end, color))
11844 }
11845 }
11846 results
11847 }
11848
11849 pub fn background_highlight_row_ranges<T: 'static>(
11850 &self,
11851 search_range: Range<Anchor>,
11852 display_snapshot: &DisplaySnapshot,
11853 count: usize,
11854 ) -> Vec<RangeInclusive<DisplayPoint>> {
11855 let mut results = Vec::new();
11856 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
11857 return vec![];
11858 };
11859
11860 let start_ix = match ranges.binary_search_by(|probe| {
11861 let cmp = probe
11862 .end
11863 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11864 if cmp.is_gt() {
11865 Ordering::Greater
11866 } else {
11867 Ordering::Less
11868 }
11869 }) {
11870 Ok(i) | Err(i) => i,
11871 };
11872 let mut push_region = |start: Option<Point>, end: Option<Point>| {
11873 if let (Some(start_display), Some(end_display)) = (start, end) {
11874 results.push(
11875 start_display.to_display_point(display_snapshot)
11876 ..=end_display.to_display_point(display_snapshot),
11877 );
11878 }
11879 };
11880 let mut start_row: Option<Point> = None;
11881 let mut end_row: Option<Point> = None;
11882 if ranges.len() > count {
11883 return Vec::new();
11884 }
11885 for range in &ranges[start_ix..] {
11886 if range
11887 .start
11888 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11889 .is_ge()
11890 {
11891 break;
11892 }
11893 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
11894 if let Some(current_row) = &end_row {
11895 if end.row == current_row.row {
11896 continue;
11897 }
11898 }
11899 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
11900 if start_row.is_none() {
11901 assert_eq!(end_row, None);
11902 start_row = Some(start);
11903 end_row = Some(end);
11904 continue;
11905 }
11906 if let Some(current_end) = end_row.as_mut() {
11907 if start.row > current_end.row + 1 {
11908 push_region(start_row, end_row);
11909 start_row = Some(start);
11910 end_row = Some(end);
11911 } else {
11912 // Merge two hunks.
11913 *current_end = end;
11914 }
11915 } else {
11916 unreachable!();
11917 }
11918 }
11919 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
11920 push_region(start_row, end_row);
11921 results
11922 }
11923
11924 pub fn gutter_highlights_in_range(
11925 &self,
11926 search_range: Range<Anchor>,
11927 display_snapshot: &DisplaySnapshot,
11928 cx: &AppContext,
11929 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11930 let mut results = Vec::new();
11931 for (color_fetcher, ranges) in self.gutter_highlights.values() {
11932 let color = color_fetcher(cx);
11933 let start_ix = match ranges.binary_search_by(|probe| {
11934 let cmp = probe
11935 .end
11936 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11937 if cmp.is_gt() {
11938 Ordering::Greater
11939 } else {
11940 Ordering::Less
11941 }
11942 }) {
11943 Ok(i) | Err(i) => i,
11944 };
11945 for range in &ranges[start_ix..] {
11946 if range
11947 .start
11948 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11949 .is_ge()
11950 {
11951 break;
11952 }
11953
11954 let start = range.start.to_display_point(display_snapshot);
11955 let end = range.end.to_display_point(display_snapshot);
11956 results.push((start..end, color))
11957 }
11958 }
11959 results
11960 }
11961
11962 /// Get the text ranges corresponding to the redaction query
11963 pub fn redacted_ranges(
11964 &self,
11965 search_range: Range<Anchor>,
11966 display_snapshot: &DisplaySnapshot,
11967 cx: &WindowContext,
11968 ) -> Vec<Range<DisplayPoint>> {
11969 display_snapshot
11970 .buffer_snapshot
11971 .redacted_ranges(search_range, |file| {
11972 if let Some(file) = file {
11973 file.is_private()
11974 && EditorSettings::get(
11975 Some(SettingsLocation {
11976 worktree_id: file.worktree_id(cx),
11977 path: file.path().as_ref(),
11978 }),
11979 cx,
11980 )
11981 .redact_private_values
11982 } else {
11983 false
11984 }
11985 })
11986 .map(|range| {
11987 range.start.to_display_point(display_snapshot)
11988 ..range.end.to_display_point(display_snapshot)
11989 })
11990 .collect()
11991 }
11992
11993 pub fn highlight_text<T: 'static>(
11994 &mut self,
11995 ranges: Vec<Range<Anchor>>,
11996 style: HighlightStyle,
11997 cx: &mut ViewContext<Self>,
11998 ) {
11999 self.display_map.update(cx, |map, _| {
12000 map.highlight_text(TypeId::of::<T>(), ranges, style)
12001 });
12002 cx.notify();
12003 }
12004
12005 pub(crate) fn highlight_inlays<T: 'static>(
12006 &mut self,
12007 highlights: Vec<InlayHighlight>,
12008 style: HighlightStyle,
12009 cx: &mut ViewContext<Self>,
12010 ) {
12011 self.display_map.update(cx, |map, _| {
12012 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
12013 });
12014 cx.notify();
12015 }
12016
12017 pub fn text_highlights<'a, T: 'static>(
12018 &'a self,
12019 cx: &'a AppContext,
12020 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
12021 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
12022 }
12023
12024 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
12025 let cleared = self
12026 .display_map
12027 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
12028 if cleared {
12029 cx.notify();
12030 }
12031 }
12032
12033 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
12034 (self.read_only(cx) || self.blink_manager.read(cx).visible())
12035 && self.focus_handle.is_focused(cx)
12036 }
12037
12038 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut ViewContext<Self>) {
12039 self.show_cursor_when_unfocused = is_enabled;
12040 cx.notify();
12041 }
12042
12043 pub fn lsp_store(&self, cx: &AppContext) -> Option<Model<LspStore>> {
12044 self.project
12045 .as_ref()
12046 .map(|project| project.read(cx).lsp_store())
12047 }
12048
12049 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
12050 cx.notify();
12051 }
12052
12053 fn on_buffer_event(
12054 &mut self,
12055 multibuffer: Model<MultiBuffer>,
12056 event: &multi_buffer::Event,
12057 cx: &mut ViewContext<Self>,
12058 ) {
12059 match event {
12060 multi_buffer::Event::Edited {
12061 singleton_buffer_edited,
12062 edited_buffer: buffer_edited,
12063 } => {
12064 self.scrollbar_marker_state.dirty = true;
12065 self.active_indent_guides_state.dirty = true;
12066 self.refresh_active_diagnostics(cx);
12067 self.refresh_code_actions(cx);
12068 if self.has_active_inline_completion() {
12069 self.update_visible_inline_completion(cx);
12070 }
12071 if let Some(buffer) = buffer_edited {
12072 let buffer_id = buffer.read(cx).remote_id();
12073 if !self.registered_buffers.contains_key(&buffer_id) {
12074 if let Some(lsp_store) = self.lsp_store(cx) {
12075 lsp_store.update(cx, |lsp_store, cx| {
12076 self.registered_buffers.insert(
12077 buffer_id,
12078 lsp_store.register_buffer_with_language_servers(&buffer, cx),
12079 );
12080 })
12081 }
12082 }
12083 }
12084 cx.emit(EditorEvent::BufferEdited);
12085 cx.emit(SearchEvent::MatchesInvalidated);
12086 if *singleton_buffer_edited {
12087 if let Some(project) = &self.project {
12088 let project = project.read(cx);
12089 #[allow(clippy::mutable_key_type)]
12090 let languages_affected = multibuffer
12091 .read(cx)
12092 .all_buffers()
12093 .into_iter()
12094 .filter_map(|buffer| {
12095 let buffer = buffer.read(cx);
12096 let language = buffer.language()?;
12097 if project.is_local()
12098 && project
12099 .language_servers_for_local_buffer(buffer, cx)
12100 .count()
12101 == 0
12102 {
12103 None
12104 } else {
12105 Some(language)
12106 }
12107 })
12108 .cloned()
12109 .collect::<HashSet<_>>();
12110 if !languages_affected.is_empty() {
12111 self.refresh_inlay_hints(
12112 InlayHintRefreshReason::BufferEdited(languages_affected),
12113 cx,
12114 );
12115 }
12116 }
12117 }
12118
12119 let Some(project) = &self.project else { return };
12120 let (telemetry, is_via_ssh) = {
12121 let project = project.read(cx);
12122 let telemetry = project.client().telemetry().clone();
12123 let is_via_ssh = project.is_via_ssh();
12124 (telemetry, is_via_ssh)
12125 };
12126 refresh_linked_ranges(self, cx);
12127 telemetry.log_edit_event("editor", is_via_ssh);
12128 }
12129 multi_buffer::Event::ExcerptsAdded {
12130 buffer,
12131 predecessor,
12132 excerpts,
12133 } => {
12134 self.tasks_update_task = Some(self.refresh_runnables(cx));
12135 let buffer_id = buffer.read(cx).remote_id();
12136 if !self.diff_map.diff_bases.contains_key(&buffer_id) {
12137 if let Some(project) = &self.project {
12138 get_unstaged_changes_for_buffers(project, [buffer.clone()], cx);
12139 }
12140 }
12141 cx.emit(EditorEvent::ExcerptsAdded {
12142 buffer: buffer.clone(),
12143 predecessor: *predecessor,
12144 excerpts: excerpts.clone(),
12145 });
12146 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
12147 }
12148 multi_buffer::Event::ExcerptsRemoved { ids } => {
12149 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
12150 let buffer = self.buffer.read(cx);
12151 self.registered_buffers
12152 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
12153 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
12154 }
12155 multi_buffer::Event::ExcerptsEdited { ids } => {
12156 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
12157 }
12158 multi_buffer::Event::ExcerptsExpanded { ids } => {
12159 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
12160 }
12161 multi_buffer::Event::Reparsed(buffer_id) => {
12162 self.tasks_update_task = Some(self.refresh_runnables(cx));
12163
12164 cx.emit(EditorEvent::Reparsed(*buffer_id));
12165 }
12166 multi_buffer::Event::LanguageChanged(buffer_id) => {
12167 linked_editing_ranges::refresh_linked_ranges(self, cx);
12168 cx.emit(EditorEvent::Reparsed(*buffer_id));
12169 cx.notify();
12170 }
12171 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
12172 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
12173 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
12174 cx.emit(EditorEvent::TitleChanged)
12175 }
12176 // multi_buffer::Event::DiffBaseChanged => {
12177 // self.scrollbar_marker_state.dirty = true;
12178 // cx.emit(EditorEvent::DiffBaseChanged);
12179 // cx.notify();
12180 // }
12181 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
12182 multi_buffer::Event::DiagnosticsUpdated => {
12183 self.refresh_active_diagnostics(cx);
12184 self.scrollbar_marker_state.dirty = true;
12185 cx.notify();
12186 }
12187 _ => {}
12188 };
12189 }
12190
12191 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
12192 cx.notify();
12193 }
12194
12195 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
12196 self.tasks_update_task = Some(self.refresh_runnables(cx));
12197 self.refresh_inline_completion(true, false, cx);
12198 self.refresh_inlay_hints(
12199 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
12200 self.selections.newest_anchor().head(),
12201 &self.buffer.read(cx).snapshot(cx),
12202 cx,
12203 )),
12204 cx,
12205 );
12206
12207 let old_cursor_shape = self.cursor_shape;
12208
12209 {
12210 let editor_settings = EditorSettings::get_global(cx);
12211 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
12212 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
12213 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
12214 }
12215
12216 if old_cursor_shape != self.cursor_shape {
12217 cx.emit(EditorEvent::CursorShapeChanged);
12218 }
12219
12220 let project_settings = ProjectSettings::get_global(cx);
12221 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
12222
12223 if self.mode == EditorMode::Full {
12224 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
12225 if self.git_blame_inline_enabled != inline_blame_enabled {
12226 self.toggle_git_blame_inline_internal(false, cx);
12227 }
12228 }
12229
12230 cx.notify();
12231 }
12232
12233 pub fn set_searchable(&mut self, searchable: bool) {
12234 self.searchable = searchable;
12235 }
12236
12237 pub fn searchable(&self) -> bool {
12238 self.searchable
12239 }
12240
12241 fn open_proposed_changes_editor(
12242 &mut self,
12243 _: &OpenProposedChangesEditor,
12244 cx: &mut ViewContext<Self>,
12245 ) {
12246 let Some(workspace) = self.workspace() else {
12247 cx.propagate();
12248 return;
12249 };
12250
12251 let selections = self.selections.all::<usize>(cx);
12252 let buffer = self.buffer.read(cx);
12253 let mut new_selections_by_buffer = HashMap::default();
12254 for selection in selections {
12255 for (buffer, range, _) in
12256 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
12257 {
12258 let mut range = range.to_point(buffer.read(cx));
12259 range.start.column = 0;
12260 range.end.column = buffer.read(cx).line_len(range.end.row);
12261 new_selections_by_buffer
12262 .entry(buffer)
12263 .or_insert(Vec::new())
12264 .push(range)
12265 }
12266 }
12267
12268 let proposed_changes_buffers = new_selections_by_buffer
12269 .into_iter()
12270 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
12271 .collect::<Vec<_>>();
12272 let proposed_changes_editor = cx.new_view(|cx| {
12273 ProposedChangesEditor::new(
12274 "Proposed changes",
12275 proposed_changes_buffers,
12276 self.project.clone(),
12277 cx,
12278 )
12279 });
12280
12281 cx.window_context().defer(move |cx| {
12282 workspace.update(cx, |workspace, cx| {
12283 workspace.active_pane().update(cx, |pane, cx| {
12284 pane.add_item(Box::new(proposed_changes_editor), true, true, None, cx);
12285 });
12286 });
12287 });
12288 }
12289
12290 pub fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext<Self>) {
12291 self.open_excerpts_common(None, true, cx)
12292 }
12293
12294 pub fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
12295 self.open_excerpts_common(None, false, cx)
12296 }
12297
12298 fn open_excerpts_common(
12299 &mut self,
12300 jump_data: Option<JumpData>,
12301 split: bool,
12302 cx: &mut ViewContext<Self>,
12303 ) {
12304 let Some(workspace) = self.workspace() else {
12305 cx.propagate();
12306 return;
12307 };
12308
12309 if self.buffer.read(cx).is_singleton() {
12310 cx.propagate();
12311 return;
12312 }
12313
12314 let mut new_selections_by_buffer = HashMap::default();
12315 match &jump_data {
12316 Some(jump_data) => {
12317 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12318 if let Some(buffer) = multi_buffer_snapshot
12319 .buffer_id_for_excerpt(jump_data.excerpt_id)
12320 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
12321 {
12322 let buffer_snapshot = buffer.read(cx).snapshot();
12323 let jump_to_point = if buffer_snapshot.can_resolve(&jump_data.anchor) {
12324 language::ToPoint::to_point(&jump_data.anchor, &buffer_snapshot)
12325 } else {
12326 buffer_snapshot.clip_point(jump_data.position, Bias::Left)
12327 };
12328 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
12329 new_selections_by_buffer.insert(
12330 buffer,
12331 (
12332 vec![jump_to_offset..jump_to_offset],
12333 Some(jump_data.line_offset_from_top),
12334 ),
12335 );
12336 }
12337 }
12338 None => {
12339 let selections = self.selections.all::<usize>(cx);
12340 let buffer = self.buffer.read(cx);
12341 for selection in selections {
12342 for (mut buffer_handle, mut range, _) in
12343 buffer.range_to_buffer_ranges(selection.range(), cx)
12344 {
12345 // When editing branch buffers, jump to the corresponding location
12346 // in their base buffer.
12347 let buffer = buffer_handle.read(cx);
12348 if let Some(base_buffer) = buffer.base_buffer() {
12349 range = buffer.range_to_version(range, &base_buffer.read(cx).version());
12350 buffer_handle = base_buffer;
12351 }
12352
12353 if selection.reversed {
12354 mem::swap(&mut range.start, &mut range.end);
12355 }
12356 new_selections_by_buffer
12357 .entry(buffer_handle)
12358 .or_insert((Vec::new(), None))
12359 .0
12360 .push(range)
12361 }
12362 }
12363 }
12364 }
12365
12366 if new_selections_by_buffer.is_empty() {
12367 return;
12368 }
12369
12370 // We defer the pane interaction because we ourselves are a workspace item
12371 // and activating a new item causes the pane to call a method on us reentrantly,
12372 // which panics if we're on the stack.
12373 cx.window_context().defer(move |cx| {
12374 workspace.update(cx, |workspace, cx| {
12375 let pane = if split {
12376 workspace.adjacent_pane(cx)
12377 } else {
12378 workspace.active_pane().clone()
12379 };
12380
12381 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
12382 let editor = buffer
12383 .read(cx)
12384 .file()
12385 .is_none()
12386 .then(|| {
12387 // Handle file-less buffers separately: those are not really the project items, so won't have a paroject path or entity id,
12388 // so `workspace.open_project_item` will never find them, always opening a new editor.
12389 // Instead, we try to activate the existing editor in the pane first.
12390 let (editor, pane_item_index) =
12391 pane.read(cx).items().enumerate().find_map(|(i, item)| {
12392 let editor = item.downcast::<Editor>()?;
12393 let singleton_buffer =
12394 editor.read(cx).buffer().read(cx).as_singleton()?;
12395 if singleton_buffer == buffer {
12396 Some((editor, i))
12397 } else {
12398 None
12399 }
12400 })?;
12401 pane.update(cx, |pane, cx| {
12402 pane.activate_item(pane_item_index, true, true, cx)
12403 });
12404 Some(editor)
12405 })
12406 .flatten()
12407 .unwrap_or_else(|| {
12408 workspace.open_project_item::<Self>(
12409 pane.clone(),
12410 buffer,
12411 true,
12412 true,
12413 cx,
12414 )
12415 });
12416
12417 editor.update(cx, |editor, cx| {
12418 let autoscroll = match scroll_offset {
12419 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
12420 None => Autoscroll::newest(),
12421 };
12422 let nav_history = editor.nav_history.take();
12423 editor.change_selections(Some(autoscroll), cx, |s| {
12424 s.select_ranges(ranges);
12425 });
12426 editor.nav_history = nav_history;
12427 });
12428 }
12429 })
12430 });
12431 }
12432
12433 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
12434 let snapshot = self.buffer.read(cx).read(cx);
12435 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
12436 Some(
12437 ranges
12438 .iter()
12439 .map(move |range| {
12440 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
12441 })
12442 .collect(),
12443 )
12444 }
12445
12446 fn selection_replacement_ranges(
12447 &self,
12448 range: Range<OffsetUtf16>,
12449 cx: &mut AppContext,
12450 ) -> Vec<Range<OffsetUtf16>> {
12451 let selections = self.selections.all::<OffsetUtf16>(cx);
12452 let newest_selection = selections
12453 .iter()
12454 .max_by_key(|selection| selection.id)
12455 .unwrap();
12456 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
12457 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
12458 let snapshot = self.buffer.read(cx).read(cx);
12459 selections
12460 .into_iter()
12461 .map(|mut selection| {
12462 selection.start.0 =
12463 (selection.start.0 as isize).saturating_add(start_delta) as usize;
12464 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
12465 snapshot.clip_offset_utf16(selection.start, Bias::Left)
12466 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
12467 })
12468 .collect()
12469 }
12470
12471 fn report_editor_event(
12472 &self,
12473 operation: &'static str,
12474 file_extension: Option<String>,
12475 cx: &AppContext,
12476 ) {
12477 if cfg!(any(test, feature = "test-support")) {
12478 return;
12479 }
12480
12481 let Some(project) = &self.project else { return };
12482
12483 // If None, we are in a file without an extension
12484 let file = self
12485 .buffer
12486 .read(cx)
12487 .as_singleton()
12488 .and_then(|b| b.read(cx).file());
12489 let file_extension = file_extension.or(file
12490 .as_ref()
12491 .and_then(|file| Path::new(file.file_name(cx)).extension())
12492 .and_then(|e| e.to_str())
12493 .map(|a| a.to_string()));
12494
12495 let vim_mode = cx
12496 .global::<SettingsStore>()
12497 .raw_user_settings()
12498 .get("vim_mode")
12499 == Some(&serde_json::Value::Bool(true));
12500
12501 let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
12502 == language::language_settings::InlineCompletionProvider::Copilot;
12503 let copilot_enabled_for_language = self
12504 .buffer
12505 .read(cx)
12506 .settings_at(0, cx)
12507 .show_inline_completions;
12508
12509 let project = project.read(cx);
12510 let telemetry = project.client().telemetry().clone();
12511 telemetry.report_editor_event(
12512 file_extension,
12513 vim_mode,
12514 operation,
12515 copilot_enabled,
12516 copilot_enabled_for_language,
12517 project.is_via_ssh(),
12518 )
12519 }
12520
12521 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
12522 /// with each line being an array of {text, highlight} objects.
12523 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
12524 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
12525 return;
12526 };
12527
12528 #[derive(Serialize)]
12529 struct Chunk<'a> {
12530 text: String,
12531 highlight: Option<&'a str>,
12532 }
12533
12534 let snapshot = buffer.read(cx).snapshot();
12535 let range = self
12536 .selected_text_range(false, cx)
12537 .and_then(|selection| {
12538 if selection.range.is_empty() {
12539 None
12540 } else {
12541 Some(selection.range)
12542 }
12543 })
12544 .unwrap_or_else(|| 0..snapshot.len());
12545
12546 let chunks = snapshot.chunks(range, true);
12547 let mut lines = Vec::new();
12548 let mut line: VecDeque<Chunk> = VecDeque::new();
12549
12550 let Some(style) = self.style.as_ref() else {
12551 return;
12552 };
12553
12554 for chunk in chunks {
12555 let highlight = chunk
12556 .syntax_highlight_id
12557 .and_then(|id| id.name(&style.syntax));
12558 let mut chunk_lines = chunk.text.split('\n').peekable();
12559 while let Some(text) = chunk_lines.next() {
12560 let mut merged_with_last_token = false;
12561 if let Some(last_token) = line.back_mut() {
12562 if last_token.highlight == highlight {
12563 last_token.text.push_str(text);
12564 merged_with_last_token = true;
12565 }
12566 }
12567
12568 if !merged_with_last_token {
12569 line.push_back(Chunk {
12570 text: text.into(),
12571 highlight,
12572 });
12573 }
12574
12575 if chunk_lines.peek().is_some() {
12576 if line.len() > 1 && line.front().unwrap().text.is_empty() {
12577 line.pop_front();
12578 }
12579 if line.len() > 1 && line.back().unwrap().text.is_empty() {
12580 line.pop_back();
12581 }
12582
12583 lines.push(mem::take(&mut line));
12584 }
12585 }
12586 }
12587
12588 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
12589 return;
12590 };
12591 cx.write_to_clipboard(ClipboardItem::new_string(lines));
12592 }
12593
12594 pub fn open_context_menu(&mut self, _: &OpenContextMenu, cx: &mut ViewContext<Self>) {
12595 self.request_autoscroll(Autoscroll::newest(), cx);
12596 let position = self.selections.newest_display(cx).start;
12597 mouse_context_menu::deploy_context_menu(self, None, position, cx);
12598 }
12599
12600 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
12601 &self.inlay_hint_cache
12602 }
12603
12604 pub fn replay_insert_event(
12605 &mut self,
12606 text: &str,
12607 relative_utf16_range: Option<Range<isize>>,
12608 cx: &mut ViewContext<Self>,
12609 ) {
12610 if !self.input_enabled {
12611 cx.emit(EditorEvent::InputIgnored { text: text.into() });
12612 return;
12613 }
12614 if let Some(relative_utf16_range) = relative_utf16_range {
12615 let selections = self.selections.all::<OffsetUtf16>(cx);
12616 self.change_selections(None, cx, |s| {
12617 let new_ranges = selections.into_iter().map(|range| {
12618 let start = OffsetUtf16(
12619 range
12620 .head()
12621 .0
12622 .saturating_add_signed(relative_utf16_range.start),
12623 );
12624 let end = OffsetUtf16(
12625 range
12626 .head()
12627 .0
12628 .saturating_add_signed(relative_utf16_range.end),
12629 );
12630 start..end
12631 });
12632 s.select_ranges(new_ranges);
12633 });
12634 }
12635
12636 self.handle_input(text, cx);
12637 }
12638
12639 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
12640 let Some(provider) = self.semantics_provider.as_ref() else {
12641 return false;
12642 };
12643
12644 let mut supports = false;
12645 self.buffer().read(cx).for_each_buffer(|buffer| {
12646 supports |= provider.supports_inlay_hints(buffer, cx);
12647 });
12648 supports
12649 }
12650
12651 pub fn focus(&self, cx: &mut WindowContext) {
12652 cx.focus(&self.focus_handle)
12653 }
12654
12655 pub fn is_focused(&self, cx: &WindowContext) -> bool {
12656 self.focus_handle.is_focused(cx)
12657 }
12658
12659 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
12660 cx.emit(EditorEvent::Focused);
12661
12662 if let Some(descendant) = self
12663 .last_focused_descendant
12664 .take()
12665 .and_then(|descendant| descendant.upgrade())
12666 {
12667 cx.focus(&descendant);
12668 } else {
12669 if let Some(blame) = self.blame.as_ref() {
12670 blame.update(cx, GitBlame::focus)
12671 }
12672
12673 self.blink_manager.update(cx, BlinkManager::enable);
12674 self.show_cursor_names(cx);
12675 self.buffer.update(cx, |buffer, cx| {
12676 buffer.finalize_last_transaction(cx);
12677 if self.leader_peer_id.is_none() {
12678 buffer.set_active_selections(
12679 &self.selections.disjoint_anchors(),
12680 self.selections.line_mode,
12681 self.cursor_shape,
12682 cx,
12683 );
12684 }
12685 });
12686 }
12687 }
12688
12689 fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
12690 cx.emit(EditorEvent::FocusedIn)
12691 }
12692
12693 fn handle_focus_out(&mut self, event: FocusOutEvent, _cx: &mut ViewContext<Self>) {
12694 if event.blurred != self.focus_handle {
12695 self.last_focused_descendant = Some(event.blurred);
12696 }
12697 }
12698
12699 pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
12700 self.blink_manager.update(cx, BlinkManager::disable);
12701 self.buffer
12702 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
12703
12704 if let Some(blame) = self.blame.as_ref() {
12705 blame.update(cx, GitBlame::blur)
12706 }
12707 if !self.hover_state.focused(cx) {
12708 hide_hover(self, cx);
12709 }
12710
12711 self.hide_context_menu(cx);
12712 cx.emit(EditorEvent::Blurred);
12713 cx.notify();
12714 }
12715
12716 pub fn register_action<A: Action>(
12717 &mut self,
12718 listener: impl Fn(&A, &mut WindowContext) + 'static,
12719 ) -> Subscription {
12720 let id = self.next_editor_action_id.post_inc();
12721 let listener = Arc::new(listener);
12722 self.editor_actions.borrow_mut().insert(
12723 id,
12724 Box::new(move |cx| {
12725 let cx = cx.window_context();
12726 let listener = listener.clone();
12727 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
12728 let action = action.downcast_ref().unwrap();
12729 if phase == DispatchPhase::Bubble {
12730 listener(action, cx)
12731 }
12732 })
12733 }),
12734 );
12735
12736 let editor_actions = self.editor_actions.clone();
12737 Subscription::new(move || {
12738 editor_actions.borrow_mut().remove(&id);
12739 })
12740 }
12741
12742 pub fn file_header_size(&self) -> u32 {
12743 FILE_HEADER_HEIGHT
12744 }
12745
12746 pub fn revert(
12747 &mut self,
12748 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
12749 cx: &mut ViewContext<Self>,
12750 ) {
12751 self.buffer().update(cx, |multi_buffer, cx| {
12752 for (buffer_id, changes) in revert_changes {
12753 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
12754 buffer.update(cx, |buffer, cx| {
12755 buffer.edit(
12756 changes.into_iter().map(|(range, text)| {
12757 (range, text.to_string().map(Arc::<str>::from))
12758 }),
12759 None,
12760 cx,
12761 );
12762 });
12763 }
12764 }
12765 });
12766 self.change_selections(None, cx, |selections| selections.refresh());
12767 }
12768
12769 pub fn to_pixel_point(
12770 &mut self,
12771 source: multi_buffer::Anchor,
12772 editor_snapshot: &EditorSnapshot,
12773 cx: &mut ViewContext<Self>,
12774 ) -> Option<gpui::Point<Pixels>> {
12775 let source_point = source.to_display_point(editor_snapshot);
12776 self.display_to_pixel_point(source_point, editor_snapshot, cx)
12777 }
12778
12779 pub fn display_to_pixel_point(
12780 &self,
12781 source: DisplayPoint,
12782 editor_snapshot: &EditorSnapshot,
12783 cx: &WindowContext,
12784 ) -> Option<gpui::Point<Pixels>> {
12785 let line_height = self.style()?.text.line_height_in_pixels(cx.rem_size());
12786 let text_layout_details = self.text_layout_details(cx);
12787 let scroll_top = text_layout_details
12788 .scroll_anchor
12789 .scroll_position(editor_snapshot)
12790 .y;
12791
12792 if source.row().as_f32() < scroll_top.floor() {
12793 return None;
12794 }
12795 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
12796 let source_y = line_height * (source.row().as_f32() - scroll_top);
12797 Some(gpui::Point::new(source_x, source_y))
12798 }
12799
12800 pub fn has_active_completions_menu(&self) -> bool {
12801 self.context_menu.borrow().as_ref().map_or(false, |menu| {
12802 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
12803 })
12804 }
12805
12806 pub fn register_addon<T: Addon>(&mut self, instance: T) {
12807 self.addons
12808 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
12809 }
12810
12811 pub fn unregister_addon<T: Addon>(&mut self) {
12812 self.addons.remove(&std::any::TypeId::of::<T>());
12813 }
12814
12815 pub fn addon<T: Addon>(&self) -> Option<&T> {
12816 let type_id = std::any::TypeId::of::<T>();
12817 self.addons
12818 .get(&type_id)
12819 .and_then(|item| item.to_any().downcast_ref::<T>())
12820 }
12821
12822 fn character_size(&self, cx: &mut ViewContext<Self>) -> gpui::Point<Pixels> {
12823 let text_layout_details = self.text_layout_details(cx);
12824 let style = &text_layout_details.editor_style;
12825 let font_id = cx.text_system().resolve_font(&style.text.font());
12826 let font_size = style.text.font_size.to_pixels(cx.rem_size());
12827 let line_height = style.text.line_height_in_pixels(cx.rem_size());
12828
12829 let em_width = cx
12830 .text_system()
12831 .typographic_bounds(font_id, font_size, 'm')
12832 .unwrap()
12833 .size
12834 .width;
12835
12836 gpui::Point::new(em_width, line_height)
12837 }
12838}
12839
12840fn get_unstaged_changes_for_buffers(
12841 project: &Model<Project>,
12842 buffers: impl IntoIterator<Item = Model<Buffer>>,
12843 cx: &mut ViewContext<Editor>,
12844) {
12845 let mut tasks = Vec::new();
12846 project.update(cx, |project, cx| {
12847 for buffer in buffers {
12848 tasks.push(project.open_unstaged_changes(buffer.clone(), cx))
12849 }
12850 });
12851 cx.spawn(|this, mut cx| async move {
12852 let change_sets = futures::future::join_all(tasks).await;
12853 this.update(&mut cx, |this, cx| {
12854 for change_set in change_sets {
12855 if let Some(change_set) = change_set.log_err() {
12856 this.diff_map.add_change_set(change_set, cx);
12857 }
12858 }
12859 })
12860 .ok();
12861 })
12862 .detach();
12863}
12864
12865fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
12866 let tab_size = tab_size.get() as usize;
12867 let mut width = offset;
12868
12869 for ch in text.chars() {
12870 width += if ch == '\t' {
12871 tab_size - (width % tab_size)
12872 } else {
12873 1
12874 };
12875 }
12876
12877 width - offset
12878}
12879
12880#[cfg(test)]
12881mod tests {
12882 use super::*;
12883
12884 #[test]
12885 fn test_string_size_with_expanded_tabs() {
12886 let nz = |val| NonZeroU32::new(val).unwrap();
12887 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
12888 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
12889 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
12890 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
12891 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
12892 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
12893 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
12894 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
12895 }
12896}
12897
12898/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
12899struct WordBreakingTokenizer<'a> {
12900 input: &'a str,
12901}
12902
12903impl<'a> WordBreakingTokenizer<'a> {
12904 fn new(input: &'a str) -> Self {
12905 Self { input }
12906 }
12907}
12908
12909fn is_char_ideographic(ch: char) -> bool {
12910 use unicode_script::Script::*;
12911 use unicode_script::UnicodeScript;
12912 matches!(ch.script(), Han | Tangut | Yi)
12913}
12914
12915fn is_grapheme_ideographic(text: &str) -> bool {
12916 text.chars().any(is_char_ideographic)
12917}
12918
12919fn is_grapheme_whitespace(text: &str) -> bool {
12920 text.chars().any(|x| x.is_whitespace())
12921}
12922
12923fn should_stay_with_preceding_ideograph(text: &str) -> bool {
12924 text.chars().next().map_or(false, |ch| {
12925 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
12926 })
12927}
12928
12929#[derive(PartialEq, Eq, Debug, Clone, Copy)]
12930struct WordBreakToken<'a> {
12931 token: &'a str,
12932 grapheme_len: usize,
12933 is_whitespace: bool,
12934}
12935
12936impl<'a> Iterator for WordBreakingTokenizer<'a> {
12937 /// Yields a span, the count of graphemes in the token, and whether it was
12938 /// whitespace. Note that it also breaks at word boundaries.
12939 type Item = WordBreakToken<'a>;
12940
12941 fn next(&mut self) -> Option<Self::Item> {
12942 use unicode_segmentation::UnicodeSegmentation;
12943 if self.input.is_empty() {
12944 return None;
12945 }
12946
12947 let mut iter = self.input.graphemes(true).peekable();
12948 let mut offset = 0;
12949 let mut graphemes = 0;
12950 if let Some(first_grapheme) = iter.next() {
12951 let is_whitespace = is_grapheme_whitespace(first_grapheme);
12952 offset += first_grapheme.len();
12953 graphemes += 1;
12954 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
12955 if let Some(grapheme) = iter.peek().copied() {
12956 if should_stay_with_preceding_ideograph(grapheme) {
12957 offset += grapheme.len();
12958 graphemes += 1;
12959 }
12960 }
12961 } else {
12962 let mut words = self.input[offset..].split_word_bound_indices().peekable();
12963 let mut next_word_bound = words.peek().copied();
12964 if next_word_bound.map_or(false, |(i, _)| i == 0) {
12965 next_word_bound = words.next();
12966 }
12967 while let Some(grapheme) = iter.peek().copied() {
12968 if next_word_bound.map_or(false, |(i, _)| i == offset) {
12969 break;
12970 };
12971 if is_grapheme_whitespace(grapheme) != is_whitespace {
12972 break;
12973 };
12974 offset += grapheme.len();
12975 graphemes += 1;
12976 iter.next();
12977 }
12978 }
12979 let token = &self.input[..offset];
12980 self.input = &self.input[offset..];
12981 if is_whitespace {
12982 Some(WordBreakToken {
12983 token: " ",
12984 grapheme_len: 1,
12985 is_whitespace: true,
12986 })
12987 } else {
12988 Some(WordBreakToken {
12989 token,
12990 grapheme_len: graphemes,
12991 is_whitespace: false,
12992 })
12993 }
12994 } else {
12995 None
12996 }
12997 }
12998}
12999
13000#[test]
13001fn test_word_breaking_tokenizer() {
13002 let tests: &[(&str, &[(&str, usize, bool)])] = &[
13003 ("", &[]),
13004 (" ", &[(" ", 1, true)]),
13005 ("Ʒ", &[("Ʒ", 1, false)]),
13006 ("Ǽ", &[("Ǽ", 1, false)]),
13007 ("⋑", &[("⋑", 1, false)]),
13008 ("⋑⋑", &[("⋑⋑", 2, false)]),
13009 (
13010 "原理,进而",
13011 &[
13012 ("原", 1, false),
13013 ("理,", 2, false),
13014 ("进", 1, false),
13015 ("而", 1, false),
13016 ],
13017 ),
13018 (
13019 "hello world",
13020 &[("hello", 5, false), (" ", 1, true), ("world", 5, false)],
13021 ),
13022 (
13023 "hello, world",
13024 &[("hello,", 6, false), (" ", 1, true), ("world", 5, false)],
13025 ),
13026 (
13027 " hello world",
13028 &[
13029 (" ", 1, true),
13030 ("hello", 5, false),
13031 (" ", 1, true),
13032 ("world", 5, false),
13033 ],
13034 ),
13035 (
13036 "这是什么 \n 钢笔",
13037 &[
13038 ("这", 1, false),
13039 ("是", 1, false),
13040 ("什", 1, false),
13041 ("么", 1, false),
13042 (" ", 1, true),
13043 ("钢", 1, false),
13044 ("笔", 1, false),
13045 ],
13046 ),
13047 (" mutton", &[(" ", 1, true), ("mutton", 6, false)]),
13048 ];
13049
13050 for (input, result) in tests {
13051 assert_eq!(
13052 WordBreakingTokenizer::new(input).collect::<Vec<_>>(),
13053 result
13054 .iter()
13055 .copied()
13056 .map(|(token, grapheme_len, is_whitespace)| WordBreakToken {
13057 token,
13058 grapheme_len,
13059 is_whitespace,
13060 })
13061 .collect::<Vec<_>>()
13062 );
13063 }
13064}
13065
13066fn wrap_with_prefix(
13067 line_prefix: String,
13068 unwrapped_text: String,
13069 wrap_column: usize,
13070 tab_size: NonZeroU32,
13071) -> String {
13072 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
13073 let mut wrapped_text = String::new();
13074 let mut current_line = line_prefix.clone();
13075
13076 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
13077 let mut current_line_len = line_prefix_len;
13078 for WordBreakToken {
13079 token,
13080 grapheme_len,
13081 is_whitespace,
13082 } in tokenizer
13083 {
13084 if current_line_len + grapheme_len > wrap_column && current_line_len != line_prefix_len {
13085 wrapped_text.push_str(current_line.trim_end());
13086 wrapped_text.push('\n');
13087 current_line.truncate(line_prefix.len());
13088 current_line_len = line_prefix_len;
13089 if !is_whitespace {
13090 current_line.push_str(token);
13091 current_line_len += grapheme_len;
13092 }
13093 } else if !is_whitespace {
13094 current_line.push_str(token);
13095 current_line_len += grapheme_len;
13096 } else if current_line_len != line_prefix_len {
13097 current_line.push(' ');
13098 current_line_len += 1;
13099 }
13100 }
13101
13102 if !current_line.is_empty() {
13103 wrapped_text.push_str(¤t_line);
13104 }
13105 wrapped_text
13106}
13107
13108#[test]
13109fn test_wrap_with_prefix() {
13110 assert_eq!(
13111 wrap_with_prefix(
13112 "# ".to_string(),
13113 "abcdefg".to_string(),
13114 4,
13115 NonZeroU32::new(4).unwrap()
13116 ),
13117 "# abcdefg"
13118 );
13119 assert_eq!(
13120 wrap_with_prefix(
13121 "".to_string(),
13122 "\thello world".to_string(),
13123 8,
13124 NonZeroU32::new(4).unwrap()
13125 ),
13126 "hello\nworld"
13127 );
13128 assert_eq!(
13129 wrap_with_prefix(
13130 "// ".to_string(),
13131 "xx \nyy zz aa bb cc".to_string(),
13132 12,
13133 NonZeroU32::new(4).unwrap()
13134 ),
13135 "// xx yy zz\n// aa bb cc"
13136 );
13137 assert_eq!(
13138 wrap_with_prefix(
13139 String::new(),
13140 "这是什么 \n 钢笔".to_string(),
13141 3,
13142 NonZeroU32::new(4).unwrap()
13143 ),
13144 "这是什\n么 钢\n笔"
13145 );
13146}
13147
13148fn hunks_for_selections(
13149 snapshot: &EditorSnapshot,
13150 selections: &[Selection<Point>],
13151) -> Vec<MultiBufferDiffHunk> {
13152 hunks_for_ranges(
13153 selections.iter().map(|selection| selection.range()),
13154 snapshot,
13155 )
13156}
13157
13158pub fn hunks_for_ranges(
13159 ranges: impl Iterator<Item = Range<Point>>,
13160 snapshot: &EditorSnapshot,
13161) -> Vec<MultiBufferDiffHunk> {
13162 let mut hunks = Vec::new();
13163 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
13164 HashMap::default();
13165 for query_range in ranges {
13166 let query_rows =
13167 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
13168 for hunk in snapshot.diff_map.diff_hunks_in_range(
13169 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
13170 &snapshot.buffer_snapshot,
13171 ) {
13172 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
13173 // when the caret is just above or just below the deleted hunk.
13174 let allow_adjacent = hunk_status(&hunk) == DiffHunkStatus::Removed;
13175 let related_to_selection = if allow_adjacent {
13176 hunk.row_range.overlaps(&query_rows)
13177 || hunk.row_range.start == query_rows.end
13178 || hunk.row_range.end == query_rows.start
13179 } else {
13180 hunk.row_range.overlaps(&query_rows)
13181 };
13182 if related_to_selection {
13183 if !processed_buffer_rows
13184 .entry(hunk.buffer_id)
13185 .or_default()
13186 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
13187 {
13188 continue;
13189 }
13190 hunks.push(hunk);
13191 }
13192 }
13193 }
13194
13195 hunks
13196}
13197
13198pub trait CollaborationHub {
13199 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
13200 fn user_participant_indices<'a>(
13201 &self,
13202 cx: &'a AppContext,
13203 ) -> &'a HashMap<u64, ParticipantIndex>;
13204 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
13205}
13206
13207impl CollaborationHub for Model<Project> {
13208 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
13209 self.read(cx).collaborators()
13210 }
13211
13212 fn user_participant_indices<'a>(
13213 &self,
13214 cx: &'a AppContext,
13215 ) -> &'a HashMap<u64, ParticipantIndex> {
13216 self.read(cx).user_store().read(cx).participant_indices()
13217 }
13218
13219 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
13220 let this = self.read(cx);
13221 let user_ids = this.collaborators().values().map(|c| c.user_id);
13222 this.user_store().read_with(cx, |user_store, cx| {
13223 user_store.participant_names(user_ids, cx)
13224 })
13225 }
13226}
13227
13228pub trait SemanticsProvider {
13229 fn hover(
13230 &self,
13231 buffer: &Model<Buffer>,
13232 position: text::Anchor,
13233 cx: &mut AppContext,
13234 ) -> Option<Task<Vec<project::Hover>>>;
13235
13236 fn inlay_hints(
13237 &self,
13238 buffer_handle: Model<Buffer>,
13239 range: Range<text::Anchor>,
13240 cx: &mut AppContext,
13241 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
13242
13243 fn resolve_inlay_hint(
13244 &self,
13245 hint: InlayHint,
13246 buffer_handle: Model<Buffer>,
13247 server_id: LanguageServerId,
13248 cx: &mut AppContext,
13249 ) -> Option<Task<anyhow::Result<InlayHint>>>;
13250
13251 fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool;
13252
13253 fn document_highlights(
13254 &self,
13255 buffer: &Model<Buffer>,
13256 position: text::Anchor,
13257 cx: &mut AppContext,
13258 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
13259
13260 fn definitions(
13261 &self,
13262 buffer: &Model<Buffer>,
13263 position: text::Anchor,
13264 kind: GotoDefinitionKind,
13265 cx: &mut AppContext,
13266 ) -> Option<Task<Result<Vec<LocationLink>>>>;
13267
13268 fn range_for_rename(
13269 &self,
13270 buffer: &Model<Buffer>,
13271 position: text::Anchor,
13272 cx: &mut AppContext,
13273 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
13274
13275 fn perform_rename(
13276 &self,
13277 buffer: &Model<Buffer>,
13278 position: text::Anchor,
13279 new_name: String,
13280 cx: &mut AppContext,
13281 ) -> Option<Task<Result<ProjectTransaction>>>;
13282}
13283
13284pub trait CompletionProvider {
13285 fn completions(
13286 &self,
13287 buffer: &Model<Buffer>,
13288 buffer_position: text::Anchor,
13289 trigger: CompletionContext,
13290 cx: &mut ViewContext<Editor>,
13291 ) -> Task<Result<Vec<Completion>>>;
13292
13293 fn resolve_completions(
13294 &self,
13295 buffer: Model<Buffer>,
13296 completion_indices: Vec<usize>,
13297 completions: Rc<RefCell<Box<[Completion]>>>,
13298 cx: &mut ViewContext<Editor>,
13299 ) -> Task<Result<bool>>;
13300
13301 fn apply_additional_edits_for_completion(
13302 &self,
13303 buffer: Model<Buffer>,
13304 completion: Completion,
13305 push_to_history: bool,
13306 cx: &mut ViewContext<Editor>,
13307 ) -> Task<Result<Option<language::Transaction>>>;
13308
13309 fn is_completion_trigger(
13310 &self,
13311 buffer: &Model<Buffer>,
13312 position: language::Anchor,
13313 text: &str,
13314 trigger_in_words: bool,
13315 cx: &mut ViewContext<Editor>,
13316 ) -> bool;
13317
13318 fn sort_completions(&self) -> bool {
13319 true
13320 }
13321}
13322
13323pub trait CodeActionProvider {
13324 fn code_actions(
13325 &self,
13326 buffer: &Model<Buffer>,
13327 range: Range<text::Anchor>,
13328 cx: &mut WindowContext,
13329 ) -> Task<Result<Vec<CodeAction>>>;
13330
13331 fn apply_code_action(
13332 &self,
13333 buffer_handle: Model<Buffer>,
13334 action: CodeAction,
13335 excerpt_id: ExcerptId,
13336 push_to_history: bool,
13337 cx: &mut WindowContext,
13338 ) -> Task<Result<ProjectTransaction>>;
13339}
13340
13341impl CodeActionProvider for Model<Project> {
13342 fn code_actions(
13343 &self,
13344 buffer: &Model<Buffer>,
13345 range: Range<text::Anchor>,
13346 cx: &mut WindowContext,
13347 ) -> Task<Result<Vec<CodeAction>>> {
13348 self.update(cx, |project, cx| {
13349 project.code_actions(buffer, range, None, cx)
13350 })
13351 }
13352
13353 fn apply_code_action(
13354 &self,
13355 buffer_handle: Model<Buffer>,
13356 action: CodeAction,
13357 _excerpt_id: ExcerptId,
13358 push_to_history: bool,
13359 cx: &mut WindowContext,
13360 ) -> Task<Result<ProjectTransaction>> {
13361 self.update(cx, |project, cx| {
13362 project.apply_code_action(buffer_handle, action, push_to_history, cx)
13363 })
13364 }
13365}
13366
13367fn snippet_completions(
13368 project: &Project,
13369 buffer: &Model<Buffer>,
13370 buffer_position: text::Anchor,
13371 cx: &mut AppContext,
13372) -> Task<Result<Vec<Completion>>> {
13373 let language = buffer.read(cx).language_at(buffer_position);
13374 let language_name = language.as_ref().map(|language| language.lsp_id());
13375 let snippet_store = project.snippets().read(cx);
13376 let snippets = snippet_store.snippets_for(language_name, cx);
13377
13378 if snippets.is_empty() {
13379 return Task::ready(Ok(vec![]));
13380 }
13381 let snapshot = buffer.read(cx).text_snapshot();
13382 let chars: String = snapshot
13383 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
13384 .collect();
13385
13386 let scope = language.map(|language| language.default_scope());
13387 let executor = cx.background_executor().clone();
13388
13389 cx.background_executor().spawn(async move {
13390 let classifier = CharClassifier::new(scope).for_completion(true);
13391 let mut last_word = chars
13392 .chars()
13393 .take_while(|c| classifier.is_word(*c))
13394 .collect::<String>();
13395 last_word = last_word.chars().rev().collect();
13396
13397 if last_word.is_empty() {
13398 return Ok(vec![]);
13399 }
13400
13401 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
13402 let to_lsp = |point: &text::Anchor| {
13403 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
13404 point_to_lsp(end)
13405 };
13406 let lsp_end = to_lsp(&buffer_position);
13407
13408 let candidates = snippets
13409 .iter()
13410 .enumerate()
13411 .flat_map(|(ix, snippet)| {
13412 snippet
13413 .prefix
13414 .iter()
13415 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
13416 })
13417 .collect::<Vec<StringMatchCandidate>>();
13418
13419 let mut matches = fuzzy::match_strings(
13420 &candidates,
13421 &last_word,
13422 last_word.chars().any(|c| c.is_uppercase()),
13423 100,
13424 &Default::default(),
13425 executor,
13426 )
13427 .await;
13428
13429 // Remove all candidates where the query's start does not match the start of any word in the candidate
13430 if let Some(query_start) = last_word.chars().next() {
13431 matches.retain(|string_match| {
13432 split_words(&string_match.string).any(|word| {
13433 // Check that the first codepoint of the word as lowercase matches the first
13434 // codepoint of the query as lowercase
13435 word.chars()
13436 .flat_map(|codepoint| codepoint.to_lowercase())
13437 .zip(query_start.to_lowercase())
13438 .all(|(word_cp, query_cp)| word_cp == query_cp)
13439 })
13440 });
13441 }
13442
13443 let matched_strings = matches
13444 .into_iter()
13445 .map(|m| m.string)
13446 .collect::<HashSet<_>>();
13447
13448 let result: Vec<Completion> = snippets
13449 .into_iter()
13450 .filter_map(|snippet| {
13451 let matching_prefix = snippet
13452 .prefix
13453 .iter()
13454 .find(|prefix| matched_strings.contains(*prefix))?;
13455 let start = as_offset - last_word.len();
13456 let start = snapshot.anchor_before(start);
13457 let range = start..buffer_position;
13458 let lsp_start = to_lsp(&start);
13459 let lsp_range = lsp::Range {
13460 start: lsp_start,
13461 end: lsp_end,
13462 };
13463 Some(Completion {
13464 old_range: range,
13465 new_text: snippet.body.clone(),
13466 label: CodeLabel {
13467 text: matching_prefix.clone(),
13468 runs: vec![],
13469 filter_range: 0..matching_prefix.len(),
13470 },
13471 server_id: LanguageServerId(usize::MAX),
13472 documentation: snippet.description.clone().map(Documentation::SingleLine),
13473 lsp_completion: lsp::CompletionItem {
13474 label: snippet.prefix.first().unwrap().clone(),
13475 kind: Some(CompletionItemKind::SNIPPET),
13476 label_details: snippet.description.as_ref().map(|description| {
13477 lsp::CompletionItemLabelDetails {
13478 detail: Some(description.clone()),
13479 description: None,
13480 }
13481 }),
13482 insert_text_format: Some(InsertTextFormat::SNIPPET),
13483 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13484 lsp::InsertReplaceEdit {
13485 new_text: snippet.body.clone(),
13486 insert: lsp_range,
13487 replace: lsp_range,
13488 },
13489 )),
13490 filter_text: Some(snippet.body.clone()),
13491 sort_text: Some(char::MAX.to_string()),
13492 ..Default::default()
13493 },
13494 confirm: None,
13495 })
13496 })
13497 .collect();
13498
13499 Ok(result)
13500 })
13501}
13502
13503impl CompletionProvider for Model<Project> {
13504 fn completions(
13505 &self,
13506 buffer: &Model<Buffer>,
13507 buffer_position: text::Anchor,
13508 options: CompletionContext,
13509 cx: &mut ViewContext<Editor>,
13510 ) -> Task<Result<Vec<Completion>>> {
13511 self.update(cx, |project, cx| {
13512 let snippets = snippet_completions(project, buffer, buffer_position, cx);
13513 let project_completions = project.completions(buffer, buffer_position, options, cx);
13514 cx.background_executor().spawn(async move {
13515 let mut completions = project_completions.await?;
13516 let snippets_completions = snippets.await?;
13517 completions.extend(snippets_completions);
13518 Ok(completions)
13519 })
13520 })
13521 }
13522
13523 fn resolve_completions(
13524 &self,
13525 buffer: Model<Buffer>,
13526 completion_indices: Vec<usize>,
13527 completions: Rc<RefCell<Box<[Completion]>>>,
13528 cx: &mut ViewContext<Editor>,
13529 ) -> Task<Result<bool>> {
13530 self.update(cx, |project, cx| {
13531 project.resolve_completions(buffer, completion_indices, completions, cx)
13532 })
13533 }
13534
13535 fn apply_additional_edits_for_completion(
13536 &self,
13537 buffer: Model<Buffer>,
13538 completion: Completion,
13539 push_to_history: bool,
13540 cx: &mut ViewContext<Editor>,
13541 ) -> Task<Result<Option<language::Transaction>>> {
13542 self.update(cx, |project, cx| {
13543 project.apply_additional_edits_for_completion(buffer, completion, push_to_history, cx)
13544 })
13545 }
13546
13547 fn is_completion_trigger(
13548 &self,
13549 buffer: &Model<Buffer>,
13550 position: language::Anchor,
13551 text: &str,
13552 trigger_in_words: bool,
13553 cx: &mut ViewContext<Editor>,
13554 ) -> bool {
13555 let mut chars = text.chars();
13556 let char = if let Some(char) = chars.next() {
13557 char
13558 } else {
13559 return false;
13560 };
13561 if chars.next().is_some() {
13562 return false;
13563 }
13564
13565 let buffer = buffer.read(cx);
13566 let snapshot = buffer.snapshot();
13567 if !snapshot.settings_at(position, cx).show_completions_on_input {
13568 return false;
13569 }
13570 let classifier = snapshot.char_classifier_at(position).for_completion(true);
13571 if trigger_in_words && classifier.is_word(char) {
13572 return true;
13573 }
13574
13575 buffer.completion_triggers().contains(text)
13576 }
13577}
13578
13579impl SemanticsProvider for Model<Project> {
13580 fn hover(
13581 &self,
13582 buffer: &Model<Buffer>,
13583 position: text::Anchor,
13584 cx: &mut AppContext,
13585 ) -> Option<Task<Vec<project::Hover>>> {
13586 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
13587 }
13588
13589 fn document_highlights(
13590 &self,
13591 buffer: &Model<Buffer>,
13592 position: text::Anchor,
13593 cx: &mut AppContext,
13594 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
13595 Some(self.update(cx, |project, cx| {
13596 project.document_highlights(buffer, position, cx)
13597 }))
13598 }
13599
13600 fn definitions(
13601 &self,
13602 buffer: &Model<Buffer>,
13603 position: text::Anchor,
13604 kind: GotoDefinitionKind,
13605 cx: &mut AppContext,
13606 ) -> Option<Task<Result<Vec<LocationLink>>>> {
13607 Some(self.update(cx, |project, cx| match kind {
13608 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
13609 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
13610 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
13611 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
13612 }))
13613 }
13614
13615 fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool {
13616 // TODO: make this work for remote projects
13617 self.read(cx)
13618 .language_servers_for_local_buffer(buffer.read(cx), cx)
13619 .any(
13620 |(_, server)| match server.capabilities().inlay_hint_provider {
13621 Some(lsp::OneOf::Left(enabled)) => enabled,
13622 Some(lsp::OneOf::Right(_)) => true,
13623 None => false,
13624 },
13625 )
13626 }
13627
13628 fn inlay_hints(
13629 &self,
13630 buffer_handle: Model<Buffer>,
13631 range: Range<text::Anchor>,
13632 cx: &mut AppContext,
13633 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
13634 Some(self.update(cx, |project, cx| {
13635 project.inlay_hints(buffer_handle, range, cx)
13636 }))
13637 }
13638
13639 fn resolve_inlay_hint(
13640 &self,
13641 hint: InlayHint,
13642 buffer_handle: Model<Buffer>,
13643 server_id: LanguageServerId,
13644 cx: &mut AppContext,
13645 ) -> Option<Task<anyhow::Result<InlayHint>>> {
13646 Some(self.update(cx, |project, cx| {
13647 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
13648 }))
13649 }
13650
13651 fn range_for_rename(
13652 &self,
13653 buffer: &Model<Buffer>,
13654 position: text::Anchor,
13655 cx: &mut AppContext,
13656 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
13657 Some(self.update(cx, |project, cx| {
13658 project.prepare_rename(buffer.clone(), position, cx)
13659 }))
13660 }
13661
13662 fn perform_rename(
13663 &self,
13664 buffer: &Model<Buffer>,
13665 position: text::Anchor,
13666 new_name: String,
13667 cx: &mut AppContext,
13668 ) -> Option<Task<Result<ProjectTransaction>>> {
13669 Some(self.update(cx, |project, cx| {
13670 project.perform_rename(buffer.clone(), position, new_name, cx)
13671 }))
13672 }
13673}
13674
13675fn inlay_hint_settings(
13676 location: Anchor,
13677 snapshot: &MultiBufferSnapshot,
13678 cx: &mut ViewContext<'_, Editor>,
13679) -> InlayHintSettings {
13680 let file = snapshot.file_at(location);
13681 let language = snapshot.language_at(location).map(|l| l.name());
13682 language_settings(language, file, cx).inlay_hints
13683}
13684
13685fn consume_contiguous_rows(
13686 contiguous_row_selections: &mut Vec<Selection<Point>>,
13687 selection: &Selection<Point>,
13688 display_map: &DisplaySnapshot,
13689 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
13690) -> (MultiBufferRow, MultiBufferRow) {
13691 contiguous_row_selections.push(selection.clone());
13692 let start_row = MultiBufferRow(selection.start.row);
13693 let mut end_row = ending_row(selection, display_map);
13694
13695 while let Some(next_selection) = selections.peek() {
13696 if next_selection.start.row <= end_row.0 {
13697 end_row = ending_row(next_selection, display_map);
13698 contiguous_row_selections.push(selections.next().unwrap().clone());
13699 } else {
13700 break;
13701 }
13702 }
13703 (start_row, end_row)
13704}
13705
13706fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
13707 if next_selection.end.column > 0 || next_selection.is_empty() {
13708 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
13709 } else {
13710 MultiBufferRow(next_selection.end.row)
13711 }
13712}
13713
13714impl EditorSnapshot {
13715 pub fn remote_selections_in_range<'a>(
13716 &'a self,
13717 range: &'a Range<Anchor>,
13718 collaboration_hub: &dyn CollaborationHub,
13719 cx: &'a AppContext,
13720 ) -> impl 'a + Iterator<Item = RemoteSelection> {
13721 let participant_names = collaboration_hub.user_names(cx);
13722 let participant_indices = collaboration_hub.user_participant_indices(cx);
13723 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
13724 let collaborators_by_replica_id = collaborators_by_peer_id
13725 .iter()
13726 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
13727 .collect::<HashMap<_, _>>();
13728 self.buffer_snapshot
13729 .selections_in_range(range, false)
13730 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
13731 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
13732 let participant_index = participant_indices.get(&collaborator.user_id).copied();
13733 let user_name = participant_names.get(&collaborator.user_id).cloned();
13734 Some(RemoteSelection {
13735 replica_id,
13736 selection,
13737 cursor_shape,
13738 line_mode,
13739 participant_index,
13740 peer_id: collaborator.peer_id,
13741 user_name,
13742 })
13743 })
13744 }
13745
13746 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
13747 self.display_snapshot.buffer_snapshot.language_at(position)
13748 }
13749
13750 pub fn is_focused(&self) -> bool {
13751 self.is_focused
13752 }
13753
13754 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
13755 self.placeholder_text.as_ref()
13756 }
13757
13758 pub fn scroll_position(&self) -> gpui::Point<f32> {
13759 self.scroll_anchor.scroll_position(&self.display_snapshot)
13760 }
13761
13762 fn gutter_dimensions(
13763 &self,
13764 font_id: FontId,
13765 font_size: Pixels,
13766 em_width: Pixels,
13767 em_advance: Pixels,
13768 max_line_number_width: Pixels,
13769 cx: &AppContext,
13770 ) -> GutterDimensions {
13771 if !self.show_gutter {
13772 return GutterDimensions::default();
13773 }
13774 let descent = cx.text_system().descent(font_id, font_size);
13775
13776 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
13777 matches!(
13778 ProjectSettings::get_global(cx).git.git_gutter,
13779 Some(GitGutterSetting::TrackedFiles)
13780 )
13781 });
13782 let gutter_settings = EditorSettings::get_global(cx).gutter;
13783 let show_line_numbers = self
13784 .show_line_numbers
13785 .unwrap_or(gutter_settings.line_numbers);
13786 let line_gutter_width = if show_line_numbers {
13787 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
13788 let min_width_for_number_on_gutter = em_advance * 4.0;
13789 max_line_number_width.max(min_width_for_number_on_gutter)
13790 } else {
13791 0.0.into()
13792 };
13793
13794 let show_code_actions = self
13795 .show_code_actions
13796 .unwrap_or(gutter_settings.code_actions);
13797
13798 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
13799
13800 let git_blame_entries_width =
13801 self.git_blame_gutter_max_author_length
13802 .map(|max_author_length| {
13803 // Length of the author name, but also space for the commit hash,
13804 // the spacing and the timestamp.
13805 let max_char_count = max_author_length
13806 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
13807 + 7 // length of commit sha
13808 + 14 // length of max relative timestamp ("60 minutes ago")
13809 + 4; // gaps and margins
13810
13811 em_advance * max_char_count
13812 });
13813
13814 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
13815 left_padding += if show_code_actions || show_runnables {
13816 em_width * 3.0
13817 } else if show_git_gutter && show_line_numbers {
13818 em_width * 2.0
13819 } else if show_git_gutter || show_line_numbers {
13820 em_width
13821 } else {
13822 px(0.)
13823 };
13824
13825 let right_padding = if gutter_settings.folds && show_line_numbers {
13826 em_width * 4.0
13827 } else if gutter_settings.folds {
13828 em_width * 3.0
13829 } else if show_line_numbers {
13830 em_width
13831 } else {
13832 px(0.)
13833 };
13834
13835 GutterDimensions {
13836 left_padding,
13837 right_padding,
13838 width: line_gutter_width + left_padding + right_padding,
13839 margin: -descent,
13840 git_blame_entries_width,
13841 }
13842 }
13843
13844 pub fn render_crease_toggle(
13845 &self,
13846 buffer_row: MultiBufferRow,
13847 row_contains_cursor: bool,
13848 editor: View<Editor>,
13849 cx: &mut WindowContext,
13850 ) -> Option<AnyElement> {
13851 let folded = self.is_line_folded(buffer_row);
13852 let mut is_foldable = false;
13853
13854 if let Some(crease) = self
13855 .crease_snapshot
13856 .query_row(buffer_row, &self.buffer_snapshot)
13857 {
13858 is_foldable = true;
13859 match crease {
13860 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
13861 if let Some(render_toggle) = render_toggle {
13862 let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
13863 if folded {
13864 editor.update(cx, |editor, cx| {
13865 editor.fold_at(&crate::FoldAt { buffer_row }, cx)
13866 });
13867 } else {
13868 editor.update(cx, |editor, cx| {
13869 editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
13870 });
13871 }
13872 });
13873 return Some((render_toggle)(buffer_row, folded, toggle_callback, cx));
13874 }
13875 }
13876 }
13877 }
13878
13879 is_foldable |= self.starts_indent(buffer_row);
13880
13881 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
13882 Some(
13883 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
13884 .toggle_state(folded)
13885 .on_click(cx.listener_for(&editor, move |this, _e, cx| {
13886 if folded {
13887 this.unfold_at(&UnfoldAt { buffer_row }, cx);
13888 } else {
13889 this.fold_at(&FoldAt { buffer_row }, cx);
13890 }
13891 }))
13892 .into_any_element(),
13893 )
13894 } else {
13895 None
13896 }
13897 }
13898
13899 pub fn render_crease_trailer(
13900 &self,
13901 buffer_row: MultiBufferRow,
13902 cx: &mut WindowContext,
13903 ) -> Option<AnyElement> {
13904 let folded = self.is_line_folded(buffer_row);
13905 if let Crease::Inline { render_trailer, .. } = self
13906 .crease_snapshot
13907 .query_row(buffer_row, &self.buffer_snapshot)?
13908 {
13909 let render_trailer = render_trailer.as_ref()?;
13910 Some(render_trailer(buffer_row, folded, cx))
13911 } else {
13912 None
13913 }
13914 }
13915}
13916
13917impl Deref for EditorSnapshot {
13918 type Target = DisplaySnapshot;
13919
13920 fn deref(&self) -> &Self::Target {
13921 &self.display_snapshot
13922 }
13923}
13924
13925#[derive(Clone, Debug, PartialEq, Eq)]
13926pub enum EditorEvent {
13927 InputIgnored {
13928 text: Arc<str>,
13929 },
13930 InputHandled {
13931 utf16_range_to_replace: Option<Range<isize>>,
13932 text: Arc<str>,
13933 },
13934 ExcerptsAdded {
13935 buffer: Model<Buffer>,
13936 predecessor: ExcerptId,
13937 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
13938 },
13939 ExcerptsRemoved {
13940 ids: Vec<ExcerptId>,
13941 },
13942 BufferFoldToggled {
13943 ids: Vec<ExcerptId>,
13944 folded: bool,
13945 },
13946 ExcerptsEdited {
13947 ids: Vec<ExcerptId>,
13948 },
13949 ExcerptsExpanded {
13950 ids: Vec<ExcerptId>,
13951 },
13952 BufferEdited,
13953 Edited {
13954 transaction_id: clock::Lamport,
13955 },
13956 Reparsed(BufferId),
13957 Focused,
13958 FocusedIn,
13959 Blurred,
13960 DirtyChanged,
13961 Saved,
13962 TitleChanged,
13963 DiffBaseChanged,
13964 SelectionsChanged {
13965 local: bool,
13966 },
13967 ScrollPositionChanged {
13968 local: bool,
13969 autoscroll: bool,
13970 },
13971 Closed,
13972 TransactionUndone {
13973 transaction_id: clock::Lamport,
13974 },
13975 TransactionBegun {
13976 transaction_id: clock::Lamport,
13977 },
13978 Reloaded,
13979 CursorShapeChanged,
13980}
13981
13982impl EventEmitter<EditorEvent> for Editor {}
13983
13984impl FocusableView for Editor {
13985 fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
13986 self.focus_handle.clone()
13987 }
13988}
13989
13990impl Render for Editor {
13991 fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
13992 let settings = ThemeSettings::get_global(cx);
13993
13994 let mut text_style = match self.mode {
13995 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
13996 color: cx.theme().colors().editor_foreground,
13997 font_family: settings.ui_font.family.clone(),
13998 font_features: settings.ui_font.features.clone(),
13999 font_fallbacks: settings.ui_font.fallbacks.clone(),
14000 font_size: rems(0.875).into(),
14001 font_weight: settings.ui_font.weight,
14002 line_height: relative(settings.buffer_line_height.value()),
14003 ..Default::default()
14004 },
14005 EditorMode::Full => TextStyle {
14006 color: cx.theme().colors().editor_foreground,
14007 font_family: settings.buffer_font.family.clone(),
14008 font_features: settings.buffer_font.features.clone(),
14009 font_fallbacks: settings.buffer_font.fallbacks.clone(),
14010 font_size: settings.buffer_font_size(cx).into(),
14011 font_weight: settings.buffer_font.weight,
14012 line_height: relative(settings.buffer_line_height.value()),
14013 ..Default::default()
14014 },
14015 };
14016 if let Some(text_style_refinement) = &self.text_style_refinement {
14017 text_style.refine(text_style_refinement)
14018 }
14019
14020 let background = match self.mode {
14021 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
14022 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
14023 EditorMode::Full => cx.theme().colors().editor_background,
14024 };
14025
14026 EditorElement::new(
14027 cx.view(),
14028 EditorStyle {
14029 background,
14030 local_player: cx.theme().players().local(),
14031 text: text_style,
14032 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
14033 syntax: cx.theme().syntax().clone(),
14034 status: cx.theme().status().clone(),
14035 inlay_hints_style: make_inlay_hints_style(cx),
14036 inline_completion_styles: make_suggestion_styles(cx),
14037 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
14038 },
14039 )
14040 }
14041}
14042
14043impl ViewInputHandler for Editor {
14044 fn text_for_range(
14045 &mut self,
14046 range_utf16: Range<usize>,
14047 adjusted_range: &mut Option<Range<usize>>,
14048 cx: &mut ViewContext<Self>,
14049 ) -> Option<String> {
14050 let snapshot = self.buffer.read(cx).read(cx);
14051 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
14052 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
14053 if (start.0..end.0) != range_utf16 {
14054 adjusted_range.replace(start.0..end.0);
14055 }
14056 Some(snapshot.text_for_range(start..end).collect())
14057 }
14058
14059 fn selected_text_range(
14060 &mut self,
14061 ignore_disabled_input: bool,
14062 cx: &mut ViewContext<Self>,
14063 ) -> Option<UTF16Selection> {
14064 // Prevent the IME menu from appearing when holding down an alphabetic key
14065 // while input is disabled.
14066 if !ignore_disabled_input && !self.input_enabled {
14067 return None;
14068 }
14069
14070 let selection = self.selections.newest::<OffsetUtf16>(cx);
14071 let range = selection.range();
14072
14073 Some(UTF16Selection {
14074 range: range.start.0..range.end.0,
14075 reversed: selection.reversed,
14076 })
14077 }
14078
14079 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
14080 let snapshot = self.buffer.read(cx).read(cx);
14081 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
14082 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
14083 }
14084
14085 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
14086 self.clear_highlights::<InputComposition>(cx);
14087 self.ime_transaction.take();
14088 }
14089
14090 fn replace_text_in_range(
14091 &mut self,
14092 range_utf16: Option<Range<usize>>,
14093 text: &str,
14094 cx: &mut ViewContext<Self>,
14095 ) {
14096 if !self.input_enabled {
14097 cx.emit(EditorEvent::InputIgnored { text: text.into() });
14098 return;
14099 }
14100
14101 self.transact(cx, |this, cx| {
14102 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
14103 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
14104 Some(this.selection_replacement_ranges(range_utf16, cx))
14105 } else {
14106 this.marked_text_ranges(cx)
14107 };
14108
14109 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
14110 let newest_selection_id = this.selections.newest_anchor().id;
14111 this.selections
14112 .all::<OffsetUtf16>(cx)
14113 .iter()
14114 .zip(ranges_to_replace.iter())
14115 .find_map(|(selection, range)| {
14116 if selection.id == newest_selection_id {
14117 Some(
14118 (range.start.0 as isize - selection.head().0 as isize)
14119 ..(range.end.0 as isize - selection.head().0 as isize),
14120 )
14121 } else {
14122 None
14123 }
14124 })
14125 });
14126
14127 cx.emit(EditorEvent::InputHandled {
14128 utf16_range_to_replace: range_to_replace,
14129 text: text.into(),
14130 });
14131
14132 if let Some(new_selected_ranges) = new_selected_ranges {
14133 this.change_selections(None, cx, |selections| {
14134 selections.select_ranges(new_selected_ranges)
14135 });
14136 this.backspace(&Default::default(), cx);
14137 }
14138
14139 this.handle_input(text, cx);
14140 });
14141
14142 if let Some(transaction) = self.ime_transaction {
14143 self.buffer.update(cx, |buffer, cx| {
14144 buffer.group_until_transaction(transaction, cx);
14145 });
14146 }
14147
14148 self.unmark_text(cx);
14149 }
14150
14151 fn replace_and_mark_text_in_range(
14152 &mut self,
14153 range_utf16: Option<Range<usize>>,
14154 text: &str,
14155 new_selected_range_utf16: Option<Range<usize>>,
14156 cx: &mut ViewContext<Self>,
14157 ) {
14158 if !self.input_enabled {
14159 return;
14160 }
14161
14162 let transaction = self.transact(cx, |this, cx| {
14163 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
14164 let snapshot = this.buffer.read(cx).read(cx);
14165 if let Some(relative_range_utf16) = range_utf16.as_ref() {
14166 for marked_range in &mut marked_ranges {
14167 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
14168 marked_range.start.0 += relative_range_utf16.start;
14169 marked_range.start =
14170 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
14171 marked_range.end =
14172 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
14173 }
14174 }
14175 Some(marked_ranges)
14176 } else if let Some(range_utf16) = range_utf16 {
14177 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
14178 Some(this.selection_replacement_ranges(range_utf16, cx))
14179 } else {
14180 None
14181 };
14182
14183 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
14184 let newest_selection_id = this.selections.newest_anchor().id;
14185 this.selections
14186 .all::<OffsetUtf16>(cx)
14187 .iter()
14188 .zip(ranges_to_replace.iter())
14189 .find_map(|(selection, range)| {
14190 if selection.id == newest_selection_id {
14191 Some(
14192 (range.start.0 as isize - selection.head().0 as isize)
14193 ..(range.end.0 as isize - selection.head().0 as isize),
14194 )
14195 } else {
14196 None
14197 }
14198 })
14199 });
14200
14201 cx.emit(EditorEvent::InputHandled {
14202 utf16_range_to_replace: range_to_replace,
14203 text: text.into(),
14204 });
14205
14206 if let Some(ranges) = ranges_to_replace {
14207 this.change_selections(None, cx, |s| s.select_ranges(ranges));
14208 }
14209
14210 let marked_ranges = {
14211 let snapshot = this.buffer.read(cx).read(cx);
14212 this.selections
14213 .disjoint_anchors()
14214 .iter()
14215 .map(|selection| {
14216 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
14217 })
14218 .collect::<Vec<_>>()
14219 };
14220
14221 if text.is_empty() {
14222 this.unmark_text(cx);
14223 } else {
14224 this.highlight_text::<InputComposition>(
14225 marked_ranges.clone(),
14226 HighlightStyle {
14227 underline: Some(UnderlineStyle {
14228 thickness: px(1.),
14229 color: None,
14230 wavy: false,
14231 }),
14232 ..Default::default()
14233 },
14234 cx,
14235 );
14236 }
14237
14238 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
14239 let use_autoclose = this.use_autoclose;
14240 let use_auto_surround = this.use_auto_surround;
14241 this.set_use_autoclose(false);
14242 this.set_use_auto_surround(false);
14243 this.handle_input(text, cx);
14244 this.set_use_autoclose(use_autoclose);
14245 this.set_use_auto_surround(use_auto_surround);
14246
14247 if let Some(new_selected_range) = new_selected_range_utf16 {
14248 let snapshot = this.buffer.read(cx).read(cx);
14249 let new_selected_ranges = marked_ranges
14250 .into_iter()
14251 .map(|marked_range| {
14252 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
14253 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
14254 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
14255 snapshot.clip_offset_utf16(new_start, Bias::Left)
14256 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
14257 })
14258 .collect::<Vec<_>>();
14259
14260 drop(snapshot);
14261 this.change_selections(None, cx, |selections| {
14262 selections.select_ranges(new_selected_ranges)
14263 });
14264 }
14265 });
14266
14267 self.ime_transaction = self.ime_transaction.or(transaction);
14268 if let Some(transaction) = self.ime_transaction {
14269 self.buffer.update(cx, |buffer, cx| {
14270 buffer.group_until_transaction(transaction, cx);
14271 });
14272 }
14273
14274 if self.text_highlights::<InputComposition>(cx).is_none() {
14275 self.ime_transaction.take();
14276 }
14277 }
14278
14279 fn bounds_for_range(
14280 &mut self,
14281 range_utf16: Range<usize>,
14282 element_bounds: gpui::Bounds<Pixels>,
14283 cx: &mut ViewContext<Self>,
14284 ) -> Option<gpui::Bounds<Pixels>> {
14285 let text_layout_details = self.text_layout_details(cx);
14286 let gpui::Point {
14287 x: em_width,
14288 y: line_height,
14289 } = self.character_size(cx);
14290
14291 let snapshot = self.snapshot(cx);
14292 let scroll_position = snapshot.scroll_position();
14293 let scroll_left = scroll_position.x * em_width;
14294
14295 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
14296 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
14297 + self.gutter_dimensions.width
14298 + self.gutter_dimensions.margin;
14299 let y = line_height * (start.row().as_f32() - scroll_position.y);
14300
14301 Some(Bounds {
14302 origin: element_bounds.origin + point(x, y),
14303 size: size(em_width, line_height),
14304 })
14305 }
14306}
14307
14308trait SelectionExt {
14309 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
14310 fn spanned_rows(
14311 &self,
14312 include_end_if_at_line_start: bool,
14313 map: &DisplaySnapshot,
14314 ) -> Range<MultiBufferRow>;
14315}
14316
14317impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
14318 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
14319 let start = self
14320 .start
14321 .to_point(&map.buffer_snapshot)
14322 .to_display_point(map);
14323 let end = self
14324 .end
14325 .to_point(&map.buffer_snapshot)
14326 .to_display_point(map);
14327 if self.reversed {
14328 end..start
14329 } else {
14330 start..end
14331 }
14332 }
14333
14334 fn spanned_rows(
14335 &self,
14336 include_end_if_at_line_start: bool,
14337 map: &DisplaySnapshot,
14338 ) -> Range<MultiBufferRow> {
14339 let start = self.start.to_point(&map.buffer_snapshot);
14340 let mut end = self.end.to_point(&map.buffer_snapshot);
14341 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
14342 end.row -= 1;
14343 }
14344
14345 let buffer_start = map.prev_line_boundary(start).0;
14346 let buffer_end = map.next_line_boundary(end).0;
14347 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
14348 }
14349}
14350
14351impl<T: InvalidationRegion> InvalidationStack<T> {
14352 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
14353 where
14354 S: Clone + ToOffset,
14355 {
14356 while let Some(region) = self.last() {
14357 let all_selections_inside_invalidation_ranges =
14358 if selections.len() == region.ranges().len() {
14359 selections
14360 .iter()
14361 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
14362 .all(|(selection, invalidation_range)| {
14363 let head = selection.head().to_offset(buffer);
14364 invalidation_range.start <= head && invalidation_range.end >= head
14365 })
14366 } else {
14367 false
14368 };
14369
14370 if all_selections_inside_invalidation_ranges {
14371 break;
14372 } else {
14373 self.pop();
14374 }
14375 }
14376 }
14377}
14378
14379impl<T> Default for InvalidationStack<T> {
14380 fn default() -> Self {
14381 Self(Default::default())
14382 }
14383}
14384
14385impl<T> Deref for InvalidationStack<T> {
14386 type Target = Vec<T>;
14387
14388 fn deref(&self) -> &Self::Target {
14389 &self.0
14390 }
14391}
14392
14393impl<T> DerefMut for InvalidationStack<T> {
14394 fn deref_mut(&mut self) -> &mut Self::Target {
14395 &mut self.0
14396 }
14397}
14398
14399impl InvalidationRegion for SnippetState {
14400 fn ranges(&self) -> &[Range<Anchor>] {
14401 &self.ranges[self.active_index]
14402 }
14403}
14404
14405pub fn diagnostic_block_renderer(
14406 diagnostic: Diagnostic,
14407 max_message_rows: Option<u8>,
14408 allow_closing: bool,
14409 _is_valid: bool,
14410) -> RenderBlock {
14411 let (text_without_backticks, code_ranges) =
14412 highlight_diagnostic_message(&diagnostic, max_message_rows);
14413
14414 Arc::new(move |cx: &mut BlockContext| {
14415 let group_id: SharedString = cx.block_id.to_string().into();
14416
14417 let mut text_style = cx.text_style().clone();
14418 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
14419 let theme_settings = ThemeSettings::get_global(cx);
14420 text_style.font_family = theme_settings.buffer_font.family.clone();
14421 text_style.font_style = theme_settings.buffer_font.style;
14422 text_style.font_features = theme_settings.buffer_font.features.clone();
14423 text_style.font_weight = theme_settings.buffer_font.weight;
14424
14425 let multi_line_diagnostic = diagnostic.message.contains('\n');
14426
14427 let buttons = |diagnostic: &Diagnostic| {
14428 if multi_line_diagnostic {
14429 v_flex()
14430 } else {
14431 h_flex()
14432 }
14433 .when(allow_closing, |div| {
14434 div.children(diagnostic.is_primary.then(|| {
14435 IconButton::new("close-block", IconName::XCircle)
14436 .icon_color(Color::Muted)
14437 .size(ButtonSize::Compact)
14438 .style(ButtonStyle::Transparent)
14439 .visible_on_hover(group_id.clone())
14440 .on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
14441 .tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
14442 }))
14443 })
14444 .child(
14445 IconButton::new("copy-block", IconName::Copy)
14446 .icon_color(Color::Muted)
14447 .size(ButtonSize::Compact)
14448 .style(ButtonStyle::Transparent)
14449 .visible_on_hover(group_id.clone())
14450 .on_click({
14451 let message = diagnostic.message.clone();
14452 move |_click, cx| {
14453 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
14454 }
14455 })
14456 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
14457 )
14458 };
14459
14460 let icon_size = buttons(&diagnostic)
14461 .into_any_element()
14462 .layout_as_root(AvailableSpace::min_size(), cx);
14463
14464 h_flex()
14465 .id(cx.block_id)
14466 .group(group_id.clone())
14467 .relative()
14468 .size_full()
14469 .block_mouse_down()
14470 .pl(cx.gutter_dimensions.width)
14471 .w(cx.max_width - cx.gutter_dimensions.full_width())
14472 .child(
14473 div()
14474 .flex()
14475 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
14476 .flex_shrink(),
14477 )
14478 .child(buttons(&diagnostic))
14479 .child(div().flex().flex_shrink_0().child(
14480 StyledText::new(text_without_backticks.clone()).with_highlights(
14481 &text_style,
14482 code_ranges.iter().map(|range| {
14483 (
14484 range.clone(),
14485 HighlightStyle {
14486 font_weight: Some(FontWeight::BOLD),
14487 ..Default::default()
14488 },
14489 )
14490 }),
14491 ),
14492 ))
14493 .into_any_element()
14494 })
14495}
14496
14497pub fn highlight_diagnostic_message(
14498 diagnostic: &Diagnostic,
14499 mut max_message_rows: Option<u8>,
14500) -> (SharedString, Vec<Range<usize>>) {
14501 let mut text_without_backticks = String::new();
14502 let mut code_ranges = Vec::new();
14503
14504 if let Some(source) = &diagnostic.source {
14505 text_without_backticks.push_str(source);
14506 code_ranges.push(0..source.len());
14507 text_without_backticks.push_str(": ");
14508 }
14509
14510 let mut prev_offset = 0;
14511 let mut in_code_block = false;
14512 let has_row_limit = max_message_rows.is_some();
14513 let mut newline_indices = diagnostic
14514 .message
14515 .match_indices('\n')
14516 .filter(|_| has_row_limit)
14517 .map(|(ix, _)| ix)
14518 .fuse()
14519 .peekable();
14520
14521 for (quote_ix, _) in diagnostic
14522 .message
14523 .match_indices('`')
14524 .chain([(diagnostic.message.len(), "")])
14525 {
14526 let mut first_newline_ix = None;
14527 let mut last_newline_ix = None;
14528 while let Some(newline_ix) = newline_indices.peek() {
14529 if *newline_ix < quote_ix {
14530 if first_newline_ix.is_none() {
14531 first_newline_ix = Some(*newline_ix);
14532 }
14533 last_newline_ix = Some(*newline_ix);
14534
14535 if let Some(rows_left) = &mut max_message_rows {
14536 if *rows_left == 0 {
14537 break;
14538 } else {
14539 *rows_left -= 1;
14540 }
14541 }
14542 let _ = newline_indices.next();
14543 } else {
14544 break;
14545 }
14546 }
14547 let prev_len = text_without_backticks.len();
14548 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
14549 text_without_backticks.push_str(new_text);
14550 if in_code_block {
14551 code_ranges.push(prev_len..text_without_backticks.len());
14552 }
14553 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
14554 in_code_block = !in_code_block;
14555 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
14556 text_without_backticks.push_str("...");
14557 break;
14558 }
14559 }
14560
14561 (text_without_backticks.into(), code_ranges)
14562}
14563
14564fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
14565 match severity {
14566 DiagnosticSeverity::ERROR => colors.error,
14567 DiagnosticSeverity::WARNING => colors.warning,
14568 DiagnosticSeverity::INFORMATION => colors.info,
14569 DiagnosticSeverity::HINT => colors.info,
14570 _ => colors.ignored,
14571 }
14572}
14573
14574pub fn styled_runs_for_code_label<'a>(
14575 label: &'a CodeLabel,
14576 syntax_theme: &'a theme::SyntaxTheme,
14577) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
14578 let fade_out = HighlightStyle {
14579 fade_out: Some(0.35),
14580 ..Default::default()
14581 };
14582
14583 let mut prev_end = label.filter_range.end;
14584 label
14585 .runs
14586 .iter()
14587 .enumerate()
14588 .flat_map(move |(ix, (range, highlight_id))| {
14589 let style = if let Some(style) = highlight_id.style(syntax_theme) {
14590 style
14591 } else {
14592 return Default::default();
14593 };
14594 let mut muted_style = style;
14595 muted_style.highlight(fade_out);
14596
14597 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
14598 if range.start >= label.filter_range.end {
14599 if range.start > prev_end {
14600 runs.push((prev_end..range.start, fade_out));
14601 }
14602 runs.push((range.clone(), muted_style));
14603 } else if range.end <= label.filter_range.end {
14604 runs.push((range.clone(), style));
14605 } else {
14606 runs.push((range.start..label.filter_range.end, style));
14607 runs.push((label.filter_range.end..range.end, muted_style));
14608 }
14609 prev_end = cmp::max(prev_end, range.end);
14610
14611 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
14612 runs.push((prev_end..label.text.len(), fade_out));
14613 }
14614
14615 runs
14616 })
14617}
14618
14619pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
14620 let mut prev_index = 0;
14621 let mut prev_codepoint: Option<char> = None;
14622 text.char_indices()
14623 .chain([(text.len(), '\0')])
14624 .filter_map(move |(index, codepoint)| {
14625 let prev_codepoint = prev_codepoint.replace(codepoint)?;
14626 let is_boundary = index == text.len()
14627 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
14628 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
14629 if is_boundary {
14630 let chunk = &text[prev_index..index];
14631 prev_index = index;
14632 Some(chunk)
14633 } else {
14634 None
14635 }
14636 })
14637}
14638
14639pub trait RangeToAnchorExt: Sized {
14640 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
14641
14642 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
14643 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
14644 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
14645 }
14646}
14647
14648impl<T: ToOffset> RangeToAnchorExt for Range<T> {
14649 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
14650 let start_offset = self.start.to_offset(snapshot);
14651 let end_offset = self.end.to_offset(snapshot);
14652 if start_offset == end_offset {
14653 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
14654 } else {
14655 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
14656 }
14657 }
14658}
14659
14660pub trait RowExt {
14661 fn as_f32(&self) -> f32;
14662
14663 fn next_row(&self) -> Self;
14664
14665 fn previous_row(&self) -> Self;
14666
14667 fn minus(&self, other: Self) -> u32;
14668}
14669
14670impl RowExt for DisplayRow {
14671 fn as_f32(&self) -> f32 {
14672 self.0 as f32
14673 }
14674
14675 fn next_row(&self) -> Self {
14676 Self(self.0 + 1)
14677 }
14678
14679 fn previous_row(&self) -> Self {
14680 Self(self.0.saturating_sub(1))
14681 }
14682
14683 fn minus(&self, other: Self) -> u32 {
14684 self.0 - other.0
14685 }
14686}
14687
14688impl RowExt for MultiBufferRow {
14689 fn as_f32(&self) -> f32 {
14690 self.0 as f32
14691 }
14692
14693 fn next_row(&self) -> Self {
14694 Self(self.0 + 1)
14695 }
14696
14697 fn previous_row(&self) -> Self {
14698 Self(self.0.saturating_sub(1))
14699 }
14700
14701 fn minus(&self, other: Self) -> u32 {
14702 self.0 - other.0
14703 }
14704}
14705
14706trait RowRangeExt {
14707 type Row;
14708
14709 fn len(&self) -> usize;
14710
14711 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
14712}
14713
14714impl RowRangeExt for Range<MultiBufferRow> {
14715 type Row = MultiBufferRow;
14716
14717 fn len(&self) -> usize {
14718 (self.end.0 - self.start.0) as usize
14719 }
14720
14721 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
14722 (self.start.0..self.end.0).map(MultiBufferRow)
14723 }
14724}
14725
14726impl RowRangeExt for Range<DisplayRow> {
14727 type Row = DisplayRow;
14728
14729 fn len(&self) -> usize {
14730 (self.end.0 - self.start.0) as usize
14731 }
14732
14733 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
14734 (self.start.0..self.end.0).map(DisplayRow)
14735 }
14736}
14737
14738fn hunk_status(hunk: &MultiBufferDiffHunk) -> DiffHunkStatus {
14739 if hunk.diff_base_byte_range.is_empty() {
14740 DiffHunkStatus::Added
14741 } else if hunk.row_range.is_empty() {
14742 DiffHunkStatus::Removed
14743 } else {
14744 DiffHunkStatus::Modified
14745 }
14746}
14747
14748/// If select range has more than one line, we
14749/// just point the cursor to range.start.
14750fn check_multiline_range(buffer: &Buffer, range: Range<usize>) -> Range<usize> {
14751 if buffer.offset_to_point(range.start).row == buffer.offset_to_point(range.end).row {
14752 range
14753 } else {
14754 range.start..range.start
14755 }
14756}
14757
14758pub struct KillRing(ClipboardItem);
14759impl Global for KillRing {}
14760
14761const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);