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 debounced_delay;
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;
31mod inline_completion_provider;
32pub mod items;
33mod linked_editing_ranges;
34mod lsp_ext;
35mod mouse_context_menu;
36pub mod movement;
37mod persistence;
38mod proposed_changes_editor;
39mod rust_analyzer_ext;
40pub mod scroll;
41mod selections_collection;
42pub mod tasks;
43
44#[cfg(test)]
45mod editor_tests;
46mod signature_help;
47#[cfg(any(test, feature = "test-support"))]
48pub mod test;
49
50use ::git::diff::DiffHunkStatus;
51pub(crate) use actions::*;
52pub use actions::{OpenExcerpts, OpenExcerptsSplit};
53use aho_corasick::AhoCorasick;
54use anyhow::{anyhow, Context as _, Result};
55use blink_manager::BlinkManager;
56use client::{Collaborator, ParticipantIndex};
57use clock::ReplicaId;
58use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
59use convert_case::{Case, Casing};
60use debounced_delay::DebouncedDelay;
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::{StringMatch, StringMatchCandidate};
73use git::blame::GitBlame;
74use gpui::{
75 div, impl_actions, point, prelude::*, px, relative, size, uniform_list, Action, AnyElement,
76 AppContext, AsyncWindowContext, AvailableSpace, BackgroundExecutor, Bounds, ClipboardEntry,
77 ClipboardItem, Context, DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusOutEvent,
78 FocusableView, FontId, FontWeight, HighlightStyle, Hsla, InteractiveText, KeyContext,
79 ListSizingBehavior, Model, ModelContext, MouseButton, PaintQuad, ParentElement, Pixels, Render,
80 ScrollStrategy, SharedString, Size, StrikethroughStyle, Styled, StyledText, Subscription, Task,
81 TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle, UniformListScrollHandle, View,
82 ViewContext, ViewInputHandler, VisualContext, WeakFocusHandle, WeakView, WindowContext,
83};
84use highlight_matching_bracket::refresh_matching_bracket_highlights;
85use hover_popover::{hide_hover, HoverState};
86pub(crate) use hunk_diff::HoveredHunk;
87use hunk_diff::{diff_hunk_to_display, ExpandedHunks};
88use indent_guides::ActiveIndentGuidesState;
89use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
90pub use inline_completion_provider::*;
91pub use items::MAX_TAB_TITLE_LEN;
92use itertools::Itertools;
93use language::{
94 language_settings::{self, all_language_settings, language_settings, InlayHintSettings},
95 markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
96 CursorShape, Diagnostic, Documentation, IndentKind, IndentSize, Language, OffsetRangeExt,
97 Point, Selection, SelectionGoal, TransactionId,
98};
99use language::{
100 point_to_lsp, BufferRow, CharClassifier, LanguageServerName, Runnable, RunnableRange,
101};
102use linked_editing_ranges::refresh_linked_ranges;
103pub use proposed_changes_editor::{
104 ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar,
105};
106use similar::{ChangeTag, TextDiff};
107use std::iter::Peekable;
108use task::{ResolvedTask, TaskTemplate, TaskVariables};
109
110use hover_links::{find_file, HoverLink, HoveredLinkState, InlayHighlight};
111pub use lsp::CompletionContext;
112use lsp::{
113 CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity, InsertTextFormat,
114 LanguageServerId,
115};
116use mouse_context_menu::MouseContextMenu;
117use movement::TextLayoutDetails;
118pub use multi_buffer::{
119 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
120 ToPoint,
121};
122use multi_buffer::{
123 ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow, ToOffsetUtf16,
124};
125use ordered_float::OrderedFloat;
126use parking_lot::{Mutex, RwLock};
127use project::{
128 lsp_store::{FormatTarget, FormatTrigger},
129 project_settings::{GitGutterSetting, ProjectSettings},
130 CodeAction, Completion, CompletionIntent, DocumentHighlight, InlayHint, Item, Location,
131 LocationLink, Project, ProjectTransaction, TaskSourceKind,
132};
133use rand::prelude::*;
134use rpc::{proto::*, ErrorExt};
135use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
136use selections_collection::{
137 resolve_selections, MutableSelectionsCollection, SelectionsCollection,
138};
139use serde::{Deserialize, Serialize};
140use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
141use smallvec::SmallVec;
142use snippet::Snippet;
143use std::{
144 any::TypeId,
145 borrow::Cow,
146 cell::RefCell,
147 cmp::{self, Ordering, Reverse},
148 mem,
149 num::NonZeroU32,
150 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
151 path::{Path, PathBuf},
152 rc::Rc,
153 sync::Arc,
154 time::{Duration, Instant},
155};
156pub use sum_tree::Bias;
157use sum_tree::TreeMap;
158use text::{BufferId, OffsetUtf16, Rope};
159use theme::{
160 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
161 ThemeColors, ThemeSettings,
162};
163use ui::{
164 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize,
165 ListItem, Popover, PopoverMenuHandle, Tooltip,
166};
167use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
168use workspace::item::{ItemHandle, PreviewTabsSettings};
169use workspace::notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt};
170use workspace::{
171 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
172};
173use workspace::{Item as WorkspaceItem, OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
174
175use crate::hover_links::find_url;
176use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
177
178pub const FILE_HEADER_HEIGHT: u32 = 2;
179pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
180pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u32 = 1;
181pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
182const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
183const MAX_LINE_LEN: usize = 1024;
184const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
185const MAX_SELECTION_HISTORY_LEN: usize = 1024;
186pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
187#[doc(hidden)]
188pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
189#[doc(hidden)]
190pub const DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
191
192pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
193pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
194
195pub fn render_parsed_markdown(
196 element_id: impl Into<ElementId>,
197 parsed: &language::ParsedMarkdown,
198 editor_style: &EditorStyle,
199 workspace: Option<WeakView<Workspace>>,
200 cx: &mut WindowContext,
201) -> InteractiveText {
202 let code_span_background_color = cx
203 .theme()
204 .colors()
205 .editor_document_highlight_read_background;
206
207 let highlights = gpui::combine_highlights(
208 parsed.highlights.iter().filter_map(|(range, highlight)| {
209 let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
210 Some((range.clone(), highlight))
211 }),
212 parsed
213 .regions
214 .iter()
215 .zip(&parsed.region_ranges)
216 .filter_map(|(region, range)| {
217 if region.code {
218 Some((
219 range.clone(),
220 HighlightStyle {
221 background_color: Some(code_span_background_color),
222 ..Default::default()
223 },
224 ))
225 } else {
226 None
227 }
228 }),
229 );
230
231 let mut links = Vec::new();
232 let mut link_ranges = Vec::new();
233 for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
234 if let Some(link) = region.link.clone() {
235 links.push(link);
236 link_ranges.push(range.clone());
237 }
238 }
239
240 InteractiveText::new(
241 element_id,
242 StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
243 )
244 .on_click(link_ranges, move |clicked_range_ix, cx| {
245 match &links[clicked_range_ix] {
246 markdown::Link::Web { url } => cx.open_url(url),
247 markdown::Link::Path { path } => {
248 if let Some(workspace) = &workspace {
249 _ = workspace.update(cx, |workspace, cx| {
250 workspace.open_abs_path(path.clone(), false, cx).detach();
251 });
252 }
253 }
254 }
255 })
256}
257
258#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
259pub(crate) enum InlayId {
260 Suggestion(usize),
261 Hint(usize),
262}
263
264impl InlayId {
265 fn id(&self) -> usize {
266 match self {
267 Self::Suggestion(id) => *id,
268 Self::Hint(id) => *id,
269 }
270 }
271}
272
273enum DiffRowHighlight {}
274enum DocumentHighlightRead {}
275enum DocumentHighlightWrite {}
276enum InputComposition {}
277
278#[derive(Copy, Clone, PartialEq, Eq)]
279pub enum Direction {
280 Prev,
281 Next,
282}
283
284#[derive(Debug, Copy, Clone, PartialEq, Eq)]
285pub enum Navigated {
286 Yes,
287 No,
288}
289
290impl Navigated {
291 pub fn from_bool(yes: bool) -> Navigated {
292 if yes {
293 Navigated::Yes
294 } else {
295 Navigated::No
296 }
297 }
298}
299
300pub fn init_settings(cx: &mut AppContext) {
301 EditorSettings::register(cx);
302}
303
304pub fn init(cx: &mut AppContext) {
305 init_settings(cx);
306
307 workspace::register_project_item::<Editor>(cx);
308 workspace::FollowableViewRegistry::register::<Editor>(cx);
309 workspace::register_serializable_item::<Editor>(cx);
310
311 cx.observe_new_views(
312 |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
313 workspace.register_action(Editor::new_file);
314 workspace.register_action(Editor::new_file_vertical);
315 workspace.register_action(Editor::new_file_horizontal);
316 },
317 )
318 .detach();
319
320 cx.on_action(move |_: &workspace::NewFile, cx| {
321 let app_state = workspace::AppState::global(cx);
322 if let Some(app_state) = app_state.upgrade() {
323 workspace::open_new(Default::default(), app_state, cx, |workspace, cx| {
324 Editor::new_file(workspace, &Default::default(), cx)
325 })
326 .detach();
327 }
328 });
329 cx.on_action(move |_: &workspace::NewWindow, cx| {
330 let app_state = workspace::AppState::global(cx);
331 if let Some(app_state) = app_state.upgrade() {
332 workspace::open_new(Default::default(), app_state, cx, |workspace, cx| {
333 Editor::new_file(workspace, &Default::default(), cx)
334 })
335 .detach();
336 }
337 });
338}
339
340pub struct SearchWithinRange;
341
342trait InvalidationRegion {
343 fn ranges(&self) -> &[Range<Anchor>];
344}
345
346#[derive(Clone, Debug, PartialEq)]
347pub enum SelectPhase {
348 Begin {
349 position: DisplayPoint,
350 add: bool,
351 click_count: usize,
352 },
353 BeginColumnar {
354 position: DisplayPoint,
355 reset: bool,
356 goal_column: u32,
357 },
358 Extend {
359 position: DisplayPoint,
360 click_count: usize,
361 },
362 Update {
363 position: DisplayPoint,
364 goal_column: u32,
365 scroll_delta: gpui::Point<f32>,
366 },
367 End,
368}
369
370#[derive(Clone, Debug)]
371pub enum SelectMode {
372 Character,
373 Word(Range<Anchor>),
374 Line(Range<Anchor>),
375 All,
376}
377
378#[derive(Copy, Clone, PartialEq, Eq, Debug)]
379pub enum EditorMode {
380 SingleLine { auto_width: bool },
381 AutoHeight { max_lines: usize },
382 Full,
383}
384
385#[derive(Copy, Clone, Debug)]
386pub enum SoftWrap {
387 /// Prefer not to wrap at all.
388 ///
389 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
390 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
391 GitDiff,
392 /// Prefer a single line generally, unless an overly long line is encountered.
393 None,
394 /// Soft wrap lines that exceed the editor width.
395 EditorWidth,
396 /// Soft wrap lines at the preferred line length.
397 Column(u32),
398 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
399 Bounded(u32),
400}
401
402#[derive(Clone)]
403pub struct EditorStyle {
404 pub background: Hsla,
405 pub local_player: PlayerColor,
406 pub text: TextStyle,
407 pub scrollbar_width: Pixels,
408 pub syntax: Arc<SyntaxTheme>,
409 pub status: StatusColors,
410 pub inlay_hints_style: HighlightStyle,
411 pub suggestions_style: HighlightStyle,
412 pub unnecessary_code_fade: f32,
413}
414
415impl Default for EditorStyle {
416 fn default() -> Self {
417 Self {
418 background: Hsla::default(),
419 local_player: PlayerColor::default(),
420 text: TextStyle::default(),
421 scrollbar_width: Pixels::default(),
422 syntax: Default::default(),
423 // HACK: Status colors don't have a real default.
424 // We should look into removing the status colors from the editor
425 // style and retrieve them directly from the theme.
426 status: StatusColors::dark(),
427 inlay_hints_style: HighlightStyle::default(),
428 suggestions_style: HighlightStyle::default(),
429 unnecessary_code_fade: Default::default(),
430 }
431 }
432}
433
434pub fn make_inlay_hints_style(cx: &WindowContext) -> HighlightStyle {
435 let show_background = language_settings::language_settings(None, None, cx)
436 .inlay_hints
437 .show_background;
438
439 HighlightStyle {
440 color: Some(cx.theme().status().hint),
441 background_color: show_background.then(|| cx.theme().status().hint_background),
442 ..HighlightStyle::default()
443 }
444}
445
446type CompletionId = usize;
447
448#[derive(Clone, Debug)]
449struct CompletionState {
450 // render_inlay_ids represents the inlay hints that are inserted
451 // for rendering the inline completions. They may be discontinuous
452 // in the event that the completion provider returns some intersection
453 // with the existing content.
454 render_inlay_ids: Vec<InlayId>,
455 // text is the resulting rope that is inserted when the user accepts a completion.
456 text: Rope,
457 // position is the position of the cursor when the completion was triggered.
458 position: multi_buffer::Anchor,
459 // delete_range is the range of text that this completion state covers.
460 // if the completion is accepted, this range should be deleted.
461 delete_range: Option<Range<multi_buffer::Anchor>>,
462}
463
464#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
465struct EditorActionId(usize);
466
467impl EditorActionId {
468 pub fn post_inc(&mut self) -> Self {
469 let answer = self.0;
470
471 *self = Self(answer + 1);
472
473 Self(answer)
474 }
475}
476
477// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
478// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
479
480type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
481type GutterHighlight = (fn(&AppContext) -> Hsla, Arc<[Range<Anchor>]>);
482
483#[derive(Default)]
484struct ScrollbarMarkerState {
485 scrollbar_size: Size<Pixels>,
486 dirty: bool,
487 markers: Arc<[PaintQuad]>,
488 pending_refresh: Option<Task<Result<()>>>,
489}
490
491impl ScrollbarMarkerState {
492 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
493 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
494 }
495}
496
497#[derive(Clone, Debug)]
498struct RunnableTasks {
499 templates: Vec<(TaskSourceKind, TaskTemplate)>,
500 offset: MultiBufferOffset,
501 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
502 column: u32,
503 // Values of all named captures, including those starting with '_'
504 extra_variables: HashMap<String, String>,
505 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
506 context_range: Range<BufferOffset>,
507}
508
509impl RunnableTasks {
510 fn resolve<'a>(
511 &'a self,
512 cx: &'a task::TaskContext,
513 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
514 self.templates.iter().filter_map(|(kind, template)| {
515 template
516 .resolve_task(&kind.to_id_base(), cx)
517 .map(|task| (kind.clone(), task))
518 })
519 }
520}
521
522#[derive(Clone)]
523struct ResolvedTasks {
524 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
525 position: Anchor,
526}
527#[derive(Copy, Clone, Debug)]
528struct MultiBufferOffset(usize);
529#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
530struct BufferOffset(usize);
531
532// Addons allow storing per-editor state in other crates (e.g. Vim)
533pub trait Addon: 'static {
534 fn extend_key_context(&self, _: &mut KeyContext, _: &AppContext) {}
535
536 fn to_any(&self) -> &dyn std::any::Any;
537}
538
539/// Zed's primary text input `View`, allowing users to edit a [`MultiBuffer`]
540///
541/// See the [module level documentation](self) for more information.
542pub struct Editor {
543 focus_handle: FocusHandle,
544 last_focused_descendant: Option<WeakFocusHandle>,
545 /// The text buffer being edited
546 buffer: Model<MultiBuffer>,
547 /// Map of how text in the buffer should be displayed.
548 /// Handles soft wraps, folds, fake inlay text insertions, etc.
549 pub display_map: Model<DisplayMap>,
550 pub selections: SelectionsCollection,
551 pub scroll_manager: ScrollManager,
552 /// When inline assist editors are linked, they all render cursors because
553 /// typing enters text into each of them, even the ones that aren't focused.
554 pub(crate) show_cursor_when_unfocused: bool,
555 columnar_selection_tail: Option<Anchor>,
556 add_selections_state: Option<AddSelectionsState>,
557 select_next_state: Option<SelectNextState>,
558 select_prev_state: Option<SelectNextState>,
559 selection_history: SelectionHistory,
560 autoclose_regions: Vec<AutocloseRegion>,
561 snippet_stack: InvalidationStack<SnippetState>,
562 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
563 ime_transaction: Option<TransactionId>,
564 active_diagnostics: Option<ActiveDiagnosticGroup>,
565 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
566
567 project: Option<Model<Project>>,
568 semantics_provider: Option<Rc<dyn SemanticsProvider>>,
569 completion_provider: Option<Box<dyn CompletionProvider>>,
570 collaboration_hub: Option<Box<dyn CollaborationHub>>,
571 blink_manager: Model<BlinkManager>,
572 show_cursor_names: bool,
573 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
574 pub show_local_selections: bool,
575 mode: EditorMode,
576 show_breadcrumbs: bool,
577 show_gutter: bool,
578 show_line_numbers: Option<bool>,
579 use_relative_line_numbers: Option<bool>,
580 show_git_diff_gutter: Option<bool>,
581 show_code_actions: Option<bool>,
582 show_runnables: Option<bool>,
583 show_wrap_guides: Option<bool>,
584 show_indent_guides: Option<bool>,
585 placeholder_text: Option<Arc<str>>,
586 highlight_order: usize,
587 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
588 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
589 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
590 scrollbar_marker_state: ScrollbarMarkerState,
591 active_indent_guides_state: ActiveIndentGuidesState,
592 nav_history: Option<ItemNavHistory>,
593 context_menu: RwLock<Option<ContextMenu>>,
594 mouse_context_menu: Option<MouseContextMenu>,
595 hunk_controls_menu_handle: PopoverMenuHandle<ui::ContextMenu>,
596 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
597 signature_help_state: SignatureHelpState,
598 auto_signature_help: Option<bool>,
599 find_all_references_task_sources: Vec<Anchor>,
600 next_completion_id: CompletionId,
601 completion_documentation_pre_resolve_debounce: DebouncedDelay,
602 available_code_actions: Option<(Location, Arc<[AvailableCodeAction]>)>,
603 code_actions_task: Option<Task<Result<()>>>,
604 document_highlights_task: Option<Task<()>>,
605 linked_editing_range_task: Option<Task<Option<()>>>,
606 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
607 pending_rename: Option<RenameState>,
608 searchable: bool,
609 cursor_shape: CursorShape,
610 current_line_highlight: Option<CurrentLineHighlight>,
611 collapse_matches: bool,
612 autoindent_mode: Option<AutoindentMode>,
613 workspace: Option<(WeakView<Workspace>, Option<WorkspaceId>)>,
614 input_enabled: bool,
615 use_modal_editing: bool,
616 read_only: bool,
617 leader_peer_id: Option<PeerId>,
618 remote_id: Option<ViewId>,
619 hover_state: HoverState,
620 gutter_hovered: bool,
621 hovered_link_state: Option<HoveredLinkState>,
622 inline_completion_provider: Option<RegisteredInlineCompletionProvider>,
623 code_action_providers: Vec<Arc<dyn CodeActionProvider>>,
624 active_inline_completion: Option<CompletionState>,
625 // enable_inline_completions is a switch that Vim can use to disable
626 // inline completions based on its mode.
627 enable_inline_completions: bool,
628 show_inline_completions_override: Option<bool>,
629 inlay_hint_cache: InlayHintCache,
630 expanded_hunks: ExpandedHunks,
631 next_inlay_id: usize,
632 _subscriptions: Vec<Subscription>,
633 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
634 gutter_dimensions: GutterDimensions,
635 style: Option<EditorStyle>,
636 text_style_refinement: Option<TextStyleRefinement>,
637 next_editor_action_id: EditorActionId,
638 editor_actions: Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut ViewContext<Self>)>>>>,
639 use_autoclose: bool,
640 use_auto_surround: bool,
641 auto_replace_emoji_shortcode: bool,
642 show_git_blame_gutter: bool,
643 show_git_blame_inline: bool,
644 show_git_blame_inline_delay_task: Option<Task<()>>,
645 git_blame_inline_enabled: bool,
646 serialize_dirty_buffers: bool,
647 show_selection_menu: Option<bool>,
648 blame: Option<Model<GitBlame>>,
649 blame_subscription: Option<Subscription>,
650 custom_context_menu: Option<
651 Box<
652 dyn 'static
653 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
654 >,
655 >,
656 last_bounds: Option<Bounds<Pixels>>,
657 expect_bounds_change: Option<Bounds<Pixels>>,
658 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
659 tasks_update_task: Option<Task<()>>,
660 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
661 breadcrumb_header: Option<String>,
662 focused_block: Option<FocusedBlock>,
663 next_scroll_position: NextScrollCursorCenterTopBottom,
664 addons: HashMap<TypeId, Box<dyn Addon>>,
665 _scroll_cursor_center_top_bottom_task: Task<()>,
666}
667
668#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
669enum NextScrollCursorCenterTopBottom {
670 #[default]
671 Center,
672 Top,
673 Bottom,
674}
675
676impl NextScrollCursorCenterTopBottom {
677 fn next(&self) -> Self {
678 match self {
679 Self::Center => Self::Top,
680 Self::Top => Self::Bottom,
681 Self::Bottom => Self::Center,
682 }
683 }
684}
685
686#[derive(Clone)]
687pub struct EditorSnapshot {
688 pub mode: EditorMode,
689 show_gutter: bool,
690 show_line_numbers: Option<bool>,
691 show_git_diff_gutter: Option<bool>,
692 show_code_actions: Option<bool>,
693 show_runnables: Option<bool>,
694 git_blame_gutter_max_author_length: Option<usize>,
695 pub display_snapshot: DisplaySnapshot,
696 pub placeholder_text: Option<Arc<str>>,
697 is_focused: bool,
698 scroll_anchor: ScrollAnchor,
699 ongoing_scroll: OngoingScroll,
700 current_line_highlight: CurrentLineHighlight,
701 gutter_hovered: bool,
702}
703
704const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
705
706#[derive(Default, Debug, Clone, Copy)]
707pub struct GutterDimensions {
708 pub left_padding: Pixels,
709 pub right_padding: Pixels,
710 pub width: Pixels,
711 pub margin: Pixels,
712 pub git_blame_entries_width: Option<Pixels>,
713}
714
715impl GutterDimensions {
716 /// The full width of the space taken up by the gutter.
717 pub fn full_width(&self) -> Pixels {
718 self.margin + self.width
719 }
720
721 /// The width of the space reserved for the fold indicators,
722 /// use alongside 'justify_end' and `gutter_width` to
723 /// right align content with the line numbers
724 pub fn fold_area_width(&self) -> Pixels {
725 self.margin + self.right_padding
726 }
727}
728
729#[derive(Debug)]
730pub struct RemoteSelection {
731 pub replica_id: ReplicaId,
732 pub selection: Selection<Anchor>,
733 pub cursor_shape: CursorShape,
734 pub peer_id: PeerId,
735 pub line_mode: bool,
736 pub participant_index: Option<ParticipantIndex>,
737 pub user_name: Option<SharedString>,
738}
739
740#[derive(Clone, Debug)]
741struct SelectionHistoryEntry {
742 selections: Arc<[Selection<Anchor>]>,
743 select_next_state: Option<SelectNextState>,
744 select_prev_state: Option<SelectNextState>,
745 add_selections_state: Option<AddSelectionsState>,
746}
747
748enum SelectionHistoryMode {
749 Normal,
750 Undoing,
751 Redoing,
752}
753
754#[derive(Clone, PartialEq, Eq, Hash)]
755struct HoveredCursor {
756 replica_id: u16,
757 selection_id: usize,
758}
759
760impl Default for SelectionHistoryMode {
761 fn default() -> Self {
762 Self::Normal
763 }
764}
765
766#[derive(Default)]
767struct SelectionHistory {
768 #[allow(clippy::type_complexity)]
769 selections_by_transaction:
770 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
771 mode: SelectionHistoryMode,
772 undo_stack: VecDeque<SelectionHistoryEntry>,
773 redo_stack: VecDeque<SelectionHistoryEntry>,
774}
775
776impl SelectionHistory {
777 fn insert_transaction(
778 &mut self,
779 transaction_id: TransactionId,
780 selections: Arc<[Selection<Anchor>]>,
781 ) {
782 self.selections_by_transaction
783 .insert(transaction_id, (selections, None));
784 }
785
786 #[allow(clippy::type_complexity)]
787 fn transaction(
788 &self,
789 transaction_id: TransactionId,
790 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
791 self.selections_by_transaction.get(&transaction_id)
792 }
793
794 #[allow(clippy::type_complexity)]
795 fn transaction_mut(
796 &mut self,
797 transaction_id: TransactionId,
798 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
799 self.selections_by_transaction.get_mut(&transaction_id)
800 }
801
802 fn push(&mut self, entry: SelectionHistoryEntry) {
803 if !entry.selections.is_empty() {
804 match self.mode {
805 SelectionHistoryMode::Normal => {
806 self.push_undo(entry);
807 self.redo_stack.clear();
808 }
809 SelectionHistoryMode::Undoing => self.push_redo(entry),
810 SelectionHistoryMode::Redoing => self.push_undo(entry),
811 }
812 }
813 }
814
815 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
816 if self
817 .undo_stack
818 .back()
819 .map_or(true, |e| e.selections != entry.selections)
820 {
821 self.undo_stack.push_back(entry);
822 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
823 self.undo_stack.pop_front();
824 }
825 }
826 }
827
828 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
829 if self
830 .redo_stack
831 .back()
832 .map_or(true, |e| e.selections != entry.selections)
833 {
834 self.redo_stack.push_back(entry);
835 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
836 self.redo_stack.pop_front();
837 }
838 }
839 }
840}
841
842struct RowHighlight {
843 index: usize,
844 range: Range<Anchor>,
845 color: Hsla,
846 should_autoscroll: bool,
847}
848
849#[derive(Clone, Debug)]
850struct AddSelectionsState {
851 above: bool,
852 stack: Vec<usize>,
853}
854
855#[derive(Clone)]
856struct SelectNextState {
857 query: AhoCorasick,
858 wordwise: bool,
859 done: bool,
860}
861
862impl std::fmt::Debug for SelectNextState {
863 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
864 f.debug_struct(std::any::type_name::<Self>())
865 .field("wordwise", &self.wordwise)
866 .field("done", &self.done)
867 .finish()
868 }
869}
870
871#[derive(Debug)]
872struct AutocloseRegion {
873 selection_id: usize,
874 range: Range<Anchor>,
875 pair: BracketPair,
876}
877
878#[derive(Debug)]
879struct SnippetState {
880 ranges: Vec<Vec<Range<Anchor>>>,
881 active_index: usize,
882}
883
884#[doc(hidden)]
885pub struct RenameState {
886 pub range: Range<Anchor>,
887 pub old_name: Arc<str>,
888 pub editor: View<Editor>,
889 block_id: CustomBlockId,
890}
891
892struct InvalidationStack<T>(Vec<T>);
893
894struct RegisteredInlineCompletionProvider {
895 provider: Arc<dyn InlineCompletionProviderHandle>,
896 _subscription: Subscription,
897}
898
899enum ContextMenu {
900 Completions(CompletionsMenu),
901 CodeActions(CodeActionsMenu),
902}
903
904impl ContextMenu {
905 fn select_first(
906 &mut self,
907 provider: Option<&dyn CompletionProvider>,
908 cx: &mut ViewContext<Editor>,
909 ) -> bool {
910 if self.visible() {
911 match self {
912 ContextMenu::Completions(menu) => menu.select_first(provider, cx),
913 ContextMenu::CodeActions(menu) => menu.select_first(cx),
914 }
915 true
916 } else {
917 false
918 }
919 }
920
921 fn select_prev(
922 &mut self,
923 provider: Option<&dyn CompletionProvider>,
924 cx: &mut ViewContext<Editor>,
925 ) -> bool {
926 if self.visible() {
927 match self {
928 ContextMenu::Completions(menu) => menu.select_prev(provider, cx),
929 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
930 }
931 true
932 } else {
933 false
934 }
935 }
936
937 fn select_next(
938 &mut self,
939 provider: Option<&dyn CompletionProvider>,
940 cx: &mut ViewContext<Editor>,
941 ) -> bool {
942 if self.visible() {
943 match self {
944 ContextMenu::Completions(menu) => menu.select_next(provider, cx),
945 ContextMenu::CodeActions(menu) => menu.select_next(cx),
946 }
947 true
948 } else {
949 false
950 }
951 }
952
953 fn select_last(
954 &mut self,
955 provider: Option<&dyn CompletionProvider>,
956 cx: &mut ViewContext<Editor>,
957 ) -> bool {
958 if self.visible() {
959 match self {
960 ContextMenu::Completions(menu) => menu.select_last(provider, cx),
961 ContextMenu::CodeActions(menu) => menu.select_last(cx),
962 }
963 true
964 } else {
965 false
966 }
967 }
968
969 fn visible(&self) -> bool {
970 match self {
971 ContextMenu::Completions(menu) => menu.visible(),
972 ContextMenu::CodeActions(menu) => menu.visible(),
973 }
974 }
975
976 fn render(
977 &self,
978 cursor_position: DisplayPoint,
979 style: &EditorStyle,
980 max_height: Pixels,
981 workspace: Option<WeakView<Workspace>>,
982 cx: &mut ViewContext<Editor>,
983 ) -> (ContextMenuOrigin, AnyElement) {
984 match self {
985 ContextMenu::Completions(menu) => (
986 ContextMenuOrigin::EditorPoint(cursor_position),
987 menu.render(style, max_height, workspace, cx),
988 ),
989 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, max_height, cx),
990 }
991 }
992}
993
994enum ContextMenuOrigin {
995 EditorPoint(DisplayPoint),
996 GutterIndicator(DisplayRow),
997}
998
999#[derive(Clone)]
1000struct CompletionsMenu {
1001 id: CompletionId,
1002 sort_completions: bool,
1003 initial_position: Anchor,
1004 buffer: Model<Buffer>,
1005 completions: Arc<RwLock<Box<[Completion]>>>,
1006 match_candidates: Arc<[StringMatchCandidate]>,
1007 matches: Arc<[StringMatch]>,
1008 selected_item: usize,
1009 scroll_handle: UniformListScrollHandle,
1010 selected_completion_documentation_resolve_debounce: Arc<Mutex<DebouncedDelay>>,
1011}
1012
1013impl CompletionsMenu {
1014 fn select_first(
1015 &mut self,
1016 provider: Option<&dyn CompletionProvider>,
1017 cx: &mut ViewContext<Editor>,
1018 ) {
1019 self.selected_item = 0;
1020 self.scroll_handle
1021 .scroll_to_item(self.selected_item, ScrollStrategy::Top);
1022 self.attempt_resolve_selected_completion_documentation(provider, cx);
1023 cx.notify();
1024 }
1025
1026 fn select_prev(
1027 &mut self,
1028 provider: Option<&dyn CompletionProvider>,
1029 cx: &mut ViewContext<Editor>,
1030 ) {
1031 if self.selected_item > 0 {
1032 self.selected_item -= 1;
1033 } else {
1034 self.selected_item = self.matches.len() - 1;
1035 }
1036 self.scroll_handle
1037 .scroll_to_item(self.selected_item, ScrollStrategy::Top);
1038 self.attempt_resolve_selected_completion_documentation(provider, cx);
1039 cx.notify();
1040 }
1041
1042 fn select_next(
1043 &mut self,
1044 provider: Option<&dyn CompletionProvider>,
1045 cx: &mut ViewContext<Editor>,
1046 ) {
1047 if self.selected_item + 1 < self.matches.len() {
1048 self.selected_item += 1;
1049 } else {
1050 self.selected_item = 0;
1051 }
1052 self.scroll_handle
1053 .scroll_to_item(self.selected_item, ScrollStrategy::Top);
1054 self.attempt_resolve_selected_completion_documentation(provider, cx);
1055 cx.notify();
1056 }
1057
1058 fn select_last(
1059 &mut self,
1060 provider: Option<&dyn CompletionProvider>,
1061 cx: &mut ViewContext<Editor>,
1062 ) {
1063 self.selected_item = self.matches.len() - 1;
1064 self.scroll_handle
1065 .scroll_to_item(self.selected_item, ScrollStrategy::Top);
1066 self.attempt_resolve_selected_completion_documentation(provider, cx);
1067 cx.notify();
1068 }
1069
1070 fn pre_resolve_completion_documentation(
1071 buffer: Model<Buffer>,
1072 completions: Arc<RwLock<Box<[Completion]>>>,
1073 matches: Arc<[StringMatch]>,
1074 editor: &Editor,
1075 cx: &mut ViewContext<Editor>,
1076 ) -> Task<()> {
1077 let settings = EditorSettings::get_global(cx);
1078 if !settings.show_completion_documentation {
1079 return Task::ready(());
1080 }
1081
1082 let Some(provider) = editor.completion_provider.as_ref() else {
1083 return Task::ready(());
1084 };
1085
1086 let resolve_task = provider.resolve_completions(
1087 buffer,
1088 matches.iter().map(|m| m.candidate_id).collect(),
1089 completions.clone(),
1090 cx,
1091 );
1092
1093 cx.spawn(move |this, mut cx| async move {
1094 if let Some(true) = resolve_task.await.log_err() {
1095 this.update(&mut cx, |_, cx| cx.notify()).ok();
1096 }
1097 })
1098 }
1099
1100 fn attempt_resolve_selected_completion_documentation(
1101 &mut self,
1102 provider: Option<&dyn CompletionProvider>,
1103 cx: &mut ViewContext<Editor>,
1104 ) {
1105 let settings = EditorSettings::get_global(cx);
1106 if !settings.show_completion_documentation {
1107 return;
1108 }
1109
1110 let completion_index = self.matches[self.selected_item].candidate_id;
1111 let Some(provider) = provider else {
1112 return;
1113 };
1114
1115 let resolve_task = provider.resolve_completions(
1116 self.buffer.clone(),
1117 vec![completion_index],
1118 self.completions.clone(),
1119 cx,
1120 );
1121
1122 let delay_ms =
1123 EditorSettings::get_global(cx).completion_documentation_secondary_query_debounce;
1124 let delay = Duration::from_millis(delay_ms);
1125
1126 self.selected_completion_documentation_resolve_debounce
1127 .lock()
1128 .fire_new(delay, cx, |_, cx| {
1129 cx.spawn(move |this, mut cx| async move {
1130 if let Some(true) = resolve_task.await.log_err() {
1131 this.update(&mut cx, |_, cx| cx.notify()).ok();
1132 }
1133 })
1134 });
1135 }
1136
1137 fn visible(&self) -> bool {
1138 !self.matches.is_empty()
1139 }
1140
1141 fn render(
1142 &self,
1143 style: &EditorStyle,
1144 max_height: Pixels,
1145 workspace: Option<WeakView<Workspace>>,
1146 cx: &mut ViewContext<Editor>,
1147 ) -> AnyElement {
1148 let settings = EditorSettings::get_global(cx);
1149 let show_completion_documentation = settings.show_completion_documentation;
1150
1151 let widest_completion_ix = self
1152 .matches
1153 .iter()
1154 .enumerate()
1155 .max_by_key(|(_, mat)| {
1156 let completions = self.completions.read();
1157 let completion = &completions[mat.candidate_id];
1158 let documentation = &completion.documentation;
1159
1160 let mut len = completion.label.text.chars().count();
1161 if let Some(Documentation::SingleLine(text)) = documentation {
1162 if show_completion_documentation {
1163 len += text.chars().count();
1164 }
1165 }
1166
1167 len
1168 })
1169 .map(|(ix, _)| ix);
1170
1171 let completions = self.completions.clone();
1172 let matches = self.matches.clone();
1173 let selected_item = self.selected_item;
1174 let style = style.clone();
1175
1176 let multiline_docs = if show_completion_documentation {
1177 let mat = &self.matches[selected_item];
1178 let multiline_docs = match &self.completions.read()[mat.candidate_id].documentation {
1179 Some(Documentation::MultiLinePlainText(text)) => {
1180 Some(div().child(SharedString::from(text.clone())))
1181 }
1182 Some(Documentation::MultiLineMarkdown(parsed)) if !parsed.text.is_empty() => {
1183 Some(div().child(render_parsed_markdown(
1184 "completions_markdown",
1185 parsed,
1186 &style,
1187 workspace,
1188 cx,
1189 )))
1190 }
1191 _ => None,
1192 };
1193 multiline_docs.map(|div| {
1194 div.id("multiline_docs")
1195 .max_h(max_height)
1196 .flex_1()
1197 .px_1p5()
1198 .py_1()
1199 .min_w(px(260.))
1200 .max_w(px(640.))
1201 .w(px(500.))
1202 .overflow_y_scroll()
1203 .occlude()
1204 })
1205 } else {
1206 None
1207 };
1208
1209 let list = uniform_list(
1210 cx.view().clone(),
1211 "completions",
1212 matches.len(),
1213 move |_editor, range, cx| {
1214 let start_ix = range.start;
1215 let completions_guard = completions.read();
1216
1217 matches[range]
1218 .iter()
1219 .enumerate()
1220 .map(|(ix, mat)| {
1221 let item_ix = start_ix + ix;
1222 let candidate_id = mat.candidate_id;
1223 let completion = &completions_guard[candidate_id];
1224
1225 let documentation = if show_completion_documentation {
1226 &completion.documentation
1227 } else {
1228 &None
1229 };
1230
1231 let highlights = gpui::combine_highlights(
1232 mat.ranges().map(|range| (range, FontWeight::BOLD.into())),
1233 styled_runs_for_code_label(&completion.label, &style.syntax).map(
1234 |(range, mut highlight)| {
1235 // Ignore font weight for syntax highlighting, as we'll use it
1236 // for fuzzy matches.
1237 highlight.font_weight = None;
1238
1239 if completion.lsp_completion.deprecated.unwrap_or(false) {
1240 highlight.strikethrough = Some(StrikethroughStyle {
1241 thickness: 1.0.into(),
1242 ..Default::default()
1243 });
1244 highlight.color = Some(cx.theme().colors().text_muted);
1245 }
1246
1247 (range, highlight)
1248 },
1249 ),
1250 );
1251 let completion_label = StyledText::new(completion.label.text.clone())
1252 .with_highlights(&style.text, highlights);
1253 let documentation_label =
1254 if let Some(Documentation::SingleLine(text)) = documentation {
1255 if text.trim().is_empty() {
1256 None
1257 } else {
1258 Some(
1259 Label::new(text.clone())
1260 .ml_4()
1261 .size(LabelSize::Small)
1262 .color(Color::Muted),
1263 )
1264 }
1265 } else {
1266 None
1267 };
1268
1269 let color_swatch = completion
1270 .color()
1271 .map(|color| div().size_4().bg(color).rounded_sm());
1272
1273 div().min_w(px(220.)).max_w(px(540.)).child(
1274 ListItem::new(mat.candidate_id)
1275 .inset(true)
1276 .selected(item_ix == selected_item)
1277 .on_click(cx.listener(move |editor, _event, cx| {
1278 cx.stop_propagation();
1279 if let Some(task) = editor.confirm_completion(
1280 &ConfirmCompletion {
1281 item_ix: Some(item_ix),
1282 },
1283 cx,
1284 ) {
1285 task.detach_and_log_err(cx)
1286 }
1287 }))
1288 .start_slot::<Div>(color_swatch)
1289 .child(h_flex().overflow_hidden().child(completion_label))
1290 .end_slot::<Label>(documentation_label),
1291 )
1292 })
1293 .collect()
1294 },
1295 )
1296 .occlude()
1297 .max_h(max_height)
1298 .track_scroll(self.scroll_handle.clone())
1299 .with_width_from_item(widest_completion_ix)
1300 .with_sizing_behavior(ListSizingBehavior::Infer);
1301
1302 Popover::new()
1303 .child(list)
1304 .when_some(multiline_docs, |popover, multiline_docs| {
1305 popover.aside(multiline_docs)
1306 })
1307 .into_any_element()
1308 }
1309
1310 pub async fn filter(&mut self, query: Option<&str>, executor: BackgroundExecutor) {
1311 let mut matches = if let Some(query) = query {
1312 fuzzy::match_strings(
1313 &self.match_candidates,
1314 query,
1315 query.chars().any(|c| c.is_uppercase()),
1316 100,
1317 &Default::default(),
1318 executor,
1319 )
1320 .await
1321 } else {
1322 self.match_candidates
1323 .iter()
1324 .enumerate()
1325 .map(|(candidate_id, candidate)| StringMatch {
1326 candidate_id,
1327 score: Default::default(),
1328 positions: Default::default(),
1329 string: candidate.string.clone(),
1330 })
1331 .collect()
1332 };
1333
1334 // Remove all candidates where the query's start does not match the start of any word in the candidate
1335 if let Some(query) = query {
1336 if let Some(query_start) = query.chars().next() {
1337 matches.retain(|string_match| {
1338 split_words(&string_match.string).any(|word| {
1339 // Check that the first codepoint of the word as lowercase matches the first
1340 // codepoint of the query as lowercase
1341 word.chars()
1342 .flat_map(|codepoint| codepoint.to_lowercase())
1343 .zip(query_start.to_lowercase())
1344 .all(|(word_cp, query_cp)| word_cp == query_cp)
1345 })
1346 });
1347 }
1348 }
1349
1350 let completions = self.completions.read();
1351 if self.sort_completions {
1352 matches.sort_unstable_by_key(|mat| {
1353 // We do want to strike a balance here between what the language server tells us
1354 // to sort by (the sort_text) and what are "obvious" good matches (i.e. when you type
1355 // `Creat` and there is a local variable called `CreateComponent`).
1356 // So what we do is: we bucket all matches into two buckets
1357 // - Strong matches
1358 // - Weak matches
1359 // Strong matches are the ones with a high fuzzy-matcher score (the "obvious" matches)
1360 // and the Weak matches are the rest.
1361 //
1362 // For the strong matches, we sort by our fuzzy-finder score first and for the weak
1363 // matches, we prefer language-server sort_text first.
1364 //
1365 // The thinking behind that: we want to show strong matches first in order of relevance(fuzzy score).
1366 // Rest of the matches(weak) can be sorted as language-server expects.
1367
1368 #[derive(PartialEq, Eq, PartialOrd, Ord)]
1369 enum MatchScore<'a> {
1370 Strong {
1371 score: Reverse<OrderedFloat<f64>>,
1372 sort_text: Option<&'a str>,
1373 sort_key: (usize, &'a str),
1374 },
1375 Weak {
1376 sort_text: Option<&'a str>,
1377 score: Reverse<OrderedFloat<f64>>,
1378 sort_key: (usize, &'a str),
1379 },
1380 }
1381
1382 let completion = &completions[mat.candidate_id];
1383 let sort_key = completion.sort_key();
1384 let sort_text = completion.lsp_completion.sort_text.as_deref();
1385 let score = Reverse(OrderedFloat(mat.score));
1386
1387 if mat.score >= 0.2 {
1388 MatchScore::Strong {
1389 score,
1390 sort_text,
1391 sort_key,
1392 }
1393 } else {
1394 MatchScore::Weak {
1395 sort_text,
1396 score,
1397 sort_key,
1398 }
1399 }
1400 });
1401 }
1402
1403 for mat in &mut matches {
1404 let completion = &completions[mat.candidate_id];
1405 mat.string.clone_from(&completion.label.text);
1406 for position in &mut mat.positions {
1407 *position += completion.label.filter_range.start;
1408 }
1409 }
1410 drop(completions);
1411
1412 self.matches = matches.into();
1413 self.selected_item = 0;
1414 }
1415}
1416
1417struct AvailableCodeAction {
1418 excerpt_id: ExcerptId,
1419 action: CodeAction,
1420 provider: Arc<dyn CodeActionProvider>,
1421}
1422
1423#[derive(Clone)]
1424struct CodeActionContents {
1425 tasks: Option<Arc<ResolvedTasks>>,
1426 actions: Option<Arc<[AvailableCodeAction]>>,
1427}
1428
1429impl CodeActionContents {
1430 fn len(&self) -> usize {
1431 match (&self.tasks, &self.actions) {
1432 (Some(tasks), Some(actions)) => actions.len() + tasks.templates.len(),
1433 (Some(tasks), None) => tasks.templates.len(),
1434 (None, Some(actions)) => actions.len(),
1435 (None, None) => 0,
1436 }
1437 }
1438
1439 fn is_empty(&self) -> bool {
1440 match (&self.tasks, &self.actions) {
1441 (Some(tasks), Some(actions)) => actions.is_empty() && tasks.templates.is_empty(),
1442 (Some(tasks), None) => tasks.templates.is_empty(),
1443 (None, Some(actions)) => actions.is_empty(),
1444 (None, None) => true,
1445 }
1446 }
1447
1448 fn iter(&self) -> impl Iterator<Item = CodeActionsItem> + '_ {
1449 self.tasks
1450 .iter()
1451 .flat_map(|tasks| {
1452 tasks
1453 .templates
1454 .iter()
1455 .map(|(kind, task)| CodeActionsItem::Task(kind.clone(), task.clone()))
1456 })
1457 .chain(self.actions.iter().flat_map(|actions| {
1458 actions.iter().map(|available| CodeActionsItem::CodeAction {
1459 excerpt_id: available.excerpt_id,
1460 action: available.action.clone(),
1461 provider: available.provider.clone(),
1462 })
1463 }))
1464 }
1465 fn get(&self, index: usize) -> Option<CodeActionsItem> {
1466 match (&self.tasks, &self.actions) {
1467 (Some(tasks), Some(actions)) => {
1468 if index < tasks.templates.len() {
1469 tasks
1470 .templates
1471 .get(index)
1472 .cloned()
1473 .map(|(kind, task)| CodeActionsItem::Task(kind, task))
1474 } else {
1475 actions.get(index - tasks.templates.len()).map(|available| {
1476 CodeActionsItem::CodeAction {
1477 excerpt_id: available.excerpt_id,
1478 action: available.action.clone(),
1479 provider: available.provider.clone(),
1480 }
1481 })
1482 }
1483 }
1484 (Some(tasks), None) => tasks
1485 .templates
1486 .get(index)
1487 .cloned()
1488 .map(|(kind, task)| CodeActionsItem::Task(kind, task)),
1489 (None, Some(actions)) => {
1490 actions
1491 .get(index)
1492 .map(|available| CodeActionsItem::CodeAction {
1493 excerpt_id: available.excerpt_id,
1494 action: available.action.clone(),
1495 provider: available.provider.clone(),
1496 })
1497 }
1498 (None, None) => None,
1499 }
1500 }
1501}
1502
1503#[allow(clippy::large_enum_variant)]
1504#[derive(Clone)]
1505enum CodeActionsItem {
1506 Task(TaskSourceKind, ResolvedTask),
1507 CodeAction {
1508 excerpt_id: ExcerptId,
1509 action: CodeAction,
1510 provider: Arc<dyn CodeActionProvider>,
1511 },
1512}
1513
1514impl CodeActionsItem {
1515 fn as_task(&self) -> Option<&ResolvedTask> {
1516 let Self::Task(_, task) = self else {
1517 return None;
1518 };
1519 Some(task)
1520 }
1521 fn as_code_action(&self) -> Option<&CodeAction> {
1522 let Self::CodeAction { action, .. } = self else {
1523 return None;
1524 };
1525 Some(action)
1526 }
1527 fn label(&self) -> String {
1528 match self {
1529 Self::CodeAction { action, .. } => action.lsp_action.title.clone(),
1530 Self::Task(_, task) => task.resolved_label.clone(),
1531 }
1532 }
1533}
1534
1535struct CodeActionsMenu {
1536 actions: CodeActionContents,
1537 buffer: Model<Buffer>,
1538 selected_item: usize,
1539 scroll_handle: UniformListScrollHandle,
1540 deployed_from_indicator: Option<DisplayRow>,
1541}
1542
1543impl CodeActionsMenu {
1544 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
1545 self.selected_item = 0;
1546 self.scroll_handle
1547 .scroll_to_item(self.selected_item, ScrollStrategy::Top);
1548 cx.notify()
1549 }
1550
1551 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
1552 if self.selected_item > 0 {
1553 self.selected_item -= 1;
1554 } else {
1555 self.selected_item = self.actions.len() - 1;
1556 }
1557 self.scroll_handle
1558 .scroll_to_item(self.selected_item, ScrollStrategy::Top);
1559 cx.notify();
1560 }
1561
1562 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
1563 if self.selected_item + 1 < self.actions.len() {
1564 self.selected_item += 1;
1565 } else {
1566 self.selected_item = 0;
1567 }
1568 self.scroll_handle
1569 .scroll_to_item(self.selected_item, ScrollStrategy::Top);
1570 cx.notify();
1571 }
1572
1573 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
1574 self.selected_item = self.actions.len() - 1;
1575 self.scroll_handle
1576 .scroll_to_item(self.selected_item, ScrollStrategy::Top);
1577 cx.notify()
1578 }
1579
1580 fn visible(&self) -> bool {
1581 !self.actions.is_empty()
1582 }
1583
1584 fn render(
1585 &self,
1586 cursor_position: DisplayPoint,
1587 _style: &EditorStyle,
1588 max_height: Pixels,
1589 cx: &mut ViewContext<Editor>,
1590 ) -> (ContextMenuOrigin, AnyElement) {
1591 let actions = self.actions.clone();
1592 let selected_item = self.selected_item;
1593 let element = uniform_list(
1594 cx.view().clone(),
1595 "code_actions_menu",
1596 self.actions.len(),
1597 move |_this, range, cx| {
1598 actions
1599 .iter()
1600 .skip(range.start)
1601 .take(range.end - range.start)
1602 .enumerate()
1603 .map(|(ix, action)| {
1604 let item_ix = range.start + ix;
1605 let selected = selected_item == item_ix;
1606 let colors = cx.theme().colors();
1607 div()
1608 .px_1()
1609 .rounded_md()
1610 .text_color(colors.text)
1611 .when(selected, |style| {
1612 style
1613 .bg(colors.element_active)
1614 .text_color(colors.text_accent)
1615 })
1616 .hover(|style| {
1617 style
1618 .bg(colors.element_hover)
1619 .text_color(colors.text_accent)
1620 })
1621 .whitespace_nowrap()
1622 .when_some(action.as_code_action(), |this, action| {
1623 this.on_mouse_down(
1624 MouseButton::Left,
1625 cx.listener(move |editor, _, cx| {
1626 cx.stop_propagation();
1627 if let Some(task) = editor.confirm_code_action(
1628 &ConfirmCodeAction {
1629 item_ix: Some(item_ix),
1630 },
1631 cx,
1632 ) {
1633 task.detach_and_log_err(cx)
1634 }
1635 }),
1636 )
1637 // TASK: It would be good to make lsp_action.title a SharedString to avoid allocating here.
1638 .child(SharedString::from(action.lsp_action.title.clone()))
1639 })
1640 .when_some(action.as_task(), |this, task| {
1641 this.on_mouse_down(
1642 MouseButton::Left,
1643 cx.listener(move |editor, _, cx| {
1644 cx.stop_propagation();
1645 if let Some(task) = editor.confirm_code_action(
1646 &ConfirmCodeAction {
1647 item_ix: Some(item_ix),
1648 },
1649 cx,
1650 ) {
1651 task.detach_and_log_err(cx)
1652 }
1653 }),
1654 )
1655 .child(SharedString::from(task.resolved_label.clone()))
1656 })
1657 })
1658 .collect()
1659 },
1660 )
1661 .elevation_1(cx)
1662 .p_1()
1663 .max_h(max_height)
1664 .occlude()
1665 .track_scroll(self.scroll_handle.clone())
1666 .with_width_from_item(
1667 self.actions
1668 .iter()
1669 .enumerate()
1670 .max_by_key(|(_, action)| match action {
1671 CodeActionsItem::Task(_, task) => task.resolved_label.chars().count(),
1672 CodeActionsItem::CodeAction { action, .. } => {
1673 action.lsp_action.title.chars().count()
1674 }
1675 })
1676 .map(|(ix, _)| ix),
1677 )
1678 .with_sizing_behavior(ListSizingBehavior::Infer)
1679 .into_any_element();
1680
1681 let cursor_position = if let Some(row) = self.deployed_from_indicator {
1682 ContextMenuOrigin::GutterIndicator(row)
1683 } else {
1684 ContextMenuOrigin::EditorPoint(cursor_position)
1685 };
1686
1687 (cursor_position, element)
1688 }
1689}
1690
1691#[derive(Debug)]
1692struct ActiveDiagnosticGroup {
1693 primary_range: Range<Anchor>,
1694 primary_message: String,
1695 group_id: usize,
1696 blocks: HashMap<CustomBlockId, Diagnostic>,
1697 is_valid: bool,
1698}
1699
1700#[derive(Serialize, Deserialize, Clone, Debug)]
1701pub struct ClipboardSelection {
1702 pub len: usize,
1703 pub is_entire_line: bool,
1704 pub first_line_indent: u32,
1705}
1706
1707#[derive(Debug)]
1708pub(crate) struct NavigationData {
1709 cursor_anchor: Anchor,
1710 cursor_position: Point,
1711 scroll_anchor: ScrollAnchor,
1712 scroll_top_row: u32,
1713}
1714
1715#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1716pub enum GotoDefinitionKind {
1717 Symbol,
1718 Declaration,
1719 Type,
1720 Implementation,
1721}
1722
1723#[derive(Debug, Clone)]
1724enum InlayHintRefreshReason {
1725 Toggle(bool),
1726 SettingsChange(InlayHintSettings),
1727 NewLinesShown,
1728 BufferEdited(HashSet<Arc<Language>>),
1729 RefreshRequested,
1730 ExcerptsRemoved(Vec<ExcerptId>),
1731}
1732
1733impl InlayHintRefreshReason {
1734 fn description(&self) -> &'static str {
1735 match self {
1736 Self::Toggle(_) => "toggle",
1737 Self::SettingsChange(_) => "settings change",
1738 Self::NewLinesShown => "new lines shown",
1739 Self::BufferEdited(_) => "buffer edited",
1740 Self::RefreshRequested => "refresh requested",
1741 Self::ExcerptsRemoved(_) => "excerpts removed",
1742 }
1743 }
1744}
1745
1746pub(crate) struct FocusedBlock {
1747 id: BlockId,
1748 focus_handle: WeakFocusHandle,
1749}
1750
1751impl Editor {
1752 pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
1753 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1754 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1755 Self::new(
1756 EditorMode::SingleLine { auto_width: false },
1757 buffer,
1758 None,
1759 false,
1760 cx,
1761 )
1762 }
1763
1764 pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
1765 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1766 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1767 Self::new(EditorMode::Full, buffer, None, false, cx)
1768 }
1769
1770 pub fn auto_width(cx: &mut ViewContext<Self>) -> Self {
1771 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1772 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1773 Self::new(
1774 EditorMode::SingleLine { auto_width: true },
1775 buffer,
1776 None,
1777 false,
1778 cx,
1779 )
1780 }
1781
1782 pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
1783 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1784 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1785 Self::new(
1786 EditorMode::AutoHeight { max_lines },
1787 buffer,
1788 None,
1789 false,
1790 cx,
1791 )
1792 }
1793
1794 pub fn for_buffer(
1795 buffer: Model<Buffer>,
1796 project: Option<Model<Project>>,
1797 cx: &mut ViewContext<Self>,
1798 ) -> Self {
1799 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1800 Self::new(EditorMode::Full, buffer, project, false, cx)
1801 }
1802
1803 pub fn for_multibuffer(
1804 buffer: Model<MultiBuffer>,
1805 project: Option<Model<Project>>,
1806 show_excerpt_controls: bool,
1807 cx: &mut ViewContext<Self>,
1808 ) -> Self {
1809 Self::new(EditorMode::Full, buffer, project, show_excerpt_controls, cx)
1810 }
1811
1812 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1813 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1814 let mut clone = Self::new(
1815 self.mode,
1816 self.buffer.clone(),
1817 self.project.clone(),
1818 show_excerpt_controls,
1819 cx,
1820 );
1821 self.display_map.update(cx, |display_map, cx| {
1822 let snapshot = display_map.snapshot(cx);
1823 clone.display_map.update(cx, |display_map, cx| {
1824 display_map.set_state(&snapshot, cx);
1825 });
1826 });
1827 clone.selections.clone_state(&self.selections);
1828 clone.scroll_manager.clone_state(&self.scroll_manager);
1829 clone.searchable = self.searchable;
1830 clone
1831 }
1832
1833 pub fn new(
1834 mode: EditorMode,
1835 buffer: Model<MultiBuffer>,
1836 project: Option<Model<Project>>,
1837 show_excerpt_controls: bool,
1838 cx: &mut ViewContext<Self>,
1839 ) -> Self {
1840 let style = cx.text_style();
1841 let font_size = style.font_size.to_pixels(cx.rem_size());
1842 let editor = cx.view().downgrade();
1843 let fold_placeholder = FoldPlaceholder {
1844 constrain_width: true,
1845 render: Arc::new(move |fold_id, fold_range, cx| {
1846 let editor = editor.clone();
1847 div()
1848 .id(fold_id)
1849 .bg(cx.theme().colors().ghost_element_background)
1850 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1851 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1852 .rounded_sm()
1853 .size_full()
1854 .cursor_pointer()
1855 .child("⋯")
1856 .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
1857 .on_click(move |_, cx| {
1858 editor
1859 .update(cx, |editor, cx| {
1860 editor.unfold_ranges(
1861 &[fold_range.start..fold_range.end],
1862 true,
1863 false,
1864 cx,
1865 );
1866 cx.stop_propagation();
1867 })
1868 .ok();
1869 })
1870 .into_any()
1871 }),
1872 merge_adjacent: true,
1873 ..Default::default()
1874 };
1875 let display_map = cx.new_model(|cx| {
1876 DisplayMap::new(
1877 buffer.clone(),
1878 style.font(),
1879 font_size,
1880 None,
1881 show_excerpt_controls,
1882 FILE_HEADER_HEIGHT,
1883 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1884 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1885 fold_placeholder,
1886 cx,
1887 )
1888 });
1889
1890 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1891
1892 let blink_manager = cx.new_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1893
1894 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1895 .then(|| language_settings::SoftWrap::None);
1896
1897 let mut project_subscriptions = Vec::new();
1898 if mode == EditorMode::Full {
1899 if let Some(project) = project.as_ref() {
1900 if buffer.read(cx).is_singleton() {
1901 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1902 cx.emit(EditorEvent::TitleChanged);
1903 }));
1904 }
1905 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1906 if let project::Event::RefreshInlayHints = event {
1907 editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1908 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1909 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1910 let focus_handle = editor.focus_handle(cx);
1911 if focus_handle.is_focused(cx) {
1912 let snapshot = buffer.read(cx).snapshot();
1913 for (range, snippet) in snippet_edits {
1914 let editor_range =
1915 language::range_from_lsp(*range).to_offset(&snapshot);
1916 editor
1917 .insert_snippet(&[editor_range], snippet.clone(), cx)
1918 .ok();
1919 }
1920 }
1921 }
1922 }
1923 }));
1924 if let Some(task_inventory) = project
1925 .read(cx)
1926 .task_store()
1927 .read(cx)
1928 .task_inventory()
1929 .cloned()
1930 {
1931 project_subscriptions.push(cx.observe(&task_inventory, |editor, _, cx| {
1932 editor.tasks_update_task = Some(editor.refresh_runnables(cx));
1933 }));
1934 }
1935 }
1936 }
1937
1938 let inlay_hint_settings = inlay_hint_settings(
1939 selections.newest_anchor().head(),
1940 &buffer.read(cx).snapshot(cx),
1941 cx,
1942 );
1943 let focus_handle = cx.focus_handle();
1944 cx.on_focus(&focus_handle, Self::handle_focus).detach();
1945 cx.on_focus_in(&focus_handle, Self::handle_focus_in)
1946 .detach();
1947 cx.on_focus_out(&focus_handle, Self::handle_focus_out)
1948 .detach();
1949 cx.on_blur(&focus_handle, Self::handle_blur).detach();
1950
1951 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1952 Some(false)
1953 } else {
1954 None
1955 };
1956
1957 let mut code_action_providers = Vec::new();
1958 if let Some(project) = project.clone() {
1959 code_action_providers.push(Arc::new(project) as Arc<_>);
1960 }
1961
1962 let mut this = Self {
1963 focus_handle,
1964 show_cursor_when_unfocused: false,
1965 last_focused_descendant: None,
1966 buffer: buffer.clone(),
1967 display_map: display_map.clone(),
1968 selections,
1969 scroll_manager: ScrollManager::new(cx),
1970 columnar_selection_tail: None,
1971 add_selections_state: None,
1972 select_next_state: None,
1973 select_prev_state: None,
1974 selection_history: Default::default(),
1975 autoclose_regions: Default::default(),
1976 snippet_stack: Default::default(),
1977 select_larger_syntax_node_stack: Vec::new(),
1978 ime_transaction: Default::default(),
1979 active_diagnostics: None,
1980 soft_wrap_mode_override,
1981 completion_provider: project.clone().map(|project| Box::new(project) as _),
1982 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
1983 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1984 project,
1985 blink_manager: blink_manager.clone(),
1986 show_local_selections: true,
1987 mode,
1988 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1989 show_gutter: mode == EditorMode::Full,
1990 show_line_numbers: None,
1991 use_relative_line_numbers: None,
1992 show_git_diff_gutter: None,
1993 show_code_actions: None,
1994 show_runnables: None,
1995 show_wrap_guides: None,
1996 show_indent_guides,
1997 placeholder_text: None,
1998 highlight_order: 0,
1999 highlighted_rows: HashMap::default(),
2000 background_highlights: Default::default(),
2001 gutter_highlights: TreeMap::default(),
2002 scrollbar_marker_state: ScrollbarMarkerState::default(),
2003 active_indent_guides_state: ActiveIndentGuidesState::default(),
2004 nav_history: None,
2005 context_menu: RwLock::new(None),
2006 mouse_context_menu: None,
2007 hunk_controls_menu_handle: PopoverMenuHandle::default(),
2008 completion_tasks: Default::default(),
2009 signature_help_state: SignatureHelpState::default(),
2010 auto_signature_help: None,
2011 find_all_references_task_sources: Vec::new(),
2012 next_completion_id: 0,
2013 completion_documentation_pre_resolve_debounce: DebouncedDelay::new(),
2014 next_inlay_id: 0,
2015 code_action_providers,
2016 available_code_actions: Default::default(),
2017 code_actions_task: Default::default(),
2018 document_highlights_task: Default::default(),
2019 linked_editing_range_task: Default::default(),
2020 pending_rename: Default::default(),
2021 searchable: true,
2022 cursor_shape: EditorSettings::get_global(cx)
2023 .cursor_shape
2024 .unwrap_or_default(),
2025 current_line_highlight: None,
2026 autoindent_mode: Some(AutoindentMode::EachLine),
2027 collapse_matches: false,
2028 workspace: None,
2029 input_enabled: true,
2030 use_modal_editing: mode == EditorMode::Full,
2031 read_only: false,
2032 use_autoclose: true,
2033 use_auto_surround: true,
2034 auto_replace_emoji_shortcode: false,
2035 leader_peer_id: None,
2036 remote_id: None,
2037 hover_state: Default::default(),
2038 hovered_link_state: Default::default(),
2039 inline_completion_provider: None,
2040 active_inline_completion: None,
2041 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
2042 expanded_hunks: ExpandedHunks::default(),
2043 gutter_hovered: false,
2044 pixel_position_of_newest_cursor: None,
2045 last_bounds: None,
2046 expect_bounds_change: None,
2047 gutter_dimensions: GutterDimensions::default(),
2048 style: None,
2049 show_cursor_names: false,
2050 hovered_cursors: Default::default(),
2051 next_editor_action_id: EditorActionId::default(),
2052 editor_actions: Rc::default(),
2053 show_inline_completions_override: None,
2054 enable_inline_completions: true,
2055 custom_context_menu: None,
2056 show_git_blame_gutter: false,
2057 show_git_blame_inline: false,
2058 show_selection_menu: None,
2059 show_git_blame_inline_delay_task: None,
2060 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
2061 serialize_dirty_buffers: ProjectSettings::get_global(cx)
2062 .session
2063 .restore_unsaved_buffers,
2064 blame: None,
2065 blame_subscription: None,
2066 tasks: Default::default(),
2067 _subscriptions: vec![
2068 cx.observe(&buffer, Self::on_buffer_changed),
2069 cx.subscribe(&buffer, Self::on_buffer_event),
2070 cx.observe(&display_map, Self::on_display_map_changed),
2071 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
2072 cx.observe_global::<SettingsStore>(Self::settings_changed),
2073 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
2074 cx.observe_window_activation(|editor, cx| {
2075 let active = cx.is_window_active();
2076 editor.blink_manager.update(cx, |blink_manager, cx| {
2077 if active {
2078 blink_manager.enable(cx);
2079 } else {
2080 blink_manager.disable(cx);
2081 }
2082 });
2083 }),
2084 ],
2085 tasks_update_task: None,
2086 linked_edit_ranges: Default::default(),
2087 previous_search_ranges: None,
2088 breadcrumb_header: None,
2089 focused_block: None,
2090 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
2091 addons: HashMap::default(),
2092 _scroll_cursor_center_top_bottom_task: Task::ready(()),
2093 text_style_refinement: None,
2094 };
2095 this.tasks_update_task = Some(this.refresh_runnables(cx));
2096 this._subscriptions.extend(project_subscriptions);
2097
2098 this.end_selection(cx);
2099 this.scroll_manager.show_scrollbar(cx);
2100
2101 if mode == EditorMode::Full {
2102 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
2103 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
2104
2105 if this.git_blame_inline_enabled {
2106 this.git_blame_inline_enabled = true;
2107 this.start_git_blame_inline(false, cx);
2108 }
2109 }
2110
2111 this.report_editor_event("open", None, cx);
2112 this
2113 }
2114
2115 pub fn mouse_menu_is_focused(&self, cx: &WindowContext) -> bool {
2116 self.mouse_context_menu
2117 .as_ref()
2118 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(cx))
2119 }
2120
2121 fn key_context(&self, cx: &ViewContext<Self>) -> KeyContext {
2122 let mut key_context = KeyContext::new_with_defaults();
2123 key_context.add("Editor");
2124 let mode = match self.mode {
2125 EditorMode::SingleLine { .. } => "single_line",
2126 EditorMode::AutoHeight { .. } => "auto_height",
2127 EditorMode::Full => "full",
2128 };
2129
2130 if EditorSettings::jupyter_enabled(cx) {
2131 key_context.add("jupyter");
2132 }
2133
2134 key_context.set("mode", mode);
2135 if self.pending_rename.is_some() {
2136 key_context.add("renaming");
2137 }
2138 if self.context_menu_visible() {
2139 match self.context_menu.read().as_ref() {
2140 Some(ContextMenu::Completions(_)) => {
2141 key_context.add("menu");
2142 key_context.add("showing_completions")
2143 }
2144 Some(ContextMenu::CodeActions(_)) => {
2145 key_context.add("menu");
2146 key_context.add("showing_code_actions")
2147 }
2148 None => {}
2149 }
2150 }
2151
2152 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
2153 if !self.focus_handle(cx).contains_focused(cx)
2154 || (self.is_focused(cx) || self.mouse_menu_is_focused(cx))
2155 {
2156 for addon in self.addons.values() {
2157 addon.extend_key_context(&mut key_context, cx)
2158 }
2159 }
2160
2161 if let Some(extension) = self
2162 .buffer
2163 .read(cx)
2164 .as_singleton()
2165 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
2166 {
2167 key_context.set("extension", extension.to_string());
2168 }
2169
2170 if self.has_active_inline_completion(cx) {
2171 key_context.add("copilot_suggestion");
2172 key_context.add("inline_completion");
2173 }
2174
2175 key_context
2176 }
2177
2178 pub fn new_file(
2179 workspace: &mut Workspace,
2180 _: &workspace::NewFile,
2181 cx: &mut ViewContext<Workspace>,
2182 ) {
2183 Self::new_in_workspace(workspace, cx).detach_and_prompt_err(
2184 "Failed to create buffer",
2185 cx,
2186 |e, _| match e.error_code() {
2187 ErrorCode::RemoteUpgradeRequired => Some(format!(
2188 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2189 e.error_tag("required").unwrap_or("the latest version")
2190 )),
2191 _ => None,
2192 },
2193 );
2194 }
2195
2196 pub fn new_in_workspace(
2197 workspace: &mut Workspace,
2198 cx: &mut ViewContext<Workspace>,
2199 ) -> Task<Result<View<Editor>>> {
2200 let project = workspace.project().clone();
2201 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2202
2203 cx.spawn(|workspace, mut cx| async move {
2204 let buffer = create.await?;
2205 workspace.update(&mut cx, |workspace, cx| {
2206 let editor =
2207 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx));
2208 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
2209 editor
2210 })
2211 })
2212 }
2213
2214 fn new_file_vertical(
2215 workspace: &mut Workspace,
2216 _: &workspace::NewFileSplitVertical,
2217 cx: &mut ViewContext<Workspace>,
2218 ) {
2219 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), cx)
2220 }
2221
2222 fn new_file_horizontal(
2223 workspace: &mut Workspace,
2224 _: &workspace::NewFileSplitHorizontal,
2225 cx: &mut ViewContext<Workspace>,
2226 ) {
2227 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), cx)
2228 }
2229
2230 fn new_file_in_direction(
2231 workspace: &mut Workspace,
2232 direction: SplitDirection,
2233 cx: &mut ViewContext<Workspace>,
2234 ) {
2235 let project = workspace.project().clone();
2236 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2237
2238 cx.spawn(|workspace, mut cx| async move {
2239 let buffer = create.await?;
2240 workspace.update(&mut cx, move |workspace, cx| {
2241 workspace.split_item(
2242 direction,
2243 Box::new(
2244 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)),
2245 ),
2246 cx,
2247 )
2248 })?;
2249 anyhow::Ok(())
2250 })
2251 .detach_and_prompt_err("Failed to create buffer", cx, |e, _| match e.error_code() {
2252 ErrorCode::RemoteUpgradeRequired => Some(format!(
2253 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2254 e.error_tag("required").unwrap_or("the latest version")
2255 )),
2256 _ => None,
2257 });
2258 }
2259
2260 pub fn leader_peer_id(&self) -> Option<PeerId> {
2261 self.leader_peer_id
2262 }
2263
2264 pub fn buffer(&self) -> &Model<MultiBuffer> {
2265 &self.buffer
2266 }
2267
2268 pub fn workspace(&self) -> Option<View<Workspace>> {
2269 self.workspace.as_ref()?.0.upgrade()
2270 }
2271
2272 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
2273 self.buffer().read(cx).title(cx)
2274 }
2275
2276 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
2277 let git_blame_gutter_max_author_length = self
2278 .render_git_blame_gutter(cx)
2279 .then(|| {
2280 if let Some(blame) = self.blame.as_ref() {
2281 let max_author_length =
2282 blame.update(cx, |blame, cx| blame.max_author_length(cx));
2283 Some(max_author_length)
2284 } else {
2285 None
2286 }
2287 })
2288 .flatten();
2289
2290 EditorSnapshot {
2291 mode: self.mode,
2292 show_gutter: self.show_gutter,
2293 show_line_numbers: self.show_line_numbers,
2294 show_git_diff_gutter: self.show_git_diff_gutter,
2295 show_code_actions: self.show_code_actions,
2296 show_runnables: self.show_runnables,
2297 git_blame_gutter_max_author_length,
2298 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
2299 scroll_anchor: self.scroll_manager.anchor(),
2300 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
2301 placeholder_text: self.placeholder_text.clone(),
2302 is_focused: self.focus_handle.is_focused(cx),
2303 current_line_highlight: self
2304 .current_line_highlight
2305 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
2306 gutter_hovered: self.gutter_hovered,
2307 }
2308 }
2309
2310 pub fn language_at<T: ToOffset>(&self, point: T, cx: &AppContext) -> Option<Arc<Language>> {
2311 self.buffer.read(cx).language_at(point, cx)
2312 }
2313
2314 pub fn file_at<T: ToOffset>(
2315 &self,
2316 point: T,
2317 cx: &AppContext,
2318 ) -> Option<Arc<dyn language::File>> {
2319 self.buffer.read(cx).read(cx).file_at(point).cloned()
2320 }
2321
2322 pub fn active_excerpt(
2323 &self,
2324 cx: &AppContext,
2325 ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
2326 self.buffer
2327 .read(cx)
2328 .excerpt_containing(self.selections.newest_anchor().head(), cx)
2329 }
2330
2331 pub fn mode(&self) -> EditorMode {
2332 self.mode
2333 }
2334
2335 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
2336 self.collaboration_hub.as_deref()
2337 }
2338
2339 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
2340 self.collaboration_hub = Some(hub);
2341 }
2342
2343 pub fn set_custom_context_menu(
2344 &mut self,
2345 f: impl 'static
2346 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
2347 ) {
2348 self.custom_context_menu = Some(Box::new(f))
2349 }
2350
2351 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
2352 self.completion_provider = provider;
2353 }
2354
2355 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
2356 self.semantics_provider.clone()
2357 }
2358
2359 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
2360 self.semantics_provider = provider;
2361 }
2362
2363 pub fn set_inline_completion_provider<T>(
2364 &mut self,
2365 provider: Option<Model<T>>,
2366 cx: &mut ViewContext<Self>,
2367 ) where
2368 T: InlineCompletionProvider,
2369 {
2370 self.inline_completion_provider =
2371 provider.map(|provider| RegisteredInlineCompletionProvider {
2372 _subscription: cx.observe(&provider, |this, _, cx| {
2373 if this.focus_handle.is_focused(cx) {
2374 this.update_visible_inline_completion(cx);
2375 }
2376 }),
2377 provider: Arc::new(provider),
2378 });
2379 self.refresh_inline_completion(false, false, cx);
2380 }
2381
2382 pub fn placeholder_text(&self, _cx: &WindowContext) -> Option<&str> {
2383 self.placeholder_text.as_deref()
2384 }
2385
2386 pub fn set_placeholder_text(
2387 &mut self,
2388 placeholder_text: impl Into<Arc<str>>,
2389 cx: &mut ViewContext<Self>,
2390 ) {
2391 let placeholder_text = Some(placeholder_text.into());
2392 if self.placeholder_text != placeholder_text {
2393 self.placeholder_text = placeholder_text;
2394 cx.notify();
2395 }
2396 }
2397
2398 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
2399 self.cursor_shape = cursor_shape;
2400
2401 // Disrupt blink for immediate user feedback that the cursor shape has changed
2402 self.blink_manager.update(cx, BlinkManager::show_cursor);
2403
2404 cx.notify();
2405 }
2406
2407 pub fn set_current_line_highlight(
2408 &mut self,
2409 current_line_highlight: Option<CurrentLineHighlight>,
2410 ) {
2411 self.current_line_highlight = current_line_highlight;
2412 }
2413
2414 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
2415 self.collapse_matches = collapse_matches;
2416 }
2417
2418 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
2419 if self.collapse_matches {
2420 return range.start..range.start;
2421 }
2422 range.clone()
2423 }
2424
2425 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
2426 if self.display_map.read(cx).clip_at_line_ends != clip {
2427 self.display_map
2428 .update(cx, |map, _| map.clip_at_line_ends = clip);
2429 }
2430 }
2431
2432 pub fn set_input_enabled(&mut self, input_enabled: bool) {
2433 self.input_enabled = input_enabled;
2434 }
2435
2436 pub fn set_inline_completions_enabled(&mut self, enabled: bool) {
2437 self.enable_inline_completions = enabled;
2438 }
2439
2440 pub fn set_autoindent(&mut self, autoindent: bool) {
2441 if autoindent {
2442 self.autoindent_mode = Some(AutoindentMode::EachLine);
2443 } else {
2444 self.autoindent_mode = None;
2445 }
2446 }
2447
2448 pub fn read_only(&self, cx: &AppContext) -> bool {
2449 self.read_only || self.buffer.read(cx).read_only()
2450 }
2451
2452 pub fn set_read_only(&mut self, read_only: bool) {
2453 self.read_only = read_only;
2454 }
2455
2456 pub fn set_use_autoclose(&mut self, autoclose: bool) {
2457 self.use_autoclose = autoclose;
2458 }
2459
2460 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
2461 self.use_auto_surround = auto_surround;
2462 }
2463
2464 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
2465 self.auto_replace_emoji_shortcode = auto_replace;
2466 }
2467
2468 pub fn toggle_inline_completions(
2469 &mut self,
2470 _: &ToggleInlineCompletions,
2471 cx: &mut ViewContext<Self>,
2472 ) {
2473 if self.show_inline_completions_override.is_some() {
2474 self.set_show_inline_completions(None, cx);
2475 } else {
2476 let cursor = self.selections.newest_anchor().head();
2477 if let Some((buffer, cursor_buffer_position)) =
2478 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
2479 {
2480 let show_inline_completions =
2481 !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx);
2482 self.set_show_inline_completions(Some(show_inline_completions), cx);
2483 }
2484 }
2485 }
2486
2487 pub fn set_show_inline_completions(
2488 &mut self,
2489 show_inline_completions: Option<bool>,
2490 cx: &mut ViewContext<Self>,
2491 ) {
2492 self.show_inline_completions_override = show_inline_completions;
2493 self.refresh_inline_completion(false, true, cx);
2494 }
2495
2496 fn should_show_inline_completions(
2497 &self,
2498 buffer: &Model<Buffer>,
2499 buffer_position: language::Anchor,
2500 cx: &AppContext,
2501 ) -> bool {
2502 if !self.snippet_stack.is_empty() {
2503 return false;
2504 }
2505
2506 if let Some(provider) = self.inline_completion_provider() {
2507 if let Some(show_inline_completions) = self.show_inline_completions_override {
2508 show_inline_completions
2509 } else {
2510 self.mode == EditorMode::Full && provider.is_enabled(buffer, buffer_position, cx)
2511 }
2512 } else {
2513 false
2514 }
2515 }
2516
2517 pub fn set_use_modal_editing(&mut self, to: bool) {
2518 self.use_modal_editing = to;
2519 }
2520
2521 pub fn use_modal_editing(&self) -> bool {
2522 self.use_modal_editing
2523 }
2524
2525 fn selections_did_change(
2526 &mut self,
2527 local: bool,
2528 old_cursor_position: &Anchor,
2529 show_completions: bool,
2530 cx: &mut ViewContext<Self>,
2531 ) {
2532 cx.invalidate_character_coordinates();
2533
2534 // Copy selections to primary selection buffer
2535 #[cfg(target_os = "linux")]
2536 if local {
2537 let selections = self.selections.all::<usize>(cx);
2538 let buffer_handle = self.buffer.read(cx).read(cx);
2539
2540 let mut text = String::new();
2541 for (index, selection) in selections.iter().enumerate() {
2542 let text_for_selection = buffer_handle
2543 .text_for_range(selection.start..selection.end)
2544 .collect::<String>();
2545
2546 text.push_str(&text_for_selection);
2547 if index != selections.len() - 1 {
2548 text.push('\n');
2549 }
2550 }
2551
2552 if !text.is_empty() {
2553 cx.write_to_primary(ClipboardItem::new_string(text));
2554 }
2555 }
2556
2557 if self.focus_handle.is_focused(cx) && self.leader_peer_id.is_none() {
2558 self.buffer.update(cx, |buffer, cx| {
2559 buffer.set_active_selections(
2560 &self.selections.disjoint_anchors(),
2561 self.selections.line_mode,
2562 self.cursor_shape,
2563 cx,
2564 )
2565 });
2566 }
2567 let display_map = self
2568 .display_map
2569 .update(cx, |display_map, cx| display_map.snapshot(cx));
2570 let buffer = &display_map.buffer_snapshot;
2571 self.add_selections_state = None;
2572 self.select_next_state = None;
2573 self.select_prev_state = None;
2574 self.select_larger_syntax_node_stack.clear();
2575 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2576 self.snippet_stack
2577 .invalidate(&self.selections.disjoint_anchors(), buffer);
2578 self.take_rename(false, cx);
2579
2580 let new_cursor_position = self.selections.newest_anchor().head();
2581
2582 self.push_to_nav_history(
2583 *old_cursor_position,
2584 Some(new_cursor_position.to_point(buffer)),
2585 cx,
2586 );
2587
2588 if local {
2589 let new_cursor_position = self.selections.newest_anchor().head();
2590 let mut context_menu = self.context_menu.write();
2591 let completion_menu = match context_menu.as_ref() {
2592 Some(ContextMenu::Completions(menu)) => Some(menu),
2593
2594 _ => {
2595 *context_menu = None;
2596 None
2597 }
2598 };
2599
2600 if let Some(completion_menu) = completion_menu {
2601 let cursor_position = new_cursor_position.to_offset(buffer);
2602 let (word_range, kind) =
2603 buffer.surrounding_word(completion_menu.initial_position, true);
2604 if kind == Some(CharKind::Word)
2605 && word_range.to_inclusive().contains(&cursor_position)
2606 {
2607 let mut completion_menu = completion_menu.clone();
2608 drop(context_menu);
2609
2610 let query = Self::completion_query(buffer, cursor_position);
2611 cx.spawn(move |this, mut cx| async move {
2612 completion_menu
2613 .filter(query.as_deref(), cx.background_executor().clone())
2614 .await;
2615
2616 this.update(&mut cx, |this, cx| {
2617 let mut context_menu = this.context_menu.write();
2618 let Some(ContextMenu::Completions(menu)) = context_menu.as_ref() else {
2619 return;
2620 };
2621
2622 if menu.id > completion_menu.id {
2623 return;
2624 }
2625
2626 *context_menu = Some(ContextMenu::Completions(completion_menu));
2627 drop(context_menu);
2628 cx.notify();
2629 })
2630 })
2631 .detach();
2632
2633 if show_completions {
2634 self.show_completions(&ShowCompletions { trigger: None }, cx);
2635 }
2636 } else {
2637 drop(context_menu);
2638 self.hide_context_menu(cx);
2639 }
2640 } else {
2641 drop(context_menu);
2642 }
2643
2644 hide_hover(self, cx);
2645
2646 if old_cursor_position.to_display_point(&display_map).row()
2647 != new_cursor_position.to_display_point(&display_map).row()
2648 {
2649 self.available_code_actions.take();
2650 }
2651 self.refresh_code_actions(cx);
2652 self.refresh_document_highlights(cx);
2653 refresh_matching_bracket_highlights(self, cx);
2654 self.discard_inline_completion(false, cx);
2655 linked_editing_ranges::refresh_linked_ranges(self, cx);
2656 if self.git_blame_inline_enabled {
2657 self.start_inline_blame_timer(cx);
2658 }
2659 }
2660
2661 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2662 cx.emit(EditorEvent::SelectionsChanged { local });
2663
2664 if self.selections.disjoint_anchors().len() == 1 {
2665 cx.emit(SearchEvent::ActiveMatchChanged)
2666 }
2667 cx.notify();
2668 }
2669
2670 pub fn change_selections<R>(
2671 &mut self,
2672 autoscroll: Option<Autoscroll>,
2673 cx: &mut ViewContext<Self>,
2674 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2675 ) -> R {
2676 self.change_selections_inner(autoscroll, true, cx, change)
2677 }
2678
2679 pub fn change_selections_inner<R>(
2680 &mut self,
2681 autoscroll: Option<Autoscroll>,
2682 request_completions: bool,
2683 cx: &mut ViewContext<Self>,
2684 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2685 ) -> R {
2686 let old_cursor_position = self.selections.newest_anchor().head();
2687 self.push_to_selection_history();
2688
2689 let (changed, result) = self.selections.change_with(cx, change);
2690
2691 if changed {
2692 if let Some(autoscroll) = autoscroll {
2693 self.request_autoscroll(autoscroll, cx);
2694 }
2695 self.selections_did_change(true, &old_cursor_position, request_completions, cx);
2696
2697 if self.should_open_signature_help_automatically(
2698 &old_cursor_position,
2699 self.signature_help_state.backspace_pressed(),
2700 cx,
2701 ) {
2702 self.show_signature_help(&ShowSignatureHelp, cx);
2703 }
2704 self.signature_help_state.set_backspace_pressed(false);
2705 }
2706
2707 result
2708 }
2709
2710 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2711 where
2712 I: IntoIterator<Item = (Range<S>, T)>,
2713 S: ToOffset,
2714 T: Into<Arc<str>>,
2715 {
2716 if self.read_only(cx) {
2717 return;
2718 }
2719
2720 self.buffer
2721 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2722 }
2723
2724 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2725 where
2726 I: IntoIterator<Item = (Range<S>, T)>,
2727 S: ToOffset,
2728 T: Into<Arc<str>>,
2729 {
2730 if self.read_only(cx) {
2731 return;
2732 }
2733
2734 self.buffer.update(cx, |buffer, cx| {
2735 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2736 });
2737 }
2738
2739 pub fn edit_with_block_indent<I, S, T>(
2740 &mut self,
2741 edits: I,
2742 original_indent_columns: Vec<u32>,
2743 cx: &mut ViewContext<Self>,
2744 ) where
2745 I: IntoIterator<Item = (Range<S>, T)>,
2746 S: ToOffset,
2747 T: Into<Arc<str>>,
2748 {
2749 if self.read_only(cx) {
2750 return;
2751 }
2752
2753 self.buffer.update(cx, |buffer, cx| {
2754 buffer.edit(
2755 edits,
2756 Some(AutoindentMode::Block {
2757 original_indent_columns,
2758 }),
2759 cx,
2760 )
2761 });
2762 }
2763
2764 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
2765 self.hide_context_menu(cx);
2766
2767 match phase {
2768 SelectPhase::Begin {
2769 position,
2770 add,
2771 click_count,
2772 } => self.begin_selection(position, add, click_count, cx),
2773 SelectPhase::BeginColumnar {
2774 position,
2775 goal_column,
2776 reset,
2777 } => self.begin_columnar_selection(position, goal_column, reset, cx),
2778 SelectPhase::Extend {
2779 position,
2780 click_count,
2781 } => self.extend_selection(position, click_count, cx),
2782 SelectPhase::Update {
2783 position,
2784 goal_column,
2785 scroll_delta,
2786 } => self.update_selection(position, goal_column, scroll_delta, cx),
2787 SelectPhase::End => self.end_selection(cx),
2788 }
2789 }
2790
2791 fn extend_selection(
2792 &mut self,
2793 position: DisplayPoint,
2794 click_count: usize,
2795 cx: &mut ViewContext<Self>,
2796 ) {
2797 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2798 let tail = self.selections.newest::<usize>(cx).tail();
2799 self.begin_selection(position, false, click_count, cx);
2800
2801 let position = position.to_offset(&display_map, Bias::Left);
2802 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2803
2804 let mut pending_selection = self
2805 .selections
2806 .pending_anchor()
2807 .expect("extend_selection not called with pending selection");
2808 if position >= tail {
2809 pending_selection.start = tail_anchor;
2810 } else {
2811 pending_selection.end = tail_anchor;
2812 pending_selection.reversed = true;
2813 }
2814
2815 let mut pending_mode = self.selections.pending_mode().unwrap();
2816 match &mut pending_mode {
2817 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2818 _ => {}
2819 }
2820
2821 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
2822 s.set_pending(pending_selection, pending_mode)
2823 });
2824 }
2825
2826 fn begin_selection(
2827 &mut self,
2828 position: DisplayPoint,
2829 add: bool,
2830 click_count: usize,
2831 cx: &mut ViewContext<Self>,
2832 ) {
2833 if !self.focus_handle.is_focused(cx) {
2834 self.last_focused_descendant = None;
2835 cx.focus(&self.focus_handle);
2836 }
2837
2838 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2839 let buffer = &display_map.buffer_snapshot;
2840 let newest_selection = self.selections.newest_anchor().clone();
2841 let position = display_map.clip_point(position, Bias::Left);
2842
2843 let start;
2844 let end;
2845 let mode;
2846 let auto_scroll;
2847 match click_count {
2848 1 => {
2849 start = buffer.anchor_before(position.to_point(&display_map));
2850 end = start;
2851 mode = SelectMode::Character;
2852 auto_scroll = true;
2853 }
2854 2 => {
2855 let range = movement::surrounding_word(&display_map, position);
2856 start = buffer.anchor_before(range.start.to_point(&display_map));
2857 end = buffer.anchor_before(range.end.to_point(&display_map));
2858 mode = SelectMode::Word(start..end);
2859 auto_scroll = true;
2860 }
2861 3 => {
2862 let position = display_map
2863 .clip_point(position, Bias::Left)
2864 .to_point(&display_map);
2865 let line_start = display_map.prev_line_boundary(position).0;
2866 let next_line_start = buffer.clip_point(
2867 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2868 Bias::Left,
2869 );
2870 start = buffer.anchor_before(line_start);
2871 end = buffer.anchor_before(next_line_start);
2872 mode = SelectMode::Line(start..end);
2873 auto_scroll = true;
2874 }
2875 _ => {
2876 start = buffer.anchor_before(0);
2877 end = buffer.anchor_before(buffer.len());
2878 mode = SelectMode::All;
2879 auto_scroll = false;
2880 }
2881 }
2882
2883 let point_to_delete: Option<usize> = {
2884 let selected_points: Vec<Selection<Point>> =
2885 self.selections.disjoint_in_range(start..end, cx);
2886
2887 if !add || click_count > 1 {
2888 None
2889 } else if !selected_points.is_empty() {
2890 Some(selected_points[0].id)
2891 } else {
2892 let clicked_point_already_selected =
2893 self.selections.disjoint.iter().find(|selection| {
2894 selection.start.to_point(buffer) == start.to_point(buffer)
2895 || selection.end.to_point(buffer) == end.to_point(buffer)
2896 });
2897
2898 clicked_point_already_selected.map(|selection| selection.id)
2899 }
2900 };
2901
2902 let selections_count = self.selections.count();
2903
2904 self.change_selections(auto_scroll.then(Autoscroll::newest), cx, |s| {
2905 if let Some(point_to_delete) = point_to_delete {
2906 s.delete(point_to_delete);
2907
2908 if selections_count == 1 {
2909 s.set_pending_anchor_range(start..end, mode);
2910 }
2911 } else {
2912 if !add {
2913 s.clear_disjoint();
2914 } else if click_count > 1 {
2915 s.delete(newest_selection.id)
2916 }
2917
2918 s.set_pending_anchor_range(start..end, mode);
2919 }
2920 });
2921 }
2922
2923 fn begin_columnar_selection(
2924 &mut self,
2925 position: DisplayPoint,
2926 goal_column: u32,
2927 reset: bool,
2928 cx: &mut ViewContext<Self>,
2929 ) {
2930 if !self.focus_handle.is_focused(cx) {
2931 self.last_focused_descendant = None;
2932 cx.focus(&self.focus_handle);
2933 }
2934
2935 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2936
2937 if reset {
2938 let pointer_position = display_map
2939 .buffer_snapshot
2940 .anchor_before(position.to_point(&display_map));
2941
2942 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
2943 s.clear_disjoint();
2944 s.set_pending_anchor_range(
2945 pointer_position..pointer_position,
2946 SelectMode::Character,
2947 );
2948 });
2949 }
2950
2951 let tail = self.selections.newest::<Point>(cx).tail();
2952 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2953
2954 if !reset {
2955 self.select_columns(
2956 tail.to_display_point(&display_map),
2957 position,
2958 goal_column,
2959 &display_map,
2960 cx,
2961 );
2962 }
2963 }
2964
2965 fn update_selection(
2966 &mut self,
2967 position: DisplayPoint,
2968 goal_column: u32,
2969 scroll_delta: gpui::Point<f32>,
2970 cx: &mut ViewContext<Self>,
2971 ) {
2972 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2973
2974 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2975 let tail = tail.to_display_point(&display_map);
2976 self.select_columns(tail, position, goal_column, &display_map, cx);
2977 } else if let Some(mut pending) = self.selections.pending_anchor() {
2978 let buffer = self.buffer.read(cx).snapshot(cx);
2979 let head;
2980 let tail;
2981 let mode = self.selections.pending_mode().unwrap();
2982 match &mode {
2983 SelectMode::Character => {
2984 head = position.to_point(&display_map);
2985 tail = pending.tail().to_point(&buffer);
2986 }
2987 SelectMode::Word(original_range) => {
2988 let original_display_range = original_range.start.to_display_point(&display_map)
2989 ..original_range.end.to_display_point(&display_map);
2990 let original_buffer_range = original_display_range.start.to_point(&display_map)
2991 ..original_display_range.end.to_point(&display_map);
2992 if movement::is_inside_word(&display_map, position)
2993 || original_display_range.contains(&position)
2994 {
2995 let word_range = movement::surrounding_word(&display_map, position);
2996 if word_range.start < original_display_range.start {
2997 head = word_range.start.to_point(&display_map);
2998 } else {
2999 head = word_range.end.to_point(&display_map);
3000 }
3001 } else {
3002 head = position.to_point(&display_map);
3003 }
3004
3005 if head <= original_buffer_range.start {
3006 tail = original_buffer_range.end;
3007 } else {
3008 tail = original_buffer_range.start;
3009 }
3010 }
3011 SelectMode::Line(original_range) => {
3012 let original_range = original_range.to_point(&display_map.buffer_snapshot);
3013
3014 let position = display_map
3015 .clip_point(position, Bias::Left)
3016 .to_point(&display_map);
3017 let line_start = display_map.prev_line_boundary(position).0;
3018 let next_line_start = buffer.clip_point(
3019 display_map.next_line_boundary(position).0 + Point::new(1, 0),
3020 Bias::Left,
3021 );
3022
3023 if line_start < original_range.start {
3024 head = line_start
3025 } else {
3026 head = next_line_start
3027 }
3028
3029 if head <= original_range.start {
3030 tail = original_range.end;
3031 } else {
3032 tail = original_range.start;
3033 }
3034 }
3035 SelectMode::All => {
3036 return;
3037 }
3038 };
3039
3040 if head < tail {
3041 pending.start = buffer.anchor_before(head);
3042 pending.end = buffer.anchor_before(tail);
3043 pending.reversed = true;
3044 } else {
3045 pending.start = buffer.anchor_before(tail);
3046 pending.end = buffer.anchor_before(head);
3047 pending.reversed = false;
3048 }
3049
3050 self.change_selections(None, cx, |s| {
3051 s.set_pending(pending, mode);
3052 });
3053 } else {
3054 log::error!("update_selection dispatched with no pending selection");
3055 return;
3056 }
3057
3058 self.apply_scroll_delta(scroll_delta, cx);
3059 cx.notify();
3060 }
3061
3062 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
3063 self.columnar_selection_tail.take();
3064 if self.selections.pending_anchor().is_some() {
3065 let selections = self.selections.all::<usize>(cx);
3066 self.change_selections(None, cx, |s| {
3067 s.select(selections);
3068 s.clear_pending();
3069 });
3070 }
3071 }
3072
3073 fn select_columns(
3074 &mut self,
3075 tail: DisplayPoint,
3076 head: DisplayPoint,
3077 goal_column: u32,
3078 display_map: &DisplaySnapshot,
3079 cx: &mut ViewContext<Self>,
3080 ) {
3081 let start_row = cmp::min(tail.row(), head.row());
3082 let end_row = cmp::max(tail.row(), head.row());
3083 let start_column = cmp::min(tail.column(), goal_column);
3084 let end_column = cmp::max(tail.column(), goal_column);
3085 let reversed = start_column < tail.column();
3086
3087 let selection_ranges = (start_row.0..=end_row.0)
3088 .map(DisplayRow)
3089 .filter_map(|row| {
3090 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
3091 let start = display_map
3092 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
3093 .to_point(display_map);
3094 let end = display_map
3095 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
3096 .to_point(display_map);
3097 if reversed {
3098 Some(end..start)
3099 } else {
3100 Some(start..end)
3101 }
3102 } else {
3103 None
3104 }
3105 })
3106 .collect::<Vec<_>>();
3107
3108 self.change_selections(None, cx, |s| {
3109 s.select_ranges(selection_ranges);
3110 });
3111 cx.notify();
3112 }
3113
3114 pub fn has_pending_nonempty_selection(&self) -> bool {
3115 let pending_nonempty_selection = match self.selections.pending_anchor() {
3116 Some(Selection { start, end, .. }) => start != end,
3117 None => false,
3118 };
3119
3120 pending_nonempty_selection
3121 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
3122 }
3123
3124 pub fn has_pending_selection(&self) -> bool {
3125 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
3126 }
3127
3128 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
3129 if self.clear_expanded_diff_hunks(cx) {
3130 cx.notify();
3131 return;
3132 }
3133 if self.dismiss_menus_and_popups(true, cx) {
3134 return;
3135 }
3136
3137 if self.mode == EditorMode::Full
3138 && self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel())
3139 {
3140 return;
3141 }
3142
3143 cx.propagate();
3144 }
3145
3146 pub fn dismiss_menus_and_popups(
3147 &mut self,
3148 should_report_inline_completion_event: bool,
3149 cx: &mut ViewContext<Self>,
3150 ) -> bool {
3151 if self.take_rename(false, cx).is_some() {
3152 return true;
3153 }
3154
3155 if hide_hover(self, cx) {
3156 return true;
3157 }
3158
3159 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
3160 return true;
3161 }
3162
3163 if self.hide_context_menu(cx).is_some() {
3164 return true;
3165 }
3166
3167 if self.mouse_context_menu.take().is_some() {
3168 return true;
3169 }
3170
3171 if self.discard_inline_completion(should_report_inline_completion_event, cx) {
3172 return true;
3173 }
3174
3175 if self.snippet_stack.pop().is_some() {
3176 return true;
3177 }
3178
3179 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
3180 self.dismiss_diagnostics(cx);
3181 return true;
3182 }
3183
3184 false
3185 }
3186
3187 fn linked_editing_ranges_for(
3188 &self,
3189 selection: Range<text::Anchor>,
3190 cx: &AppContext,
3191 ) -> Option<HashMap<Model<Buffer>, Vec<Range<text::Anchor>>>> {
3192 if self.linked_edit_ranges.is_empty() {
3193 return None;
3194 }
3195 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
3196 selection.end.buffer_id.and_then(|end_buffer_id| {
3197 if selection.start.buffer_id != Some(end_buffer_id) {
3198 return None;
3199 }
3200 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
3201 let snapshot = buffer.read(cx).snapshot();
3202 self.linked_edit_ranges
3203 .get(end_buffer_id, selection.start..selection.end, &snapshot)
3204 .map(|ranges| (ranges, snapshot, buffer))
3205 })?;
3206 use text::ToOffset as TO;
3207 // find offset from the start of current range to current cursor position
3208 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
3209
3210 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
3211 let start_difference = start_offset - start_byte_offset;
3212 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
3213 let end_difference = end_offset - start_byte_offset;
3214 // Current range has associated linked ranges.
3215 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3216 for range in linked_ranges.iter() {
3217 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
3218 let end_offset = start_offset + end_difference;
3219 let start_offset = start_offset + start_difference;
3220 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
3221 continue;
3222 }
3223 if self.selections.disjoint_anchor_ranges().iter().any(|s| {
3224 if s.start.buffer_id != selection.start.buffer_id
3225 || s.end.buffer_id != selection.end.buffer_id
3226 {
3227 return false;
3228 }
3229 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
3230 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
3231 }) {
3232 continue;
3233 }
3234 let start = buffer_snapshot.anchor_after(start_offset);
3235 let end = buffer_snapshot.anchor_after(end_offset);
3236 linked_edits
3237 .entry(buffer.clone())
3238 .or_default()
3239 .push(start..end);
3240 }
3241 Some(linked_edits)
3242 }
3243
3244 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3245 let text: Arc<str> = text.into();
3246
3247 if self.read_only(cx) {
3248 return;
3249 }
3250
3251 let selections = self.selections.all_adjusted(cx);
3252 let mut bracket_inserted = false;
3253 let mut edits = Vec::new();
3254 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3255 let mut new_selections = Vec::with_capacity(selections.len());
3256 let mut new_autoclose_regions = Vec::new();
3257 let snapshot = self.buffer.read(cx).read(cx);
3258
3259 for (selection, autoclose_region) in
3260 self.selections_with_autoclose_regions(selections, &snapshot)
3261 {
3262 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
3263 // Determine if the inserted text matches the opening or closing
3264 // bracket of any of this language's bracket pairs.
3265 let mut bracket_pair = None;
3266 let mut is_bracket_pair_start = false;
3267 let mut is_bracket_pair_end = false;
3268 if !text.is_empty() {
3269 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
3270 // and they are removing the character that triggered IME popup.
3271 for (pair, enabled) in scope.brackets() {
3272 if !pair.close && !pair.surround {
3273 continue;
3274 }
3275
3276 if enabled && pair.start.ends_with(text.as_ref()) {
3277 let prefix_len = pair.start.len() - text.len();
3278 let preceding_text_matches_prefix = prefix_len == 0
3279 || (selection.start.column >= (prefix_len as u32)
3280 && snapshot.contains_str_at(
3281 Point::new(
3282 selection.start.row,
3283 selection.start.column - (prefix_len as u32),
3284 ),
3285 &pair.start[..prefix_len],
3286 ));
3287 if preceding_text_matches_prefix {
3288 bracket_pair = Some(pair.clone());
3289 is_bracket_pair_start = true;
3290 break;
3291 }
3292 }
3293 if pair.end.as_str() == text.as_ref() {
3294 bracket_pair = Some(pair.clone());
3295 is_bracket_pair_end = true;
3296 break;
3297 }
3298 }
3299 }
3300
3301 if let Some(bracket_pair) = bracket_pair {
3302 let snapshot_settings = snapshot.settings_at(selection.start, cx);
3303 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
3304 let auto_surround =
3305 self.use_auto_surround && snapshot_settings.use_auto_surround;
3306 if selection.is_empty() {
3307 if is_bracket_pair_start {
3308 // If the inserted text is a suffix of an opening bracket and the
3309 // selection is preceded by the rest of the opening bracket, then
3310 // insert the closing bracket.
3311 let following_text_allows_autoclose = snapshot
3312 .chars_at(selection.start)
3313 .next()
3314 .map_or(true, |c| scope.should_autoclose_before(c));
3315
3316 let is_closing_quote = if bracket_pair.end == bracket_pair.start
3317 && bracket_pair.start.len() == 1
3318 {
3319 let target = bracket_pair.start.chars().next().unwrap();
3320 let current_line_count = snapshot
3321 .reversed_chars_at(selection.start)
3322 .take_while(|&c| c != '\n')
3323 .filter(|&c| c == target)
3324 .count();
3325 current_line_count % 2 == 1
3326 } else {
3327 false
3328 };
3329
3330 if autoclose
3331 && bracket_pair.close
3332 && following_text_allows_autoclose
3333 && !is_closing_quote
3334 {
3335 let anchor = snapshot.anchor_before(selection.end);
3336 new_selections.push((selection.map(|_| anchor), text.len()));
3337 new_autoclose_regions.push((
3338 anchor,
3339 text.len(),
3340 selection.id,
3341 bracket_pair.clone(),
3342 ));
3343 edits.push((
3344 selection.range(),
3345 format!("{}{}", text, bracket_pair.end).into(),
3346 ));
3347 bracket_inserted = true;
3348 continue;
3349 }
3350 }
3351
3352 if let Some(region) = autoclose_region {
3353 // If the selection is followed by an auto-inserted closing bracket,
3354 // then don't insert that closing bracket again; just move the selection
3355 // past the closing bracket.
3356 let should_skip = selection.end == region.range.end.to_point(&snapshot)
3357 && text.as_ref() == region.pair.end.as_str();
3358 if should_skip {
3359 let anchor = snapshot.anchor_after(selection.end);
3360 new_selections
3361 .push((selection.map(|_| anchor), region.pair.end.len()));
3362 continue;
3363 }
3364 }
3365
3366 let always_treat_brackets_as_autoclosed = snapshot
3367 .settings_at(selection.start, cx)
3368 .always_treat_brackets_as_autoclosed;
3369 if always_treat_brackets_as_autoclosed
3370 && is_bracket_pair_end
3371 && snapshot.contains_str_at(selection.end, text.as_ref())
3372 {
3373 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
3374 // and the inserted text is a closing bracket and the selection is followed
3375 // by the closing bracket then move the selection past the closing bracket.
3376 let anchor = snapshot.anchor_after(selection.end);
3377 new_selections.push((selection.map(|_| anchor), text.len()));
3378 continue;
3379 }
3380 }
3381 // If an opening bracket is 1 character long and is typed while
3382 // text is selected, then surround that text with the bracket pair.
3383 else if auto_surround
3384 && bracket_pair.surround
3385 && is_bracket_pair_start
3386 && bracket_pair.start.chars().count() == 1
3387 {
3388 edits.push((selection.start..selection.start, text.clone()));
3389 edits.push((
3390 selection.end..selection.end,
3391 bracket_pair.end.as_str().into(),
3392 ));
3393 bracket_inserted = true;
3394 new_selections.push((
3395 Selection {
3396 id: selection.id,
3397 start: snapshot.anchor_after(selection.start),
3398 end: snapshot.anchor_before(selection.end),
3399 reversed: selection.reversed,
3400 goal: selection.goal,
3401 },
3402 0,
3403 ));
3404 continue;
3405 }
3406 }
3407 }
3408
3409 if self.auto_replace_emoji_shortcode
3410 && selection.is_empty()
3411 && text.as_ref().ends_with(':')
3412 {
3413 if let Some(possible_emoji_short_code) =
3414 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
3415 {
3416 if !possible_emoji_short_code.is_empty() {
3417 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
3418 let emoji_shortcode_start = Point::new(
3419 selection.start.row,
3420 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
3421 );
3422
3423 // Remove shortcode from buffer
3424 edits.push((
3425 emoji_shortcode_start..selection.start,
3426 "".to_string().into(),
3427 ));
3428 new_selections.push((
3429 Selection {
3430 id: selection.id,
3431 start: snapshot.anchor_after(emoji_shortcode_start),
3432 end: snapshot.anchor_before(selection.start),
3433 reversed: selection.reversed,
3434 goal: selection.goal,
3435 },
3436 0,
3437 ));
3438
3439 // Insert emoji
3440 let selection_start_anchor = snapshot.anchor_after(selection.start);
3441 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3442 edits.push((selection.start..selection.end, emoji.to_string().into()));
3443
3444 continue;
3445 }
3446 }
3447 }
3448 }
3449
3450 // If not handling any auto-close operation, then just replace the selected
3451 // text with the given input and move the selection to the end of the
3452 // newly inserted text.
3453 let anchor = snapshot.anchor_after(selection.end);
3454 if !self.linked_edit_ranges.is_empty() {
3455 let start_anchor = snapshot.anchor_before(selection.start);
3456
3457 let is_word_char = text.chars().next().map_or(true, |char| {
3458 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
3459 classifier.is_word(char)
3460 });
3461
3462 if is_word_char {
3463 if let Some(ranges) = self
3464 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3465 {
3466 for (buffer, edits) in ranges {
3467 linked_edits
3468 .entry(buffer.clone())
3469 .or_default()
3470 .extend(edits.into_iter().map(|range| (range, text.clone())));
3471 }
3472 }
3473 }
3474 }
3475
3476 new_selections.push((selection.map(|_| anchor), 0));
3477 edits.push((selection.start..selection.end, text.clone()));
3478 }
3479
3480 drop(snapshot);
3481
3482 self.transact(cx, |this, cx| {
3483 this.buffer.update(cx, |buffer, cx| {
3484 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3485 });
3486 for (buffer, edits) in linked_edits {
3487 buffer.update(cx, |buffer, cx| {
3488 let snapshot = buffer.snapshot();
3489 let edits = edits
3490 .into_iter()
3491 .map(|(range, text)| {
3492 use text::ToPoint as TP;
3493 let end_point = TP::to_point(&range.end, &snapshot);
3494 let start_point = TP::to_point(&range.start, &snapshot);
3495 (start_point..end_point, text)
3496 })
3497 .sorted_by_key(|(range, _)| range.start)
3498 .collect::<Vec<_>>();
3499 buffer.edit(edits, None, cx);
3500 })
3501 }
3502 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3503 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3504 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3505 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
3506 .zip(new_selection_deltas)
3507 .map(|(selection, delta)| Selection {
3508 id: selection.id,
3509 start: selection.start + delta,
3510 end: selection.end + delta,
3511 reversed: selection.reversed,
3512 goal: SelectionGoal::None,
3513 })
3514 .collect::<Vec<_>>();
3515
3516 let mut i = 0;
3517 for (position, delta, selection_id, pair) in new_autoclose_regions {
3518 let position = position.to_offset(&map.buffer_snapshot) + delta;
3519 let start = map.buffer_snapshot.anchor_before(position);
3520 let end = map.buffer_snapshot.anchor_after(position);
3521 while let Some(existing_state) = this.autoclose_regions.get(i) {
3522 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
3523 Ordering::Less => i += 1,
3524 Ordering::Greater => break,
3525 Ordering::Equal => {
3526 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
3527 Ordering::Less => i += 1,
3528 Ordering::Equal => break,
3529 Ordering::Greater => break,
3530 }
3531 }
3532 }
3533 }
3534 this.autoclose_regions.insert(
3535 i,
3536 AutocloseRegion {
3537 selection_id,
3538 range: start..end,
3539 pair,
3540 },
3541 );
3542 }
3543
3544 let had_active_inline_completion = this.has_active_inline_completion(cx);
3545 this.change_selections_inner(Some(Autoscroll::fit()), false, cx, |s| {
3546 s.select(new_selections)
3547 });
3548
3549 if !bracket_inserted {
3550 if let Some(on_type_format_task) =
3551 this.trigger_on_type_formatting(text.to_string(), cx)
3552 {
3553 on_type_format_task.detach_and_log_err(cx);
3554 }
3555 }
3556
3557 let editor_settings = EditorSettings::get_global(cx);
3558 if bracket_inserted
3559 && (editor_settings.auto_signature_help
3560 || editor_settings.show_signature_help_after_edits)
3561 {
3562 this.show_signature_help(&ShowSignatureHelp, cx);
3563 }
3564
3565 let trigger_in_words = !had_active_inline_completion;
3566 this.trigger_completion_on_input(&text, trigger_in_words, cx);
3567 linked_editing_ranges::refresh_linked_ranges(this, cx);
3568 this.refresh_inline_completion(true, false, cx);
3569 });
3570 }
3571
3572 fn find_possible_emoji_shortcode_at_position(
3573 snapshot: &MultiBufferSnapshot,
3574 position: Point,
3575 ) -> Option<String> {
3576 let mut chars = Vec::new();
3577 let mut found_colon = false;
3578 for char in snapshot.reversed_chars_at(position).take(100) {
3579 // Found a possible emoji shortcode in the middle of the buffer
3580 if found_colon {
3581 if char.is_whitespace() {
3582 chars.reverse();
3583 return Some(chars.iter().collect());
3584 }
3585 // If the previous character is not a whitespace, we are in the middle of a word
3586 // and we only want to complete the shortcode if the word is made up of other emojis
3587 let mut containing_word = String::new();
3588 for ch in snapshot
3589 .reversed_chars_at(position)
3590 .skip(chars.len() + 1)
3591 .take(100)
3592 {
3593 if ch.is_whitespace() {
3594 break;
3595 }
3596 containing_word.push(ch);
3597 }
3598 let containing_word = containing_word.chars().rev().collect::<String>();
3599 if util::word_consists_of_emojis(containing_word.as_str()) {
3600 chars.reverse();
3601 return Some(chars.iter().collect());
3602 }
3603 }
3604
3605 if char.is_whitespace() || !char.is_ascii() {
3606 return None;
3607 }
3608 if char == ':' {
3609 found_colon = true;
3610 } else {
3611 chars.push(char);
3612 }
3613 }
3614 // Found a possible emoji shortcode at the beginning of the buffer
3615 chars.reverse();
3616 Some(chars.iter().collect())
3617 }
3618
3619 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
3620 self.transact(cx, |this, cx| {
3621 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3622 let selections = this.selections.all::<usize>(cx);
3623 let multi_buffer = this.buffer.read(cx);
3624 let buffer = multi_buffer.snapshot(cx);
3625 selections
3626 .iter()
3627 .map(|selection| {
3628 let start_point = selection.start.to_point(&buffer);
3629 let mut indent =
3630 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3631 indent.len = cmp::min(indent.len, start_point.column);
3632 let start = selection.start;
3633 let end = selection.end;
3634 let selection_is_empty = start == end;
3635 let language_scope = buffer.language_scope_at(start);
3636 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3637 &language_scope
3638 {
3639 let leading_whitespace_len = buffer
3640 .reversed_chars_at(start)
3641 .take_while(|c| c.is_whitespace() && *c != '\n')
3642 .map(|c| c.len_utf8())
3643 .sum::<usize>();
3644
3645 let trailing_whitespace_len = buffer
3646 .chars_at(end)
3647 .take_while(|c| c.is_whitespace() && *c != '\n')
3648 .map(|c| c.len_utf8())
3649 .sum::<usize>();
3650
3651 let insert_extra_newline =
3652 language.brackets().any(|(pair, enabled)| {
3653 let pair_start = pair.start.trim_end();
3654 let pair_end = pair.end.trim_start();
3655
3656 enabled
3657 && pair.newline
3658 && buffer.contains_str_at(
3659 end + trailing_whitespace_len,
3660 pair_end,
3661 )
3662 && buffer.contains_str_at(
3663 (start - leading_whitespace_len)
3664 .saturating_sub(pair_start.len()),
3665 pair_start,
3666 )
3667 });
3668
3669 // Comment extension on newline is allowed only for cursor selections
3670 let comment_delimiter = maybe!({
3671 if !selection_is_empty {
3672 return None;
3673 }
3674
3675 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
3676 return None;
3677 }
3678
3679 let delimiters = language.line_comment_prefixes();
3680 let max_len_of_delimiter =
3681 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3682 let (snapshot, range) =
3683 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3684
3685 let mut index_of_first_non_whitespace = 0;
3686 let comment_candidate = snapshot
3687 .chars_for_range(range)
3688 .skip_while(|c| {
3689 let should_skip = c.is_whitespace();
3690 if should_skip {
3691 index_of_first_non_whitespace += 1;
3692 }
3693 should_skip
3694 })
3695 .take(max_len_of_delimiter)
3696 .collect::<String>();
3697 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3698 comment_candidate.starts_with(comment_prefix.as_ref())
3699 })?;
3700 let cursor_is_placed_after_comment_marker =
3701 index_of_first_non_whitespace + comment_prefix.len()
3702 <= start_point.column as usize;
3703 if cursor_is_placed_after_comment_marker {
3704 Some(comment_prefix.clone())
3705 } else {
3706 None
3707 }
3708 });
3709 (comment_delimiter, insert_extra_newline)
3710 } else {
3711 (None, false)
3712 };
3713
3714 let capacity_for_delimiter = comment_delimiter
3715 .as_deref()
3716 .map(str::len)
3717 .unwrap_or_default();
3718 let mut new_text =
3719 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3720 new_text.push('\n');
3721 new_text.extend(indent.chars());
3722 if let Some(delimiter) = &comment_delimiter {
3723 new_text.push_str(delimiter);
3724 }
3725 if insert_extra_newline {
3726 new_text = new_text.repeat(2);
3727 }
3728
3729 let anchor = buffer.anchor_after(end);
3730 let new_selection = selection.map(|_| anchor);
3731 (
3732 (start..end, new_text),
3733 (insert_extra_newline, new_selection),
3734 )
3735 })
3736 .unzip()
3737 };
3738
3739 this.edit_with_autoindent(edits, cx);
3740 let buffer = this.buffer.read(cx).snapshot(cx);
3741 let new_selections = selection_fixup_info
3742 .into_iter()
3743 .map(|(extra_newline_inserted, new_selection)| {
3744 let mut cursor = new_selection.end.to_point(&buffer);
3745 if extra_newline_inserted {
3746 cursor.row -= 1;
3747 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3748 }
3749 new_selection.map(|_| cursor)
3750 })
3751 .collect();
3752
3753 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
3754 this.refresh_inline_completion(true, false, cx);
3755 });
3756 }
3757
3758 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
3759 let buffer = self.buffer.read(cx);
3760 let snapshot = buffer.snapshot(cx);
3761
3762 let mut edits = Vec::new();
3763 let mut rows = Vec::new();
3764
3765 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3766 let cursor = selection.head();
3767 let row = cursor.row;
3768
3769 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3770
3771 let newline = "\n".to_string();
3772 edits.push((start_of_line..start_of_line, newline));
3773
3774 rows.push(row + rows_inserted as u32);
3775 }
3776
3777 self.transact(cx, |editor, cx| {
3778 editor.edit(edits, cx);
3779
3780 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3781 let mut index = 0;
3782 s.move_cursors_with(|map, _, _| {
3783 let row = rows[index];
3784 index += 1;
3785
3786 let point = Point::new(row, 0);
3787 let boundary = map.next_line_boundary(point).1;
3788 let clipped = map.clip_point(boundary, Bias::Left);
3789
3790 (clipped, SelectionGoal::None)
3791 });
3792 });
3793
3794 let mut indent_edits = Vec::new();
3795 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3796 for row in rows {
3797 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3798 for (row, indent) in indents {
3799 if indent.len == 0 {
3800 continue;
3801 }
3802
3803 let text = match indent.kind {
3804 IndentKind::Space => " ".repeat(indent.len as usize),
3805 IndentKind::Tab => "\t".repeat(indent.len as usize),
3806 };
3807 let point = Point::new(row.0, 0);
3808 indent_edits.push((point..point, text));
3809 }
3810 }
3811 editor.edit(indent_edits, cx);
3812 });
3813 }
3814
3815 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
3816 let buffer = self.buffer.read(cx);
3817 let snapshot = buffer.snapshot(cx);
3818
3819 let mut edits = Vec::new();
3820 let mut rows = Vec::new();
3821 let mut rows_inserted = 0;
3822
3823 for selection in self.selections.all_adjusted(cx) {
3824 let cursor = selection.head();
3825 let row = cursor.row;
3826
3827 let point = Point::new(row + 1, 0);
3828 let start_of_line = snapshot.clip_point(point, Bias::Left);
3829
3830 let newline = "\n".to_string();
3831 edits.push((start_of_line..start_of_line, newline));
3832
3833 rows_inserted += 1;
3834 rows.push(row + rows_inserted);
3835 }
3836
3837 self.transact(cx, |editor, cx| {
3838 editor.edit(edits, cx);
3839
3840 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3841 let mut index = 0;
3842 s.move_cursors_with(|map, _, _| {
3843 let row = rows[index];
3844 index += 1;
3845
3846 let point = Point::new(row, 0);
3847 let boundary = map.next_line_boundary(point).1;
3848 let clipped = map.clip_point(boundary, Bias::Left);
3849
3850 (clipped, SelectionGoal::None)
3851 });
3852 });
3853
3854 let mut indent_edits = Vec::new();
3855 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3856 for row in rows {
3857 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3858 for (row, indent) in indents {
3859 if indent.len == 0 {
3860 continue;
3861 }
3862
3863 let text = match indent.kind {
3864 IndentKind::Space => " ".repeat(indent.len as usize),
3865 IndentKind::Tab => "\t".repeat(indent.len as usize),
3866 };
3867 let point = Point::new(row.0, 0);
3868 indent_edits.push((point..point, text));
3869 }
3870 }
3871 editor.edit(indent_edits, cx);
3872 });
3873 }
3874
3875 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3876 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3877 original_indent_columns: Vec::new(),
3878 });
3879 self.insert_with_autoindent_mode(text, autoindent, cx);
3880 }
3881
3882 fn insert_with_autoindent_mode(
3883 &mut self,
3884 text: &str,
3885 autoindent_mode: Option<AutoindentMode>,
3886 cx: &mut ViewContext<Self>,
3887 ) {
3888 if self.read_only(cx) {
3889 return;
3890 }
3891
3892 let text: Arc<str> = text.into();
3893 self.transact(cx, |this, cx| {
3894 let old_selections = this.selections.all_adjusted(cx);
3895 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3896 let anchors = {
3897 let snapshot = buffer.read(cx);
3898 old_selections
3899 .iter()
3900 .map(|s| {
3901 let anchor = snapshot.anchor_after(s.head());
3902 s.map(|_| anchor)
3903 })
3904 .collect::<Vec<_>>()
3905 };
3906 buffer.edit(
3907 old_selections
3908 .iter()
3909 .map(|s| (s.start..s.end, text.clone())),
3910 autoindent_mode,
3911 cx,
3912 );
3913 anchors
3914 });
3915
3916 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3917 s.select_anchors(selection_anchors);
3918 })
3919 });
3920 }
3921
3922 fn trigger_completion_on_input(
3923 &mut self,
3924 text: &str,
3925 trigger_in_words: bool,
3926 cx: &mut ViewContext<Self>,
3927 ) {
3928 if self.is_completion_trigger(text, trigger_in_words, cx) {
3929 self.show_completions(
3930 &ShowCompletions {
3931 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3932 },
3933 cx,
3934 );
3935 } else {
3936 self.hide_context_menu(cx);
3937 }
3938 }
3939
3940 fn is_completion_trigger(
3941 &self,
3942 text: &str,
3943 trigger_in_words: bool,
3944 cx: &mut ViewContext<Self>,
3945 ) -> bool {
3946 let position = self.selections.newest_anchor().head();
3947 let multibuffer = self.buffer.read(cx);
3948 let Some(buffer) = position
3949 .buffer_id
3950 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3951 else {
3952 return false;
3953 };
3954
3955 if let Some(completion_provider) = &self.completion_provider {
3956 completion_provider.is_completion_trigger(
3957 &buffer,
3958 position.text_anchor,
3959 text,
3960 trigger_in_words,
3961 cx,
3962 )
3963 } else {
3964 false
3965 }
3966 }
3967
3968 /// If any empty selections is touching the start of its innermost containing autoclose
3969 /// region, expand it to select the brackets.
3970 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
3971 let selections = self.selections.all::<usize>(cx);
3972 let buffer = self.buffer.read(cx).read(cx);
3973 let new_selections = self
3974 .selections_with_autoclose_regions(selections, &buffer)
3975 .map(|(mut selection, region)| {
3976 if !selection.is_empty() {
3977 return selection;
3978 }
3979
3980 if let Some(region) = region {
3981 let mut range = region.range.to_offset(&buffer);
3982 if selection.start == range.start && range.start >= region.pair.start.len() {
3983 range.start -= region.pair.start.len();
3984 if buffer.contains_str_at(range.start, ®ion.pair.start)
3985 && buffer.contains_str_at(range.end, ®ion.pair.end)
3986 {
3987 range.end += region.pair.end.len();
3988 selection.start = range.start;
3989 selection.end = range.end;
3990
3991 return selection;
3992 }
3993 }
3994 }
3995
3996 let always_treat_brackets_as_autoclosed = buffer
3997 .settings_at(selection.start, cx)
3998 .always_treat_brackets_as_autoclosed;
3999
4000 if !always_treat_brackets_as_autoclosed {
4001 return selection;
4002 }
4003
4004 if let Some(scope) = buffer.language_scope_at(selection.start) {
4005 for (pair, enabled) in scope.brackets() {
4006 if !enabled || !pair.close {
4007 continue;
4008 }
4009
4010 if buffer.contains_str_at(selection.start, &pair.end) {
4011 let pair_start_len = pair.start.len();
4012 if buffer.contains_str_at(selection.start - pair_start_len, &pair.start)
4013 {
4014 selection.start -= pair_start_len;
4015 selection.end += pair.end.len();
4016
4017 return selection;
4018 }
4019 }
4020 }
4021 }
4022
4023 selection
4024 })
4025 .collect();
4026
4027 drop(buffer);
4028 self.change_selections(None, cx, |selections| selections.select(new_selections));
4029 }
4030
4031 /// Iterate the given selections, and for each one, find the smallest surrounding
4032 /// autoclose region. This uses the ordering of the selections and the autoclose
4033 /// regions to avoid repeated comparisons.
4034 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
4035 &'a self,
4036 selections: impl IntoIterator<Item = Selection<D>>,
4037 buffer: &'a MultiBufferSnapshot,
4038 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
4039 let mut i = 0;
4040 let mut regions = self.autoclose_regions.as_slice();
4041 selections.into_iter().map(move |selection| {
4042 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
4043
4044 let mut enclosing = None;
4045 while let Some(pair_state) = regions.get(i) {
4046 if pair_state.range.end.to_offset(buffer) < range.start {
4047 regions = ®ions[i + 1..];
4048 i = 0;
4049 } else if pair_state.range.start.to_offset(buffer) > range.end {
4050 break;
4051 } else {
4052 if pair_state.selection_id == selection.id {
4053 enclosing = Some(pair_state);
4054 }
4055 i += 1;
4056 }
4057 }
4058
4059 (selection, enclosing)
4060 })
4061 }
4062
4063 /// Remove any autoclose regions that no longer contain their selection.
4064 fn invalidate_autoclose_regions(
4065 &mut self,
4066 mut selections: &[Selection<Anchor>],
4067 buffer: &MultiBufferSnapshot,
4068 ) {
4069 self.autoclose_regions.retain(|state| {
4070 let mut i = 0;
4071 while let Some(selection) = selections.get(i) {
4072 if selection.end.cmp(&state.range.start, buffer).is_lt() {
4073 selections = &selections[1..];
4074 continue;
4075 }
4076 if selection.start.cmp(&state.range.end, buffer).is_gt() {
4077 break;
4078 }
4079 if selection.id == state.selection_id {
4080 return true;
4081 } else {
4082 i += 1;
4083 }
4084 }
4085 false
4086 });
4087 }
4088
4089 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
4090 let offset = position.to_offset(buffer);
4091 let (word_range, kind) = buffer.surrounding_word(offset, true);
4092 if offset > word_range.start && kind == Some(CharKind::Word) {
4093 Some(
4094 buffer
4095 .text_for_range(word_range.start..offset)
4096 .collect::<String>(),
4097 )
4098 } else {
4099 None
4100 }
4101 }
4102
4103 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
4104 self.refresh_inlay_hints(
4105 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
4106 cx,
4107 );
4108 }
4109
4110 pub fn inlay_hints_enabled(&self) -> bool {
4111 self.inlay_hint_cache.enabled
4112 }
4113
4114 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
4115 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
4116 return;
4117 }
4118
4119 let reason_description = reason.description();
4120 let ignore_debounce = matches!(
4121 reason,
4122 InlayHintRefreshReason::SettingsChange(_)
4123 | InlayHintRefreshReason::Toggle(_)
4124 | InlayHintRefreshReason::ExcerptsRemoved(_)
4125 );
4126 let (invalidate_cache, required_languages) = match reason {
4127 InlayHintRefreshReason::Toggle(enabled) => {
4128 self.inlay_hint_cache.enabled = enabled;
4129 if enabled {
4130 (InvalidationStrategy::RefreshRequested, None)
4131 } else {
4132 self.inlay_hint_cache.clear();
4133 self.splice_inlays(
4134 self.visible_inlay_hints(cx)
4135 .iter()
4136 .map(|inlay| inlay.id)
4137 .collect(),
4138 Vec::new(),
4139 cx,
4140 );
4141 return;
4142 }
4143 }
4144 InlayHintRefreshReason::SettingsChange(new_settings) => {
4145 match self.inlay_hint_cache.update_settings(
4146 &self.buffer,
4147 new_settings,
4148 self.visible_inlay_hints(cx),
4149 cx,
4150 ) {
4151 ControlFlow::Break(Some(InlaySplice {
4152 to_remove,
4153 to_insert,
4154 })) => {
4155 self.splice_inlays(to_remove, to_insert, cx);
4156 return;
4157 }
4158 ControlFlow::Break(None) => return,
4159 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
4160 }
4161 }
4162 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
4163 if let Some(InlaySplice {
4164 to_remove,
4165 to_insert,
4166 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
4167 {
4168 self.splice_inlays(to_remove, to_insert, cx);
4169 }
4170 return;
4171 }
4172 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
4173 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
4174 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
4175 }
4176 InlayHintRefreshReason::RefreshRequested => {
4177 (InvalidationStrategy::RefreshRequested, None)
4178 }
4179 };
4180
4181 if let Some(InlaySplice {
4182 to_remove,
4183 to_insert,
4184 }) = self.inlay_hint_cache.spawn_hint_refresh(
4185 reason_description,
4186 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
4187 invalidate_cache,
4188 ignore_debounce,
4189 cx,
4190 ) {
4191 self.splice_inlays(to_remove, to_insert, cx);
4192 }
4193 }
4194
4195 fn visible_inlay_hints(&self, cx: &ViewContext<'_, Editor>) -> Vec<Inlay> {
4196 self.display_map
4197 .read(cx)
4198 .current_inlays()
4199 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
4200 .cloned()
4201 .collect()
4202 }
4203
4204 pub fn excerpts_for_inlay_hints_query(
4205 &self,
4206 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
4207 cx: &mut ViewContext<Editor>,
4208 ) -> HashMap<ExcerptId, (Model<Buffer>, clock::Global, Range<usize>)> {
4209 let Some(project) = self.project.as_ref() else {
4210 return HashMap::default();
4211 };
4212 let project = project.read(cx);
4213 let multi_buffer = self.buffer().read(cx);
4214 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
4215 let multi_buffer_visible_start = self
4216 .scroll_manager
4217 .anchor()
4218 .anchor
4219 .to_point(&multi_buffer_snapshot);
4220 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
4221 multi_buffer_visible_start
4222 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
4223 Bias::Left,
4224 );
4225 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
4226 multi_buffer
4227 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
4228 .into_iter()
4229 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
4230 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
4231 let buffer = buffer_handle.read(cx);
4232 let buffer_file = project::File::from_dyn(buffer.file())?;
4233 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
4234 let worktree_entry = buffer_worktree
4235 .read(cx)
4236 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
4237 if worktree_entry.is_ignored {
4238 return None;
4239 }
4240
4241 let language = buffer.language()?;
4242 if let Some(restrict_to_languages) = restrict_to_languages {
4243 if !restrict_to_languages.contains(language) {
4244 return None;
4245 }
4246 }
4247 Some((
4248 excerpt_id,
4249 (
4250 buffer_handle,
4251 buffer.version().clone(),
4252 excerpt_visible_range,
4253 ),
4254 ))
4255 })
4256 .collect()
4257 }
4258
4259 pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
4260 TextLayoutDetails {
4261 text_system: cx.text_system().clone(),
4262 editor_style: self.style.clone().unwrap(),
4263 rem_size: cx.rem_size(),
4264 scroll_anchor: self.scroll_manager.anchor(),
4265 visible_rows: self.visible_line_count(),
4266 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
4267 }
4268 }
4269
4270 fn splice_inlays(
4271 &self,
4272 to_remove: Vec<InlayId>,
4273 to_insert: Vec<Inlay>,
4274 cx: &mut ViewContext<Self>,
4275 ) {
4276 self.display_map.update(cx, |display_map, cx| {
4277 display_map.splice_inlays(to_remove, to_insert, cx);
4278 });
4279 cx.notify();
4280 }
4281
4282 fn trigger_on_type_formatting(
4283 &self,
4284 input: String,
4285 cx: &mut ViewContext<Self>,
4286 ) -> Option<Task<Result<()>>> {
4287 if input.len() != 1 {
4288 return None;
4289 }
4290
4291 let project = self.project.as_ref()?;
4292 let position = self.selections.newest_anchor().head();
4293 let (buffer, buffer_position) = self
4294 .buffer
4295 .read(cx)
4296 .text_anchor_for_position(position, cx)?;
4297
4298 let settings = language_settings::language_settings(
4299 buffer
4300 .read(cx)
4301 .language_at(buffer_position)
4302 .map(|l| l.name()),
4303 buffer.read(cx).file(),
4304 cx,
4305 );
4306 if !settings.use_on_type_format {
4307 return None;
4308 }
4309
4310 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
4311 // hence we do LSP request & edit on host side only — add formats to host's history.
4312 let push_to_lsp_host_history = true;
4313 // If this is not the host, append its history with new edits.
4314 let push_to_client_history = project.read(cx).is_via_collab();
4315
4316 let on_type_formatting = project.update(cx, |project, cx| {
4317 project.on_type_format(
4318 buffer.clone(),
4319 buffer_position,
4320 input,
4321 push_to_lsp_host_history,
4322 cx,
4323 )
4324 });
4325 Some(cx.spawn(|editor, mut cx| async move {
4326 if let Some(transaction) = on_type_formatting.await? {
4327 if push_to_client_history {
4328 buffer
4329 .update(&mut cx, |buffer, _| {
4330 buffer.push_transaction(transaction, Instant::now());
4331 })
4332 .ok();
4333 }
4334 editor.update(&mut cx, |editor, cx| {
4335 editor.refresh_document_highlights(cx);
4336 })?;
4337 }
4338 Ok(())
4339 }))
4340 }
4341
4342 pub fn show_completions(&mut self, options: &ShowCompletions, cx: &mut ViewContext<Self>) {
4343 if self.pending_rename.is_some() {
4344 return;
4345 }
4346
4347 let Some(provider) = self.completion_provider.as_ref() else {
4348 return;
4349 };
4350
4351 let position = self.selections.newest_anchor().head();
4352 let (buffer, buffer_position) =
4353 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
4354 output
4355 } else {
4356 return;
4357 };
4358
4359 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
4360 let is_followup_invoke = {
4361 let context_menu_state = self.context_menu.read();
4362 matches!(
4363 context_menu_state.deref(),
4364 Some(ContextMenu::Completions(_))
4365 )
4366 };
4367 let trigger_kind = match (&options.trigger, is_followup_invoke) {
4368 (_, true) => CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS,
4369 (Some(trigger), _) if buffer.read(cx).completion_triggers().contains(trigger) => {
4370 CompletionTriggerKind::TRIGGER_CHARACTER
4371 }
4372
4373 _ => CompletionTriggerKind::INVOKED,
4374 };
4375 let completion_context = CompletionContext {
4376 trigger_character: options.trigger.as_ref().and_then(|trigger| {
4377 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
4378 Some(String::from(trigger))
4379 } else {
4380 None
4381 }
4382 }),
4383 trigger_kind,
4384 };
4385 let completions = provider.completions(&buffer, buffer_position, completion_context, cx);
4386 let sort_completions = provider.sort_completions();
4387
4388 let id = post_inc(&mut self.next_completion_id);
4389 let task = cx.spawn(|this, mut cx| {
4390 async move {
4391 this.update(&mut cx, |this, _| {
4392 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
4393 })?;
4394 let completions = completions.await.log_err();
4395 let menu = if let Some(completions) = completions {
4396 let mut menu = CompletionsMenu {
4397 id,
4398 sort_completions,
4399 initial_position: position,
4400 match_candidates: completions
4401 .iter()
4402 .enumerate()
4403 .map(|(id, completion)| {
4404 StringMatchCandidate::new(
4405 id,
4406 completion.label.text[completion.label.filter_range.clone()]
4407 .into(),
4408 )
4409 })
4410 .collect(),
4411 buffer: buffer.clone(),
4412 completions: Arc::new(RwLock::new(completions.into())),
4413 matches: Vec::new().into(),
4414 selected_item: 0,
4415 scroll_handle: UniformListScrollHandle::new(),
4416 selected_completion_documentation_resolve_debounce: Arc::new(Mutex::new(
4417 DebouncedDelay::new(),
4418 )),
4419 };
4420 menu.filter(query.as_deref(), cx.background_executor().clone())
4421 .await;
4422
4423 if menu.matches.is_empty() {
4424 None
4425 } else {
4426 this.update(&mut cx, |editor, cx| {
4427 let completions = menu.completions.clone();
4428 let matches = menu.matches.clone();
4429
4430 let delay_ms = EditorSettings::get_global(cx)
4431 .completion_documentation_secondary_query_debounce;
4432 let delay = Duration::from_millis(delay_ms);
4433 editor
4434 .completion_documentation_pre_resolve_debounce
4435 .fire_new(delay, cx, |editor, cx| {
4436 CompletionsMenu::pre_resolve_completion_documentation(
4437 buffer,
4438 completions,
4439 matches,
4440 editor,
4441 cx,
4442 )
4443 });
4444 })
4445 .ok();
4446 Some(menu)
4447 }
4448 } else {
4449 None
4450 };
4451
4452 this.update(&mut cx, |this, cx| {
4453 let mut context_menu = this.context_menu.write();
4454 match context_menu.as_ref() {
4455 None => {}
4456
4457 Some(ContextMenu::Completions(prev_menu)) => {
4458 if prev_menu.id > id {
4459 return;
4460 }
4461 }
4462
4463 _ => return,
4464 }
4465
4466 if this.focus_handle.is_focused(cx) && menu.is_some() {
4467 let menu = menu.unwrap();
4468 *context_menu = Some(ContextMenu::Completions(menu));
4469 drop(context_menu);
4470 this.discard_inline_completion(false, cx);
4471 cx.notify();
4472 } else if this.completion_tasks.len() <= 1 {
4473 // If there are no more completion tasks and the last menu was
4474 // empty, we should hide it. If it was already hidden, we should
4475 // also show the copilot completion when available.
4476 drop(context_menu);
4477 if this.hide_context_menu(cx).is_none() {
4478 this.update_visible_inline_completion(cx);
4479 }
4480 }
4481 })?;
4482
4483 Ok::<_, anyhow::Error>(())
4484 }
4485 .log_err()
4486 });
4487
4488 self.completion_tasks.push((id, task));
4489 }
4490
4491 pub fn confirm_completion(
4492 &mut self,
4493 action: &ConfirmCompletion,
4494 cx: &mut ViewContext<Self>,
4495 ) -> Option<Task<Result<()>>> {
4496 self.do_completion(action.item_ix, CompletionIntent::Complete, cx)
4497 }
4498
4499 pub fn compose_completion(
4500 &mut self,
4501 action: &ComposeCompletion,
4502 cx: &mut ViewContext<Self>,
4503 ) -> Option<Task<Result<()>>> {
4504 self.do_completion(action.item_ix, CompletionIntent::Compose, cx)
4505 }
4506
4507 fn do_completion(
4508 &mut self,
4509 item_ix: Option<usize>,
4510 intent: CompletionIntent,
4511 cx: &mut ViewContext<Editor>,
4512 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
4513 use language::ToOffset as _;
4514
4515 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
4516 menu
4517 } else {
4518 return None;
4519 };
4520
4521 let mat = completions_menu
4522 .matches
4523 .get(item_ix.unwrap_or(completions_menu.selected_item))?;
4524 let buffer_handle = completions_menu.buffer;
4525 let completions = completions_menu.completions.read();
4526 let completion = completions.get(mat.candidate_id)?;
4527 cx.stop_propagation();
4528
4529 let snippet;
4530 let text;
4531
4532 if completion.is_snippet() {
4533 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4534 text = snippet.as_ref().unwrap().text.clone();
4535 } else {
4536 snippet = None;
4537 text = completion.new_text.clone();
4538 };
4539 let selections = self.selections.all::<usize>(cx);
4540 let buffer = buffer_handle.read(cx);
4541 let old_range = completion.old_range.to_offset(buffer);
4542 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4543
4544 let newest_selection = self.selections.newest_anchor();
4545 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4546 return None;
4547 }
4548
4549 let lookbehind = newest_selection
4550 .start
4551 .text_anchor
4552 .to_offset(buffer)
4553 .saturating_sub(old_range.start);
4554 let lookahead = old_range
4555 .end
4556 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4557 let mut common_prefix_len = old_text
4558 .bytes()
4559 .zip(text.bytes())
4560 .take_while(|(a, b)| a == b)
4561 .count();
4562
4563 let snapshot = self.buffer.read(cx).snapshot(cx);
4564 let mut range_to_replace: Option<Range<isize>> = None;
4565 let mut ranges = Vec::new();
4566 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4567 for selection in &selections {
4568 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4569 let start = selection.start.saturating_sub(lookbehind);
4570 let end = selection.end + lookahead;
4571 if selection.id == newest_selection.id {
4572 range_to_replace = Some(
4573 ((start + common_prefix_len) as isize - selection.start as isize)
4574 ..(end as isize - selection.start as isize),
4575 );
4576 }
4577 ranges.push(start + common_prefix_len..end);
4578 } else {
4579 common_prefix_len = 0;
4580 ranges.clear();
4581 ranges.extend(selections.iter().map(|s| {
4582 if s.id == newest_selection.id {
4583 range_to_replace = Some(
4584 old_range.start.to_offset_utf16(&snapshot).0 as isize
4585 - selection.start as isize
4586 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4587 - selection.start as isize,
4588 );
4589 old_range.clone()
4590 } else {
4591 s.start..s.end
4592 }
4593 }));
4594 break;
4595 }
4596 if !self.linked_edit_ranges.is_empty() {
4597 let start_anchor = snapshot.anchor_before(selection.head());
4598 let end_anchor = snapshot.anchor_after(selection.tail());
4599 if let Some(ranges) = self
4600 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4601 {
4602 for (buffer, edits) in ranges {
4603 linked_edits.entry(buffer.clone()).or_default().extend(
4604 edits
4605 .into_iter()
4606 .map(|range| (range, text[common_prefix_len..].to_owned())),
4607 );
4608 }
4609 }
4610 }
4611 }
4612 let text = &text[common_prefix_len..];
4613
4614 cx.emit(EditorEvent::InputHandled {
4615 utf16_range_to_replace: range_to_replace,
4616 text: text.into(),
4617 });
4618
4619 self.transact(cx, |this, cx| {
4620 if let Some(mut snippet) = snippet {
4621 snippet.text = text.to_string();
4622 for tabstop in snippet.tabstops.iter_mut().flatten() {
4623 tabstop.start -= common_prefix_len as isize;
4624 tabstop.end -= common_prefix_len as isize;
4625 }
4626
4627 this.insert_snippet(&ranges, snippet, cx).log_err();
4628 } else {
4629 this.buffer.update(cx, |buffer, cx| {
4630 buffer.edit(
4631 ranges.iter().map(|range| (range.clone(), text)),
4632 this.autoindent_mode.clone(),
4633 cx,
4634 );
4635 });
4636 }
4637 for (buffer, edits) in linked_edits {
4638 buffer.update(cx, |buffer, cx| {
4639 let snapshot = buffer.snapshot();
4640 let edits = edits
4641 .into_iter()
4642 .map(|(range, text)| {
4643 use text::ToPoint as TP;
4644 let end_point = TP::to_point(&range.end, &snapshot);
4645 let start_point = TP::to_point(&range.start, &snapshot);
4646 (start_point..end_point, text)
4647 })
4648 .sorted_by_key(|(range, _)| range.start)
4649 .collect::<Vec<_>>();
4650 buffer.edit(edits, None, cx);
4651 })
4652 }
4653
4654 this.refresh_inline_completion(true, false, cx);
4655 });
4656
4657 let show_new_completions_on_confirm = completion
4658 .confirm
4659 .as_ref()
4660 .map_or(false, |confirm| confirm(intent, cx));
4661 if show_new_completions_on_confirm {
4662 self.show_completions(&ShowCompletions { trigger: None }, cx);
4663 }
4664
4665 let provider = self.completion_provider.as_ref()?;
4666 let apply_edits = provider.apply_additional_edits_for_completion(
4667 buffer_handle,
4668 completion.clone(),
4669 true,
4670 cx,
4671 );
4672
4673 let editor_settings = EditorSettings::get_global(cx);
4674 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4675 // After the code completion is finished, users often want to know what signatures are needed.
4676 // so we should automatically call signature_help
4677 self.show_signature_help(&ShowSignatureHelp, cx);
4678 }
4679
4680 Some(cx.foreground_executor().spawn(async move {
4681 apply_edits.await?;
4682 Ok(())
4683 }))
4684 }
4685
4686 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
4687 let mut context_menu = self.context_menu.write();
4688 if let Some(ContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4689 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4690 // Toggle if we're selecting the same one
4691 *context_menu = None;
4692 cx.notify();
4693 return;
4694 } else {
4695 // Otherwise, clear it and start a new one
4696 *context_menu = None;
4697 cx.notify();
4698 }
4699 }
4700 drop(context_menu);
4701 let snapshot = self.snapshot(cx);
4702 let deployed_from_indicator = action.deployed_from_indicator;
4703 let mut task = self.code_actions_task.take();
4704 let action = action.clone();
4705 cx.spawn(|editor, mut cx| async move {
4706 while let Some(prev_task) = task {
4707 prev_task.await.log_err();
4708 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4709 }
4710
4711 let spawned_test_task = editor.update(&mut cx, |editor, cx| {
4712 if editor.focus_handle.is_focused(cx) {
4713 let multibuffer_point = action
4714 .deployed_from_indicator
4715 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4716 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4717 let (buffer, buffer_row) = snapshot
4718 .buffer_snapshot
4719 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4720 .and_then(|(buffer_snapshot, range)| {
4721 editor
4722 .buffer
4723 .read(cx)
4724 .buffer(buffer_snapshot.remote_id())
4725 .map(|buffer| (buffer, range.start.row))
4726 })?;
4727 let (_, code_actions) = editor
4728 .available_code_actions
4729 .clone()
4730 .and_then(|(location, code_actions)| {
4731 let snapshot = location.buffer.read(cx).snapshot();
4732 let point_range = location.range.to_point(&snapshot);
4733 let point_range = point_range.start.row..=point_range.end.row;
4734 if point_range.contains(&buffer_row) {
4735 Some((location, code_actions))
4736 } else {
4737 None
4738 }
4739 })
4740 .unzip();
4741 let buffer_id = buffer.read(cx).remote_id();
4742 let tasks = editor
4743 .tasks
4744 .get(&(buffer_id, buffer_row))
4745 .map(|t| Arc::new(t.to_owned()));
4746 if tasks.is_none() && code_actions.is_none() {
4747 return None;
4748 }
4749
4750 editor.completion_tasks.clear();
4751 editor.discard_inline_completion(false, cx);
4752 let task_context =
4753 tasks
4754 .as_ref()
4755 .zip(editor.project.clone())
4756 .map(|(tasks, project)| {
4757 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4758 });
4759
4760 Some(cx.spawn(|editor, mut cx| async move {
4761 let task_context = match task_context {
4762 Some(task_context) => task_context.await,
4763 None => None,
4764 };
4765 let resolved_tasks =
4766 tasks.zip(task_context).map(|(tasks, task_context)| {
4767 Arc::new(ResolvedTasks {
4768 templates: tasks.resolve(&task_context).collect(),
4769 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4770 multibuffer_point.row,
4771 tasks.column,
4772 )),
4773 })
4774 });
4775 let spawn_straight_away = resolved_tasks
4776 .as_ref()
4777 .map_or(false, |tasks| tasks.templates.len() == 1)
4778 && code_actions
4779 .as_ref()
4780 .map_or(true, |actions| actions.is_empty());
4781 if let Ok(task) = editor.update(&mut cx, |editor, cx| {
4782 *editor.context_menu.write() =
4783 Some(ContextMenu::CodeActions(CodeActionsMenu {
4784 buffer,
4785 actions: CodeActionContents {
4786 tasks: resolved_tasks,
4787 actions: code_actions,
4788 },
4789 selected_item: Default::default(),
4790 scroll_handle: UniformListScrollHandle::default(),
4791 deployed_from_indicator,
4792 }));
4793 if spawn_straight_away {
4794 if let Some(task) = editor.confirm_code_action(
4795 &ConfirmCodeAction { item_ix: Some(0) },
4796 cx,
4797 ) {
4798 cx.notify();
4799 return task;
4800 }
4801 }
4802 cx.notify();
4803 Task::ready(Ok(()))
4804 }) {
4805 task.await
4806 } else {
4807 Ok(())
4808 }
4809 }))
4810 } else {
4811 Some(Task::ready(Ok(())))
4812 }
4813 })?;
4814 if let Some(task) = spawned_test_task {
4815 task.await?;
4816 }
4817
4818 Ok::<_, anyhow::Error>(())
4819 })
4820 .detach_and_log_err(cx);
4821 }
4822
4823 pub fn confirm_code_action(
4824 &mut self,
4825 action: &ConfirmCodeAction,
4826 cx: &mut ViewContext<Self>,
4827 ) -> Option<Task<Result<()>>> {
4828 let actions_menu = if let ContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
4829 menu
4830 } else {
4831 return None;
4832 };
4833 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4834 let action = actions_menu.actions.get(action_ix)?;
4835 let title = action.label();
4836 let buffer = actions_menu.buffer;
4837 let workspace = self.workspace()?;
4838
4839 match action {
4840 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4841 workspace.update(cx, |workspace, cx| {
4842 workspace::tasks::schedule_resolved_task(
4843 workspace,
4844 task_source_kind,
4845 resolved_task,
4846 false,
4847 cx,
4848 );
4849
4850 Some(Task::ready(Ok(())))
4851 })
4852 }
4853 CodeActionsItem::CodeAction {
4854 excerpt_id,
4855 action,
4856 provider,
4857 } => {
4858 let apply_code_action =
4859 provider.apply_code_action(buffer, action, excerpt_id, true, cx);
4860 let workspace = workspace.downgrade();
4861 Some(cx.spawn(|editor, cx| async move {
4862 let project_transaction = apply_code_action.await?;
4863 Self::open_project_transaction(
4864 &editor,
4865 workspace,
4866 project_transaction,
4867 title,
4868 cx,
4869 )
4870 .await
4871 }))
4872 }
4873 }
4874 }
4875
4876 pub async fn open_project_transaction(
4877 this: &WeakView<Editor>,
4878 workspace: WeakView<Workspace>,
4879 transaction: ProjectTransaction,
4880 title: String,
4881 mut cx: AsyncWindowContext,
4882 ) -> Result<()> {
4883 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4884 cx.update(|cx| {
4885 entries.sort_unstable_by_key(|(buffer, _)| {
4886 buffer.read(cx).file().map(|f| f.path().clone())
4887 });
4888 })?;
4889
4890 // If the project transaction's edits are all contained within this editor, then
4891 // avoid opening a new editor to display them.
4892
4893 if let Some((buffer, transaction)) = entries.first() {
4894 if entries.len() == 1 {
4895 let excerpt = this.update(&mut cx, |editor, cx| {
4896 editor
4897 .buffer()
4898 .read(cx)
4899 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4900 })?;
4901 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4902 if excerpted_buffer == *buffer {
4903 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4904 let excerpt_range = excerpt_range.to_offset(buffer);
4905 buffer
4906 .edited_ranges_for_transaction::<usize>(transaction)
4907 .all(|range| {
4908 excerpt_range.start <= range.start
4909 && excerpt_range.end >= range.end
4910 })
4911 })?;
4912
4913 if all_edits_within_excerpt {
4914 return Ok(());
4915 }
4916 }
4917 }
4918 }
4919 } else {
4920 return Ok(());
4921 }
4922
4923 let mut ranges_to_highlight = Vec::new();
4924 let excerpt_buffer = cx.new_model(|cx| {
4925 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4926 for (buffer_handle, transaction) in &entries {
4927 let buffer = buffer_handle.read(cx);
4928 ranges_to_highlight.extend(
4929 multibuffer.push_excerpts_with_context_lines(
4930 buffer_handle.clone(),
4931 buffer
4932 .edited_ranges_for_transaction::<usize>(transaction)
4933 .collect(),
4934 DEFAULT_MULTIBUFFER_CONTEXT,
4935 cx,
4936 ),
4937 );
4938 }
4939 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4940 multibuffer
4941 })?;
4942
4943 workspace.update(&mut cx, |workspace, cx| {
4944 let project = workspace.project().clone();
4945 let editor =
4946 cx.new_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, cx));
4947 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
4948 editor.update(cx, |editor, cx| {
4949 editor.highlight_background::<Self>(
4950 &ranges_to_highlight,
4951 |theme| theme.editor_highlighted_line_background,
4952 cx,
4953 );
4954 });
4955 })?;
4956
4957 Ok(())
4958 }
4959
4960 pub fn clear_code_action_providers(&mut self) {
4961 self.code_action_providers.clear();
4962 self.available_code_actions.take();
4963 }
4964
4965 pub fn push_code_action_provider(
4966 &mut self,
4967 provider: Arc<dyn CodeActionProvider>,
4968 cx: &mut ViewContext<Self>,
4969 ) {
4970 self.code_action_providers.push(provider);
4971 self.refresh_code_actions(cx);
4972 }
4973
4974 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4975 let buffer = self.buffer.read(cx);
4976 let newest_selection = self.selections.newest_anchor().clone();
4977 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4978 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4979 if start_buffer != end_buffer {
4980 return None;
4981 }
4982
4983 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
4984 cx.background_executor()
4985 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4986 .await;
4987
4988 let (providers, tasks) = this.update(&mut cx, |this, cx| {
4989 let providers = this.code_action_providers.clone();
4990 let tasks = this
4991 .code_action_providers
4992 .iter()
4993 .map(|provider| provider.code_actions(&start_buffer, start..end, cx))
4994 .collect::<Vec<_>>();
4995 (providers, tasks)
4996 })?;
4997
4998 let mut actions = Vec::new();
4999 for (provider, provider_actions) in
5000 providers.into_iter().zip(future::join_all(tasks).await)
5001 {
5002 if let Some(provider_actions) = provider_actions.log_err() {
5003 actions.extend(provider_actions.into_iter().map(|action| {
5004 AvailableCodeAction {
5005 excerpt_id: newest_selection.start.excerpt_id,
5006 action,
5007 provider: provider.clone(),
5008 }
5009 }));
5010 }
5011 }
5012
5013 this.update(&mut cx, |this, cx| {
5014 this.available_code_actions = if actions.is_empty() {
5015 None
5016 } else {
5017 Some((
5018 Location {
5019 buffer: start_buffer,
5020 range: start..end,
5021 },
5022 actions.into(),
5023 ))
5024 };
5025 cx.notify();
5026 })
5027 }));
5028 None
5029 }
5030
5031 fn start_inline_blame_timer(&mut self, cx: &mut ViewContext<Self>) {
5032 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
5033 self.show_git_blame_inline = false;
5034
5035 self.show_git_blame_inline_delay_task = Some(cx.spawn(|this, mut cx| async move {
5036 cx.background_executor().timer(delay).await;
5037
5038 this.update(&mut cx, |this, cx| {
5039 this.show_git_blame_inline = true;
5040 cx.notify();
5041 })
5042 .log_err();
5043 }));
5044 }
5045 }
5046
5047 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
5048 if self.pending_rename.is_some() {
5049 return None;
5050 }
5051
5052 let provider = self.semantics_provider.clone()?;
5053 let buffer = self.buffer.read(cx);
5054 let newest_selection = self.selections.newest_anchor().clone();
5055 let cursor_position = newest_selection.head();
5056 let (cursor_buffer, cursor_buffer_position) =
5057 buffer.text_anchor_for_position(cursor_position, cx)?;
5058 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
5059 if cursor_buffer != tail_buffer {
5060 return None;
5061 }
5062
5063 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
5064 cx.background_executor()
5065 .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT)
5066 .await;
5067
5068 let highlights = if let Some(highlights) = cx
5069 .update(|cx| {
5070 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
5071 })
5072 .ok()
5073 .flatten()
5074 {
5075 highlights.await.log_err()
5076 } else {
5077 None
5078 };
5079
5080 if let Some(highlights) = highlights {
5081 this.update(&mut cx, |this, cx| {
5082 if this.pending_rename.is_some() {
5083 return;
5084 }
5085
5086 let buffer_id = cursor_position.buffer_id;
5087 let buffer = this.buffer.read(cx);
5088 if !buffer
5089 .text_anchor_for_position(cursor_position, cx)
5090 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
5091 {
5092 return;
5093 }
5094
5095 let cursor_buffer_snapshot = cursor_buffer.read(cx);
5096 let mut write_ranges = Vec::new();
5097 let mut read_ranges = Vec::new();
5098 for highlight in highlights {
5099 for (excerpt_id, excerpt_range) in
5100 buffer.excerpts_for_buffer(&cursor_buffer, cx)
5101 {
5102 let start = highlight
5103 .range
5104 .start
5105 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
5106 let end = highlight
5107 .range
5108 .end
5109 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
5110 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
5111 continue;
5112 }
5113
5114 let range = Anchor {
5115 buffer_id,
5116 excerpt_id,
5117 text_anchor: start,
5118 }..Anchor {
5119 buffer_id,
5120 excerpt_id,
5121 text_anchor: end,
5122 };
5123 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
5124 write_ranges.push(range);
5125 } else {
5126 read_ranges.push(range);
5127 }
5128 }
5129 }
5130
5131 this.highlight_background::<DocumentHighlightRead>(
5132 &read_ranges,
5133 |theme| theme.editor_document_highlight_read_background,
5134 cx,
5135 );
5136 this.highlight_background::<DocumentHighlightWrite>(
5137 &write_ranges,
5138 |theme| theme.editor_document_highlight_write_background,
5139 cx,
5140 );
5141 cx.notify();
5142 })
5143 .log_err();
5144 }
5145 }));
5146 None
5147 }
5148
5149 pub fn refresh_inline_completion(
5150 &mut self,
5151 debounce: bool,
5152 user_requested: bool,
5153 cx: &mut ViewContext<Self>,
5154 ) -> Option<()> {
5155 let provider = self.inline_completion_provider()?;
5156 let cursor = self.selections.newest_anchor().head();
5157 let (buffer, cursor_buffer_position) =
5158 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5159
5160 if !user_requested
5161 && (!self.enable_inline_completions
5162 || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx))
5163 {
5164 self.discard_inline_completion(false, cx);
5165 return None;
5166 }
5167
5168 self.update_visible_inline_completion(cx);
5169 provider.refresh(buffer, cursor_buffer_position, debounce, cx);
5170 Some(())
5171 }
5172
5173 fn cycle_inline_completion(
5174 &mut self,
5175 direction: Direction,
5176 cx: &mut ViewContext<Self>,
5177 ) -> Option<()> {
5178 let provider = self.inline_completion_provider()?;
5179 let cursor = self.selections.newest_anchor().head();
5180 let (buffer, cursor_buffer_position) =
5181 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5182 if !self.enable_inline_completions
5183 || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
5184 {
5185 return None;
5186 }
5187
5188 provider.cycle(buffer, cursor_buffer_position, direction, cx);
5189 self.update_visible_inline_completion(cx);
5190
5191 Some(())
5192 }
5193
5194 pub fn show_inline_completion(&mut self, _: &ShowInlineCompletion, cx: &mut ViewContext<Self>) {
5195 if !self.has_active_inline_completion(cx) {
5196 self.refresh_inline_completion(false, true, cx);
5197 return;
5198 }
5199
5200 self.update_visible_inline_completion(cx);
5201 }
5202
5203 pub fn display_cursor_names(&mut self, _: &DisplayCursorNames, cx: &mut ViewContext<Self>) {
5204 self.show_cursor_names(cx);
5205 }
5206
5207 fn show_cursor_names(&mut self, cx: &mut ViewContext<Self>) {
5208 self.show_cursor_names = true;
5209 cx.notify();
5210 cx.spawn(|this, mut cx| async move {
5211 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5212 this.update(&mut cx, |this, cx| {
5213 this.show_cursor_names = false;
5214 cx.notify()
5215 })
5216 .ok()
5217 })
5218 .detach();
5219 }
5220
5221 pub fn next_inline_completion(&mut self, _: &NextInlineCompletion, cx: &mut ViewContext<Self>) {
5222 if self.has_active_inline_completion(cx) {
5223 self.cycle_inline_completion(Direction::Next, cx);
5224 } else {
5225 let is_copilot_disabled = self.refresh_inline_completion(false, true, cx).is_none();
5226 if is_copilot_disabled {
5227 cx.propagate();
5228 }
5229 }
5230 }
5231
5232 pub fn previous_inline_completion(
5233 &mut self,
5234 _: &PreviousInlineCompletion,
5235 cx: &mut ViewContext<Self>,
5236 ) {
5237 if self.has_active_inline_completion(cx) {
5238 self.cycle_inline_completion(Direction::Prev, cx);
5239 } else {
5240 let is_copilot_disabled = self.refresh_inline_completion(false, true, cx).is_none();
5241 if is_copilot_disabled {
5242 cx.propagate();
5243 }
5244 }
5245 }
5246
5247 pub fn accept_inline_completion(
5248 &mut self,
5249 _: &AcceptInlineCompletion,
5250 cx: &mut ViewContext<Self>,
5251 ) {
5252 let Some(completion) = self.take_active_inline_completion(cx) else {
5253 return;
5254 };
5255 if let Some(provider) = self.inline_completion_provider() {
5256 provider.accept(cx);
5257 }
5258
5259 cx.emit(EditorEvent::InputHandled {
5260 utf16_range_to_replace: None,
5261 text: completion.text.to_string().into(),
5262 });
5263
5264 if let Some(range) = completion.delete_range {
5265 self.change_selections(None, cx, |s| s.select_ranges([range]))
5266 }
5267 self.insert_with_autoindent_mode(&completion.text.to_string(), None, cx);
5268 self.refresh_inline_completion(true, true, cx);
5269 cx.notify();
5270 }
5271
5272 pub fn accept_partial_inline_completion(
5273 &mut self,
5274 _: &AcceptPartialInlineCompletion,
5275 cx: &mut ViewContext<Self>,
5276 ) {
5277 if self.selections.count() == 1 && self.has_active_inline_completion(cx) {
5278 if let Some(completion) = self.take_active_inline_completion(cx) {
5279 let mut partial_completion = completion
5280 .text
5281 .chars()
5282 .by_ref()
5283 .take_while(|c| c.is_alphabetic())
5284 .collect::<String>();
5285 if partial_completion.is_empty() {
5286 partial_completion = completion
5287 .text
5288 .chars()
5289 .by_ref()
5290 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5291 .collect::<String>();
5292 }
5293
5294 cx.emit(EditorEvent::InputHandled {
5295 utf16_range_to_replace: None,
5296 text: partial_completion.clone().into(),
5297 });
5298
5299 if let Some(range) = completion.delete_range {
5300 self.change_selections(None, cx, |s| s.select_ranges([range]))
5301 }
5302 self.insert_with_autoindent_mode(&partial_completion, None, cx);
5303
5304 self.refresh_inline_completion(true, true, cx);
5305 cx.notify();
5306 }
5307 }
5308 }
5309
5310 fn discard_inline_completion(
5311 &mut self,
5312 should_report_inline_completion_event: bool,
5313 cx: &mut ViewContext<Self>,
5314 ) -> bool {
5315 if let Some(provider) = self.inline_completion_provider() {
5316 provider.discard(should_report_inline_completion_event, cx);
5317 }
5318
5319 self.take_active_inline_completion(cx).is_some()
5320 }
5321
5322 pub fn has_active_inline_completion(&self, cx: &AppContext) -> bool {
5323 if let Some(completion) = self.active_inline_completion.as_ref() {
5324 let buffer = self.buffer.read(cx).read(cx);
5325 completion.position.is_valid(&buffer)
5326 } else {
5327 false
5328 }
5329 }
5330
5331 fn take_active_inline_completion(
5332 &mut self,
5333 cx: &mut ViewContext<Self>,
5334 ) -> Option<CompletionState> {
5335 let completion = self.active_inline_completion.take()?;
5336 let render_inlay_ids = completion.render_inlay_ids.clone();
5337 self.display_map.update(cx, |map, cx| {
5338 map.splice_inlays(render_inlay_ids, Default::default(), cx);
5339 });
5340 let buffer = self.buffer.read(cx).read(cx);
5341
5342 if completion.position.is_valid(&buffer) {
5343 Some(completion)
5344 } else {
5345 None
5346 }
5347 }
5348
5349 fn update_visible_inline_completion(&mut self, cx: &mut ViewContext<Self>) {
5350 let selection = self.selections.newest_anchor();
5351 let cursor = selection.head();
5352
5353 let excerpt_id = cursor.excerpt_id;
5354
5355 if self.context_menu.read().is_none()
5356 && self.completion_tasks.is_empty()
5357 && selection.start == selection.end
5358 {
5359 if let Some(provider) = self.inline_completion_provider() {
5360 if let Some((buffer, cursor_buffer_position)) =
5361 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5362 {
5363 if let Some(proposal) =
5364 provider.active_completion_text(&buffer, cursor_buffer_position, cx)
5365 {
5366 let mut to_remove = Vec::new();
5367 if let Some(completion) = self.active_inline_completion.take() {
5368 to_remove.extend(completion.render_inlay_ids.iter());
5369 }
5370
5371 let to_add = proposal
5372 .inlays
5373 .iter()
5374 .filter_map(|inlay| {
5375 let snapshot = self.buffer.read(cx).snapshot(cx);
5376 let id = post_inc(&mut self.next_inlay_id);
5377 match inlay {
5378 InlayProposal::Hint(position, hint) => {
5379 let position =
5380 snapshot.anchor_in_excerpt(excerpt_id, *position)?;
5381 Some(Inlay::hint(id, position, hint))
5382 }
5383 InlayProposal::Suggestion(position, text) => {
5384 let position =
5385 snapshot.anchor_in_excerpt(excerpt_id, *position)?;
5386 Some(Inlay::suggestion(id, position, text.clone()))
5387 }
5388 }
5389 })
5390 .collect_vec();
5391
5392 self.active_inline_completion = Some(CompletionState {
5393 position: cursor,
5394 text: proposal.text,
5395 delete_range: proposal.delete_range.and_then(|range| {
5396 let snapshot = self.buffer.read(cx).snapshot(cx);
5397 let start = snapshot.anchor_in_excerpt(excerpt_id, range.start);
5398 let end = snapshot.anchor_in_excerpt(excerpt_id, range.end);
5399 Some(start?..end?)
5400 }),
5401 render_inlay_ids: to_add.iter().map(|i| i.id).collect(),
5402 });
5403
5404 self.display_map
5405 .update(cx, move |map, cx| map.splice_inlays(to_remove, to_add, cx));
5406
5407 cx.notify();
5408 return;
5409 }
5410 }
5411 }
5412 }
5413
5414 self.discard_inline_completion(false, cx);
5415 }
5416
5417 fn inline_completion_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5418 Some(self.inline_completion_provider.as_ref()?.provider.clone())
5419 }
5420
5421 fn render_code_actions_indicator(
5422 &self,
5423 _style: &EditorStyle,
5424 row: DisplayRow,
5425 is_active: bool,
5426 cx: &mut ViewContext<Self>,
5427 ) -> Option<IconButton> {
5428 if self.available_code_actions.is_some() {
5429 Some(
5430 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5431 .shape(ui::IconButtonShape::Square)
5432 .icon_size(IconSize::XSmall)
5433 .icon_color(Color::Muted)
5434 .selected(is_active)
5435 .tooltip({
5436 let focus_handle = self.focus_handle.clone();
5437 move |cx| {
5438 Tooltip::for_action_in(
5439 "Toggle Code Actions",
5440 &ToggleCodeActions {
5441 deployed_from_indicator: None,
5442 },
5443 &focus_handle,
5444 cx,
5445 )
5446 }
5447 })
5448 .on_click(cx.listener(move |editor, _e, cx| {
5449 editor.focus(cx);
5450 editor.toggle_code_actions(
5451 &ToggleCodeActions {
5452 deployed_from_indicator: Some(row),
5453 },
5454 cx,
5455 );
5456 })),
5457 )
5458 } else {
5459 None
5460 }
5461 }
5462
5463 fn clear_tasks(&mut self) {
5464 self.tasks.clear()
5465 }
5466
5467 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5468 if self.tasks.insert(key, value).is_some() {
5469 // This case should hopefully be rare, but just in case...
5470 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5471 }
5472 }
5473
5474 fn build_tasks_context(
5475 project: &Model<Project>,
5476 buffer: &Model<Buffer>,
5477 buffer_row: u32,
5478 tasks: &Arc<RunnableTasks>,
5479 cx: &mut ViewContext<Self>,
5480 ) -> Task<Option<task::TaskContext>> {
5481 let position = Point::new(buffer_row, tasks.column);
5482 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
5483 let location = Location {
5484 buffer: buffer.clone(),
5485 range: range_start..range_start,
5486 };
5487 // Fill in the environmental variables from the tree-sitter captures
5488 let mut captured_task_variables = TaskVariables::default();
5489 for (capture_name, value) in tasks.extra_variables.clone() {
5490 captured_task_variables.insert(
5491 task::VariableName::Custom(capture_name.into()),
5492 value.clone(),
5493 );
5494 }
5495 project.update(cx, |project, cx| {
5496 project.task_store().update(cx, |task_store, cx| {
5497 task_store.task_context_for_location(captured_task_variables, location, cx)
5498 })
5499 })
5500 }
5501
5502 pub fn spawn_nearest_task(&mut self, action: &SpawnNearestTask, cx: &mut ViewContext<Self>) {
5503 let Some((workspace, _)) = self.workspace.clone() else {
5504 return;
5505 };
5506 let Some(project) = self.project.clone() else {
5507 return;
5508 };
5509
5510 // Try to find a closest, enclosing node using tree-sitter that has a
5511 // task
5512 let Some((buffer, buffer_row, tasks)) = self
5513 .find_enclosing_node_task(cx)
5514 // Or find the task that's closest in row-distance.
5515 .or_else(|| self.find_closest_task(cx))
5516 else {
5517 return;
5518 };
5519
5520 let reveal_strategy = action.reveal;
5521 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
5522 cx.spawn(|_, mut cx| async move {
5523 let context = task_context.await?;
5524 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
5525
5526 let resolved = resolved_task.resolved.as_mut()?;
5527 resolved.reveal = reveal_strategy;
5528
5529 workspace
5530 .update(&mut cx, |workspace, cx| {
5531 workspace::tasks::schedule_resolved_task(
5532 workspace,
5533 task_source_kind,
5534 resolved_task,
5535 false,
5536 cx,
5537 );
5538 })
5539 .ok()
5540 })
5541 .detach();
5542 }
5543
5544 fn find_closest_task(
5545 &mut self,
5546 cx: &mut ViewContext<Self>,
5547 ) -> Option<(Model<Buffer>, u32, Arc<RunnableTasks>)> {
5548 let cursor_row = self.selections.newest_adjusted(cx).head().row;
5549
5550 let ((buffer_id, row), tasks) = self
5551 .tasks
5552 .iter()
5553 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
5554
5555 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
5556 let tasks = Arc::new(tasks.to_owned());
5557 Some((buffer, *row, tasks))
5558 }
5559
5560 fn find_enclosing_node_task(
5561 &mut self,
5562 cx: &mut ViewContext<Self>,
5563 ) -> Option<(Model<Buffer>, u32, Arc<RunnableTasks>)> {
5564 let snapshot = self.buffer.read(cx).snapshot(cx);
5565 let offset = self.selections.newest::<usize>(cx).head();
5566 let excerpt = snapshot.excerpt_containing(offset..offset)?;
5567 let buffer_id = excerpt.buffer().remote_id();
5568
5569 let layer = excerpt.buffer().syntax_layer_at(offset)?;
5570 let mut cursor = layer.node().walk();
5571
5572 while cursor.goto_first_child_for_byte(offset).is_some() {
5573 if cursor.node().end_byte() == offset {
5574 cursor.goto_next_sibling();
5575 }
5576 }
5577
5578 // Ascend to the smallest ancestor that contains the range and has a task.
5579 loop {
5580 let node = cursor.node();
5581 let node_range = node.byte_range();
5582 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
5583
5584 // Check if this node contains our offset
5585 if node_range.start <= offset && node_range.end >= offset {
5586 // If it contains offset, check for task
5587 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
5588 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
5589 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
5590 }
5591 }
5592
5593 if !cursor.goto_parent() {
5594 break;
5595 }
5596 }
5597 None
5598 }
5599
5600 fn render_run_indicator(
5601 &self,
5602 _style: &EditorStyle,
5603 is_active: bool,
5604 row: DisplayRow,
5605 cx: &mut ViewContext<Self>,
5606 ) -> IconButton {
5607 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5608 .shape(ui::IconButtonShape::Square)
5609 .icon_size(IconSize::XSmall)
5610 .icon_color(Color::Muted)
5611 .selected(is_active)
5612 .on_click(cx.listener(move |editor, _e, cx| {
5613 editor.focus(cx);
5614 editor.toggle_code_actions(
5615 &ToggleCodeActions {
5616 deployed_from_indicator: Some(row),
5617 },
5618 cx,
5619 );
5620 }))
5621 }
5622
5623 pub fn context_menu_visible(&self) -> bool {
5624 self.context_menu
5625 .read()
5626 .as_ref()
5627 .map_or(false, |menu| menu.visible())
5628 }
5629
5630 fn render_context_menu(
5631 &self,
5632 cursor_position: DisplayPoint,
5633 style: &EditorStyle,
5634 max_height: Pixels,
5635 cx: &mut ViewContext<Editor>,
5636 ) -> Option<(ContextMenuOrigin, AnyElement)> {
5637 self.context_menu.read().as_ref().map(|menu| {
5638 menu.render(
5639 cursor_position,
5640 style,
5641 max_height,
5642 self.workspace.as_ref().map(|(w, _)| w.clone()),
5643 cx,
5644 )
5645 })
5646 }
5647
5648 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
5649 cx.notify();
5650 self.completion_tasks.clear();
5651 let context_menu = self.context_menu.write().take();
5652 if context_menu.is_some() {
5653 self.update_visible_inline_completion(cx);
5654 }
5655 context_menu
5656 }
5657
5658 pub fn insert_snippet(
5659 &mut self,
5660 insertion_ranges: &[Range<usize>],
5661 snippet: Snippet,
5662 cx: &mut ViewContext<Self>,
5663 ) -> Result<()> {
5664 struct Tabstop<T> {
5665 is_end_tabstop: bool,
5666 ranges: Vec<Range<T>>,
5667 }
5668
5669 let tabstops = self.buffer.update(cx, |buffer, cx| {
5670 let snippet_text: Arc<str> = snippet.text.clone().into();
5671 buffer.edit(
5672 insertion_ranges
5673 .iter()
5674 .cloned()
5675 .map(|range| (range, snippet_text.clone())),
5676 Some(AutoindentMode::EachLine),
5677 cx,
5678 );
5679
5680 let snapshot = &*buffer.read(cx);
5681 let snippet = &snippet;
5682 snippet
5683 .tabstops
5684 .iter()
5685 .map(|tabstop| {
5686 let is_end_tabstop = tabstop.first().map_or(false, |tabstop| {
5687 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
5688 });
5689 let mut tabstop_ranges = tabstop
5690 .iter()
5691 .flat_map(|tabstop_range| {
5692 let mut delta = 0_isize;
5693 insertion_ranges.iter().map(move |insertion_range| {
5694 let insertion_start = insertion_range.start as isize + delta;
5695 delta +=
5696 snippet.text.len() as isize - insertion_range.len() as isize;
5697
5698 let start = ((insertion_start + tabstop_range.start) as usize)
5699 .min(snapshot.len());
5700 let end = ((insertion_start + tabstop_range.end) as usize)
5701 .min(snapshot.len());
5702 snapshot.anchor_before(start)..snapshot.anchor_after(end)
5703 })
5704 })
5705 .collect::<Vec<_>>();
5706 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
5707
5708 Tabstop {
5709 is_end_tabstop,
5710 ranges: tabstop_ranges,
5711 }
5712 })
5713 .collect::<Vec<_>>()
5714 });
5715 if let Some(tabstop) = tabstops.first() {
5716 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5717 s.select_ranges(tabstop.ranges.iter().cloned());
5718 });
5719
5720 // If we're already at the last tabstop and it's at the end of the snippet,
5721 // we're done, we don't need to keep the state around.
5722 if !tabstop.is_end_tabstop {
5723 let ranges = tabstops
5724 .into_iter()
5725 .map(|tabstop| tabstop.ranges)
5726 .collect::<Vec<_>>();
5727 self.snippet_stack.push(SnippetState {
5728 active_index: 0,
5729 ranges,
5730 });
5731 }
5732
5733 // Check whether the just-entered snippet ends with an auto-closable bracket.
5734 if self.autoclose_regions.is_empty() {
5735 let snapshot = self.buffer.read(cx).snapshot(cx);
5736 for selection in &mut self.selections.all::<Point>(cx) {
5737 let selection_head = selection.head();
5738 let Some(scope) = snapshot.language_scope_at(selection_head) else {
5739 continue;
5740 };
5741
5742 let mut bracket_pair = None;
5743 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
5744 let prev_chars = snapshot
5745 .reversed_chars_at(selection_head)
5746 .collect::<String>();
5747 for (pair, enabled) in scope.brackets() {
5748 if enabled
5749 && pair.close
5750 && prev_chars.starts_with(pair.start.as_str())
5751 && next_chars.starts_with(pair.end.as_str())
5752 {
5753 bracket_pair = Some(pair.clone());
5754 break;
5755 }
5756 }
5757 if let Some(pair) = bracket_pair {
5758 let start = snapshot.anchor_after(selection_head);
5759 let end = snapshot.anchor_after(selection_head);
5760 self.autoclose_regions.push(AutocloseRegion {
5761 selection_id: selection.id,
5762 range: start..end,
5763 pair,
5764 });
5765 }
5766 }
5767 }
5768 }
5769 Ok(())
5770 }
5771
5772 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5773 self.move_to_snippet_tabstop(Bias::Right, cx)
5774 }
5775
5776 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5777 self.move_to_snippet_tabstop(Bias::Left, cx)
5778 }
5779
5780 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
5781 if let Some(mut snippet) = self.snippet_stack.pop() {
5782 match bias {
5783 Bias::Left => {
5784 if snippet.active_index > 0 {
5785 snippet.active_index -= 1;
5786 } else {
5787 self.snippet_stack.push(snippet);
5788 return false;
5789 }
5790 }
5791 Bias::Right => {
5792 if snippet.active_index + 1 < snippet.ranges.len() {
5793 snippet.active_index += 1;
5794 } else {
5795 self.snippet_stack.push(snippet);
5796 return false;
5797 }
5798 }
5799 }
5800 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
5801 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5802 s.select_anchor_ranges(current_ranges.iter().cloned())
5803 });
5804 // If snippet state is not at the last tabstop, push it back on the stack
5805 if snippet.active_index + 1 < snippet.ranges.len() {
5806 self.snippet_stack.push(snippet);
5807 }
5808 return true;
5809 }
5810 }
5811
5812 false
5813 }
5814
5815 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
5816 self.transact(cx, |this, cx| {
5817 this.select_all(&SelectAll, cx);
5818 this.insert("", cx);
5819 });
5820 }
5821
5822 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
5823 self.transact(cx, |this, cx| {
5824 this.select_autoclose_pair(cx);
5825 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
5826 if !this.linked_edit_ranges.is_empty() {
5827 let selections = this.selections.all::<MultiBufferPoint>(cx);
5828 let snapshot = this.buffer.read(cx).snapshot(cx);
5829
5830 for selection in selections.iter() {
5831 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
5832 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
5833 if selection_start.buffer_id != selection_end.buffer_id {
5834 continue;
5835 }
5836 if let Some(ranges) =
5837 this.linked_editing_ranges_for(selection_start..selection_end, cx)
5838 {
5839 for (buffer, entries) in ranges {
5840 linked_ranges.entry(buffer).or_default().extend(entries);
5841 }
5842 }
5843 }
5844 }
5845
5846 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
5847 if !this.selections.line_mode {
5848 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
5849 for selection in &mut selections {
5850 if selection.is_empty() {
5851 let old_head = selection.head();
5852 let mut new_head =
5853 movement::left(&display_map, old_head.to_display_point(&display_map))
5854 .to_point(&display_map);
5855 if let Some((buffer, line_buffer_range)) = display_map
5856 .buffer_snapshot
5857 .buffer_line_for_row(MultiBufferRow(old_head.row))
5858 {
5859 let indent_size =
5860 buffer.indent_size_for_line(line_buffer_range.start.row);
5861 let indent_len = match indent_size.kind {
5862 IndentKind::Space => {
5863 buffer.settings_at(line_buffer_range.start, cx).tab_size
5864 }
5865 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
5866 };
5867 if old_head.column <= indent_size.len && old_head.column > 0 {
5868 let indent_len = indent_len.get();
5869 new_head = cmp::min(
5870 new_head,
5871 MultiBufferPoint::new(
5872 old_head.row,
5873 ((old_head.column - 1) / indent_len) * indent_len,
5874 ),
5875 );
5876 }
5877 }
5878
5879 selection.set_head(new_head, SelectionGoal::None);
5880 }
5881 }
5882 }
5883
5884 this.signature_help_state.set_backspace_pressed(true);
5885 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5886 this.insert("", cx);
5887 let empty_str: Arc<str> = Arc::from("");
5888 for (buffer, edits) in linked_ranges {
5889 let snapshot = buffer.read(cx).snapshot();
5890 use text::ToPoint as TP;
5891
5892 let edits = edits
5893 .into_iter()
5894 .map(|range| {
5895 let end_point = TP::to_point(&range.end, &snapshot);
5896 let mut start_point = TP::to_point(&range.start, &snapshot);
5897
5898 if end_point == start_point {
5899 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
5900 .saturating_sub(1);
5901 start_point = TP::to_point(&offset, &snapshot);
5902 };
5903
5904 (start_point..end_point, empty_str.clone())
5905 })
5906 .sorted_by_key(|(range, _)| range.start)
5907 .collect::<Vec<_>>();
5908 buffer.update(cx, |this, cx| {
5909 this.edit(edits, None, cx);
5910 })
5911 }
5912 this.refresh_inline_completion(true, false, cx);
5913 linked_editing_ranges::refresh_linked_ranges(this, cx);
5914 });
5915 }
5916
5917 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
5918 self.transact(cx, |this, cx| {
5919 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5920 let line_mode = s.line_mode;
5921 s.move_with(|map, selection| {
5922 if selection.is_empty() && !line_mode {
5923 let cursor = movement::right(map, selection.head());
5924 selection.end = cursor;
5925 selection.reversed = true;
5926 selection.goal = SelectionGoal::None;
5927 }
5928 })
5929 });
5930 this.insert("", cx);
5931 this.refresh_inline_completion(true, false, cx);
5932 });
5933 }
5934
5935 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
5936 if self.move_to_prev_snippet_tabstop(cx) {
5937 return;
5938 }
5939
5940 self.outdent(&Outdent, cx);
5941 }
5942
5943 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
5944 if self.move_to_next_snippet_tabstop(cx) || self.read_only(cx) {
5945 return;
5946 }
5947
5948 let mut selections = self.selections.all_adjusted(cx);
5949 let buffer = self.buffer.read(cx);
5950 let snapshot = buffer.snapshot(cx);
5951 let rows_iter = selections.iter().map(|s| s.head().row);
5952 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
5953
5954 let mut edits = Vec::new();
5955 let mut prev_edited_row = 0;
5956 let mut row_delta = 0;
5957 for selection in &mut selections {
5958 if selection.start.row != prev_edited_row {
5959 row_delta = 0;
5960 }
5961 prev_edited_row = selection.end.row;
5962
5963 // If the selection is non-empty, then increase the indentation of the selected lines.
5964 if !selection.is_empty() {
5965 row_delta =
5966 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5967 continue;
5968 }
5969
5970 // If the selection is empty and the cursor is in the leading whitespace before the
5971 // suggested indentation, then auto-indent the line.
5972 let cursor = selection.head();
5973 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
5974 if let Some(suggested_indent) =
5975 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
5976 {
5977 if cursor.column < suggested_indent.len
5978 && cursor.column <= current_indent.len
5979 && current_indent.len <= suggested_indent.len
5980 {
5981 selection.start = Point::new(cursor.row, suggested_indent.len);
5982 selection.end = selection.start;
5983 if row_delta == 0 {
5984 edits.extend(Buffer::edit_for_indent_size_adjustment(
5985 cursor.row,
5986 current_indent,
5987 suggested_indent,
5988 ));
5989 row_delta = suggested_indent.len - current_indent.len;
5990 }
5991 continue;
5992 }
5993 }
5994
5995 // Otherwise, insert a hard or soft tab.
5996 let settings = buffer.settings_at(cursor, cx);
5997 let tab_size = if settings.hard_tabs {
5998 IndentSize::tab()
5999 } else {
6000 let tab_size = settings.tab_size.get();
6001 let char_column = snapshot
6002 .text_for_range(Point::new(cursor.row, 0)..cursor)
6003 .flat_map(str::chars)
6004 .count()
6005 + row_delta as usize;
6006 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
6007 IndentSize::spaces(chars_to_next_tab_stop)
6008 };
6009 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
6010 selection.end = selection.start;
6011 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
6012 row_delta += tab_size.len;
6013 }
6014
6015 self.transact(cx, |this, cx| {
6016 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
6017 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6018 this.refresh_inline_completion(true, false, cx);
6019 });
6020 }
6021
6022 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
6023 if self.read_only(cx) {
6024 return;
6025 }
6026 let mut selections = self.selections.all::<Point>(cx);
6027 let mut prev_edited_row = 0;
6028 let mut row_delta = 0;
6029 let mut edits = Vec::new();
6030 let buffer = self.buffer.read(cx);
6031 let snapshot = buffer.snapshot(cx);
6032 for selection in &mut selections {
6033 if selection.start.row != prev_edited_row {
6034 row_delta = 0;
6035 }
6036 prev_edited_row = selection.end.row;
6037
6038 row_delta =
6039 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
6040 }
6041
6042 self.transact(cx, |this, cx| {
6043 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
6044 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6045 });
6046 }
6047
6048 fn indent_selection(
6049 buffer: &MultiBuffer,
6050 snapshot: &MultiBufferSnapshot,
6051 selection: &mut Selection<Point>,
6052 edits: &mut Vec<(Range<Point>, String)>,
6053 delta_for_start_row: u32,
6054 cx: &AppContext,
6055 ) -> u32 {
6056 let settings = buffer.settings_at(selection.start, cx);
6057 let tab_size = settings.tab_size.get();
6058 let indent_kind = if settings.hard_tabs {
6059 IndentKind::Tab
6060 } else {
6061 IndentKind::Space
6062 };
6063 let mut start_row = selection.start.row;
6064 let mut end_row = selection.end.row + 1;
6065
6066 // If a selection ends at the beginning of a line, don't indent
6067 // that last line.
6068 if selection.end.column == 0 && selection.end.row > selection.start.row {
6069 end_row -= 1;
6070 }
6071
6072 // Avoid re-indenting a row that has already been indented by a
6073 // previous selection, but still update this selection's column
6074 // to reflect that indentation.
6075 if delta_for_start_row > 0 {
6076 start_row += 1;
6077 selection.start.column += delta_for_start_row;
6078 if selection.end.row == selection.start.row {
6079 selection.end.column += delta_for_start_row;
6080 }
6081 }
6082
6083 let mut delta_for_end_row = 0;
6084 let has_multiple_rows = start_row + 1 != end_row;
6085 for row in start_row..end_row {
6086 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
6087 let indent_delta = match (current_indent.kind, indent_kind) {
6088 (IndentKind::Space, IndentKind::Space) => {
6089 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
6090 IndentSize::spaces(columns_to_next_tab_stop)
6091 }
6092 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
6093 (_, IndentKind::Tab) => IndentSize::tab(),
6094 };
6095
6096 let start = if has_multiple_rows || current_indent.len < selection.start.column {
6097 0
6098 } else {
6099 selection.start.column
6100 };
6101 let row_start = Point::new(row, start);
6102 edits.push((
6103 row_start..row_start,
6104 indent_delta.chars().collect::<String>(),
6105 ));
6106
6107 // Update this selection's endpoints to reflect the indentation.
6108 if row == selection.start.row {
6109 selection.start.column += indent_delta.len;
6110 }
6111 if row == selection.end.row {
6112 selection.end.column += indent_delta.len;
6113 delta_for_end_row = indent_delta.len;
6114 }
6115 }
6116
6117 if selection.start.row == selection.end.row {
6118 delta_for_start_row + delta_for_end_row
6119 } else {
6120 delta_for_end_row
6121 }
6122 }
6123
6124 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
6125 if self.read_only(cx) {
6126 return;
6127 }
6128 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6129 let selections = self.selections.all::<Point>(cx);
6130 let mut deletion_ranges = Vec::new();
6131 let mut last_outdent = None;
6132 {
6133 let buffer = self.buffer.read(cx);
6134 let snapshot = buffer.snapshot(cx);
6135 for selection in &selections {
6136 let settings = buffer.settings_at(selection.start, cx);
6137 let tab_size = settings.tab_size.get();
6138 let mut rows = selection.spanned_rows(false, &display_map);
6139
6140 // Avoid re-outdenting a row that has already been outdented by a
6141 // previous selection.
6142 if let Some(last_row) = last_outdent {
6143 if last_row == rows.start {
6144 rows.start = rows.start.next_row();
6145 }
6146 }
6147 let has_multiple_rows = rows.len() > 1;
6148 for row in rows.iter_rows() {
6149 let indent_size = snapshot.indent_size_for_line(row);
6150 if indent_size.len > 0 {
6151 let deletion_len = match indent_size.kind {
6152 IndentKind::Space => {
6153 let columns_to_prev_tab_stop = indent_size.len % tab_size;
6154 if columns_to_prev_tab_stop == 0 {
6155 tab_size
6156 } else {
6157 columns_to_prev_tab_stop
6158 }
6159 }
6160 IndentKind::Tab => 1,
6161 };
6162 let start = if has_multiple_rows
6163 || deletion_len > selection.start.column
6164 || indent_size.len < selection.start.column
6165 {
6166 0
6167 } else {
6168 selection.start.column - deletion_len
6169 };
6170 deletion_ranges.push(
6171 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
6172 );
6173 last_outdent = Some(row);
6174 }
6175 }
6176 }
6177 }
6178
6179 self.transact(cx, |this, cx| {
6180 this.buffer.update(cx, |buffer, cx| {
6181 let empty_str: Arc<str> = Arc::default();
6182 buffer.edit(
6183 deletion_ranges
6184 .into_iter()
6185 .map(|range| (range, empty_str.clone())),
6186 None,
6187 cx,
6188 );
6189 });
6190 let selections = this.selections.all::<usize>(cx);
6191 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6192 });
6193 }
6194
6195 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
6196 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6197 let selections = self.selections.all::<Point>(cx);
6198
6199 let mut new_cursors = Vec::new();
6200 let mut edit_ranges = Vec::new();
6201 let mut selections = selections.iter().peekable();
6202 while let Some(selection) = selections.next() {
6203 let mut rows = selection.spanned_rows(false, &display_map);
6204 let goal_display_column = selection.head().to_display_point(&display_map).column();
6205
6206 // Accumulate contiguous regions of rows that we want to delete.
6207 while let Some(next_selection) = selections.peek() {
6208 let next_rows = next_selection.spanned_rows(false, &display_map);
6209 if next_rows.start <= rows.end {
6210 rows.end = next_rows.end;
6211 selections.next().unwrap();
6212 } else {
6213 break;
6214 }
6215 }
6216
6217 let buffer = &display_map.buffer_snapshot;
6218 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
6219 let edit_end;
6220 let cursor_buffer_row;
6221 if buffer.max_point().row >= rows.end.0 {
6222 // If there's a line after the range, delete the \n from the end of the row range
6223 // and position the cursor on the next line.
6224 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
6225 cursor_buffer_row = rows.end;
6226 } else {
6227 // If there isn't a line after the range, delete the \n from the line before the
6228 // start of the row range and position the cursor there.
6229 edit_start = edit_start.saturating_sub(1);
6230 edit_end = buffer.len();
6231 cursor_buffer_row = rows.start.previous_row();
6232 }
6233
6234 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
6235 *cursor.column_mut() =
6236 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
6237
6238 new_cursors.push((
6239 selection.id,
6240 buffer.anchor_after(cursor.to_point(&display_map)),
6241 ));
6242 edit_ranges.push(edit_start..edit_end);
6243 }
6244
6245 self.transact(cx, |this, cx| {
6246 let buffer = this.buffer.update(cx, |buffer, cx| {
6247 let empty_str: Arc<str> = Arc::default();
6248 buffer.edit(
6249 edit_ranges
6250 .into_iter()
6251 .map(|range| (range, empty_str.clone())),
6252 None,
6253 cx,
6254 );
6255 buffer.snapshot(cx)
6256 });
6257 let new_selections = new_cursors
6258 .into_iter()
6259 .map(|(id, cursor)| {
6260 let cursor = cursor.to_point(&buffer);
6261 Selection {
6262 id,
6263 start: cursor,
6264 end: cursor,
6265 reversed: false,
6266 goal: SelectionGoal::None,
6267 }
6268 })
6269 .collect();
6270
6271 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6272 s.select(new_selections);
6273 });
6274 });
6275 }
6276
6277 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
6278 if self.read_only(cx) {
6279 return;
6280 }
6281 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
6282 for selection in self.selections.all::<Point>(cx) {
6283 let start = MultiBufferRow(selection.start.row);
6284 let end = if selection.start.row == selection.end.row {
6285 MultiBufferRow(selection.start.row + 1)
6286 } else {
6287 MultiBufferRow(selection.end.row)
6288 };
6289
6290 if let Some(last_row_range) = row_ranges.last_mut() {
6291 if start <= last_row_range.end {
6292 last_row_range.end = end;
6293 continue;
6294 }
6295 }
6296 row_ranges.push(start..end);
6297 }
6298
6299 let snapshot = self.buffer.read(cx).snapshot(cx);
6300 let mut cursor_positions = Vec::new();
6301 for row_range in &row_ranges {
6302 let anchor = snapshot.anchor_before(Point::new(
6303 row_range.end.previous_row().0,
6304 snapshot.line_len(row_range.end.previous_row()),
6305 ));
6306 cursor_positions.push(anchor..anchor);
6307 }
6308
6309 self.transact(cx, |this, cx| {
6310 for row_range in row_ranges.into_iter().rev() {
6311 for row in row_range.iter_rows().rev() {
6312 let end_of_line = Point::new(row.0, snapshot.line_len(row));
6313 let next_line_row = row.next_row();
6314 let indent = snapshot.indent_size_for_line(next_line_row);
6315 let start_of_next_line = Point::new(next_line_row.0, indent.len);
6316
6317 let replace = if snapshot.line_len(next_line_row) > indent.len {
6318 " "
6319 } else {
6320 ""
6321 };
6322
6323 this.buffer.update(cx, |buffer, cx| {
6324 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
6325 });
6326 }
6327 }
6328
6329 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6330 s.select_anchor_ranges(cursor_positions)
6331 });
6332 });
6333 }
6334
6335 pub fn sort_lines_case_sensitive(
6336 &mut self,
6337 _: &SortLinesCaseSensitive,
6338 cx: &mut ViewContext<Self>,
6339 ) {
6340 self.manipulate_lines(cx, |lines| lines.sort())
6341 }
6342
6343 pub fn sort_lines_case_insensitive(
6344 &mut self,
6345 _: &SortLinesCaseInsensitive,
6346 cx: &mut ViewContext<Self>,
6347 ) {
6348 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
6349 }
6350
6351 pub fn unique_lines_case_insensitive(
6352 &mut self,
6353 _: &UniqueLinesCaseInsensitive,
6354 cx: &mut ViewContext<Self>,
6355 ) {
6356 self.manipulate_lines(cx, |lines| {
6357 let mut seen = HashSet::default();
6358 lines.retain(|line| seen.insert(line.to_lowercase()));
6359 })
6360 }
6361
6362 pub fn unique_lines_case_sensitive(
6363 &mut self,
6364 _: &UniqueLinesCaseSensitive,
6365 cx: &mut ViewContext<Self>,
6366 ) {
6367 self.manipulate_lines(cx, |lines| {
6368 let mut seen = HashSet::default();
6369 lines.retain(|line| seen.insert(*line));
6370 })
6371 }
6372
6373 pub fn revert_file(&mut self, _: &RevertFile, cx: &mut ViewContext<Self>) {
6374 let mut revert_changes = HashMap::default();
6375 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
6376 for hunk in hunks_for_rows(
6377 Some(MultiBufferRow(0)..multi_buffer_snapshot.max_buffer_row()).into_iter(),
6378 &multi_buffer_snapshot,
6379 ) {
6380 Self::prepare_revert_change(&mut revert_changes, self.buffer(), &hunk, cx);
6381 }
6382 if !revert_changes.is_empty() {
6383 self.transact(cx, |editor, cx| {
6384 editor.revert(revert_changes, cx);
6385 });
6386 }
6387 }
6388
6389 pub fn reload_file(&mut self, _: &ReloadFile, cx: &mut ViewContext<Self>) {
6390 let Some(project) = self.project.clone() else {
6391 return;
6392 };
6393 self.reload(project, cx).detach_and_notify_err(cx);
6394 }
6395
6396 pub fn revert_selected_hunks(&mut self, _: &RevertSelectedHunks, cx: &mut ViewContext<Self>) {
6397 let revert_changes = self.gather_revert_changes(&self.selections.disjoint_anchors(), cx);
6398 if !revert_changes.is_empty() {
6399 self.transact(cx, |editor, cx| {
6400 editor.revert(revert_changes, cx);
6401 });
6402 }
6403 }
6404
6405 pub fn open_active_item_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
6406 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
6407 let project_path = buffer.read(cx).project_path(cx)?;
6408 let project = self.project.as_ref()?.read(cx);
6409 let entry = project.entry_for_path(&project_path, cx)?;
6410 let parent = match &entry.canonical_path {
6411 Some(canonical_path) => canonical_path.to_path_buf(),
6412 None => project.absolute_path(&project_path, cx)?,
6413 }
6414 .parent()?
6415 .to_path_buf();
6416 Some(parent)
6417 }) {
6418 cx.dispatch_action(OpenTerminal { working_directory }.boxed_clone());
6419 }
6420 }
6421
6422 fn gather_revert_changes(
6423 &mut self,
6424 selections: &[Selection<Anchor>],
6425 cx: &mut ViewContext<'_, Editor>,
6426 ) -> HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>> {
6427 let mut revert_changes = HashMap::default();
6428 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
6429 for hunk in hunks_for_selections(&multi_buffer_snapshot, selections) {
6430 Self::prepare_revert_change(&mut revert_changes, self.buffer(), &hunk, cx);
6431 }
6432 revert_changes
6433 }
6434
6435 pub fn prepare_revert_change(
6436 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
6437 multi_buffer: &Model<MultiBuffer>,
6438 hunk: &MultiBufferDiffHunk,
6439 cx: &AppContext,
6440 ) -> Option<()> {
6441 let buffer = multi_buffer.read(cx).buffer(hunk.buffer_id)?;
6442 let buffer = buffer.read(cx);
6443 let original_text = buffer.diff_base()?.slice(hunk.diff_base_byte_range.clone());
6444 let buffer_snapshot = buffer.snapshot();
6445 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
6446 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
6447 probe
6448 .0
6449 .start
6450 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
6451 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
6452 }) {
6453 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
6454 Some(())
6455 } else {
6456 None
6457 }
6458 }
6459
6460 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
6461 self.manipulate_lines(cx, |lines| lines.reverse())
6462 }
6463
6464 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
6465 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
6466 }
6467
6468 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6469 where
6470 Fn: FnMut(&mut Vec<&str>),
6471 {
6472 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6473 let buffer = self.buffer.read(cx).snapshot(cx);
6474
6475 let mut edits = Vec::new();
6476
6477 let selections = self.selections.all::<Point>(cx);
6478 let mut selections = selections.iter().peekable();
6479 let mut contiguous_row_selections = Vec::new();
6480 let mut new_selections = Vec::new();
6481 let mut added_lines = 0;
6482 let mut removed_lines = 0;
6483
6484 while let Some(selection) = selections.next() {
6485 let (start_row, end_row) = consume_contiguous_rows(
6486 &mut contiguous_row_selections,
6487 selection,
6488 &display_map,
6489 &mut selections,
6490 );
6491
6492 let start_point = Point::new(start_row.0, 0);
6493 let end_point = Point::new(
6494 end_row.previous_row().0,
6495 buffer.line_len(end_row.previous_row()),
6496 );
6497 let text = buffer
6498 .text_for_range(start_point..end_point)
6499 .collect::<String>();
6500
6501 let mut lines = text.split('\n').collect_vec();
6502
6503 let lines_before = lines.len();
6504 callback(&mut lines);
6505 let lines_after = lines.len();
6506
6507 edits.push((start_point..end_point, lines.join("\n")));
6508
6509 // Selections must change based on added and removed line count
6510 let start_row =
6511 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
6512 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
6513 new_selections.push(Selection {
6514 id: selection.id,
6515 start: start_row,
6516 end: end_row,
6517 goal: SelectionGoal::None,
6518 reversed: selection.reversed,
6519 });
6520
6521 if lines_after > lines_before {
6522 added_lines += lines_after - lines_before;
6523 } else if lines_before > lines_after {
6524 removed_lines += lines_before - lines_after;
6525 }
6526 }
6527
6528 self.transact(cx, |this, cx| {
6529 let buffer = this.buffer.update(cx, |buffer, cx| {
6530 buffer.edit(edits, None, cx);
6531 buffer.snapshot(cx)
6532 });
6533
6534 // Recalculate offsets on newly edited buffer
6535 let new_selections = new_selections
6536 .iter()
6537 .map(|s| {
6538 let start_point = Point::new(s.start.0, 0);
6539 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
6540 Selection {
6541 id: s.id,
6542 start: buffer.point_to_offset(start_point),
6543 end: buffer.point_to_offset(end_point),
6544 goal: s.goal,
6545 reversed: s.reversed,
6546 }
6547 })
6548 .collect();
6549
6550 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6551 s.select(new_selections);
6552 });
6553
6554 this.request_autoscroll(Autoscroll::fit(), cx);
6555 });
6556 }
6557
6558 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
6559 self.manipulate_text(cx, |text| text.to_uppercase())
6560 }
6561
6562 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
6563 self.manipulate_text(cx, |text| text.to_lowercase())
6564 }
6565
6566 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
6567 self.manipulate_text(cx, |text| {
6568 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6569 // https://github.com/rutrum/convert-case/issues/16
6570 text.split('\n')
6571 .map(|line| line.to_case(Case::Title))
6572 .join("\n")
6573 })
6574 }
6575
6576 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
6577 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
6578 }
6579
6580 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
6581 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
6582 }
6583
6584 pub fn convert_to_upper_camel_case(
6585 &mut self,
6586 _: &ConvertToUpperCamelCase,
6587 cx: &mut ViewContext<Self>,
6588 ) {
6589 self.manipulate_text(cx, |text| {
6590 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6591 // https://github.com/rutrum/convert-case/issues/16
6592 text.split('\n')
6593 .map(|line| line.to_case(Case::UpperCamel))
6594 .join("\n")
6595 })
6596 }
6597
6598 pub fn convert_to_lower_camel_case(
6599 &mut self,
6600 _: &ConvertToLowerCamelCase,
6601 cx: &mut ViewContext<Self>,
6602 ) {
6603 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
6604 }
6605
6606 pub fn convert_to_opposite_case(
6607 &mut self,
6608 _: &ConvertToOppositeCase,
6609 cx: &mut ViewContext<Self>,
6610 ) {
6611 self.manipulate_text(cx, |text| {
6612 text.chars()
6613 .fold(String::with_capacity(text.len()), |mut t, c| {
6614 if c.is_uppercase() {
6615 t.extend(c.to_lowercase());
6616 } else {
6617 t.extend(c.to_uppercase());
6618 }
6619 t
6620 })
6621 })
6622 }
6623
6624 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6625 where
6626 Fn: FnMut(&str) -> String,
6627 {
6628 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6629 let buffer = self.buffer.read(cx).snapshot(cx);
6630
6631 let mut new_selections = Vec::new();
6632 let mut edits = Vec::new();
6633 let mut selection_adjustment = 0i32;
6634
6635 for selection in self.selections.all::<usize>(cx) {
6636 let selection_is_empty = selection.is_empty();
6637
6638 let (start, end) = if selection_is_empty {
6639 let word_range = movement::surrounding_word(
6640 &display_map,
6641 selection.start.to_display_point(&display_map),
6642 );
6643 let start = word_range.start.to_offset(&display_map, Bias::Left);
6644 let end = word_range.end.to_offset(&display_map, Bias::Left);
6645 (start, end)
6646 } else {
6647 (selection.start, selection.end)
6648 };
6649
6650 let text = buffer.text_for_range(start..end).collect::<String>();
6651 let old_length = text.len() as i32;
6652 let text = callback(&text);
6653
6654 new_selections.push(Selection {
6655 start: (start as i32 - selection_adjustment) as usize,
6656 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
6657 goal: SelectionGoal::None,
6658 ..selection
6659 });
6660
6661 selection_adjustment += old_length - text.len() as i32;
6662
6663 edits.push((start..end, text));
6664 }
6665
6666 self.transact(cx, |this, cx| {
6667 this.buffer.update(cx, |buffer, cx| {
6668 buffer.edit(edits, None, cx);
6669 });
6670
6671 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6672 s.select(new_selections);
6673 });
6674
6675 this.request_autoscroll(Autoscroll::fit(), cx);
6676 });
6677 }
6678
6679 pub fn duplicate_line(&mut self, upwards: bool, cx: &mut ViewContext<Self>) {
6680 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6681 let buffer = &display_map.buffer_snapshot;
6682 let selections = self.selections.all::<Point>(cx);
6683
6684 let mut edits = Vec::new();
6685 let mut selections_iter = selections.iter().peekable();
6686 while let Some(selection) = selections_iter.next() {
6687 // Avoid duplicating the same lines twice.
6688 let mut rows = selection.spanned_rows(false, &display_map);
6689
6690 while let Some(next_selection) = selections_iter.peek() {
6691 let next_rows = next_selection.spanned_rows(false, &display_map);
6692 if next_rows.start < rows.end {
6693 rows.end = next_rows.end;
6694 selections_iter.next().unwrap();
6695 } else {
6696 break;
6697 }
6698 }
6699
6700 // Copy the text from the selected row region and splice it either at the start
6701 // or end of the region.
6702 let start = Point::new(rows.start.0, 0);
6703 let end = Point::new(
6704 rows.end.previous_row().0,
6705 buffer.line_len(rows.end.previous_row()),
6706 );
6707 let text = buffer
6708 .text_for_range(start..end)
6709 .chain(Some("\n"))
6710 .collect::<String>();
6711 let insert_location = if upwards {
6712 Point::new(rows.end.0, 0)
6713 } else {
6714 start
6715 };
6716 edits.push((insert_location..insert_location, text));
6717 }
6718
6719 self.transact(cx, |this, cx| {
6720 this.buffer.update(cx, |buffer, cx| {
6721 buffer.edit(edits, None, cx);
6722 });
6723
6724 this.request_autoscroll(Autoscroll::fit(), cx);
6725 });
6726 }
6727
6728 pub fn duplicate_line_up(&mut self, _: &DuplicateLineUp, cx: &mut ViewContext<Self>) {
6729 self.duplicate_line(true, cx);
6730 }
6731
6732 pub fn duplicate_line_down(&mut self, _: &DuplicateLineDown, cx: &mut ViewContext<Self>) {
6733 self.duplicate_line(false, cx);
6734 }
6735
6736 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
6737 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6738 let buffer = self.buffer.read(cx).snapshot(cx);
6739
6740 let mut edits = Vec::new();
6741 let mut unfold_ranges = Vec::new();
6742 let mut refold_ranges = Vec::new();
6743
6744 let selections = self.selections.all::<Point>(cx);
6745 let mut selections = selections.iter().peekable();
6746 let mut contiguous_row_selections = Vec::new();
6747 let mut new_selections = Vec::new();
6748
6749 while let Some(selection) = selections.next() {
6750 // Find all the selections that span a contiguous row range
6751 let (start_row, end_row) = consume_contiguous_rows(
6752 &mut contiguous_row_selections,
6753 selection,
6754 &display_map,
6755 &mut selections,
6756 );
6757
6758 // Move the text spanned by the row range to be before the line preceding the row range
6759 if start_row.0 > 0 {
6760 let range_to_move = Point::new(
6761 start_row.previous_row().0,
6762 buffer.line_len(start_row.previous_row()),
6763 )
6764 ..Point::new(
6765 end_row.previous_row().0,
6766 buffer.line_len(end_row.previous_row()),
6767 );
6768 let insertion_point = display_map
6769 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
6770 .0;
6771
6772 // Don't move lines across excerpts
6773 if buffer
6774 .excerpt_boundaries_in_range((
6775 Bound::Excluded(insertion_point),
6776 Bound::Included(range_to_move.end),
6777 ))
6778 .next()
6779 .is_none()
6780 {
6781 let text = buffer
6782 .text_for_range(range_to_move.clone())
6783 .flat_map(|s| s.chars())
6784 .skip(1)
6785 .chain(['\n'])
6786 .collect::<String>();
6787
6788 edits.push((
6789 buffer.anchor_after(range_to_move.start)
6790 ..buffer.anchor_before(range_to_move.end),
6791 String::new(),
6792 ));
6793 let insertion_anchor = buffer.anchor_after(insertion_point);
6794 edits.push((insertion_anchor..insertion_anchor, text));
6795
6796 let row_delta = range_to_move.start.row - insertion_point.row + 1;
6797
6798 // Move selections up
6799 new_selections.extend(contiguous_row_selections.drain(..).map(
6800 |mut selection| {
6801 selection.start.row -= row_delta;
6802 selection.end.row -= row_delta;
6803 selection
6804 },
6805 ));
6806
6807 // Move folds up
6808 unfold_ranges.push(range_to_move.clone());
6809 for fold in display_map.folds_in_range(
6810 buffer.anchor_before(range_to_move.start)
6811 ..buffer.anchor_after(range_to_move.end),
6812 ) {
6813 let mut start = fold.range.start.to_point(&buffer);
6814 let mut end = fold.range.end.to_point(&buffer);
6815 start.row -= row_delta;
6816 end.row -= row_delta;
6817 refold_ranges.push((start..end, fold.placeholder.clone()));
6818 }
6819 }
6820 }
6821
6822 // If we didn't move line(s), preserve the existing selections
6823 new_selections.append(&mut contiguous_row_selections);
6824 }
6825
6826 self.transact(cx, |this, cx| {
6827 this.unfold_ranges(&unfold_ranges, true, true, cx);
6828 this.buffer.update(cx, |buffer, cx| {
6829 for (range, text) in edits {
6830 buffer.edit([(range, text)], None, cx);
6831 }
6832 });
6833 this.fold_ranges(refold_ranges, true, cx);
6834 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6835 s.select(new_selections);
6836 })
6837 });
6838 }
6839
6840 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
6841 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6842 let buffer = self.buffer.read(cx).snapshot(cx);
6843
6844 let mut edits = Vec::new();
6845 let mut unfold_ranges = Vec::new();
6846 let mut refold_ranges = Vec::new();
6847
6848 let selections = self.selections.all::<Point>(cx);
6849 let mut selections = selections.iter().peekable();
6850 let mut contiguous_row_selections = Vec::new();
6851 let mut new_selections = Vec::new();
6852
6853 while let Some(selection) = selections.next() {
6854 // Find all the selections that span a contiguous row range
6855 let (start_row, end_row) = consume_contiguous_rows(
6856 &mut contiguous_row_selections,
6857 selection,
6858 &display_map,
6859 &mut selections,
6860 );
6861
6862 // Move the text spanned by the row range to be after the last line of the row range
6863 if end_row.0 <= buffer.max_point().row {
6864 let range_to_move =
6865 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
6866 let insertion_point = display_map
6867 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
6868 .0;
6869
6870 // Don't move lines across excerpt boundaries
6871 if buffer
6872 .excerpt_boundaries_in_range((
6873 Bound::Excluded(range_to_move.start),
6874 Bound::Included(insertion_point),
6875 ))
6876 .next()
6877 .is_none()
6878 {
6879 let mut text = String::from("\n");
6880 text.extend(buffer.text_for_range(range_to_move.clone()));
6881 text.pop(); // Drop trailing newline
6882 edits.push((
6883 buffer.anchor_after(range_to_move.start)
6884 ..buffer.anchor_before(range_to_move.end),
6885 String::new(),
6886 ));
6887 let insertion_anchor = buffer.anchor_after(insertion_point);
6888 edits.push((insertion_anchor..insertion_anchor, text));
6889
6890 let row_delta = insertion_point.row - range_to_move.end.row + 1;
6891
6892 // Move selections down
6893 new_selections.extend(contiguous_row_selections.drain(..).map(
6894 |mut selection| {
6895 selection.start.row += row_delta;
6896 selection.end.row += row_delta;
6897 selection
6898 },
6899 ));
6900
6901 // Move folds down
6902 unfold_ranges.push(range_to_move.clone());
6903 for fold in display_map.folds_in_range(
6904 buffer.anchor_before(range_to_move.start)
6905 ..buffer.anchor_after(range_to_move.end),
6906 ) {
6907 let mut start = fold.range.start.to_point(&buffer);
6908 let mut end = fold.range.end.to_point(&buffer);
6909 start.row += row_delta;
6910 end.row += row_delta;
6911 refold_ranges.push((start..end, fold.placeholder.clone()));
6912 }
6913 }
6914 }
6915
6916 // If we didn't move line(s), preserve the existing selections
6917 new_selections.append(&mut contiguous_row_selections);
6918 }
6919
6920 self.transact(cx, |this, cx| {
6921 this.unfold_ranges(&unfold_ranges, true, true, cx);
6922 this.buffer.update(cx, |buffer, cx| {
6923 for (range, text) in edits {
6924 buffer.edit([(range, text)], None, cx);
6925 }
6926 });
6927 this.fold_ranges(refold_ranges, true, cx);
6928 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
6929 });
6930 }
6931
6932 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
6933 let text_layout_details = &self.text_layout_details(cx);
6934 self.transact(cx, |this, cx| {
6935 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6936 let mut edits: Vec<(Range<usize>, String)> = Default::default();
6937 let line_mode = s.line_mode;
6938 s.move_with(|display_map, selection| {
6939 if !selection.is_empty() || line_mode {
6940 return;
6941 }
6942
6943 let mut head = selection.head();
6944 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
6945 if head.column() == display_map.line_len(head.row()) {
6946 transpose_offset = display_map
6947 .buffer_snapshot
6948 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6949 }
6950
6951 if transpose_offset == 0 {
6952 return;
6953 }
6954
6955 *head.column_mut() += 1;
6956 head = display_map.clip_point(head, Bias::Right);
6957 let goal = SelectionGoal::HorizontalPosition(
6958 display_map
6959 .x_for_display_point(head, text_layout_details)
6960 .into(),
6961 );
6962 selection.collapse_to(head, goal);
6963
6964 let transpose_start = display_map
6965 .buffer_snapshot
6966 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6967 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
6968 let transpose_end = display_map
6969 .buffer_snapshot
6970 .clip_offset(transpose_offset + 1, Bias::Right);
6971 if let Some(ch) =
6972 display_map.buffer_snapshot.chars_at(transpose_start).next()
6973 {
6974 edits.push((transpose_start..transpose_offset, String::new()));
6975 edits.push((transpose_end..transpose_end, ch.to_string()));
6976 }
6977 }
6978 });
6979 edits
6980 });
6981 this.buffer
6982 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
6983 let selections = this.selections.all::<usize>(cx);
6984 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6985 s.select(selections);
6986 });
6987 });
6988 }
6989
6990 pub fn rewrap(&mut self, _: &Rewrap, cx: &mut ViewContext<Self>) {
6991 self.rewrap_impl(true, cx)
6992 }
6993
6994 pub fn rewrap_impl(&mut self, only_text: bool, cx: &mut ViewContext<Self>) {
6995 let buffer = self.buffer.read(cx).snapshot(cx);
6996 let selections = self.selections.all::<Point>(cx);
6997 let mut selections = selections.iter().peekable();
6998
6999 let mut edits = Vec::new();
7000 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
7001
7002 while let Some(selection) = selections.next() {
7003 let mut start_row = selection.start.row;
7004 let mut end_row = selection.end.row;
7005
7006 // Skip selections that overlap with a range that has already been rewrapped.
7007 let selection_range = start_row..end_row;
7008 if rewrapped_row_ranges
7009 .iter()
7010 .any(|range| range.overlaps(&selection_range))
7011 {
7012 continue;
7013 }
7014
7015 let mut should_rewrap = !only_text;
7016
7017 if let Some(language_scope) = buffer.language_scope_at(selection.head()) {
7018 match language_scope.language_name().0.as_ref() {
7019 "Markdown" | "Plain Text" => {
7020 should_rewrap = true;
7021 }
7022 _ => {}
7023 }
7024 }
7025
7026 let tab_size = buffer.settings_at(selection.head(), cx).tab_size;
7027
7028 // Since not all lines in the selection may be at the same indent
7029 // level, choose the indent size that is the most common between all
7030 // of the lines.
7031 //
7032 // If there is a tie, we use the deepest indent.
7033 let (indent_size, indent_end) = {
7034 let mut indent_size_occurrences = HashMap::default();
7035 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
7036
7037 for row in start_row..=end_row {
7038 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
7039 rows_by_indent_size.entry(indent).or_default().push(row);
7040 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
7041 }
7042
7043 let indent_size = indent_size_occurrences
7044 .into_iter()
7045 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
7046 .map(|(indent, _)| indent)
7047 .unwrap_or_default();
7048 let row = rows_by_indent_size[&indent_size][0];
7049 let indent_end = Point::new(row, indent_size.len);
7050
7051 (indent_size, indent_end)
7052 };
7053
7054 let mut line_prefix = indent_size.chars().collect::<String>();
7055
7056 if let Some(comment_prefix) =
7057 buffer
7058 .language_scope_at(selection.head())
7059 .and_then(|language| {
7060 language
7061 .line_comment_prefixes()
7062 .iter()
7063 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
7064 .cloned()
7065 })
7066 {
7067 line_prefix.push_str(&comment_prefix);
7068 should_rewrap = true;
7069 }
7070
7071 if !should_rewrap {
7072 continue;
7073 }
7074
7075 if selection.is_empty() {
7076 'expand_upwards: while start_row > 0 {
7077 let prev_row = start_row - 1;
7078 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
7079 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
7080 {
7081 start_row = prev_row;
7082 } else {
7083 break 'expand_upwards;
7084 }
7085 }
7086
7087 'expand_downwards: while end_row < buffer.max_point().row {
7088 let next_row = end_row + 1;
7089 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
7090 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
7091 {
7092 end_row = next_row;
7093 } else {
7094 break 'expand_downwards;
7095 }
7096 }
7097 }
7098
7099 let start = Point::new(start_row, 0);
7100 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
7101 let selection_text = buffer.text_for_range(start..end).collect::<String>();
7102 let Some(lines_without_prefixes) = selection_text
7103 .lines()
7104 .map(|line| {
7105 line.strip_prefix(&line_prefix)
7106 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
7107 .ok_or_else(|| {
7108 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
7109 })
7110 })
7111 .collect::<Result<Vec<_>, _>>()
7112 .log_err()
7113 else {
7114 continue;
7115 };
7116
7117 let wrap_column = buffer
7118 .settings_at(Point::new(start_row, 0), cx)
7119 .preferred_line_length as usize;
7120 let wrapped_text = wrap_with_prefix(
7121 line_prefix,
7122 lines_without_prefixes.join(" "),
7123 wrap_column,
7124 tab_size,
7125 );
7126
7127 let diff = TextDiff::from_lines(&selection_text, &wrapped_text);
7128 let mut offset = start.to_offset(&buffer);
7129 let mut moved_since_edit = true;
7130
7131 for change in diff.iter_all_changes() {
7132 let value = change.value();
7133 match change.tag() {
7134 ChangeTag::Equal => {
7135 offset += value.len();
7136 moved_since_edit = true;
7137 }
7138 ChangeTag::Delete => {
7139 let start = buffer.anchor_after(offset);
7140 let end = buffer.anchor_before(offset + value.len());
7141
7142 if moved_since_edit {
7143 edits.push((start..end, String::new()));
7144 } else {
7145 edits.last_mut().unwrap().0.end = end;
7146 }
7147
7148 offset += value.len();
7149 moved_since_edit = false;
7150 }
7151 ChangeTag::Insert => {
7152 if moved_since_edit {
7153 let anchor = buffer.anchor_after(offset);
7154 edits.push((anchor..anchor, value.to_string()));
7155 } else {
7156 edits.last_mut().unwrap().1.push_str(value);
7157 }
7158
7159 moved_since_edit = false;
7160 }
7161 }
7162 }
7163
7164 rewrapped_row_ranges.push(start_row..=end_row);
7165 }
7166
7167 self.buffer
7168 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
7169 }
7170
7171 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
7172 let mut text = String::new();
7173 let buffer = self.buffer.read(cx).snapshot(cx);
7174 let mut selections = self.selections.all::<Point>(cx);
7175 let mut clipboard_selections = Vec::with_capacity(selections.len());
7176 {
7177 let max_point = buffer.max_point();
7178 let mut is_first = true;
7179 for selection in &mut selections {
7180 let is_entire_line = selection.is_empty() || self.selections.line_mode;
7181 if is_entire_line {
7182 selection.start = Point::new(selection.start.row, 0);
7183 if !selection.is_empty() && selection.end.column == 0 {
7184 selection.end = cmp::min(max_point, selection.end);
7185 } else {
7186 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
7187 }
7188 selection.goal = SelectionGoal::None;
7189 }
7190 if is_first {
7191 is_first = false;
7192 } else {
7193 text += "\n";
7194 }
7195 let mut len = 0;
7196 for chunk in buffer.text_for_range(selection.start..selection.end) {
7197 text.push_str(chunk);
7198 len += chunk.len();
7199 }
7200 clipboard_selections.push(ClipboardSelection {
7201 len,
7202 is_entire_line,
7203 first_line_indent: buffer
7204 .indent_size_for_line(MultiBufferRow(selection.start.row))
7205 .len,
7206 });
7207 }
7208 }
7209
7210 self.transact(cx, |this, cx| {
7211 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7212 s.select(selections);
7213 });
7214 this.insert("", cx);
7215 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
7216 text,
7217 clipboard_selections,
7218 ));
7219 });
7220 }
7221
7222 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
7223 let selections = self.selections.all::<Point>(cx);
7224 let buffer = self.buffer.read(cx).read(cx);
7225 let mut text = String::new();
7226
7227 let mut clipboard_selections = Vec::with_capacity(selections.len());
7228 {
7229 let max_point = buffer.max_point();
7230 let mut is_first = true;
7231 for selection in selections.iter() {
7232 let mut start = selection.start;
7233 let mut end = selection.end;
7234 let is_entire_line = selection.is_empty() || self.selections.line_mode;
7235 if is_entire_line {
7236 start = Point::new(start.row, 0);
7237 end = cmp::min(max_point, Point::new(end.row + 1, 0));
7238 }
7239 if is_first {
7240 is_first = false;
7241 } else {
7242 text += "\n";
7243 }
7244 let mut len = 0;
7245 for chunk in buffer.text_for_range(start..end) {
7246 text.push_str(chunk);
7247 len += chunk.len();
7248 }
7249 clipboard_selections.push(ClipboardSelection {
7250 len,
7251 is_entire_line,
7252 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
7253 });
7254 }
7255 }
7256
7257 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
7258 text,
7259 clipboard_selections,
7260 ));
7261 }
7262
7263 pub fn do_paste(
7264 &mut self,
7265 text: &String,
7266 clipboard_selections: Option<Vec<ClipboardSelection>>,
7267 handle_entire_lines: bool,
7268 cx: &mut ViewContext<Self>,
7269 ) {
7270 if self.read_only(cx) {
7271 return;
7272 }
7273
7274 let clipboard_text = Cow::Borrowed(text);
7275
7276 self.transact(cx, |this, cx| {
7277 if let Some(mut clipboard_selections) = clipboard_selections {
7278 let old_selections = this.selections.all::<usize>(cx);
7279 let all_selections_were_entire_line =
7280 clipboard_selections.iter().all(|s| s.is_entire_line);
7281 let first_selection_indent_column =
7282 clipboard_selections.first().map(|s| s.first_line_indent);
7283 if clipboard_selections.len() != old_selections.len() {
7284 clipboard_selections.drain(..);
7285 }
7286 let cursor_offset = this.selections.last::<usize>(cx).head();
7287 let mut auto_indent_on_paste = true;
7288
7289 this.buffer.update(cx, |buffer, cx| {
7290 let snapshot = buffer.read(cx);
7291 auto_indent_on_paste =
7292 snapshot.settings_at(cursor_offset, cx).auto_indent_on_paste;
7293
7294 let mut start_offset = 0;
7295 let mut edits = Vec::new();
7296 let mut original_indent_columns = Vec::new();
7297 for (ix, selection) in old_selections.iter().enumerate() {
7298 let to_insert;
7299 let entire_line;
7300 let original_indent_column;
7301 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
7302 let end_offset = start_offset + clipboard_selection.len;
7303 to_insert = &clipboard_text[start_offset..end_offset];
7304 entire_line = clipboard_selection.is_entire_line;
7305 start_offset = end_offset + 1;
7306 original_indent_column = Some(clipboard_selection.first_line_indent);
7307 } else {
7308 to_insert = clipboard_text.as_str();
7309 entire_line = all_selections_were_entire_line;
7310 original_indent_column = first_selection_indent_column
7311 }
7312
7313 // If the corresponding selection was empty when this slice of the
7314 // clipboard text was written, then the entire line containing the
7315 // selection was copied. If this selection is also currently empty,
7316 // then paste the line before the current line of the buffer.
7317 let range = if selection.is_empty() && handle_entire_lines && entire_line {
7318 let column = selection.start.to_point(&snapshot).column as usize;
7319 let line_start = selection.start - column;
7320 line_start..line_start
7321 } else {
7322 selection.range()
7323 };
7324
7325 edits.push((range, to_insert));
7326 original_indent_columns.extend(original_indent_column);
7327 }
7328 drop(snapshot);
7329
7330 buffer.edit(
7331 edits,
7332 if auto_indent_on_paste {
7333 Some(AutoindentMode::Block {
7334 original_indent_columns,
7335 })
7336 } else {
7337 None
7338 },
7339 cx,
7340 );
7341 });
7342
7343 let selections = this.selections.all::<usize>(cx);
7344 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
7345 } else {
7346 this.insert(&clipboard_text, cx);
7347 }
7348 });
7349 }
7350
7351 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
7352 if let Some(item) = cx.read_from_clipboard() {
7353 let entries = item.entries();
7354
7355 match entries.first() {
7356 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
7357 // of all the pasted entries.
7358 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
7359 .do_paste(
7360 clipboard_string.text(),
7361 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
7362 true,
7363 cx,
7364 ),
7365 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, cx),
7366 }
7367 }
7368 }
7369
7370 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
7371 if self.read_only(cx) {
7372 return;
7373 }
7374
7375 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
7376 if let Some((selections, _)) =
7377 self.selection_history.transaction(transaction_id).cloned()
7378 {
7379 self.change_selections(None, cx, |s| {
7380 s.select_anchors(selections.to_vec());
7381 });
7382 }
7383 self.request_autoscroll(Autoscroll::fit(), cx);
7384 self.unmark_text(cx);
7385 self.refresh_inline_completion(true, false, cx);
7386 cx.emit(EditorEvent::Edited { transaction_id });
7387 cx.emit(EditorEvent::TransactionUndone { transaction_id });
7388 }
7389 }
7390
7391 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
7392 if self.read_only(cx) {
7393 return;
7394 }
7395
7396 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
7397 if let Some((_, Some(selections))) =
7398 self.selection_history.transaction(transaction_id).cloned()
7399 {
7400 self.change_selections(None, cx, |s| {
7401 s.select_anchors(selections.to_vec());
7402 });
7403 }
7404 self.request_autoscroll(Autoscroll::fit(), cx);
7405 self.unmark_text(cx);
7406 self.refresh_inline_completion(true, false, cx);
7407 cx.emit(EditorEvent::Edited { transaction_id });
7408 }
7409 }
7410
7411 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
7412 self.buffer
7413 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
7414 }
7415
7416 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut ViewContext<Self>) {
7417 self.buffer
7418 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
7419 }
7420
7421 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
7422 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7423 let line_mode = s.line_mode;
7424 s.move_with(|map, selection| {
7425 let cursor = if selection.is_empty() && !line_mode {
7426 movement::left(map, selection.start)
7427 } else {
7428 selection.start
7429 };
7430 selection.collapse_to(cursor, SelectionGoal::None);
7431 });
7432 })
7433 }
7434
7435 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
7436 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7437 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
7438 })
7439 }
7440
7441 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
7442 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7443 let line_mode = s.line_mode;
7444 s.move_with(|map, selection| {
7445 let cursor = if selection.is_empty() && !line_mode {
7446 movement::right(map, selection.end)
7447 } else {
7448 selection.end
7449 };
7450 selection.collapse_to(cursor, SelectionGoal::None)
7451 });
7452 })
7453 }
7454
7455 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
7456 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7457 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
7458 })
7459 }
7460
7461 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
7462 if self.take_rename(true, cx).is_some() {
7463 return;
7464 }
7465
7466 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7467 cx.propagate();
7468 return;
7469 }
7470
7471 let text_layout_details = &self.text_layout_details(cx);
7472 let selection_count = self.selections.count();
7473 let first_selection = self.selections.first_anchor();
7474
7475 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7476 let line_mode = s.line_mode;
7477 s.move_with(|map, selection| {
7478 if !selection.is_empty() && !line_mode {
7479 selection.goal = SelectionGoal::None;
7480 }
7481 let (cursor, goal) = movement::up(
7482 map,
7483 selection.start,
7484 selection.goal,
7485 false,
7486 text_layout_details,
7487 );
7488 selection.collapse_to(cursor, goal);
7489 });
7490 });
7491
7492 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7493 {
7494 cx.propagate();
7495 }
7496 }
7497
7498 pub fn move_up_by_lines(&mut self, action: &MoveUpByLines, cx: &mut ViewContext<Self>) {
7499 if self.take_rename(true, cx).is_some() {
7500 return;
7501 }
7502
7503 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7504 cx.propagate();
7505 return;
7506 }
7507
7508 let text_layout_details = &self.text_layout_details(cx);
7509
7510 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7511 let line_mode = s.line_mode;
7512 s.move_with(|map, selection| {
7513 if !selection.is_empty() && !line_mode {
7514 selection.goal = SelectionGoal::None;
7515 }
7516 let (cursor, goal) = movement::up_by_rows(
7517 map,
7518 selection.start,
7519 action.lines,
7520 selection.goal,
7521 false,
7522 text_layout_details,
7523 );
7524 selection.collapse_to(cursor, goal);
7525 });
7526 })
7527 }
7528
7529 pub fn move_down_by_lines(&mut self, action: &MoveDownByLines, cx: &mut ViewContext<Self>) {
7530 if self.take_rename(true, cx).is_some() {
7531 return;
7532 }
7533
7534 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7535 cx.propagate();
7536 return;
7537 }
7538
7539 let text_layout_details = &self.text_layout_details(cx);
7540
7541 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7542 let line_mode = s.line_mode;
7543 s.move_with(|map, selection| {
7544 if !selection.is_empty() && !line_mode {
7545 selection.goal = SelectionGoal::None;
7546 }
7547 let (cursor, goal) = movement::down_by_rows(
7548 map,
7549 selection.start,
7550 action.lines,
7551 selection.goal,
7552 false,
7553 text_layout_details,
7554 );
7555 selection.collapse_to(cursor, goal);
7556 });
7557 })
7558 }
7559
7560 pub fn select_down_by_lines(&mut self, action: &SelectDownByLines, cx: &mut ViewContext<Self>) {
7561 let text_layout_details = &self.text_layout_details(cx);
7562 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7563 s.move_heads_with(|map, head, goal| {
7564 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
7565 })
7566 })
7567 }
7568
7569 pub fn select_up_by_lines(&mut self, action: &SelectUpByLines, cx: &mut ViewContext<Self>) {
7570 let text_layout_details = &self.text_layout_details(cx);
7571 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7572 s.move_heads_with(|map, head, goal| {
7573 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
7574 })
7575 })
7576 }
7577
7578 pub fn select_page_up(&mut self, _: &SelectPageUp, cx: &mut ViewContext<Self>) {
7579 let Some(row_count) = self.visible_row_count() else {
7580 return;
7581 };
7582
7583 let text_layout_details = &self.text_layout_details(cx);
7584
7585 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7586 s.move_heads_with(|map, head, goal| {
7587 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
7588 })
7589 })
7590 }
7591
7592 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
7593 if self.take_rename(true, cx).is_some() {
7594 return;
7595 }
7596
7597 if self
7598 .context_menu
7599 .write()
7600 .as_mut()
7601 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
7602 .unwrap_or(false)
7603 {
7604 return;
7605 }
7606
7607 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7608 cx.propagate();
7609 return;
7610 }
7611
7612 let Some(row_count) = self.visible_row_count() else {
7613 return;
7614 };
7615
7616 let autoscroll = if action.center_cursor {
7617 Autoscroll::center()
7618 } else {
7619 Autoscroll::fit()
7620 };
7621
7622 let text_layout_details = &self.text_layout_details(cx);
7623
7624 self.change_selections(Some(autoscroll), cx, |s| {
7625 let line_mode = s.line_mode;
7626 s.move_with(|map, selection| {
7627 if !selection.is_empty() && !line_mode {
7628 selection.goal = SelectionGoal::None;
7629 }
7630 let (cursor, goal) = movement::up_by_rows(
7631 map,
7632 selection.end,
7633 row_count,
7634 selection.goal,
7635 false,
7636 text_layout_details,
7637 );
7638 selection.collapse_to(cursor, goal);
7639 });
7640 });
7641 }
7642
7643 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
7644 let text_layout_details = &self.text_layout_details(cx);
7645 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7646 s.move_heads_with(|map, head, goal| {
7647 movement::up(map, head, goal, false, text_layout_details)
7648 })
7649 })
7650 }
7651
7652 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
7653 self.take_rename(true, cx);
7654
7655 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7656 cx.propagate();
7657 return;
7658 }
7659
7660 let text_layout_details = &self.text_layout_details(cx);
7661 let selection_count = self.selections.count();
7662 let first_selection = self.selections.first_anchor();
7663
7664 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7665 let line_mode = s.line_mode;
7666 s.move_with(|map, selection| {
7667 if !selection.is_empty() && !line_mode {
7668 selection.goal = SelectionGoal::None;
7669 }
7670 let (cursor, goal) = movement::down(
7671 map,
7672 selection.end,
7673 selection.goal,
7674 false,
7675 text_layout_details,
7676 );
7677 selection.collapse_to(cursor, goal);
7678 });
7679 });
7680
7681 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7682 {
7683 cx.propagate();
7684 }
7685 }
7686
7687 pub fn select_page_down(&mut self, _: &SelectPageDown, cx: &mut ViewContext<Self>) {
7688 let Some(row_count) = self.visible_row_count() else {
7689 return;
7690 };
7691
7692 let text_layout_details = &self.text_layout_details(cx);
7693
7694 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7695 s.move_heads_with(|map, head, goal| {
7696 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
7697 })
7698 })
7699 }
7700
7701 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
7702 if self.take_rename(true, cx).is_some() {
7703 return;
7704 }
7705
7706 if self
7707 .context_menu
7708 .write()
7709 .as_mut()
7710 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
7711 .unwrap_or(false)
7712 {
7713 return;
7714 }
7715
7716 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7717 cx.propagate();
7718 return;
7719 }
7720
7721 let Some(row_count) = self.visible_row_count() else {
7722 return;
7723 };
7724
7725 let autoscroll = if action.center_cursor {
7726 Autoscroll::center()
7727 } else {
7728 Autoscroll::fit()
7729 };
7730
7731 let text_layout_details = &self.text_layout_details(cx);
7732 self.change_selections(Some(autoscroll), cx, |s| {
7733 let line_mode = s.line_mode;
7734 s.move_with(|map, selection| {
7735 if !selection.is_empty() && !line_mode {
7736 selection.goal = SelectionGoal::None;
7737 }
7738 let (cursor, goal) = movement::down_by_rows(
7739 map,
7740 selection.end,
7741 row_count,
7742 selection.goal,
7743 false,
7744 text_layout_details,
7745 );
7746 selection.collapse_to(cursor, goal);
7747 });
7748 });
7749 }
7750
7751 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
7752 let text_layout_details = &self.text_layout_details(cx);
7753 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7754 s.move_heads_with(|map, head, goal| {
7755 movement::down(map, head, goal, false, text_layout_details)
7756 })
7757 });
7758 }
7759
7760 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
7761 if let Some(context_menu) = self.context_menu.write().as_mut() {
7762 context_menu.select_first(self.completion_provider.as_deref(), cx);
7763 }
7764 }
7765
7766 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
7767 if let Some(context_menu) = self.context_menu.write().as_mut() {
7768 context_menu.select_prev(self.completion_provider.as_deref(), cx);
7769 }
7770 }
7771
7772 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
7773 if let Some(context_menu) = self.context_menu.write().as_mut() {
7774 context_menu.select_next(self.completion_provider.as_deref(), cx);
7775 }
7776 }
7777
7778 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
7779 if let Some(context_menu) = self.context_menu.write().as_mut() {
7780 context_menu.select_last(self.completion_provider.as_deref(), cx);
7781 }
7782 }
7783
7784 pub fn move_to_previous_word_start(
7785 &mut self,
7786 _: &MoveToPreviousWordStart,
7787 cx: &mut ViewContext<Self>,
7788 ) {
7789 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7790 s.move_cursors_with(|map, head, _| {
7791 (
7792 movement::previous_word_start(map, head),
7793 SelectionGoal::None,
7794 )
7795 });
7796 })
7797 }
7798
7799 pub fn move_to_previous_subword_start(
7800 &mut self,
7801 _: &MoveToPreviousSubwordStart,
7802 cx: &mut ViewContext<Self>,
7803 ) {
7804 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7805 s.move_cursors_with(|map, head, _| {
7806 (
7807 movement::previous_subword_start(map, head),
7808 SelectionGoal::None,
7809 )
7810 });
7811 })
7812 }
7813
7814 pub fn select_to_previous_word_start(
7815 &mut self,
7816 _: &SelectToPreviousWordStart,
7817 cx: &mut ViewContext<Self>,
7818 ) {
7819 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7820 s.move_heads_with(|map, head, _| {
7821 (
7822 movement::previous_word_start(map, head),
7823 SelectionGoal::None,
7824 )
7825 });
7826 })
7827 }
7828
7829 pub fn select_to_previous_subword_start(
7830 &mut self,
7831 _: &SelectToPreviousSubwordStart,
7832 cx: &mut ViewContext<Self>,
7833 ) {
7834 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7835 s.move_heads_with(|map, head, _| {
7836 (
7837 movement::previous_subword_start(map, head),
7838 SelectionGoal::None,
7839 )
7840 });
7841 })
7842 }
7843
7844 pub fn delete_to_previous_word_start(
7845 &mut self,
7846 action: &DeleteToPreviousWordStart,
7847 cx: &mut ViewContext<Self>,
7848 ) {
7849 self.transact(cx, |this, cx| {
7850 this.select_autoclose_pair(cx);
7851 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7852 let line_mode = s.line_mode;
7853 s.move_with(|map, selection| {
7854 if selection.is_empty() && !line_mode {
7855 let cursor = if action.ignore_newlines {
7856 movement::previous_word_start(map, selection.head())
7857 } else {
7858 movement::previous_word_start_or_newline(map, selection.head())
7859 };
7860 selection.set_head(cursor, SelectionGoal::None);
7861 }
7862 });
7863 });
7864 this.insert("", cx);
7865 });
7866 }
7867
7868 pub fn delete_to_previous_subword_start(
7869 &mut self,
7870 _: &DeleteToPreviousSubwordStart,
7871 cx: &mut ViewContext<Self>,
7872 ) {
7873 self.transact(cx, |this, cx| {
7874 this.select_autoclose_pair(cx);
7875 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7876 let line_mode = s.line_mode;
7877 s.move_with(|map, selection| {
7878 if selection.is_empty() && !line_mode {
7879 let cursor = movement::previous_subword_start(map, selection.head());
7880 selection.set_head(cursor, SelectionGoal::None);
7881 }
7882 });
7883 });
7884 this.insert("", cx);
7885 });
7886 }
7887
7888 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
7889 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7890 s.move_cursors_with(|map, head, _| {
7891 (movement::next_word_end(map, head), SelectionGoal::None)
7892 });
7893 })
7894 }
7895
7896 pub fn move_to_next_subword_end(
7897 &mut self,
7898 _: &MoveToNextSubwordEnd,
7899 cx: &mut ViewContext<Self>,
7900 ) {
7901 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7902 s.move_cursors_with(|map, head, _| {
7903 (movement::next_subword_end(map, head), SelectionGoal::None)
7904 });
7905 })
7906 }
7907
7908 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
7909 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7910 s.move_heads_with(|map, head, _| {
7911 (movement::next_word_end(map, head), SelectionGoal::None)
7912 });
7913 })
7914 }
7915
7916 pub fn select_to_next_subword_end(
7917 &mut self,
7918 _: &SelectToNextSubwordEnd,
7919 cx: &mut ViewContext<Self>,
7920 ) {
7921 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7922 s.move_heads_with(|map, head, _| {
7923 (movement::next_subword_end(map, head), SelectionGoal::None)
7924 });
7925 })
7926 }
7927
7928 pub fn delete_to_next_word_end(
7929 &mut self,
7930 action: &DeleteToNextWordEnd,
7931 cx: &mut ViewContext<Self>,
7932 ) {
7933 self.transact(cx, |this, cx| {
7934 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7935 let line_mode = s.line_mode;
7936 s.move_with(|map, selection| {
7937 if selection.is_empty() && !line_mode {
7938 let cursor = if action.ignore_newlines {
7939 movement::next_word_end(map, selection.head())
7940 } else {
7941 movement::next_word_end_or_newline(map, selection.head())
7942 };
7943 selection.set_head(cursor, SelectionGoal::None);
7944 }
7945 });
7946 });
7947 this.insert("", cx);
7948 });
7949 }
7950
7951 pub fn delete_to_next_subword_end(
7952 &mut self,
7953 _: &DeleteToNextSubwordEnd,
7954 cx: &mut ViewContext<Self>,
7955 ) {
7956 self.transact(cx, |this, cx| {
7957 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7958 s.move_with(|map, selection| {
7959 if selection.is_empty() {
7960 let cursor = movement::next_subword_end(map, selection.head());
7961 selection.set_head(cursor, SelectionGoal::None);
7962 }
7963 });
7964 });
7965 this.insert("", cx);
7966 });
7967 }
7968
7969 pub fn move_to_beginning_of_line(
7970 &mut self,
7971 action: &MoveToBeginningOfLine,
7972 cx: &mut ViewContext<Self>,
7973 ) {
7974 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7975 s.move_cursors_with(|map, head, _| {
7976 (
7977 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7978 SelectionGoal::None,
7979 )
7980 });
7981 })
7982 }
7983
7984 pub fn select_to_beginning_of_line(
7985 &mut self,
7986 action: &SelectToBeginningOfLine,
7987 cx: &mut ViewContext<Self>,
7988 ) {
7989 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7990 s.move_heads_with(|map, head, _| {
7991 (
7992 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7993 SelectionGoal::None,
7994 )
7995 });
7996 });
7997 }
7998
7999 pub fn delete_to_beginning_of_line(
8000 &mut self,
8001 _: &DeleteToBeginningOfLine,
8002 cx: &mut ViewContext<Self>,
8003 ) {
8004 self.transact(cx, |this, cx| {
8005 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8006 s.move_with(|_, selection| {
8007 selection.reversed = true;
8008 });
8009 });
8010
8011 this.select_to_beginning_of_line(
8012 &SelectToBeginningOfLine {
8013 stop_at_soft_wraps: false,
8014 },
8015 cx,
8016 );
8017 this.backspace(&Backspace, cx);
8018 });
8019 }
8020
8021 pub fn move_to_end_of_line(&mut self, action: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
8022 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8023 s.move_cursors_with(|map, head, _| {
8024 (
8025 movement::line_end(map, head, action.stop_at_soft_wraps),
8026 SelectionGoal::None,
8027 )
8028 });
8029 })
8030 }
8031
8032 pub fn select_to_end_of_line(
8033 &mut self,
8034 action: &SelectToEndOfLine,
8035 cx: &mut ViewContext<Self>,
8036 ) {
8037 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8038 s.move_heads_with(|map, head, _| {
8039 (
8040 movement::line_end(map, head, action.stop_at_soft_wraps),
8041 SelectionGoal::None,
8042 )
8043 });
8044 })
8045 }
8046
8047 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
8048 self.transact(cx, |this, cx| {
8049 this.select_to_end_of_line(
8050 &SelectToEndOfLine {
8051 stop_at_soft_wraps: false,
8052 },
8053 cx,
8054 );
8055 this.delete(&Delete, cx);
8056 });
8057 }
8058
8059 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
8060 self.transact(cx, |this, cx| {
8061 this.select_to_end_of_line(
8062 &SelectToEndOfLine {
8063 stop_at_soft_wraps: false,
8064 },
8065 cx,
8066 );
8067 this.cut(&Cut, cx);
8068 });
8069 }
8070
8071 pub fn move_to_start_of_paragraph(
8072 &mut self,
8073 _: &MoveToStartOfParagraph,
8074 cx: &mut ViewContext<Self>,
8075 ) {
8076 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8077 cx.propagate();
8078 return;
8079 }
8080
8081 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8082 s.move_with(|map, selection| {
8083 selection.collapse_to(
8084 movement::start_of_paragraph(map, selection.head(), 1),
8085 SelectionGoal::None,
8086 )
8087 });
8088 })
8089 }
8090
8091 pub fn move_to_end_of_paragraph(
8092 &mut self,
8093 _: &MoveToEndOfParagraph,
8094 cx: &mut ViewContext<Self>,
8095 ) {
8096 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8097 cx.propagate();
8098 return;
8099 }
8100
8101 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8102 s.move_with(|map, selection| {
8103 selection.collapse_to(
8104 movement::end_of_paragraph(map, selection.head(), 1),
8105 SelectionGoal::None,
8106 )
8107 });
8108 })
8109 }
8110
8111 pub fn select_to_start_of_paragraph(
8112 &mut self,
8113 _: &SelectToStartOfParagraph,
8114 cx: &mut ViewContext<Self>,
8115 ) {
8116 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8117 cx.propagate();
8118 return;
8119 }
8120
8121 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8122 s.move_heads_with(|map, head, _| {
8123 (
8124 movement::start_of_paragraph(map, head, 1),
8125 SelectionGoal::None,
8126 )
8127 });
8128 })
8129 }
8130
8131 pub fn select_to_end_of_paragraph(
8132 &mut self,
8133 _: &SelectToEndOfParagraph,
8134 cx: &mut ViewContext<Self>,
8135 ) {
8136 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8137 cx.propagate();
8138 return;
8139 }
8140
8141 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8142 s.move_heads_with(|map, head, _| {
8143 (
8144 movement::end_of_paragraph(map, head, 1),
8145 SelectionGoal::None,
8146 )
8147 });
8148 })
8149 }
8150
8151 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
8152 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8153 cx.propagate();
8154 return;
8155 }
8156
8157 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8158 s.select_ranges(vec![0..0]);
8159 });
8160 }
8161
8162 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
8163 let mut selection = self.selections.last::<Point>(cx);
8164 selection.set_head(Point::zero(), SelectionGoal::None);
8165
8166 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8167 s.select(vec![selection]);
8168 });
8169 }
8170
8171 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
8172 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8173 cx.propagate();
8174 return;
8175 }
8176
8177 let cursor = self.buffer.read(cx).read(cx).len();
8178 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8179 s.select_ranges(vec![cursor..cursor])
8180 });
8181 }
8182
8183 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
8184 self.nav_history = nav_history;
8185 }
8186
8187 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
8188 self.nav_history.as_ref()
8189 }
8190
8191 fn push_to_nav_history(
8192 &mut self,
8193 cursor_anchor: Anchor,
8194 new_position: Option<Point>,
8195 cx: &mut ViewContext<Self>,
8196 ) {
8197 if let Some(nav_history) = self.nav_history.as_mut() {
8198 let buffer = self.buffer.read(cx).read(cx);
8199 let cursor_position = cursor_anchor.to_point(&buffer);
8200 let scroll_state = self.scroll_manager.anchor();
8201 let scroll_top_row = scroll_state.top_row(&buffer);
8202 drop(buffer);
8203
8204 if let Some(new_position) = new_position {
8205 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
8206 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
8207 return;
8208 }
8209 }
8210
8211 nav_history.push(
8212 Some(NavigationData {
8213 cursor_anchor,
8214 cursor_position,
8215 scroll_anchor: scroll_state,
8216 scroll_top_row,
8217 }),
8218 cx,
8219 );
8220 }
8221 }
8222
8223 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
8224 let buffer = self.buffer.read(cx).snapshot(cx);
8225 let mut selection = self.selections.first::<usize>(cx);
8226 selection.set_head(buffer.len(), SelectionGoal::None);
8227 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8228 s.select(vec![selection]);
8229 });
8230 }
8231
8232 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
8233 let end = self.buffer.read(cx).read(cx).len();
8234 self.change_selections(None, cx, |s| {
8235 s.select_ranges(vec![0..end]);
8236 });
8237 }
8238
8239 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
8240 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8241 let mut selections = self.selections.all::<Point>(cx);
8242 let max_point = display_map.buffer_snapshot.max_point();
8243 for selection in &mut selections {
8244 let rows = selection.spanned_rows(true, &display_map);
8245 selection.start = Point::new(rows.start.0, 0);
8246 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
8247 selection.reversed = false;
8248 }
8249 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8250 s.select(selections);
8251 });
8252 }
8253
8254 pub fn split_selection_into_lines(
8255 &mut self,
8256 _: &SplitSelectionIntoLines,
8257 cx: &mut ViewContext<Self>,
8258 ) {
8259 let mut to_unfold = Vec::new();
8260 let mut new_selection_ranges = Vec::new();
8261 {
8262 let selections = self.selections.all::<Point>(cx);
8263 let buffer = self.buffer.read(cx).read(cx);
8264 for selection in selections {
8265 for row in selection.start.row..selection.end.row {
8266 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
8267 new_selection_ranges.push(cursor..cursor);
8268 }
8269 new_selection_ranges.push(selection.end..selection.end);
8270 to_unfold.push(selection.start..selection.end);
8271 }
8272 }
8273 self.unfold_ranges(&to_unfold, true, true, cx);
8274 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8275 s.select_ranges(new_selection_ranges);
8276 });
8277 }
8278
8279 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
8280 self.add_selection(true, cx);
8281 }
8282
8283 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
8284 self.add_selection(false, cx);
8285 }
8286
8287 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
8288 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8289 let mut selections = self.selections.all::<Point>(cx);
8290 let text_layout_details = self.text_layout_details(cx);
8291 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
8292 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
8293 let range = oldest_selection.display_range(&display_map).sorted();
8294
8295 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
8296 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
8297 let positions = start_x.min(end_x)..start_x.max(end_x);
8298
8299 selections.clear();
8300 let mut stack = Vec::new();
8301 for row in range.start.row().0..=range.end.row().0 {
8302 if let Some(selection) = self.selections.build_columnar_selection(
8303 &display_map,
8304 DisplayRow(row),
8305 &positions,
8306 oldest_selection.reversed,
8307 &text_layout_details,
8308 ) {
8309 stack.push(selection.id);
8310 selections.push(selection);
8311 }
8312 }
8313
8314 if above {
8315 stack.reverse();
8316 }
8317
8318 AddSelectionsState { above, stack }
8319 });
8320
8321 let last_added_selection = *state.stack.last().unwrap();
8322 let mut new_selections = Vec::new();
8323 if above == state.above {
8324 let end_row = if above {
8325 DisplayRow(0)
8326 } else {
8327 display_map.max_point().row()
8328 };
8329
8330 'outer: for selection in selections {
8331 if selection.id == last_added_selection {
8332 let range = selection.display_range(&display_map).sorted();
8333 debug_assert_eq!(range.start.row(), range.end.row());
8334 let mut row = range.start.row();
8335 let positions =
8336 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
8337 px(start)..px(end)
8338 } else {
8339 let start_x =
8340 display_map.x_for_display_point(range.start, &text_layout_details);
8341 let end_x =
8342 display_map.x_for_display_point(range.end, &text_layout_details);
8343 start_x.min(end_x)..start_x.max(end_x)
8344 };
8345
8346 while row != end_row {
8347 if above {
8348 row.0 -= 1;
8349 } else {
8350 row.0 += 1;
8351 }
8352
8353 if let Some(new_selection) = self.selections.build_columnar_selection(
8354 &display_map,
8355 row,
8356 &positions,
8357 selection.reversed,
8358 &text_layout_details,
8359 ) {
8360 state.stack.push(new_selection.id);
8361 if above {
8362 new_selections.push(new_selection);
8363 new_selections.push(selection);
8364 } else {
8365 new_selections.push(selection);
8366 new_selections.push(new_selection);
8367 }
8368
8369 continue 'outer;
8370 }
8371 }
8372 }
8373
8374 new_selections.push(selection);
8375 }
8376 } else {
8377 new_selections = selections;
8378 new_selections.retain(|s| s.id != last_added_selection);
8379 state.stack.pop();
8380 }
8381
8382 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8383 s.select(new_selections);
8384 });
8385 if state.stack.len() > 1 {
8386 self.add_selections_state = Some(state);
8387 }
8388 }
8389
8390 pub fn select_next_match_internal(
8391 &mut self,
8392 display_map: &DisplaySnapshot,
8393 replace_newest: bool,
8394 autoscroll: Option<Autoscroll>,
8395 cx: &mut ViewContext<Self>,
8396 ) -> Result<()> {
8397 fn select_next_match_ranges(
8398 this: &mut Editor,
8399 range: Range<usize>,
8400 replace_newest: bool,
8401 auto_scroll: Option<Autoscroll>,
8402 cx: &mut ViewContext<Editor>,
8403 ) {
8404 this.unfold_ranges(&[range.clone()], false, true, cx);
8405 this.change_selections(auto_scroll, cx, |s| {
8406 if replace_newest {
8407 s.delete(s.newest_anchor().id);
8408 }
8409 s.insert_range(range.clone());
8410 });
8411 }
8412
8413 let buffer = &display_map.buffer_snapshot;
8414 let mut selections = self.selections.all::<usize>(cx);
8415 if let Some(mut select_next_state) = self.select_next_state.take() {
8416 let query = &select_next_state.query;
8417 if !select_next_state.done {
8418 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
8419 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
8420 let mut next_selected_range = None;
8421
8422 let bytes_after_last_selection =
8423 buffer.bytes_in_range(last_selection.end..buffer.len());
8424 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
8425 let query_matches = query
8426 .stream_find_iter(bytes_after_last_selection)
8427 .map(|result| (last_selection.end, result))
8428 .chain(
8429 query
8430 .stream_find_iter(bytes_before_first_selection)
8431 .map(|result| (0, result)),
8432 );
8433
8434 for (start_offset, query_match) in query_matches {
8435 let query_match = query_match.unwrap(); // can only fail due to I/O
8436 let offset_range =
8437 start_offset + query_match.start()..start_offset + query_match.end();
8438 let display_range = offset_range.start.to_display_point(display_map)
8439 ..offset_range.end.to_display_point(display_map);
8440
8441 if !select_next_state.wordwise
8442 || (!movement::is_inside_word(display_map, display_range.start)
8443 && !movement::is_inside_word(display_map, display_range.end))
8444 {
8445 // TODO: This is n^2, because we might check all the selections
8446 if !selections
8447 .iter()
8448 .any(|selection| selection.range().overlaps(&offset_range))
8449 {
8450 next_selected_range = Some(offset_range);
8451 break;
8452 }
8453 }
8454 }
8455
8456 if let Some(next_selected_range) = next_selected_range {
8457 select_next_match_ranges(
8458 self,
8459 next_selected_range,
8460 replace_newest,
8461 autoscroll,
8462 cx,
8463 );
8464 } else {
8465 select_next_state.done = true;
8466 }
8467 }
8468
8469 self.select_next_state = Some(select_next_state);
8470 } else {
8471 let mut only_carets = true;
8472 let mut same_text_selected = true;
8473 let mut selected_text = None;
8474
8475 let mut selections_iter = selections.iter().peekable();
8476 while let Some(selection) = selections_iter.next() {
8477 if selection.start != selection.end {
8478 only_carets = false;
8479 }
8480
8481 if same_text_selected {
8482 if selected_text.is_none() {
8483 selected_text =
8484 Some(buffer.text_for_range(selection.range()).collect::<String>());
8485 }
8486
8487 if let Some(next_selection) = selections_iter.peek() {
8488 if next_selection.range().len() == selection.range().len() {
8489 let next_selected_text = buffer
8490 .text_for_range(next_selection.range())
8491 .collect::<String>();
8492 if Some(next_selected_text) != selected_text {
8493 same_text_selected = false;
8494 selected_text = None;
8495 }
8496 } else {
8497 same_text_selected = false;
8498 selected_text = None;
8499 }
8500 }
8501 }
8502 }
8503
8504 if only_carets {
8505 for selection in &mut selections {
8506 let word_range = movement::surrounding_word(
8507 display_map,
8508 selection.start.to_display_point(display_map),
8509 );
8510 selection.start = word_range.start.to_offset(display_map, Bias::Left);
8511 selection.end = word_range.end.to_offset(display_map, Bias::Left);
8512 selection.goal = SelectionGoal::None;
8513 selection.reversed = false;
8514 select_next_match_ranges(
8515 self,
8516 selection.start..selection.end,
8517 replace_newest,
8518 autoscroll,
8519 cx,
8520 );
8521 }
8522
8523 if selections.len() == 1 {
8524 let selection = selections
8525 .last()
8526 .expect("ensured that there's only one selection");
8527 let query = buffer
8528 .text_for_range(selection.start..selection.end)
8529 .collect::<String>();
8530 let is_empty = query.is_empty();
8531 let select_state = SelectNextState {
8532 query: AhoCorasick::new(&[query])?,
8533 wordwise: true,
8534 done: is_empty,
8535 };
8536 self.select_next_state = Some(select_state);
8537 } else {
8538 self.select_next_state = None;
8539 }
8540 } else if let Some(selected_text) = selected_text {
8541 self.select_next_state = Some(SelectNextState {
8542 query: AhoCorasick::new(&[selected_text])?,
8543 wordwise: false,
8544 done: false,
8545 });
8546 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
8547 }
8548 }
8549 Ok(())
8550 }
8551
8552 pub fn select_all_matches(
8553 &mut self,
8554 _action: &SelectAllMatches,
8555 cx: &mut ViewContext<Self>,
8556 ) -> Result<()> {
8557 self.push_to_selection_history();
8558 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8559
8560 self.select_next_match_internal(&display_map, false, None, cx)?;
8561 let Some(select_next_state) = self.select_next_state.as_mut() else {
8562 return Ok(());
8563 };
8564 if select_next_state.done {
8565 return Ok(());
8566 }
8567
8568 let mut new_selections = self.selections.all::<usize>(cx);
8569
8570 let buffer = &display_map.buffer_snapshot;
8571 let query_matches = select_next_state
8572 .query
8573 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
8574
8575 for query_match in query_matches {
8576 let query_match = query_match.unwrap(); // can only fail due to I/O
8577 let offset_range = query_match.start()..query_match.end();
8578 let display_range = offset_range.start.to_display_point(&display_map)
8579 ..offset_range.end.to_display_point(&display_map);
8580
8581 if !select_next_state.wordwise
8582 || (!movement::is_inside_word(&display_map, display_range.start)
8583 && !movement::is_inside_word(&display_map, display_range.end))
8584 {
8585 self.selections.change_with(cx, |selections| {
8586 new_selections.push(Selection {
8587 id: selections.new_selection_id(),
8588 start: offset_range.start,
8589 end: offset_range.end,
8590 reversed: false,
8591 goal: SelectionGoal::None,
8592 });
8593 });
8594 }
8595 }
8596
8597 new_selections.sort_by_key(|selection| selection.start);
8598 let mut ix = 0;
8599 while ix + 1 < new_selections.len() {
8600 let current_selection = &new_selections[ix];
8601 let next_selection = &new_selections[ix + 1];
8602 if current_selection.range().overlaps(&next_selection.range()) {
8603 if current_selection.id < next_selection.id {
8604 new_selections.remove(ix + 1);
8605 } else {
8606 new_selections.remove(ix);
8607 }
8608 } else {
8609 ix += 1;
8610 }
8611 }
8612
8613 select_next_state.done = true;
8614 self.unfold_ranges(
8615 &new_selections
8616 .iter()
8617 .map(|selection| selection.range())
8618 .collect::<Vec<_>>(),
8619 false,
8620 false,
8621 cx,
8622 );
8623 self.change_selections(Some(Autoscroll::fit()), cx, |selections| {
8624 selections.select(new_selections)
8625 });
8626
8627 Ok(())
8628 }
8629
8630 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
8631 self.push_to_selection_history();
8632 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8633 self.select_next_match_internal(
8634 &display_map,
8635 action.replace_newest,
8636 Some(Autoscroll::newest()),
8637 cx,
8638 )?;
8639 Ok(())
8640 }
8641
8642 pub fn select_previous(
8643 &mut self,
8644 action: &SelectPrevious,
8645 cx: &mut ViewContext<Self>,
8646 ) -> Result<()> {
8647 self.push_to_selection_history();
8648 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8649 let buffer = &display_map.buffer_snapshot;
8650 let mut selections = self.selections.all::<usize>(cx);
8651 if let Some(mut select_prev_state) = self.select_prev_state.take() {
8652 let query = &select_prev_state.query;
8653 if !select_prev_state.done {
8654 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
8655 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
8656 let mut next_selected_range = None;
8657 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
8658 let bytes_before_last_selection =
8659 buffer.reversed_bytes_in_range(0..last_selection.start);
8660 let bytes_after_first_selection =
8661 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
8662 let query_matches = query
8663 .stream_find_iter(bytes_before_last_selection)
8664 .map(|result| (last_selection.start, result))
8665 .chain(
8666 query
8667 .stream_find_iter(bytes_after_first_selection)
8668 .map(|result| (buffer.len(), result)),
8669 );
8670 for (end_offset, query_match) in query_matches {
8671 let query_match = query_match.unwrap(); // can only fail due to I/O
8672 let offset_range =
8673 end_offset - query_match.end()..end_offset - query_match.start();
8674 let display_range = offset_range.start.to_display_point(&display_map)
8675 ..offset_range.end.to_display_point(&display_map);
8676
8677 if !select_prev_state.wordwise
8678 || (!movement::is_inside_word(&display_map, display_range.start)
8679 && !movement::is_inside_word(&display_map, display_range.end))
8680 {
8681 next_selected_range = Some(offset_range);
8682 break;
8683 }
8684 }
8685
8686 if let Some(next_selected_range) = next_selected_range {
8687 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
8688 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8689 if action.replace_newest {
8690 s.delete(s.newest_anchor().id);
8691 }
8692 s.insert_range(next_selected_range);
8693 });
8694 } else {
8695 select_prev_state.done = true;
8696 }
8697 }
8698
8699 self.select_prev_state = Some(select_prev_state);
8700 } else {
8701 let mut only_carets = true;
8702 let mut same_text_selected = true;
8703 let mut selected_text = None;
8704
8705 let mut selections_iter = selections.iter().peekable();
8706 while let Some(selection) = selections_iter.next() {
8707 if selection.start != selection.end {
8708 only_carets = false;
8709 }
8710
8711 if same_text_selected {
8712 if selected_text.is_none() {
8713 selected_text =
8714 Some(buffer.text_for_range(selection.range()).collect::<String>());
8715 }
8716
8717 if let Some(next_selection) = selections_iter.peek() {
8718 if next_selection.range().len() == selection.range().len() {
8719 let next_selected_text = buffer
8720 .text_for_range(next_selection.range())
8721 .collect::<String>();
8722 if Some(next_selected_text) != selected_text {
8723 same_text_selected = false;
8724 selected_text = None;
8725 }
8726 } else {
8727 same_text_selected = false;
8728 selected_text = None;
8729 }
8730 }
8731 }
8732 }
8733
8734 if only_carets {
8735 for selection in &mut selections {
8736 let word_range = movement::surrounding_word(
8737 &display_map,
8738 selection.start.to_display_point(&display_map),
8739 );
8740 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
8741 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
8742 selection.goal = SelectionGoal::None;
8743 selection.reversed = false;
8744 }
8745 if selections.len() == 1 {
8746 let selection = selections
8747 .last()
8748 .expect("ensured that there's only one selection");
8749 let query = buffer
8750 .text_for_range(selection.start..selection.end)
8751 .collect::<String>();
8752 let is_empty = query.is_empty();
8753 let select_state = SelectNextState {
8754 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
8755 wordwise: true,
8756 done: is_empty,
8757 };
8758 self.select_prev_state = Some(select_state);
8759 } else {
8760 self.select_prev_state = None;
8761 }
8762
8763 self.unfold_ranges(
8764 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
8765 false,
8766 true,
8767 cx,
8768 );
8769 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8770 s.select(selections);
8771 });
8772 } else if let Some(selected_text) = selected_text {
8773 self.select_prev_state = Some(SelectNextState {
8774 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
8775 wordwise: false,
8776 done: false,
8777 });
8778 self.select_previous(action, cx)?;
8779 }
8780 }
8781 Ok(())
8782 }
8783
8784 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
8785 if self.read_only(cx) {
8786 return;
8787 }
8788 let text_layout_details = &self.text_layout_details(cx);
8789 self.transact(cx, |this, cx| {
8790 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
8791 let mut edits = Vec::new();
8792 let mut selection_edit_ranges = Vec::new();
8793 let mut last_toggled_row = None;
8794 let snapshot = this.buffer.read(cx).read(cx);
8795 let empty_str: Arc<str> = Arc::default();
8796 let mut suffixes_inserted = Vec::new();
8797 let ignore_indent = action.ignore_indent;
8798
8799 fn comment_prefix_range(
8800 snapshot: &MultiBufferSnapshot,
8801 row: MultiBufferRow,
8802 comment_prefix: &str,
8803 comment_prefix_whitespace: &str,
8804 ignore_indent: bool,
8805 ) -> Range<Point> {
8806 let indent_size = if ignore_indent {
8807 0
8808 } else {
8809 snapshot.indent_size_for_line(row).len
8810 };
8811
8812 let start = Point::new(row.0, indent_size);
8813
8814 let mut line_bytes = snapshot
8815 .bytes_in_range(start..snapshot.max_point())
8816 .flatten()
8817 .copied();
8818
8819 // If this line currently begins with the line comment prefix, then record
8820 // the range containing the prefix.
8821 if line_bytes
8822 .by_ref()
8823 .take(comment_prefix.len())
8824 .eq(comment_prefix.bytes())
8825 {
8826 // Include any whitespace that matches the comment prefix.
8827 let matching_whitespace_len = line_bytes
8828 .zip(comment_prefix_whitespace.bytes())
8829 .take_while(|(a, b)| a == b)
8830 .count() as u32;
8831 let end = Point::new(
8832 start.row,
8833 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
8834 );
8835 start..end
8836 } else {
8837 start..start
8838 }
8839 }
8840
8841 fn comment_suffix_range(
8842 snapshot: &MultiBufferSnapshot,
8843 row: MultiBufferRow,
8844 comment_suffix: &str,
8845 comment_suffix_has_leading_space: bool,
8846 ) -> Range<Point> {
8847 let end = Point::new(row.0, snapshot.line_len(row));
8848 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
8849
8850 let mut line_end_bytes = snapshot
8851 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
8852 .flatten()
8853 .copied();
8854
8855 let leading_space_len = if suffix_start_column > 0
8856 && line_end_bytes.next() == Some(b' ')
8857 && comment_suffix_has_leading_space
8858 {
8859 1
8860 } else {
8861 0
8862 };
8863
8864 // If this line currently begins with the line comment prefix, then record
8865 // the range containing the prefix.
8866 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
8867 let start = Point::new(end.row, suffix_start_column - leading_space_len);
8868 start..end
8869 } else {
8870 end..end
8871 }
8872 }
8873
8874 // TODO: Handle selections that cross excerpts
8875 for selection in &mut selections {
8876 let start_column = snapshot
8877 .indent_size_for_line(MultiBufferRow(selection.start.row))
8878 .len;
8879 let language = if let Some(language) =
8880 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
8881 {
8882 language
8883 } else {
8884 continue;
8885 };
8886
8887 selection_edit_ranges.clear();
8888
8889 // If multiple selections contain a given row, avoid processing that
8890 // row more than once.
8891 let mut start_row = MultiBufferRow(selection.start.row);
8892 if last_toggled_row == Some(start_row) {
8893 start_row = start_row.next_row();
8894 }
8895 let end_row =
8896 if selection.end.row > selection.start.row && selection.end.column == 0 {
8897 MultiBufferRow(selection.end.row - 1)
8898 } else {
8899 MultiBufferRow(selection.end.row)
8900 };
8901 last_toggled_row = Some(end_row);
8902
8903 if start_row > end_row {
8904 continue;
8905 }
8906
8907 // If the language has line comments, toggle those.
8908 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
8909
8910 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
8911 if ignore_indent {
8912 full_comment_prefixes = full_comment_prefixes
8913 .into_iter()
8914 .map(|s| Arc::from(s.trim_end()))
8915 .collect();
8916 }
8917
8918 if !full_comment_prefixes.is_empty() {
8919 let first_prefix = full_comment_prefixes
8920 .first()
8921 .expect("prefixes is non-empty");
8922 let prefix_trimmed_lengths = full_comment_prefixes
8923 .iter()
8924 .map(|p| p.trim_end_matches(' ').len())
8925 .collect::<SmallVec<[usize; 4]>>();
8926
8927 let mut all_selection_lines_are_comments = true;
8928
8929 for row in start_row.0..=end_row.0 {
8930 let row = MultiBufferRow(row);
8931 if start_row < end_row && snapshot.is_line_blank(row) {
8932 continue;
8933 }
8934
8935 let prefix_range = full_comment_prefixes
8936 .iter()
8937 .zip(prefix_trimmed_lengths.iter().copied())
8938 .map(|(prefix, trimmed_prefix_len)| {
8939 comment_prefix_range(
8940 snapshot.deref(),
8941 row,
8942 &prefix[..trimmed_prefix_len],
8943 &prefix[trimmed_prefix_len..],
8944 ignore_indent,
8945 )
8946 })
8947 .max_by_key(|range| range.end.column - range.start.column)
8948 .expect("prefixes is non-empty");
8949
8950 if prefix_range.is_empty() {
8951 all_selection_lines_are_comments = false;
8952 }
8953
8954 selection_edit_ranges.push(prefix_range);
8955 }
8956
8957 if all_selection_lines_are_comments {
8958 edits.extend(
8959 selection_edit_ranges
8960 .iter()
8961 .cloned()
8962 .map(|range| (range, empty_str.clone())),
8963 );
8964 } else {
8965 let min_column = selection_edit_ranges
8966 .iter()
8967 .map(|range| range.start.column)
8968 .min()
8969 .unwrap_or(0);
8970 edits.extend(selection_edit_ranges.iter().map(|range| {
8971 let position = Point::new(range.start.row, min_column);
8972 (position..position, first_prefix.clone())
8973 }));
8974 }
8975 } else if let Some((full_comment_prefix, comment_suffix)) =
8976 language.block_comment_delimiters()
8977 {
8978 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
8979 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
8980 let prefix_range = comment_prefix_range(
8981 snapshot.deref(),
8982 start_row,
8983 comment_prefix,
8984 comment_prefix_whitespace,
8985 ignore_indent,
8986 );
8987 let suffix_range = comment_suffix_range(
8988 snapshot.deref(),
8989 end_row,
8990 comment_suffix.trim_start_matches(' '),
8991 comment_suffix.starts_with(' '),
8992 );
8993
8994 if prefix_range.is_empty() || suffix_range.is_empty() {
8995 edits.push((
8996 prefix_range.start..prefix_range.start,
8997 full_comment_prefix.clone(),
8998 ));
8999 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
9000 suffixes_inserted.push((end_row, comment_suffix.len()));
9001 } else {
9002 edits.push((prefix_range, empty_str.clone()));
9003 edits.push((suffix_range, empty_str.clone()));
9004 }
9005 } else {
9006 continue;
9007 }
9008 }
9009
9010 drop(snapshot);
9011 this.buffer.update(cx, |buffer, cx| {
9012 buffer.edit(edits, None, cx);
9013 });
9014
9015 // Adjust selections so that they end before any comment suffixes that
9016 // were inserted.
9017 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
9018 let mut selections = this.selections.all::<Point>(cx);
9019 let snapshot = this.buffer.read(cx).read(cx);
9020 for selection in &mut selections {
9021 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
9022 match row.cmp(&MultiBufferRow(selection.end.row)) {
9023 Ordering::Less => {
9024 suffixes_inserted.next();
9025 continue;
9026 }
9027 Ordering::Greater => break,
9028 Ordering::Equal => {
9029 if selection.end.column == snapshot.line_len(row) {
9030 if selection.is_empty() {
9031 selection.start.column -= suffix_len as u32;
9032 }
9033 selection.end.column -= suffix_len as u32;
9034 }
9035 break;
9036 }
9037 }
9038 }
9039 }
9040
9041 drop(snapshot);
9042 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
9043
9044 let selections = this.selections.all::<Point>(cx);
9045 let selections_on_single_row = selections.windows(2).all(|selections| {
9046 selections[0].start.row == selections[1].start.row
9047 && selections[0].end.row == selections[1].end.row
9048 && selections[0].start.row == selections[0].end.row
9049 });
9050 let selections_selecting = selections
9051 .iter()
9052 .any(|selection| selection.start != selection.end);
9053 let advance_downwards = action.advance_downwards
9054 && selections_on_single_row
9055 && !selections_selecting
9056 && !matches!(this.mode, EditorMode::SingleLine { .. });
9057
9058 if advance_downwards {
9059 let snapshot = this.buffer.read(cx).snapshot(cx);
9060
9061 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
9062 s.move_cursors_with(|display_snapshot, display_point, _| {
9063 let mut point = display_point.to_point(display_snapshot);
9064 point.row += 1;
9065 point = snapshot.clip_point(point, Bias::Left);
9066 let display_point = point.to_display_point(display_snapshot);
9067 let goal = SelectionGoal::HorizontalPosition(
9068 display_snapshot
9069 .x_for_display_point(display_point, text_layout_details)
9070 .into(),
9071 );
9072 (display_point, goal)
9073 })
9074 });
9075 }
9076 });
9077 }
9078
9079 pub fn select_enclosing_symbol(
9080 &mut self,
9081 _: &SelectEnclosingSymbol,
9082 cx: &mut ViewContext<Self>,
9083 ) {
9084 let buffer = self.buffer.read(cx).snapshot(cx);
9085 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
9086
9087 fn update_selection(
9088 selection: &Selection<usize>,
9089 buffer_snap: &MultiBufferSnapshot,
9090 ) -> Option<Selection<usize>> {
9091 let cursor = selection.head();
9092 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
9093 for symbol in symbols.iter().rev() {
9094 let start = symbol.range.start.to_offset(buffer_snap);
9095 let end = symbol.range.end.to_offset(buffer_snap);
9096 let new_range = start..end;
9097 if start < selection.start || end > selection.end {
9098 return Some(Selection {
9099 id: selection.id,
9100 start: new_range.start,
9101 end: new_range.end,
9102 goal: SelectionGoal::None,
9103 reversed: selection.reversed,
9104 });
9105 }
9106 }
9107 None
9108 }
9109
9110 let mut selected_larger_symbol = false;
9111 let new_selections = old_selections
9112 .iter()
9113 .map(|selection| match update_selection(selection, &buffer) {
9114 Some(new_selection) => {
9115 if new_selection.range() != selection.range() {
9116 selected_larger_symbol = true;
9117 }
9118 new_selection
9119 }
9120 None => selection.clone(),
9121 })
9122 .collect::<Vec<_>>();
9123
9124 if selected_larger_symbol {
9125 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9126 s.select(new_selections);
9127 });
9128 }
9129 }
9130
9131 pub fn select_larger_syntax_node(
9132 &mut self,
9133 _: &SelectLargerSyntaxNode,
9134 cx: &mut ViewContext<Self>,
9135 ) {
9136 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9137 let buffer = self.buffer.read(cx).snapshot(cx);
9138 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
9139
9140 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
9141 let mut selected_larger_node = false;
9142 let new_selections = old_selections
9143 .iter()
9144 .map(|selection| {
9145 let old_range = selection.start..selection.end;
9146 let mut new_range = old_range.clone();
9147 while let Some(containing_range) =
9148 buffer.range_for_syntax_ancestor(new_range.clone())
9149 {
9150 new_range = containing_range;
9151 if !display_map.intersects_fold(new_range.start)
9152 && !display_map.intersects_fold(new_range.end)
9153 {
9154 break;
9155 }
9156 }
9157
9158 selected_larger_node |= new_range != old_range;
9159 Selection {
9160 id: selection.id,
9161 start: new_range.start,
9162 end: new_range.end,
9163 goal: SelectionGoal::None,
9164 reversed: selection.reversed,
9165 }
9166 })
9167 .collect::<Vec<_>>();
9168
9169 if selected_larger_node {
9170 stack.push(old_selections);
9171 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9172 s.select(new_selections);
9173 });
9174 }
9175 self.select_larger_syntax_node_stack = stack;
9176 }
9177
9178 pub fn select_smaller_syntax_node(
9179 &mut self,
9180 _: &SelectSmallerSyntaxNode,
9181 cx: &mut ViewContext<Self>,
9182 ) {
9183 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
9184 if let Some(selections) = stack.pop() {
9185 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9186 s.select(selections.to_vec());
9187 });
9188 }
9189 self.select_larger_syntax_node_stack = stack;
9190 }
9191
9192 fn refresh_runnables(&mut self, cx: &mut ViewContext<Self>) -> Task<()> {
9193 if !EditorSettings::get_global(cx).gutter.runnables {
9194 self.clear_tasks();
9195 return Task::ready(());
9196 }
9197 let project = self.project.as_ref().map(Model::downgrade);
9198 cx.spawn(|this, mut cx| async move {
9199 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
9200 let Some(project) = project.and_then(|p| p.upgrade()) else {
9201 return;
9202 };
9203 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
9204 this.display_map.update(cx, |map, cx| map.snapshot(cx))
9205 }) else {
9206 return;
9207 };
9208
9209 let hide_runnables = project
9210 .update(&mut cx, |project, cx| {
9211 // Do not display any test indicators in non-dev server remote projects.
9212 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
9213 })
9214 .unwrap_or(true);
9215 if hide_runnables {
9216 return;
9217 }
9218 let new_rows =
9219 cx.background_executor()
9220 .spawn({
9221 let snapshot = display_snapshot.clone();
9222 async move {
9223 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
9224 }
9225 })
9226 .await;
9227 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
9228
9229 this.update(&mut cx, |this, _| {
9230 this.clear_tasks();
9231 for (key, value) in rows {
9232 this.insert_tasks(key, value);
9233 }
9234 })
9235 .ok();
9236 })
9237 }
9238 fn fetch_runnable_ranges(
9239 snapshot: &DisplaySnapshot,
9240 range: Range<Anchor>,
9241 ) -> Vec<language::RunnableRange> {
9242 snapshot.buffer_snapshot.runnable_ranges(range).collect()
9243 }
9244
9245 fn runnable_rows(
9246 project: Model<Project>,
9247 snapshot: DisplaySnapshot,
9248 runnable_ranges: Vec<RunnableRange>,
9249 mut cx: AsyncWindowContext,
9250 ) -> Vec<((BufferId, u32), RunnableTasks)> {
9251 runnable_ranges
9252 .into_iter()
9253 .filter_map(|mut runnable| {
9254 let tasks = cx
9255 .update(|cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
9256 .ok()?;
9257 if tasks.is_empty() {
9258 return None;
9259 }
9260
9261 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
9262
9263 let row = snapshot
9264 .buffer_snapshot
9265 .buffer_line_for_row(MultiBufferRow(point.row))?
9266 .1
9267 .start
9268 .row;
9269
9270 let context_range =
9271 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
9272 Some((
9273 (runnable.buffer_id, row),
9274 RunnableTasks {
9275 templates: tasks,
9276 offset: MultiBufferOffset(runnable.run_range.start),
9277 context_range,
9278 column: point.column,
9279 extra_variables: runnable.extra_captures,
9280 },
9281 ))
9282 })
9283 .collect()
9284 }
9285
9286 fn templates_with_tags(
9287 project: &Model<Project>,
9288 runnable: &mut Runnable,
9289 cx: &WindowContext<'_>,
9290 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
9291 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
9292 let (worktree_id, file) = project
9293 .buffer_for_id(runnable.buffer, cx)
9294 .and_then(|buffer| buffer.read(cx).file())
9295 .map(|file| (file.worktree_id(cx), file.clone()))
9296 .unzip();
9297
9298 (
9299 project.task_store().read(cx).task_inventory().cloned(),
9300 worktree_id,
9301 file,
9302 )
9303 });
9304
9305 let tags = mem::take(&mut runnable.tags);
9306 let mut tags: Vec<_> = tags
9307 .into_iter()
9308 .flat_map(|tag| {
9309 let tag = tag.0.clone();
9310 inventory
9311 .as_ref()
9312 .into_iter()
9313 .flat_map(|inventory| {
9314 inventory.read(cx).list_tasks(
9315 file.clone(),
9316 Some(runnable.language.clone()),
9317 worktree_id,
9318 cx,
9319 )
9320 })
9321 .filter(move |(_, template)| {
9322 template.tags.iter().any(|source_tag| source_tag == &tag)
9323 })
9324 })
9325 .sorted_by_key(|(kind, _)| kind.to_owned())
9326 .collect();
9327 if let Some((leading_tag_source, _)) = tags.first() {
9328 // Strongest source wins; if we have worktree tag binding, prefer that to
9329 // global and language bindings;
9330 // if we have a global binding, prefer that to language binding.
9331 let first_mismatch = tags
9332 .iter()
9333 .position(|(tag_source, _)| tag_source != leading_tag_source);
9334 if let Some(index) = first_mismatch {
9335 tags.truncate(index);
9336 }
9337 }
9338
9339 tags
9340 }
9341
9342 pub fn move_to_enclosing_bracket(
9343 &mut self,
9344 _: &MoveToEnclosingBracket,
9345 cx: &mut ViewContext<Self>,
9346 ) {
9347 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9348 s.move_offsets_with(|snapshot, selection| {
9349 let Some(enclosing_bracket_ranges) =
9350 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
9351 else {
9352 return;
9353 };
9354
9355 let mut best_length = usize::MAX;
9356 let mut best_inside = false;
9357 let mut best_in_bracket_range = false;
9358 let mut best_destination = None;
9359 for (open, close) in enclosing_bracket_ranges {
9360 let close = close.to_inclusive();
9361 let length = close.end() - open.start;
9362 let inside = selection.start >= open.end && selection.end <= *close.start();
9363 let in_bracket_range = open.to_inclusive().contains(&selection.head())
9364 || close.contains(&selection.head());
9365
9366 // If best is next to a bracket and current isn't, skip
9367 if !in_bracket_range && best_in_bracket_range {
9368 continue;
9369 }
9370
9371 // Prefer smaller lengths unless best is inside and current isn't
9372 if length > best_length && (best_inside || !inside) {
9373 continue;
9374 }
9375
9376 best_length = length;
9377 best_inside = inside;
9378 best_in_bracket_range = in_bracket_range;
9379 best_destination = Some(
9380 if close.contains(&selection.start) && close.contains(&selection.end) {
9381 if inside {
9382 open.end
9383 } else {
9384 open.start
9385 }
9386 } else if inside {
9387 *close.start()
9388 } else {
9389 *close.end()
9390 },
9391 );
9392 }
9393
9394 if let Some(destination) = best_destination {
9395 selection.collapse_to(destination, SelectionGoal::None);
9396 }
9397 })
9398 });
9399 }
9400
9401 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
9402 self.end_selection(cx);
9403 self.selection_history.mode = SelectionHistoryMode::Undoing;
9404 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
9405 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
9406 self.select_next_state = entry.select_next_state;
9407 self.select_prev_state = entry.select_prev_state;
9408 self.add_selections_state = entry.add_selections_state;
9409 self.request_autoscroll(Autoscroll::newest(), cx);
9410 }
9411 self.selection_history.mode = SelectionHistoryMode::Normal;
9412 }
9413
9414 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
9415 self.end_selection(cx);
9416 self.selection_history.mode = SelectionHistoryMode::Redoing;
9417 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
9418 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
9419 self.select_next_state = entry.select_next_state;
9420 self.select_prev_state = entry.select_prev_state;
9421 self.add_selections_state = entry.add_selections_state;
9422 self.request_autoscroll(Autoscroll::newest(), cx);
9423 }
9424 self.selection_history.mode = SelectionHistoryMode::Normal;
9425 }
9426
9427 pub fn expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
9428 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
9429 }
9430
9431 pub fn expand_excerpts_down(
9432 &mut self,
9433 action: &ExpandExcerptsDown,
9434 cx: &mut ViewContext<Self>,
9435 ) {
9436 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
9437 }
9438
9439 pub fn expand_excerpts_up(&mut self, action: &ExpandExcerptsUp, cx: &mut ViewContext<Self>) {
9440 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
9441 }
9442
9443 pub fn expand_excerpts_for_direction(
9444 &mut self,
9445 lines: u32,
9446 direction: ExpandExcerptDirection,
9447 cx: &mut ViewContext<Self>,
9448 ) {
9449 let selections = self.selections.disjoint_anchors();
9450
9451 let lines = if lines == 0 {
9452 EditorSettings::get_global(cx).expand_excerpt_lines
9453 } else {
9454 lines
9455 };
9456
9457 self.buffer.update(cx, |buffer, cx| {
9458 buffer.expand_excerpts(
9459 selections
9460 .iter()
9461 .map(|selection| selection.head().excerpt_id)
9462 .dedup(),
9463 lines,
9464 direction,
9465 cx,
9466 )
9467 })
9468 }
9469
9470 pub fn expand_excerpt(
9471 &mut self,
9472 excerpt: ExcerptId,
9473 direction: ExpandExcerptDirection,
9474 cx: &mut ViewContext<Self>,
9475 ) {
9476 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
9477 self.buffer.update(cx, |buffer, cx| {
9478 buffer.expand_excerpts([excerpt], lines, direction, cx)
9479 })
9480 }
9481
9482 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
9483 self.go_to_diagnostic_impl(Direction::Next, cx)
9484 }
9485
9486 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
9487 self.go_to_diagnostic_impl(Direction::Prev, cx)
9488 }
9489
9490 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
9491 let buffer = self.buffer.read(cx).snapshot(cx);
9492 let selection = self.selections.newest::<usize>(cx);
9493
9494 // If there is an active Diagnostic Popover jump to its diagnostic instead.
9495 if direction == Direction::Next {
9496 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
9497 let (group_id, jump_to) = popover.activation_info();
9498 if self.activate_diagnostics(group_id, cx) {
9499 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9500 let mut new_selection = s.newest_anchor().clone();
9501 new_selection.collapse_to(jump_to, SelectionGoal::None);
9502 s.select_anchors(vec![new_selection.clone()]);
9503 });
9504 }
9505 return;
9506 }
9507 }
9508
9509 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
9510 active_diagnostics
9511 .primary_range
9512 .to_offset(&buffer)
9513 .to_inclusive()
9514 });
9515 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
9516 if active_primary_range.contains(&selection.head()) {
9517 *active_primary_range.start()
9518 } else {
9519 selection.head()
9520 }
9521 } else {
9522 selection.head()
9523 };
9524 let snapshot = self.snapshot(cx);
9525 loop {
9526 let diagnostics = if direction == Direction::Prev {
9527 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
9528 } else {
9529 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
9530 }
9531 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start));
9532 let group = diagnostics
9533 // relies on diagnostics_in_range to return diagnostics with the same starting range to
9534 // be sorted in a stable way
9535 // skip until we are at current active diagnostic, if it exists
9536 .skip_while(|entry| {
9537 (match direction {
9538 Direction::Prev => entry.range.start >= search_start,
9539 Direction::Next => entry.range.start <= search_start,
9540 }) && self
9541 .active_diagnostics
9542 .as_ref()
9543 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
9544 })
9545 .find_map(|entry| {
9546 if entry.diagnostic.is_primary
9547 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
9548 && !entry.range.is_empty()
9549 // if we match with the active diagnostic, skip it
9550 && Some(entry.diagnostic.group_id)
9551 != self.active_diagnostics.as_ref().map(|d| d.group_id)
9552 {
9553 Some((entry.range, entry.diagnostic.group_id))
9554 } else {
9555 None
9556 }
9557 });
9558
9559 if let Some((primary_range, group_id)) = group {
9560 if self.activate_diagnostics(group_id, cx) {
9561 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9562 s.select(vec![Selection {
9563 id: selection.id,
9564 start: primary_range.start,
9565 end: primary_range.start,
9566 reversed: false,
9567 goal: SelectionGoal::None,
9568 }]);
9569 });
9570 }
9571 break;
9572 } else {
9573 // Cycle around to the start of the buffer, potentially moving back to the start of
9574 // the currently active diagnostic.
9575 active_primary_range.take();
9576 if direction == Direction::Prev {
9577 if search_start == buffer.len() {
9578 break;
9579 } else {
9580 search_start = buffer.len();
9581 }
9582 } else if search_start == 0 {
9583 break;
9584 } else {
9585 search_start = 0;
9586 }
9587 }
9588 }
9589 }
9590
9591 fn go_to_next_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
9592 let snapshot = self
9593 .display_map
9594 .update(cx, |display_map, cx| display_map.snapshot(cx));
9595 let selection = self.selections.newest::<Point>(cx);
9596 self.go_to_hunk_after_position(&snapshot, selection.head(), cx);
9597 }
9598
9599 fn go_to_hunk_after_position(
9600 &mut self,
9601 snapshot: &DisplaySnapshot,
9602 position: Point,
9603 cx: &mut ViewContext<'_, Editor>,
9604 ) -> Option<MultiBufferDiffHunk> {
9605 if let Some(hunk) = self.go_to_next_hunk_in_direction(
9606 snapshot,
9607 position,
9608 false,
9609 snapshot
9610 .buffer_snapshot
9611 .git_diff_hunks_in_range(MultiBufferRow(position.row + 1)..MultiBufferRow::MAX),
9612 cx,
9613 ) {
9614 return Some(hunk);
9615 }
9616
9617 let wrapped_point = Point::zero();
9618 self.go_to_next_hunk_in_direction(
9619 snapshot,
9620 wrapped_point,
9621 true,
9622 snapshot.buffer_snapshot.git_diff_hunks_in_range(
9623 MultiBufferRow(wrapped_point.row + 1)..MultiBufferRow::MAX,
9624 ),
9625 cx,
9626 )
9627 }
9628
9629 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
9630 let snapshot = self
9631 .display_map
9632 .update(cx, |display_map, cx| display_map.snapshot(cx));
9633 let selection = self.selections.newest::<Point>(cx);
9634
9635 self.go_to_hunk_before_position(&snapshot, selection.head(), cx);
9636 }
9637
9638 fn go_to_hunk_before_position(
9639 &mut self,
9640 snapshot: &DisplaySnapshot,
9641 position: Point,
9642 cx: &mut ViewContext<'_, Editor>,
9643 ) -> Option<MultiBufferDiffHunk> {
9644 if let Some(hunk) = self.go_to_next_hunk_in_direction(
9645 snapshot,
9646 position,
9647 false,
9648 snapshot
9649 .buffer_snapshot
9650 .git_diff_hunks_in_range_rev(MultiBufferRow(0)..MultiBufferRow(position.row)),
9651 cx,
9652 ) {
9653 return Some(hunk);
9654 }
9655
9656 let wrapped_point = snapshot.buffer_snapshot.max_point();
9657 self.go_to_next_hunk_in_direction(
9658 snapshot,
9659 wrapped_point,
9660 true,
9661 snapshot
9662 .buffer_snapshot
9663 .git_diff_hunks_in_range_rev(MultiBufferRow(0)..MultiBufferRow(wrapped_point.row)),
9664 cx,
9665 )
9666 }
9667
9668 fn go_to_next_hunk_in_direction(
9669 &mut self,
9670 snapshot: &DisplaySnapshot,
9671 initial_point: Point,
9672 is_wrapped: bool,
9673 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
9674 cx: &mut ViewContext<Editor>,
9675 ) -> Option<MultiBufferDiffHunk> {
9676 let display_point = initial_point.to_display_point(snapshot);
9677 let mut hunks = hunks
9678 .map(|hunk| (diff_hunk_to_display(&hunk, snapshot), hunk))
9679 .filter(|(display_hunk, _)| {
9680 is_wrapped || !display_hunk.contains_display_row(display_point.row())
9681 })
9682 .dedup();
9683
9684 if let Some((display_hunk, hunk)) = hunks.next() {
9685 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9686 let row = display_hunk.start_display_row();
9687 let point = DisplayPoint::new(row, 0);
9688 s.select_display_ranges([point..point]);
9689 });
9690
9691 Some(hunk)
9692 } else {
9693 None
9694 }
9695 }
9696
9697 pub fn go_to_definition(
9698 &mut self,
9699 _: &GoToDefinition,
9700 cx: &mut ViewContext<Self>,
9701 ) -> Task<Result<Navigated>> {
9702 let definition = self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
9703 cx.spawn(|editor, mut cx| async move {
9704 if definition.await? == Navigated::Yes {
9705 return Ok(Navigated::Yes);
9706 }
9707 match editor.update(&mut cx, |editor, cx| {
9708 editor.find_all_references(&FindAllReferences, cx)
9709 })? {
9710 Some(references) => references.await,
9711 None => Ok(Navigated::No),
9712 }
9713 })
9714 }
9715
9716 pub fn go_to_declaration(
9717 &mut self,
9718 _: &GoToDeclaration,
9719 cx: &mut ViewContext<Self>,
9720 ) -> Task<Result<Navigated>> {
9721 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, cx)
9722 }
9723
9724 pub fn go_to_declaration_split(
9725 &mut self,
9726 _: &GoToDeclaration,
9727 cx: &mut ViewContext<Self>,
9728 ) -> Task<Result<Navigated>> {
9729 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, cx)
9730 }
9731
9732 pub fn go_to_implementation(
9733 &mut self,
9734 _: &GoToImplementation,
9735 cx: &mut ViewContext<Self>,
9736 ) -> Task<Result<Navigated>> {
9737 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx)
9738 }
9739
9740 pub fn go_to_implementation_split(
9741 &mut self,
9742 _: &GoToImplementationSplit,
9743 cx: &mut ViewContext<Self>,
9744 ) -> Task<Result<Navigated>> {
9745 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx)
9746 }
9747
9748 pub fn go_to_type_definition(
9749 &mut self,
9750 _: &GoToTypeDefinition,
9751 cx: &mut ViewContext<Self>,
9752 ) -> Task<Result<Navigated>> {
9753 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx)
9754 }
9755
9756 pub fn go_to_definition_split(
9757 &mut self,
9758 _: &GoToDefinitionSplit,
9759 cx: &mut ViewContext<Self>,
9760 ) -> Task<Result<Navigated>> {
9761 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx)
9762 }
9763
9764 pub fn go_to_type_definition_split(
9765 &mut self,
9766 _: &GoToTypeDefinitionSplit,
9767 cx: &mut ViewContext<Self>,
9768 ) -> Task<Result<Navigated>> {
9769 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx)
9770 }
9771
9772 fn go_to_definition_of_kind(
9773 &mut self,
9774 kind: GotoDefinitionKind,
9775 split: bool,
9776 cx: &mut ViewContext<Self>,
9777 ) -> Task<Result<Navigated>> {
9778 let Some(provider) = self.semantics_provider.clone() else {
9779 return Task::ready(Ok(Navigated::No));
9780 };
9781 let head = self.selections.newest::<usize>(cx).head();
9782 let buffer = self.buffer.read(cx);
9783 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
9784 text_anchor
9785 } else {
9786 return Task::ready(Ok(Navigated::No));
9787 };
9788
9789 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
9790 return Task::ready(Ok(Navigated::No));
9791 };
9792
9793 cx.spawn(|editor, mut cx| async move {
9794 let definitions = definitions.await?;
9795 let navigated = editor
9796 .update(&mut cx, |editor, cx| {
9797 editor.navigate_to_hover_links(
9798 Some(kind),
9799 definitions
9800 .into_iter()
9801 .filter(|location| {
9802 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
9803 })
9804 .map(HoverLink::Text)
9805 .collect::<Vec<_>>(),
9806 split,
9807 cx,
9808 )
9809 })?
9810 .await?;
9811 anyhow::Ok(navigated)
9812 })
9813 }
9814
9815 pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext<Self>) {
9816 let position = self.selections.newest_anchor().head();
9817 let Some((buffer, buffer_position)) =
9818 self.buffer.read(cx).text_anchor_for_position(position, cx)
9819 else {
9820 return;
9821 };
9822
9823 cx.spawn(|editor, mut cx| async move {
9824 if let Some((_, url)) = find_url(&buffer, buffer_position, cx.clone()) {
9825 editor.update(&mut cx, |_, cx| {
9826 cx.open_url(&url);
9827 })
9828 } else {
9829 Ok(())
9830 }
9831 })
9832 .detach();
9833 }
9834
9835 pub fn open_file(&mut self, _: &OpenFile, cx: &mut ViewContext<Self>) {
9836 let Some(workspace) = self.workspace() else {
9837 return;
9838 };
9839
9840 let position = self.selections.newest_anchor().head();
9841
9842 let Some((buffer, buffer_position)) =
9843 self.buffer.read(cx).text_anchor_for_position(position, cx)
9844 else {
9845 return;
9846 };
9847
9848 let project = self.project.clone();
9849
9850 cx.spawn(|_, mut cx| async move {
9851 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
9852
9853 if let Some((_, path)) = result {
9854 workspace
9855 .update(&mut cx, |workspace, cx| {
9856 workspace.open_resolved_path(path, cx)
9857 })?
9858 .await?;
9859 }
9860 anyhow::Ok(())
9861 })
9862 .detach();
9863 }
9864
9865 pub(crate) fn navigate_to_hover_links(
9866 &mut self,
9867 kind: Option<GotoDefinitionKind>,
9868 mut definitions: Vec<HoverLink>,
9869 split: bool,
9870 cx: &mut ViewContext<Editor>,
9871 ) -> Task<Result<Navigated>> {
9872 // If there is one definition, just open it directly
9873 if definitions.len() == 1 {
9874 let definition = definitions.pop().unwrap();
9875
9876 enum TargetTaskResult {
9877 Location(Option<Location>),
9878 AlreadyNavigated,
9879 }
9880
9881 let target_task = match definition {
9882 HoverLink::Text(link) => {
9883 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
9884 }
9885 HoverLink::InlayHint(lsp_location, server_id) => {
9886 let computation = self.compute_target_location(lsp_location, server_id, cx);
9887 cx.background_executor().spawn(async move {
9888 let location = computation.await?;
9889 Ok(TargetTaskResult::Location(location))
9890 })
9891 }
9892 HoverLink::Url(url) => {
9893 cx.open_url(&url);
9894 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
9895 }
9896 HoverLink::File(path) => {
9897 if let Some(workspace) = self.workspace() {
9898 cx.spawn(|_, mut cx| async move {
9899 workspace
9900 .update(&mut cx, |workspace, cx| {
9901 workspace.open_resolved_path(path, cx)
9902 })?
9903 .await
9904 .map(|_| TargetTaskResult::AlreadyNavigated)
9905 })
9906 } else {
9907 Task::ready(Ok(TargetTaskResult::Location(None)))
9908 }
9909 }
9910 };
9911 cx.spawn(|editor, mut cx| async move {
9912 let target = match target_task.await.context("target resolution task")? {
9913 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
9914 TargetTaskResult::Location(None) => return Ok(Navigated::No),
9915 TargetTaskResult::Location(Some(target)) => target,
9916 };
9917
9918 editor.update(&mut cx, |editor, cx| {
9919 let Some(workspace) = editor.workspace() else {
9920 return Navigated::No;
9921 };
9922 let pane = workspace.read(cx).active_pane().clone();
9923
9924 let range = target.range.to_offset(target.buffer.read(cx));
9925 let range = editor.range_for_match(&range);
9926
9927 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
9928 let buffer = target.buffer.read(cx);
9929 let range = check_multiline_range(buffer, range);
9930 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
9931 s.select_ranges([range]);
9932 });
9933 } else {
9934 cx.window_context().defer(move |cx| {
9935 let target_editor: View<Self> =
9936 workspace.update(cx, |workspace, cx| {
9937 let pane = if split {
9938 workspace.adjacent_pane(cx)
9939 } else {
9940 workspace.active_pane().clone()
9941 };
9942
9943 workspace.open_project_item(
9944 pane,
9945 target.buffer.clone(),
9946 true,
9947 true,
9948 cx,
9949 )
9950 });
9951 target_editor.update(cx, |target_editor, cx| {
9952 // When selecting a definition in a different buffer, disable the nav history
9953 // to avoid creating a history entry at the previous cursor location.
9954 pane.update(cx, |pane, _| pane.disable_history());
9955 let buffer = target.buffer.read(cx);
9956 let range = check_multiline_range(buffer, range);
9957 target_editor.change_selections(
9958 Some(Autoscroll::focused()),
9959 cx,
9960 |s| {
9961 s.select_ranges([range]);
9962 },
9963 );
9964 pane.update(cx, |pane, _| pane.enable_history());
9965 });
9966 });
9967 }
9968 Navigated::Yes
9969 })
9970 })
9971 } else if !definitions.is_empty() {
9972 cx.spawn(|editor, mut cx| async move {
9973 let (title, location_tasks, workspace) = editor
9974 .update(&mut cx, |editor, cx| {
9975 let tab_kind = match kind {
9976 Some(GotoDefinitionKind::Implementation) => "Implementations",
9977 _ => "Definitions",
9978 };
9979 let title = definitions
9980 .iter()
9981 .find_map(|definition| match definition {
9982 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
9983 let buffer = origin.buffer.read(cx);
9984 format!(
9985 "{} for {}",
9986 tab_kind,
9987 buffer
9988 .text_for_range(origin.range.clone())
9989 .collect::<String>()
9990 )
9991 }),
9992 HoverLink::InlayHint(_, _) => None,
9993 HoverLink::Url(_) => None,
9994 HoverLink::File(_) => None,
9995 })
9996 .unwrap_or(tab_kind.to_string());
9997 let location_tasks = definitions
9998 .into_iter()
9999 .map(|definition| match definition {
10000 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
10001 HoverLink::InlayHint(lsp_location, server_id) => {
10002 editor.compute_target_location(lsp_location, server_id, cx)
10003 }
10004 HoverLink::Url(_) => Task::ready(Ok(None)),
10005 HoverLink::File(_) => Task::ready(Ok(None)),
10006 })
10007 .collect::<Vec<_>>();
10008 (title, location_tasks, editor.workspace().clone())
10009 })
10010 .context("location tasks preparation")?;
10011
10012 let locations = future::join_all(location_tasks)
10013 .await
10014 .into_iter()
10015 .filter_map(|location| location.transpose())
10016 .collect::<Result<_>>()
10017 .context("location tasks")?;
10018
10019 let Some(workspace) = workspace else {
10020 return Ok(Navigated::No);
10021 };
10022 let opened = workspace
10023 .update(&mut cx, |workspace, cx| {
10024 Self::open_locations_in_multibuffer(workspace, locations, title, split, cx)
10025 })
10026 .ok();
10027
10028 anyhow::Ok(Navigated::from_bool(opened.is_some()))
10029 })
10030 } else {
10031 Task::ready(Ok(Navigated::No))
10032 }
10033 }
10034
10035 fn compute_target_location(
10036 &self,
10037 lsp_location: lsp::Location,
10038 server_id: LanguageServerId,
10039 cx: &mut ViewContext<Self>,
10040 ) -> Task<anyhow::Result<Option<Location>>> {
10041 let Some(project) = self.project.clone() else {
10042 return Task::Ready(Some(Ok(None)));
10043 };
10044
10045 cx.spawn(move |editor, mut cx| async move {
10046 let location_task = editor.update(&mut cx, |_, cx| {
10047 project.update(cx, |project, cx| {
10048 let language_server_name = project
10049 .language_server_statuses(cx)
10050 .find(|(id, _)| server_id == *id)
10051 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
10052 language_server_name.map(|language_server_name| {
10053 project.open_local_buffer_via_lsp(
10054 lsp_location.uri.clone(),
10055 server_id,
10056 language_server_name,
10057 cx,
10058 )
10059 })
10060 })
10061 })?;
10062 let location = match location_task {
10063 Some(task) => Some({
10064 let target_buffer_handle = task.await.context("open local buffer")?;
10065 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
10066 let target_start = target_buffer
10067 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
10068 let target_end = target_buffer
10069 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
10070 target_buffer.anchor_after(target_start)
10071 ..target_buffer.anchor_before(target_end)
10072 })?;
10073 Location {
10074 buffer: target_buffer_handle,
10075 range,
10076 }
10077 }),
10078 None => None,
10079 };
10080 Ok(location)
10081 })
10082 }
10083
10084 pub fn find_all_references(
10085 &mut self,
10086 _: &FindAllReferences,
10087 cx: &mut ViewContext<Self>,
10088 ) -> Option<Task<Result<Navigated>>> {
10089 let selection = self.selections.newest::<usize>(cx);
10090 let multi_buffer = self.buffer.read(cx);
10091 let head = selection.head();
10092
10093 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
10094 let head_anchor = multi_buffer_snapshot.anchor_at(
10095 head,
10096 if head < selection.tail() {
10097 Bias::Right
10098 } else {
10099 Bias::Left
10100 },
10101 );
10102
10103 match self
10104 .find_all_references_task_sources
10105 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
10106 {
10107 Ok(_) => {
10108 log::info!(
10109 "Ignoring repeated FindAllReferences invocation with the position of already running task"
10110 );
10111 return None;
10112 }
10113 Err(i) => {
10114 self.find_all_references_task_sources.insert(i, head_anchor);
10115 }
10116 }
10117
10118 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
10119 let workspace = self.workspace()?;
10120 let project = workspace.read(cx).project().clone();
10121 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
10122 Some(cx.spawn(|editor, mut cx| async move {
10123 let _cleanup = defer({
10124 let mut cx = cx.clone();
10125 move || {
10126 let _ = editor.update(&mut cx, |editor, _| {
10127 if let Ok(i) =
10128 editor
10129 .find_all_references_task_sources
10130 .binary_search_by(|anchor| {
10131 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
10132 })
10133 {
10134 editor.find_all_references_task_sources.remove(i);
10135 }
10136 });
10137 }
10138 });
10139
10140 let locations = references.await?;
10141 if locations.is_empty() {
10142 return anyhow::Ok(Navigated::No);
10143 }
10144
10145 workspace.update(&mut cx, |workspace, cx| {
10146 let title = locations
10147 .first()
10148 .as_ref()
10149 .map(|location| {
10150 let buffer = location.buffer.read(cx);
10151 format!(
10152 "References to `{}`",
10153 buffer
10154 .text_for_range(location.range.clone())
10155 .collect::<String>()
10156 )
10157 })
10158 .unwrap();
10159 Self::open_locations_in_multibuffer(workspace, locations, title, false, cx);
10160 Navigated::Yes
10161 })
10162 }))
10163 }
10164
10165 /// Opens a multibuffer with the given project locations in it
10166 pub fn open_locations_in_multibuffer(
10167 workspace: &mut Workspace,
10168 mut locations: Vec<Location>,
10169 title: String,
10170 split: bool,
10171 cx: &mut ViewContext<Workspace>,
10172 ) {
10173 // If there are multiple definitions, open them in a multibuffer
10174 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
10175 let mut locations = locations.into_iter().peekable();
10176 let mut ranges_to_highlight = Vec::new();
10177 let capability = workspace.project().read(cx).capability();
10178
10179 let excerpt_buffer = cx.new_model(|cx| {
10180 let mut multibuffer = MultiBuffer::new(capability);
10181 while let Some(location) = locations.next() {
10182 let buffer = location.buffer.read(cx);
10183 let mut ranges_for_buffer = Vec::new();
10184 let range = location.range.to_offset(buffer);
10185 ranges_for_buffer.push(range.clone());
10186
10187 while let Some(next_location) = locations.peek() {
10188 if next_location.buffer == location.buffer {
10189 ranges_for_buffer.push(next_location.range.to_offset(buffer));
10190 locations.next();
10191 } else {
10192 break;
10193 }
10194 }
10195
10196 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
10197 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
10198 location.buffer.clone(),
10199 ranges_for_buffer,
10200 DEFAULT_MULTIBUFFER_CONTEXT,
10201 cx,
10202 ))
10203 }
10204
10205 multibuffer.with_title(title)
10206 });
10207
10208 let editor = cx.new_view(|cx| {
10209 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), true, cx)
10210 });
10211 editor.update(cx, |editor, cx| {
10212 if let Some(first_range) = ranges_to_highlight.first() {
10213 editor.change_selections(None, cx, |selections| {
10214 selections.clear_disjoint();
10215 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
10216 });
10217 }
10218 editor.highlight_background::<Self>(
10219 &ranges_to_highlight,
10220 |theme| theme.editor_highlighted_line_background,
10221 cx,
10222 );
10223 });
10224
10225 let item = Box::new(editor);
10226 let item_id = item.item_id();
10227
10228 if split {
10229 workspace.split_item(SplitDirection::Right, item.clone(), cx);
10230 } else {
10231 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
10232 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
10233 pane.close_current_preview_item(cx)
10234 } else {
10235 None
10236 }
10237 });
10238 workspace.add_item_to_active_pane(item.clone(), destination_index, true, cx);
10239 }
10240 workspace.active_pane().update(cx, |pane, cx| {
10241 pane.set_preview_item_id(Some(item_id), cx);
10242 });
10243 }
10244
10245 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
10246 use language::ToOffset as _;
10247
10248 let provider = self.semantics_provider.clone()?;
10249 let selection = self.selections.newest_anchor().clone();
10250 let (cursor_buffer, cursor_buffer_position) = self
10251 .buffer
10252 .read(cx)
10253 .text_anchor_for_position(selection.head(), cx)?;
10254 let (tail_buffer, cursor_buffer_position_end) = self
10255 .buffer
10256 .read(cx)
10257 .text_anchor_for_position(selection.tail(), cx)?;
10258 if tail_buffer != cursor_buffer {
10259 return None;
10260 }
10261
10262 let snapshot = cursor_buffer.read(cx).snapshot();
10263 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
10264 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
10265 let prepare_rename = provider
10266 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
10267 .unwrap_or_else(|| Task::ready(Ok(None)));
10268 drop(snapshot);
10269
10270 Some(cx.spawn(|this, mut cx| async move {
10271 let rename_range = if let Some(range) = prepare_rename.await? {
10272 Some(range)
10273 } else {
10274 this.update(&mut cx, |this, cx| {
10275 let buffer = this.buffer.read(cx).snapshot(cx);
10276 let mut buffer_highlights = this
10277 .document_highlights_for_position(selection.head(), &buffer)
10278 .filter(|highlight| {
10279 highlight.start.excerpt_id == selection.head().excerpt_id
10280 && highlight.end.excerpt_id == selection.head().excerpt_id
10281 });
10282 buffer_highlights
10283 .next()
10284 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
10285 })?
10286 };
10287 if let Some(rename_range) = rename_range {
10288 this.update(&mut cx, |this, cx| {
10289 let snapshot = cursor_buffer.read(cx).snapshot();
10290 let rename_buffer_range = rename_range.to_offset(&snapshot);
10291 let cursor_offset_in_rename_range =
10292 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
10293 let cursor_offset_in_rename_range_end =
10294 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
10295
10296 this.take_rename(false, cx);
10297 let buffer = this.buffer.read(cx).read(cx);
10298 let cursor_offset = selection.head().to_offset(&buffer);
10299 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
10300 let rename_end = rename_start + rename_buffer_range.len();
10301 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
10302 let mut old_highlight_id = None;
10303 let old_name: Arc<str> = buffer
10304 .chunks(rename_start..rename_end, true)
10305 .map(|chunk| {
10306 if old_highlight_id.is_none() {
10307 old_highlight_id = chunk.syntax_highlight_id;
10308 }
10309 chunk.text
10310 })
10311 .collect::<String>()
10312 .into();
10313
10314 drop(buffer);
10315
10316 // Position the selection in the rename editor so that it matches the current selection.
10317 this.show_local_selections = false;
10318 let rename_editor = cx.new_view(|cx| {
10319 let mut editor = Editor::single_line(cx);
10320 editor.buffer.update(cx, |buffer, cx| {
10321 buffer.edit([(0..0, old_name.clone())], None, cx)
10322 });
10323 let rename_selection_range = match cursor_offset_in_rename_range
10324 .cmp(&cursor_offset_in_rename_range_end)
10325 {
10326 Ordering::Equal => {
10327 editor.select_all(&SelectAll, cx);
10328 return editor;
10329 }
10330 Ordering::Less => {
10331 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
10332 }
10333 Ordering::Greater => {
10334 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
10335 }
10336 };
10337 if rename_selection_range.end > old_name.len() {
10338 editor.select_all(&SelectAll, cx);
10339 } else {
10340 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
10341 s.select_ranges([rename_selection_range]);
10342 });
10343 }
10344 editor
10345 });
10346 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
10347 if e == &EditorEvent::Focused {
10348 cx.emit(EditorEvent::FocusedIn)
10349 }
10350 })
10351 .detach();
10352
10353 let write_highlights =
10354 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
10355 let read_highlights =
10356 this.clear_background_highlights::<DocumentHighlightRead>(cx);
10357 let ranges = write_highlights
10358 .iter()
10359 .flat_map(|(_, ranges)| ranges.iter())
10360 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
10361 .cloned()
10362 .collect();
10363
10364 this.highlight_text::<Rename>(
10365 ranges,
10366 HighlightStyle {
10367 fade_out: Some(0.6),
10368 ..Default::default()
10369 },
10370 cx,
10371 );
10372 let rename_focus_handle = rename_editor.focus_handle(cx);
10373 cx.focus(&rename_focus_handle);
10374 let block_id = this.insert_blocks(
10375 [BlockProperties {
10376 style: BlockStyle::Flex,
10377 placement: BlockPlacement::Below(range.start),
10378 height: 1,
10379 render: Box::new({
10380 let rename_editor = rename_editor.clone();
10381 move |cx: &mut BlockContext| {
10382 let mut text_style = cx.editor_style.text.clone();
10383 if let Some(highlight_style) = old_highlight_id
10384 .and_then(|h| h.style(&cx.editor_style.syntax))
10385 {
10386 text_style = text_style.highlight(highlight_style);
10387 }
10388 div()
10389 .pl(cx.anchor_x)
10390 .child(EditorElement::new(
10391 &rename_editor,
10392 EditorStyle {
10393 background: cx.theme().system().transparent,
10394 local_player: cx.editor_style.local_player,
10395 text: text_style,
10396 scrollbar_width: cx.editor_style.scrollbar_width,
10397 syntax: cx.editor_style.syntax.clone(),
10398 status: cx.editor_style.status.clone(),
10399 inlay_hints_style: HighlightStyle {
10400 font_weight: Some(FontWeight::BOLD),
10401 ..make_inlay_hints_style(cx)
10402 },
10403 suggestions_style: HighlightStyle {
10404 color: Some(cx.theme().status().predictive),
10405 ..HighlightStyle::default()
10406 },
10407 ..EditorStyle::default()
10408 },
10409 ))
10410 .into_any_element()
10411 }
10412 }),
10413 priority: 0,
10414 }],
10415 Some(Autoscroll::fit()),
10416 cx,
10417 )[0];
10418 this.pending_rename = Some(RenameState {
10419 range,
10420 old_name,
10421 editor: rename_editor,
10422 block_id,
10423 });
10424 })?;
10425 }
10426
10427 Ok(())
10428 }))
10429 }
10430
10431 pub fn confirm_rename(
10432 &mut self,
10433 _: &ConfirmRename,
10434 cx: &mut ViewContext<Self>,
10435 ) -> Option<Task<Result<()>>> {
10436 let rename = self.take_rename(false, cx)?;
10437 let workspace = self.workspace()?.downgrade();
10438 let (buffer, start) = self
10439 .buffer
10440 .read(cx)
10441 .text_anchor_for_position(rename.range.start, cx)?;
10442 let (end_buffer, _) = self
10443 .buffer
10444 .read(cx)
10445 .text_anchor_for_position(rename.range.end, cx)?;
10446 if buffer != end_buffer {
10447 return None;
10448 }
10449
10450 let old_name = rename.old_name;
10451 let new_name = rename.editor.read(cx).text(cx);
10452
10453 let rename = self.semantics_provider.as_ref()?.perform_rename(
10454 &buffer,
10455 start,
10456 new_name.clone(),
10457 cx,
10458 )?;
10459
10460 Some(cx.spawn(|editor, mut cx| async move {
10461 let project_transaction = rename.await?;
10462 Self::open_project_transaction(
10463 &editor,
10464 workspace,
10465 project_transaction,
10466 format!("Rename: {} → {}", old_name, new_name),
10467 cx.clone(),
10468 )
10469 .await?;
10470
10471 editor.update(&mut cx, |editor, cx| {
10472 editor.refresh_document_highlights(cx);
10473 })?;
10474 Ok(())
10475 }))
10476 }
10477
10478 fn take_rename(
10479 &mut self,
10480 moving_cursor: bool,
10481 cx: &mut ViewContext<Self>,
10482 ) -> Option<RenameState> {
10483 let rename = self.pending_rename.take()?;
10484 if rename.editor.focus_handle(cx).is_focused(cx) {
10485 cx.focus(&self.focus_handle);
10486 }
10487
10488 self.remove_blocks(
10489 [rename.block_id].into_iter().collect(),
10490 Some(Autoscroll::fit()),
10491 cx,
10492 );
10493 self.clear_highlights::<Rename>(cx);
10494 self.show_local_selections = true;
10495
10496 if moving_cursor {
10497 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
10498 editor.selections.newest::<usize>(cx).head()
10499 });
10500
10501 // Update the selection to match the position of the selection inside
10502 // the rename editor.
10503 let snapshot = self.buffer.read(cx).read(cx);
10504 let rename_range = rename.range.to_offset(&snapshot);
10505 let cursor_in_editor = snapshot
10506 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
10507 .min(rename_range.end);
10508 drop(snapshot);
10509
10510 self.change_selections(None, cx, |s| {
10511 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
10512 });
10513 } else {
10514 self.refresh_document_highlights(cx);
10515 }
10516
10517 Some(rename)
10518 }
10519
10520 pub fn pending_rename(&self) -> Option<&RenameState> {
10521 self.pending_rename.as_ref()
10522 }
10523
10524 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
10525 let project = match &self.project {
10526 Some(project) => project.clone(),
10527 None => return None,
10528 };
10529
10530 Some(self.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffer, cx))
10531 }
10532
10533 fn format_selections(
10534 &mut self,
10535 _: &FormatSelections,
10536 cx: &mut ViewContext<Self>,
10537 ) -> Option<Task<Result<()>>> {
10538 let project = match &self.project {
10539 Some(project) => project.clone(),
10540 None => return None,
10541 };
10542
10543 let selections = self
10544 .selections
10545 .all_adjusted(cx)
10546 .into_iter()
10547 .filter(|s| !s.is_empty())
10548 .collect_vec();
10549
10550 Some(self.perform_format(
10551 project,
10552 FormatTrigger::Manual,
10553 FormatTarget::Ranges(selections),
10554 cx,
10555 ))
10556 }
10557
10558 fn perform_format(
10559 &mut self,
10560 project: Model<Project>,
10561 trigger: FormatTrigger,
10562 target: FormatTarget,
10563 cx: &mut ViewContext<Self>,
10564 ) -> Task<Result<()>> {
10565 let buffer = self.buffer().clone();
10566 let mut buffers = buffer.read(cx).all_buffers();
10567 if trigger == FormatTrigger::Save {
10568 buffers.retain(|buffer| buffer.read(cx).is_dirty());
10569 }
10570
10571 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
10572 let format = project.update(cx, |project, cx| {
10573 project.format(buffers, true, trigger, target, cx)
10574 });
10575
10576 cx.spawn(|_, mut cx| async move {
10577 let transaction = futures::select_biased! {
10578 () = timeout => {
10579 log::warn!("timed out waiting for formatting");
10580 None
10581 }
10582 transaction = format.log_err().fuse() => transaction,
10583 };
10584
10585 buffer
10586 .update(&mut cx, |buffer, cx| {
10587 if let Some(transaction) = transaction {
10588 if !buffer.is_singleton() {
10589 buffer.push_transaction(&transaction.0, cx);
10590 }
10591 }
10592
10593 cx.notify();
10594 })
10595 .ok();
10596
10597 Ok(())
10598 })
10599 }
10600
10601 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
10602 if let Some(project) = self.project.clone() {
10603 self.buffer.update(cx, |multi_buffer, cx| {
10604 project.update(cx, |project, cx| {
10605 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
10606 });
10607 })
10608 }
10609 }
10610
10611 fn cancel_language_server_work(
10612 &mut self,
10613 _: &actions::CancelLanguageServerWork,
10614 cx: &mut ViewContext<Self>,
10615 ) {
10616 if let Some(project) = self.project.clone() {
10617 self.buffer.update(cx, |multi_buffer, cx| {
10618 project.update(cx, |project, cx| {
10619 project.cancel_language_server_work_for_buffers(multi_buffer.all_buffers(), cx);
10620 });
10621 })
10622 }
10623 }
10624
10625 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
10626 cx.show_character_palette();
10627 }
10628
10629 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
10630 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
10631 let buffer = self.buffer.read(cx).snapshot(cx);
10632 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
10633 let is_valid = buffer
10634 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
10635 .any(|entry| {
10636 entry.diagnostic.is_primary
10637 && !entry.range.is_empty()
10638 && entry.range.start == primary_range_start
10639 && entry.diagnostic.message == active_diagnostics.primary_message
10640 });
10641
10642 if is_valid != active_diagnostics.is_valid {
10643 active_diagnostics.is_valid = is_valid;
10644 let mut new_styles = HashMap::default();
10645 for (block_id, diagnostic) in &active_diagnostics.blocks {
10646 new_styles.insert(
10647 *block_id,
10648 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
10649 );
10650 }
10651 self.display_map.update(cx, |display_map, _cx| {
10652 display_map.replace_blocks(new_styles)
10653 });
10654 }
10655 }
10656 }
10657
10658 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
10659 self.dismiss_diagnostics(cx);
10660 let snapshot = self.snapshot(cx);
10661 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
10662 let buffer = self.buffer.read(cx).snapshot(cx);
10663
10664 let mut primary_range = None;
10665 let mut primary_message = None;
10666 let mut group_end = Point::zero();
10667 let diagnostic_group = buffer
10668 .diagnostic_group::<MultiBufferPoint>(group_id)
10669 .filter_map(|entry| {
10670 if snapshot.is_line_folded(MultiBufferRow(entry.range.start.row))
10671 && (entry.range.start.row == entry.range.end.row
10672 || snapshot.is_line_folded(MultiBufferRow(entry.range.end.row)))
10673 {
10674 return None;
10675 }
10676 if entry.range.end > group_end {
10677 group_end = entry.range.end;
10678 }
10679 if entry.diagnostic.is_primary {
10680 primary_range = Some(entry.range.clone());
10681 primary_message = Some(entry.diagnostic.message.clone());
10682 }
10683 Some(entry)
10684 })
10685 .collect::<Vec<_>>();
10686 let primary_range = primary_range?;
10687 let primary_message = primary_message?;
10688 let primary_range =
10689 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
10690
10691 let blocks = display_map
10692 .insert_blocks(
10693 diagnostic_group.iter().map(|entry| {
10694 let diagnostic = entry.diagnostic.clone();
10695 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
10696 BlockProperties {
10697 style: BlockStyle::Fixed,
10698 placement: BlockPlacement::Below(
10699 buffer.anchor_after(entry.range.start),
10700 ),
10701 height: message_height,
10702 render: diagnostic_block_renderer(diagnostic, None, true, true),
10703 priority: 0,
10704 }
10705 }),
10706 cx,
10707 )
10708 .into_iter()
10709 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
10710 .collect();
10711
10712 Some(ActiveDiagnosticGroup {
10713 primary_range,
10714 primary_message,
10715 group_id,
10716 blocks,
10717 is_valid: true,
10718 })
10719 });
10720 self.active_diagnostics.is_some()
10721 }
10722
10723 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
10724 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
10725 self.display_map.update(cx, |display_map, cx| {
10726 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
10727 });
10728 cx.notify();
10729 }
10730 }
10731
10732 pub fn set_selections_from_remote(
10733 &mut self,
10734 selections: Vec<Selection<Anchor>>,
10735 pending_selection: Option<Selection<Anchor>>,
10736 cx: &mut ViewContext<Self>,
10737 ) {
10738 let old_cursor_position = self.selections.newest_anchor().head();
10739 self.selections.change_with(cx, |s| {
10740 s.select_anchors(selections);
10741 if let Some(pending_selection) = pending_selection {
10742 s.set_pending(pending_selection, SelectMode::Character);
10743 } else {
10744 s.clear_pending();
10745 }
10746 });
10747 self.selections_did_change(false, &old_cursor_position, true, cx);
10748 }
10749
10750 fn push_to_selection_history(&mut self) {
10751 self.selection_history.push(SelectionHistoryEntry {
10752 selections: self.selections.disjoint_anchors(),
10753 select_next_state: self.select_next_state.clone(),
10754 select_prev_state: self.select_prev_state.clone(),
10755 add_selections_state: self.add_selections_state.clone(),
10756 });
10757 }
10758
10759 pub fn transact(
10760 &mut self,
10761 cx: &mut ViewContext<Self>,
10762 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
10763 ) -> Option<TransactionId> {
10764 self.start_transaction_at(Instant::now(), cx);
10765 update(self, cx);
10766 self.end_transaction_at(Instant::now(), cx)
10767 }
10768
10769 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
10770 self.end_selection(cx);
10771 if let Some(tx_id) = self
10772 .buffer
10773 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
10774 {
10775 self.selection_history
10776 .insert_transaction(tx_id, self.selections.disjoint_anchors());
10777 cx.emit(EditorEvent::TransactionBegun {
10778 transaction_id: tx_id,
10779 })
10780 }
10781 }
10782
10783 fn end_transaction_at(
10784 &mut self,
10785 now: Instant,
10786 cx: &mut ViewContext<Self>,
10787 ) -> Option<TransactionId> {
10788 if let Some(transaction_id) = self
10789 .buffer
10790 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
10791 {
10792 if let Some((_, end_selections)) =
10793 self.selection_history.transaction_mut(transaction_id)
10794 {
10795 *end_selections = Some(self.selections.disjoint_anchors());
10796 } else {
10797 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
10798 }
10799
10800 cx.emit(EditorEvent::Edited { transaction_id });
10801 Some(transaction_id)
10802 } else {
10803 None
10804 }
10805 }
10806
10807 pub fn toggle_fold(&mut self, _: &actions::ToggleFold, cx: &mut ViewContext<Self>) {
10808 let selection = self.selections.newest::<Point>(cx);
10809
10810 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10811 let range = if selection.is_empty() {
10812 let point = selection.head().to_display_point(&display_map);
10813 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
10814 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
10815 .to_point(&display_map);
10816 start..end
10817 } else {
10818 selection.range()
10819 };
10820 if display_map.folds_in_range(range).next().is_some() {
10821 self.unfold_lines(&Default::default(), cx)
10822 } else {
10823 self.fold(&Default::default(), cx)
10824 }
10825 }
10826
10827 pub fn toggle_fold_recursive(
10828 &mut self,
10829 _: &actions::ToggleFoldRecursive,
10830 cx: &mut ViewContext<Self>,
10831 ) {
10832 let selection = self.selections.newest::<Point>(cx);
10833
10834 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10835 let range = if selection.is_empty() {
10836 let point = selection.head().to_display_point(&display_map);
10837 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
10838 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
10839 .to_point(&display_map);
10840 start..end
10841 } else {
10842 selection.range()
10843 };
10844 if display_map.folds_in_range(range).next().is_some() {
10845 self.unfold_recursive(&Default::default(), cx)
10846 } else {
10847 self.fold_recursive(&Default::default(), cx)
10848 }
10849 }
10850
10851 pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
10852 let mut fold_ranges = Vec::new();
10853 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10854 let selections = self.selections.all_adjusted(cx);
10855
10856 for selection in selections {
10857 let range = selection.range().sorted();
10858 let buffer_start_row = range.start.row;
10859
10860 if range.start.row != range.end.row {
10861 let mut found = false;
10862 let mut row = range.start.row;
10863 while row <= range.end.row {
10864 if let Some((foldable_range, fold_text)) =
10865 { display_map.foldable_range(MultiBufferRow(row)) }
10866 {
10867 found = true;
10868 row = foldable_range.end.row + 1;
10869 fold_ranges.push((foldable_range, fold_text));
10870 } else {
10871 row += 1
10872 }
10873 }
10874 if found {
10875 continue;
10876 }
10877 }
10878
10879 for row in (0..=range.start.row).rev() {
10880 if let Some((foldable_range, fold_text)) =
10881 display_map.foldable_range(MultiBufferRow(row))
10882 {
10883 if foldable_range.end.row >= buffer_start_row {
10884 fold_ranges.push((foldable_range, fold_text));
10885 if row <= range.start.row {
10886 break;
10887 }
10888 }
10889 }
10890 }
10891 }
10892
10893 self.fold_ranges(fold_ranges, true, cx);
10894 }
10895
10896 fn fold_at_level(&mut self, fold_at: &FoldAtLevel, cx: &mut ViewContext<Self>) {
10897 let fold_at_level = fold_at.level;
10898 let snapshot = self.buffer.read(cx).snapshot(cx);
10899 let mut fold_ranges = Vec::new();
10900 let mut stack = vec![(0, snapshot.max_buffer_row().0, 1)];
10901
10902 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
10903 while start_row < end_row {
10904 match self.snapshot(cx).foldable_range(MultiBufferRow(start_row)) {
10905 Some(foldable_range) => {
10906 let nested_start_row = foldable_range.0.start.row + 1;
10907 let nested_end_row = foldable_range.0.end.row;
10908
10909 if current_level < fold_at_level {
10910 stack.push((nested_start_row, nested_end_row, current_level + 1));
10911 } else if current_level == fold_at_level {
10912 fold_ranges.push(foldable_range);
10913 }
10914
10915 start_row = nested_end_row + 1;
10916 }
10917 None => start_row += 1,
10918 }
10919 }
10920 }
10921
10922 self.fold_ranges(fold_ranges, true, cx);
10923 }
10924
10925 pub fn fold_all(&mut self, _: &actions::FoldAll, cx: &mut ViewContext<Self>) {
10926 let mut fold_ranges = Vec::new();
10927 let snapshot = self.buffer.read(cx).snapshot(cx);
10928
10929 for row in 0..snapshot.max_buffer_row().0 {
10930 if let Some(foldable_range) = self.snapshot(cx).foldable_range(MultiBufferRow(row)) {
10931 fold_ranges.push(foldable_range);
10932 }
10933 }
10934
10935 self.fold_ranges(fold_ranges, true, cx);
10936 }
10937
10938 pub fn fold_recursive(&mut self, _: &actions::FoldRecursive, cx: &mut ViewContext<Self>) {
10939 let mut fold_ranges = Vec::new();
10940 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10941 let selections = self.selections.all_adjusted(cx);
10942
10943 for selection in selections {
10944 let range = selection.range().sorted();
10945 let buffer_start_row = range.start.row;
10946
10947 if range.start.row != range.end.row {
10948 let mut found = false;
10949 for row in range.start.row..=range.end.row {
10950 if let Some((foldable_range, fold_text)) =
10951 { display_map.foldable_range(MultiBufferRow(row)) }
10952 {
10953 found = true;
10954 fold_ranges.push((foldable_range, fold_text));
10955 }
10956 }
10957 if found {
10958 continue;
10959 }
10960 }
10961
10962 for row in (0..=range.start.row).rev() {
10963 if let Some((foldable_range, fold_text)) =
10964 display_map.foldable_range(MultiBufferRow(row))
10965 {
10966 if foldable_range.end.row >= buffer_start_row {
10967 fold_ranges.push((foldable_range, fold_text));
10968 } else {
10969 break;
10970 }
10971 }
10972 }
10973 }
10974
10975 self.fold_ranges(fold_ranges, true, cx);
10976 }
10977
10978 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
10979 let buffer_row = fold_at.buffer_row;
10980 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10981
10982 if let Some((fold_range, placeholder)) = display_map.foldable_range(buffer_row) {
10983 let autoscroll = self
10984 .selections
10985 .all::<Point>(cx)
10986 .iter()
10987 .any(|selection| fold_range.overlaps(&selection.range()));
10988
10989 self.fold_ranges([(fold_range, placeholder)], autoscroll, cx);
10990 }
10991 }
10992
10993 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
10994 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10995 let buffer = &display_map.buffer_snapshot;
10996 let selections = self.selections.all::<Point>(cx);
10997 let ranges = selections
10998 .iter()
10999 .map(|s| {
11000 let range = s.display_range(&display_map).sorted();
11001 let mut start = range.start.to_point(&display_map);
11002 let mut end = range.end.to_point(&display_map);
11003 start.column = 0;
11004 end.column = buffer.line_len(MultiBufferRow(end.row));
11005 start..end
11006 })
11007 .collect::<Vec<_>>();
11008
11009 self.unfold_ranges(&ranges, true, true, cx);
11010 }
11011
11012 pub fn unfold_recursive(&mut self, _: &UnfoldRecursive, cx: &mut ViewContext<Self>) {
11013 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11014 let selections = self.selections.all::<Point>(cx);
11015 let ranges = selections
11016 .iter()
11017 .map(|s| {
11018 let mut range = s.display_range(&display_map).sorted();
11019 *range.start.column_mut() = 0;
11020 *range.end.column_mut() = display_map.line_len(range.end.row());
11021 let start = range.start.to_point(&display_map);
11022 let end = range.end.to_point(&display_map);
11023 start..end
11024 })
11025 .collect::<Vec<_>>();
11026
11027 self.unfold_ranges(&ranges, true, true, cx);
11028 }
11029
11030 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
11031 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11032
11033 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
11034 ..Point::new(
11035 unfold_at.buffer_row.0,
11036 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
11037 );
11038
11039 let autoscroll = self
11040 .selections
11041 .all::<Point>(cx)
11042 .iter()
11043 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
11044
11045 self.unfold_ranges(&[intersection_range], true, autoscroll, cx)
11046 }
11047
11048 pub fn unfold_all(&mut self, _: &actions::UnfoldAll, cx: &mut ViewContext<Self>) {
11049 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11050 self.unfold_ranges(
11051 &[Point::zero()..display_map.max_point().to_point(&display_map)],
11052 true,
11053 true,
11054 cx,
11055 );
11056 }
11057
11058 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
11059 let selections = self.selections.all::<Point>(cx);
11060 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11061 let line_mode = self.selections.line_mode;
11062 let ranges = selections.into_iter().map(|s| {
11063 if line_mode {
11064 let start = Point::new(s.start.row, 0);
11065 let end = Point::new(
11066 s.end.row,
11067 display_map
11068 .buffer_snapshot
11069 .line_len(MultiBufferRow(s.end.row)),
11070 );
11071 (start..end, display_map.fold_placeholder.clone())
11072 } else {
11073 (s.start..s.end, display_map.fold_placeholder.clone())
11074 }
11075 });
11076 self.fold_ranges(ranges, true, cx);
11077 }
11078
11079 pub fn fold_ranges<T: ToOffset + Clone>(
11080 &mut self,
11081 ranges: impl IntoIterator<Item = (Range<T>, FoldPlaceholder)>,
11082 auto_scroll: bool,
11083 cx: &mut ViewContext<Self>,
11084 ) {
11085 let mut fold_ranges = Vec::new();
11086 let mut buffers_affected = HashMap::default();
11087 let multi_buffer = self.buffer().read(cx);
11088 for (fold_range, fold_text) in ranges {
11089 if let Some((_, buffer, _)) =
11090 multi_buffer.excerpt_containing(fold_range.start.clone(), cx)
11091 {
11092 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
11093 };
11094 fold_ranges.push((fold_range, fold_text));
11095 }
11096
11097 let mut ranges = fold_ranges.into_iter().peekable();
11098 if ranges.peek().is_some() {
11099 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
11100
11101 if auto_scroll {
11102 self.request_autoscroll(Autoscroll::fit(), cx);
11103 }
11104
11105 for buffer in buffers_affected.into_values() {
11106 self.sync_expanded_diff_hunks(buffer, cx);
11107 }
11108
11109 cx.notify();
11110
11111 if let Some(active_diagnostics) = self.active_diagnostics.take() {
11112 // Clear diagnostics block when folding a range that contains it.
11113 let snapshot = self.snapshot(cx);
11114 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
11115 drop(snapshot);
11116 self.active_diagnostics = Some(active_diagnostics);
11117 self.dismiss_diagnostics(cx);
11118 } else {
11119 self.active_diagnostics = Some(active_diagnostics);
11120 }
11121 }
11122
11123 self.scrollbar_marker_state.dirty = true;
11124 }
11125 }
11126
11127 /// Removes any folds whose ranges intersect any of the given ranges.
11128 pub fn unfold_ranges<T: ToOffset + Clone>(
11129 &mut self,
11130 ranges: &[Range<T>],
11131 inclusive: bool,
11132 auto_scroll: bool,
11133 cx: &mut ViewContext<Self>,
11134 ) {
11135 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
11136 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
11137 });
11138 }
11139
11140 /// Removes any folds with the given ranges.
11141 pub fn remove_folds_with_type<T: ToOffset + Clone>(
11142 &mut self,
11143 ranges: &[Range<T>],
11144 type_id: TypeId,
11145 auto_scroll: bool,
11146 cx: &mut ViewContext<Self>,
11147 ) {
11148 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
11149 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
11150 });
11151 }
11152
11153 fn remove_folds_with<T: ToOffset + Clone>(
11154 &mut self,
11155 ranges: &[Range<T>],
11156 auto_scroll: bool,
11157 cx: &mut ViewContext<Self>,
11158 update: impl FnOnce(&mut DisplayMap, &mut ModelContext<DisplayMap>),
11159 ) {
11160 if ranges.is_empty() {
11161 return;
11162 }
11163
11164 let mut buffers_affected = HashMap::default();
11165 let multi_buffer = self.buffer().read(cx);
11166 for range in ranges {
11167 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
11168 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
11169 };
11170 }
11171
11172 self.display_map.update(cx, update);
11173 if auto_scroll {
11174 self.request_autoscroll(Autoscroll::fit(), cx);
11175 }
11176
11177 for buffer in buffers_affected.into_values() {
11178 self.sync_expanded_diff_hunks(buffer, cx);
11179 }
11180
11181 cx.notify();
11182 self.scrollbar_marker_state.dirty = true;
11183 self.active_indent_guides_state.dirty = true;
11184 }
11185
11186 pub fn default_fold_placeholder(&self, cx: &AppContext) -> FoldPlaceholder {
11187 self.display_map.read(cx).fold_placeholder.clone()
11188 }
11189
11190 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
11191 if hovered != self.gutter_hovered {
11192 self.gutter_hovered = hovered;
11193 cx.notify();
11194 }
11195 }
11196
11197 pub fn insert_blocks(
11198 &mut self,
11199 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
11200 autoscroll: Option<Autoscroll>,
11201 cx: &mut ViewContext<Self>,
11202 ) -> Vec<CustomBlockId> {
11203 let blocks = self
11204 .display_map
11205 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
11206 if let Some(autoscroll) = autoscroll {
11207 self.request_autoscroll(autoscroll, cx);
11208 }
11209 cx.notify();
11210 blocks
11211 }
11212
11213 pub fn resize_blocks(
11214 &mut self,
11215 heights: HashMap<CustomBlockId, u32>,
11216 autoscroll: Option<Autoscroll>,
11217 cx: &mut ViewContext<Self>,
11218 ) {
11219 self.display_map
11220 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
11221 if let Some(autoscroll) = autoscroll {
11222 self.request_autoscroll(autoscroll, cx);
11223 }
11224 cx.notify();
11225 }
11226
11227 pub fn replace_blocks(
11228 &mut self,
11229 renderers: HashMap<CustomBlockId, RenderBlock>,
11230 autoscroll: Option<Autoscroll>,
11231 cx: &mut ViewContext<Self>,
11232 ) {
11233 self.display_map
11234 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
11235 if let Some(autoscroll) = autoscroll {
11236 self.request_autoscroll(autoscroll, cx);
11237 }
11238 cx.notify();
11239 }
11240
11241 pub fn remove_blocks(
11242 &mut self,
11243 block_ids: HashSet<CustomBlockId>,
11244 autoscroll: Option<Autoscroll>,
11245 cx: &mut ViewContext<Self>,
11246 ) {
11247 self.display_map.update(cx, |display_map, cx| {
11248 display_map.remove_blocks(block_ids, cx)
11249 });
11250 if let Some(autoscroll) = autoscroll {
11251 self.request_autoscroll(autoscroll, cx);
11252 }
11253 cx.notify();
11254 }
11255
11256 pub fn row_for_block(
11257 &self,
11258 block_id: CustomBlockId,
11259 cx: &mut ViewContext<Self>,
11260 ) -> Option<DisplayRow> {
11261 self.display_map
11262 .update(cx, |map, cx| map.row_for_block(block_id, cx))
11263 }
11264
11265 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
11266 self.focused_block = Some(focused_block);
11267 }
11268
11269 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
11270 self.focused_block.take()
11271 }
11272
11273 pub fn insert_creases(
11274 &mut self,
11275 creases: impl IntoIterator<Item = Crease>,
11276 cx: &mut ViewContext<Self>,
11277 ) -> Vec<CreaseId> {
11278 self.display_map
11279 .update(cx, |map, cx| map.insert_creases(creases, cx))
11280 }
11281
11282 pub fn remove_creases(
11283 &mut self,
11284 ids: impl IntoIterator<Item = CreaseId>,
11285 cx: &mut ViewContext<Self>,
11286 ) {
11287 self.display_map
11288 .update(cx, |map, cx| map.remove_creases(ids, cx));
11289 }
11290
11291 pub fn longest_row(&self, cx: &mut AppContext) -> DisplayRow {
11292 self.display_map
11293 .update(cx, |map, cx| map.snapshot(cx))
11294 .longest_row()
11295 }
11296
11297 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
11298 self.display_map
11299 .update(cx, |map, cx| map.snapshot(cx))
11300 .max_point()
11301 }
11302
11303 pub fn text(&self, cx: &AppContext) -> String {
11304 self.buffer.read(cx).read(cx).text()
11305 }
11306
11307 pub fn text_option(&self, cx: &AppContext) -> Option<String> {
11308 let text = self.text(cx);
11309 let text = text.trim();
11310
11311 if text.is_empty() {
11312 return None;
11313 }
11314
11315 Some(text.to_string())
11316 }
11317
11318 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
11319 self.transact(cx, |this, cx| {
11320 this.buffer
11321 .read(cx)
11322 .as_singleton()
11323 .expect("you can only call set_text on editors for singleton buffers")
11324 .update(cx, |buffer, cx| buffer.set_text(text, cx));
11325 });
11326 }
11327
11328 pub fn display_text(&self, cx: &mut AppContext) -> String {
11329 self.display_map
11330 .update(cx, |map, cx| map.snapshot(cx))
11331 .text()
11332 }
11333
11334 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
11335 let mut wrap_guides = smallvec::smallvec![];
11336
11337 if self.show_wrap_guides == Some(false) {
11338 return wrap_guides;
11339 }
11340
11341 let settings = self.buffer.read(cx).settings_at(0, cx);
11342 if settings.show_wrap_guides {
11343 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
11344 wrap_guides.push((soft_wrap as usize, true));
11345 } else if let SoftWrap::Bounded(soft_wrap) = self.soft_wrap_mode(cx) {
11346 wrap_guides.push((soft_wrap as usize, true));
11347 }
11348 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
11349 }
11350
11351 wrap_guides
11352 }
11353
11354 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
11355 let settings = self.buffer.read(cx).settings_at(0, cx);
11356 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
11357 match mode {
11358 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
11359 SoftWrap::None
11360 }
11361 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
11362 language_settings::SoftWrap::PreferredLineLength => {
11363 SoftWrap::Column(settings.preferred_line_length)
11364 }
11365 language_settings::SoftWrap::Bounded => {
11366 SoftWrap::Bounded(settings.preferred_line_length)
11367 }
11368 }
11369 }
11370
11371 pub fn set_soft_wrap_mode(
11372 &mut self,
11373 mode: language_settings::SoftWrap,
11374 cx: &mut ViewContext<Self>,
11375 ) {
11376 self.soft_wrap_mode_override = Some(mode);
11377 cx.notify();
11378 }
11379
11380 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
11381 self.text_style_refinement = Some(style);
11382 }
11383
11384 /// called by the Element so we know what style we were most recently rendered with.
11385 pub(crate) fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
11386 let rem_size = cx.rem_size();
11387 self.display_map.update(cx, |map, cx| {
11388 map.set_font(
11389 style.text.font(),
11390 style.text.font_size.to_pixels(rem_size),
11391 cx,
11392 )
11393 });
11394 self.style = Some(style);
11395 }
11396
11397 pub fn style(&self) -> Option<&EditorStyle> {
11398 self.style.as_ref()
11399 }
11400
11401 // Called by the element. This method is not designed to be called outside of the editor
11402 // element's layout code because it does not notify when rewrapping is computed synchronously.
11403 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
11404 self.display_map
11405 .update(cx, |map, cx| map.set_wrap_width(width, cx))
11406 }
11407
11408 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
11409 if self.soft_wrap_mode_override.is_some() {
11410 self.soft_wrap_mode_override.take();
11411 } else {
11412 let soft_wrap = match self.soft_wrap_mode(cx) {
11413 SoftWrap::GitDiff => return,
11414 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
11415 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
11416 language_settings::SoftWrap::None
11417 }
11418 };
11419 self.soft_wrap_mode_override = Some(soft_wrap);
11420 }
11421 cx.notify();
11422 }
11423
11424 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, cx: &mut ViewContext<Self>) {
11425 let Some(workspace) = self.workspace() else {
11426 return;
11427 };
11428 let fs = workspace.read(cx).app_state().fs.clone();
11429 let current_show = TabBarSettings::get_global(cx).show;
11430 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
11431 setting.show = Some(!current_show);
11432 });
11433 }
11434
11435 pub fn toggle_indent_guides(&mut self, _: &ToggleIndentGuides, cx: &mut ViewContext<Self>) {
11436 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
11437 self.buffer
11438 .read(cx)
11439 .settings_at(0, cx)
11440 .indent_guides
11441 .enabled
11442 });
11443 self.show_indent_guides = Some(!currently_enabled);
11444 cx.notify();
11445 }
11446
11447 fn should_show_indent_guides(&self) -> Option<bool> {
11448 self.show_indent_guides
11449 }
11450
11451 pub fn toggle_line_numbers(&mut self, _: &ToggleLineNumbers, cx: &mut ViewContext<Self>) {
11452 let mut editor_settings = EditorSettings::get_global(cx).clone();
11453 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
11454 EditorSettings::override_global(editor_settings, cx);
11455 }
11456
11457 pub fn should_use_relative_line_numbers(&self, cx: &WindowContext) -> bool {
11458 self.use_relative_line_numbers
11459 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
11460 }
11461
11462 pub fn toggle_relative_line_numbers(
11463 &mut self,
11464 _: &ToggleRelativeLineNumbers,
11465 cx: &mut ViewContext<Self>,
11466 ) {
11467 let is_relative = self.should_use_relative_line_numbers(cx);
11468 self.set_relative_line_number(Some(!is_relative), cx)
11469 }
11470
11471 pub fn set_relative_line_number(
11472 &mut self,
11473 is_relative: Option<bool>,
11474 cx: &mut ViewContext<Self>,
11475 ) {
11476 self.use_relative_line_numbers = is_relative;
11477 cx.notify();
11478 }
11479
11480 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
11481 self.show_gutter = show_gutter;
11482 cx.notify();
11483 }
11484
11485 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut ViewContext<Self>) {
11486 self.show_line_numbers = Some(show_line_numbers);
11487 cx.notify();
11488 }
11489
11490 pub fn set_show_git_diff_gutter(
11491 &mut self,
11492 show_git_diff_gutter: bool,
11493 cx: &mut ViewContext<Self>,
11494 ) {
11495 self.show_git_diff_gutter = Some(show_git_diff_gutter);
11496 cx.notify();
11497 }
11498
11499 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut ViewContext<Self>) {
11500 self.show_code_actions = Some(show_code_actions);
11501 cx.notify();
11502 }
11503
11504 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut ViewContext<Self>) {
11505 self.show_runnables = Some(show_runnables);
11506 cx.notify();
11507 }
11508
11509 pub fn set_masked(&mut self, masked: bool, cx: &mut ViewContext<Self>) {
11510 if self.display_map.read(cx).masked != masked {
11511 self.display_map.update(cx, |map, _| map.masked = masked);
11512 }
11513 cx.notify()
11514 }
11515
11516 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut ViewContext<Self>) {
11517 self.show_wrap_guides = Some(show_wrap_guides);
11518 cx.notify();
11519 }
11520
11521 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut ViewContext<Self>) {
11522 self.show_indent_guides = Some(show_indent_guides);
11523 cx.notify();
11524 }
11525
11526 pub fn working_directory(&self, cx: &WindowContext) -> Option<PathBuf> {
11527 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
11528 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
11529 if let Some(dir) = file.abs_path(cx).parent() {
11530 return Some(dir.to_owned());
11531 }
11532 }
11533
11534 if let Some(project_path) = buffer.read(cx).project_path(cx) {
11535 return Some(project_path.path.to_path_buf());
11536 }
11537 }
11538
11539 None
11540 }
11541
11542 fn target_file<'a>(&self, cx: &'a AppContext) -> Option<&'a dyn language::LocalFile> {
11543 self.active_excerpt(cx)?
11544 .1
11545 .read(cx)
11546 .file()
11547 .and_then(|f| f.as_local())
11548 }
11549
11550 pub fn reveal_in_finder(&mut self, _: &RevealInFileManager, cx: &mut ViewContext<Self>) {
11551 if let Some(target) = self.target_file(cx) {
11552 cx.reveal_path(&target.abs_path(cx));
11553 }
11554 }
11555
11556 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
11557 if let Some(file) = self.target_file(cx) {
11558 if let Some(path) = file.abs_path(cx).to_str() {
11559 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
11560 }
11561 }
11562 }
11563
11564 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
11565 if let Some(file) = self.target_file(cx) {
11566 if let Some(path) = file.path().to_str() {
11567 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
11568 }
11569 }
11570 }
11571
11572 pub fn toggle_git_blame(&mut self, _: &ToggleGitBlame, cx: &mut ViewContext<Self>) {
11573 self.show_git_blame_gutter = !self.show_git_blame_gutter;
11574
11575 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
11576 self.start_git_blame(true, cx);
11577 }
11578
11579 cx.notify();
11580 }
11581
11582 pub fn toggle_git_blame_inline(
11583 &mut self,
11584 _: &ToggleGitBlameInline,
11585 cx: &mut ViewContext<Self>,
11586 ) {
11587 self.toggle_git_blame_inline_internal(true, cx);
11588 cx.notify();
11589 }
11590
11591 pub fn git_blame_inline_enabled(&self) -> bool {
11592 self.git_blame_inline_enabled
11593 }
11594
11595 pub fn toggle_selection_menu(&mut self, _: &ToggleSelectionMenu, cx: &mut ViewContext<Self>) {
11596 self.show_selection_menu = self
11597 .show_selection_menu
11598 .map(|show_selections_menu| !show_selections_menu)
11599 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
11600
11601 cx.notify();
11602 }
11603
11604 pub fn selection_menu_enabled(&self, cx: &AppContext) -> bool {
11605 self.show_selection_menu
11606 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
11607 }
11608
11609 fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
11610 if let Some(project) = self.project.as_ref() {
11611 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
11612 return;
11613 };
11614
11615 if buffer.read(cx).file().is_none() {
11616 return;
11617 }
11618
11619 let focused = self.focus_handle(cx).contains_focused(cx);
11620
11621 let project = project.clone();
11622 let blame =
11623 cx.new_model(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
11624 self.blame_subscription = Some(cx.observe(&blame, |_, _, cx| cx.notify()));
11625 self.blame = Some(blame);
11626 }
11627 }
11628
11629 fn toggle_git_blame_inline_internal(
11630 &mut self,
11631 user_triggered: bool,
11632 cx: &mut ViewContext<Self>,
11633 ) {
11634 if self.git_blame_inline_enabled {
11635 self.git_blame_inline_enabled = false;
11636 self.show_git_blame_inline = false;
11637 self.show_git_blame_inline_delay_task.take();
11638 } else {
11639 self.git_blame_inline_enabled = true;
11640 self.start_git_blame_inline(user_triggered, cx);
11641 }
11642
11643 cx.notify();
11644 }
11645
11646 fn start_git_blame_inline(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
11647 self.start_git_blame(user_triggered, cx);
11648
11649 if ProjectSettings::get_global(cx)
11650 .git
11651 .inline_blame_delay()
11652 .is_some()
11653 {
11654 self.start_inline_blame_timer(cx);
11655 } else {
11656 self.show_git_blame_inline = true
11657 }
11658 }
11659
11660 pub fn blame(&self) -> Option<&Model<GitBlame>> {
11661 self.blame.as_ref()
11662 }
11663
11664 pub fn render_git_blame_gutter(&mut self, cx: &mut WindowContext) -> bool {
11665 self.show_git_blame_gutter && self.has_blame_entries(cx)
11666 }
11667
11668 pub fn render_git_blame_inline(&mut self, cx: &mut WindowContext) -> bool {
11669 self.show_git_blame_inline
11670 && self.focus_handle.is_focused(cx)
11671 && !self.newest_selection_head_on_empty_line(cx)
11672 && self.has_blame_entries(cx)
11673 }
11674
11675 fn has_blame_entries(&self, cx: &mut WindowContext) -> bool {
11676 self.blame()
11677 .map_or(false, |blame| blame.read(cx).has_generated_entries())
11678 }
11679
11680 fn newest_selection_head_on_empty_line(&mut self, cx: &mut WindowContext) -> bool {
11681 let cursor_anchor = self.selections.newest_anchor().head();
11682
11683 let snapshot = self.buffer.read(cx).snapshot(cx);
11684 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
11685
11686 snapshot.line_len(buffer_row) == 0
11687 }
11688
11689 fn get_permalink_to_line(&mut self, cx: &mut ViewContext<Self>) -> Task<Result<url::Url>> {
11690 let buffer_and_selection = maybe!({
11691 let selection = self.selections.newest::<Point>(cx);
11692 let selection_range = selection.range();
11693
11694 let (buffer, selection) = if let Some(buffer) = self.buffer().read(cx).as_singleton() {
11695 (buffer, selection_range.start.row..selection_range.end.row)
11696 } else {
11697 let buffer_ranges = self
11698 .buffer()
11699 .read(cx)
11700 .range_to_buffer_ranges(selection_range, cx);
11701
11702 let (buffer, range, _) = if selection.reversed {
11703 buffer_ranges.first()
11704 } else {
11705 buffer_ranges.last()
11706 }?;
11707
11708 let snapshot = buffer.read(cx).snapshot();
11709 let selection = text::ToPoint::to_point(&range.start, &snapshot).row
11710 ..text::ToPoint::to_point(&range.end, &snapshot).row;
11711 (buffer.clone(), selection)
11712 };
11713
11714 Some((buffer, selection))
11715 });
11716
11717 let Some((buffer, selection)) = buffer_and_selection else {
11718 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
11719 };
11720
11721 let Some(project) = self.project.as_ref() else {
11722 return Task::ready(Err(anyhow!("editor does not have project")));
11723 };
11724
11725 project.update(cx, |project, cx| {
11726 project.get_permalink_to_line(&buffer, selection, cx)
11727 })
11728 }
11729
11730 pub fn copy_permalink_to_line(&mut self, _: &CopyPermalinkToLine, cx: &mut ViewContext<Self>) {
11731 let permalink_task = self.get_permalink_to_line(cx);
11732 let workspace = self.workspace();
11733
11734 cx.spawn(|_, mut cx| async move {
11735 match permalink_task.await {
11736 Ok(permalink) => {
11737 cx.update(|cx| {
11738 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
11739 })
11740 .ok();
11741 }
11742 Err(err) => {
11743 let message = format!("Failed to copy permalink: {err}");
11744
11745 Err::<(), anyhow::Error>(err).log_err();
11746
11747 if let Some(workspace) = workspace {
11748 workspace
11749 .update(&mut cx, |workspace, cx| {
11750 struct CopyPermalinkToLine;
11751
11752 workspace.show_toast(
11753 Toast::new(
11754 NotificationId::unique::<CopyPermalinkToLine>(),
11755 message,
11756 ),
11757 cx,
11758 )
11759 })
11760 .ok();
11761 }
11762 }
11763 }
11764 })
11765 .detach();
11766 }
11767
11768 pub fn copy_file_location(&mut self, _: &CopyFileLocation, cx: &mut ViewContext<Self>) {
11769 let selection = self.selections.newest::<Point>(cx).start.row + 1;
11770 if let Some(file) = self.target_file(cx) {
11771 if let Some(path) = file.path().to_str() {
11772 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
11773 }
11774 }
11775 }
11776
11777 pub fn open_permalink_to_line(&mut self, _: &OpenPermalinkToLine, cx: &mut ViewContext<Self>) {
11778 let permalink_task = self.get_permalink_to_line(cx);
11779 let workspace = self.workspace();
11780
11781 cx.spawn(|_, mut cx| async move {
11782 match permalink_task.await {
11783 Ok(permalink) => {
11784 cx.update(|cx| {
11785 cx.open_url(permalink.as_ref());
11786 })
11787 .ok();
11788 }
11789 Err(err) => {
11790 let message = format!("Failed to open permalink: {err}");
11791
11792 Err::<(), anyhow::Error>(err).log_err();
11793
11794 if let Some(workspace) = workspace {
11795 workspace
11796 .update(&mut cx, |workspace, cx| {
11797 struct OpenPermalinkToLine;
11798
11799 workspace.show_toast(
11800 Toast::new(
11801 NotificationId::unique::<OpenPermalinkToLine>(),
11802 message,
11803 ),
11804 cx,
11805 )
11806 })
11807 .ok();
11808 }
11809 }
11810 }
11811 })
11812 .detach();
11813 }
11814
11815 /// Adds a row highlight for the given range. If a row has multiple highlights, the
11816 /// last highlight added will be used.
11817 ///
11818 /// If the range ends at the beginning of a line, then that line will not be highlighted.
11819 pub fn highlight_rows<T: 'static>(
11820 &mut self,
11821 range: Range<Anchor>,
11822 color: Hsla,
11823 should_autoscroll: bool,
11824 cx: &mut ViewContext<Self>,
11825 ) {
11826 let snapshot = self.buffer().read(cx).snapshot(cx);
11827 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
11828 let ix = row_highlights.binary_search_by(|highlight| {
11829 Ordering::Equal
11830 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
11831 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
11832 });
11833
11834 if let Err(mut ix) = ix {
11835 let index = post_inc(&mut self.highlight_order);
11836
11837 // If this range intersects with the preceding highlight, then merge it with
11838 // the preceding highlight. Otherwise insert a new highlight.
11839 let mut merged = false;
11840 if ix > 0 {
11841 let prev_highlight = &mut row_highlights[ix - 1];
11842 if prev_highlight
11843 .range
11844 .end
11845 .cmp(&range.start, &snapshot)
11846 .is_ge()
11847 {
11848 ix -= 1;
11849 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
11850 prev_highlight.range.end = range.end;
11851 }
11852 merged = true;
11853 prev_highlight.index = index;
11854 prev_highlight.color = color;
11855 prev_highlight.should_autoscroll = should_autoscroll;
11856 }
11857 }
11858
11859 if !merged {
11860 row_highlights.insert(
11861 ix,
11862 RowHighlight {
11863 range: range.clone(),
11864 index,
11865 color,
11866 should_autoscroll,
11867 },
11868 );
11869 }
11870
11871 // If any of the following highlights intersect with this one, merge them.
11872 while let Some(next_highlight) = row_highlights.get(ix + 1) {
11873 let highlight = &row_highlights[ix];
11874 if next_highlight
11875 .range
11876 .start
11877 .cmp(&highlight.range.end, &snapshot)
11878 .is_le()
11879 {
11880 if next_highlight
11881 .range
11882 .end
11883 .cmp(&highlight.range.end, &snapshot)
11884 .is_gt()
11885 {
11886 row_highlights[ix].range.end = next_highlight.range.end;
11887 }
11888 row_highlights.remove(ix + 1);
11889 } else {
11890 break;
11891 }
11892 }
11893 }
11894 }
11895
11896 /// Remove any highlighted row ranges of the given type that intersect the
11897 /// given ranges.
11898 pub fn remove_highlighted_rows<T: 'static>(
11899 &mut self,
11900 ranges_to_remove: Vec<Range<Anchor>>,
11901 cx: &mut ViewContext<Self>,
11902 ) {
11903 let snapshot = self.buffer().read(cx).snapshot(cx);
11904 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
11905 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
11906 row_highlights.retain(|highlight| {
11907 while let Some(range_to_remove) = ranges_to_remove.peek() {
11908 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
11909 Ordering::Less | Ordering::Equal => {
11910 ranges_to_remove.next();
11911 }
11912 Ordering::Greater => {
11913 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
11914 Ordering::Less | Ordering::Equal => {
11915 return false;
11916 }
11917 Ordering::Greater => break,
11918 }
11919 }
11920 }
11921 }
11922
11923 true
11924 })
11925 }
11926
11927 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
11928 pub fn clear_row_highlights<T: 'static>(&mut self) {
11929 self.highlighted_rows.remove(&TypeId::of::<T>());
11930 }
11931
11932 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
11933 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
11934 self.highlighted_rows
11935 .get(&TypeId::of::<T>())
11936 .map_or(&[] as &[_], |vec| vec.as_slice())
11937 .iter()
11938 .map(|highlight| (highlight.range.clone(), highlight.color))
11939 }
11940
11941 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
11942 /// Rerturns a map of display rows that are highlighted and their corresponding highlight color.
11943 /// Allows to ignore certain kinds of highlights.
11944 pub fn highlighted_display_rows(
11945 &mut self,
11946 cx: &mut WindowContext,
11947 ) -> BTreeMap<DisplayRow, Hsla> {
11948 let snapshot = self.snapshot(cx);
11949 let mut used_highlight_orders = HashMap::default();
11950 self.highlighted_rows
11951 .iter()
11952 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
11953 .fold(
11954 BTreeMap::<DisplayRow, Hsla>::new(),
11955 |mut unique_rows, highlight| {
11956 let start = highlight.range.start.to_display_point(&snapshot);
11957 let end = highlight.range.end.to_display_point(&snapshot);
11958 let start_row = start.row().0;
11959 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
11960 && end.column() == 0
11961 {
11962 end.row().0.saturating_sub(1)
11963 } else {
11964 end.row().0
11965 };
11966 for row in start_row..=end_row {
11967 let used_index =
11968 used_highlight_orders.entry(row).or_insert(highlight.index);
11969 if highlight.index >= *used_index {
11970 *used_index = highlight.index;
11971 unique_rows.insert(DisplayRow(row), highlight.color);
11972 }
11973 }
11974 unique_rows
11975 },
11976 )
11977 }
11978
11979 pub fn highlighted_display_row_for_autoscroll(
11980 &self,
11981 snapshot: &DisplaySnapshot,
11982 ) -> Option<DisplayRow> {
11983 self.highlighted_rows
11984 .values()
11985 .flat_map(|highlighted_rows| highlighted_rows.iter())
11986 .filter_map(|highlight| {
11987 if highlight.should_autoscroll {
11988 Some(highlight.range.start.to_display_point(snapshot).row())
11989 } else {
11990 None
11991 }
11992 })
11993 .min()
11994 }
11995
11996 pub fn set_search_within_ranges(
11997 &mut self,
11998 ranges: &[Range<Anchor>],
11999 cx: &mut ViewContext<Self>,
12000 ) {
12001 self.highlight_background::<SearchWithinRange>(
12002 ranges,
12003 |colors| colors.editor_document_highlight_read_background,
12004 cx,
12005 )
12006 }
12007
12008 pub fn set_breadcrumb_header(&mut self, new_header: String) {
12009 self.breadcrumb_header = Some(new_header);
12010 }
12011
12012 pub fn clear_search_within_ranges(&mut self, cx: &mut ViewContext<Self>) {
12013 self.clear_background_highlights::<SearchWithinRange>(cx);
12014 }
12015
12016 pub fn highlight_background<T: 'static>(
12017 &mut self,
12018 ranges: &[Range<Anchor>],
12019 color_fetcher: fn(&ThemeColors) -> Hsla,
12020 cx: &mut ViewContext<Self>,
12021 ) {
12022 self.background_highlights
12023 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
12024 self.scrollbar_marker_state.dirty = true;
12025 cx.notify();
12026 }
12027
12028 pub fn clear_background_highlights<T: 'static>(
12029 &mut self,
12030 cx: &mut ViewContext<Self>,
12031 ) -> Option<BackgroundHighlight> {
12032 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
12033 if !text_highlights.1.is_empty() {
12034 self.scrollbar_marker_state.dirty = true;
12035 cx.notify();
12036 }
12037 Some(text_highlights)
12038 }
12039
12040 pub fn highlight_gutter<T: 'static>(
12041 &mut self,
12042 ranges: &[Range<Anchor>],
12043 color_fetcher: fn(&AppContext) -> Hsla,
12044 cx: &mut ViewContext<Self>,
12045 ) {
12046 self.gutter_highlights
12047 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
12048 cx.notify();
12049 }
12050
12051 pub fn clear_gutter_highlights<T: 'static>(
12052 &mut self,
12053 cx: &mut ViewContext<Self>,
12054 ) -> Option<GutterHighlight> {
12055 cx.notify();
12056 self.gutter_highlights.remove(&TypeId::of::<T>())
12057 }
12058
12059 #[cfg(feature = "test-support")]
12060 pub fn all_text_background_highlights(
12061 &mut self,
12062 cx: &mut ViewContext<Self>,
12063 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
12064 let snapshot = self.snapshot(cx);
12065 let buffer = &snapshot.buffer_snapshot;
12066 let start = buffer.anchor_before(0);
12067 let end = buffer.anchor_after(buffer.len());
12068 let theme = cx.theme().colors();
12069 self.background_highlights_in_range(start..end, &snapshot, theme)
12070 }
12071
12072 #[cfg(feature = "test-support")]
12073 pub fn search_background_highlights(
12074 &mut self,
12075 cx: &mut ViewContext<Self>,
12076 ) -> Vec<Range<Point>> {
12077 let snapshot = self.buffer().read(cx).snapshot(cx);
12078
12079 let highlights = self
12080 .background_highlights
12081 .get(&TypeId::of::<items::BufferSearchHighlights>());
12082
12083 if let Some((_color, ranges)) = highlights {
12084 ranges
12085 .iter()
12086 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
12087 .collect_vec()
12088 } else {
12089 vec![]
12090 }
12091 }
12092
12093 fn document_highlights_for_position<'a>(
12094 &'a self,
12095 position: Anchor,
12096 buffer: &'a MultiBufferSnapshot,
12097 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
12098 let read_highlights = self
12099 .background_highlights
12100 .get(&TypeId::of::<DocumentHighlightRead>())
12101 .map(|h| &h.1);
12102 let write_highlights = self
12103 .background_highlights
12104 .get(&TypeId::of::<DocumentHighlightWrite>())
12105 .map(|h| &h.1);
12106 let left_position = position.bias_left(buffer);
12107 let right_position = position.bias_right(buffer);
12108 read_highlights
12109 .into_iter()
12110 .chain(write_highlights)
12111 .flat_map(move |ranges| {
12112 let start_ix = match ranges.binary_search_by(|probe| {
12113 let cmp = probe.end.cmp(&left_position, buffer);
12114 if cmp.is_ge() {
12115 Ordering::Greater
12116 } else {
12117 Ordering::Less
12118 }
12119 }) {
12120 Ok(i) | Err(i) => i,
12121 };
12122
12123 ranges[start_ix..]
12124 .iter()
12125 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
12126 })
12127 }
12128
12129 pub fn has_background_highlights<T: 'static>(&self) -> bool {
12130 self.background_highlights
12131 .get(&TypeId::of::<T>())
12132 .map_or(false, |(_, highlights)| !highlights.is_empty())
12133 }
12134
12135 pub fn background_highlights_in_range(
12136 &self,
12137 search_range: Range<Anchor>,
12138 display_snapshot: &DisplaySnapshot,
12139 theme: &ThemeColors,
12140 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
12141 let mut results = Vec::new();
12142 for (color_fetcher, ranges) in self.background_highlights.values() {
12143 let color = color_fetcher(theme);
12144 let start_ix = match ranges.binary_search_by(|probe| {
12145 let cmp = probe
12146 .end
12147 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
12148 if cmp.is_gt() {
12149 Ordering::Greater
12150 } else {
12151 Ordering::Less
12152 }
12153 }) {
12154 Ok(i) | Err(i) => i,
12155 };
12156 for range in &ranges[start_ix..] {
12157 if range
12158 .start
12159 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
12160 .is_ge()
12161 {
12162 break;
12163 }
12164
12165 let start = range.start.to_display_point(display_snapshot);
12166 let end = range.end.to_display_point(display_snapshot);
12167 results.push((start..end, color))
12168 }
12169 }
12170 results
12171 }
12172
12173 pub fn background_highlight_row_ranges<T: 'static>(
12174 &self,
12175 search_range: Range<Anchor>,
12176 display_snapshot: &DisplaySnapshot,
12177 count: usize,
12178 ) -> Vec<RangeInclusive<DisplayPoint>> {
12179 let mut results = Vec::new();
12180 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
12181 return vec![];
12182 };
12183
12184 let start_ix = match ranges.binary_search_by(|probe| {
12185 let cmp = probe
12186 .end
12187 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
12188 if cmp.is_gt() {
12189 Ordering::Greater
12190 } else {
12191 Ordering::Less
12192 }
12193 }) {
12194 Ok(i) | Err(i) => i,
12195 };
12196 let mut push_region = |start: Option<Point>, end: Option<Point>| {
12197 if let (Some(start_display), Some(end_display)) = (start, end) {
12198 results.push(
12199 start_display.to_display_point(display_snapshot)
12200 ..=end_display.to_display_point(display_snapshot),
12201 );
12202 }
12203 };
12204 let mut start_row: Option<Point> = None;
12205 let mut end_row: Option<Point> = None;
12206 if ranges.len() > count {
12207 return Vec::new();
12208 }
12209 for range in &ranges[start_ix..] {
12210 if range
12211 .start
12212 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
12213 .is_ge()
12214 {
12215 break;
12216 }
12217 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
12218 if let Some(current_row) = &end_row {
12219 if end.row == current_row.row {
12220 continue;
12221 }
12222 }
12223 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
12224 if start_row.is_none() {
12225 assert_eq!(end_row, None);
12226 start_row = Some(start);
12227 end_row = Some(end);
12228 continue;
12229 }
12230 if let Some(current_end) = end_row.as_mut() {
12231 if start.row > current_end.row + 1 {
12232 push_region(start_row, end_row);
12233 start_row = Some(start);
12234 end_row = Some(end);
12235 } else {
12236 // Merge two hunks.
12237 *current_end = end;
12238 }
12239 } else {
12240 unreachable!();
12241 }
12242 }
12243 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
12244 push_region(start_row, end_row);
12245 results
12246 }
12247
12248 pub fn gutter_highlights_in_range(
12249 &self,
12250 search_range: Range<Anchor>,
12251 display_snapshot: &DisplaySnapshot,
12252 cx: &AppContext,
12253 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
12254 let mut results = Vec::new();
12255 for (color_fetcher, ranges) in self.gutter_highlights.values() {
12256 let color = color_fetcher(cx);
12257 let start_ix = match ranges.binary_search_by(|probe| {
12258 let cmp = probe
12259 .end
12260 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
12261 if cmp.is_gt() {
12262 Ordering::Greater
12263 } else {
12264 Ordering::Less
12265 }
12266 }) {
12267 Ok(i) | Err(i) => i,
12268 };
12269 for range in &ranges[start_ix..] {
12270 if range
12271 .start
12272 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
12273 .is_ge()
12274 {
12275 break;
12276 }
12277
12278 let start = range.start.to_display_point(display_snapshot);
12279 let end = range.end.to_display_point(display_snapshot);
12280 results.push((start..end, color))
12281 }
12282 }
12283 results
12284 }
12285
12286 /// Get the text ranges corresponding to the redaction query
12287 pub fn redacted_ranges(
12288 &self,
12289 search_range: Range<Anchor>,
12290 display_snapshot: &DisplaySnapshot,
12291 cx: &WindowContext,
12292 ) -> Vec<Range<DisplayPoint>> {
12293 display_snapshot
12294 .buffer_snapshot
12295 .redacted_ranges(search_range, |file| {
12296 if let Some(file) = file {
12297 file.is_private()
12298 && EditorSettings::get(
12299 Some(SettingsLocation {
12300 worktree_id: file.worktree_id(cx),
12301 path: file.path().as_ref(),
12302 }),
12303 cx,
12304 )
12305 .redact_private_values
12306 } else {
12307 false
12308 }
12309 })
12310 .map(|range| {
12311 range.start.to_display_point(display_snapshot)
12312 ..range.end.to_display_point(display_snapshot)
12313 })
12314 .collect()
12315 }
12316
12317 pub fn highlight_text<T: 'static>(
12318 &mut self,
12319 ranges: Vec<Range<Anchor>>,
12320 style: HighlightStyle,
12321 cx: &mut ViewContext<Self>,
12322 ) {
12323 self.display_map.update(cx, |map, _| {
12324 map.highlight_text(TypeId::of::<T>(), ranges, style)
12325 });
12326 cx.notify();
12327 }
12328
12329 pub(crate) fn highlight_inlays<T: 'static>(
12330 &mut self,
12331 highlights: Vec<InlayHighlight>,
12332 style: HighlightStyle,
12333 cx: &mut ViewContext<Self>,
12334 ) {
12335 self.display_map.update(cx, |map, _| {
12336 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
12337 });
12338 cx.notify();
12339 }
12340
12341 pub fn text_highlights<'a, T: 'static>(
12342 &'a self,
12343 cx: &'a AppContext,
12344 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
12345 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
12346 }
12347
12348 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
12349 let cleared = self
12350 .display_map
12351 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
12352 if cleared {
12353 cx.notify();
12354 }
12355 }
12356
12357 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
12358 (self.read_only(cx) || self.blink_manager.read(cx).visible())
12359 && self.focus_handle.is_focused(cx)
12360 }
12361
12362 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut ViewContext<Self>) {
12363 self.show_cursor_when_unfocused = is_enabled;
12364 cx.notify();
12365 }
12366
12367 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
12368 cx.notify();
12369 }
12370
12371 fn on_buffer_event(
12372 &mut self,
12373 multibuffer: Model<MultiBuffer>,
12374 event: &multi_buffer::Event,
12375 cx: &mut ViewContext<Self>,
12376 ) {
12377 match event {
12378 multi_buffer::Event::Edited {
12379 singleton_buffer_edited,
12380 } => {
12381 self.scrollbar_marker_state.dirty = true;
12382 self.active_indent_guides_state.dirty = true;
12383 self.refresh_active_diagnostics(cx);
12384 self.refresh_code_actions(cx);
12385 if self.has_active_inline_completion(cx) {
12386 self.update_visible_inline_completion(cx);
12387 }
12388 cx.emit(EditorEvent::BufferEdited);
12389 cx.emit(SearchEvent::MatchesInvalidated);
12390 if *singleton_buffer_edited {
12391 if let Some(project) = &self.project {
12392 let project = project.read(cx);
12393 #[allow(clippy::mutable_key_type)]
12394 let languages_affected = multibuffer
12395 .read(cx)
12396 .all_buffers()
12397 .into_iter()
12398 .filter_map(|buffer| {
12399 let buffer = buffer.read(cx);
12400 let language = buffer.language()?;
12401 if project.is_local()
12402 && project.language_servers_for_buffer(buffer, cx).count() == 0
12403 {
12404 None
12405 } else {
12406 Some(language)
12407 }
12408 })
12409 .cloned()
12410 .collect::<HashSet<_>>();
12411 if !languages_affected.is_empty() {
12412 self.refresh_inlay_hints(
12413 InlayHintRefreshReason::BufferEdited(languages_affected),
12414 cx,
12415 );
12416 }
12417 }
12418 }
12419
12420 let Some(project) = &self.project else { return };
12421 let (telemetry, is_via_ssh) = {
12422 let project = project.read(cx);
12423 let telemetry = project.client().telemetry().clone();
12424 let is_via_ssh = project.is_via_ssh();
12425 (telemetry, is_via_ssh)
12426 };
12427 refresh_linked_ranges(self, cx);
12428 telemetry.log_edit_event("editor", is_via_ssh);
12429 }
12430 multi_buffer::Event::ExcerptsAdded {
12431 buffer,
12432 predecessor,
12433 excerpts,
12434 } => {
12435 self.tasks_update_task = Some(self.refresh_runnables(cx));
12436 cx.emit(EditorEvent::ExcerptsAdded {
12437 buffer: buffer.clone(),
12438 predecessor: *predecessor,
12439 excerpts: excerpts.clone(),
12440 });
12441 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
12442 }
12443 multi_buffer::Event::ExcerptsRemoved { ids } => {
12444 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
12445 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
12446 }
12447 multi_buffer::Event::ExcerptsEdited { ids } => {
12448 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
12449 }
12450 multi_buffer::Event::ExcerptsExpanded { ids } => {
12451 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
12452 }
12453 multi_buffer::Event::Reparsed(buffer_id) => {
12454 self.tasks_update_task = Some(self.refresh_runnables(cx));
12455
12456 cx.emit(EditorEvent::Reparsed(*buffer_id));
12457 }
12458 multi_buffer::Event::LanguageChanged(buffer_id) => {
12459 linked_editing_ranges::refresh_linked_ranges(self, cx);
12460 cx.emit(EditorEvent::Reparsed(*buffer_id));
12461 cx.notify();
12462 }
12463 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
12464 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
12465 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
12466 cx.emit(EditorEvent::TitleChanged)
12467 }
12468 multi_buffer::Event::DiffBaseChanged => {
12469 self.scrollbar_marker_state.dirty = true;
12470 cx.emit(EditorEvent::DiffBaseChanged);
12471 cx.notify();
12472 }
12473 multi_buffer::Event::DiffUpdated { buffer } => {
12474 self.sync_expanded_diff_hunks(buffer.clone(), cx);
12475 cx.notify();
12476 }
12477 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
12478 multi_buffer::Event::DiagnosticsUpdated => {
12479 self.refresh_active_diagnostics(cx);
12480 self.scrollbar_marker_state.dirty = true;
12481 cx.notify();
12482 }
12483 _ => {}
12484 };
12485 }
12486
12487 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
12488 cx.notify();
12489 }
12490
12491 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
12492 self.tasks_update_task = Some(self.refresh_runnables(cx));
12493 self.refresh_inline_completion(true, false, cx);
12494 self.refresh_inlay_hints(
12495 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
12496 self.selections.newest_anchor().head(),
12497 &self.buffer.read(cx).snapshot(cx),
12498 cx,
12499 )),
12500 cx,
12501 );
12502
12503 let old_cursor_shape = self.cursor_shape;
12504
12505 {
12506 let editor_settings = EditorSettings::get_global(cx);
12507 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
12508 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
12509 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
12510 }
12511
12512 if old_cursor_shape != self.cursor_shape {
12513 cx.emit(EditorEvent::CursorShapeChanged);
12514 }
12515
12516 let project_settings = ProjectSettings::get_global(cx);
12517 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
12518
12519 if self.mode == EditorMode::Full {
12520 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
12521 if self.git_blame_inline_enabled != inline_blame_enabled {
12522 self.toggle_git_blame_inline_internal(false, cx);
12523 }
12524 }
12525
12526 cx.notify();
12527 }
12528
12529 pub fn set_searchable(&mut self, searchable: bool) {
12530 self.searchable = searchable;
12531 }
12532
12533 pub fn searchable(&self) -> bool {
12534 self.searchable
12535 }
12536
12537 fn open_proposed_changes_editor(
12538 &mut self,
12539 _: &OpenProposedChangesEditor,
12540 cx: &mut ViewContext<Self>,
12541 ) {
12542 let Some(workspace) = self.workspace() else {
12543 cx.propagate();
12544 return;
12545 };
12546
12547 let selections = self.selections.all::<usize>(cx);
12548 let buffer = self.buffer.read(cx);
12549 let mut new_selections_by_buffer = HashMap::default();
12550 for selection in selections {
12551 for (buffer, range, _) in
12552 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
12553 {
12554 let mut range = range.to_point(buffer.read(cx));
12555 range.start.column = 0;
12556 range.end.column = buffer.read(cx).line_len(range.end.row);
12557 new_selections_by_buffer
12558 .entry(buffer)
12559 .or_insert(Vec::new())
12560 .push(range)
12561 }
12562 }
12563
12564 let proposed_changes_buffers = new_selections_by_buffer
12565 .into_iter()
12566 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
12567 .collect::<Vec<_>>();
12568 let proposed_changes_editor = cx.new_view(|cx| {
12569 ProposedChangesEditor::new(
12570 "Proposed changes",
12571 proposed_changes_buffers,
12572 self.project.clone(),
12573 cx,
12574 )
12575 });
12576
12577 cx.window_context().defer(move |cx| {
12578 workspace.update(cx, |workspace, cx| {
12579 workspace.active_pane().update(cx, |pane, cx| {
12580 pane.add_item(Box::new(proposed_changes_editor), true, true, None, cx);
12581 });
12582 });
12583 });
12584 }
12585
12586 pub fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext<Self>) {
12587 self.open_excerpts_common(true, cx)
12588 }
12589
12590 pub fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
12591 self.open_excerpts_common(false, cx)
12592 }
12593
12594 fn open_excerpts_common(&mut self, split: bool, cx: &mut ViewContext<Self>) {
12595 let selections = self.selections.all::<usize>(cx);
12596 let buffer = self.buffer.read(cx);
12597 if buffer.is_singleton() {
12598 cx.propagate();
12599 return;
12600 }
12601
12602 let Some(workspace) = self.workspace() else {
12603 cx.propagate();
12604 return;
12605 };
12606
12607 let mut new_selections_by_buffer = HashMap::default();
12608 for selection in selections {
12609 for (mut buffer_handle, mut range, _) in
12610 buffer.range_to_buffer_ranges(selection.range(), cx)
12611 {
12612 // When editing branch buffers, jump to the corresponding location
12613 // in their base buffer.
12614 let buffer = buffer_handle.read(cx);
12615 if let Some(base_buffer) = buffer.diff_base_buffer() {
12616 range = buffer.range_to_version(range, &base_buffer.read(cx).version());
12617 buffer_handle = base_buffer;
12618 }
12619
12620 if selection.reversed {
12621 mem::swap(&mut range.start, &mut range.end);
12622 }
12623 new_selections_by_buffer
12624 .entry(buffer_handle)
12625 .or_insert(Vec::new())
12626 .push(range)
12627 }
12628 }
12629
12630 // We defer the pane interaction because we ourselves are a workspace item
12631 // and activating a new item causes the pane to call a method on us reentrantly,
12632 // which panics if we're on the stack.
12633 cx.window_context().defer(move |cx| {
12634 workspace.update(cx, |workspace, cx| {
12635 let pane = if split {
12636 workspace.adjacent_pane(cx)
12637 } else {
12638 workspace.active_pane().clone()
12639 };
12640
12641 for (buffer, ranges) in new_selections_by_buffer {
12642 let editor =
12643 workspace.open_project_item::<Self>(pane.clone(), buffer, true, true, cx);
12644 editor.update(cx, |editor, cx| {
12645 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
12646 s.select_ranges(ranges);
12647 });
12648 });
12649 }
12650 })
12651 });
12652 }
12653
12654 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
12655 let snapshot = self.buffer.read(cx).read(cx);
12656 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
12657 Some(
12658 ranges
12659 .iter()
12660 .map(move |range| {
12661 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
12662 })
12663 .collect(),
12664 )
12665 }
12666
12667 fn selection_replacement_ranges(
12668 &self,
12669 range: Range<OffsetUtf16>,
12670 cx: &mut AppContext,
12671 ) -> Vec<Range<OffsetUtf16>> {
12672 let selections = self.selections.all::<OffsetUtf16>(cx);
12673 let newest_selection = selections
12674 .iter()
12675 .max_by_key(|selection| selection.id)
12676 .unwrap();
12677 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
12678 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
12679 let snapshot = self.buffer.read(cx).read(cx);
12680 selections
12681 .into_iter()
12682 .map(|mut selection| {
12683 selection.start.0 =
12684 (selection.start.0 as isize).saturating_add(start_delta) as usize;
12685 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
12686 snapshot.clip_offset_utf16(selection.start, Bias::Left)
12687 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
12688 })
12689 .collect()
12690 }
12691
12692 fn report_editor_event(
12693 &self,
12694 operation: &'static str,
12695 file_extension: Option<String>,
12696 cx: &AppContext,
12697 ) {
12698 if cfg!(any(test, feature = "test-support")) {
12699 return;
12700 }
12701
12702 let Some(project) = &self.project else { return };
12703
12704 // If None, we are in a file without an extension
12705 let file = self
12706 .buffer
12707 .read(cx)
12708 .as_singleton()
12709 .and_then(|b| b.read(cx).file());
12710 let file_extension = file_extension.or(file
12711 .as_ref()
12712 .and_then(|file| Path::new(file.file_name(cx)).extension())
12713 .and_then(|e| e.to_str())
12714 .map(|a| a.to_string()));
12715
12716 let vim_mode = cx
12717 .global::<SettingsStore>()
12718 .raw_user_settings()
12719 .get("vim_mode")
12720 == Some(&serde_json::Value::Bool(true));
12721
12722 let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
12723 == language::language_settings::InlineCompletionProvider::Copilot;
12724 let copilot_enabled_for_language = self
12725 .buffer
12726 .read(cx)
12727 .settings_at(0, cx)
12728 .show_inline_completions;
12729
12730 let project = project.read(cx);
12731 let telemetry = project.client().telemetry().clone();
12732 telemetry.report_editor_event(
12733 file_extension,
12734 vim_mode,
12735 operation,
12736 copilot_enabled,
12737 copilot_enabled_for_language,
12738 project.is_via_ssh(),
12739 )
12740 }
12741
12742 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
12743 /// with each line being an array of {text, highlight} objects.
12744 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
12745 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
12746 return;
12747 };
12748
12749 #[derive(Serialize)]
12750 struct Chunk<'a> {
12751 text: String,
12752 highlight: Option<&'a str>,
12753 }
12754
12755 let snapshot = buffer.read(cx).snapshot();
12756 let range = self
12757 .selected_text_range(false, cx)
12758 .and_then(|selection| {
12759 if selection.range.is_empty() {
12760 None
12761 } else {
12762 Some(selection.range)
12763 }
12764 })
12765 .unwrap_or_else(|| 0..snapshot.len());
12766
12767 let chunks = snapshot.chunks(range, true);
12768 let mut lines = Vec::new();
12769 let mut line: VecDeque<Chunk> = VecDeque::new();
12770
12771 let Some(style) = self.style.as_ref() else {
12772 return;
12773 };
12774
12775 for chunk in chunks {
12776 let highlight = chunk
12777 .syntax_highlight_id
12778 .and_then(|id| id.name(&style.syntax));
12779 let mut chunk_lines = chunk.text.split('\n').peekable();
12780 while let Some(text) = chunk_lines.next() {
12781 let mut merged_with_last_token = false;
12782 if let Some(last_token) = line.back_mut() {
12783 if last_token.highlight == highlight {
12784 last_token.text.push_str(text);
12785 merged_with_last_token = true;
12786 }
12787 }
12788
12789 if !merged_with_last_token {
12790 line.push_back(Chunk {
12791 text: text.into(),
12792 highlight,
12793 });
12794 }
12795
12796 if chunk_lines.peek().is_some() {
12797 if line.len() > 1 && line.front().unwrap().text.is_empty() {
12798 line.pop_front();
12799 }
12800 if line.len() > 1 && line.back().unwrap().text.is_empty() {
12801 line.pop_back();
12802 }
12803
12804 lines.push(mem::take(&mut line));
12805 }
12806 }
12807 }
12808
12809 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
12810 return;
12811 };
12812 cx.write_to_clipboard(ClipboardItem::new_string(lines));
12813 }
12814
12815 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
12816 &self.inlay_hint_cache
12817 }
12818
12819 pub fn replay_insert_event(
12820 &mut self,
12821 text: &str,
12822 relative_utf16_range: Option<Range<isize>>,
12823 cx: &mut ViewContext<Self>,
12824 ) {
12825 if !self.input_enabled {
12826 cx.emit(EditorEvent::InputIgnored { text: text.into() });
12827 return;
12828 }
12829 if let Some(relative_utf16_range) = relative_utf16_range {
12830 let selections = self.selections.all::<OffsetUtf16>(cx);
12831 self.change_selections(None, cx, |s| {
12832 let new_ranges = selections.into_iter().map(|range| {
12833 let start = OffsetUtf16(
12834 range
12835 .head()
12836 .0
12837 .saturating_add_signed(relative_utf16_range.start),
12838 );
12839 let end = OffsetUtf16(
12840 range
12841 .head()
12842 .0
12843 .saturating_add_signed(relative_utf16_range.end),
12844 );
12845 start..end
12846 });
12847 s.select_ranges(new_ranges);
12848 });
12849 }
12850
12851 self.handle_input(text, cx);
12852 }
12853
12854 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
12855 let Some(provider) = self.semantics_provider.as_ref() else {
12856 return false;
12857 };
12858
12859 let mut supports = false;
12860 self.buffer().read(cx).for_each_buffer(|buffer| {
12861 supports |= provider.supports_inlay_hints(buffer, cx);
12862 });
12863 supports
12864 }
12865
12866 pub fn focus(&self, cx: &mut WindowContext) {
12867 cx.focus(&self.focus_handle)
12868 }
12869
12870 pub fn is_focused(&self, cx: &WindowContext) -> bool {
12871 self.focus_handle.is_focused(cx)
12872 }
12873
12874 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
12875 cx.emit(EditorEvent::Focused);
12876
12877 if let Some(descendant) = self
12878 .last_focused_descendant
12879 .take()
12880 .and_then(|descendant| descendant.upgrade())
12881 {
12882 cx.focus(&descendant);
12883 } else {
12884 if let Some(blame) = self.blame.as_ref() {
12885 blame.update(cx, GitBlame::focus)
12886 }
12887
12888 self.blink_manager.update(cx, BlinkManager::enable);
12889 self.show_cursor_names(cx);
12890 self.buffer.update(cx, |buffer, cx| {
12891 buffer.finalize_last_transaction(cx);
12892 if self.leader_peer_id.is_none() {
12893 buffer.set_active_selections(
12894 &self.selections.disjoint_anchors(),
12895 self.selections.line_mode,
12896 self.cursor_shape,
12897 cx,
12898 );
12899 }
12900 });
12901 }
12902 }
12903
12904 fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
12905 cx.emit(EditorEvent::FocusedIn)
12906 }
12907
12908 fn handle_focus_out(&mut self, event: FocusOutEvent, _cx: &mut ViewContext<Self>) {
12909 if event.blurred != self.focus_handle {
12910 self.last_focused_descendant = Some(event.blurred);
12911 }
12912 }
12913
12914 pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
12915 self.blink_manager.update(cx, BlinkManager::disable);
12916 self.buffer
12917 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
12918
12919 if let Some(blame) = self.blame.as_ref() {
12920 blame.update(cx, GitBlame::blur)
12921 }
12922 if !self.hover_state.focused(cx) {
12923 hide_hover(self, cx);
12924 }
12925
12926 self.hide_context_menu(cx);
12927 cx.emit(EditorEvent::Blurred);
12928 cx.notify();
12929 }
12930
12931 pub fn register_action<A: Action>(
12932 &mut self,
12933 listener: impl Fn(&A, &mut WindowContext) + 'static,
12934 ) -> Subscription {
12935 let id = self.next_editor_action_id.post_inc();
12936 let listener = Arc::new(listener);
12937 self.editor_actions.borrow_mut().insert(
12938 id,
12939 Box::new(move |cx| {
12940 let cx = cx.window_context();
12941 let listener = listener.clone();
12942 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
12943 let action = action.downcast_ref().unwrap();
12944 if phase == DispatchPhase::Bubble {
12945 listener(action, cx)
12946 }
12947 })
12948 }),
12949 );
12950
12951 let editor_actions = self.editor_actions.clone();
12952 Subscription::new(move || {
12953 editor_actions.borrow_mut().remove(&id);
12954 })
12955 }
12956
12957 pub fn file_header_size(&self) -> u32 {
12958 FILE_HEADER_HEIGHT
12959 }
12960
12961 pub fn revert(
12962 &mut self,
12963 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
12964 cx: &mut ViewContext<Self>,
12965 ) {
12966 self.buffer().update(cx, |multi_buffer, cx| {
12967 for (buffer_id, changes) in revert_changes {
12968 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
12969 buffer.update(cx, |buffer, cx| {
12970 buffer.edit(
12971 changes.into_iter().map(|(range, text)| {
12972 (range, text.to_string().map(Arc::<str>::from))
12973 }),
12974 None,
12975 cx,
12976 );
12977 });
12978 }
12979 }
12980 });
12981 self.change_selections(None, cx, |selections| selections.refresh());
12982 }
12983
12984 pub fn to_pixel_point(
12985 &mut self,
12986 source: multi_buffer::Anchor,
12987 editor_snapshot: &EditorSnapshot,
12988 cx: &mut ViewContext<Self>,
12989 ) -> Option<gpui::Point<Pixels>> {
12990 let source_point = source.to_display_point(editor_snapshot);
12991 self.display_to_pixel_point(source_point, editor_snapshot, cx)
12992 }
12993
12994 pub fn display_to_pixel_point(
12995 &mut self,
12996 source: DisplayPoint,
12997 editor_snapshot: &EditorSnapshot,
12998 cx: &mut ViewContext<Self>,
12999 ) -> Option<gpui::Point<Pixels>> {
13000 let line_height = self.style()?.text.line_height_in_pixels(cx.rem_size());
13001 let text_layout_details = self.text_layout_details(cx);
13002 let scroll_top = text_layout_details
13003 .scroll_anchor
13004 .scroll_position(editor_snapshot)
13005 .y;
13006
13007 if source.row().as_f32() < scroll_top.floor() {
13008 return None;
13009 }
13010 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
13011 let source_y = line_height * (source.row().as_f32() - scroll_top);
13012 Some(gpui::Point::new(source_x, source_y))
13013 }
13014
13015 pub fn has_active_completions_menu(&self) -> bool {
13016 self.context_menu.read().as_ref().map_or(false, |menu| {
13017 menu.visible() && matches!(menu, ContextMenu::Completions(_))
13018 })
13019 }
13020
13021 pub fn register_addon<T: Addon>(&mut self, instance: T) {
13022 self.addons
13023 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
13024 }
13025
13026 pub fn unregister_addon<T: Addon>(&mut self) {
13027 self.addons.remove(&std::any::TypeId::of::<T>());
13028 }
13029
13030 pub fn addon<T: Addon>(&self) -> Option<&T> {
13031 let type_id = std::any::TypeId::of::<T>();
13032 self.addons
13033 .get(&type_id)
13034 .and_then(|item| item.to_any().downcast_ref::<T>())
13035 }
13036}
13037
13038fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
13039 let tab_size = tab_size.get() as usize;
13040 let mut width = offset;
13041
13042 for ch in text.chars() {
13043 width += if ch == '\t' {
13044 tab_size - (width % tab_size)
13045 } else {
13046 1
13047 };
13048 }
13049
13050 width - offset
13051}
13052
13053#[cfg(test)]
13054mod tests {
13055 use super::*;
13056
13057 #[test]
13058 fn test_string_size_with_expanded_tabs() {
13059 let nz = |val| NonZeroU32::new(val).unwrap();
13060 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
13061 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
13062 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
13063 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
13064 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
13065 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
13066 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
13067 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
13068 }
13069}
13070
13071/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
13072struct WordBreakingTokenizer<'a> {
13073 input: &'a str,
13074}
13075
13076impl<'a> WordBreakingTokenizer<'a> {
13077 fn new(input: &'a str) -> Self {
13078 Self { input }
13079 }
13080}
13081
13082fn is_char_ideographic(ch: char) -> bool {
13083 use unicode_script::Script::*;
13084 use unicode_script::UnicodeScript;
13085 matches!(ch.script(), Han | Tangut | Yi)
13086}
13087
13088fn is_grapheme_ideographic(text: &str) -> bool {
13089 text.chars().any(is_char_ideographic)
13090}
13091
13092fn is_grapheme_whitespace(text: &str) -> bool {
13093 text.chars().any(|x| x.is_whitespace())
13094}
13095
13096fn should_stay_with_preceding_ideograph(text: &str) -> bool {
13097 text.chars().next().map_or(false, |ch| {
13098 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
13099 })
13100}
13101
13102#[derive(PartialEq, Eq, Debug, Clone, Copy)]
13103struct WordBreakToken<'a> {
13104 token: &'a str,
13105 grapheme_len: usize,
13106 is_whitespace: bool,
13107}
13108
13109impl<'a> Iterator for WordBreakingTokenizer<'a> {
13110 /// Yields a span, the count of graphemes in the token, and whether it was
13111 /// whitespace. Note that it also breaks at word boundaries.
13112 type Item = WordBreakToken<'a>;
13113
13114 fn next(&mut self) -> Option<Self::Item> {
13115 use unicode_segmentation::UnicodeSegmentation;
13116 if self.input.is_empty() {
13117 return None;
13118 }
13119
13120 let mut iter = self.input.graphemes(true).peekable();
13121 let mut offset = 0;
13122 let mut graphemes = 0;
13123 if let Some(first_grapheme) = iter.next() {
13124 let is_whitespace = is_grapheme_whitespace(first_grapheme);
13125 offset += first_grapheme.len();
13126 graphemes += 1;
13127 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
13128 if let Some(grapheme) = iter.peek().copied() {
13129 if should_stay_with_preceding_ideograph(grapheme) {
13130 offset += grapheme.len();
13131 graphemes += 1;
13132 }
13133 }
13134 } else {
13135 let mut words = self.input[offset..].split_word_bound_indices().peekable();
13136 let mut next_word_bound = words.peek().copied();
13137 if next_word_bound.map_or(false, |(i, _)| i == 0) {
13138 next_word_bound = words.next();
13139 }
13140 while let Some(grapheme) = iter.peek().copied() {
13141 if next_word_bound.map_or(false, |(i, _)| i == offset) {
13142 break;
13143 };
13144 if is_grapheme_whitespace(grapheme) != is_whitespace {
13145 break;
13146 };
13147 offset += grapheme.len();
13148 graphemes += 1;
13149 iter.next();
13150 }
13151 }
13152 let token = &self.input[..offset];
13153 self.input = &self.input[offset..];
13154 if is_whitespace {
13155 Some(WordBreakToken {
13156 token: " ",
13157 grapheme_len: 1,
13158 is_whitespace: true,
13159 })
13160 } else {
13161 Some(WordBreakToken {
13162 token,
13163 grapheme_len: graphemes,
13164 is_whitespace: false,
13165 })
13166 }
13167 } else {
13168 None
13169 }
13170 }
13171}
13172
13173#[test]
13174fn test_word_breaking_tokenizer() {
13175 let tests: &[(&str, &[(&str, usize, bool)])] = &[
13176 ("", &[]),
13177 (" ", &[(" ", 1, true)]),
13178 ("Ʒ", &[("Ʒ", 1, false)]),
13179 ("Ǽ", &[("Ǽ", 1, false)]),
13180 ("⋑", &[("⋑", 1, false)]),
13181 ("⋑⋑", &[("⋑⋑", 2, false)]),
13182 (
13183 "原理,进而",
13184 &[
13185 ("原", 1, false),
13186 ("理,", 2, false),
13187 ("进", 1, false),
13188 ("而", 1, false),
13189 ],
13190 ),
13191 (
13192 "hello world",
13193 &[("hello", 5, false), (" ", 1, true), ("world", 5, false)],
13194 ),
13195 (
13196 "hello, world",
13197 &[("hello,", 6, false), (" ", 1, true), ("world", 5, false)],
13198 ),
13199 (
13200 " hello world",
13201 &[
13202 (" ", 1, true),
13203 ("hello", 5, false),
13204 (" ", 1, true),
13205 ("world", 5, false),
13206 ],
13207 ),
13208 (
13209 "这是什么 \n 钢笔",
13210 &[
13211 ("这", 1, false),
13212 ("是", 1, false),
13213 ("什", 1, false),
13214 ("么", 1, false),
13215 (" ", 1, true),
13216 ("钢", 1, false),
13217 ("笔", 1, false),
13218 ],
13219 ),
13220 (" mutton", &[(" ", 1, true), ("mutton", 6, false)]),
13221 ];
13222
13223 for (input, result) in tests {
13224 assert_eq!(
13225 WordBreakingTokenizer::new(input).collect::<Vec<_>>(),
13226 result
13227 .iter()
13228 .copied()
13229 .map(|(token, grapheme_len, is_whitespace)| WordBreakToken {
13230 token,
13231 grapheme_len,
13232 is_whitespace,
13233 })
13234 .collect::<Vec<_>>()
13235 );
13236 }
13237}
13238
13239fn wrap_with_prefix(
13240 line_prefix: String,
13241 unwrapped_text: String,
13242 wrap_column: usize,
13243 tab_size: NonZeroU32,
13244) -> String {
13245 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
13246 let mut wrapped_text = String::new();
13247 let mut current_line = line_prefix.clone();
13248
13249 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
13250 let mut current_line_len = line_prefix_len;
13251 for WordBreakToken {
13252 token,
13253 grapheme_len,
13254 is_whitespace,
13255 } in tokenizer
13256 {
13257 if current_line_len + grapheme_len > wrap_column && current_line_len != line_prefix_len {
13258 wrapped_text.push_str(current_line.trim_end());
13259 wrapped_text.push('\n');
13260 current_line.truncate(line_prefix.len());
13261 current_line_len = line_prefix_len;
13262 if !is_whitespace {
13263 current_line.push_str(token);
13264 current_line_len += grapheme_len;
13265 }
13266 } else if !is_whitespace {
13267 current_line.push_str(token);
13268 current_line_len += grapheme_len;
13269 } else if current_line_len != line_prefix_len {
13270 current_line.push(' ');
13271 current_line_len += 1;
13272 }
13273 }
13274
13275 if !current_line.is_empty() {
13276 wrapped_text.push_str(¤t_line);
13277 }
13278 wrapped_text
13279}
13280
13281#[test]
13282fn test_wrap_with_prefix() {
13283 assert_eq!(
13284 wrap_with_prefix(
13285 "# ".to_string(),
13286 "abcdefg".to_string(),
13287 4,
13288 NonZeroU32::new(4).unwrap()
13289 ),
13290 "# abcdefg"
13291 );
13292 assert_eq!(
13293 wrap_with_prefix(
13294 "".to_string(),
13295 "\thello world".to_string(),
13296 8,
13297 NonZeroU32::new(4).unwrap()
13298 ),
13299 "hello\nworld"
13300 );
13301 assert_eq!(
13302 wrap_with_prefix(
13303 "// ".to_string(),
13304 "xx \nyy zz aa bb cc".to_string(),
13305 12,
13306 NonZeroU32::new(4).unwrap()
13307 ),
13308 "// xx yy zz\n// aa bb cc"
13309 );
13310 assert_eq!(
13311 wrap_with_prefix(
13312 String::new(),
13313 "这是什么 \n 钢笔".to_string(),
13314 3,
13315 NonZeroU32::new(4).unwrap()
13316 ),
13317 "这是什\n么 钢\n笔"
13318 );
13319}
13320
13321fn hunks_for_selections(
13322 multi_buffer_snapshot: &MultiBufferSnapshot,
13323 selections: &[Selection<Anchor>],
13324) -> Vec<MultiBufferDiffHunk> {
13325 let buffer_rows_for_selections = selections.iter().map(|selection| {
13326 let head = selection.head();
13327 let tail = selection.tail();
13328 let start = MultiBufferRow(tail.to_point(multi_buffer_snapshot).row);
13329 let end = MultiBufferRow(head.to_point(multi_buffer_snapshot).row);
13330 if start > end {
13331 end..start
13332 } else {
13333 start..end
13334 }
13335 });
13336
13337 hunks_for_rows(buffer_rows_for_selections, multi_buffer_snapshot)
13338}
13339
13340pub fn hunks_for_rows(
13341 rows: impl Iterator<Item = Range<MultiBufferRow>>,
13342 multi_buffer_snapshot: &MultiBufferSnapshot,
13343) -> Vec<MultiBufferDiffHunk> {
13344 let mut hunks = Vec::new();
13345 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
13346 HashMap::default();
13347 for selected_multi_buffer_rows in rows {
13348 let query_rows =
13349 selected_multi_buffer_rows.start..selected_multi_buffer_rows.end.next_row();
13350 for hunk in multi_buffer_snapshot.git_diff_hunks_in_range(query_rows.clone()) {
13351 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
13352 // when the caret is just above or just below the deleted hunk.
13353 let allow_adjacent = hunk_status(&hunk) == DiffHunkStatus::Removed;
13354 let related_to_selection = if allow_adjacent {
13355 hunk.row_range.overlaps(&query_rows)
13356 || hunk.row_range.start == query_rows.end
13357 || hunk.row_range.end == query_rows.start
13358 } else {
13359 // `selected_multi_buffer_rows` are inclusive (e.g. [2..2] means 2nd row is selected)
13360 // `hunk.row_range` is exclusive (e.g. [2..3] means 2nd row is selected)
13361 hunk.row_range.overlaps(&selected_multi_buffer_rows)
13362 || selected_multi_buffer_rows.end == hunk.row_range.start
13363 };
13364 if related_to_selection {
13365 if !processed_buffer_rows
13366 .entry(hunk.buffer_id)
13367 .or_default()
13368 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
13369 {
13370 continue;
13371 }
13372 hunks.push(hunk);
13373 }
13374 }
13375 }
13376
13377 hunks
13378}
13379
13380pub trait CollaborationHub {
13381 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
13382 fn user_participant_indices<'a>(
13383 &self,
13384 cx: &'a AppContext,
13385 ) -> &'a HashMap<u64, ParticipantIndex>;
13386 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
13387}
13388
13389impl CollaborationHub for Model<Project> {
13390 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
13391 self.read(cx).collaborators()
13392 }
13393
13394 fn user_participant_indices<'a>(
13395 &self,
13396 cx: &'a AppContext,
13397 ) -> &'a HashMap<u64, ParticipantIndex> {
13398 self.read(cx).user_store().read(cx).participant_indices()
13399 }
13400
13401 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
13402 let this = self.read(cx);
13403 let user_ids = this.collaborators().values().map(|c| c.user_id);
13404 this.user_store().read_with(cx, |user_store, cx| {
13405 user_store.participant_names(user_ids, cx)
13406 })
13407 }
13408}
13409
13410pub trait SemanticsProvider {
13411 fn hover(
13412 &self,
13413 buffer: &Model<Buffer>,
13414 position: text::Anchor,
13415 cx: &mut AppContext,
13416 ) -> Option<Task<Vec<project::Hover>>>;
13417
13418 fn inlay_hints(
13419 &self,
13420 buffer_handle: Model<Buffer>,
13421 range: Range<text::Anchor>,
13422 cx: &mut AppContext,
13423 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
13424
13425 fn resolve_inlay_hint(
13426 &self,
13427 hint: InlayHint,
13428 buffer_handle: Model<Buffer>,
13429 server_id: LanguageServerId,
13430 cx: &mut AppContext,
13431 ) -> Option<Task<anyhow::Result<InlayHint>>>;
13432
13433 fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool;
13434
13435 fn document_highlights(
13436 &self,
13437 buffer: &Model<Buffer>,
13438 position: text::Anchor,
13439 cx: &mut AppContext,
13440 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
13441
13442 fn definitions(
13443 &self,
13444 buffer: &Model<Buffer>,
13445 position: text::Anchor,
13446 kind: GotoDefinitionKind,
13447 cx: &mut AppContext,
13448 ) -> Option<Task<Result<Vec<LocationLink>>>>;
13449
13450 fn range_for_rename(
13451 &self,
13452 buffer: &Model<Buffer>,
13453 position: text::Anchor,
13454 cx: &mut AppContext,
13455 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
13456
13457 fn perform_rename(
13458 &self,
13459 buffer: &Model<Buffer>,
13460 position: text::Anchor,
13461 new_name: String,
13462 cx: &mut AppContext,
13463 ) -> Option<Task<Result<ProjectTransaction>>>;
13464}
13465
13466pub trait CompletionProvider {
13467 fn completions(
13468 &self,
13469 buffer: &Model<Buffer>,
13470 buffer_position: text::Anchor,
13471 trigger: CompletionContext,
13472 cx: &mut ViewContext<Editor>,
13473 ) -> Task<Result<Vec<Completion>>>;
13474
13475 fn resolve_completions(
13476 &self,
13477 buffer: Model<Buffer>,
13478 completion_indices: Vec<usize>,
13479 completions: Arc<RwLock<Box<[Completion]>>>,
13480 cx: &mut ViewContext<Editor>,
13481 ) -> Task<Result<bool>>;
13482
13483 fn apply_additional_edits_for_completion(
13484 &self,
13485 buffer: Model<Buffer>,
13486 completion: Completion,
13487 push_to_history: bool,
13488 cx: &mut ViewContext<Editor>,
13489 ) -> Task<Result<Option<language::Transaction>>>;
13490
13491 fn is_completion_trigger(
13492 &self,
13493 buffer: &Model<Buffer>,
13494 position: language::Anchor,
13495 text: &str,
13496 trigger_in_words: bool,
13497 cx: &mut ViewContext<Editor>,
13498 ) -> bool;
13499
13500 fn sort_completions(&self) -> bool {
13501 true
13502 }
13503}
13504
13505pub trait CodeActionProvider {
13506 fn code_actions(
13507 &self,
13508 buffer: &Model<Buffer>,
13509 range: Range<text::Anchor>,
13510 cx: &mut WindowContext,
13511 ) -> Task<Result<Vec<CodeAction>>>;
13512
13513 fn apply_code_action(
13514 &self,
13515 buffer_handle: Model<Buffer>,
13516 action: CodeAction,
13517 excerpt_id: ExcerptId,
13518 push_to_history: bool,
13519 cx: &mut WindowContext,
13520 ) -> Task<Result<ProjectTransaction>>;
13521}
13522
13523impl CodeActionProvider for Model<Project> {
13524 fn code_actions(
13525 &self,
13526 buffer: &Model<Buffer>,
13527 range: Range<text::Anchor>,
13528 cx: &mut WindowContext,
13529 ) -> Task<Result<Vec<CodeAction>>> {
13530 self.update(cx, |project, cx| project.code_actions(buffer, range, cx))
13531 }
13532
13533 fn apply_code_action(
13534 &self,
13535 buffer_handle: Model<Buffer>,
13536 action: CodeAction,
13537 _excerpt_id: ExcerptId,
13538 push_to_history: bool,
13539 cx: &mut WindowContext,
13540 ) -> Task<Result<ProjectTransaction>> {
13541 self.update(cx, |project, cx| {
13542 project.apply_code_action(buffer_handle, action, push_to_history, cx)
13543 })
13544 }
13545}
13546
13547fn snippet_completions(
13548 project: &Project,
13549 buffer: &Model<Buffer>,
13550 buffer_position: text::Anchor,
13551 cx: &mut AppContext,
13552) -> Vec<Completion> {
13553 let language = buffer.read(cx).language_at(buffer_position);
13554 let language_name = language.as_ref().map(|language| language.lsp_id());
13555 let snippet_store = project.snippets().read(cx);
13556 let snippets = snippet_store.snippets_for(language_name, cx);
13557
13558 if snippets.is_empty() {
13559 return vec![];
13560 }
13561 let snapshot = buffer.read(cx).text_snapshot();
13562 let chars = snapshot.reversed_chars_for_range(text::Anchor::MIN..buffer_position);
13563
13564 let scope = language.map(|language| language.default_scope());
13565 let classifier = CharClassifier::new(scope).for_completion(true);
13566 let mut last_word = chars
13567 .take_while(|c| classifier.is_word(*c))
13568 .collect::<String>();
13569 last_word = last_word.chars().rev().collect();
13570 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
13571 let to_lsp = |point: &text::Anchor| {
13572 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
13573 point_to_lsp(end)
13574 };
13575 let lsp_end = to_lsp(&buffer_position);
13576 snippets
13577 .into_iter()
13578 .filter_map(|snippet| {
13579 let matching_prefix = snippet
13580 .prefix
13581 .iter()
13582 .find(|prefix| prefix.starts_with(&last_word))?;
13583 let start = as_offset - last_word.len();
13584 let start = snapshot.anchor_before(start);
13585 let range = start..buffer_position;
13586 let lsp_start = to_lsp(&start);
13587 let lsp_range = lsp::Range {
13588 start: lsp_start,
13589 end: lsp_end,
13590 };
13591 Some(Completion {
13592 old_range: range,
13593 new_text: snippet.body.clone(),
13594 label: CodeLabel {
13595 text: matching_prefix.clone(),
13596 runs: vec![],
13597 filter_range: 0..matching_prefix.len(),
13598 },
13599 server_id: LanguageServerId(usize::MAX),
13600 documentation: snippet.description.clone().map(Documentation::SingleLine),
13601 lsp_completion: lsp::CompletionItem {
13602 label: snippet.prefix.first().unwrap().clone(),
13603 kind: Some(CompletionItemKind::SNIPPET),
13604 label_details: snippet.description.as_ref().map(|description| {
13605 lsp::CompletionItemLabelDetails {
13606 detail: Some(description.clone()),
13607 description: None,
13608 }
13609 }),
13610 insert_text_format: Some(InsertTextFormat::SNIPPET),
13611 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13612 lsp::InsertReplaceEdit {
13613 new_text: snippet.body.clone(),
13614 insert: lsp_range,
13615 replace: lsp_range,
13616 },
13617 )),
13618 filter_text: Some(snippet.body.clone()),
13619 sort_text: Some(char::MAX.to_string()),
13620 ..Default::default()
13621 },
13622 confirm: None,
13623 })
13624 })
13625 .collect()
13626}
13627
13628impl CompletionProvider for Model<Project> {
13629 fn completions(
13630 &self,
13631 buffer: &Model<Buffer>,
13632 buffer_position: text::Anchor,
13633 options: CompletionContext,
13634 cx: &mut ViewContext<Editor>,
13635 ) -> Task<Result<Vec<Completion>>> {
13636 self.update(cx, |project, cx| {
13637 let snippets = snippet_completions(project, buffer, buffer_position, cx);
13638 let project_completions = project.completions(buffer, buffer_position, options, cx);
13639 cx.background_executor().spawn(async move {
13640 let mut completions = project_completions.await?;
13641 //let snippets = snippets.into_iter().;
13642 completions.extend(snippets);
13643 Ok(completions)
13644 })
13645 })
13646 }
13647
13648 fn resolve_completions(
13649 &self,
13650 buffer: Model<Buffer>,
13651 completion_indices: Vec<usize>,
13652 completions: Arc<RwLock<Box<[Completion]>>>,
13653 cx: &mut ViewContext<Editor>,
13654 ) -> Task<Result<bool>> {
13655 self.update(cx, |project, cx| {
13656 project.resolve_completions(buffer, completion_indices, completions, cx)
13657 })
13658 }
13659
13660 fn apply_additional_edits_for_completion(
13661 &self,
13662 buffer: Model<Buffer>,
13663 completion: Completion,
13664 push_to_history: bool,
13665 cx: &mut ViewContext<Editor>,
13666 ) -> Task<Result<Option<language::Transaction>>> {
13667 self.update(cx, |project, cx| {
13668 project.apply_additional_edits_for_completion(buffer, completion, push_to_history, cx)
13669 })
13670 }
13671
13672 fn is_completion_trigger(
13673 &self,
13674 buffer: &Model<Buffer>,
13675 position: language::Anchor,
13676 text: &str,
13677 trigger_in_words: bool,
13678 cx: &mut ViewContext<Editor>,
13679 ) -> bool {
13680 if !EditorSettings::get_global(cx).show_completions_on_input {
13681 return false;
13682 }
13683
13684 let mut chars = text.chars();
13685 let char = if let Some(char) = chars.next() {
13686 char
13687 } else {
13688 return false;
13689 };
13690 if chars.next().is_some() {
13691 return false;
13692 }
13693
13694 let buffer = buffer.read(cx);
13695 let classifier = buffer
13696 .snapshot()
13697 .char_classifier_at(position)
13698 .for_completion(true);
13699 if trigger_in_words && classifier.is_word(char) {
13700 return true;
13701 }
13702
13703 buffer.completion_triggers().contains(text)
13704 }
13705}
13706
13707impl SemanticsProvider for Model<Project> {
13708 fn hover(
13709 &self,
13710 buffer: &Model<Buffer>,
13711 position: text::Anchor,
13712 cx: &mut AppContext,
13713 ) -> Option<Task<Vec<project::Hover>>> {
13714 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
13715 }
13716
13717 fn document_highlights(
13718 &self,
13719 buffer: &Model<Buffer>,
13720 position: text::Anchor,
13721 cx: &mut AppContext,
13722 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
13723 Some(self.update(cx, |project, cx| {
13724 project.document_highlights(buffer, position, cx)
13725 }))
13726 }
13727
13728 fn definitions(
13729 &self,
13730 buffer: &Model<Buffer>,
13731 position: text::Anchor,
13732 kind: GotoDefinitionKind,
13733 cx: &mut AppContext,
13734 ) -> Option<Task<Result<Vec<LocationLink>>>> {
13735 Some(self.update(cx, |project, cx| match kind {
13736 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
13737 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
13738 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
13739 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
13740 }))
13741 }
13742
13743 fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool {
13744 // TODO: make this work for remote projects
13745 self.read(cx)
13746 .language_servers_for_buffer(buffer.read(cx), cx)
13747 .any(
13748 |(_, server)| match server.capabilities().inlay_hint_provider {
13749 Some(lsp::OneOf::Left(enabled)) => enabled,
13750 Some(lsp::OneOf::Right(_)) => true,
13751 None => false,
13752 },
13753 )
13754 }
13755
13756 fn inlay_hints(
13757 &self,
13758 buffer_handle: Model<Buffer>,
13759 range: Range<text::Anchor>,
13760 cx: &mut AppContext,
13761 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
13762 Some(self.update(cx, |project, cx| {
13763 project.inlay_hints(buffer_handle, range, cx)
13764 }))
13765 }
13766
13767 fn resolve_inlay_hint(
13768 &self,
13769 hint: InlayHint,
13770 buffer_handle: Model<Buffer>,
13771 server_id: LanguageServerId,
13772 cx: &mut AppContext,
13773 ) -> Option<Task<anyhow::Result<InlayHint>>> {
13774 Some(self.update(cx, |project, cx| {
13775 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
13776 }))
13777 }
13778
13779 fn range_for_rename(
13780 &self,
13781 buffer: &Model<Buffer>,
13782 position: text::Anchor,
13783 cx: &mut AppContext,
13784 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
13785 Some(self.update(cx, |project, cx| {
13786 project.prepare_rename(buffer.clone(), position, cx)
13787 }))
13788 }
13789
13790 fn perform_rename(
13791 &self,
13792 buffer: &Model<Buffer>,
13793 position: text::Anchor,
13794 new_name: String,
13795 cx: &mut AppContext,
13796 ) -> Option<Task<Result<ProjectTransaction>>> {
13797 Some(self.update(cx, |project, cx| {
13798 project.perform_rename(buffer.clone(), position, new_name, cx)
13799 }))
13800 }
13801}
13802
13803fn inlay_hint_settings(
13804 location: Anchor,
13805 snapshot: &MultiBufferSnapshot,
13806 cx: &mut ViewContext<'_, Editor>,
13807) -> InlayHintSettings {
13808 let file = snapshot.file_at(location);
13809 let language = snapshot.language_at(location).map(|l| l.name());
13810 language_settings(language, file, cx).inlay_hints
13811}
13812
13813fn consume_contiguous_rows(
13814 contiguous_row_selections: &mut Vec<Selection<Point>>,
13815 selection: &Selection<Point>,
13816 display_map: &DisplaySnapshot,
13817 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
13818) -> (MultiBufferRow, MultiBufferRow) {
13819 contiguous_row_selections.push(selection.clone());
13820 let start_row = MultiBufferRow(selection.start.row);
13821 let mut end_row = ending_row(selection, display_map);
13822
13823 while let Some(next_selection) = selections.peek() {
13824 if next_selection.start.row <= end_row.0 {
13825 end_row = ending_row(next_selection, display_map);
13826 contiguous_row_selections.push(selections.next().unwrap().clone());
13827 } else {
13828 break;
13829 }
13830 }
13831 (start_row, end_row)
13832}
13833
13834fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
13835 if next_selection.end.column > 0 || next_selection.is_empty() {
13836 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
13837 } else {
13838 MultiBufferRow(next_selection.end.row)
13839 }
13840}
13841
13842impl EditorSnapshot {
13843 pub fn remote_selections_in_range<'a>(
13844 &'a self,
13845 range: &'a Range<Anchor>,
13846 collaboration_hub: &dyn CollaborationHub,
13847 cx: &'a AppContext,
13848 ) -> impl 'a + Iterator<Item = RemoteSelection> {
13849 let participant_names = collaboration_hub.user_names(cx);
13850 let participant_indices = collaboration_hub.user_participant_indices(cx);
13851 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
13852 let collaborators_by_replica_id = collaborators_by_peer_id
13853 .iter()
13854 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
13855 .collect::<HashMap<_, _>>();
13856 self.buffer_snapshot
13857 .selections_in_range(range, false)
13858 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
13859 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
13860 let participant_index = participant_indices.get(&collaborator.user_id).copied();
13861 let user_name = participant_names.get(&collaborator.user_id).cloned();
13862 Some(RemoteSelection {
13863 replica_id,
13864 selection,
13865 cursor_shape,
13866 line_mode,
13867 participant_index,
13868 peer_id: collaborator.peer_id,
13869 user_name,
13870 })
13871 })
13872 }
13873
13874 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
13875 self.display_snapshot.buffer_snapshot.language_at(position)
13876 }
13877
13878 pub fn is_focused(&self) -> bool {
13879 self.is_focused
13880 }
13881
13882 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
13883 self.placeholder_text.as_ref()
13884 }
13885
13886 pub fn scroll_position(&self) -> gpui::Point<f32> {
13887 self.scroll_anchor.scroll_position(&self.display_snapshot)
13888 }
13889
13890 fn gutter_dimensions(
13891 &self,
13892 font_id: FontId,
13893 font_size: Pixels,
13894 em_width: Pixels,
13895 em_advance: Pixels,
13896 max_line_number_width: Pixels,
13897 cx: &AppContext,
13898 ) -> GutterDimensions {
13899 if !self.show_gutter {
13900 return GutterDimensions::default();
13901 }
13902 let descent = cx.text_system().descent(font_id, font_size);
13903
13904 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
13905 matches!(
13906 ProjectSettings::get_global(cx).git.git_gutter,
13907 Some(GitGutterSetting::TrackedFiles)
13908 )
13909 });
13910 let gutter_settings = EditorSettings::get_global(cx).gutter;
13911 let show_line_numbers = self
13912 .show_line_numbers
13913 .unwrap_or(gutter_settings.line_numbers);
13914 let line_gutter_width = if show_line_numbers {
13915 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
13916 let min_width_for_number_on_gutter = em_advance * 4.0;
13917 max_line_number_width.max(min_width_for_number_on_gutter)
13918 } else {
13919 0.0.into()
13920 };
13921
13922 let show_code_actions = self
13923 .show_code_actions
13924 .unwrap_or(gutter_settings.code_actions);
13925
13926 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
13927
13928 let git_blame_entries_width =
13929 self.git_blame_gutter_max_author_length
13930 .map(|max_author_length| {
13931 // Length of the author name, but also space for the commit hash,
13932 // the spacing and the timestamp.
13933 let max_char_count = max_author_length
13934 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
13935 + 7 // length of commit sha
13936 + 14 // length of max relative timestamp ("60 minutes ago")
13937 + 4; // gaps and margins
13938
13939 em_advance * max_char_count
13940 });
13941
13942 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
13943 left_padding += if show_code_actions || show_runnables {
13944 em_width * 3.0
13945 } else if show_git_gutter && show_line_numbers {
13946 em_width * 2.0
13947 } else if show_git_gutter || show_line_numbers {
13948 em_width
13949 } else {
13950 px(0.)
13951 };
13952
13953 let right_padding = if gutter_settings.folds && show_line_numbers {
13954 em_width * 4.0
13955 } else if gutter_settings.folds {
13956 em_width * 3.0
13957 } else if show_line_numbers {
13958 em_width
13959 } else {
13960 px(0.)
13961 };
13962
13963 GutterDimensions {
13964 left_padding,
13965 right_padding,
13966 width: line_gutter_width + left_padding + right_padding,
13967 margin: -descent,
13968 git_blame_entries_width,
13969 }
13970 }
13971
13972 pub fn render_fold_toggle(
13973 &self,
13974 buffer_row: MultiBufferRow,
13975 row_contains_cursor: bool,
13976 editor: View<Editor>,
13977 cx: &mut WindowContext,
13978 ) -> Option<AnyElement> {
13979 let folded = self.is_line_folded(buffer_row);
13980
13981 if let Some(crease) = self
13982 .crease_snapshot
13983 .query_row(buffer_row, &self.buffer_snapshot)
13984 {
13985 let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
13986 if folded {
13987 editor.update(cx, |editor, cx| {
13988 editor.fold_at(&crate::FoldAt { buffer_row }, cx)
13989 });
13990 } else {
13991 editor.update(cx, |editor, cx| {
13992 editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
13993 });
13994 }
13995 });
13996
13997 Some((crease.render_toggle)(
13998 buffer_row,
13999 folded,
14000 toggle_callback,
14001 cx,
14002 ))
14003 } else if folded
14004 || (self.starts_indent(buffer_row) && (row_contains_cursor || self.gutter_hovered))
14005 {
14006 Some(
14007 Disclosure::new(("indent-fold-indicator", buffer_row.0), !folded)
14008 .selected(folded)
14009 .on_click(cx.listener_for(&editor, move |this, _e, cx| {
14010 if folded {
14011 this.unfold_at(&UnfoldAt { buffer_row }, cx);
14012 } else {
14013 this.fold_at(&FoldAt { buffer_row }, cx);
14014 }
14015 }))
14016 .into_any_element(),
14017 )
14018 } else {
14019 None
14020 }
14021 }
14022
14023 pub fn render_crease_trailer(
14024 &self,
14025 buffer_row: MultiBufferRow,
14026 cx: &mut WindowContext,
14027 ) -> Option<AnyElement> {
14028 let folded = self.is_line_folded(buffer_row);
14029 let crease = self
14030 .crease_snapshot
14031 .query_row(buffer_row, &self.buffer_snapshot)?;
14032 Some((crease.render_trailer)(buffer_row, folded, cx))
14033 }
14034}
14035
14036impl Deref for EditorSnapshot {
14037 type Target = DisplaySnapshot;
14038
14039 fn deref(&self) -> &Self::Target {
14040 &self.display_snapshot
14041 }
14042}
14043
14044#[derive(Clone, Debug, PartialEq, Eq)]
14045pub enum EditorEvent {
14046 InputIgnored {
14047 text: Arc<str>,
14048 },
14049 InputHandled {
14050 utf16_range_to_replace: Option<Range<isize>>,
14051 text: Arc<str>,
14052 },
14053 ExcerptsAdded {
14054 buffer: Model<Buffer>,
14055 predecessor: ExcerptId,
14056 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
14057 },
14058 ExcerptsRemoved {
14059 ids: Vec<ExcerptId>,
14060 },
14061 ExcerptsEdited {
14062 ids: Vec<ExcerptId>,
14063 },
14064 ExcerptsExpanded {
14065 ids: Vec<ExcerptId>,
14066 },
14067 BufferEdited,
14068 Edited {
14069 transaction_id: clock::Lamport,
14070 },
14071 Reparsed(BufferId),
14072 Focused,
14073 FocusedIn,
14074 Blurred,
14075 DirtyChanged,
14076 Saved,
14077 TitleChanged,
14078 DiffBaseChanged,
14079 SelectionsChanged {
14080 local: bool,
14081 },
14082 ScrollPositionChanged {
14083 local: bool,
14084 autoscroll: bool,
14085 },
14086 Closed,
14087 TransactionUndone {
14088 transaction_id: clock::Lamport,
14089 },
14090 TransactionBegun {
14091 transaction_id: clock::Lamport,
14092 },
14093 Reloaded,
14094 CursorShapeChanged,
14095}
14096
14097impl EventEmitter<EditorEvent> for Editor {}
14098
14099impl FocusableView for Editor {
14100 fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
14101 self.focus_handle.clone()
14102 }
14103}
14104
14105impl Render for Editor {
14106 fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
14107 let settings = ThemeSettings::get_global(cx);
14108
14109 let mut text_style = match self.mode {
14110 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
14111 color: cx.theme().colors().editor_foreground,
14112 font_family: settings.ui_font.family.clone(),
14113 font_features: settings.ui_font.features.clone(),
14114 font_fallbacks: settings.ui_font.fallbacks.clone(),
14115 font_size: rems(0.875).into(),
14116 font_weight: settings.ui_font.weight,
14117 line_height: relative(settings.buffer_line_height.value()),
14118 ..Default::default()
14119 },
14120 EditorMode::Full => TextStyle {
14121 color: cx.theme().colors().editor_foreground,
14122 font_family: settings.buffer_font.family.clone(),
14123 font_features: settings.buffer_font.features.clone(),
14124 font_fallbacks: settings.buffer_font.fallbacks.clone(),
14125 font_size: settings.buffer_font_size(cx).into(),
14126 font_weight: settings.buffer_font.weight,
14127 line_height: relative(settings.buffer_line_height.value()),
14128 ..Default::default()
14129 },
14130 };
14131 if let Some(text_style_refinement) = &self.text_style_refinement {
14132 text_style.refine(text_style_refinement)
14133 }
14134
14135 let background = match self.mode {
14136 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
14137 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
14138 EditorMode::Full => cx.theme().colors().editor_background,
14139 };
14140
14141 EditorElement::new(
14142 cx.view(),
14143 EditorStyle {
14144 background,
14145 local_player: cx.theme().players().local(),
14146 text: text_style,
14147 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
14148 syntax: cx.theme().syntax().clone(),
14149 status: cx.theme().status().clone(),
14150 inlay_hints_style: make_inlay_hints_style(cx),
14151 suggestions_style: HighlightStyle {
14152 color: Some(cx.theme().status().predictive),
14153 ..HighlightStyle::default()
14154 },
14155 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
14156 },
14157 )
14158 }
14159}
14160
14161impl ViewInputHandler for Editor {
14162 fn text_for_range(
14163 &mut self,
14164 range_utf16: Range<usize>,
14165 cx: &mut ViewContext<Self>,
14166 ) -> Option<String> {
14167 Some(
14168 self.buffer
14169 .read(cx)
14170 .read(cx)
14171 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
14172 .collect(),
14173 )
14174 }
14175
14176 fn selected_text_range(
14177 &mut self,
14178 ignore_disabled_input: bool,
14179 cx: &mut ViewContext<Self>,
14180 ) -> Option<UTF16Selection> {
14181 // Prevent the IME menu from appearing when holding down an alphabetic key
14182 // while input is disabled.
14183 if !ignore_disabled_input && !self.input_enabled {
14184 return None;
14185 }
14186
14187 let selection = self.selections.newest::<OffsetUtf16>(cx);
14188 let range = selection.range();
14189
14190 Some(UTF16Selection {
14191 range: range.start.0..range.end.0,
14192 reversed: selection.reversed,
14193 })
14194 }
14195
14196 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
14197 let snapshot = self.buffer.read(cx).read(cx);
14198 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
14199 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
14200 }
14201
14202 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
14203 self.clear_highlights::<InputComposition>(cx);
14204 self.ime_transaction.take();
14205 }
14206
14207 fn replace_text_in_range(
14208 &mut self,
14209 range_utf16: Option<Range<usize>>,
14210 text: &str,
14211 cx: &mut ViewContext<Self>,
14212 ) {
14213 if !self.input_enabled {
14214 cx.emit(EditorEvent::InputIgnored { text: text.into() });
14215 return;
14216 }
14217
14218 self.transact(cx, |this, cx| {
14219 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
14220 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
14221 Some(this.selection_replacement_ranges(range_utf16, cx))
14222 } else {
14223 this.marked_text_ranges(cx)
14224 };
14225
14226 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
14227 let newest_selection_id = this.selections.newest_anchor().id;
14228 this.selections
14229 .all::<OffsetUtf16>(cx)
14230 .iter()
14231 .zip(ranges_to_replace.iter())
14232 .find_map(|(selection, range)| {
14233 if selection.id == newest_selection_id {
14234 Some(
14235 (range.start.0 as isize - selection.head().0 as isize)
14236 ..(range.end.0 as isize - selection.head().0 as isize),
14237 )
14238 } else {
14239 None
14240 }
14241 })
14242 });
14243
14244 cx.emit(EditorEvent::InputHandled {
14245 utf16_range_to_replace: range_to_replace,
14246 text: text.into(),
14247 });
14248
14249 if let Some(new_selected_ranges) = new_selected_ranges {
14250 this.change_selections(None, cx, |selections| {
14251 selections.select_ranges(new_selected_ranges)
14252 });
14253 this.backspace(&Default::default(), cx);
14254 }
14255
14256 this.handle_input(text, cx);
14257 });
14258
14259 if let Some(transaction) = self.ime_transaction {
14260 self.buffer.update(cx, |buffer, cx| {
14261 buffer.group_until_transaction(transaction, cx);
14262 });
14263 }
14264
14265 self.unmark_text(cx);
14266 }
14267
14268 fn replace_and_mark_text_in_range(
14269 &mut self,
14270 range_utf16: Option<Range<usize>>,
14271 text: &str,
14272 new_selected_range_utf16: Option<Range<usize>>,
14273 cx: &mut ViewContext<Self>,
14274 ) {
14275 if !self.input_enabled {
14276 return;
14277 }
14278
14279 let transaction = self.transact(cx, |this, cx| {
14280 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
14281 let snapshot = this.buffer.read(cx).read(cx);
14282 if let Some(relative_range_utf16) = range_utf16.as_ref() {
14283 for marked_range in &mut marked_ranges {
14284 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
14285 marked_range.start.0 += relative_range_utf16.start;
14286 marked_range.start =
14287 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
14288 marked_range.end =
14289 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
14290 }
14291 }
14292 Some(marked_ranges)
14293 } else if let Some(range_utf16) = range_utf16 {
14294 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
14295 Some(this.selection_replacement_ranges(range_utf16, cx))
14296 } else {
14297 None
14298 };
14299
14300 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
14301 let newest_selection_id = this.selections.newest_anchor().id;
14302 this.selections
14303 .all::<OffsetUtf16>(cx)
14304 .iter()
14305 .zip(ranges_to_replace.iter())
14306 .find_map(|(selection, range)| {
14307 if selection.id == newest_selection_id {
14308 Some(
14309 (range.start.0 as isize - selection.head().0 as isize)
14310 ..(range.end.0 as isize - selection.head().0 as isize),
14311 )
14312 } else {
14313 None
14314 }
14315 })
14316 });
14317
14318 cx.emit(EditorEvent::InputHandled {
14319 utf16_range_to_replace: range_to_replace,
14320 text: text.into(),
14321 });
14322
14323 if let Some(ranges) = ranges_to_replace {
14324 this.change_selections(None, cx, |s| s.select_ranges(ranges));
14325 }
14326
14327 let marked_ranges = {
14328 let snapshot = this.buffer.read(cx).read(cx);
14329 this.selections
14330 .disjoint_anchors()
14331 .iter()
14332 .map(|selection| {
14333 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
14334 })
14335 .collect::<Vec<_>>()
14336 };
14337
14338 if text.is_empty() {
14339 this.unmark_text(cx);
14340 } else {
14341 this.highlight_text::<InputComposition>(
14342 marked_ranges.clone(),
14343 HighlightStyle {
14344 underline: Some(UnderlineStyle {
14345 thickness: px(1.),
14346 color: None,
14347 wavy: false,
14348 }),
14349 ..Default::default()
14350 },
14351 cx,
14352 );
14353 }
14354
14355 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
14356 let use_autoclose = this.use_autoclose;
14357 let use_auto_surround = this.use_auto_surround;
14358 this.set_use_autoclose(false);
14359 this.set_use_auto_surround(false);
14360 this.handle_input(text, cx);
14361 this.set_use_autoclose(use_autoclose);
14362 this.set_use_auto_surround(use_auto_surround);
14363
14364 if let Some(new_selected_range) = new_selected_range_utf16 {
14365 let snapshot = this.buffer.read(cx).read(cx);
14366 let new_selected_ranges = marked_ranges
14367 .into_iter()
14368 .map(|marked_range| {
14369 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
14370 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
14371 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
14372 snapshot.clip_offset_utf16(new_start, Bias::Left)
14373 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
14374 })
14375 .collect::<Vec<_>>();
14376
14377 drop(snapshot);
14378 this.change_selections(None, cx, |selections| {
14379 selections.select_ranges(new_selected_ranges)
14380 });
14381 }
14382 });
14383
14384 self.ime_transaction = self.ime_transaction.or(transaction);
14385 if let Some(transaction) = self.ime_transaction {
14386 self.buffer.update(cx, |buffer, cx| {
14387 buffer.group_until_transaction(transaction, cx);
14388 });
14389 }
14390
14391 if self.text_highlights::<InputComposition>(cx).is_none() {
14392 self.ime_transaction.take();
14393 }
14394 }
14395
14396 fn bounds_for_range(
14397 &mut self,
14398 range_utf16: Range<usize>,
14399 element_bounds: gpui::Bounds<Pixels>,
14400 cx: &mut ViewContext<Self>,
14401 ) -> Option<gpui::Bounds<Pixels>> {
14402 let text_layout_details = self.text_layout_details(cx);
14403 let style = &text_layout_details.editor_style;
14404 let font_id = cx.text_system().resolve_font(&style.text.font());
14405 let font_size = style.text.font_size.to_pixels(cx.rem_size());
14406 let line_height = style.text.line_height_in_pixels(cx.rem_size());
14407
14408 let em_width = cx
14409 .text_system()
14410 .typographic_bounds(font_id, font_size, 'm')
14411 .unwrap()
14412 .size
14413 .width;
14414
14415 let snapshot = self.snapshot(cx);
14416 let scroll_position = snapshot.scroll_position();
14417 let scroll_left = scroll_position.x * em_width;
14418
14419 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
14420 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
14421 + self.gutter_dimensions.width;
14422 let y = line_height * (start.row().as_f32() - scroll_position.y);
14423
14424 Some(Bounds {
14425 origin: element_bounds.origin + point(x, y),
14426 size: size(em_width, line_height),
14427 })
14428 }
14429}
14430
14431trait SelectionExt {
14432 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
14433 fn spanned_rows(
14434 &self,
14435 include_end_if_at_line_start: bool,
14436 map: &DisplaySnapshot,
14437 ) -> Range<MultiBufferRow>;
14438}
14439
14440impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
14441 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
14442 let start = self
14443 .start
14444 .to_point(&map.buffer_snapshot)
14445 .to_display_point(map);
14446 let end = self
14447 .end
14448 .to_point(&map.buffer_snapshot)
14449 .to_display_point(map);
14450 if self.reversed {
14451 end..start
14452 } else {
14453 start..end
14454 }
14455 }
14456
14457 fn spanned_rows(
14458 &self,
14459 include_end_if_at_line_start: bool,
14460 map: &DisplaySnapshot,
14461 ) -> Range<MultiBufferRow> {
14462 let start = self.start.to_point(&map.buffer_snapshot);
14463 let mut end = self.end.to_point(&map.buffer_snapshot);
14464 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
14465 end.row -= 1;
14466 }
14467
14468 let buffer_start = map.prev_line_boundary(start).0;
14469 let buffer_end = map.next_line_boundary(end).0;
14470 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
14471 }
14472}
14473
14474impl<T: InvalidationRegion> InvalidationStack<T> {
14475 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
14476 where
14477 S: Clone + ToOffset,
14478 {
14479 while let Some(region) = self.last() {
14480 let all_selections_inside_invalidation_ranges =
14481 if selections.len() == region.ranges().len() {
14482 selections
14483 .iter()
14484 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
14485 .all(|(selection, invalidation_range)| {
14486 let head = selection.head().to_offset(buffer);
14487 invalidation_range.start <= head && invalidation_range.end >= head
14488 })
14489 } else {
14490 false
14491 };
14492
14493 if all_selections_inside_invalidation_ranges {
14494 break;
14495 } else {
14496 self.pop();
14497 }
14498 }
14499 }
14500}
14501
14502impl<T> Default for InvalidationStack<T> {
14503 fn default() -> Self {
14504 Self(Default::default())
14505 }
14506}
14507
14508impl<T> Deref for InvalidationStack<T> {
14509 type Target = Vec<T>;
14510
14511 fn deref(&self) -> &Self::Target {
14512 &self.0
14513 }
14514}
14515
14516impl<T> DerefMut for InvalidationStack<T> {
14517 fn deref_mut(&mut self) -> &mut Self::Target {
14518 &mut self.0
14519 }
14520}
14521
14522impl InvalidationRegion for SnippetState {
14523 fn ranges(&self) -> &[Range<Anchor>] {
14524 &self.ranges[self.active_index]
14525 }
14526}
14527
14528pub fn diagnostic_block_renderer(
14529 diagnostic: Diagnostic,
14530 max_message_rows: Option<u8>,
14531 allow_closing: bool,
14532 _is_valid: bool,
14533) -> RenderBlock {
14534 let (text_without_backticks, code_ranges) =
14535 highlight_diagnostic_message(&diagnostic, max_message_rows);
14536
14537 Box::new(move |cx: &mut BlockContext| {
14538 let group_id: SharedString = cx.block_id.to_string().into();
14539
14540 let mut text_style = cx.text_style().clone();
14541 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
14542 let theme_settings = ThemeSettings::get_global(cx);
14543 text_style.font_family = theme_settings.buffer_font.family.clone();
14544 text_style.font_style = theme_settings.buffer_font.style;
14545 text_style.font_features = theme_settings.buffer_font.features.clone();
14546 text_style.font_weight = theme_settings.buffer_font.weight;
14547
14548 let multi_line_diagnostic = diagnostic.message.contains('\n');
14549
14550 let buttons = |diagnostic: &Diagnostic| {
14551 if multi_line_diagnostic {
14552 v_flex()
14553 } else {
14554 h_flex()
14555 }
14556 .when(allow_closing, |div| {
14557 div.children(diagnostic.is_primary.then(|| {
14558 IconButton::new("close-block", IconName::XCircle)
14559 .icon_color(Color::Muted)
14560 .size(ButtonSize::Compact)
14561 .style(ButtonStyle::Transparent)
14562 .visible_on_hover(group_id.clone())
14563 .on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
14564 .tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
14565 }))
14566 })
14567 .child(
14568 IconButton::new("copy-block", IconName::Copy)
14569 .icon_color(Color::Muted)
14570 .size(ButtonSize::Compact)
14571 .style(ButtonStyle::Transparent)
14572 .visible_on_hover(group_id.clone())
14573 .on_click({
14574 let message = diagnostic.message.clone();
14575 move |_click, cx| {
14576 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
14577 }
14578 })
14579 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
14580 )
14581 };
14582
14583 let icon_size = buttons(&diagnostic)
14584 .into_any_element()
14585 .layout_as_root(AvailableSpace::min_size(), cx);
14586
14587 h_flex()
14588 .id(cx.block_id)
14589 .group(group_id.clone())
14590 .relative()
14591 .size_full()
14592 .pl(cx.gutter_dimensions.width)
14593 .w(cx.max_width - cx.gutter_dimensions.full_width())
14594 .child(
14595 div()
14596 .flex()
14597 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
14598 .flex_shrink(),
14599 )
14600 .child(buttons(&diagnostic))
14601 .child(div().flex().flex_shrink_0().child(
14602 StyledText::new(text_without_backticks.clone()).with_highlights(
14603 &text_style,
14604 code_ranges.iter().map(|range| {
14605 (
14606 range.clone(),
14607 HighlightStyle {
14608 font_weight: Some(FontWeight::BOLD),
14609 ..Default::default()
14610 },
14611 )
14612 }),
14613 ),
14614 ))
14615 .into_any_element()
14616 })
14617}
14618
14619pub fn highlight_diagnostic_message(
14620 diagnostic: &Diagnostic,
14621 mut max_message_rows: Option<u8>,
14622) -> (SharedString, Vec<Range<usize>>) {
14623 let mut text_without_backticks = String::new();
14624 let mut code_ranges = Vec::new();
14625
14626 if let Some(source) = &diagnostic.source {
14627 text_without_backticks.push_str(source);
14628 code_ranges.push(0..source.len());
14629 text_without_backticks.push_str(": ");
14630 }
14631
14632 let mut prev_offset = 0;
14633 let mut in_code_block = false;
14634 let has_row_limit = max_message_rows.is_some();
14635 let mut newline_indices = diagnostic
14636 .message
14637 .match_indices('\n')
14638 .filter(|_| has_row_limit)
14639 .map(|(ix, _)| ix)
14640 .fuse()
14641 .peekable();
14642
14643 for (quote_ix, _) in diagnostic
14644 .message
14645 .match_indices('`')
14646 .chain([(diagnostic.message.len(), "")])
14647 {
14648 let mut first_newline_ix = None;
14649 let mut last_newline_ix = None;
14650 while let Some(newline_ix) = newline_indices.peek() {
14651 if *newline_ix < quote_ix {
14652 if first_newline_ix.is_none() {
14653 first_newline_ix = Some(*newline_ix);
14654 }
14655 last_newline_ix = Some(*newline_ix);
14656
14657 if let Some(rows_left) = &mut max_message_rows {
14658 if *rows_left == 0 {
14659 break;
14660 } else {
14661 *rows_left -= 1;
14662 }
14663 }
14664 let _ = newline_indices.next();
14665 } else {
14666 break;
14667 }
14668 }
14669 let prev_len = text_without_backticks.len();
14670 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
14671 text_without_backticks.push_str(new_text);
14672 if in_code_block {
14673 code_ranges.push(prev_len..text_without_backticks.len());
14674 }
14675 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
14676 in_code_block = !in_code_block;
14677 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
14678 text_without_backticks.push_str("...");
14679 break;
14680 }
14681 }
14682
14683 (text_without_backticks.into(), code_ranges)
14684}
14685
14686fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
14687 match severity {
14688 DiagnosticSeverity::ERROR => colors.error,
14689 DiagnosticSeverity::WARNING => colors.warning,
14690 DiagnosticSeverity::INFORMATION => colors.info,
14691 DiagnosticSeverity::HINT => colors.info,
14692 _ => colors.ignored,
14693 }
14694}
14695
14696pub fn styled_runs_for_code_label<'a>(
14697 label: &'a CodeLabel,
14698 syntax_theme: &'a theme::SyntaxTheme,
14699) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
14700 let fade_out = HighlightStyle {
14701 fade_out: Some(0.35),
14702 ..Default::default()
14703 };
14704
14705 let mut prev_end = label.filter_range.end;
14706 label
14707 .runs
14708 .iter()
14709 .enumerate()
14710 .flat_map(move |(ix, (range, highlight_id))| {
14711 let style = if let Some(style) = highlight_id.style(syntax_theme) {
14712 style
14713 } else {
14714 return Default::default();
14715 };
14716 let mut muted_style = style;
14717 muted_style.highlight(fade_out);
14718
14719 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
14720 if range.start >= label.filter_range.end {
14721 if range.start > prev_end {
14722 runs.push((prev_end..range.start, fade_out));
14723 }
14724 runs.push((range.clone(), muted_style));
14725 } else if range.end <= label.filter_range.end {
14726 runs.push((range.clone(), style));
14727 } else {
14728 runs.push((range.start..label.filter_range.end, style));
14729 runs.push((label.filter_range.end..range.end, muted_style));
14730 }
14731 prev_end = cmp::max(prev_end, range.end);
14732
14733 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
14734 runs.push((prev_end..label.text.len(), fade_out));
14735 }
14736
14737 runs
14738 })
14739}
14740
14741pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
14742 let mut prev_index = 0;
14743 let mut prev_codepoint: Option<char> = None;
14744 text.char_indices()
14745 .chain([(text.len(), '\0')])
14746 .filter_map(move |(index, codepoint)| {
14747 let prev_codepoint = prev_codepoint.replace(codepoint)?;
14748 let is_boundary = index == text.len()
14749 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
14750 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
14751 if is_boundary {
14752 let chunk = &text[prev_index..index];
14753 prev_index = index;
14754 Some(chunk)
14755 } else {
14756 None
14757 }
14758 })
14759}
14760
14761pub trait RangeToAnchorExt: Sized {
14762 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
14763
14764 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
14765 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
14766 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
14767 }
14768}
14769
14770impl<T: ToOffset> RangeToAnchorExt for Range<T> {
14771 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
14772 let start_offset = self.start.to_offset(snapshot);
14773 let end_offset = self.end.to_offset(snapshot);
14774 if start_offset == end_offset {
14775 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
14776 } else {
14777 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
14778 }
14779 }
14780}
14781
14782pub trait RowExt {
14783 fn as_f32(&self) -> f32;
14784
14785 fn next_row(&self) -> Self;
14786
14787 fn previous_row(&self) -> Self;
14788
14789 fn minus(&self, other: Self) -> u32;
14790}
14791
14792impl RowExt for DisplayRow {
14793 fn as_f32(&self) -> f32 {
14794 self.0 as f32
14795 }
14796
14797 fn next_row(&self) -> Self {
14798 Self(self.0 + 1)
14799 }
14800
14801 fn previous_row(&self) -> Self {
14802 Self(self.0.saturating_sub(1))
14803 }
14804
14805 fn minus(&self, other: Self) -> u32 {
14806 self.0 - other.0
14807 }
14808}
14809
14810impl RowExt for MultiBufferRow {
14811 fn as_f32(&self) -> f32 {
14812 self.0 as f32
14813 }
14814
14815 fn next_row(&self) -> Self {
14816 Self(self.0 + 1)
14817 }
14818
14819 fn previous_row(&self) -> Self {
14820 Self(self.0.saturating_sub(1))
14821 }
14822
14823 fn minus(&self, other: Self) -> u32 {
14824 self.0 - other.0
14825 }
14826}
14827
14828trait RowRangeExt {
14829 type Row;
14830
14831 fn len(&self) -> usize;
14832
14833 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
14834}
14835
14836impl RowRangeExt for Range<MultiBufferRow> {
14837 type Row = MultiBufferRow;
14838
14839 fn len(&self) -> usize {
14840 (self.end.0 - self.start.0) as usize
14841 }
14842
14843 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
14844 (self.start.0..self.end.0).map(MultiBufferRow)
14845 }
14846}
14847
14848impl RowRangeExt for Range<DisplayRow> {
14849 type Row = DisplayRow;
14850
14851 fn len(&self) -> usize {
14852 (self.end.0 - self.start.0) as usize
14853 }
14854
14855 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
14856 (self.start.0..self.end.0).map(DisplayRow)
14857 }
14858}
14859
14860fn hunk_status(hunk: &MultiBufferDiffHunk) -> DiffHunkStatus {
14861 if hunk.diff_base_byte_range.is_empty() {
14862 DiffHunkStatus::Added
14863 } else if hunk.row_range.is_empty() {
14864 DiffHunkStatus::Removed
14865 } else {
14866 DiffHunkStatus::Modified
14867 }
14868}
14869
14870/// If select range has more than one line, we
14871/// just point the cursor to range.start.
14872fn check_multiline_range(buffer: &Buffer, range: Range<usize>) -> Range<usize> {
14873 if buffer.offset_to_point(range.start).row == buffer.offset_to_point(range.end).row {
14874 range
14875 } else {
14876 range.start..range.start
14877 }
14878}
14879
14880const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);