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, ProjectPath, 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 let text_layout_details = &self.text_layout_details(cx);
8786 self.transact(cx, |this, cx| {
8787 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
8788 let mut edits = Vec::new();
8789 let mut selection_edit_ranges = Vec::new();
8790 let mut last_toggled_row = None;
8791 let snapshot = this.buffer.read(cx).read(cx);
8792 let empty_str: Arc<str> = Arc::default();
8793 let mut suffixes_inserted = Vec::new();
8794 let ignore_indent = action.ignore_indent;
8795
8796 fn comment_prefix_range(
8797 snapshot: &MultiBufferSnapshot,
8798 row: MultiBufferRow,
8799 comment_prefix: &str,
8800 comment_prefix_whitespace: &str,
8801 ignore_indent: bool,
8802 ) -> Range<Point> {
8803 let indent_size = if ignore_indent {
8804 0
8805 } else {
8806 snapshot.indent_size_for_line(row).len
8807 };
8808
8809 let start = Point::new(row.0, indent_size);
8810
8811 let mut line_bytes = snapshot
8812 .bytes_in_range(start..snapshot.max_point())
8813 .flatten()
8814 .copied();
8815
8816 // If this line currently begins with the line comment prefix, then record
8817 // the range containing the prefix.
8818 if line_bytes
8819 .by_ref()
8820 .take(comment_prefix.len())
8821 .eq(comment_prefix.bytes())
8822 {
8823 // Include any whitespace that matches the comment prefix.
8824 let matching_whitespace_len = line_bytes
8825 .zip(comment_prefix_whitespace.bytes())
8826 .take_while(|(a, b)| a == b)
8827 .count() as u32;
8828 let end = Point::new(
8829 start.row,
8830 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
8831 );
8832 start..end
8833 } else {
8834 start..start
8835 }
8836 }
8837
8838 fn comment_suffix_range(
8839 snapshot: &MultiBufferSnapshot,
8840 row: MultiBufferRow,
8841 comment_suffix: &str,
8842 comment_suffix_has_leading_space: bool,
8843 ) -> Range<Point> {
8844 let end = Point::new(row.0, snapshot.line_len(row));
8845 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
8846
8847 let mut line_end_bytes = snapshot
8848 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
8849 .flatten()
8850 .copied();
8851
8852 let leading_space_len = if suffix_start_column > 0
8853 && line_end_bytes.next() == Some(b' ')
8854 && comment_suffix_has_leading_space
8855 {
8856 1
8857 } else {
8858 0
8859 };
8860
8861 // If this line currently begins with the line comment prefix, then record
8862 // the range containing the prefix.
8863 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
8864 let start = Point::new(end.row, suffix_start_column - leading_space_len);
8865 start..end
8866 } else {
8867 end..end
8868 }
8869 }
8870
8871 // TODO: Handle selections that cross excerpts
8872 for selection in &mut selections {
8873 let start_column = snapshot
8874 .indent_size_for_line(MultiBufferRow(selection.start.row))
8875 .len;
8876 let language = if let Some(language) =
8877 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
8878 {
8879 language
8880 } else {
8881 continue;
8882 };
8883
8884 selection_edit_ranges.clear();
8885
8886 // If multiple selections contain a given row, avoid processing that
8887 // row more than once.
8888 let mut start_row = MultiBufferRow(selection.start.row);
8889 if last_toggled_row == Some(start_row) {
8890 start_row = start_row.next_row();
8891 }
8892 let end_row =
8893 if selection.end.row > selection.start.row && selection.end.column == 0 {
8894 MultiBufferRow(selection.end.row - 1)
8895 } else {
8896 MultiBufferRow(selection.end.row)
8897 };
8898 last_toggled_row = Some(end_row);
8899
8900 if start_row > end_row {
8901 continue;
8902 }
8903
8904 // If the language has line comments, toggle those.
8905 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
8906
8907 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
8908 if ignore_indent {
8909 full_comment_prefixes = full_comment_prefixes
8910 .into_iter()
8911 .map(|s| Arc::from(s.trim_end()))
8912 .collect();
8913 }
8914
8915 if !full_comment_prefixes.is_empty() {
8916 let first_prefix = full_comment_prefixes
8917 .first()
8918 .expect("prefixes is non-empty");
8919 let prefix_trimmed_lengths = full_comment_prefixes
8920 .iter()
8921 .map(|p| p.trim_end_matches(' ').len())
8922 .collect::<SmallVec<[usize; 4]>>();
8923
8924 let mut all_selection_lines_are_comments = true;
8925
8926 for row in start_row.0..=end_row.0 {
8927 let row = MultiBufferRow(row);
8928 if start_row < end_row && snapshot.is_line_blank(row) {
8929 continue;
8930 }
8931
8932 let prefix_range = full_comment_prefixes
8933 .iter()
8934 .zip(prefix_trimmed_lengths.iter().copied())
8935 .map(|(prefix, trimmed_prefix_len)| {
8936 comment_prefix_range(
8937 snapshot.deref(),
8938 row,
8939 &prefix[..trimmed_prefix_len],
8940 &prefix[trimmed_prefix_len..],
8941 ignore_indent,
8942 )
8943 })
8944 .max_by_key(|range| range.end.column - range.start.column)
8945 .expect("prefixes is non-empty");
8946
8947 if prefix_range.is_empty() {
8948 all_selection_lines_are_comments = false;
8949 }
8950
8951 selection_edit_ranges.push(prefix_range);
8952 }
8953
8954 if all_selection_lines_are_comments {
8955 edits.extend(
8956 selection_edit_ranges
8957 .iter()
8958 .cloned()
8959 .map(|range| (range, empty_str.clone())),
8960 );
8961 } else {
8962 let min_column = selection_edit_ranges
8963 .iter()
8964 .map(|range| range.start.column)
8965 .min()
8966 .unwrap_or(0);
8967 edits.extend(selection_edit_ranges.iter().map(|range| {
8968 let position = Point::new(range.start.row, min_column);
8969 (position..position, first_prefix.clone())
8970 }));
8971 }
8972 } else if let Some((full_comment_prefix, comment_suffix)) =
8973 language.block_comment_delimiters()
8974 {
8975 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
8976 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
8977 let prefix_range = comment_prefix_range(
8978 snapshot.deref(),
8979 start_row,
8980 comment_prefix,
8981 comment_prefix_whitespace,
8982 ignore_indent,
8983 );
8984 let suffix_range = comment_suffix_range(
8985 snapshot.deref(),
8986 end_row,
8987 comment_suffix.trim_start_matches(' '),
8988 comment_suffix.starts_with(' '),
8989 );
8990
8991 if prefix_range.is_empty() || suffix_range.is_empty() {
8992 edits.push((
8993 prefix_range.start..prefix_range.start,
8994 full_comment_prefix.clone(),
8995 ));
8996 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
8997 suffixes_inserted.push((end_row, comment_suffix.len()));
8998 } else {
8999 edits.push((prefix_range, empty_str.clone()));
9000 edits.push((suffix_range, empty_str.clone()));
9001 }
9002 } else {
9003 continue;
9004 }
9005 }
9006
9007 drop(snapshot);
9008 this.buffer.update(cx, |buffer, cx| {
9009 buffer.edit(edits, None, cx);
9010 });
9011
9012 // Adjust selections so that they end before any comment suffixes that
9013 // were inserted.
9014 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
9015 let mut selections = this.selections.all::<Point>(cx);
9016 let snapshot = this.buffer.read(cx).read(cx);
9017 for selection in &mut selections {
9018 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
9019 match row.cmp(&MultiBufferRow(selection.end.row)) {
9020 Ordering::Less => {
9021 suffixes_inserted.next();
9022 continue;
9023 }
9024 Ordering::Greater => break,
9025 Ordering::Equal => {
9026 if selection.end.column == snapshot.line_len(row) {
9027 if selection.is_empty() {
9028 selection.start.column -= suffix_len as u32;
9029 }
9030 selection.end.column -= suffix_len as u32;
9031 }
9032 break;
9033 }
9034 }
9035 }
9036 }
9037
9038 drop(snapshot);
9039 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
9040
9041 let selections = this.selections.all::<Point>(cx);
9042 let selections_on_single_row = selections.windows(2).all(|selections| {
9043 selections[0].start.row == selections[1].start.row
9044 && selections[0].end.row == selections[1].end.row
9045 && selections[0].start.row == selections[0].end.row
9046 });
9047 let selections_selecting = selections
9048 .iter()
9049 .any(|selection| selection.start != selection.end);
9050 let advance_downwards = action.advance_downwards
9051 && selections_on_single_row
9052 && !selections_selecting
9053 && !matches!(this.mode, EditorMode::SingleLine { .. });
9054
9055 if advance_downwards {
9056 let snapshot = this.buffer.read(cx).snapshot(cx);
9057
9058 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
9059 s.move_cursors_with(|display_snapshot, display_point, _| {
9060 let mut point = display_point.to_point(display_snapshot);
9061 point.row += 1;
9062 point = snapshot.clip_point(point, Bias::Left);
9063 let display_point = point.to_display_point(display_snapshot);
9064 let goal = SelectionGoal::HorizontalPosition(
9065 display_snapshot
9066 .x_for_display_point(display_point, text_layout_details)
9067 .into(),
9068 );
9069 (display_point, goal)
9070 })
9071 });
9072 }
9073 });
9074 }
9075
9076 pub fn select_enclosing_symbol(
9077 &mut self,
9078 _: &SelectEnclosingSymbol,
9079 cx: &mut ViewContext<Self>,
9080 ) {
9081 let buffer = self.buffer.read(cx).snapshot(cx);
9082 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
9083
9084 fn update_selection(
9085 selection: &Selection<usize>,
9086 buffer_snap: &MultiBufferSnapshot,
9087 ) -> Option<Selection<usize>> {
9088 let cursor = selection.head();
9089 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
9090 for symbol in symbols.iter().rev() {
9091 let start = symbol.range.start.to_offset(buffer_snap);
9092 let end = symbol.range.end.to_offset(buffer_snap);
9093 let new_range = start..end;
9094 if start < selection.start || end > selection.end {
9095 return Some(Selection {
9096 id: selection.id,
9097 start: new_range.start,
9098 end: new_range.end,
9099 goal: SelectionGoal::None,
9100 reversed: selection.reversed,
9101 });
9102 }
9103 }
9104 None
9105 }
9106
9107 let mut selected_larger_symbol = false;
9108 let new_selections = old_selections
9109 .iter()
9110 .map(|selection| match update_selection(selection, &buffer) {
9111 Some(new_selection) => {
9112 if new_selection.range() != selection.range() {
9113 selected_larger_symbol = true;
9114 }
9115 new_selection
9116 }
9117 None => selection.clone(),
9118 })
9119 .collect::<Vec<_>>();
9120
9121 if selected_larger_symbol {
9122 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9123 s.select(new_selections);
9124 });
9125 }
9126 }
9127
9128 pub fn select_larger_syntax_node(
9129 &mut self,
9130 _: &SelectLargerSyntaxNode,
9131 cx: &mut ViewContext<Self>,
9132 ) {
9133 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9134 let buffer = self.buffer.read(cx).snapshot(cx);
9135 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
9136
9137 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
9138 let mut selected_larger_node = false;
9139 let new_selections = old_selections
9140 .iter()
9141 .map(|selection| {
9142 let old_range = selection.start..selection.end;
9143 let mut new_range = old_range.clone();
9144 while let Some(containing_range) =
9145 buffer.range_for_syntax_ancestor(new_range.clone())
9146 {
9147 new_range = containing_range;
9148 if !display_map.intersects_fold(new_range.start)
9149 && !display_map.intersects_fold(new_range.end)
9150 {
9151 break;
9152 }
9153 }
9154
9155 selected_larger_node |= new_range != old_range;
9156 Selection {
9157 id: selection.id,
9158 start: new_range.start,
9159 end: new_range.end,
9160 goal: SelectionGoal::None,
9161 reversed: selection.reversed,
9162 }
9163 })
9164 .collect::<Vec<_>>();
9165
9166 if selected_larger_node {
9167 stack.push(old_selections);
9168 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9169 s.select(new_selections);
9170 });
9171 }
9172 self.select_larger_syntax_node_stack = stack;
9173 }
9174
9175 pub fn select_smaller_syntax_node(
9176 &mut self,
9177 _: &SelectSmallerSyntaxNode,
9178 cx: &mut ViewContext<Self>,
9179 ) {
9180 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
9181 if let Some(selections) = stack.pop() {
9182 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9183 s.select(selections.to_vec());
9184 });
9185 }
9186 self.select_larger_syntax_node_stack = stack;
9187 }
9188
9189 fn refresh_runnables(&mut self, cx: &mut ViewContext<Self>) -> Task<()> {
9190 if !EditorSettings::get_global(cx).gutter.runnables {
9191 self.clear_tasks();
9192 return Task::ready(());
9193 }
9194 let project = self.project.clone();
9195 cx.spawn(|this, mut cx| async move {
9196 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
9197 this.display_map.update(cx, |map, cx| map.snapshot(cx))
9198 }) else {
9199 return;
9200 };
9201
9202 let Some(project) = project else {
9203 return;
9204 };
9205
9206 let hide_runnables = project
9207 .update(&mut cx, |project, cx| {
9208 // Do not display any test indicators in non-dev server remote projects.
9209 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
9210 })
9211 .unwrap_or(true);
9212 if hide_runnables {
9213 return;
9214 }
9215 let new_rows =
9216 cx.background_executor()
9217 .spawn({
9218 let snapshot = display_snapshot.clone();
9219 async move {
9220 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
9221 }
9222 })
9223 .await;
9224 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
9225
9226 this.update(&mut cx, |this, _| {
9227 this.clear_tasks();
9228 for (key, value) in rows {
9229 this.insert_tasks(key, value);
9230 }
9231 })
9232 .ok();
9233 })
9234 }
9235 fn fetch_runnable_ranges(
9236 snapshot: &DisplaySnapshot,
9237 range: Range<Anchor>,
9238 ) -> Vec<language::RunnableRange> {
9239 snapshot.buffer_snapshot.runnable_ranges(range).collect()
9240 }
9241
9242 fn runnable_rows(
9243 project: Model<Project>,
9244 snapshot: DisplaySnapshot,
9245 runnable_ranges: Vec<RunnableRange>,
9246 mut cx: AsyncWindowContext,
9247 ) -> Vec<((BufferId, u32), RunnableTasks)> {
9248 runnable_ranges
9249 .into_iter()
9250 .filter_map(|mut runnable| {
9251 let tasks = cx
9252 .update(|cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
9253 .ok()?;
9254 if tasks.is_empty() {
9255 return None;
9256 }
9257
9258 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
9259
9260 let row = snapshot
9261 .buffer_snapshot
9262 .buffer_line_for_row(MultiBufferRow(point.row))?
9263 .1
9264 .start
9265 .row;
9266
9267 let context_range =
9268 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
9269 Some((
9270 (runnable.buffer_id, row),
9271 RunnableTasks {
9272 templates: tasks,
9273 offset: MultiBufferOffset(runnable.run_range.start),
9274 context_range,
9275 column: point.column,
9276 extra_variables: runnable.extra_captures,
9277 },
9278 ))
9279 })
9280 .collect()
9281 }
9282
9283 fn templates_with_tags(
9284 project: &Model<Project>,
9285 runnable: &mut Runnable,
9286 cx: &WindowContext<'_>,
9287 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
9288 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
9289 let (worktree_id, file) = project
9290 .buffer_for_id(runnable.buffer, cx)
9291 .and_then(|buffer| buffer.read(cx).file())
9292 .map(|file| (file.worktree_id(cx), file.clone()))
9293 .unzip();
9294
9295 (
9296 project.task_store().read(cx).task_inventory().cloned(),
9297 worktree_id,
9298 file,
9299 )
9300 });
9301
9302 let tags = mem::take(&mut runnable.tags);
9303 let mut tags: Vec<_> = tags
9304 .into_iter()
9305 .flat_map(|tag| {
9306 let tag = tag.0.clone();
9307 inventory
9308 .as_ref()
9309 .into_iter()
9310 .flat_map(|inventory| {
9311 inventory.read(cx).list_tasks(
9312 file.clone(),
9313 Some(runnable.language.clone()),
9314 worktree_id,
9315 cx,
9316 )
9317 })
9318 .filter(move |(_, template)| {
9319 template.tags.iter().any(|source_tag| source_tag == &tag)
9320 })
9321 })
9322 .sorted_by_key(|(kind, _)| kind.to_owned())
9323 .collect();
9324 if let Some((leading_tag_source, _)) = tags.first() {
9325 // Strongest source wins; if we have worktree tag binding, prefer that to
9326 // global and language bindings;
9327 // if we have a global binding, prefer that to language binding.
9328 let first_mismatch = tags
9329 .iter()
9330 .position(|(tag_source, _)| tag_source != leading_tag_source);
9331 if let Some(index) = first_mismatch {
9332 tags.truncate(index);
9333 }
9334 }
9335
9336 tags
9337 }
9338
9339 pub fn move_to_enclosing_bracket(
9340 &mut self,
9341 _: &MoveToEnclosingBracket,
9342 cx: &mut ViewContext<Self>,
9343 ) {
9344 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9345 s.move_offsets_with(|snapshot, selection| {
9346 let Some(enclosing_bracket_ranges) =
9347 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
9348 else {
9349 return;
9350 };
9351
9352 let mut best_length = usize::MAX;
9353 let mut best_inside = false;
9354 let mut best_in_bracket_range = false;
9355 let mut best_destination = None;
9356 for (open, close) in enclosing_bracket_ranges {
9357 let close = close.to_inclusive();
9358 let length = close.end() - open.start;
9359 let inside = selection.start >= open.end && selection.end <= *close.start();
9360 let in_bracket_range = open.to_inclusive().contains(&selection.head())
9361 || close.contains(&selection.head());
9362
9363 // If best is next to a bracket and current isn't, skip
9364 if !in_bracket_range && best_in_bracket_range {
9365 continue;
9366 }
9367
9368 // Prefer smaller lengths unless best is inside and current isn't
9369 if length > best_length && (best_inside || !inside) {
9370 continue;
9371 }
9372
9373 best_length = length;
9374 best_inside = inside;
9375 best_in_bracket_range = in_bracket_range;
9376 best_destination = Some(
9377 if close.contains(&selection.start) && close.contains(&selection.end) {
9378 if inside {
9379 open.end
9380 } else {
9381 open.start
9382 }
9383 } else if inside {
9384 *close.start()
9385 } else {
9386 *close.end()
9387 },
9388 );
9389 }
9390
9391 if let Some(destination) = best_destination {
9392 selection.collapse_to(destination, SelectionGoal::None);
9393 }
9394 })
9395 });
9396 }
9397
9398 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
9399 self.end_selection(cx);
9400 self.selection_history.mode = SelectionHistoryMode::Undoing;
9401 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
9402 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
9403 self.select_next_state = entry.select_next_state;
9404 self.select_prev_state = entry.select_prev_state;
9405 self.add_selections_state = entry.add_selections_state;
9406 self.request_autoscroll(Autoscroll::newest(), cx);
9407 }
9408 self.selection_history.mode = SelectionHistoryMode::Normal;
9409 }
9410
9411 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
9412 self.end_selection(cx);
9413 self.selection_history.mode = SelectionHistoryMode::Redoing;
9414 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
9415 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
9416 self.select_next_state = entry.select_next_state;
9417 self.select_prev_state = entry.select_prev_state;
9418 self.add_selections_state = entry.add_selections_state;
9419 self.request_autoscroll(Autoscroll::newest(), cx);
9420 }
9421 self.selection_history.mode = SelectionHistoryMode::Normal;
9422 }
9423
9424 pub fn expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
9425 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
9426 }
9427
9428 pub fn expand_excerpts_down(
9429 &mut self,
9430 action: &ExpandExcerptsDown,
9431 cx: &mut ViewContext<Self>,
9432 ) {
9433 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
9434 }
9435
9436 pub fn expand_excerpts_up(&mut self, action: &ExpandExcerptsUp, cx: &mut ViewContext<Self>) {
9437 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
9438 }
9439
9440 pub fn expand_excerpts_for_direction(
9441 &mut self,
9442 lines: u32,
9443 direction: ExpandExcerptDirection,
9444 cx: &mut ViewContext<Self>,
9445 ) {
9446 let selections = self.selections.disjoint_anchors();
9447
9448 let lines = if lines == 0 {
9449 EditorSettings::get_global(cx).expand_excerpt_lines
9450 } else {
9451 lines
9452 };
9453
9454 self.buffer.update(cx, |buffer, cx| {
9455 buffer.expand_excerpts(
9456 selections
9457 .iter()
9458 .map(|selection| selection.head().excerpt_id)
9459 .dedup(),
9460 lines,
9461 direction,
9462 cx,
9463 )
9464 })
9465 }
9466
9467 pub fn expand_excerpt(
9468 &mut self,
9469 excerpt: ExcerptId,
9470 direction: ExpandExcerptDirection,
9471 cx: &mut ViewContext<Self>,
9472 ) {
9473 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
9474 self.buffer.update(cx, |buffer, cx| {
9475 buffer.expand_excerpts([excerpt], lines, direction, cx)
9476 })
9477 }
9478
9479 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
9480 self.go_to_diagnostic_impl(Direction::Next, cx)
9481 }
9482
9483 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
9484 self.go_to_diagnostic_impl(Direction::Prev, cx)
9485 }
9486
9487 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
9488 let buffer = self.buffer.read(cx).snapshot(cx);
9489 let selection = self.selections.newest::<usize>(cx);
9490
9491 // If there is an active Diagnostic Popover jump to its diagnostic instead.
9492 if direction == Direction::Next {
9493 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
9494 let (group_id, jump_to) = popover.activation_info();
9495 if self.activate_diagnostics(group_id, cx) {
9496 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9497 let mut new_selection = s.newest_anchor().clone();
9498 new_selection.collapse_to(jump_to, SelectionGoal::None);
9499 s.select_anchors(vec![new_selection.clone()]);
9500 });
9501 }
9502 return;
9503 }
9504 }
9505
9506 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
9507 active_diagnostics
9508 .primary_range
9509 .to_offset(&buffer)
9510 .to_inclusive()
9511 });
9512 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
9513 if active_primary_range.contains(&selection.head()) {
9514 *active_primary_range.start()
9515 } else {
9516 selection.head()
9517 }
9518 } else {
9519 selection.head()
9520 };
9521 let snapshot = self.snapshot(cx);
9522 loop {
9523 let diagnostics = if direction == Direction::Prev {
9524 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
9525 } else {
9526 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
9527 }
9528 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start));
9529 let group = diagnostics
9530 // relies on diagnostics_in_range to return diagnostics with the same starting range to
9531 // be sorted in a stable way
9532 // skip until we are at current active diagnostic, if it exists
9533 .skip_while(|entry| {
9534 (match direction {
9535 Direction::Prev => entry.range.start >= search_start,
9536 Direction::Next => entry.range.start <= search_start,
9537 }) && self
9538 .active_diagnostics
9539 .as_ref()
9540 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
9541 })
9542 .find_map(|entry| {
9543 if entry.diagnostic.is_primary
9544 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
9545 && !entry.range.is_empty()
9546 // if we match with the active diagnostic, skip it
9547 && Some(entry.diagnostic.group_id)
9548 != self.active_diagnostics.as_ref().map(|d| d.group_id)
9549 {
9550 Some((entry.range, entry.diagnostic.group_id))
9551 } else {
9552 None
9553 }
9554 });
9555
9556 if let Some((primary_range, group_id)) = group {
9557 if self.activate_diagnostics(group_id, cx) {
9558 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9559 s.select(vec![Selection {
9560 id: selection.id,
9561 start: primary_range.start,
9562 end: primary_range.start,
9563 reversed: false,
9564 goal: SelectionGoal::None,
9565 }]);
9566 });
9567 }
9568 break;
9569 } else {
9570 // Cycle around to the start of the buffer, potentially moving back to the start of
9571 // the currently active diagnostic.
9572 active_primary_range.take();
9573 if direction == Direction::Prev {
9574 if search_start == buffer.len() {
9575 break;
9576 } else {
9577 search_start = buffer.len();
9578 }
9579 } else if search_start == 0 {
9580 break;
9581 } else {
9582 search_start = 0;
9583 }
9584 }
9585 }
9586 }
9587
9588 fn go_to_next_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
9589 let snapshot = self
9590 .display_map
9591 .update(cx, |display_map, cx| display_map.snapshot(cx));
9592 let selection = self.selections.newest::<Point>(cx);
9593 self.go_to_hunk_after_position(&snapshot, selection.head(), cx);
9594 }
9595
9596 fn go_to_hunk_after_position(
9597 &mut self,
9598 snapshot: &DisplaySnapshot,
9599 position: Point,
9600 cx: &mut ViewContext<'_, Editor>,
9601 ) -> Option<MultiBufferDiffHunk> {
9602 if let Some(hunk) = self.go_to_next_hunk_in_direction(
9603 snapshot,
9604 position,
9605 false,
9606 snapshot
9607 .buffer_snapshot
9608 .git_diff_hunks_in_range(MultiBufferRow(position.row + 1)..MultiBufferRow::MAX),
9609 cx,
9610 ) {
9611 return Some(hunk);
9612 }
9613
9614 let wrapped_point = Point::zero();
9615 self.go_to_next_hunk_in_direction(
9616 snapshot,
9617 wrapped_point,
9618 true,
9619 snapshot.buffer_snapshot.git_diff_hunks_in_range(
9620 MultiBufferRow(wrapped_point.row + 1)..MultiBufferRow::MAX,
9621 ),
9622 cx,
9623 )
9624 }
9625
9626 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
9627 let snapshot = self
9628 .display_map
9629 .update(cx, |display_map, cx| display_map.snapshot(cx));
9630 let selection = self.selections.newest::<Point>(cx);
9631
9632 self.go_to_hunk_before_position(&snapshot, selection.head(), cx);
9633 }
9634
9635 fn go_to_hunk_before_position(
9636 &mut self,
9637 snapshot: &DisplaySnapshot,
9638 position: Point,
9639 cx: &mut ViewContext<'_, Editor>,
9640 ) -> Option<MultiBufferDiffHunk> {
9641 if let Some(hunk) = self.go_to_next_hunk_in_direction(
9642 snapshot,
9643 position,
9644 false,
9645 snapshot
9646 .buffer_snapshot
9647 .git_diff_hunks_in_range_rev(MultiBufferRow(0)..MultiBufferRow(position.row)),
9648 cx,
9649 ) {
9650 return Some(hunk);
9651 }
9652
9653 let wrapped_point = snapshot.buffer_snapshot.max_point();
9654 self.go_to_next_hunk_in_direction(
9655 snapshot,
9656 wrapped_point,
9657 true,
9658 snapshot
9659 .buffer_snapshot
9660 .git_diff_hunks_in_range_rev(MultiBufferRow(0)..MultiBufferRow(wrapped_point.row)),
9661 cx,
9662 )
9663 }
9664
9665 fn go_to_next_hunk_in_direction(
9666 &mut self,
9667 snapshot: &DisplaySnapshot,
9668 initial_point: Point,
9669 is_wrapped: bool,
9670 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
9671 cx: &mut ViewContext<Editor>,
9672 ) -> Option<MultiBufferDiffHunk> {
9673 let display_point = initial_point.to_display_point(snapshot);
9674 let mut hunks = hunks
9675 .map(|hunk| (diff_hunk_to_display(&hunk, snapshot), hunk))
9676 .filter(|(display_hunk, _)| {
9677 is_wrapped || !display_hunk.contains_display_row(display_point.row())
9678 })
9679 .dedup();
9680
9681 if let Some((display_hunk, hunk)) = hunks.next() {
9682 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9683 let row = display_hunk.start_display_row();
9684 let point = DisplayPoint::new(row, 0);
9685 s.select_display_ranges([point..point]);
9686 });
9687
9688 Some(hunk)
9689 } else {
9690 None
9691 }
9692 }
9693
9694 pub fn go_to_definition(
9695 &mut self,
9696 _: &GoToDefinition,
9697 cx: &mut ViewContext<Self>,
9698 ) -> Task<Result<Navigated>> {
9699 let definition = self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
9700 cx.spawn(|editor, mut cx| async move {
9701 if definition.await? == Navigated::Yes {
9702 return Ok(Navigated::Yes);
9703 }
9704 match editor.update(&mut cx, |editor, cx| {
9705 editor.find_all_references(&FindAllReferences, cx)
9706 })? {
9707 Some(references) => references.await,
9708 None => Ok(Navigated::No),
9709 }
9710 })
9711 }
9712
9713 pub fn go_to_declaration(
9714 &mut self,
9715 _: &GoToDeclaration,
9716 cx: &mut ViewContext<Self>,
9717 ) -> Task<Result<Navigated>> {
9718 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, cx)
9719 }
9720
9721 pub fn go_to_declaration_split(
9722 &mut self,
9723 _: &GoToDeclaration,
9724 cx: &mut ViewContext<Self>,
9725 ) -> Task<Result<Navigated>> {
9726 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, cx)
9727 }
9728
9729 pub fn go_to_implementation(
9730 &mut self,
9731 _: &GoToImplementation,
9732 cx: &mut ViewContext<Self>,
9733 ) -> Task<Result<Navigated>> {
9734 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx)
9735 }
9736
9737 pub fn go_to_implementation_split(
9738 &mut self,
9739 _: &GoToImplementationSplit,
9740 cx: &mut ViewContext<Self>,
9741 ) -> Task<Result<Navigated>> {
9742 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx)
9743 }
9744
9745 pub fn go_to_type_definition(
9746 &mut self,
9747 _: &GoToTypeDefinition,
9748 cx: &mut ViewContext<Self>,
9749 ) -> Task<Result<Navigated>> {
9750 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx)
9751 }
9752
9753 pub fn go_to_definition_split(
9754 &mut self,
9755 _: &GoToDefinitionSplit,
9756 cx: &mut ViewContext<Self>,
9757 ) -> Task<Result<Navigated>> {
9758 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx)
9759 }
9760
9761 pub fn go_to_type_definition_split(
9762 &mut self,
9763 _: &GoToTypeDefinitionSplit,
9764 cx: &mut ViewContext<Self>,
9765 ) -> Task<Result<Navigated>> {
9766 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx)
9767 }
9768
9769 fn go_to_definition_of_kind(
9770 &mut self,
9771 kind: GotoDefinitionKind,
9772 split: bool,
9773 cx: &mut ViewContext<Self>,
9774 ) -> Task<Result<Navigated>> {
9775 let Some(provider) = self.semantics_provider.clone() else {
9776 return Task::ready(Ok(Navigated::No));
9777 };
9778 let head = self.selections.newest::<usize>(cx).head();
9779 let buffer = self.buffer.read(cx);
9780 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
9781 text_anchor
9782 } else {
9783 return Task::ready(Ok(Navigated::No));
9784 };
9785
9786 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
9787 return Task::ready(Ok(Navigated::No));
9788 };
9789
9790 cx.spawn(|editor, mut cx| async move {
9791 let definitions = definitions.await?;
9792 let navigated = editor
9793 .update(&mut cx, |editor, cx| {
9794 editor.navigate_to_hover_links(
9795 Some(kind),
9796 definitions
9797 .into_iter()
9798 .filter(|location| {
9799 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
9800 })
9801 .map(HoverLink::Text)
9802 .collect::<Vec<_>>(),
9803 split,
9804 cx,
9805 )
9806 })?
9807 .await?;
9808 anyhow::Ok(navigated)
9809 })
9810 }
9811
9812 pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext<Self>) {
9813 let position = self.selections.newest_anchor().head();
9814 let Some((buffer, buffer_position)) =
9815 self.buffer.read(cx).text_anchor_for_position(position, cx)
9816 else {
9817 return;
9818 };
9819
9820 cx.spawn(|editor, mut cx| async move {
9821 if let Some((_, url)) = find_url(&buffer, buffer_position, cx.clone()) {
9822 editor.update(&mut cx, |_, cx| {
9823 cx.open_url(&url);
9824 })
9825 } else {
9826 Ok(())
9827 }
9828 })
9829 .detach();
9830 }
9831
9832 pub fn open_file(&mut self, _: &OpenFile, cx: &mut ViewContext<Self>) {
9833 let Some(workspace) = self.workspace() else {
9834 return;
9835 };
9836
9837 let position = self.selections.newest_anchor().head();
9838
9839 let Some((buffer, buffer_position)) =
9840 self.buffer.read(cx).text_anchor_for_position(position, cx)
9841 else {
9842 return;
9843 };
9844
9845 let project = self.project.clone();
9846
9847 cx.spawn(|_, mut cx| async move {
9848 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
9849
9850 if let Some((_, path)) = result {
9851 workspace
9852 .update(&mut cx, |workspace, cx| {
9853 workspace.open_resolved_path(path, cx)
9854 })?
9855 .await?;
9856 }
9857 anyhow::Ok(())
9858 })
9859 .detach();
9860 }
9861
9862 pub(crate) fn navigate_to_hover_links(
9863 &mut self,
9864 kind: Option<GotoDefinitionKind>,
9865 mut definitions: Vec<HoverLink>,
9866 split: bool,
9867 cx: &mut ViewContext<Editor>,
9868 ) -> Task<Result<Navigated>> {
9869 // If there is one definition, just open it directly
9870 if definitions.len() == 1 {
9871 let definition = definitions.pop().unwrap();
9872
9873 enum TargetTaskResult {
9874 Location(Option<Location>),
9875 AlreadyNavigated,
9876 }
9877
9878 let target_task = match definition {
9879 HoverLink::Text(link) => {
9880 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
9881 }
9882 HoverLink::InlayHint(lsp_location, server_id) => {
9883 let computation = self.compute_target_location(lsp_location, server_id, cx);
9884 cx.background_executor().spawn(async move {
9885 let location = computation.await?;
9886 Ok(TargetTaskResult::Location(location))
9887 })
9888 }
9889 HoverLink::Url(url) => {
9890 cx.open_url(&url);
9891 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
9892 }
9893 HoverLink::File(path) => {
9894 if let Some(workspace) = self.workspace() {
9895 cx.spawn(|_, mut cx| async move {
9896 workspace
9897 .update(&mut cx, |workspace, cx| {
9898 workspace.open_resolved_path(path, cx)
9899 })?
9900 .await
9901 .map(|_| TargetTaskResult::AlreadyNavigated)
9902 })
9903 } else {
9904 Task::ready(Ok(TargetTaskResult::Location(None)))
9905 }
9906 }
9907 };
9908 cx.spawn(|editor, mut cx| async move {
9909 let target = match target_task.await.context("target resolution task")? {
9910 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
9911 TargetTaskResult::Location(None) => return Ok(Navigated::No),
9912 TargetTaskResult::Location(Some(target)) => target,
9913 };
9914
9915 editor.update(&mut cx, |editor, cx| {
9916 let Some(workspace) = editor.workspace() else {
9917 return Navigated::No;
9918 };
9919 let pane = workspace.read(cx).active_pane().clone();
9920
9921 let range = target.range.to_offset(target.buffer.read(cx));
9922 let range = editor.range_for_match(&range);
9923
9924 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
9925 let buffer = target.buffer.read(cx);
9926 let range = check_multiline_range(buffer, range);
9927 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
9928 s.select_ranges([range]);
9929 });
9930 } else {
9931 cx.window_context().defer(move |cx| {
9932 let target_editor: View<Self> =
9933 workspace.update(cx, |workspace, cx| {
9934 let pane = if split {
9935 workspace.adjacent_pane(cx)
9936 } else {
9937 workspace.active_pane().clone()
9938 };
9939
9940 workspace.open_project_item(
9941 pane,
9942 target.buffer.clone(),
9943 true,
9944 true,
9945 cx,
9946 )
9947 });
9948 target_editor.update(cx, |target_editor, cx| {
9949 // When selecting a definition in a different buffer, disable the nav history
9950 // to avoid creating a history entry at the previous cursor location.
9951 pane.update(cx, |pane, _| pane.disable_history());
9952 let buffer = target.buffer.read(cx);
9953 let range = check_multiline_range(buffer, range);
9954 target_editor.change_selections(
9955 Some(Autoscroll::focused()),
9956 cx,
9957 |s| {
9958 s.select_ranges([range]);
9959 },
9960 );
9961 pane.update(cx, |pane, _| pane.enable_history());
9962 });
9963 });
9964 }
9965 Navigated::Yes
9966 })
9967 })
9968 } else if !definitions.is_empty() {
9969 cx.spawn(|editor, mut cx| async move {
9970 let (title, location_tasks, workspace) = editor
9971 .update(&mut cx, |editor, cx| {
9972 let tab_kind = match kind {
9973 Some(GotoDefinitionKind::Implementation) => "Implementations",
9974 _ => "Definitions",
9975 };
9976 let title = definitions
9977 .iter()
9978 .find_map(|definition| match definition {
9979 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
9980 let buffer = origin.buffer.read(cx);
9981 format!(
9982 "{} for {}",
9983 tab_kind,
9984 buffer
9985 .text_for_range(origin.range.clone())
9986 .collect::<String>()
9987 )
9988 }),
9989 HoverLink::InlayHint(_, _) => None,
9990 HoverLink::Url(_) => None,
9991 HoverLink::File(_) => None,
9992 })
9993 .unwrap_or(tab_kind.to_string());
9994 let location_tasks = definitions
9995 .into_iter()
9996 .map(|definition| match definition {
9997 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
9998 HoverLink::InlayHint(lsp_location, server_id) => {
9999 editor.compute_target_location(lsp_location, server_id, cx)
10000 }
10001 HoverLink::Url(_) => Task::ready(Ok(None)),
10002 HoverLink::File(_) => Task::ready(Ok(None)),
10003 })
10004 .collect::<Vec<_>>();
10005 (title, location_tasks, editor.workspace().clone())
10006 })
10007 .context("location tasks preparation")?;
10008
10009 let locations = future::join_all(location_tasks)
10010 .await
10011 .into_iter()
10012 .filter_map(|location| location.transpose())
10013 .collect::<Result<_>>()
10014 .context("location tasks")?;
10015
10016 let Some(workspace) = workspace else {
10017 return Ok(Navigated::No);
10018 };
10019 let opened = workspace
10020 .update(&mut cx, |workspace, cx| {
10021 Self::open_locations_in_multibuffer(workspace, locations, title, split, cx)
10022 })
10023 .ok();
10024
10025 anyhow::Ok(Navigated::from_bool(opened.is_some()))
10026 })
10027 } else {
10028 Task::ready(Ok(Navigated::No))
10029 }
10030 }
10031
10032 fn compute_target_location(
10033 &self,
10034 lsp_location: lsp::Location,
10035 server_id: LanguageServerId,
10036 cx: &mut ViewContext<Self>,
10037 ) -> Task<anyhow::Result<Option<Location>>> {
10038 let Some(project) = self.project.clone() else {
10039 return Task::Ready(Some(Ok(None)));
10040 };
10041
10042 cx.spawn(move |editor, mut cx| async move {
10043 let location_task = editor.update(&mut cx, |_, cx| {
10044 project.update(cx, |project, cx| {
10045 let language_server_name = project
10046 .language_server_statuses(cx)
10047 .find(|(id, _)| server_id == *id)
10048 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
10049 language_server_name.map(|language_server_name| {
10050 project.open_local_buffer_via_lsp(
10051 lsp_location.uri.clone(),
10052 server_id,
10053 language_server_name,
10054 cx,
10055 )
10056 })
10057 })
10058 })?;
10059 let location = match location_task {
10060 Some(task) => Some({
10061 let target_buffer_handle = task.await.context("open local buffer")?;
10062 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
10063 let target_start = target_buffer
10064 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
10065 let target_end = target_buffer
10066 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
10067 target_buffer.anchor_after(target_start)
10068 ..target_buffer.anchor_before(target_end)
10069 })?;
10070 Location {
10071 buffer: target_buffer_handle,
10072 range,
10073 }
10074 }),
10075 None => None,
10076 };
10077 Ok(location)
10078 })
10079 }
10080
10081 pub fn find_all_references(
10082 &mut self,
10083 _: &FindAllReferences,
10084 cx: &mut ViewContext<Self>,
10085 ) -> Option<Task<Result<Navigated>>> {
10086 let selection = self.selections.newest::<usize>(cx);
10087 let multi_buffer = self.buffer.read(cx);
10088 let head = selection.head();
10089
10090 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
10091 let head_anchor = multi_buffer_snapshot.anchor_at(
10092 head,
10093 if head < selection.tail() {
10094 Bias::Right
10095 } else {
10096 Bias::Left
10097 },
10098 );
10099
10100 match self
10101 .find_all_references_task_sources
10102 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
10103 {
10104 Ok(_) => {
10105 log::info!(
10106 "Ignoring repeated FindAllReferences invocation with the position of already running task"
10107 );
10108 return None;
10109 }
10110 Err(i) => {
10111 self.find_all_references_task_sources.insert(i, head_anchor);
10112 }
10113 }
10114
10115 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
10116 let workspace = self.workspace()?;
10117 let project = workspace.read(cx).project().clone();
10118 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
10119 Some(cx.spawn(|editor, mut cx| async move {
10120 let _cleanup = defer({
10121 let mut cx = cx.clone();
10122 move || {
10123 let _ = editor.update(&mut cx, |editor, _| {
10124 if let Ok(i) =
10125 editor
10126 .find_all_references_task_sources
10127 .binary_search_by(|anchor| {
10128 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
10129 })
10130 {
10131 editor.find_all_references_task_sources.remove(i);
10132 }
10133 });
10134 }
10135 });
10136
10137 let locations = references.await?;
10138 if locations.is_empty() {
10139 return anyhow::Ok(Navigated::No);
10140 }
10141
10142 workspace.update(&mut cx, |workspace, cx| {
10143 let title = locations
10144 .first()
10145 .as_ref()
10146 .map(|location| {
10147 let buffer = location.buffer.read(cx);
10148 format!(
10149 "References to `{}`",
10150 buffer
10151 .text_for_range(location.range.clone())
10152 .collect::<String>()
10153 )
10154 })
10155 .unwrap();
10156 Self::open_locations_in_multibuffer(workspace, locations, title, false, cx);
10157 Navigated::Yes
10158 })
10159 }))
10160 }
10161
10162 /// Opens a multibuffer with the given project locations in it
10163 pub fn open_locations_in_multibuffer(
10164 workspace: &mut Workspace,
10165 mut locations: Vec<Location>,
10166 title: String,
10167 split: bool,
10168 cx: &mut ViewContext<Workspace>,
10169 ) {
10170 // If there are multiple definitions, open them in a multibuffer
10171 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
10172 let mut locations = locations.into_iter().peekable();
10173 let mut ranges_to_highlight = Vec::new();
10174 let capability = workspace.project().read(cx).capability();
10175
10176 let excerpt_buffer = cx.new_model(|cx| {
10177 let mut multibuffer = MultiBuffer::new(capability);
10178 while let Some(location) = locations.next() {
10179 let buffer = location.buffer.read(cx);
10180 let mut ranges_for_buffer = Vec::new();
10181 let range = location.range.to_offset(buffer);
10182 ranges_for_buffer.push(range.clone());
10183
10184 while let Some(next_location) = locations.peek() {
10185 if next_location.buffer == location.buffer {
10186 ranges_for_buffer.push(next_location.range.to_offset(buffer));
10187 locations.next();
10188 } else {
10189 break;
10190 }
10191 }
10192
10193 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
10194 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
10195 location.buffer.clone(),
10196 ranges_for_buffer,
10197 DEFAULT_MULTIBUFFER_CONTEXT,
10198 cx,
10199 ))
10200 }
10201
10202 multibuffer.with_title(title)
10203 });
10204
10205 let editor = cx.new_view(|cx| {
10206 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), true, cx)
10207 });
10208 editor.update(cx, |editor, cx| {
10209 if let Some(first_range) = ranges_to_highlight.first() {
10210 editor.change_selections(None, cx, |selections| {
10211 selections.clear_disjoint();
10212 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
10213 });
10214 }
10215 editor.highlight_background::<Self>(
10216 &ranges_to_highlight,
10217 |theme| theme.editor_highlighted_line_background,
10218 cx,
10219 );
10220 });
10221
10222 let item = Box::new(editor);
10223 let item_id = item.item_id();
10224
10225 if split {
10226 workspace.split_item(SplitDirection::Right, item.clone(), cx);
10227 } else {
10228 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
10229 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
10230 pane.close_current_preview_item(cx)
10231 } else {
10232 None
10233 }
10234 });
10235 workspace.add_item_to_active_pane(item.clone(), destination_index, true, cx);
10236 }
10237 workspace.active_pane().update(cx, |pane, cx| {
10238 pane.set_preview_item_id(Some(item_id), cx);
10239 });
10240 }
10241
10242 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
10243 use language::ToOffset as _;
10244
10245 let provider = self.semantics_provider.clone()?;
10246 let selection = self.selections.newest_anchor().clone();
10247 let (cursor_buffer, cursor_buffer_position) = self
10248 .buffer
10249 .read(cx)
10250 .text_anchor_for_position(selection.head(), cx)?;
10251 let (tail_buffer, cursor_buffer_position_end) = self
10252 .buffer
10253 .read(cx)
10254 .text_anchor_for_position(selection.tail(), cx)?;
10255 if tail_buffer != cursor_buffer {
10256 return None;
10257 }
10258
10259 let snapshot = cursor_buffer.read(cx).snapshot();
10260 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
10261 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
10262 let prepare_rename = provider
10263 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
10264 .unwrap_or_else(|| Task::ready(Ok(None)));
10265 drop(snapshot);
10266
10267 Some(cx.spawn(|this, mut cx| async move {
10268 let rename_range = if let Some(range) = prepare_rename.await? {
10269 Some(range)
10270 } else {
10271 this.update(&mut cx, |this, cx| {
10272 let buffer = this.buffer.read(cx).snapshot(cx);
10273 let mut buffer_highlights = this
10274 .document_highlights_for_position(selection.head(), &buffer)
10275 .filter(|highlight| {
10276 highlight.start.excerpt_id == selection.head().excerpt_id
10277 && highlight.end.excerpt_id == selection.head().excerpt_id
10278 });
10279 buffer_highlights
10280 .next()
10281 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
10282 })?
10283 };
10284 if let Some(rename_range) = rename_range {
10285 this.update(&mut cx, |this, cx| {
10286 let snapshot = cursor_buffer.read(cx).snapshot();
10287 let rename_buffer_range = rename_range.to_offset(&snapshot);
10288 let cursor_offset_in_rename_range =
10289 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
10290 let cursor_offset_in_rename_range_end =
10291 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
10292
10293 this.take_rename(false, cx);
10294 let buffer = this.buffer.read(cx).read(cx);
10295 let cursor_offset = selection.head().to_offset(&buffer);
10296 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
10297 let rename_end = rename_start + rename_buffer_range.len();
10298 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
10299 let mut old_highlight_id = None;
10300 let old_name: Arc<str> = buffer
10301 .chunks(rename_start..rename_end, true)
10302 .map(|chunk| {
10303 if old_highlight_id.is_none() {
10304 old_highlight_id = chunk.syntax_highlight_id;
10305 }
10306 chunk.text
10307 })
10308 .collect::<String>()
10309 .into();
10310
10311 drop(buffer);
10312
10313 // Position the selection in the rename editor so that it matches the current selection.
10314 this.show_local_selections = false;
10315 let rename_editor = cx.new_view(|cx| {
10316 let mut editor = Editor::single_line(cx);
10317 editor.buffer.update(cx, |buffer, cx| {
10318 buffer.edit([(0..0, old_name.clone())], None, cx)
10319 });
10320 let rename_selection_range = match cursor_offset_in_rename_range
10321 .cmp(&cursor_offset_in_rename_range_end)
10322 {
10323 Ordering::Equal => {
10324 editor.select_all(&SelectAll, cx);
10325 return editor;
10326 }
10327 Ordering::Less => {
10328 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
10329 }
10330 Ordering::Greater => {
10331 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
10332 }
10333 };
10334 if rename_selection_range.end > old_name.len() {
10335 editor.select_all(&SelectAll, cx);
10336 } else {
10337 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
10338 s.select_ranges([rename_selection_range]);
10339 });
10340 }
10341 editor
10342 });
10343 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
10344 if e == &EditorEvent::Focused {
10345 cx.emit(EditorEvent::FocusedIn)
10346 }
10347 })
10348 .detach();
10349
10350 let write_highlights =
10351 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
10352 let read_highlights =
10353 this.clear_background_highlights::<DocumentHighlightRead>(cx);
10354 let ranges = write_highlights
10355 .iter()
10356 .flat_map(|(_, ranges)| ranges.iter())
10357 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
10358 .cloned()
10359 .collect();
10360
10361 this.highlight_text::<Rename>(
10362 ranges,
10363 HighlightStyle {
10364 fade_out: Some(0.6),
10365 ..Default::default()
10366 },
10367 cx,
10368 );
10369 let rename_focus_handle = rename_editor.focus_handle(cx);
10370 cx.focus(&rename_focus_handle);
10371 let block_id = this.insert_blocks(
10372 [BlockProperties {
10373 style: BlockStyle::Flex,
10374 placement: BlockPlacement::Below(range.start),
10375 height: 1,
10376 render: Box::new({
10377 let rename_editor = rename_editor.clone();
10378 move |cx: &mut BlockContext| {
10379 let mut text_style = cx.editor_style.text.clone();
10380 if let Some(highlight_style) = old_highlight_id
10381 .and_then(|h| h.style(&cx.editor_style.syntax))
10382 {
10383 text_style = text_style.highlight(highlight_style);
10384 }
10385 div()
10386 .pl(cx.anchor_x)
10387 .child(EditorElement::new(
10388 &rename_editor,
10389 EditorStyle {
10390 background: cx.theme().system().transparent,
10391 local_player: cx.editor_style.local_player,
10392 text: text_style,
10393 scrollbar_width: cx.editor_style.scrollbar_width,
10394 syntax: cx.editor_style.syntax.clone(),
10395 status: cx.editor_style.status.clone(),
10396 inlay_hints_style: HighlightStyle {
10397 font_weight: Some(FontWeight::BOLD),
10398 ..make_inlay_hints_style(cx)
10399 },
10400 suggestions_style: HighlightStyle {
10401 color: Some(cx.theme().status().predictive),
10402 ..HighlightStyle::default()
10403 },
10404 ..EditorStyle::default()
10405 },
10406 ))
10407 .into_any_element()
10408 }
10409 }),
10410 priority: 0,
10411 }],
10412 Some(Autoscroll::fit()),
10413 cx,
10414 )[0];
10415 this.pending_rename = Some(RenameState {
10416 range,
10417 old_name,
10418 editor: rename_editor,
10419 block_id,
10420 });
10421 })?;
10422 }
10423
10424 Ok(())
10425 }))
10426 }
10427
10428 pub fn confirm_rename(
10429 &mut self,
10430 _: &ConfirmRename,
10431 cx: &mut ViewContext<Self>,
10432 ) -> Option<Task<Result<()>>> {
10433 let rename = self.take_rename(false, cx)?;
10434 let workspace = self.workspace()?.downgrade();
10435 let (buffer, start) = self
10436 .buffer
10437 .read(cx)
10438 .text_anchor_for_position(rename.range.start, cx)?;
10439 let (end_buffer, _) = self
10440 .buffer
10441 .read(cx)
10442 .text_anchor_for_position(rename.range.end, cx)?;
10443 if buffer != end_buffer {
10444 return None;
10445 }
10446
10447 let old_name = rename.old_name;
10448 let new_name = rename.editor.read(cx).text(cx);
10449
10450 let rename = self.semantics_provider.as_ref()?.perform_rename(
10451 &buffer,
10452 start,
10453 new_name.clone(),
10454 cx,
10455 )?;
10456
10457 Some(cx.spawn(|editor, mut cx| async move {
10458 let project_transaction = rename.await?;
10459 Self::open_project_transaction(
10460 &editor,
10461 workspace,
10462 project_transaction,
10463 format!("Rename: {} → {}", old_name, new_name),
10464 cx.clone(),
10465 )
10466 .await?;
10467
10468 editor.update(&mut cx, |editor, cx| {
10469 editor.refresh_document_highlights(cx);
10470 })?;
10471 Ok(())
10472 }))
10473 }
10474
10475 fn take_rename(
10476 &mut self,
10477 moving_cursor: bool,
10478 cx: &mut ViewContext<Self>,
10479 ) -> Option<RenameState> {
10480 let rename = self.pending_rename.take()?;
10481 if rename.editor.focus_handle(cx).is_focused(cx) {
10482 cx.focus(&self.focus_handle);
10483 }
10484
10485 self.remove_blocks(
10486 [rename.block_id].into_iter().collect(),
10487 Some(Autoscroll::fit()),
10488 cx,
10489 );
10490 self.clear_highlights::<Rename>(cx);
10491 self.show_local_selections = true;
10492
10493 if moving_cursor {
10494 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
10495 editor.selections.newest::<usize>(cx).head()
10496 });
10497
10498 // Update the selection to match the position of the selection inside
10499 // the rename editor.
10500 let snapshot = self.buffer.read(cx).read(cx);
10501 let rename_range = rename.range.to_offset(&snapshot);
10502 let cursor_in_editor = snapshot
10503 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
10504 .min(rename_range.end);
10505 drop(snapshot);
10506
10507 self.change_selections(None, cx, |s| {
10508 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
10509 });
10510 } else {
10511 self.refresh_document_highlights(cx);
10512 }
10513
10514 Some(rename)
10515 }
10516
10517 pub fn pending_rename(&self) -> Option<&RenameState> {
10518 self.pending_rename.as_ref()
10519 }
10520
10521 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
10522 let project = match &self.project {
10523 Some(project) => project.clone(),
10524 None => return None,
10525 };
10526
10527 Some(self.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffer, cx))
10528 }
10529
10530 fn format_selections(
10531 &mut self,
10532 _: &FormatSelections,
10533 cx: &mut ViewContext<Self>,
10534 ) -> Option<Task<Result<()>>> {
10535 let project = match &self.project {
10536 Some(project) => project.clone(),
10537 None => return None,
10538 };
10539
10540 let selections = self
10541 .selections
10542 .all_adjusted(cx)
10543 .into_iter()
10544 .filter(|s| !s.is_empty())
10545 .collect_vec();
10546
10547 Some(self.perform_format(
10548 project,
10549 FormatTrigger::Manual,
10550 FormatTarget::Ranges(selections),
10551 cx,
10552 ))
10553 }
10554
10555 fn perform_format(
10556 &mut self,
10557 project: Model<Project>,
10558 trigger: FormatTrigger,
10559 target: FormatTarget,
10560 cx: &mut ViewContext<Self>,
10561 ) -> Task<Result<()>> {
10562 let buffer = self.buffer().clone();
10563 let mut buffers = buffer.read(cx).all_buffers();
10564 if trigger == FormatTrigger::Save {
10565 buffers.retain(|buffer| buffer.read(cx).is_dirty());
10566 }
10567
10568 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
10569 let format = project.update(cx, |project, cx| {
10570 project.format(buffers, true, trigger, target, cx)
10571 });
10572
10573 cx.spawn(|_, mut cx| async move {
10574 let transaction = futures::select_biased! {
10575 () = timeout => {
10576 log::warn!("timed out waiting for formatting");
10577 None
10578 }
10579 transaction = format.log_err().fuse() => transaction,
10580 };
10581
10582 buffer
10583 .update(&mut cx, |buffer, cx| {
10584 if let Some(transaction) = transaction {
10585 if !buffer.is_singleton() {
10586 buffer.push_transaction(&transaction.0, cx);
10587 }
10588 }
10589
10590 cx.notify();
10591 })
10592 .ok();
10593
10594 Ok(())
10595 })
10596 }
10597
10598 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
10599 if let Some(project) = self.project.clone() {
10600 self.buffer.update(cx, |multi_buffer, cx| {
10601 project.update(cx, |project, cx| {
10602 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
10603 });
10604 })
10605 }
10606 }
10607
10608 fn cancel_language_server_work(
10609 &mut self,
10610 _: &actions::CancelLanguageServerWork,
10611 cx: &mut ViewContext<Self>,
10612 ) {
10613 if let Some(project) = self.project.clone() {
10614 self.buffer.update(cx, |multi_buffer, cx| {
10615 project.update(cx, |project, cx| {
10616 project.cancel_language_server_work_for_buffers(multi_buffer.all_buffers(), cx);
10617 });
10618 })
10619 }
10620 }
10621
10622 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
10623 cx.show_character_palette();
10624 }
10625
10626 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
10627 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
10628 let buffer = self.buffer.read(cx).snapshot(cx);
10629 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
10630 let is_valid = buffer
10631 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
10632 .any(|entry| {
10633 entry.diagnostic.is_primary
10634 && !entry.range.is_empty()
10635 && entry.range.start == primary_range_start
10636 && entry.diagnostic.message == active_diagnostics.primary_message
10637 });
10638
10639 if is_valid != active_diagnostics.is_valid {
10640 active_diagnostics.is_valid = is_valid;
10641 let mut new_styles = HashMap::default();
10642 for (block_id, diagnostic) in &active_diagnostics.blocks {
10643 new_styles.insert(
10644 *block_id,
10645 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
10646 );
10647 }
10648 self.display_map.update(cx, |display_map, _cx| {
10649 display_map.replace_blocks(new_styles)
10650 });
10651 }
10652 }
10653 }
10654
10655 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
10656 self.dismiss_diagnostics(cx);
10657 let snapshot = self.snapshot(cx);
10658 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
10659 let buffer = self.buffer.read(cx).snapshot(cx);
10660
10661 let mut primary_range = None;
10662 let mut primary_message = None;
10663 let mut group_end = Point::zero();
10664 let diagnostic_group = buffer
10665 .diagnostic_group::<MultiBufferPoint>(group_id)
10666 .filter_map(|entry| {
10667 if snapshot.is_line_folded(MultiBufferRow(entry.range.start.row))
10668 && (entry.range.start.row == entry.range.end.row
10669 || snapshot.is_line_folded(MultiBufferRow(entry.range.end.row)))
10670 {
10671 return None;
10672 }
10673 if entry.range.end > group_end {
10674 group_end = entry.range.end;
10675 }
10676 if entry.diagnostic.is_primary {
10677 primary_range = Some(entry.range.clone());
10678 primary_message = Some(entry.diagnostic.message.clone());
10679 }
10680 Some(entry)
10681 })
10682 .collect::<Vec<_>>();
10683 let primary_range = primary_range?;
10684 let primary_message = primary_message?;
10685 let primary_range =
10686 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
10687
10688 let blocks = display_map
10689 .insert_blocks(
10690 diagnostic_group.iter().map(|entry| {
10691 let diagnostic = entry.diagnostic.clone();
10692 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
10693 BlockProperties {
10694 style: BlockStyle::Fixed,
10695 placement: BlockPlacement::Below(
10696 buffer.anchor_after(entry.range.start),
10697 ),
10698 height: message_height,
10699 render: diagnostic_block_renderer(diagnostic, None, true, true),
10700 priority: 0,
10701 }
10702 }),
10703 cx,
10704 )
10705 .into_iter()
10706 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
10707 .collect();
10708
10709 Some(ActiveDiagnosticGroup {
10710 primary_range,
10711 primary_message,
10712 group_id,
10713 blocks,
10714 is_valid: true,
10715 })
10716 });
10717 self.active_diagnostics.is_some()
10718 }
10719
10720 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
10721 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
10722 self.display_map.update(cx, |display_map, cx| {
10723 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
10724 });
10725 cx.notify();
10726 }
10727 }
10728
10729 pub fn set_selections_from_remote(
10730 &mut self,
10731 selections: Vec<Selection<Anchor>>,
10732 pending_selection: Option<Selection<Anchor>>,
10733 cx: &mut ViewContext<Self>,
10734 ) {
10735 let old_cursor_position = self.selections.newest_anchor().head();
10736 self.selections.change_with(cx, |s| {
10737 s.select_anchors(selections);
10738 if let Some(pending_selection) = pending_selection {
10739 s.set_pending(pending_selection, SelectMode::Character);
10740 } else {
10741 s.clear_pending();
10742 }
10743 });
10744 self.selections_did_change(false, &old_cursor_position, true, cx);
10745 }
10746
10747 fn push_to_selection_history(&mut self) {
10748 self.selection_history.push(SelectionHistoryEntry {
10749 selections: self.selections.disjoint_anchors(),
10750 select_next_state: self.select_next_state.clone(),
10751 select_prev_state: self.select_prev_state.clone(),
10752 add_selections_state: self.add_selections_state.clone(),
10753 });
10754 }
10755
10756 pub fn transact(
10757 &mut self,
10758 cx: &mut ViewContext<Self>,
10759 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
10760 ) -> Option<TransactionId> {
10761 self.start_transaction_at(Instant::now(), cx);
10762 update(self, cx);
10763 self.end_transaction_at(Instant::now(), cx)
10764 }
10765
10766 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
10767 self.end_selection(cx);
10768 if let Some(tx_id) = self
10769 .buffer
10770 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
10771 {
10772 self.selection_history
10773 .insert_transaction(tx_id, self.selections.disjoint_anchors());
10774 cx.emit(EditorEvent::TransactionBegun {
10775 transaction_id: tx_id,
10776 })
10777 }
10778 }
10779
10780 fn end_transaction_at(
10781 &mut self,
10782 now: Instant,
10783 cx: &mut ViewContext<Self>,
10784 ) -> Option<TransactionId> {
10785 if let Some(transaction_id) = self
10786 .buffer
10787 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
10788 {
10789 if let Some((_, end_selections)) =
10790 self.selection_history.transaction_mut(transaction_id)
10791 {
10792 *end_selections = Some(self.selections.disjoint_anchors());
10793 } else {
10794 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
10795 }
10796
10797 cx.emit(EditorEvent::Edited { transaction_id });
10798 Some(transaction_id)
10799 } else {
10800 None
10801 }
10802 }
10803
10804 pub fn toggle_fold(&mut self, _: &actions::ToggleFold, cx: &mut ViewContext<Self>) {
10805 let selection = self.selections.newest::<Point>(cx);
10806
10807 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10808 let range = if selection.is_empty() {
10809 let point = selection.head().to_display_point(&display_map);
10810 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
10811 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
10812 .to_point(&display_map);
10813 start..end
10814 } else {
10815 selection.range()
10816 };
10817 if display_map.folds_in_range(range).next().is_some() {
10818 self.unfold_lines(&Default::default(), cx)
10819 } else {
10820 self.fold(&Default::default(), cx)
10821 }
10822 }
10823
10824 pub fn toggle_fold_recursive(
10825 &mut self,
10826 _: &actions::ToggleFoldRecursive,
10827 cx: &mut ViewContext<Self>,
10828 ) {
10829 let selection = self.selections.newest::<Point>(cx);
10830
10831 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10832 let range = if selection.is_empty() {
10833 let point = selection.head().to_display_point(&display_map);
10834 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
10835 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
10836 .to_point(&display_map);
10837 start..end
10838 } else {
10839 selection.range()
10840 };
10841 if display_map.folds_in_range(range).next().is_some() {
10842 self.unfold_recursive(&Default::default(), cx)
10843 } else {
10844 self.fold_recursive(&Default::default(), cx)
10845 }
10846 }
10847
10848 pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
10849 let mut fold_ranges = Vec::new();
10850 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10851 let selections = self.selections.all_adjusted(cx);
10852
10853 for selection in selections {
10854 let range = selection.range().sorted();
10855 let buffer_start_row = range.start.row;
10856
10857 if range.start.row != range.end.row {
10858 let mut found = false;
10859 let mut row = range.start.row;
10860 while row <= range.end.row {
10861 if let Some((foldable_range, fold_text)) =
10862 { display_map.foldable_range(MultiBufferRow(row)) }
10863 {
10864 found = true;
10865 row = foldable_range.end.row + 1;
10866 fold_ranges.push((foldable_range, fold_text));
10867 } else {
10868 row += 1
10869 }
10870 }
10871 if found {
10872 continue;
10873 }
10874 }
10875
10876 for row in (0..=range.start.row).rev() {
10877 if let Some((foldable_range, fold_text)) =
10878 display_map.foldable_range(MultiBufferRow(row))
10879 {
10880 if foldable_range.end.row >= buffer_start_row {
10881 fold_ranges.push((foldable_range, fold_text));
10882 if row <= range.start.row {
10883 break;
10884 }
10885 }
10886 }
10887 }
10888 }
10889
10890 self.fold_ranges(fold_ranges, true, cx);
10891 }
10892
10893 fn fold_at_level(&mut self, fold_at: &FoldAtLevel, cx: &mut ViewContext<Self>) {
10894 let fold_at_level = fold_at.level;
10895 let snapshot = self.buffer.read(cx).snapshot(cx);
10896 let mut fold_ranges = Vec::new();
10897 let mut stack = vec![(0, snapshot.max_buffer_row().0, 1)];
10898
10899 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
10900 while start_row < end_row {
10901 match self.snapshot(cx).foldable_range(MultiBufferRow(start_row)) {
10902 Some(foldable_range) => {
10903 let nested_start_row = foldable_range.0.start.row + 1;
10904 let nested_end_row = foldable_range.0.end.row;
10905
10906 if current_level < fold_at_level {
10907 stack.push((nested_start_row, nested_end_row, current_level + 1));
10908 } else if current_level == fold_at_level {
10909 fold_ranges.push(foldable_range);
10910 }
10911
10912 start_row = nested_end_row + 1;
10913 }
10914 None => start_row += 1,
10915 }
10916 }
10917 }
10918
10919 self.fold_ranges(fold_ranges, true, cx);
10920 }
10921
10922 pub fn fold_all(&mut self, _: &actions::FoldAll, cx: &mut ViewContext<Self>) {
10923 let mut fold_ranges = Vec::new();
10924 let snapshot = self.buffer.read(cx).snapshot(cx);
10925
10926 for row in 0..snapshot.max_buffer_row().0 {
10927 if let Some(foldable_range) = self.snapshot(cx).foldable_range(MultiBufferRow(row)) {
10928 fold_ranges.push(foldable_range);
10929 }
10930 }
10931
10932 self.fold_ranges(fold_ranges, true, cx);
10933 }
10934
10935 pub fn fold_recursive(&mut self, _: &actions::FoldRecursive, cx: &mut ViewContext<Self>) {
10936 let mut fold_ranges = Vec::new();
10937 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10938 let selections = self.selections.all_adjusted(cx);
10939
10940 for selection in selections {
10941 let range = selection.range().sorted();
10942 let buffer_start_row = range.start.row;
10943
10944 if range.start.row != range.end.row {
10945 let mut found = false;
10946 for row in range.start.row..=range.end.row {
10947 if let Some((foldable_range, fold_text)) =
10948 { display_map.foldable_range(MultiBufferRow(row)) }
10949 {
10950 found = true;
10951 fold_ranges.push((foldable_range, fold_text));
10952 }
10953 }
10954 if found {
10955 continue;
10956 }
10957 }
10958
10959 for row in (0..=range.start.row).rev() {
10960 if let Some((foldable_range, fold_text)) =
10961 display_map.foldable_range(MultiBufferRow(row))
10962 {
10963 if foldable_range.end.row >= buffer_start_row {
10964 fold_ranges.push((foldable_range, fold_text));
10965 } else {
10966 break;
10967 }
10968 }
10969 }
10970 }
10971
10972 self.fold_ranges(fold_ranges, true, cx);
10973 }
10974
10975 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
10976 let buffer_row = fold_at.buffer_row;
10977 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10978
10979 if let Some((fold_range, placeholder)) = display_map.foldable_range(buffer_row) {
10980 let autoscroll = self
10981 .selections
10982 .all::<Point>(cx)
10983 .iter()
10984 .any(|selection| fold_range.overlaps(&selection.range()));
10985
10986 self.fold_ranges([(fold_range, placeholder)], autoscroll, cx);
10987 }
10988 }
10989
10990 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
10991 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10992 let buffer = &display_map.buffer_snapshot;
10993 let selections = self.selections.all::<Point>(cx);
10994 let ranges = selections
10995 .iter()
10996 .map(|s| {
10997 let range = s.display_range(&display_map).sorted();
10998 let mut start = range.start.to_point(&display_map);
10999 let mut end = range.end.to_point(&display_map);
11000 start.column = 0;
11001 end.column = buffer.line_len(MultiBufferRow(end.row));
11002 start..end
11003 })
11004 .collect::<Vec<_>>();
11005
11006 self.unfold_ranges(&ranges, true, true, cx);
11007 }
11008
11009 pub fn unfold_recursive(&mut self, _: &UnfoldRecursive, cx: &mut ViewContext<Self>) {
11010 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11011 let selections = self.selections.all::<Point>(cx);
11012 let ranges = selections
11013 .iter()
11014 .map(|s| {
11015 let mut range = s.display_range(&display_map).sorted();
11016 *range.start.column_mut() = 0;
11017 *range.end.column_mut() = display_map.line_len(range.end.row());
11018 let start = range.start.to_point(&display_map);
11019 let end = range.end.to_point(&display_map);
11020 start..end
11021 })
11022 .collect::<Vec<_>>();
11023
11024 self.unfold_ranges(&ranges, true, true, cx);
11025 }
11026
11027 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
11028 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11029
11030 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
11031 ..Point::new(
11032 unfold_at.buffer_row.0,
11033 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
11034 );
11035
11036 let autoscroll = self
11037 .selections
11038 .all::<Point>(cx)
11039 .iter()
11040 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
11041
11042 self.unfold_ranges(&[intersection_range], true, autoscroll, cx)
11043 }
11044
11045 pub fn unfold_all(&mut self, _: &actions::UnfoldAll, cx: &mut ViewContext<Self>) {
11046 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11047 self.unfold_ranges(
11048 &[Point::zero()..display_map.max_point().to_point(&display_map)],
11049 true,
11050 true,
11051 cx,
11052 );
11053 }
11054
11055 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
11056 let selections = self.selections.all::<Point>(cx);
11057 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11058 let line_mode = self.selections.line_mode;
11059 let ranges = selections.into_iter().map(|s| {
11060 if line_mode {
11061 let start = Point::new(s.start.row, 0);
11062 let end = Point::new(
11063 s.end.row,
11064 display_map
11065 .buffer_snapshot
11066 .line_len(MultiBufferRow(s.end.row)),
11067 );
11068 (start..end, display_map.fold_placeholder.clone())
11069 } else {
11070 (s.start..s.end, display_map.fold_placeholder.clone())
11071 }
11072 });
11073 self.fold_ranges(ranges, true, cx);
11074 }
11075
11076 pub fn fold_ranges<T: ToOffset + Clone>(
11077 &mut self,
11078 ranges: impl IntoIterator<Item = (Range<T>, FoldPlaceholder)>,
11079 auto_scroll: bool,
11080 cx: &mut ViewContext<Self>,
11081 ) {
11082 let mut fold_ranges = Vec::new();
11083 let mut buffers_affected = HashMap::default();
11084 let multi_buffer = self.buffer().read(cx);
11085 for (fold_range, fold_text) in ranges {
11086 if let Some((_, buffer, _)) =
11087 multi_buffer.excerpt_containing(fold_range.start.clone(), cx)
11088 {
11089 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
11090 };
11091 fold_ranges.push((fold_range, fold_text));
11092 }
11093
11094 let mut ranges = fold_ranges.into_iter().peekable();
11095 if ranges.peek().is_some() {
11096 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
11097
11098 if auto_scroll {
11099 self.request_autoscroll(Autoscroll::fit(), cx);
11100 }
11101
11102 for buffer in buffers_affected.into_values() {
11103 self.sync_expanded_diff_hunks(buffer, cx);
11104 }
11105
11106 cx.notify();
11107
11108 if let Some(active_diagnostics) = self.active_diagnostics.take() {
11109 // Clear diagnostics block when folding a range that contains it.
11110 let snapshot = self.snapshot(cx);
11111 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
11112 drop(snapshot);
11113 self.active_diagnostics = Some(active_diagnostics);
11114 self.dismiss_diagnostics(cx);
11115 } else {
11116 self.active_diagnostics = Some(active_diagnostics);
11117 }
11118 }
11119
11120 self.scrollbar_marker_state.dirty = true;
11121 }
11122 }
11123
11124 /// Removes any folds whose ranges intersect any of the given ranges.
11125 pub fn unfold_ranges<T: ToOffset + Clone>(
11126 &mut self,
11127 ranges: &[Range<T>],
11128 inclusive: bool,
11129 auto_scroll: bool,
11130 cx: &mut ViewContext<Self>,
11131 ) {
11132 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
11133 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
11134 });
11135 }
11136
11137 /// Removes any folds with the given ranges.
11138 pub fn remove_folds_with_type<T: ToOffset + Clone>(
11139 &mut self,
11140 ranges: &[Range<T>],
11141 type_id: TypeId,
11142 auto_scroll: bool,
11143 cx: &mut ViewContext<Self>,
11144 ) {
11145 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
11146 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
11147 });
11148 }
11149
11150 fn remove_folds_with<T: ToOffset + Clone>(
11151 &mut self,
11152 ranges: &[Range<T>],
11153 auto_scroll: bool,
11154 cx: &mut ViewContext<Self>,
11155 update: impl FnOnce(&mut DisplayMap, &mut ModelContext<DisplayMap>),
11156 ) {
11157 if ranges.is_empty() {
11158 return;
11159 }
11160
11161 let mut buffers_affected = HashMap::default();
11162 let multi_buffer = self.buffer().read(cx);
11163 for range in ranges {
11164 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
11165 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
11166 };
11167 }
11168
11169 self.display_map.update(cx, update);
11170 if auto_scroll {
11171 self.request_autoscroll(Autoscroll::fit(), cx);
11172 }
11173
11174 for buffer in buffers_affected.into_values() {
11175 self.sync_expanded_diff_hunks(buffer, cx);
11176 }
11177
11178 cx.notify();
11179 self.scrollbar_marker_state.dirty = true;
11180 self.active_indent_guides_state.dirty = true;
11181 }
11182
11183 pub fn default_fold_placeholder(&self, cx: &AppContext) -> FoldPlaceholder {
11184 self.display_map.read(cx).fold_placeholder.clone()
11185 }
11186
11187 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
11188 if hovered != self.gutter_hovered {
11189 self.gutter_hovered = hovered;
11190 cx.notify();
11191 }
11192 }
11193
11194 pub fn insert_blocks(
11195 &mut self,
11196 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
11197 autoscroll: Option<Autoscroll>,
11198 cx: &mut ViewContext<Self>,
11199 ) -> Vec<CustomBlockId> {
11200 let blocks = self
11201 .display_map
11202 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
11203 if let Some(autoscroll) = autoscroll {
11204 self.request_autoscroll(autoscroll, cx);
11205 }
11206 cx.notify();
11207 blocks
11208 }
11209
11210 pub fn resize_blocks(
11211 &mut self,
11212 heights: HashMap<CustomBlockId, u32>,
11213 autoscroll: Option<Autoscroll>,
11214 cx: &mut ViewContext<Self>,
11215 ) {
11216 self.display_map
11217 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
11218 if let Some(autoscroll) = autoscroll {
11219 self.request_autoscroll(autoscroll, cx);
11220 }
11221 cx.notify();
11222 }
11223
11224 pub fn replace_blocks(
11225 &mut self,
11226 renderers: HashMap<CustomBlockId, RenderBlock>,
11227 autoscroll: Option<Autoscroll>,
11228 cx: &mut ViewContext<Self>,
11229 ) {
11230 self.display_map
11231 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
11232 if let Some(autoscroll) = autoscroll {
11233 self.request_autoscroll(autoscroll, cx);
11234 }
11235 cx.notify();
11236 }
11237
11238 pub fn remove_blocks(
11239 &mut self,
11240 block_ids: HashSet<CustomBlockId>,
11241 autoscroll: Option<Autoscroll>,
11242 cx: &mut ViewContext<Self>,
11243 ) {
11244 self.display_map.update(cx, |display_map, cx| {
11245 display_map.remove_blocks(block_ids, cx)
11246 });
11247 if let Some(autoscroll) = autoscroll {
11248 self.request_autoscroll(autoscroll, cx);
11249 }
11250 cx.notify();
11251 }
11252
11253 pub fn row_for_block(
11254 &self,
11255 block_id: CustomBlockId,
11256 cx: &mut ViewContext<Self>,
11257 ) -> Option<DisplayRow> {
11258 self.display_map
11259 .update(cx, |map, cx| map.row_for_block(block_id, cx))
11260 }
11261
11262 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
11263 self.focused_block = Some(focused_block);
11264 }
11265
11266 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
11267 self.focused_block.take()
11268 }
11269
11270 pub fn insert_creases(
11271 &mut self,
11272 creases: impl IntoIterator<Item = Crease>,
11273 cx: &mut ViewContext<Self>,
11274 ) -> Vec<CreaseId> {
11275 self.display_map
11276 .update(cx, |map, cx| map.insert_creases(creases, cx))
11277 }
11278
11279 pub fn remove_creases(
11280 &mut self,
11281 ids: impl IntoIterator<Item = CreaseId>,
11282 cx: &mut ViewContext<Self>,
11283 ) {
11284 self.display_map
11285 .update(cx, |map, cx| map.remove_creases(ids, cx));
11286 }
11287
11288 pub fn longest_row(&self, cx: &mut AppContext) -> DisplayRow {
11289 self.display_map
11290 .update(cx, |map, cx| map.snapshot(cx))
11291 .longest_row()
11292 }
11293
11294 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
11295 self.display_map
11296 .update(cx, |map, cx| map.snapshot(cx))
11297 .max_point()
11298 }
11299
11300 pub fn text(&self, cx: &AppContext) -> String {
11301 self.buffer.read(cx).read(cx).text()
11302 }
11303
11304 pub fn text_option(&self, cx: &AppContext) -> Option<String> {
11305 let text = self.text(cx);
11306 let text = text.trim();
11307
11308 if text.is_empty() {
11309 return None;
11310 }
11311
11312 Some(text.to_string())
11313 }
11314
11315 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
11316 self.transact(cx, |this, cx| {
11317 this.buffer
11318 .read(cx)
11319 .as_singleton()
11320 .expect("you can only call set_text on editors for singleton buffers")
11321 .update(cx, |buffer, cx| buffer.set_text(text, cx));
11322 });
11323 }
11324
11325 pub fn display_text(&self, cx: &mut AppContext) -> String {
11326 self.display_map
11327 .update(cx, |map, cx| map.snapshot(cx))
11328 .text()
11329 }
11330
11331 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
11332 let mut wrap_guides = smallvec::smallvec![];
11333
11334 if self.show_wrap_guides == Some(false) {
11335 return wrap_guides;
11336 }
11337
11338 let settings = self.buffer.read(cx).settings_at(0, cx);
11339 if settings.show_wrap_guides {
11340 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
11341 wrap_guides.push((soft_wrap as usize, true));
11342 } else if let SoftWrap::Bounded(soft_wrap) = self.soft_wrap_mode(cx) {
11343 wrap_guides.push((soft_wrap as usize, true));
11344 }
11345 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
11346 }
11347
11348 wrap_guides
11349 }
11350
11351 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
11352 let settings = self.buffer.read(cx).settings_at(0, cx);
11353 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
11354 match mode {
11355 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
11356 SoftWrap::None
11357 }
11358 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
11359 language_settings::SoftWrap::PreferredLineLength => {
11360 SoftWrap::Column(settings.preferred_line_length)
11361 }
11362 language_settings::SoftWrap::Bounded => {
11363 SoftWrap::Bounded(settings.preferred_line_length)
11364 }
11365 }
11366 }
11367
11368 pub fn set_soft_wrap_mode(
11369 &mut self,
11370 mode: language_settings::SoftWrap,
11371 cx: &mut ViewContext<Self>,
11372 ) {
11373 self.soft_wrap_mode_override = Some(mode);
11374 cx.notify();
11375 }
11376
11377 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
11378 self.text_style_refinement = Some(style);
11379 }
11380
11381 /// called by the Element so we know what style we were most recently rendered with.
11382 pub(crate) fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
11383 let rem_size = cx.rem_size();
11384 self.display_map.update(cx, |map, cx| {
11385 map.set_font(
11386 style.text.font(),
11387 style.text.font_size.to_pixels(rem_size),
11388 cx,
11389 )
11390 });
11391 self.style = Some(style);
11392 }
11393
11394 pub fn style(&self) -> Option<&EditorStyle> {
11395 self.style.as_ref()
11396 }
11397
11398 // Called by the element. This method is not designed to be called outside of the editor
11399 // element's layout code because it does not notify when rewrapping is computed synchronously.
11400 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
11401 self.display_map
11402 .update(cx, |map, cx| map.set_wrap_width(width, cx))
11403 }
11404
11405 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
11406 if self.soft_wrap_mode_override.is_some() {
11407 self.soft_wrap_mode_override.take();
11408 } else {
11409 let soft_wrap = match self.soft_wrap_mode(cx) {
11410 SoftWrap::GitDiff => return,
11411 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
11412 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
11413 language_settings::SoftWrap::None
11414 }
11415 };
11416 self.soft_wrap_mode_override = Some(soft_wrap);
11417 }
11418 cx.notify();
11419 }
11420
11421 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, cx: &mut ViewContext<Self>) {
11422 let Some(workspace) = self.workspace() else {
11423 return;
11424 };
11425 let fs = workspace.read(cx).app_state().fs.clone();
11426 let current_show = TabBarSettings::get_global(cx).show;
11427 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
11428 setting.show = Some(!current_show);
11429 });
11430 }
11431
11432 pub fn toggle_indent_guides(&mut self, _: &ToggleIndentGuides, cx: &mut ViewContext<Self>) {
11433 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
11434 self.buffer
11435 .read(cx)
11436 .settings_at(0, cx)
11437 .indent_guides
11438 .enabled
11439 });
11440 self.show_indent_guides = Some(!currently_enabled);
11441 cx.notify();
11442 }
11443
11444 fn should_show_indent_guides(&self) -> Option<bool> {
11445 self.show_indent_guides
11446 }
11447
11448 pub fn toggle_line_numbers(&mut self, _: &ToggleLineNumbers, cx: &mut ViewContext<Self>) {
11449 let mut editor_settings = EditorSettings::get_global(cx).clone();
11450 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
11451 EditorSettings::override_global(editor_settings, cx);
11452 }
11453
11454 pub fn should_use_relative_line_numbers(&self, cx: &WindowContext) -> bool {
11455 self.use_relative_line_numbers
11456 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
11457 }
11458
11459 pub fn toggle_relative_line_numbers(
11460 &mut self,
11461 _: &ToggleRelativeLineNumbers,
11462 cx: &mut ViewContext<Self>,
11463 ) {
11464 let is_relative = self.should_use_relative_line_numbers(cx);
11465 self.set_relative_line_number(Some(!is_relative), cx)
11466 }
11467
11468 pub fn set_relative_line_number(
11469 &mut self,
11470 is_relative: Option<bool>,
11471 cx: &mut ViewContext<Self>,
11472 ) {
11473 self.use_relative_line_numbers = is_relative;
11474 cx.notify();
11475 }
11476
11477 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
11478 self.show_gutter = show_gutter;
11479 cx.notify();
11480 }
11481
11482 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut ViewContext<Self>) {
11483 self.show_line_numbers = Some(show_line_numbers);
11484 cx.notify();
11485 }
11486
11487 pub fn set_show_git_diff_gutter(
11488 &mut self,
11489 show_git_diff_gutter: bool,
11490 cx: &mut ViewContext<Self>,
11491 ) {
11492 self.show_git_diff_gutter = Some(show_git_diff_gutter);
11493 cx.notify();
11494 }
11495
11496 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut ViewContext<Self>) {
11497 self.show_code_actions = Some(show_code_actions);
11498 cx.notify();
11499 }
11500
11501 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut ViewContext<Self>) {
11502 self.show_runnables = Some(show_runnables);
11503 cx.notify();
11504 }
11505
11506 pub fn set_masked(&mut self, masked: bool, cx: &mut ViewContext<Self>) {
11507 if self.display_map.read(cx).masked != masked {
11508 self.display_map.update(cx, |map, _| map.masked = masked);
11509 }
11510 cx.notify()
11511 }
11512
11513 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut ViewContext<Self>) {
11514 self.show_wrap_guides = Some(show_wrap_guides);
11515 cx.notify();
11516 }
11517
11518 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut ViewContext<Self>) {
11519 self.show_indent_guides = Some(show_indent_guides);
11520 cx.notify();
11521 }
11522
11523 pub fn working_directory(&self, cx: &WindowContext) -> Option<PathBuf> {
11524 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
11525 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
11526 if let Some(dir) = file.abs_path(cx).parent() {
11527 return Some(dir.to_owned());
11528 }
11529 }
11530
11531 if let Some(project_path) = buffer.read(cx).project_path(cx) {
11532 return Some(project_path.path.to_path_buf());
11533 }
11534 }
11535
11536 None
11537 }
11538
11539 fn target_file<'a>(&self, cx: &'a AppContext) -> Option<&'a dyn language::LocalFile> {
11540 self.active_excerpt(cx)?
11541 .1
11542 .read(cx)
11543 .file()
11544 .and_then(|f| f.as_local())
11545 }
11546
11547 pub fn reveal_in_finder(&mut self, _: &RevealInFileManager, cx: &mut ViewContext<Self>) {
11548 if let Some(target) = self.target_file(cx) {
11549 cx.reveal_path(&target.abs_path(cx));
11550 }
11551 }
11552
11553 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
11554 if let Some(file) = self.target_file(cx) {
11555 if let Some(path) = file.abs_path(cx).to_str() {
11556 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
11557 }
11558 }
11559 }
11560
11561 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
11562 if let Some(file) = self.target_file(cx) {
11563 if let Some(path) = file.path().to_str() {
11564 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
11565 }
11566 }
11567 }
11568
11569 pub fn toggle_git_blame(&mut self, _: &ToggleGitBlame, cx: &mut ViewContext<Self>) {
11570 self.show_git_blame_gutter = !self.show_git_blame_gutter;
11571
11572 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
11573 self.start_git_blame(true, cx);
11574 }
11575
11576 cx.notify();
11577 }
11578
11579 pub fn toggle_git_blame_inline(
11580 &mut self,
11581 _: &ToggleGitBlameInline,
11582 cx: &mut ViewContext<Self>,
11583 ) {
11584 self.toggle_git_blame_inline_internal(true, cx);
11585 cx.notify();
11586 }
11587
11588 pub fn git_blame_inline_enabled(&self) -> bool {
11589 self.git_blame_inline_enabled
11590 }
11591
11592 pub fn toggle_selection_menu(&mut self, _: &ToggleSelectionMenu, cx: &mut ViewContext<Self>) {
11593 self.show_selection_menu = self
11594 .show_selection_menu
11595 .map(|show_selections_menu| !show_selections_menu)
11596 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
11597
11598 cx.notify();
11599 }
11600
11601 pub fn selection_menu_enabled(&self, cx: &AppContext) -> bool {
11602 self.show_selection_menu
11603 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
11604 }
11605
11606 fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
11607 if let Some(project) = self.project.as_ref() {
11608 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
11609 return;
11610 };
11611
11612 if buffer.read(cx).file().is_none() {
11613 return;
11614 }
11615
11616 let focused = self.focus_handle(cx).contains_focused(cx);
11617
11618 let project = project.clone();
11619 let blame =
11620 cx.new_model(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
11621 self.blame_subscription = Some(cx.observe(&blame, |_, _, cx| cx.notify()));
11622 self.blame = Some(blame);
11623 }
11624 }
11625
11626 fn toggle_git_blame_inline_internal(
11627 &mut self,
11628 user_triggered: bool,
11629 cx: &mut ViewContext<Self>,
11630 ) {
11631 if self.git_blame_inline_enabled {
11632 self.git_blame_inline_enabled = false;
11633 self.show_git_blame_inline = false;
11634 self.show_git_blame_inline_delay_task.take();
11635 } else {
11636 self.git_blame_inline_enabled = true;
11637 self.start_git_blame_inline(user_triggered, cx);
11638 }
11639
11640 cx.notify();
11641 }
11642
11643 fn start_git_blame_inline(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
11644 self.start_git_blame(user_triggered, cx);
11645
11646 if ProjectSettings::get_global(cx)
11647 .git
11648 .inline_blame_delay()
11649 .is_some()
11650 {
11651 self.start_inline_blame_timer(cx);
11652 } else {
11653 self.show_git_blame_inline = true
11654 }
11655 }
11656
11657 pub fn blame(&self) -> Option<&Model<GitBlame>> {
11658 self.blame.as_ref()
11659 }
11660
11661 pub fn render_git_blame_gutter(&mut self, cx: &mut WindowContext) -> bool {
11662 self.show_git_blame_gutter && self.has_blame_entries(cx)
11663 }
11664
11665 pub fn render_git_blame_inline(&mut self, cx: &mut WindowContext) -> bool {
11666 self.show_git_blame_inline
11667 && self.focus_handle.is_focused(cx)
11668 && !self.newest_selection_head_on_empty_line(cx)
11669 && self.has_blame_entries(cx)
11670 }
11671
11672 fn has_blame_entries(&self, cx: &mut WindowContext) -> bool {
11673 self.blame()
11674 .map_or(false, |blame| blame.read(cx).has_generated_entries())
11675 }
11676
11677 fn newest_selection_head_on_empty_line(&mut self, cx: &mut WindowContext) -> bool {
11678 let cursor_anchor = self.selections.newest_anchor().head();
11679
11680 let snapshot = self.buffer.read(cx).snapshot(cx);
11681 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
11682
11683 snapshot.line_len(buffer_row) == 0
11684 }
11685
11686 fn get_permalink_to_line(&mut self, cx: &mut ViewContext<Self>) -> Task<Result<url::Url>> {
11687 let buffer_and_selection = maybe!({
11688 let selection = self.selections.newest::<Point>(cx);
11689 let selection_range = selection.range();
11690
11691 let (buffer, selection) = if let Some(buffer) = self.buffer().read(cx).as_singleton() {
11692 (buffer, selection_range.start.row..selection_range.end.row)
11693 } else {
11694 let buffer_ranges = self
11695 .buffer()
11696 .read(cx)
11697 .range_to_buffer_ranges(selection_range, cx);
11698
11699 let (buffer, range, _) = if selection.reversed {
11700 buffer_ranges.first()
11701 } else {
11702 buffer_ranges.last()
11703 }?;
11704
11705 let snapshot = buffer.read(cx).snapshot();
11706 let selection = text::ToPoint::to_point(&range.start, &snapshot).row
11707 ..text::ToPoint::to_point(&range.end, &snapshot).row;
11708 (buffer.clone(), selection)
11709 };
11710
11711 Some((buffer, selection))
11712 });
11713
11714 let Some((buffer, selection)) = buffer_and_selection else {
11715 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
11716 };
11717
11718 let Some(project) = self.project.as_ref() else {
11719 return Task::ready(Err(anyhow!("editor does not have project")));
11720 };
11721
11722 project.update(cx, |project, cx| {
11723 project.get_permalink_to_line(&buffer, selection, cx)
11724 })
11725 }
11726
11727 pub fn copy_permalink_to_line(&mut self, _: &CopyPermalinkToLine, cx: &mut ViewContext<Self>) {
11728 let permalink_task = self.get_permalink_to_line(cx);
11729 let workspace = self.workspace();
11730
11731 cx.spawn(|_, mut cx| async move {
11732 match permalink_task.await {
11733 Ok(permalink) => {
11734 cx.update(|cx| {
11735 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
11736 })
11737 .ok();
11738 }
11739 Err(err) => {
11740 let message = format!("Failed to copy permalink: {err}");
11741
11742 Err::<(), anyhow::Error>(err).log_err();
11743
11744 if let Some(workspace) = workspace {
11745 workspace
11746 .update(&mut cx, |workspace, cx| {
11747 struct CopyPermalinkToLine;
11748
11749 workspace.show_toast(
11750 Toast::new(
11751 NotificationId::unique::<CopyPermalinkToLine>(),
11752 message,
11753 ),
11754 cx,
11755 )
11756 })
11757 .ok();
11758 }
11759 }
11760 }
11761 })
11762 .detach();
11763 }
11764
11765 pub fn copy_file_location(&mut self, _: &CopyFileLocation, cx: &mut ViewContext<Self>) {
11766 let selection = self.selections.newest::<Point>(cx).start.row + 1;
11767 if let Some(file) = self.target_file(cx) {
11768 if let Some(path) = file.path().to_str() {
11769 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
11770 }
11771 }
11772 }
11773
11774 pub fn open_permalink_to_line(&mut self, _: &OpenPermalinkToLine, cx: &mut ViewContext<Self>) {
11775 let permalink_task = self.get_permalink_to_line(cx);
11776 let workspace = self.workspace();
11777
11778 cx.spawn(|_, mut cx| async move {
11779 match permalink_task.await {
11780 Ok(permalink) => {
11781 cx.update(|cx| {
11782 cx.open_url(permalink.as_ref());
11783 })
11784 .ok();
11785 }
11786 Err(err) => {
11787 let message = format!("Failed to open permalink: {err}");
11788
11789 Err::<(), anyhow::Error>(err).log_err();
11790
11791 if let Some(workspace) = workspace {
11792 workspace
11793 .update(&mut cx, |workspace, cx| {
11794 struct OpenPermalinkToLine;
11795
11796 workspace.show_toast(
11797 Toast::new(
11798 NotificationId::unique::<OpenPermalinkToLine>(),
11799 message,
11800 ),
11801 cx,
11802 )
11803 })
11804 .ok();
11805 }
11806 }
11807 }
11808 })
11809 .detach();
11810 }
11811
11812 /// Adds a row highlight for the given range. If a row has multiple highlights, the
11813 /// last highlight added will be used.
11814 ///
11815 /// If the range ends at the beginning of a line, then that line will not be highlighted.
11816 pub fn highlight_rows<T: 'static>(
11817 &mut self,
11818 range: Range<Anchor>,
11819 color: Hsla,
11820 should_autoscroll: bool,
11821 cx: &mut ViewContext<Self>,
11822 ) {
11823 let snapshot = self.buffer().read(cx).snapshot(cx);
11824 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
11825 let ix = row_highlights.binary_search_by(|highlight| {
11826 Ordering::Equal
11827 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
11828 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
11829 });
11830
11831 if let Err(mut ix) = ix {
11832 let index = post_inc(&mut self.highlight_order);
11833
11834 // If this range intersects with the preceding highlight, then merge it with
11835 // the preceding highlight. Otherwise insert a new highlight.
11836 let mut merged = false;
11837 if ix > 0 {
11838 let prev_highlight = &mut row_highlights[ix - 1];
11839 if prev_highlight
11840 .range
11841 .end
11842 .cmp(&range.start, &snapshot)
11843 .is_ge()
11844 {
11845 ix -= 1;
11846 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
11847 prev_highlight.range.end = range.end;
11848 }
11849 merged = true;
11850 prev_highlight.index = index;
11851 prev_highlight.color = color;
11852 prev_highlight.should_autoscroll = should_autoscroll;
11853 }
11854 }
11855
11856 if !merged {
11857 row_highlights.insert(
11858 ix,
11859 RowHighlight {
11860 range: range.clone(),
11861 index,
11862 color,
11863 should_autoscroll,
11864 },
11865 );
11866 }
11867
11868 // If any of the following highlights intersect with this one, merge them.
11869 while let Some(next_highlight) = row_highlights.get(ix + 1) {
11870 let highlight = &row_highlights[ix];
11871 if next_highlight
11872 .range
11873 .start
11874 .cmp(&highlight.range.end, &snapshot)
11875 .is_le()
11876 {
11877 if next_highlight
11878 .range
11879 .end
11880 .cmp(&highlight.range.end, &snapshot)
11881 .is_gt()
11882 {
11883 row_highlights[ix].range.end = next_highlight.range.end;
11884 }
11885 row_highlights.remove(ix + 1);
11886 } else {
11887 break;
11888 }
11889 }
11890 }
11891 }
11892
11893 /// Remove any highlighted row ranges of the given type that intersect the
11894 /// given ranges.
11895 pub fn remove_highlighted_rows<T: 'static>(
11896 &mut self,
11897 ranges_to_remove: Vec<Range<Anchor>>,
11898 cx: &mut ViewContext<Self>,
11899 ) {
11900 let snapshot = self.buffer().read(cx).snapshot(cx);
11901 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
11902 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
11903 row_highlights.retain(|highlight| {
11904 while let Some(range_to_remove) = ranges_to_remove.peek() {
11905 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
11906 Ordering::Less | Ordering::Equal => {
11907 ranges_to_remove.next();
11908 }
11909 Ordering::Greater => {
11910 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
11911 Ordering::Less | Ordering::Equal => {
11912 return false;
11913 }
11914 Ordering::Greater => break,
11915 }
11916 }
11917 }
11918 }
11919
11920 true
11921 })
11922 }
11923
11924 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
11925 pub fn clear_row_highlights<T: 'static>(&mut self) {
11926 self.highlighted_rows.remove(&TypeId::of::<T>());
11927 }
11928
11929 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
11930 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
11931 self.highlighted_rows
11932 .get(&TypeId::of::<T>())
11933 .map_or(&[] as &[_], |vec| vec.as_slice())
11934 .iter()
11935 .map(|highlight| (highlight.range.clone(), highlight.color))
11936 }
11937
11938 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
11939 /// Rerturns a map of display rows that are highlighted and their corresponding highlight color.
11940 /// Allows to ignore certain kinds of highlights.
11941 pub fn highlighted_display_rows(
11942 &mut self,
11943 cx: &mut WindowContext,
11944 ) -> BTreeMap<DisplayRow, Hsla> {
11945 let snapshot = self.snapshot(cx);
11946 let mut used_highlight_orders = HashMap::default();
11947 self.highlighted_rows
11948 .iter()
11949 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
11950 .fold(
11951 BTreeMap::<DisplayRow, Hsla>::new(),
11952 |mut unique_rows, highlight| {
11953 let start = highlight.range.start.to_display_point(&snapshot);
11954 let end = highlight.range.end.to_display_point(&snapshot);
11955 let start_row = start.row().0;
11956 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
11957 && end.column() == 0
11958 {
11959 end.row().0.saturating_sub(1)
11960 } else {
11961 end.row().0
11962 };
11963 for row in start_row..=end_row {
11964 let used_index =
11965 used_highlight_orders.entry(row).or_insert(highlight.index);
11966 if highlight.index >= *used_index {
11967 *used_index = highlight.index;
11968 unique_rows.insert(DisplayRow(row), highlight.color);
11969 }
11970 }
11971 unique_rows
11972 },
11973 )
11974 }
11975
11976 pub fn highlighted_display_row_for_autoscroll(
11977 &self,
11978 snapshot: &DisplaySnapshot,
11979 ) -> Option<DisplayRow> {
11980 self.highlighted_rows
11981 .values()
11982 .flat_map(|highlighted_rows| highlighted_rows.iter())
11983 .filter_map(|highlight| {
11984 if highlight.should_autoscroll {
11985 Some(highlight.range.start.to_display_point(snapshot).row())
11986 } else {
11987 None
11988 }
11989 })
11990 .min()
11991 }
11992
11993 pub fn set_search_within_ranges(
11994 &mut self,
11995 ranges: &[Range<Anchor>],
11996 cx: &mut ViewContext<Self>,
11997 ) {
11998 self.highlight_background::<SearchWithinRange>(
11999 ranges,
12000 |colors| colors.editor_document_highlight_read_background,
12001 cx,
12002 )
12003 }
12004
12005 pub fn set_breadcrumb_header(&mut self, new_header: String) {
12006 self.breadcrumb_header = Some(new_header);
12007 }
12008
12009 pub fn clear_search_within_ranges(&mut self, cx: &mut ViewContext<Self>) {
12010 self.clear_background_highlights::<SearchWithinRange>(cx);
12011 }
12012
12013 pub fn highlight_background<T: 'static>(
12014 &mut self,
12015 ranges: &[Range<Anchor>],
12016 color_fetcher: fn(&ThemeColors) -> Hsla,
12017 cx: &mut ViewContext<Self>,
12018 ) {
12019 self.background_highlights
12020 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
12021 self.scrollbar_marker_state.dirty = true;
12022 cx.notify();
12023 }
12024
12025 pub fn clear_background_highlights<T: 'static>(
12026 &mut self,
12027 cx: &mut ViewContext<Self>,
12028 ) -> Option<BackgroundHighlight> {
12029 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
12030 if !text_highlights.1.is_empty() {
12031 self.scrollbar_marker_state.dirty = true;
12032 cx.notify();
12033 }
12034 Some(text_highlights)
12035 }
12036
12037 pub fn highlight_gutter<T: 'static>(
12038 &mut self,
12039 ranges: &[Range<Anchor>],
12040 color_fetcher: fn(&AppContext) -> Hsla,
12041 cx: &mut ViewContext<Self>,
12042 ) {
12043 self.gutter_highlights
12044 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
12045 cx.notify();
12046 }
12047
12048 pub fn clear_gutter_highlights<T: 'static>(
12049 &mut self,
12050 cx: &mut ViewContext<Self>,
12051 ) -> Option<GutterHighlight> {
12052 cx.notify();
12053 self.gutter_highlights.remove(&TypeId::of::<T>())
12054 }
12055
12056 #[cfg(feature = "test-support")]
12057 pub fn all_text_background_highlights(
12058 &mut self,
12059 cx: &mut ViewContext<Self>,
12060 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
12061 let snapshot = self.snapshot(cx);
12062 let buffer = &snapshot.buffer_snapshot;
12063 let start = buffer.anchor_before(0);
12064 let end = buffer.anchor_after(buffer.len());
12065 let theme = cx.theme().colors();
12066 self.background_highlights_in_range(start..end, &snapshot, theme)
12067 }
12068
12069 #[cfg(feature = "test-support")]
12070 pub fn search_background_highlights(
12071 &mut self,
12072 cx: &mut ViewContext<Self>,
12073 ) -> Vec<Range<Point>> {
12074 let snapshot = self.buffer().read(cx).snapshot(cx);
12075
12076 let highlights = self
12077 .background_highlights
12078 .get(&TypeId::of::<items::BufferSearchHighlights>());
12079
12080 if let Some((_color, ranges)) = highlights {
12081 ranges
12082 .iter()
12083 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
12084 .collect_vec()
12085 } else {
12086 vec![]
12087 }
12088 }
12089
12090 fn document_highlights_for_position<'a>(
12091 &'a self,
12092 position: Anchor,
12093 buffer: &'a MultiBufferSnapshot,
12094 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
12095 let read_highlights = self
12096 .background_highlights
12097 .get(&TypeId::of::<DocumentHighlightRead>())
12098 .map(|h| &h.1);
12099 let write_highlights = self
12100 .background_highlights
12101 .get(&TypeId::of::<DocumentHighlightWrite>())
12102 .map(|h| &h.1);
12103 let left_position = position.bias_left(buffer);
12104 let right_position = position.bias_right(buffer);
12105 read_highlights
12106 .into_iter()
12107 .chain(write_highlights)
12108 .flat_map(move |ranges| {
12109 let start_ix = match ranges.binary_search_by(|probe| {
12110 let cmp = probe.end.cmp(&left_position, buffer);
12111 if cmp.is_ge() {
12112 Ordering::Greater
12113 } else {
12114 Ordering::Less
12115 }
12116 }) {
12117 Ok(i) | Err(i) => i,
12118 };
12119
12120 ranges[start_ix..]
12121 .iter()
12122 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
12123 })
12124 }
12125
12126 pub fn has_background_highlights<T: 'static>(&self) -> bool {
12127 self.background_highlights
12128 .get(&TypeId::of::<T>())
12129 .map_or(false, |(_, highlights)| !highlights.is_empty())
12130 }
12131
12132 pub fn background_highlights_in_range(
12133 &self,
12134 search_range: Range<Anchor>,
12135 display_snapshot: &DisplaySnapshot,
12136 theme: &ThemeColors,
12137 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
12138 let mut results = Vec::new();
12139 for (color_fetcher, ranges) in self.background_highlights.values() {
12140 let color = color_fetcher(theme);
12141 let start_ix = match ranges.binary_search_by(|probe| {
12142 let cmp = probe
12143 .end
12144 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
12145 if cmp.is_gt() {
12146 Ordering::Greater
12147 } else {
12148 Ordering::Less
12149 }
12150 }) {
12151 Ok(i) | Err(i) => i,
12152 };
12153 for range in &ranges[start_ix..] {
12154 if range
12155 .start
12156 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
12157 .is_ge()
12158 {
12159 break;
12160 }
12161
12162 let start = range.start.to_display_point(display_snapshot);
12163 let end = range.end.to_display_point(display_snapshot);
12164 results.push((start..end, color))
12165 }
12166 }
12167 results
12168 }
12169
12170 pub fn background_highlight_row_ranges<T: 'static>(
12171 &self,
12172 search_range: Range<Anchor>,
12173 display_snapshot: &DisplaySnapshot,
12174 count: usize,
12175 ) -> Vec<RangeInclusive<DisplayPoint>> {
12176 let mut results = Vec::new();
12177 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
12178 return vec![];
12179 };
12180
12181 let start_ix = match ranges.binary_search_by(|probe| {
12182 let cmp = probe
12183 .end
12184 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
12185 if cmp.is_gt() {
12186 Ordering::Greater
12187 } else {
12188 Ordering::Less
12189 }
12190 }) {
12191 Ok(i) | Err(i) => i,
12192 };
12193 let mut push_region = |start: Option<Point>, end: Option<Point>| {
12194 if let (Some(start_display), Some(end_display)) = (start, end) {
12195 results.push(
12196 start_display.to_display_point(display_snapshot)
12197 ..=end_display.to_display_point(display_snapshot),
12198 );
12199 }
12200 };
12201 let mut start_row: Option<Point> = None;
12202 let mut end_row: Option<Point> = None;
12203 if ranges.len() > count {
12204 return Vec::new();
12205 }
12206 for range in &ranges[start_ix..] {
12207 if range
12208 .start
12209 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
12210 .is_ge()
12211 {
12212 break;
12213 }
12214 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
12215 if let Some(current_row) = &end_row {
12216 if end.row == current_row.row {
12217 continue;
12218 }
12219 }
12220 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
12221 if start_row.is_none() {
12222 assert_eq!(end_row, None);
12223 start_row = Some(start);
12224 end_row = Some(end);
12225 continue;
12226 }
12227 if let Some(current_end) = end_row.as_mut() {
12228 if start.row > current_end.row + 1 {
12229 push_region(start_row, end_row);
12230 start_row = Some(start);
12231 end_row = Some(end);
12232 } else {
12233 // Merge two hunks.
12234 *current_end = end;
12235 }
12236 } else {
12237 unreachable!();
12238 }
12239 }
12240 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
12241 push_region(start_row, end_row);
12242 results
12243 }
12244
12245 pub fn gutter_highlights_in_range(
12246 &self,
12247 search_range: Range<Anchor>,
12248 display_snapshot: &DisplaySnapshot,
12249 cx: &AppContext,
12250 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
12251 let mut results = Vec::new();
12252 for (color_fetcher, ranges) in self.gutter_highlights.values() {
12253 let color = color_fetcher(cx);
12254 let start_ix = match ranges.binary_search_by(|probe| {
12255 let cmp = probe
12256 .end
12257 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
12258 if cmp.is_gt() {
12259 Ordering::Greater
12260 } else {
12261 Ordering::Less
12262 }
12263 }) {
12264 Ok(i) | Err(i) => i,
12265 };
12266 for range in &ranges[start_ix..] {
12267 if range
12268 .start
12269 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
12270 .is_ge()
12271 {
12272 break;
12273 }
12274
12275 let start = range.start.to_display_point(display_snapshot);
12276 let end = range.end.to_display_point(display_snapshot);
12277 results.push((start..end, color))
12278 }
12279 }
12280 results
12281 }
12282
12283 /// Get the text ranges corresponding to the redaction query
12284 pub fn redacted_ranges(
12285 &self,
12286 search_range: Range<Anchor>,
12287 display_snapshot: &DisplaySnapshot,
12288 cx: &WindowContext,
12289 ) -> Vec<Range<DisplayPoint>> {
12290 display_snapshot
12291 .buffer_snapshot
12292 .redacted_ranges(search_range, |file| {
12293 if let Some(file) = file {
12294 file.is_private()
12295 && EditorSettings::get(
12296 Some(SettingsLocation {
12297 worktree_id: file.worktree_id(cx),
12298 path: file.path().as_ref(),
12299 }),
12300 cx,
12301 )
12302 .redact_private_values
12303 } else {
12304 false
12305 }
12306 })
12307 .map(|range| {
12308 range.start.to_display_point(display_snapshot)
12309 ..range.end.to_display_point(display_snapshot)
12310 })
12311 .collect()
12312 }
12313
12314 pub fn highlight_text<T: 'static>(
12315 &mut self,
12316 ranges: Vec<Range<Anchor>>,
12317 style: HighlightStyle,
12318 cx: &mut ViewContext<Self>,
12319 ) {
12320 self.display_map.update(cx, |map, _| {
12321 map.highlight_text(TypeId::of::<T>(), ranges, style)
12322 });
12323 cx.notify();
12324 }
12325
12326 pub(crate) fn highlight_inlays<T: 'static>(
12327 &mut self,
12328 highlights: Vec<InlayHighlight>,
12329 style: HighlightStyle,
12330 cx: &mut ViewContext<Self>,
12331 ) {
12332 self.display_map.update(cx, |map, _| {
12333 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
12334 });
12335 cx.notify();
12336 }
12337
12338 pub fn text_highlights<'a, T: 'static>(
12339 &'a self,
12340 cx: &'a AppContext,
12341 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
12342 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
12343 }
12344
12345 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
12346 let cleared = self
12347 .display_map
12348 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
12349 if cleared {
12350 cx.notify();
12351 }
12352 }
12353
12354 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
12355 (self.read_only(cx) || self.blink_manager.read(cx).visible())
12356 && self.focus_handle.is_focused(cx)
12357 }
12358
12359 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut ViewContext<Self>) {
12360 self.show_cursor_when_unfocused = is_enabled;
12361 cx.notify();
12362 }
12363
12364 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
12365 cx.notify();
12366 }
12367
12368 fn on_buffer_event(
12369 &mut self,
12370 multibuffer: Model<MultiBuffer>,
12371 event: &multi_buffer::Event,
12372 cx: &mut ViewContext<Self>,
12373 ) {
12374 match event {
12375 multi_buffer::Event::Edited {
12376 singleton_buffer_edited,
12377 } => {
12378 self.scrollbar_marker_state.dirty = true;
12379 self.active_indent_guides_state.dirty = true;
12380 self.refresh_active_diagnostics(cx);
12381 self.refresh_code_actions(cx);
12382 if self.has_active_inline_completion(cx) {
12383 self.update_visible_inline_completion(cx);
12384 }
12385 cx.emit(EditorEvent::BufferEdited);
12386 cx.emit(SearchEvent::MatchesInvalidated);
12387 if *singleton_buffer_edited {
12388 if let Some(project) = &self.project {
12389 let project = project.read(cx);
12390 #[allow(clippy::mutable_key_type)]
12391 let languages_affected = multibuffer
12392 .read(cx)
12393 .all_buffers()
12394 .into_iter()
12395 .filter_map(|buffer| {
12396 let buffer = buffer.read(cx);
12397 let language = buffer.language()?;
12398 if project.is_local()
12399 && project.language_servers_for_buffer(buffer, cx).count() == 0
12400 {
12401 None
12402 } else {
12403 Some(language)
12404 }
12405 })
12406 .cloned()
12407 .collect::<HashSet<_>>();
12408 if !languages_affected.is_empty() {
12409 self.refresh_inlay_hints(
12410 InlayHintRefreshReason::BufferEdited(languages_affected),
12411 cx,
12412 );
12413 }
12414 }
12415 }
12416
12417 let Some(project) = &self.project else { return };
12418 let (telemetry, is_via_ssh) = {
12419 let project = project.read(cx);
12420 let telemetry = project.client().telemetry().clone();
12421 let is_via_ssh = project.is_via_ssh();
12422 (telemetry, is_via_ssh)
12423 };
12424 refresh_linked_ranges(self, cx);
12425 telemetry.log_edit_event("editor", is_via_ssh);
12426 }
12427 multi_buffer::Event::ExcerptsAdded {
12428 buffer,
12429 predecessor,
12430 excerpts,
12431 } => {
12432 self.tasks_update_task = Some(self.refresh_runnables(cx));
12433 cx.emit(EditorEvent::ExcerptsAdded {
12434 buffer: buffer.clone(),
12435 predecessor: *predecessor,
12436 excerpts: excerpts.clone(),
12437 });
12438 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
12439 }
12440 multi_buffer::Event::ExcerptsRemoved { ids } => {
12441 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
12442 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
12443 }
12444 multi_buffer::Event::ExcerptsEdited { ids } => {
12445 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
12446 }
12447 multi_buffer::Event::ExcerptsExpanded { ids } => {
12448 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
12449 }
12450 multi_buffer::Event::Reparsed(buffer_id) => {
12451 self.tasks_update_task = Some(self.refresh_runnables(cx));
12452
12453 cx.emit(EditorEvent::Reparsed(*buffer_id));
12454 }
12455 multi_buffer::Event::LanguageChanged(buffer_id) => {
12456 linked_editing_ranges::refresh_linked_ranges(self, cx);
12457 cx.emit(EditorEvent::Reparsed(*buffer_id));
12458 cx.notify();
12459 }
12460 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
12461 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
12462 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
12463 cx.emit(EditorEvent::TitleChanged)
12464 }
12465 multi_buffer::Event::DiffBaseChanged => {
12466 self.scrollbar_marker_state.dirty = true;
12467 cx.emit(EditorEvent::DiffBaseChanged);
12468 cx.notify();
12469 }
12470 multi_buffer::Event::DiffUpdated { buffer } => {
12471 self.sync_expanded_diff_hunks(buffer.clone(), cx);
12472 cx.notify();
12473 }
12474 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
12475 multi_buffer::Event::DiagnosticsUpdated => {
12476 self.refresh_active_diagnostics(cx);
12477 self.scrollbar_marker_state.dirty = true;
12478 cx.notify();
12479 }
12480 _ => {}
12481 };
12482 }
12483
12484 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
12485 cx.notify();
12486 }
12487
12488 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
12489 self.tasks_update_task = Some(self.refresh_runnables(cx));
12490 self.refresh_inline_completion(true, false, cx);
12491 self.refresh_inlay_hints(
12492 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
12493 self.selections.newest_anchor().head(),
12494 &self.buffer.read(cx).snapshot(cx),
12495 cx,
12496 )),
12497 cx,
12498 );
12499
12500 let old_cursor_shape = self.cursor_shape;
12501
12502 {
12503 let editor_settings = EditorSettings::get_global(cx);
12504 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
12505 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
12506 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
12507 }
12508
12509 if old_cursor_shape != self.cursor_shape {
12510 cx.emit(EditorEvent::CursorShapeChanged);
12511 }
12512
12513 let project_settings = ProjectSettings::get_global(cx);
12514 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
12515
12516 if self.mode == EditorMode::Full {
12517 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
12518 if self.git_blame_inline_enabled != inline_blame_enabled {
12519 self.toggle_git_blame_inline_internal(false, cx);
12520 }
12521 }
12522
12523 cx.notify();
12524 }
12525
12526 pub fn set_searchable(&mut self, searchable: bool) {
12527 self.searchable = searchable;
12528 }
12529
12530 pub fn searchable(&self) -> bool {
12531 self.searchable
12532 }
12533
12534 fn open_proposed_changes_editor(
12535 &mut self,
12536 _: &OpenProposedChangesEditor,
12537 cx: &mut ViewContext<Self>,
12538 ) {
12539 let Some(workspace) = self.workspace() else {
12540 cx.propagate();
12541 return;
12542 };
12543
12544 let selections = self.selections.all::<usize>(cx);
12545 let buffer = self.buffer.read(cx);
12546 let mut new_selections_by_buffer = HashMap::default();
12547 for selection in selections {
12548 for (buffer, range, _) in
12549 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
12550 {
12551 let mut range = range.to_point(buffer.read(cx));
12552 range.start.column = 0;
12553 range.end.column = buffer.read(cx).line_len(range.end.row);
12554 new_selections_by_buffer
12555 .entry(buffer)
12556 .or_insert(Vec::new())
12557 .push(range)
12558 }
12559 }
12560
12561 let proposed_changes_buffers = new_selections_by_buffer
12562 .into_iter()
12563 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
12564 .collect::<Vec<_>>();
12565 let proposed_changes_editor = cx.new_view(|cx| {
12566 ProposedChangesEditor::new(
12567 "Proposed changes",
12568 proposed_changes_buffers,
12569 self.project.clone(),
12570 cx,
12571 )
12572 });
12573
12574 cx.window_context().defer(move |cx| {
12575 workspace.update(cx, |workspace, cx| {
12576 workspace.active_pane().update(cx, |pane, cx| {
12577 pane.add_item(Box::new(proposed_changes_editor), true, true, None, cx);
12578 });
12579 });
12580 });
12581 }
12582
12583 pub fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext<Self>) {
12584 self.open_excerpts_common(true, cx)
12585 }
12586
12587 pub fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
12588 self.open_excerpts_common(false, cx)
12589 }
12590
12591 fn open_excerpts_common(&mut self, split: bool, cx: &mut ViewContext<Self>) {
12592 let selections = self.selections.all::<usize>(cx);
12593 let buffer = self.buffer.read(cx);
12594 if buffer.is_singleton() {
12595 cx.propagate();
12596 return;
12597 }
12598
12599 let Some(workspace) = self.workspace() else {
12600 cx.propagate();
12601 return;
12602 };
12603
12604 let mut new_selections_by_buffer = HashMap::default();
12605 for selection in selections {
12606 for (mut buffer_handle, mut range, _) in
12607 buffer.range_to_buffer_ranges(selection.range(), cx)
12608 {
12609 // When editing branch buffers, jump to the corresponding location
12610 // in their base buffer.
12611 let buffer = buffer_handle.read(cx);
12612 if let Some(base_buffer) = buffer.diff_base_buffer() {
12613 range = buffer.range_to_version(range, &base_buffer.read(cx).version());
12614 buffer_handle = base_buffer;
12615 }
12616
12617 if selection.reversed {
12618 mem::swap(&mut range.start, &mut range.end);
12619 }
12620 new_selections_by_buffer
12621 .entry(buffer_handle)
12622 .or_insert(Vec::new())
12623 .push(range)
12624 }
12625 }
12626
12627 // We defer the pane interaction because we ourselves are a workspace item
12628 // and activating a new item causes the pane to call a method on us reentrantly,
12629 // which panics if we're on the stack.
12630 cx.window_context().defer(move |cx| {
12631 workspace.update(cx, |workspace, cx| {
12632 let pane = if split {
12633 workspace.adjacent_pane(cx)
12634 } else {
12635 workspace.active_pane().clone()
12636 };
12637
12638 for (buffer, ranges) in new_selections_by_buffer {
12639 let editor =
12640 workspace.open_project_item::<Self>(pane.clone(), buffer, true, true, cx);
12641 editor.update(cx, |editor, cx| {
12642 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
12643 s.select_ranges(ranges);
12644 });
12645 });
12646 }
12647 })
12648 });
12649 }
12650
12651 fn jump(
12652 &mut self,
12653 path: ProjectPath,
12654 position: Point,
12655 anchor: language::Anchor,
12656 offset_from_top: u32,
12657 cx: &mut ViewContext<Self>,
12658 ) {
12659 let workspace = self.workspace();
12660 cx.spawn(|_, mut cx| async move {
12661 let workspace = workspace.ok_or_else(|| anyhow!("cannot jump without workspace"))?;
12662 let editor = workspace.update(&mut cx, |workspace, cx| {
12663 // Reset the preview item id before opening the new item
12664 workspace.active_pane().update(cx, |pane, cx| {
12665 pane.set_preview_item_id(None, cx);
12666 });
12667 workspace.open_path_preview(path, None, true, true, cx)
12668 })?;
12669 let editor = editor
12670 .await?
12671 .downcast::<Editor>()
12672 .ok_or_else(|| anyhow!("opened item was not an editor"))?
12673 .downgrade();
12674 editor.update(&mut cx, |editor, cx| {
12675 let buffer = editor
12676 .buffer()
12677 .read(cx)
12678 .as_singleton()
12679 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
12680 let buffer = buffer.read(cx);
12681 let cursor = if buffer.can_resolve(&anchor) {
12682 language::ToPoint::to_point(&anchor, buffer)
12683 } else {
12684 buffer.clip_point(position, Bias::Left)
12685 };
12686
12687 let nav_history = editor.nav_history.take();
12688 editor.change_selections(
12689 Some(Autoscroll::top_relative(offset_from_top as usize)),
12690 cx,
12691 |s| {
12692 s.select_ranges([cursor..cursor]);
12693 },
12694 );
12695 editor.nav_history = nav_history;
12696
12697 anyhow::Ok(())
12698 })??;
12699
12700 anyhow::Ok(())
12701 })
12702 .detach_and_log_err(cx);
12703 }
12704
12705 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
12706 let snapshot = self.buffer.read(cx).read(cx);
12707 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
12708 Some(
12709 ranges
12710 .iter()
12711 .map(move |range| {
12712 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
12713 })
12714 .collect(),
12715 )
12716 }
12717
12718 fn selection_replacement_ranges(
12719 &self,
12720 range: Range<OffsetUtf16>,
12721 cx: &mut AppContext,
12722 ) -> Vec<Range<OffsetUtf16>> {
12723 let selections = self.selections.all::<OffsetUtf16>(cx);
12724 let newest_selection = selections
12725 .iter()
12726 .max_by_key(|selection| selection.id)
12727 .unwrap();
12728 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
12729 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
12730 let snapshot = self.buffer.read(cx).read(cx);
12731 selections
12732 .into_iter()
12733 .map(|mut selection| {
12734 selection.start.0 =
12735 (selection.start.0 as isize).saturating_add(start_delta) as usize;
12736 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
12737 snapshot.clip_offset_utf16(selection.start, Bias::Left)
12738 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
12739 })
12740 .collect()
12741 }
12742
12743 fn report_editor_event(
12744 &self,
12745 operation: &'static str,
12746 file_extension: Option<String>,
12747 cx: &AppContext,
12748 ) {
12749 if cfg!(any(test, feature = "test-support")) {
12750 return;
12751 }
12752
12753 let Some(project) = &self.project else { return };
12754
12755 // If None, we are in a file without an extension
12756 let file = self
12757 .buffer
12758 .read(cx)
12759 .as_singleton()
12760 .and_then(|b| b.read(cx).file());
12761 let file_extension = file_extension.or(file
12762 .as_ref()
12763 .and_then(|file| Path::new(file.file_name(cx)).extension())
12764 .and_then(|e| e.to_str())
12765 .map(|a| a.to_string()));
12766
12767 let vim_mode = cx
12768 .global::<SettingsStore>()
12769 .raw_user_settings()
12770 .get("vim_mode")
12771 == Some(&serde_json::Value::Bool(true));
12772
12773 let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
12774 == language::language_settings::InlineCompletionProvider::Copilot;
12775 let copilot_enabled_for_language = self
12776 .buffer
12777 .read(cx)
12778 .settings_at(0, cx)
12779 .show_inline_completions;
12780
12781 let project = project.read(cx);
12782 let telemetry = project.client().telemetry().clone();
12783 telemetry.report_editor_event(
12784 file_extension,
12785 vim_mode,
12786 operation,
12787 copilot_enabled,
12788 copilot_enabled_for_language,
12789 project.is_via_ssh(),
12790 )
12791 }
12792
12793 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
12794 /// with each line being an array of {text, highlight} objects.
12795 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
12796 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
12797 return;
12798 };
12799
12800 #[derive(Serialize)]
12801 struct Chunk<'a> {
12802 text: String,
12803 highlight: Option<&'a str>,
12804 }
12805
12806 let snapshot = buffer.read(cx).snapshot();
12807 let range = self
12808 .selected_text_range(false, cx)
12809 .and_then(|selection| {
12810 if selection.range.is_empty() {
12811 None
12812 } else {
12813 Some(selection.range)
12814 }
12815 })
12816 .unwrap_or_else(|| 0..snapshot.len());
12817
12818 let chunks = snapshot.chunks(range, true);
12819 let mut lines = Vec::new();
12820 let mut line: VecDeque<Chunk> = VecDeque::new();
12821
12822 let Some(style) = self.style.as_ref() else {
12823 return;
12824 };
12825
12826 for chunk in chunks {
12827 let highlight = chunk
12828 .syntax_highlight_id
12829 .and_then(|id| id.name(&style.syntax));
12830 let mut chunk_lines = chunk.text.split('\n').peekable();
12831 while let Some(text) = chunk_lines.next() {
12832 let mut merged_with_last_token = false;
12833 if let Some(last_token) = line.back_mut() {
12834 if last_token.highlight == highlight {
12835 last_token.text.push_str(text);
12836 merged_with_last_token = true;
12837 }
12838 }
12839
12840 if !merged_with_last_token {
12841 line.push_back(Chunk {
12842 text: text.into(),
12843 highlight,
12844 });
12845 }
12846
12847 if chunk_lines.peek().is_some() {
12848 if line.len() > 1 && line.front().unwrap().text.is_empty() {
12849 line.pop_front();
12850 }
12851 if line.len() > 1 && line.back().unwrap().text.is_empty() {
12852 line.pop_back();
12853 }
12854
12855 lines.push(mem::take(&mut line));
12856 }
12857 }
12858 }
12859
12860 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
12861 return;
12862 };
12863 cx.write_to_clipboard(ClipboardItem::new_string(lines));
12864 }
12865
12866 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
12867 &self.inlay_hint_cache
12868 }
12869
12870 pub fn replay_insert_event(
12871 &mut self,
12872 text: &str,
12873 relative_utf16_range: Option<Range<isize>>,
12874 cx: &mut ViewContext<Self>,
12875 ) {
12876 if !self.input_enabled {
12877 cx.emit(EditorEvent::InputIgnored { text: text.into() });
12878 return;
12879 }
12880 if let Some(relative_utf16_range) = relative_utf16_range {
12881 let selections = self.selections.all::<OffsetUtf16>(cx);
12882 self.change_selections(None, cx, |s| {
12883 let new_ranges = selections.into_iter().map(|range| {
12884 let start = OffsetUtf16(
12885 range
12886 .head()
12887 .0
12888 .saturating_add_signed(relative_utf16_range.start),
12889 );
12890 let end = OffsetUtf16(
12891 range
12892 .head()
12893 .0
12894 .saturating_add_signed(relative_utf16_range.end),
12895 );
12896 start..end
12897 });
12898 s.select_ranges(new_ranges);
12899 });
12900 }
12901
12902 self.handle_input(text, cx);
12903 }
12904
12905 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
12906 let Some(provider) = self.semantics_provider.as_ref() else {
12907 return false;
12908 };
12909
12910 let mut supports = false;
12911 self.buffer().read(cx).for_each_buffer(|buffer| {
12912 supports |= provider.supports_inlay_hints(buffer, cx);
12913 });
12914 supports
12915 }
12916
12917 pub fn focus(&self, cx: &mut WindowContext) {
12918 cx.focus(&self.focus_handle)
12919 }
12920
12921 pub fn is_focused(&self, cx: &WindowContext) -> bool {
12922 self.focus_handle.is_focused(cx)
12923 }
12924
12925 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
12926 cx.emit(EditorEvent::Focused);
12927
12928 if let Some(descendant) = self
12929 .last_focused_descendant
12930 .take()
12931 .and_then(|descendant| descendant.upgrade())
12932 {
12933 cx.focus(&descendant);
12934 } else {
12935 if let Some(blame) = self.blame.as_ref() {
12936 blame.update(cx, GitBlame::focus)
12937 }
12938
12939 self.blink_manager.update(cx, BlinkManager::enable);
12940 self.show_cursor_names(cx);
12941 self.buffer.update(cx, |buffer, cx| {
12942 buffer.finalize_last_transaction(cx);
12943 if self.leader_peer_id.is_none() {
12944 buffer.set_active_selections(
12945 &self.selections.disjoint_anchors(),
12946 self.selections.line_mode,
12947 self.cursor_shape,
12948 cx,
12949 );
12950 }
12951 });
12952 }
12953 }
12954
12955 fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
12956 cx.emit(EditorEvent::FocusedIn)
12957 }
12958
12959 fn handle_focus_out(&mut self, event: FocusOutEvent, _cx: &mut ViewContext<Self>) {
12960 if event.blurred != self.focus_handle {
12961 self.last_focused_descendant = Some(event.blurred);
12962 }
12963 }
12964
12965 pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
12966 self.blink_manager.update(cx, BlinkManager::disable);
12967 self.buffer
12968 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
12969
12970 if let Some(blame) = self.blame.as_ref() {
12971 blame.update(cx, GitBlame::blur)
12972 }
12973 if !self.hover_state.focused(cx) {
12974 hide_hover(self, cx);
12975 }
12976
12977 self.hide_context_menu(cx);
12978 cx.emit(EditorEvent::Blurred);
12979 cx.notify();
12980 }
12981
12982 pub fn register_action<A: Action>(
12983 &mut self,
12984 listener: impl Fn(&A, &mut WindowContext) + 'static,
12985 ) -> Subscription {
12986 let id = self.next_editor_action_id.post_inc();
12987 let listener = Arc::new(listener);
12988 self.editor_actions.borrow_mut().insert(
12989 id,
12990 Box::new(move |cx| {
12991 let cx = cx.window_context();
12992 let listener = listener.clone();
12993 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
12994 let action = action.downcast_ref().unwrap();
12995 if phase == DispatchPhase::Bubble {
12996 listener(action, cx)
12997 }
12998 })
12999 }),
13000 );
13001
13002 let editor_actions = self.editor_actions.clone();
13003 Subscription::new(move || {
13004 editor_actions.borrow_mut().remove(&id);
13005 })
13006 }
13007
13008 pub fn file_header_size(&self) -> u32 {
13009 FILE_HEADER_HEIGHT
13010 }
13011
13012 pub fn revert(
13013 &mut self,
13014 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
13015 cx: &mut ViewContext<Self>,
13016 ) {
13017 self.buffer().update(cx, |multi_buffer, cx| {
13018 for (buffer_id, changes) in revert_changes {
13019 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
13020 buffer.update(cx, |buffer, cx| {
13021 buffer.edit(
13022 changes.into_iter().map(|(range, text)| {
13023 (range, text.to_string().map(Arc::<str>::from))
13024 }),
13025 None,
13026 cx,
13027 );
13028 });
13029 }
13030 }
13031 });
13032 self.change_selections(None, cx, |selections| selections.refresh());
13033 }
13034
13035 pub fn to_pixel_point(
13036 &mut self,
13037 source: multi_buffer::Anchor,
13038 editor_snapshot: &EditorSnapshot,
13039 cx: &mut ViewContext<Self>,
13040 ) -> Option<gpui::Point<Pixels>> {
13041 let source_point = source.to_display_point(editor_snapshot);
13042 self.display_to_pixel_point(source_point, editor_snapshot, cx)
13043 }
13044
13045 pub fn display_to_pixel_point(
13046 &mut self,
13047 source: DisplayPoint,
13048 editor_snapshot: &EditorSnapshot,
13049 cx: &mut ViewContext<Self>,
13050 ) -> Option<gpui::Point<Pixels>> {
13051 let line_height = self.style()?.text.line_height_in_pixels(cx.rem_size());
13052 let text_layout_details = self.text_layout_details(cx);
13053 let scroll_top = text_layout_details
13054 .scroll_anchor
13055 .scroll_position(editor_snapshot)
13056 .y;
13057
13058 if source.row().as_f32() < scroll_top.floor() {
13059 return None;
13060 }
13061 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
13062 let source_y = line_height * (source.row().as_f32() - scroll_top);
13063 Some(gpui::Point::new(source_x, source_y))
13064 }
13065
13066 pub fn has_active_completions_menu(&self) -> bool {
13067 self.context_menu.read().as_ref().map_or(false, |menu| {
13068 menu.visible() && matches!(menu, ContextMenu::Completions(_))
13069 })
13070 }
13071
13072 pub fn register_addon<T: Addon>(&mut self, instance: T) {
13073 self.addons
13074 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
13075 }
13076
13077 pub fn unregister_addon<T: Addon>(&mut self) {
13078 self.addons.remove(&std::any::TypeId::of::<T>());
13079 }
13080
13081 pub fn addon<T: Addon>(&self) -> Option<&T> {
13082 let type_id = std::any::TypeId::of::<T>();
13083 self.addons
13084 .get(&type_id)
13085 .and_then(|item| item.to_any().downcast_ref::<T>())
13086 }
13087}
13088
13089fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
13090 let tab_size = tab_size.get() as usize;
13091 let mut width = offset;
13092
13093 for ch in text.chars() {
13094 width += if ch == '\t' {
13095 tab_size - (width % tab_size)
13096 } else {
13097 1
13098 };
13099 }
13100
13101 width - offset
13102}
13103
13104#[cfg(test)]
13105mod tests {
13106 use super::*;
13107
13108 #[test]
13109 fn test_string_size_with_expanded_tabs() {
13110 let nz = |val| NonZeroU32::new(val).unwrap();
13111 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
13112 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
13113 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
13114 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
13115 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
13116 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
13117 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
13118 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
13119 }
13120}
13121
13122/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
13123struct WordBreakingTokenizer<'a> {
13124 input: &'a str,
13125}
13126
13127impl<'a> WordBreakingTokenizer<'a> {
13128 fn new(input: &'a str) -> Self {
13129 Self { input }
13130 }
13131}
13132
13133fn is_char_ideographic(ch: char) -> bool {
13134 use unicode_script::Script::*;
13135 use unicode_script::UnicodeScript;
13136 matches!(ch.script(), Han | Tangut | Yi)
13137}
13138
13139fn is_grapheme_ideographic(text: &str) -> bool {
13140 text.chars().any(is_char_ideographic)
13141}
13142
13143fn is_grapheme_whitespace(text: &str) -> bool {
13144 text.chars().any(|x| x.is_whitespace())
13145}
13146
13147fn should_stay_with_preceding_ideograph(text: &str) -> bool {
13148 text.chars().next().map_or(false, |ch| {
13149 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
13150 })
13151}
13152
13153#[derive(PartialEq, Eq, Debug, Clone, Copy)]
13154struct WordBreakToken<'a> {
13155 token: &'a str,
13156 grapheme_len: usize,
13157 is_whitespace: bool,
13158}
13159
13160impl<'a> Iterator for WordBreakingTokenizer<'a> {
13161 /// Yields a span, the count of graphemes in the token, and whether it was
13162 /// whitespace. Note that it also breaks at word boundaries.
13163 type Item = WordBreakToken<'a>;
13164
13165 fn next(&mut self) -> Option<Self::Item> {
13166 use unicode_segmentation::UnicodeSegmentation;
13167 if self.input.is_empty() {
13168 return None;
13169 }
13170
13171 let mut iter = self.input.graphemes(true).peekable();
13172 let mut offset = 0;
13173 let mut graphemes = 0;
13174 if let Some(first_grapheme) = iter.next() {
13175 let is_whitespace = is_grapheme_whitespace(first_grapheme);
13176 offset += first_grapheme.len();
13177 graphemes += 1;
13178 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
13179 if let Some(grapheme) = iter.peek().copied() {
13180 if should_stay_with_preceding_ideograph(grapheme) {
13181 offset += grapheme.len();
13182 graphemes += 1;
13183 }
13184 }
13185 } else {
13186 let mut words = self.input[offset..].split_word_bound_indices().peekable();
13187 let mut next_word_bound = words.peek().copied();
13188 if next_word_bound.map_or(false, |(i, _)| i == 0) {
13189 next_word_bound = words.next();
13190 }
13191 while let Some(grapheme) = iter.peek().copied() {
13192 if next_word_bound.map_or(false, |(i, _)| i == offset) {
13193 break;
13194 };
13195 if is_grapheme_whitespace(grapheme) != is_whitespace {
13196 break;
13197 };
13198 offset += grapheme.len();
13199 graphemes += 1;
13200 iter.next();
13201 }
13202 }
13203 let token = &self.input[..offset];
13204 self.input = &self.input[offset..];
13205 if is_whitespace {
13206 Some(WordBreakToken {
13207 token: " ",
13208 grapheme_len: 1,
13209 is_whitespace: true,
13210 })
13211 } else {
13212 Some(WordBreakToken {
13213 token,
13214 grapheme_len: graphemes,
13215 is_whitespace: false,
13216 })
13217 }
13218 } else {
13219 None
13220 }
13221 }
13222}
13223
13224#[test]
13225fn test_word_breaking_tokenizer() {
13226 let tests: &[(&str, &[(&str, usize, bool)])] = &[
13227 ("", &[]),
13228 (" ", &[(" ", 1, true)]),
13229 ("Ʒ", &[("Ʒ", 1, false)]),
13230 ("Ǽ", &[("Ǽ", 1, false)]),
13231 ("⋑", &[("⋑", 1, false)]),
13232 ("⋑⋑", &[("⋑⋑", 2, false)]),
13233 (
13234 "原理,进而",
13235 &[
13236 ("原", 1, false),
13237 ("理,", 2, false),
13238 ("进", 1, false),
13239 ("而", 1, false),
13240 ],
13241 ),
13242 (
13243 "hello world",
13244 &[("hello", 5, false), (" ", 1, true), ("world", 5, false)],
13245 ),
13246 (
13247 "hello, world",
13248 &[("hello,", 6, false), (" ", 1, true), ("world", 5, false)],
13249 ),
13250 (
13251 " hello world",
13252 &[
13253 (" ", 1, true),
13254 ("hello", 5, false),
13255 (" ", 1, true),
13256 ("world", 5, false),
13257 ],
13258 ),
13259 (
13260 "这是什么 \n 钢笔",
13261 &[
13262 ("这", 1, false),
13263 ("是", 1, false),
13264 ("什", 1, false),
13265 ("么", 1, false),
13266 (" ", 1, true),
13267 ("钢", 1, false),
13268 ("笔", 1, false),
13269 ],
13270 ),
13271 (" mutton", &[(" ", 1, true), ("mutton", 6, false)]),
13272 ];
13273
13274 for (input, result) in tests {
13275 assert_eq!(
13276 WordBreakingTokenizer::new(input).collect::<Vec<_>>(),
13277 result
13278 .iter()
13279 .copied()
13280 .map(|(token, grapheme_len, is_whitespace)| WordBreakToken {
13281 token,
13282 grapheme_len,
13283 is_whitespace,
13284 })
13285 .collect::<Vec<_>>()
13286 );
13287 }
13288}
13289
13290fn wrap_with_prefix(
13291 line_prefix: String,
13292 unwrapped_text: String,
13293 wrap_column: usize,
13294 tab_size: NonZeroU32,
13295) -> String {
13296 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
13297 let mut wrapped_text = String::new();
13298 let mut current_line = line_prefix.clone();
13299
13300 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
13301 let mut current_line_len = line_prefix_len;
13302 for WordBreakToken {
13303 token,
13304 grapheme_len,
13305 is_whitespace,
13306 } in tokenizer
13307 {
13308 if current_line_len + grapheme_len > wrap_column && current_line_len != line_prefix_len {
13309 wrapped_text.push_str(current_line.trim_end());
13310 wrapped_text.push('\n');
13311 current_line.truncate(line_prefix.len());
13312 current_line_len = line_prefix_len;
13313 if !is_whitespace {
13314 current_line.push_str(token);
13315 current_line_len += grapheme_len;
13316 }
13317 } else if !is_whitespace {
13318 current_line.push_str(token);
13319 current_line_len += grapheme_len;
13320 } else if current_line_len != line_prefix_len {
13321 current_line.push(' ');
13322 current_line_len += 1;
13323 }
13324 }
13325
13326 if !current_line.is_empty() {
13327 wrapped_text.push_str(¤t_line);
13328 }
13329 wrapped_text
13330}
13331
13332#[test]
13333fn test_wrap_with_prefix() {
13334 assert_eq!(
13335 wrap_with_prefix(
13336 "# ".to_string(),
13337 "abcdefg".to_string(),
13338 4,
13339 NonZeroU32::new(4).unwrap()
13340 ),
13341 "# abcdefg"
13342 );
13343 assert_eq!(
13344 wrap_with_prefix(
13345 "".to_string(),
13346 "\thello world".to_string(),
13347 8,
13348 NonZeroU32::new(4).unwrap()
13349 ),
13350 "hello\nworld"
13351 );
13352 assert_eq!(
13353 wrap_with_prefix(
13354 "// ".to_string(),
13355 "xx \nyy zz aa bb cc".to_string(),
13356 12,
13357 NonZeroU32::new(4).unwrap()
13358 ),
13359 "// xx yy zz\n// aa bb cc"
13360 );
13361 assert_eq!(
13362 wrap_with_prefix(
13363 String::new(),
13364 "这是什么 \n 钢笔".to_string(),
13365 3,
13366 NonZeroU32::new(4).unwrap()
13367 ),
13368 "这是什\n么 钢\n笔"
13369 );
13370}
13371
13372fn hunks_for_selections(
13373 multi_buffer_snapshot: &MultiBufferSnapshot,
13374 selections: &[Selection<Anchor>],
13375) -> Vec<MultiBufferDiffHunk> {
13376 let buffer_rows_for_selections = selections.iter().map(|selection| {
13377 let head = selection.head();
13378 let tail = selection.tail();
13379 let start = MultiBufferRow(tail.to_point(multi_buffer_snapshot).row);
13380 let end = MultiBufferRow(head.to_point(multi_buffer_snapshot).row);
13381 if start > end {
13382 end..start
13383 } else {
13384 start..end
13385 }
13386 });
13387
13388 hunks_for_rows(buffer_rows_for_selections, multi_buffer_snapshot)
13389}
13390
13391pub fn hunks_for_rows(
13392 rows: impl Iterator<Item = Range<MultiBufferRow>>,
13393 multi_buffer_snapshot: &MultiBufferSnapshot,
13394) -> Vec<MultiBufferDiffHunk> {
13395 let mut hunks = Vec::new();
13396 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
13397 HashMap::default();
13398 for selected_multi_buffer_rows in rows {
13399 let query_rows =
13400 selected_multi_buffer_rows.start..selected_multi_buffer_rows.end.next_row();
13401 for hunk in multi_buffer_snapshot.git_diff_hunks_in_range(query_rows.clone()) {
13402 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
13403 // when the caret is just above or just below the deleted hunk.
13404 let allow_adjacent = hunk_status(&hunk) == DiffHunkStatus::Removed;
13405 let related_to_selection = if allow_adjacent {
13406 hunk.row_range.overlaps(&query_rows)
13407 || hunk.row_range.start == query_rows.end
13408 || hunk.row_range.end == query_rows.start
13409 } else {
13410 // `selected_multi_buffer_rows` are inclusive (e.g. [2..2] means 2nd row is selected)
13411 // `hunk.row_range` is exclusive (e.g. [2..3] means 2nd row is selected)
13412 hunk.row_range.overlaps(&selected_multi_buffer_rows)
13413 || selected_multi_buffer_rows.end == hunk.row_range.start
13414 };
13415 if related_to_selection {
13416 if !processed_buffer_rows
13417 .entry(hunk.buffer_id)
13418 .or_default()
13419 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
13420 {
13421 continue;
13422 }
13423 hunks.push(hunk);
13424 }
13425 }
13426 }
13427
13428 hunks
13429}
13430
13431pub trait CollaborationHub {
13432 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
13433 fn user_participant_indices<'a>(
13434 &self,
13435 cx: &'a AppContext,
13436 ) -> &'a HashMap<u64, ParticipantIndex>;
13437 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
13438}
13439
13440impl CollaborationHub for Model<Project> {
13441 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
13442 self.read(cx).collaborators()
13443 }
13444
13445 fn user_participant_indices<'a>(
13446 &self,
13447 cx: &'a AppContext,
13448 ) -> &'a HashMap<u64, ParticipantIndex> {
13449 self.read(cx).user_store().read(cx).participant_indices()
13450 }
13451
13452 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
13453 let this = self.read(cx);
13454 let user_ids = this.collaborators().values().map(|c| c.user_id);
13455 this.user_store().read_with(cx, |user_store, cx| {
13456 user_store.participant_names(user_ids, cx)
13457 })
13458 }
13459}
13460
13461pub trait SemanticsProvider {
13462 fn hover(
13463 &self,
13464 buffer: &Model<Buffer>,
13465 position: text::Anchor,
13466 cx: &mut AppContext,
13467 ) -> Option<Task<Vec<project::Hover>>>;
13468
13469 fn inlay_hints(
13470 &self,
13471 buffer_handle: Model<Buffer>,
13472 range: Range<text::Anchor>,
13473 cx: &mut AppContext,
13474 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
13475
13476 fn resolve_inlay_hint(
13477 &self,
13478 hint: InlayHint,
13479 buffer_handle: Model<Buffer>,
13480 server_id: LanguageServerId,
13481 cx: &mut AppContext,
13482 ) -> Option<Task<anyhow::Result<InlayHint>>>;
13483
13484 fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool;
13485
13486 fn document_highlights(
13487 &self,
13488 buffer: &Model<Buffer>,
13489 position: text::Anchor,
13490 cx: &mut AppContext,
13491 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
13492
13493 fn definitions(
13494 &self,
13495 buffer: &Model<Buffer>,
13496 position: text::Anchor,
13497 kind: GotoDefinitionKind,
13498 cx: &mut AppContext,
13499 ) -> Option<Task<Result<Vec<LocationLink>>>>;
13500
13501 fn range_for_rename(
13502 &self,
13503 buffer: &Model<Buffer>,
13504 position: text::Anchor,
13505 cx: &mut AppContext,
13506 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
13507
13508 fn perform_rename(
13509 &self,
13510 buffer: &Model<Buffer>,
13511 position: text::Anchor,
13512 new_name: String,
13513 cx: &mut AppContext,
13514 ) -> Option<Task<Result<ProjectTransaction>>>;
13515}
13516
13517pub trait CompletionProvider {
13518 fn completions(
13519 &self,
13520 buffer: &Model<Buffer>,
13521 buffer_position: text::Anchor,
13522 trigger: CompletionContext,
13523 cx: &mut ViewContext<Editor>,
13524 ) -> Task<Result<Vec<Completion>>>;
13525
13526 fn resolve_completions(
13527 &self,
13528 buffer: Model<Buffer>,
13529 completion_indices: Vec<usize>,
13530 completions: Arc<RwLock<Box<[Completion]>>>,
13531 cx: &mut ViewContext<Editor>,
13532 ) -> Task<Result<bool>>;
13533
13534 fn apply_additional_edits_for_completion(
13535 &self,
13536 buffer: Model<Buffer>,
13537 completion: Completion,
13538 push_to_history: bool,
13539 cx: &mut ViewContext<Editor>,
13540 ) -> Task<Result<Option<language::Transaction>>>;
13541
13542 fn is_completion_trigger(
13543 &self,
13544 buffer: &Model<Buffer>,
13545 position: language::Anchor,
13546 text: &str,
13547 trigger_in_words: bool,
13548 cx: &mut ViewContext<Editor>,
13549 ) -> bool;
13550
13551 fn sort_completions(&self) -> bool {
13552 true
13553 }
13554}
13555
13556pub trait CodeActionProvider {
13557 fn code_actions(
13558 &self,
13559 buffer: &Model<Buffer>,
13560 range: Range<text::Anchor>,
13561 cx: &mut WindowContext,
13562 ) -> Task<Result<Vec<CodeAction>>>;
13563
13564 fn apply_code_action(
13565 &self,
13566 buffer_handle: Model<Buffer>,
13567 action: CodeAction,
13568 excerpt_id: ExcerptId,
13569 push_to_history: bool,
13570 cx: &mut WindowContext,
13571 ) -> Task<Result<ProjectTransaction>>;
13572}
13573
13574impl CodeActionProvider for Model<Project> {
13575 fn code_actions(
13576 &self,
13577 buffer: &Model<Buffer>,
13578 range: Range<text::Anchor>,
13579 cx: &mut WindowContext,
13580 ) -> Task<Result<Vec<CodeAction>>> {
13581 self.update(cx, |project, cx| project.code_actions(buffer, range, cx))
13582 }
13583
13584 fn apply_code_action(
13585 &self,
13586 buffer_handle: Model<Buffer>,
13587 action: CodeAction,
13588 _excerpt_id: ExcerptId,
13589 push_to_history: bool,
13590 cx: &mut WindowContext,
13591 ) -> Task<Result<ProjectTransaction>> {
13592 self.update(cx, |project, cx| {
13593 project.apply_code_action(buffer_handle, action, push_to_history, cx)
13594 })
13595 }
13596}
13597
13598fn snippet_completions(
13599 project: &Project,
13600 buffer: &Model<Buffer>,
13601 buffer_position: text::Anchor,
13602 cx: &mut AppContext,
13603) -> Vec<Completion> {
13604 let language = buffer.read(cx).language_at(buffer_position);
13605 let language_name = language.as_ref().map(|language| language.lsp_id());
13606 let snippet_store = project.snippets().read(cx);
13607 let snippets = snippet_store.snippets_for(language_name, cx);
13608
13609 if snippets.is_empty() {
13610 return vec![];
13611 }
13612 let snapshot = buffer.read(cx).text_snapshot();
13613 let chars = snapshot.reversed_chars_for_range(text::Anchor::MIN..buffer_position);
13614
13615 let scope = language.map(|language| language.default_scope());
13616 let classifier = CharClassifier::new(scope).for_completion(true);
13617 let mut last_word = chars
13618 .take_while(|c| classifier.is_word(*c))
13619 .collect::<String>();
13620 last_word = last_word.chars().rev().collect();
13621 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
13622 let to_lsp = |point: &text::Anchor| {
13623 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
13624 point_to_lsp(end)
13625 };
13626 let lsp_end = to_lsp(&buffer_position);
13627 snippets
13628 .into_iter()
13629 .filter_map(|snippet| {
13630 let matching_prefix = snippet
13631 .prefix
13632 .iter()
13633 .find(|prefix| prefix.starts_with(&last_word))?;
13634 let start = as_offset - last_word.len();
13635 let start = snapshot.anchor_before(start);
13636 let range = start..buffer_position;
13637 let lsp_start = to_lsp(&start);
13638 let lsp_range = lsp::Range {
13639 start: lsp_start,
13640 end: lsp_end,
13641 };
13642 Some(Completion {
13643 old_range: range,
13644 new_text: snippet.body.clone(),
13645 label: CodeLabel {
13646 text: matching_prefix.clone(),
13647 runs: vec![],
13648 filter_range: 0..matching_prefix.len(),
13649 },
13650 server_id: LanguageServerId(usize::MAX),
13651 documentation: snippet.description.clone().map(Documentation::SingleLine),
13652 lsp_completion: lsp::CompletionItem {
13653 label: snippet.prefix.first().unwrap().clone(),
13654 kind: Some(CompletionItemKind::SNIPPET),
13655 label_details: snippet.description.as_ref().map(|description| {
13656 lsp::CompletionItemLabelDetails {
13657 detail: Some(description.clone()),
13658 description: None,
13659 }
13660 }),
13661 insert_text_format: Some(InsertTextFormat::SNIPPET),
13662 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13663 lsp::InsertReplaceEdit {
13664 new_text: snippet.body.clone(),
13665 insert: lsp_range,
13666 replace: lsp_range,
13667 },
13668 )),
13669 filter_text: Some(snippet.body.clone()),
13670 sort_text: Some(char::MAX.to_string()),
13671 ..Default::default()
13672 },
13673 confirm: None,
13674 })
13675 })
13676 .collect()
13677}
13678
13679impl CompletionProvider for Model<Project> {
13680 fn completions(
13681 &self,
13682 buffer: &Model<Buffer>,
13683 buffer_position: text::Anchor,
13684 options: CompletionContext,
13685 cx: &mut ViewContext<Editor>,
13686 ) -> Task<Result<Vec<Completion>>> {
13687 self.update(cx, |project, cx| {
13688 let snippets = snippet_completions(project, buffer, buffer_position, cx);
13689 let project_completions = project.completions(buffer, buffer_position, options, cx);
13690 cx.background_executor().spawn(async move {
13691 let mut completions = project_completions.await?;
13692 //let snippets = snippets.into_iter().;
13693 completions.extend(snippets);
13694 Ok(completions)
13695 })
13696 })
13697 }
13698
13699 fn resolve_completions(
13700 &self,
13701 buffer: Model<Buffer>,
13702 completion_indices: Vec<usize>,
13703 completions: Arc<RwLock<Box<[Completion]>>>,
13704 cx: &mut ViewContext<Editor>,
13705 ) -> Task<Result<bool>> {
13706 self.update(cx, |project, cx| {
13707 project.resolve_completions(buffer, completion_indices, completions, cx)
13708 })
13709 }
13710
13711 fn apply_additional_edits_for_completion(
13712 &self,
13713 buffer: Model<Buffer>,
13714 completion: Completion,
13715 push_to_history: bool,
13716 cx: &mut ViewContext<Editor>,
13717 ) -> Task<Result<Option<language::Transaction>>> {
13718 self.update(cx, |project, cx| {
13719 project.apply_additional_edits_for_completion(buffer, completion, push_to_history, cx)
13720 })
13721 }
13722
13723 fn is_completion_trigger(
13724 &self,
13725 buffer: &Model<Buffer>,
13726 position: language::Anchor,
13727 text: &str,
13728 trigger_in_words: bool,
13729 cx: &mut ViewContext<Editor>,
13730 ) -> bool {
13731 if !EditorSettings::get_global(cx).show_completions_on_input {
13732 return false;
13733 }
13734
13735 let mut chars = text.chars();
13736 let char = if let Some(char) = chars.next() {
13737 char
13738 } else {
13739 return false;
13740 };
13741 if chars.next().is_some() {
13742 return false;
13743 }
13744
13745 let buffer = buffer.read(cx);
13746 let classifier = buffer
13747 .snapshot()
13748 .char_classifier_at(position)
13749 .for_completion(true);
13750 if trigger_in_words && classifier.is_word(char) {
13751 return true;
13752 }
13753
13754 buffer
13755 .completion_triggers()
13756 .iter()
13757 .any(|string| string == text)
13758 }
13759}
13760
13761impl SemanticsProvider for Model<Project> {
13762 fn hover(
13763 &self,
13764 buffer: &Model<Buffer>,
13765 position: text::Anchor,
13766 cx: &mut AppContext,
13767 ) -> Option<Task<Vec<project::Hover>>> {
13768 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
13769 }
13770
13771 fn document_highlights(
13772 &self,
13773 buffer: &Model<Buffer>,
13774 position: text::Anchor,
13775 cx: &mut AppContext,
13776 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
13777 Some(self.update(cx, |project, cx| {
13778 project.document_highlights(buffer, position, cx)
13779 }))
13780 }
13781
13782 fn definitions(
13783 &self,
13784 buffer: &Model<Buffer>,
13785 position: text::Anchor,
13786 kind: GotoDefinitionKind,
13787 cx: &mut AppContext,
13788 ) -> Option<Task<Result<Vec<LocationLink>>>> {
13789 Some(self.update(cx, |project, cx| match kind {
13790 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
13791 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
13792 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
13793 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
13794 }))
13795 }
13796
13797 fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool {
13798 // TODO: make this work for remote projects
13799 self.read(cx)
13800 .language_servers_for_buffer(buffer.read(cx), cx)
13801 .any(
13802 |(_, server)| match server.capabilities().inlay_hint_provider {
13803 Some(lsp::OneOf::Left(enabled)) => enabled,
13804 Some(lsp::OneOf::Right(_)) => true,
13805 None => false,
13806 },
13807 )
13808 }
13809
13810 fn inlay_hints(
13811 &self,
13812 buffer_handle: Model<Buffer>,
13813 range: Range<text::Anchor>,
13814 cx: &mut AppContext,
13815 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
13816 Some(self.update(cx, |project, cx| {
13817 project.inlay_hints(buffer_handle, range, cx)
13818 }))
13819 }
13820
13821 fn resolve_inlay_hint(
13822 &self,
13823 hint: InlayHint,
13824 buffer_handle: Model<Buffer>,
13825 server_id: LanguageServerId,
13826 cx: &mut AppContext,
13827 ) -> Option<Task<anyhow::Result<InlayHint>>> {
13828 Some(self.update(cx, |project, cx| {
13829 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
13830 }))
13831 }
13832
13833 fn range_for_rename(
13834 &self,
13835 buffer: &Model<Buffer>,
13836 position: text::Anchor,
13837 cx: &mut AppContext,
13838 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
13839 Some(self.update(cx, |project, cx| {
13840 project.prepare_rename(buffer.clone(), position, cx)
13841 }))
13842 }
13843
13844 fn perform_rename(
13845 &self,
13846 buffer: &Model<Buffer>,
13847 position: text::Anchor,
13848 new_name: String,
13849 cx: &mut AppContext,
13850 ) -> Option<Task<Result<ProjectTransaction>>> {
13851 Some(self.update(cx, |project, cx| {
13852 project.perform_rename(buffer.clone(), position, new_name, cx)
13853 }))
13854 }
13855}
13856
13857fn inlay_hint_settings(
13858 location: Anchor,
13859 snapshot: &MultiBufferSnapshot,
13860 cx: &mut ViewContext<'_, Editor>,
13861) -> InlayHintSettings {
13862 let file = snapshot.file_at(location);
13863 let language = snapshot.language_at(location).map(|l| l.name());
13864 language_settings(language, file, cx).inlay_hints
13865}
13866
13867fn consume_contiguous_rows(
13868 contiguous_row_selections: &mut Vec<Selection<Point>>,
13869 selection: &Selection<Point>,
13870 display_map: &DisplaySnapshot,
13871 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
13872) -> (MultiBufferRow, MultiBufferRow) {
13873 contiguous_row_selections.push(selection.clone());
13874 let start_row = MultiBufferRow(selection.start.row);
13875 let mut end_row = ending_row(selection, display_map);
13876
13877 while let Some(next_selection) = selections.peek() {
13878 if next_selection.start.row <= end_row.0 {
13879 end_row = ending_row(next_selection, display_map);
13880 contiguous_row_selections.push(selections.next().unwrap().clone());
13881 } else {
13882 break;
13883 }
13884 }
13885 (start_row, end_row)
13886}
13887
13888fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
13889 if next_selection.end.column > 0 || next_selection.is_empty() {
13890 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
13891 } else {
13892 MultiBufferRow(next_selection.end.row)
13893 }
13894}
13895
13896impl EditorSnapshot {
13897 pub fn remote_selections_in_range<'a>(
13898 &'a self,
13899 range: &'a Range<Anchor>,
13900 collaboration_hub: &dyn CollaborationHub,
13901 cx: &'a AppContext,
13902 ) -> impl 'a + Iterator<Item = RemoteSelection> {
13903 let participant_names = collaboration_hub.user_names(cx);
13904 let participant_indices = collaboration_hub.user_participant_indices(cx);
13905 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
13906 let collaborators_by_replica_id = collaborators_by_peer_id
13907 .iter()
13908 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
13909 .collect::<HashMap<_, _>>();
13910 self.buffer_snapshot
13911 .selections_in_range(range, false)
13912 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
13913 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
13914 let participant_index = participant_indices.get(&collaborator.user_id).copied();
13915 let user_name = participant_names.get(&collaborator.user_id).cloned();
13916 Some(RemoteSelection {
13917 replica_id,
13918 selection,
13919 cursor_shape,
13920 line_mode,
13921 participant_index,
13922 peer_id: collaborator.peer_id,
13923 user_name,
13924 })
13925 })
13926 }
13927
13928 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
13929 self.display_snapshot.buffer_snapshot.language_at(position)
13930 }
13931
13932 pub fn is_focused(&self) -> bool {
13933 self.is_focused
13934 }
13935
13936 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
13937 self.placeholder_text.as_ref()
13938 }
13939
13940 pub fn scroll_position(&self) -> gpui::Point<f32> {
13941 self.scroll_anchor.scroll_position(&self.display_snapshot)
13942 }
13943
13944 fn gutter_dimensions(
13945 &self,
13946 font_id: FontId,
13947 font_size: Pixels,
13948 em_width: Pixels,
13949 em_advance: Pixels,
13950 max_line_number_width: Pixels,
13951 cx: &AppContext,
13952 ) -> GutterDimensions {
13953 if !self.show_gutter {
13954 return GutterDimensions::default();
13955 }
13956 let descent = cx.text_system().descent(font_id, font_size);
13957
13958 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
13959 matches!(
13960 ProjectSettings::get_global(cx).git.git_gutter,
13961 Some(GitGutterSetting::TrackedFiles)
13962 )
13963 });
13964 let gutter_settings = EditorSettings::get_global(cx).gutter;
13965 let show_line_numbers = self
13966 .show_line_numbers
13967 .unwrap_or(gutter_settings.line_numbers);
13968 let line_gutter_width = if show_line_numbers {
13969 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
13970 let min_width_for_number_on_gutter = em_advance * 4.0;
13971 max_line_number_width.max(min_width_for_number_on_gutter)
13972 } else {
13973 0.0.into()
13974 };
13975
13976 let show_code_actions = self
13977 .show_code_actions
13978 .unwrap_or(gutter_settings.code_actions);
13979
13980 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
13981
13982 let git_blame_entries_width =
13983 self.git_blame_gutter_max_author_length
13984 .map(|max_author_length| {
13985 // Length of the author name, but also space for the commit hash,
13986 // the spacing and the timestamp.
13987 let max_char_count = max_author_length
13988 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
13989 + 7 // length of commit sha
13990 + 14 // length of max relative timestamp ("60 minutes ago")
13991 + 4; // gaps and margins
13992
13993 em_advance * max_char_count
13994 });
13995
13996 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
13997 left_padding += if show_code_actions || show_runnables {
13998 em_width * 3.0
13999 } else if show_git_gutter && show_line_numbers {
14000 em_width * 2.0
14001 } else if show_git_gutter || show_line_numbers {
14002 em_width
14003 } else {
14004 px(0.)
14005 };
14006
14007 let right_padding = if gutter_settings.folds && show_line_numbers {
14008 em_width * 4.0
14009 } else if gutter_settings.folds {
14010 em_width * 3.0
14011 } else if show_line_numbers {
14012 em_width
14013 } else {
14014 px(0.)
14015 };
14016
14017 GutterDimensions {
14018 left_padding,
14019 right_padding,
14020 width: line_gutter_width + left_padding + right_padding,
14021 margin: -descent,
14022 git_blame_entries_width,
14023 }
14024 }
14025
14026 pub fn render_fold_toggle(
14027 &self,
14028 buffer_row: MultiBufferRow,
14029 row_contains_cursor: bool,
14030 editor: View<Editor>,
14031 cx: &mut WindowContext,
14032 ) -> Option<AnyElement> {
14033 let folded = self.is_line_folded(buffer_row);
14034
14035 if let Some(crease) = self
14036 .crease_snapshot
14037 .query_row(buffer_row, &self.buffer_snapshot)
14038 {
14039 let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
14040 if folded {
14041 editor.update(cx, |editor, cx| {
14042 editor.fold_at(&crate::FoldAt { buffer_row }, cx)
14043 });
14044 } else {
14045 editor.update(cx, |editor, cx| {
14046 editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
14047 });
14048 }
14049 });
14050
14051 Some((crease.render_toggle)(
14052 buffer_row,
14053 folded,
14054 toggle_callback,
14055 cx,
14056 ))
14057 } else if folded
14058 || (self.starts_indent(buffer_row) && (row_contains_cursor || self.gutter_hovered))
14059 {
14060 Some(
14061 Disclosure::new(("indent-fold-indicator", buffer_row.0), !folded)
14062 .selected(folded)
14063 .on_click(cx.listener_for(&editor, move |this, _e, cx| {
14064 if folded {
14065 this.unfold_at(&UnfoldAt { buffer_row }, cx);
14066 } else {
14067 this.fold_at(&FoldAt { buffer_row }, cx);
14068 }
14069 }))
14070 .into_any_element(),
14071 )
14072 } else {
14073 None
14074 }
14075 }
14076
14077 pub fn render_crease_trailer(
14078 &self,
14079 buffer_row: MultiBufferRow,
14080 cx: &mut WindowContext,
14081 ) -> Option<AnyElement> {
14082 let folded = self.is_line_folded(buffer_row);
14083 let crease = self
14084 .crease_snapshot
14085 .query_row(buffer_row, &self.buffer_snapshot)?;
14086 Some((crease.render_trailer)(buffer_row, folded, cx))
14087 }
14088}
14089
14090impl Deref for EditorSnapshot {
14091 type Target = DisplaySnapshot;
14092
14093 fn deref(&self) -> &Self::Target {
14094 &self.display_snapshot
14095 }
14096}
14097
14098#[derive(Clone, Debug, PartialEq, Eq)]
14099pub enum EditorEvent {
14100 InputIgnored {
14101 text: Arc<str>,
14102 },
14103 InputHandled {
14104 utf16_range_to_replace: Option<Range<isize>>,
14105 text: Arc<str>,
14106 },
14107 ExcerptsAdded {
14108 buffer: Model<Buffer>,
14109 predecessor: ExcerptId,
14110 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
14111 },
14112 ExcerptsRemoved {
14113 ids: Vec<ExcerptId>,
14114 },
14115 ExcerptsEdited {
14116 ids: Vec<ExcerptId>,
14117 },
14118 ExcerptsExpanded {
14119 ids: Vec<ExcerptId>,
14120 },
14121 BufferEdited,
14122 Edited {
14123 transaction_id: clock::Lamport,
14124 },
14125 Reparsed(BufferId),
14126 Focused,
14127 FocusedIn,
14128 Blurred,
14129 DirtyChanged,
14130 Saved,
14131 TitleChanged,
14132 DiffBaseChanged,
14133 SelectionsChanged {
14134 local: bool,
14135 },
14136 ScrollPositionChanged {
14137 local: bool,
14138 autoscroll: bool,
14139 },
14140 Closed,
14141 TransactionUndone {
14142 transaction_id: clock::Lamport,
14143 },
14144 TransactionBegun {
14145 transaction_id: clock::Lamport,
14146 },
14147 Reloaded,
14148 CursorShapeChanged,
14149}
14150
14151impl EventEmitter<EditorEvent> for Editor {}
14152
14153impl FocusableView for Editor {
14154 fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
14155 self.focus_handle.clone()
14156 }
14157}
14158
14159impl Render for Editor {
14160 fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
14161 let settings = ThemeSettings::get_global(cx);
14162
14163 let mut text_style = match self.mode {
14164 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
14165 color: cx.theme().colors().editor_foreground,
14166 font_family: settings.ui_font.family.clone(),
14167 font_features: settings.ui_font.features.clone(),
14168 font_fallbacks: settings.ui_font.fallbacks.clone(),
14169 font_size: rems(0.875).into(),
14170 font_weight: settings.ui_font.weight,
14171 line_height: relative(settings.buffer_line_height.value()),
14172 ..Default::default()
14173 },
14174 EditorMode::Full => TextStyle {
14175 color: cx.theme().colors().editor_foreground,
14176 font_family: settings.buffer_font.family.clone(),
14177 font_features: settings.buffer_font.features.clone(),
14178 font_fallbacks: settings.buffer_font.fallbacks.clone(),
14179 font_size: settings.buffer_font_size(cx).into(),
14180 font_weight: settings.buffer_font.weight,
14181 line_height: relative(settings.buffer_line_height.value()),
14182 ..Default::default()
14183 },
14184 };
14185 if let Some(text_style_refinement) = &self.text_style_refinement {
14186 text_style.refine(text_style_refinement)
14187 }
14188
14189 let background = match self.mode {
14190 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
14191 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
14192 EditorMode::Full => cx.theme().colors().editor_background,
14193 };
14194
14195 EditorElement::new(
14196 cx.view(),
14197 EditorStyle {
14198 background,
14199 local_player: cx.theme().players().local(),
14200 text: text_style,
14201 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
14202 syntax: cx.theme().syntax().clone(),
14203 status: cx.theme().status().clone(),
14204 inlay_hints_style: make_inlay_hints_style(cx),
14205 suggestions_style: HighlightStyle {
14206 color: Some(cx.theme().status().predictive),
14207 ..HighlightStyle::default()
14208 },
14209 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
14210 },
14211 )
14212 }
14213}
14214
14215impl ViewInputHandler for Editor {
14216 fn text_for_range(
14217 &mut self,
14218 range_utf16: Range<usize>,
14219 cx: &mut ViewContext<Self>,
14220 ) -> Option<String> {
14221 Some(
14222 self.buffer
14223 .read(cx)
14224 .read(cx)
14225 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
14226 .collect(),
14227 )
14228 }
14229
14230 fn selected_text_range(
14231 &mut self,
14232 ignore_disabled_input: bool,
14233 cx: &mut ViewContext<Self>,
14234 ) -> Option<UTF16Selection> {
14235 // Prevent the IME menu from appearing when holding down an alphabetic key
14236 // while input is disabled.
14237 if !ignore_disabled_input && !self.input_enabled {
14238 return None;
14239 }
14240
14241 let selection = self.selections.newest::<OffsetUtf16>(cx);
14242 let range = selection.range();
14243
14244 Some(UTF16Selection {
14245 range: range.start.0..range.end.0,
14246 reversed: selection.reversed,
14247 })
14248 }
14249
14250 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
14251 let snapshot = self.buffer.read(cx).read(cx);
14252 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
14253 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
14254 }
14255
14256 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
14257 self.clear_highlights::<InputComposition>(cx);
14258 self.ime_transaction.take();
14259 }
14260
14261 fn replace_text_in_range(
14262 &mut self,
14263 range_utf16: Option<Range<usize>>,
14264 text: &str,
14265 cx: &mut ViewContext<Self>,
14266 ) {
14267 if !self.input_enabled {
14268 cx.emit(EditorEvent::InputIgnored { text: text.into() });
14269 return;
14270 }
14271
14272 self.transact(cx, |this, cx| {
14273 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
14274 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
14275 Some(this.selection_replacement_ranges(range_utf16, cx))
14276 } else {
14277 this.marked_text_ranges(cx)
14278 };
14279
14280 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
14281 let newest_selection_id = this.selections.newest_anchor().id;
14282 this.selections
14283 .all::<OffsetUtf16>(cx)
14284 .iter()
14285 .zip(ranges_to_replace.iter())
14286 .find_map(|(selection, range)| {
14287 if selection.id == newest_selection_id {
14288 Some(
14289 (range.start.0 as isize - selection.head().0 as isize)
14290 ..(range.end.0 as isize - selection.head().0 as isize),
14291 )
14292 } else {
14293 None
14294 }
14295 })
14296 });
14297
14298 cx.emit(EditorEvent::InputHandled {
14299 utf16_range_to_replace: range_to_replace,
14300 text: text.into(),
14301 });
14302
14303 if let Some(new_selected_ranges) = new_selected_ranges {
14304 this.change_selections(None, cx, |selections| {
14305 selections.select_ranges(new_selected_ranges)
14306 });
14307 this.backspace(&Default::default(), cx);
14308 }
14309
14310 this.handle_input(text, cx);
14311 });
14312
14313 if let Some(transaction) = self.ime_transaction {
14314 self.buffer.update(cx, |buffer, cx| {
14315 buffer.group_until_transaction(transaction, cx);
14316 });
14317 }
14318
14319 self.unmark_text(cx);
14320 }
14321
14322 fn replace_and_mark_text_in_range(
14323 &mut self,
14324 range_utf16: Option<Range<usize>>,
14325 text: &str,
14326 new_selected_range_utf16: Option<Range<usize>>,
14327 cx: &mut ViewContext<Self>,
14328 ) {
14329 if !self.input_enabled {
14330 return;
14331 }
14332
14333 let transaction = self.transact(cx, |this, cx| {
14334 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
14335 let snapshot = this.buffer.read(cx).read(cx);
14336 if let Some(relative_range_utf16) = range_utf16.as_ref() {
14337 for marked_range in &mut marked_ranges {
14338 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
14339 marked_range.start.0 += relative_range_utf16.start;
14340 marked_range.start =
14341 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
14342 marked_range.end =
14343 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
14344 }
14345 }
14346 Some(marked_ranges)
14347 } else if let Some(range_utf16) = range_utf16 {
14348 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
14349 Some(this.selection_replacement_ranges(range_utf16, cx))
14350 } else {
14351 None
14352 };
14353
14354 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
14355 let newest_selection_id = this.selections.newest_anchor().id;
14356 this.selections
14357 .all::<OffsetUtf16>(cx)
14358 .iter()
14359 .zip(ranges_to_replace.iter())
14360 .find_map(|(selection, range)| {
14361 if selection.id == newest_selection_id {
14362 Some(
14363 (range.start.0 as isize - selection.head().0 as isize)
14364 ..(range.end.0 as isize - selection.head().0 as isize),
14365 )
14366 } else {
14367 None
14368 }
14369 })
14370 });
14371
14372 cx.emit(EditorEvent::InputHandled {
14373 utf16_range_to_replace: range_to_replace,
14374 text: text.into(),
14375 });
14376
14377 if let Some(ranges) = ranges_to_replace {
14378 this.change_selections(None, cx, |s| s.select_ranges(ranges));
14379 }
14380
14381 let marked_ranges = {
14382 let snapshot = this.buffer.read(cx).read(cx);
14383 this.selections
14384 .disjoint_anchors()
14385 .iter()
14386 .map(|selection| {
14387 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
14388 })
14389 .collect::<Vec<_>>()
14390 };
14391
14392 if text.is_empty() {
14393 this.unmark_text(cx);
14394 } else {
14395 this.highlight_text::<InputComposition>(
14396 marked_ranges.clone(),
14397 HighlightStyle {
14398 underline: Some(UnderlineStyle {
14399 thickness: px(1.),
14400 color: None,
14401 wavy: false,
14402 }),
14403 ..Default::default()
14404 },
14405 cx,
14406 );
14407 }
14408
14409 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
14410 let use_autoclose = this.use_autoclose;
14411 let use_auto_surround = this.use_auto_surround;
14412 this.set_use_autoclose(false);
14413 this.set_use_auto_surround(false);
14414 this.handle_input(text, cx);
14415 this.set_use_autoclose(use_autoclose);
14416 this.set_use_auto_surround(use_auto_surround);
14417
14418 if let Some(new_selected_range) = new_selected_range_utf16 {
14419 let snapshot = this.buffer.read(cx).read(cx);
14420 let new_selected_ranges = marked_ranges
14421 .into_iter()
14422 .map(|marked_range| {
14423 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
14424 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
14425 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
14426 snapshot.clip_offset_utf16(new_start, Bias::Left)
14427 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
14428 })
14429 .collect::<Vec<_>>();
14430
14431 drop(snapshot);
14432 this.change_selections(None, cx, |selections| {
14433 selections.select_ranges(new_selected_ranges)
14434 });
14435 }
14436 });
14437
14438 self.ime_transaction = self.ime_transaction.or(transaction);
14439 if let Some(transaction) = self.ime_transaction {
14440 self.buffer.update(cx, |buffer, cx| {
14441 buffer.group_until_transaction(transaction, cx);
14442 });
14443 }
14444
14445 if self.text_highlights::<InputComposition>(cx).is_none() {
14446 self.ime_transaction.take();
14447 }
14448 }
14449
14450 fn bounds_for_range(
14451 &mut self,
14452 range_utf16: Range<usize>,
14453 element_bounds: gpui::Bounds<Pixels>,
14454 cx: &mut ViewContext<Self>,
14455 ) -> Option<gpui::Bounds<Pixels>> {
14456 let text_layout_details = self.text_layout_details(cx);
14457 let style = &text_layout_details.editor_style;
14458 let font_id = cx.text_system().resolve_font(&style.text.font());
14459 let font_size = style.text.font_size.to_pixels(cx.rem_size());
14460 let line_height = style.text.line_height_in_pixels(cx.rem_size());
14461
14462 let em_width = cx
14463 .text_system()
14464 .typographic_bounds(font_id, font_size, 'm')
14465 .unwrap()
14466 .size
14467 .width;
14468
14469 let snapshot = self.snapshot(cx);
14470 let scroll_position = snapshot.scroll_position();
14471 let scroll_left = scroll_position.x * em_width;
14472
14473 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
14474 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
14475 + self.gutter_dimensions.width;
14476 let y = line_height * (start.row().as_f32() - scroll_position.y);
14477
14478 Some(Bounds {
14479 origin: element_bounds.origin + point(x, y),
14480 size: size(em_width, line_height),
14481 })
14482 }
14483}
14484
14485trait SelectionExt {
14486 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
14487 fn spanned_rows(
14488 &self,
14489 include_end_if_at_line_start: bool,
14490 map: &DisplaySnapshot,
14491 ) -> Range<MultiBufferRow>;
14492}
14493
14494impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
14495 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
14496 let start = self
14497 .start
14498 .to_point(&map.buffer_snapshot)
14499 .to_display_point(map);
14500 let end = self
14501 .end
14502 .to_point(&map.buffer_snapshot)
14503 .to_display_point(map);
14504 if self.reversed {
14505 end..start
14506 } else {
14507 start..end
14508 }
14509 }
14510
14511 fn spanned_rows(
14512 &self,
14513 include_end_if_at_line_start: bool,
14514 map: &DisplaySnapshot,
14515 ) -> Range<MultiBufferRow> {
14516 let start = self.start.to_point(&map.buffer_snapshot);
14517 let mut end = self.end.to_point(&map.buffer_snapshot);
14518 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
14519 end.row -= 1;
14520 }
14521
14522 let buffer_start = map.prev_line_boundary(start).0;
14523 let buffer_end = map.next_line_boundary(end).0;
14524 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
14525 }
14526}
14527
14528impl<T: InvalidationRegion> InvalidationStack<T> {
14529 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
14530 where
14531 S: Clone + ToOffset,
14532 {
14533 while let Some(region) = self.last() {
14534 let all_selections_inside_invalidation_ranges =
14535 if selections.len() == region.ranges().len() {
14536 selections
14537 .iter()
14538 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
14539 .all(|(selection, invalidation_range)| {
14540 let head = selection.head().to_offset(buffer);
14541 invalidation_range.start <= head && invalidation_range.end >= head
14542 })
14543 } else {
14544 false
14545 };
14546
14547 if all_selections_inside_invalidation_ranges {
14548 break;
14549 } else {
14550 self.pop();
14551 }
14552 }
14553 }
14554}
14555
14556impl<T> Default for InvalidationStack<T> {
14557 fn default() -> Self {
14558 Self(Default::default())
14559 }
14560}
14561
14562impl<T> Deref for InvalidationStack<T> {
14563 type Target = Vec<T>;
14564
14565 fn deref(&self) -> &Self::Target {
14566 &self.0
14567 }
14568}
14569
14570impl<T> DerefMut for InvalidationStack<T> {
14571 fn deref_mut(&mut self) -> &mut Self::Target {
14572 &mut self.0
14573 }
14574}
14575
14576impl InvalidationRegion for SnippetState {
14577 fn ranges(&self) -> &[Range<Anchor>] {
14578 &self.ranges[self.active_index]
14579 }
14580}
14581
14582pub fn diagnostic_block_renderer(
14583 diagnostic: Diagnostic,
14584 max_message_rows: Option<u8>,
14585 allow_closing: bool,
14586 _is_valid: bool,
14587) -> RenderBlock {
14588 let (text_without_backticks, code_ranges) =
14589 highlight_diagnostic_message(&diagnostic, max_message_rows);
14590
14591 Box::new(move |cx: &mut BlockContext| {
14592 let group_id: SharedString = cx.block_id.to_string().into();
14593
14594 let mut text_style = cx.text_style().clone();
14595 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
14596 let theme_settings = ThemeSettings::get_global(cx);
14597 text_style.font_family = theme_settings.buffer_font.family.clone();
14598 text_style.font_style = theme_settings.buffer_font.style;
14599 text_style.font_features = theme_settings.buffer_font.features.clone();
14600 text_style.font_weight = theme_settings.buffer_font.weight;
14601
14602 let multi_line_diagnostic = diagnostic.message.contains('\n');
14603
14604 let buttons = |diagnostic: &Diagnostic| {
14605 if multi_line_diagnostic {
14606 v_flex()
14607 } else {
14608 h_flex()
14609 }
14610 .when(allow_closing, |div| {
14611 div.children(diagnostic.is_primary.then(|| {
14612 IconButton::new("close-block", IconName::XCircle)
14613 .icon_color(Color::Muted)
14614 .size(ButtonSize::Compact)
14615 .style(ButtonStyle::Transparent)
14616 .visible_on_hover(group_id.clone())
14617 .on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
14618 .tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
14619 }))
14620 })
14621 .child(
14622 IconButton::new("copy-block", IconName::Copy)
14623 .icon_color(Color::Muted)
14624 .size(ButtonSize::Compact)
14625 .style(ButtonStyle::Transparent)
14626 .visible_on_hover(group_id.clone())
14627 .on_click({
14628 let message = diagnostic.message.clone();
14629 move |_click, cx| {
14630 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
14631 }
14632 })
14633 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
14634 )
14635 };
14636
14637 let icon_size = buttons(&diagnostic)
14638 .into_any_element()
14639 .layout_as_root(AvailableSpace::min_size(), cx);
14640
14641 h_flex()
14642 .id(cx.block_id)
14643 .group(group_id.clone())
14644 .relative()
14645 .size_full()
14646 .pl(cx.gutter_dimensions.width)
14647 .w(cx.max_width - cx.gutter_dimensions.full_width())
14648 .child(
14649 div()
14650 .flex()
14651 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
14652 .flex_shrink(),
14653 )
14654 .child(buttons(&diagnostic))
14655 .child(div().flex().flex_shrink_0().child(
14656 StyledText::new(text_without_backticks.clone()).with_highlights(
14657 &text_style,
14658 code_ranges.iter().map(|range| {
14659 (
14660 range.clone(),
14661 HighlightStyle {
14662 font_weight: Some(FontWeight::BOLD),
14663 ..Default::default()
14664 },
14665 )
14666 }),
14667 ),
14668 ))
14669 .into_any_element()
14670 })
14671}
14672
14673pub fn highlight_diagnostic_message(
14674 diagnostic: &Diagnostic,
14675 mut max_message_rows: Option<u8>,
14676) -> (SharedString, Vec<Range<usize>>) {
14677 let mut text_without_backticks = String::new();
14678 let mut code_ranges = Vec::new();
14679
14680 if let Some(source) = &diagnostic.source {
14681 text_without_backticks.push_str(source);
14682 code_ranges.push(0..source.len());
14683 text_without_backticks.push_str(": ");
14684 }
14685
14686 let mut prev_offset = 0;
14687 let mut in_code_block = false;
14688 let has_row_limit = max_message_rows.is_some();
14689 let mut newline_indices = diagnostic
14690 .message
14691 .match_indices('\n')
14692 .filter(|_| has_row_limit)
14693 .map(|(ix, _)| ix)
14694 .fuse()
14695 .peekable();
14696
14697 for (quote_ix, _) in diagnostic
14698 .message
14699 .match_indices('`')
14700 .chain([(diagnostic.message.len(), "")])
14701 {
14702 let mut first_newline_ix = None;
14703 let mut last_newline_ix = None;
14704 while let Some(newline_ix) = newline_indices.peek() {
14705 if *newline_ix < quote_ix {
14706 if first_newline_ix.is_none() {
14707 first_newline_ix = Some(*newline_ix);
14708 }
14709 last_newline_ix = Some(*newline_ix);
14710
14711 if let Some(rows_left) = &mut max_message_rows {
14712 if *rows_left == 0 {
14713 break;
14714 } else {
14715 *rows_left -= 1;
14716 }
14717 }
14718 let _ = newline_indices.next();
14719 } else {
14720 break;
14721 }
14722 }
14723 let prev_len = text_without_backticks.len();
14724 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
14725 text_without_backticks.push_str(new_text);
14726 if in_code_block {
14727 code_ranges.push(prev_len..text_without_backticks.len());
14728 }
14729 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
14730 in_code_block = !in_code_block;
14731 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
14732 text_without_backticks.push_str("...");
14733 break;
14734 }
14735 }
14736
14737 (text_without_backticks.into(), code_ranges)
14738}
14739
14740fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
14741 match severity {
14742 DiagnosticSeverity::ERROR => colors.error,
14743 DiagnosticSeverity::WARNING => colors.warning,
14744 DiagnosticSeverity::INFORMATION => colors.info,
14745 DiagnosticSeverity::HINT => colors.info,
14746 _ => colors.ignored,
14747 }
14748}
14749
14750pub fn styled_runs_for_code_label<'a>(
14751 label: &'a CodeLabel,
14752 syntax_theme: &'a theme::SyntaxTheme,
14753) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
14754 let fade_out = HighlightStyle {
14755 fade_out: Some(0.35),
14756 ..Default::default()
14757 };
14758
14759 let mut prev_end = label.filter_range.end;
14760 label
14761 .runs
14762 .iter()
14763 .enumerate()
14764 .flat_map(move |(ix, (range, highlight_id))| {
14765 let style = if let Some(style) = highlight_id.style(syntax_theme) {
14766 style
14767 } else {
14768 return Default::default();
14769 };
14770 let mut muted_style = style;
14771 muted_style.highlight(fade_out);
14772
14773 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
14774 if range.start >= label.filter_range.end {
14775 if range.start > prev_end {
14776 runs.push((prev_end..range.start, fade_out));
14777 }
14778 runs.push((range.clone(), muted_style));
14779 } else if range.end <= label.filter_range.end {
14780 runs.push((range.clone(), style));
14781 } else {
14782 runs.push((range.start..label.filter_range.end, style));
14783 runs.push((label.filter_range.end..range.end, muted_style));
14784 }
14785 prev_end = cmp::max(prev_end, range.end);
14786
14787 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
14788 runs.push((prev_end..label.text.len(), fade_out));
14789 }
14790
14791 runs
14792 })
14793}
14794
14795pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
14796 let mut prev_index = 0;
14797 let mut prev_codepoint: Option<char> = None;
14798 text.char_indices()
14799 .chain([(text.len(), '\0')])
14800 .filter_map(move |(index, codepoint)| {
14801 let prev_codepoint = prev_codepoint.replace(codepoint)?;
14802 let is_boundary = index == text.len()
14803 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
14804 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
14805 if is_boundary {
14806 let chunk = &text[prev_index..index];
14807 prev_index = index;
14808 Some(chunk)
14809 } else {
14810 None
14811 }
14812 })
14813}
14814
14815pub trait RangeToAnchorExt: Sized {
14816 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
14817
14818 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
14819 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
14820 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
14821 }
14822}
14823
14824impl<T: ToOffset> RangeToAnchorExt for Range<T> {
14825 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
14826 let start_offset = self.start.to_offset(snapshot);
14827 let end_offset = self.end.to_offset(snapshot);
14828 if start_offset == end_offset {
14829 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
14830 } else {
14831 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
14832 }
14833 }
14834}
14835
14836pub trait RowExt {
14837 fn as_f32(&self) -> f32;
14838
14839 fn next_row(&self) -> Self;
14840
14841 fn previous_row(&self) -> Self;
14842
14843 fn minus(&self, other: Self) -> u32;
14844}
14845
14846impl RowExt for DisplayRow {
14847 fn as_f32(&self) -> f32 {
14848 self.0 as f32
14849 }
14850
14851 fn next_row(&self) -> Self {
14852 Self(self.0 + 1)
14853 }
14854
14855 fn previous_row(&self) -> Self {
14856 Self(self.0.saturating_sub(1))
14857 }
14858
14859 fn minus(&self, other: Self) -> u32 {
14860 self.0 - other.0
14861 }
14862}
14863
14864impl RowExt for MultiBufferRow {
14865 fn as_f32(&self) -> f32 {
14866 self.0 as f32
14867 }
14868
14869 fn next_row(&self) -> Self {
14870 Self(self.0 + 1)
14871 }
14872
14873 fn previous_row(&self) -> Self {
14874 Self(self.0.saturating_sub(1))
14875 }
14876
14877 fn minus(&self, other: Self) -> u32 {
14878 self.0 - other.0
14879 }
14880}
14881
14882trait RowRangeExt {
14883 type Row;
14884
14885 fn len(&self) -> usize;
14886
14887 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
14888}
14889
14890impl RowRangeExt for Range<MultiBufferRow> {
14891 type Row = MultiBufferRow;
14892
14893 fn len(&self) -> usize {
14894 (self.end.0 - self.start.0) as usize
14895 }
14896
14897 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
14898 (self.start.0..self.end.0).map(MultiBufferRow)
14899 }
14900}
14901
14902impl RowRangeExt for Range<DisplayRow> {
14903 type Row = DisplayRow;
14904
14905 fn len(&self) -> usize {
14906 (self.end.0 - self.start.0) as usize
14907 }
14908
14909 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
14910 (self.start.0..self.end.0).map(DisplayRow)
14911 }
14912}
14913
14914fn hunk_status(hunk: &MultiBufferDiffHunk) -> DiffHunkStatus {
14915 if hunk.diff_base_byte_range.is_empty() {
14916 DiffHunkStatus::Added
14917 } else if hunk.row_range.is_empty() {
14918 DiffHunkStatus::Removed
14919 } else {
14920 DiffHunkStatus::Modified
14921 }
14922}
14923
14924/// If select range has more than one line, we
14925/// just point the cursor to range.start.
14926fn check_multiline_range(buffer: &Buffer, range: Range<usize>) -> Range<usize> {
14927 if buffer.offset_to_point(range.start).row == buffer.offset_to_point(range.end).row {
14928 range
14929 } else {
14930 range.start..range.start
14931 }
14932}