1#![allow(rustdoc::private_intra_doc_links)]
2//! This is the place where everything editor-related is stored (data-wise) and displayed (ui-wise).
3//! The main point of interest in this crate is [`Editor`] type, which is used in every other Zed part as a user input element.
4//! It comes in different flavors: single line, multiline and a fixed height one.
5//!
6//! Editor contains of multiple large submodules:
7//! * [`element`] — the place where all rendering happens
8//! * [`display_map`] - chunks up text in the editor into the logical blocks, establishes coordinates and mapping between each of them.
9//! Contains all metadata related to text transformations (folds, fake inlay text insertions, soft wraps, tab markup, etc.).
10//! * [`inlay_hint_cache`] - is a storage of inlay hints out of LSP requests, responsible for querying LSP and updating `display_map`'s state accordingly.
11//!
12//! All other submodules and structs are mostly concerned with holding editor data about the way it displays current buffer region(s).
13//!
14//! If you're looking to improve Vim mode, you should check out Vim crate that wraps Editor and overrides its behavior.
15pub mod actions;
16mod blame_entry_tooltip;
17mod blink_manager;
18mod clangd_ext;
19mod code_context_menus;
20pub mod display_map;
21mod editor_settings;
22mod editor_settings_controls;
23mod element;
24mod git;
25mod highlight_matching_bracket;
26mod hover_links;
27mod hover_popover;
28mod hunk_diff;
29mod indent_guides;
30mod inlay_hint_cache;
31pub mod items;
32mod linked_editing_ranges;
33mod lsp_ext;
34mod mouse_context_menu;
35pub mod movement;
36mod persistence;
37mod proposed_changes_editor;
38mod rust_analyzer_ext;
39pub mod scroll;
40mod selections_collection;
41pub mod tasks;
42
43#[cfg(test)]
44mod editor_tests;
45#[cfg(test)]
46mod inline_completion_tests;
47mod signature_help;
48#[cfg(any(test, feature = "test-support"))]
49pub mod test;
50
51use ::git::diff::DiffHunkStatus;
52pub(crate) use actions::*;
53pub use actions::{OpenExcerpts, OpenExcerptsSplit};
54use aho_corasick::AhoCorasick;
55use anyhow::{anyhow, Context as _, Result};
56use blink_manager::BlinkManager;
57use client::{Collaborator, ParticipantIndex};
58use clock::ReplicaId;
59use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
60use convert_case::{Case, Casing};
61use display_map::*;
62pub use display_map::{DisplayPoint, FoldPlaceholder};
63pub use editor_settings::{
64 CurrentLineHighlight, EditorSettings, ScrollBeyondLastLine, SearchSettings, ShowScrollbar,
65};
66pub use editor_settings_controls::*;
67use element::LineWithInvisibles;
68pub use element::{
69 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
70};
71use futures::{future, FutureExt};
72use fuzzy::StringMatchCandidate;
73
74use code_context_menus::{
75 AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
76 CompletionsMenu, ContextMenuOrigin,
77};
78use git::blame::GitBlame;
79use gpui::{
80 div, impl_actions, point, prelude::*, px, relative, size, Action, AnyElement, AppContext,
81 AsyncWindowContext, AvailableSpace, Bounds, ClipboardEntry, ClipboardItem, Context,
82 DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusOutEvent, FocusableView, FontId,
83 FontWeight, Global, HighlightStyle, Hsla, InteractiveText, KeyContext, Model, ModelContext,
84 MouseButton, PaintQuad, ParentElement, Pixels, Render, SharedString, Size, Styled, StyledText,
85 Subscription, Task, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle,
86 UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext, WeakFocusHandle,
87 WeakView, WindowContext,
88};
89use highlight_matching_bracket::refresh_matching_bracket_highlights;
90use hover_popover::{hide_hover, HoverState};
91pub(crate) use hunk_diff::HoveredHunk;
92use hunk_diff::{diff_hunk_to_display, DiffMap, DiffMapSnapshot};
93use indent_guides::ActiveIndentGuidesState;
94use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
95pub use inline_completion::Direction;
96use inline_completion::{InlineCompletionProvider, InlineCompletionProviderHandle};
97pub use items::MAX_TAB_TITLE_LEN;
98use itertools::Itertools;
99use language::{
100 language_settings::{self, all_language_settings, language_settings, InlayHintSettings},
101 markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
102 CursorShape, Diagnostic, Documentation, IndentKind, IndentSize, Language, OffsetRangeExt,
103 Point, Selection, SelectionGoal, TransactionId,
104};
105use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
106use linked_editing_ranges::refresh_linked_ranges;
107use mouse_context_menu::MouseContextMenu;
108pub use proposed_changes_editor::{
109 ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar,
110};
111use similar::{ChangeTag, TextDiff};
112use std::iter::Peekable;
113use task::{ResolvedTask, TaskTemplate, TaskVariables};
114
115use hover_links::{find_file, HoverLink, HoveredLinkState, InlayHighlight};
116pub use lsp::CompletionContext;
117use lsp::{
118 CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity, InsertTextFormat,
119 LanguageServerId, LanguageServerName,
120};
121
122use movement::TextLayoutDetails;
123pub use multi_buffer::{
124 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
125 ToPoint,
126};
127use multi_buffer::{
128 ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow, ToOffsetUtf16,
129};
130use parking_lot::RwLock;
131use project::{
132 lsp_store::{FormatTarget, FormatTrigger, OpenLspBufferHandle},
133 project_settings::{GitGutterSetting, ProjectSettings},
134 CodeAction, Completion, CompletionIntent, DocumentHighlight, InlayHint, Location, LocationLink,
135 LspStore, Project, ProjectItem, ProjectTransaction, TaskSourceKind,
136};
137use rand::prelude::*;
138use rpc::{proto::*, ErrorExt};
139use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
140use selections_collection::{
141 resolve_selections, MutableSelectionsCollection, SelectionsCollection,
142};
143use serde::{Deserialize, Serialize};
144use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
145use smallvec::SmallVec;
146use snippet::Snippet;
147use std::{
148 any::TypeId,
149 borrow::Cow,
150 cell::RefCell,
151 cmp::{self, Ordering, Reverse},
152 mem,
153 num::NonZeroU32,
154 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
155 path::{Path, PathBuf},
156 rc::Rc,
157 sync::Arc,
158 time::{Duration, Instant},
159};
160pub use sum_tree::Bias;
161use sum_tree::TreeMap;
162use text::{BufferId, OffsetUtf16, Rope};
163use theme::{
164 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
165 ThemeColors, ThemeSettings,
166};
167use ui::{
168 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize,
169 PopoverMenuHandle, Tooltip,
170};
171use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
172use workspace::item::{ItemHandle, PreviewTabsSettings};
173use workspace::notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt};
174use workspace::{
175 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
176};
177use workspace::{Item as WorkspaceItem, OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
178
179use crate::hover_links::find_url;
180use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
181
182pub const FILE_HEADER_HEIGHT: u32 = 2;
183pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
184pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u32 = 1;
185pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
186const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
187const MAX_LINE_LEN: usize = 1024;
188const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
189const MAX_SELECTION_HISTORY_LEN: usize = 1024;
190pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
191#[doc(hidden)]
192pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
193#[doc(hidden)]
194pub const DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
195
196pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
197pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
198
199pub fn render_parsed_markdown(
200 element_id: impl Into<ElementId>,
201 parsed: &language::ParsedMarkdown,
202 editor_style: &EditorStyle,
203 workspace: Option<WeakView<Workspace>>,
204 cx: &mut WindowContext,
205) -> InteractiveText {
206 let code_span_background_color = cx
207 .theme()
208 .colors()
209 .editor_document_highlight_read_background;
210
211 let highlights = gpui::combine_highlights(
212 parsed.highlights.iter().filter_map(|(range, highlight)| {
213 let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
214 Some((range.clone(), highlight))
215 }),
216 parsed
217 .regions
218 .iter()
219 .zip(&parsed.region_ranges)
220 .filter_map(|(region, range)| {
221 if region.code {
222 Some((
223 range.clone(),
224 HighlightStyle {
225 background_color: Some(code_span_background_color),
226 ..Default::default()
227 },
228 ))
229 } else {
230 None
231 }
232 }),
233 );
234
235 let mut links = Vec::new();
236 let mut link_ranges = Vec::new();
237 for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
238 if let Some(link) = region.link.clone() {
239 links.push(link);
240 link_ranges.push(range.clone());
241 }
242 }
243
244 InteractiveText::new(
245 element_id,
246 StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
247 )
248 .on_click(link_ranges, move |clicked_range_ix, cx| {
249 match &links[clicked_range_ix] {
250 markdown::Link::Web { url } => cx.open_url(url),
251 markdown::Link::Path { path } => {
252 if let Some(workspace) = &workspace {
253 _ = workspace.update(cx, |workspace, cx| {
254 workspace.open_abs_path(path.clone(), false, cx).detach();
255 });
256 }
257 }
258 }
259 })
260}
261
262#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
263pub(crate) enum InlayId {
264 Suggestion(usize),
265 Hint(usize),
266}
267
268impl InlayId {
269 fn id(&self) -> usize {
270 match self {
271 Self::Suggestion(id) => *id,
272 Self::Hint(id) => *id,
273 }
274 }
275}
276
277enum DiffRowHighlight {}
278enum DocumentHighlightRead {}
279enum DocumentHighlightWrite {}
280enum InputComposition {}
281
282#[derive(Debug, Copy, Clone, PartialEq, Eq)]
283pub enum Navigated {
284 Yes,
285 No,
286}
287
288impl Navigated {
289 pub fn from_bool(yes: bool) -> Navigated {
290 if yes {
291 Navigated::Yes
292 } else {
293 Navigated::No
294 }
295 }
296}
297
298pub fn init_settings(cx: &mut AppContext) {
299 EditorSettings::register(cx);
300}
301
302pub fn init(cx: &mut AppContext) {
303 init_settings(cx);
304
305 workspace::register_project_item::<Editor>(cx);
306 workspace::FollowableViewRegistry::register::<Editor>(cx);
307 workspace::register_serializable_item::<Editor>(cx);
308
309 cx.observe_new_views(
310 |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
311 workspace.register_action(Editor::new_file);
312 workspace.register_action(Editor::new_file_vertical);
313 workspace.register_action(Editor::new_file_horizontal);
314 },
315 )
316 .detach();
317
318 cx.on_action(move |_: &workspace::NewFile, cx| {
319 let app_state = workspace::AppState::global(cx);
320 if let Some(app_state) = app_state.upgrade() {
321 workspace::open_new(Default::default(), app_state, cx, |workspace, cx| {
322 Editor::new_file(workspace, &Default::default(), cx)
323 })
324 .detach();
325 }
326 });
327 cx.on_action(move |_: &workspace::NewWindow, cx| {
328 let app_state = workspace::AppState::global(cx);
329 if let Some(app_state) = app_state.upgrade() {
330 workspace::open_new(Default::default(), app_state, cx, |workspace, cx| {
331 Editor::new_file(workspace, &Default::default(), cx)
332 })
333 .detach();
334 }
335 });
336 git::project_diff::init(cx);
337}
338
339pub struct SearchWithinRange;
340
341trait InvalidationRegion {
342 fn ranges(&self) -> &[Range<Anchor>];
343}
344
345#[derive(Clone, Debug, PartialEq)]
346pub enum SelectPhase {
347 Begin {
348 position: DisplayPoint,
349 add: bool,
350 click_count: usize,
351 },
352 BeginColumnar {
353 position: DisplayPoint,
354 reset: bool,
355 goal_column: u32,
356 },
357 Extend {
358 position: DisplayPoint,
359 click_count: usize,
360 },
361 Update {
362 position: DisplayPoint,
363 goal_column: u32,
364 scroll_delta: gpui::Point<f32>,
365 },
366 End,
367}
368
369#[derive(Clone, Debug)]
370pub enum SelectMode {
371 Character,
372 Word(Range<Anchor>),
373 Line(Range<Anchor>),
374 All,
375}
376
377#[derive(Copy, Clone, PartialEq, Eq, Debug)]
378pub enum EditorMode {
379 SingleLine { auto_width: bool },
380 AutoHeight { max_lines: usize },
381 Full,
382}
383
384#[derive(Copy, Clone, Debug)]
385pub enum SoftWrap {
386 /// Prefer not to wrap at all.
387 ///
388 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
389 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
390 GitDiff,
391 /// Prefer a single line generally, unless an overly long line is encountered.
392 None,
393 /// Soft wrap lines that exceed the editor width.
394 EditorWidth,
395 /// Soft wrap lines at the preferred line length.
396 Column(u32),
397 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
398 Bounded(u32),
399}
400
401#[derive(Clone)]
402pub struct EditorStyle {
403 pub background: Hsla,
404 pub local_player: PlayerColor,
405 pub text: TextStyle,
406 pub scrollbar_width: Pixels,
407 pub syntax: Arc<SyntaxTheme>,
408 pub status: StatusColors,
409 pub inlay_hints_style: HighlightStyle,
410 pub suggestions_style: HighlightStyle,
411 pub unnecessary_code_fade: f32,
412}
413
414impl Default for EditorStyle {
415 fn default() -> Self {
416 Self {
417 background: Hsla::default(),
418 local_player: PlayerColor::default(),
419 text: TextStyle::default(),
420 scrollbar_width: Pixels::default(),
421 syntax: Default::default(),
422 // HACK: Status colors don't have a real default.
423 // We should look into removing the status colors from the editor
424 // style and retrieve them directly from the theme.
425 status: StatusColors::dark(),
426 inlay_hints_style: HighlightStyle::default(),
427 suggestions_style: HighlightStyle::default(),
428 unnecessary_code_fade: Default::default(),
429 }
430 }
431}
432
433pub fn make_inlay_hints_style(cx: &WindowContext) -> HighlightStyle {
434 let show_background = language_settings::language_settings(None, None, cx)
435 .inlay_hints
436 .show_background;
437
438 HighlightStyle {
439 color: Some(cx.theme().status().hint),
440 background_color: show_background.then(|| cx.theme().status().hint_background),
441 ..HighlightStyle::default()
442 }
443}
444
445type CompletionId = usize;
446
447enum InlineCompletion {
448 Edit(Vec<(Range<Anchor>, String)>),
449 Move(Anchor),
450}
451
452struct InlineCompletionState {
453 inlay_ids: Vec<InlayId>,
454 completion: InlineCompletion,
455 invalidation_range: Range<Anchor>,
456}
457
458enum InlineCompletionHighlight {}
459
460#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
461struct EditorActionId(usize);
462
463impl EditorActionId {
464 pub fn post_inc(&mut self) -> Self {
465 let answer = self.0;
466
467 *self = Self(answer + 1);
468
469 Self(answer)
470 }
471}
472
473// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
474// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
475
476type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
477type GutterHighlight = (fn(&AppContext) -> Hsla, Arc<[Range<Anchor>]>);
478
479#[derive(Default)]
480struct ScrollbarMarkerState {
481 scrollbar_size: Size<Pixels>,
482 dirty: bool,
483 markers: Arc<[PaintQuad]>,
484 pending_refresh: Option<Task<Result<()>>>,
485}
486
487impl ScrollbarMarkerState {
488 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
489 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
490 }
491}
492
493#[derive(Clone, Debug)]
494struct RunnableTasks {
495 templates: Vec<(TaskSourceKind, TaskTemplate)>,
496 offset: MultiBufferOffset,
497 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
498 column: u32,
499 // Values of all named captures, including those starting with '_'
500 extra_variables: HashMap<String, String>,
501 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
502 context_range: Range<BufferOffset>,
503}
504
505impl RunnableTasks {
506 fn resolve<'a>(
507 &'a self,
508 cx: &'a task::TaskContext,
509 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
510 self.templates.iter().filter_map(|(kind, template)| {
511 template
512 .resolve_task(&kind.to_id_base(), cx)
513 .map(|task| (kind.clone(), task))
514 })
515 }
516}
517
518#[derive(Clone)]
519struct ResolvedTasks {
520 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
521 position: Anchor,
522}
523#[derive(Copy, Clone, Debug)]
524struct MultiBufferOffset(usize);
525#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
526struct BufferOffset(usize);
527
528// Addons allow storing per-editor state in other crates (e.g. Vim)
529pub trait Addon: 'static {
530 fn extend_key_context(&self, _: &mut KeyContext, _: &AppContext) {}
531
532 fn to_any(&self) -> &dyn std::any::Any;
533}
534
535#[derive(Debug, Copy, Clone, PartialEq, Eq)]
536pub enum IsVimMode {
537 Yes,
538 No,
539}
540
541/// Zed's primary text input `View`, allowing users to edit a [`MultiBuffer`]
542///
543/// See the [module level documentation](self) for more information.
544pub struct Editor {
545 focus_handle: FocusHandle,
546 last_focused_descendant: Option<WeakFocusHandle>,
547 /// The text buffer being edited
548 buffer: Model<MultiBuffer>,
549 /// Map of how text in the buffer should be displayed.
550 /// Handles soft wraps, folds, fake inlay text insertions, etc.
551 pub display_map: Model<DisplayMap>,
552 pub selections: SelectionsCollection,
553 pub scroll_manager: ScrollManager,
554 /// When inline assist editors are linked, they all render cursors because
555 /// typing enters text into each of them, even the ones that aren't focused.
556 pub(crate) show_cursor_when_unfocused: bool,
557 columnar_selection_tail: Option<Anchor>,
558 add_selections_state: Option<AddSelectionsState>,
559 select_next_state: Option<SelectNextState>,
560 select_prev_state: Option<SelectNextState>,
561 selection_history: SelectionHistory,
562 autoclose_regions: Vec<AutocloseRegion>,
563 snippet_stack: InvalidationStack<SnippetState>,
564 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
565 ime_transaction: Option<TransactionId>,
566 active_diagnostics: Option<ActiveDiagnosticGroup>,
567 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
568
569 project: Option<Model<Project>>,
570 semantics_provider: Option<Rc<dyn SemanticsProvider>>,
571 completion_provider: Option<Box<dyn CompletionProvider>>,
572 collaboration_hub: Option<Box<dyn CollaborationHub>>,
573 blink_manager: Model<BlinkManager>,
574 show_cursor_names: bool,
575 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
576 pub show_local_selections: bool,
577 mode: EditorMode,
578 show_breadcrumbs: bool,
579 show_gutter: bool,
580 show_line_numbers: Option<bool>,
581 use_relative_line_numbers: Option<bool>,
582 show_git_diff_gutter: Option<bool>,
583 show_code_actions: Option<bool>,
584 show_runnables: Option<bool>,
585 show_wrap_guides: Option<bool>,
586 show_indent_guides: Option<bool>,
587 placeholder_text: Option<Arc<str>>,
588 highlight_order: usize,
589 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
590 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
591 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
592 scrollbar_marker_state: ScrollbarMarkerState,
593 active_indent_guides_state: ActiveIndentGuidesState,
594 nav_history: Option<ItemNavHistory>,
595 context_menu: RwLock<Option<CodeContextMenu>>,
596 mouse_context_menu: Option<MouseContextMenu>,
597 hunk_controls_menu_handle: PopoverMenuHandle<ui::ContextMenu>,
598 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
599 signature_help_state: SignatureHelpState,
600 auto_signature_help: Option<bool>,
601 find_all_references_task_sources: Vec<Anchor>,
602 next_completion_id: CompletionId,
603 available_code_actions: Option<(Location, Arc<[AvailableCodeAction]>)>,
604 code_actions_task: Option<Task<Result<()>>>,
605 document_highlights_task: Option<Task<()>>,
606 linked_editing_range_task: Option<Task<Option<()>>>,
607 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
608 pending_rename: Option<RenameState>,
609 searchable: bool,
610 cursor_shape: CursorShape,
611 current_line_highlight: Option<CurrentLineHighlight>,
612 collapse_matches: bool,
613 autoindent_mode: Option<AutoindentMode>,
614 workspace: Option<(WeakView<Workspace>, Option<WorkspaceId>)>,
615 input_enabled: bool,
616 use_modal_editing: bool,
617 read_only: bool,
618 leader_peer_id: Option<PeerId>,
619 remote_id: Option<ViewId>,
620 hover_state: HoverState,
621 gutter_hovered: bool,
622 hovered_link_state: Option<HoveredLinkState>,
623 inline_completion_provider: Option<RegisteredInlineCompletionProvider>,
624 code_action_providers: Vec<Arc<dyn CodeActionProvider>>,
625 active_inline_completion: Option<InlineCompletionState>,
626 // enable_inline_completions is a switch that Vim can use to disable
627 // inline completions based on its mode.
628 enable_inline_completions: bool,
629 show_inline_completions_override: Option<bool>,
630 inlay_hint_cache: InlayHintCache,
631 diff_map: DiffMap,
632 next_inlay_id: usize,
633 _subscriptions: Vec<Subscription>,
634 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
635 gutter_dimensions: GutterDimensions,
636 style: Option<EditorStyle>,
637 text_style_refinement: Option<TextStyleRefinement>,
638 next_editor_action_id: EditorActionId,
639 editor_actions: Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut ViewContext<Self>)>>>>,
640 use_autoclose: bool,
641 use_auto_surround: bool,
642 auto_replace_emoji_shortcode: bool,
643 show_git_blame_gutter: bool,
644 show_git_blame_inline: bool,
645 show_git_blame_inline_delay_task: Option<Task<()>>,
646 git_blame_inline_enabled: bool,
647 serialize_dirty_buffers: bool,
648 show_selection_menu: Option<bool>,
649 blame: Option<Model<GitBlame>>,
650 blame_subscription: Option<Subscription>,
651 custom_context_menu: Option<
652 Box<
653 dyn 'static
654 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
655 >,
656 >,
657 last_bounds: Option<Bounds<Pixels>>,
658 expect_bounds_change: Option<Bounds<Pixels>>,
659 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
660 tasks_update_task: Option<Task<()>>,
661 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
662 breadcrumb_header: Option<String>,
663 focused_block: Option<FocusedBlock>,
664 next_scroll_position: NextScrollCursorCenterTopBottom,
665 addons: HashMap<TypeId, Box<dyn Addon>>,
666 registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
667 _scroll_cursor_center_top_bottom_task: Task<()>,
668}
669
670#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
671enum NextScrollCursorCenterTopBottom {
672 #[default]
673 Center,
674 Top,
675 Bottom,
676}
677
678impl NextScrollCursorCenterTopBottom {
679 fn next(&self) -> Self {
680 match self {
681 Self::Center => Self::Top,
682 Self::Top => Self::Bottom,
683 Self::Bottom => Self::Center,
684 }
685 }
686}
687
688#[derive(Clone)]
689pub struct EditorSnapshot {
690 pub mode: EditorMode,
691 show_gutter: bool,
692 show_line_numbers: Option<bool>,
693 show_git_diff_gutter: Option<bool>,
694 show_code_actions: Option<bool>,
695 show_runnables: Option<bool>,
696 git_blame_gutter_max_author_length: Option<usize>,
697 pub display_snapshot: DisplaySnapshot,
698 pub placeholder_text: Option<Arc<str>>,
699 diff_map: DiffMapSnapshot,
700 is_focused: bool,
701 scroll_anchor: ScrollAnchor,
702 ongoing_scroll: OngoingScroll,
703 current_line_highlight: CurrentLineHighlight,
704 gutter_hovered: bool,
705}
706
707const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
708
709#[derive(Default, Debug, Clone, Copy)]
710pub struct GutterDimensions {
711 pub left_padding: Pixels,
712 pub right_padding: Pixels,
713 pub width: Pixels,
714 pub margin: Pixels,
715 pub git_blame_entries_width: Option<Pixels>,
716}
717
718impl GutterDimensions {
719 /// The full width of the space taken up by the gutter.
720 pub fn full_width(&self) -> Pixels {
721 self.margin + self.width
722 }
723
724 /// The width of the space reserved for the fold indicators,
725 /// use alongside 'justify_end' and `gutter_width` to
726 /// right align content with the line numbers
727 pub fn fold_area_width(&self) -> Pixels {
728 self.margin + self.right_padding
729 }
730}
731
732#[derive(Debug)]
733pub struct RemoteSelection {
734 pub replica_id: ReplicaId,
735 pub selection: Selection<Anchor>,
736 pub cursor_shape: CursorShape,
737 pub peer_id: PeerId,
738 pub line_mode: bool,
739 pub participant_index: Option<ParticipantIndex>,
740 pub user_name: Option<SharedString>,
741}
742
743#[derive(Clone, Debug)]
744struct SelectionHistoryEntry {
745 selections: Arc<[Selection<Anchor>]>,
746 select_next_state: Option<SelectNextState>,
747 select_prev_state: Option<SelectNextState>,
748 add_selections_state: Option<AddSelectionsState>,
749}
750
751enum SelectionHistoryMode {
752 Normal,
753 Undoing,
754 Redoing,
755}
756
757#[derive(Clone, PartialEq, Eq, Hash)]
758struct HoveredCursor {
759 replica_id: u16,
760 selection_id: usize,
761}
762
763impl Default for SelectionHistoryMode {
764 fn default() -> Self {
765 Self::Normal
766 }
767}
768
769#[derive(Default)]
770struct SelectionHistory {
771 #[allow(clippy::type_complexity)]
772 selections_by_transaction:
773 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
774 mode: SelectionHistoryMode,
775 undo_stack: VecDeque<SelectionHistoryEntry>,
776 redo_stack: VecDeque<SelectionHistoryEntry>,
777}
778
779impl SelectionHistory {
780 fn insert_transaction(
781 &mut self,
782 transaction_id: TransactionId,
783 selections: Arc<[Selection<Anchor>]>,
784 ) {
785 self.selections_by_transaction
786 .insert(transaction_id, (selections, None));
787 }
788
789 #[allow(clippy::type_complexity)]
790 fn transaction(
791 &self,
792 transaction_id: TransactionId,
793 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
794 self.selections_by_transaction.get(&transaction_id)
795 }
796
797 #[allow(clippy::type_complexity)]
798 fn transaction_mut(
799 &mut self,
800 transaction_id: TransactionId,
801 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
802 self.selections_by_transaction.get_mut(&transaction_id)
803 }
804
805 fn push(&mut self, entry: SelectionHistoryEntry) {
806 if !entry.selections.is_empty() {
807 match self.mode {
808 SelectionHistoryMode::Normal => {
809 self.push_undo(entry);
810 self.redo_stack.clear();
811 }
812 SelectionHistoryMode::Undoing => self.push_redo(entry),
813 SelectionHistoryMode::Redoing => self.push_undo(entry),
814 }
815 }
816 }
817
818 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
819 if self
820 .undo_stack
821 .back()
822 .map_or(true, |e| e.selections != entry.selections)
823 {
824 self.undo_stack.push_back(entry);
825 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
826 self.undo_stack.pop_front();
827 }
828 }
829 }
830
831 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
832 if self
833 .redo_stack
834 .back()
835 .map_or(true, |e| e.selections != entry.selections)
836 {
837 self.redo_stack.push_back(entry);
838 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
839 self.redo_stack.pop_front();
840 }
841 }
842 }
843}
844
845struct RowHighlight {
846 index: usize,
847 range: Range<Anchor>,
848 color: Hsla,
849 should_autoscroll: bool,
850}
851
852#[derive(Clone, Debug)]
853struct AddSelectionsState {
854 above: bool,
855 stack: Vec<usize>,
856}
857
858#[derive(Clone)]
859struct SelectNextState {
860 query: AhoCorasick,
861 wordwise: bool,
862 done: bool,
863}
864
865impl std::fmt::Debug for SelectNextState {
866 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
867 f.debug_struct(std::any::type_name::<Self>())
868 .field("wordwise", &self.wordwise)
869 .field("done", &self.done)
870 .finish()
871 }
872}
873
874#[derive(Debug)]
875struct AutocloseRegion {
876 selection_id: usize,
877 range: Range<Anchor>,
878 pair: BracketPair,
879}
880
881#[derive(Debug)]
882struct SnippetState {
883 ranges: Vec<Vec<Range<Anchor>>>,
884 active_index: usize,
885 choices: Vec<Option<Vec<String>>>,
886}
887
888#[doc(hidden)]
889pub struct RenameState {
890 pub range: Range<Anchor>,
891 pub old_name: Arc<str>,
892 pub editor: View<Editor>,
893 block_id: CustomBlockId,
894}
895
896struct InvalidationStack<T>(Vec<T>);
897
898struct RegisteredInlineCompletionProvider {
899 provider: Arc<dyn InlineCompletionProviderHandle>,
900 _subscription: Subscription,
901}
902
903#[derive(Debug)]
904struct ActiveDiagnosticGroup {
905 primary_range: Range<Anchor>,
906 primary_message: String,
907 group_id: usize,
908 blocks: HashMap<CustomBlockId, Diagnostic>,
909 is_valid: bool,
910}
911
912#[derive(Serialize, Deserialize, Clone, Debug)]
913pub struct ClipboardSelection {
914 pub len: usize,
915 pub is_entire_line: bool,
916 pub first_line_indent: u32,
917}
918
919#[derive(Debug)]
920pub(crate) struct NavigationData {
921 cursor_anchor: Anchor,
922 cursor_position: Point,
923 scroll_anchor: ScrollAnchor,
924 scroll_top_row: u32,
925}
926
927#[derive(Debug, Clone, Copy, PartialEq, Eq)]
928pub enum GotoDefinitionKind {
929 Symbol,
930 Declaration,
931 Type,
932 Implementation,
933}
934
935#[derive(Debug, Clone)]
936enum InlayHintRefreshReason {
937 Toggle(bool),
938 SettingsChange(InlayHintSettings),
939 NewLinesShown,
940 BufferEdited(HashSet<Arc<Language>>),
941 RefreshRequested,
942 ExcerptsRemoved(Vec<ExcerptId>),
943}
944
945impl InlayHintRefreshReason {
946 fn description(&self) -> &'static str {
947 match self {
948 Self::Toggle(_) => "toggle",
949 Self::SettingsChange(_) => "settings change",
950 Self::NewLinesShown => "new lines shown",
951 Self::BufferEdited(_) => "buffer edited",
952 Self::RefreshRequested => "refresh requested",
953 Self::ExcerptsRemoved(_) => "excerpts removed",
954 }
955 }
956}
957
958pub(crate) struct FocusedBlock {
959 id: BlockId,
960 focus_handle: WeakFocusHandle,
961}
962
963#[derive(Clone)]
964struct JumpData {
965 excerpt_id: ExcerptId,
966 position: Point,
967 anchor: text::Anchor,
968 path: Option<project::ProjectPath>,
969 line_offset_from_top: u32,
970}
971
972impl Editor {
973 pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
974 let buffer = cx.new_model(|cx| Buffer::local("", cx));
975 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
976 Self::new(
977 EditorMode::SingleLine { auto_width: false },
978 buffer,
979 None,
980 false,
981 cx,
982 )
983 }
984
985 pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
986 let buffer = cx.new_model(|cx| Buffer::local("", cx));
987 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
988 Self::new(EditorMode::Full, buffer, None, false, cx)
989 }
990
991 pub fn auto_width(cx: &mut ViewContext<Self>) -> Self {
992 let buffer = cx.new_model(|cx| Buffer::local("", cx));
993 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
994 Self::new(
995 EditorMode::SingleLine { auto_width: true },
996 buffer,
997 None,
998 false,
999 cx,
1000 )
1001 }
1002
1003 pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
1004 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1005 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1006 Self::new(
1007 EditorMode::AutoHeight { max_lines },
1008 buffer,
1009 None,
1010 false,
1011 cx,
1012 )
1013 }
1014
1015 pub fn for_buffer(
1016 buffer: Model<Buffer>,
1017 project: Option<Model<Project>>,
1018 cx: &mut ViewContext<Self>,
1019 ) -> Self {
1020 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1021 Self::new(EditorMode::Full, buffer, project, false, cx)
1022 }
1023
1024 pub fn for_multibuffer(
1025 buffer: Model<MultiBuffer>,
1026 project: Option<Model<Project>>,
1027 show_excerpt_controls: bool,
1028 cx: &mut ViewContext<Self>,
1029 ) -> Self {
1030 Self::new(EditorMode::Full, buffer, project, show_excerpt_controls, cx)
1031 }
1032
1033 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1034 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1035 let mut clone = Self::new(
1036 self.mode,
1037 self.buffer.clone(),
1038 self.project.clone(),
1039 show_excerpt_controls,
1040 cx,
1041 );
1042 self.display_map.update(cx, |display_map, cx| {
1043 let snapshot = display_map.snapshot(cx);
1044 clone.display_map.update(cx, |display_map, cx| {
1045 display_map.set_state(&snapshot, cx);
1046 });
1047 });
1048 clone.selections.clone_state(&self.selections);
1049 clone.scroll_manager.clone_state(&self.scroll_manager);
1050 clone.searchable = self.searchable;
1051 clone
1052 }
1053
1054 pub fn new(
1055 mode: EditorMode,
1056 buffer: Model<MultiBuffer>,
1057 project: Option<Model<Project>>,
1058 show_excerpt_controls: bool,
1059 cx: &mut ViewContext<Self>,
1060 ) -> Self {
1061 let style = cx.text_style();
1062 let font_size = style.font_size.to_pixels(cx.rem_size());
1063 let editor = cx.view().downgrade();
1064 let fold_placeholder = FoldPlaceholder {
1065 constrain_width: true,
1066 render: Arc::new(move |fold_id, fold_range, cx| {
1067 let editor = editor.clone();
1068 div()
1069 .id(fold_id)
1070 .bg(cx.theme().colors().ghost_element_background)
1071 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1072 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1073 .rounded_sm()
1074 .size_full()
1075 .cursor_pointer()
1076 .child("⋯")
1077 .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
1078 .on_click(move |_, cx| {
1079 editor
1080 .update(cx, |editor, cx| {
1081 editor.unfold_ranges(
1082 &[fold_range.start..fold_range.end],
1083 true,
1084 false,
1085 cx,
1086 );
1087 cx.stop_propagation();
1088 })
1089 .ok();
1090 })
1091 .into_any()
1092 }),
1093 merge_adjacent: true,
1094 ..Default::default()
1095 };
1096 let display_map = cx.new_model(|cx| {
1097 DisplayMap::new(
1098 buffer.clone(),
1099 style.font(),
1100 font_size,
1101 None,
1102 show_excerpt_controls,
1103 FILE_HEADER_HEIGHT,
1104 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1105 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1106 fold_placeholder,
1107 cx,
1108 )
1109 });
1110
1111 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1112
1113 let blink_manager = cx.new_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1114
1115 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1116 .then(|| language_settings::SoftWrap::None);
1117
1118 let mut project_subscriptions = Vec::new();
1119 if mode == EditorMode::Full {
1120 if let Some(project) = project.as_ref() {
1121 if buffer.read(cx).is_singleton() {
1122 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1123 cx.emit(EditorEvent::TitleChanged);
1124 }));
1125 }
1126 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1127 if let project::Event::RefreshInlayHints = event {
1128 editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1129 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1130 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1131 let focus_handle = editor.focus_handle(cx);
1132 if focus_handle.is_focused(cx) {
1133 let snapshot = buffer.read(cx).snapshot();
1134 for (range, snippet) in snippet_edits {
1135 let editor_range =
1136 language::range_from_lsp(*range).to_offset(&snapshot);
1137 editor
1138 .insert_snippet(&[editor_range], snippet.clone(), cx)
1139 .ok();
1140 }
1141 }
1142 }
1143 }
1144 }));
1145 if let Some(task_inventory) = project
1146 .read(cx)
1147 .task_store()
1148 .read(cx)
1149 .task_inventory()
1150 .cloned()
1151 {
1152 project_subscriptions.push(cx.observe(&task_inventory, |editor, _, cx| {
1153 editor.tasks_update_task = Some(editor.refresh_runnables(cx));
1154 }));
1155 }
1156 }
1157 }
1158
1159 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1160
1161 let inlay_hint_settings =
1162 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1163 let focus_handle = cx.focus_handle();
1164 cx.on_focus(&focus_handle, Self::handle_focus).detach();
1165 cx.on_focus_in(&focus_handle, Self::handle_focus_in)
1166 .detach();
1167 cx.on_focus_out(&focus_handle, Self::handle_focus_out)
1168 .detach();
1169 cx.on_blur(&focus_handle, Self::handle_blur).detach();
1170
1171 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1172 Some(false)
1173 } else {
1174 None
1175 };
1176
1177 let mut code_action_providers = Vec::new();
1178 if let Some(project) = project.clone() {
1179 get_unstaged_changes_for_buffers(&project, buffer.read(cx).all_buffers(), cx);
1180 code_action_providers.push(Arc::new(project) as Arc<_>);
1181 }
1182
1183 let mut this = Self {
1184 focus_handle,
1185 show_cursor_when_unfocused: false,
1186 last_focused_descendant: None,
1187 buffer: buffer.clone(),
1188 display_map: display_map.clone(),
1189 selections,
1190 scroll_manager: ScrollManager::new(cx),
1191 columnar_selection_tail: None,
1192 add_selections_state: None,
1193 select_next_state: None,
1194 select_prev_state: None,
1195 selection_history: Default::default(),
1196 autoclose_regions: Default::default(),
1197 snippet_stack: Default::default(),
1198 select_larger_syntax_node_stack: Vec::new(),
1199 ime_transaction: Default::default(),
1200 active_diagnostics: None,
1201 soft_wrap_mode_override,
1202 completion_provider: project.clone().map(|project| Box::new(project) as _),
1203 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
1204 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1205 project,
1206 blink_manager: blink_manager.clone(),
1207 show_local_selections: true,
1208 mode,
1209 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1210 show_gutter: mode == EditorMode::Full,
1211 show_line_numbers: None,
1212 use_relative_line_numbers: None,
1213 show_git_diff_gutter: None,
1214 show_code_actions: None,
1215 show_runnables: None,
1216 show_wrap_guides: None,
1217 show_indent_guides,
1218 placeholder_text: None,
1219 highlight_order: 0,
1220 highlighted_rows: HashMap::default(),
1221 background_highlights: Default::default(),
1222 gutter_highlights: TreeMap::default(),
1223 scrollbar_marker_state: ScrollbarMarkerState::default(),
1224 active_indent_guides_state: ActiveIndentGuidesState::default(),
1225 nav_history: None,
1226 context_menu: RwLock::new(None),
1227 mouse_context_menu: None,
1228 hunk_controls_menu_handle: PopoverMenuHandle::default(),
1229 completion_tasks: Default::default(),
1230 signature_help_state: SignatureHelpState::default(),
1231 auto_signature_help: None,
1232 find_all_references_task_sources: Vec::new(),
1233 next_completion_id: 0,
1234 next_inlay_id: 0,
1235 code_action_providers,
1236 available_code_actions: Default::default(),
1237 code_actions_task: Default::default(),
1238 document_highlights_task: Default::default(),
1239 linked_editing_range_task: Default::default(),
1240 pending_rename: Default::default(),
1241 searchable: true,
1242 cursor_shape: EditorSettings::get_global(cx)
1243 .cursor_shape
1244 .unwrap_or_default(),
1245 current_line_highlight: None,
1246 autoindent_mode: Some(AutoindentMode::EachLine),
1247 collapse_matches: false,
1248 workspace: None,
1249 input_enabled: true,
1250 use_modal_editing: mode == EditorMode::Full,
1251 read_only: false,
1252 use_autoclose: true,
1253 use_auto_surround: true,
1254 auto_replace_emoji_shortcode: false,
1255 leader_peer_id: None,
1256 remote_id: None,
1257 hover_state: Default::default(),
1258 hovered_link_state: Default::default(),
1259 inline_completion_provider: None,
1260 active_inline_completion: None,
1261 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1262 diff_map: DiffMap::default(),
1263 gutter_hovered: false,
1264 pixel_position_of_newest_cursor: None,
1265 last_bounds: None,
1266 expect_bounds_change: None,
1267 gutter_dimensions: GutterDimensions::default(),
1268 style: None,
1269 show_cursor_names: false,
1270 hovered_cursors: Default::default(),
1271 next_editor_action_id: EditorActionId::default(),
1272 editor_actions: Rc::default(),
1273 show_inline_completions_override: None,
1274 enable_inline_completions: true,
1275 custom_context_menu: None,
1276 show_git_blame_gutter: false,
1277 show_git_blame_inline: false,
1278 show_selection_menu: None,
1279 show_git_blame_inline_delay_task: None,
1280 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1281 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1282 .session
1283 .restore_unsaved_buffers,
1284 blame: None,
1285 blame_subscription: None,
1286 tasks: Default::default(),
1287 _subscriptions: vec![
1288 cx.observe(&buffer, Self::on_buffer_changed),
1289 cx.subscribe(&buffer, Self::on_buffer_event),
1290 cx.observe(&display_map, Self::on_display_map_changed),
1291 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1292 cx.observe_global::<SettingsStore>(Self::settings_changed),
1293 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1294 cx.observe_window_activation(|editor, cx| {
1295 let active = cx.is_window_active();
1296 editor.blink_manager.update(cx, |blink_manager, cx| {
1297 if active {
1298 blink_manager.enable(cx);
1299 } else {
1300 blink_manager.disable(cx);
1301 }
1302 });
1303 }),
1304 ],
1305 tasks_update_task: None,
1306 linked_edit_ranges: Default::default(),
1307 previous_search_ranges: None,
1308 breadcrumb_header: None,
1309 focused_block: None,
1310 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1311 addons: HashMap::default(),
1312 registered_buffers: HashMap::default(),
1313 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1314 text_style_refinement: None,
1315 };
1316 this.tasks_update_task = Some(this.refresh_runnables(cx));
1317 this._subscriptions.extend(project_subscriptions);
1318
1319 this.end_selection(cx);
1320 this.scroll_manager.show_scrollbar(cx);
1321
1322 if mode == EditorMode::Full {
1323 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1324 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1325
1326 if this.git_blame_inline_enabled {
1327 this.git_blame_inline_enabled = true;
1328 this.start_git_blame_inline(false, cx);
1329 }
1330
1331 if let Some(buffer) = buffer.read(cx).as_singleton() {
1332 if let Some(project) = this.project.as_ref() {
1333 let lsp_store = project.read(cx).lsp_store();
1334 let handle = lsp_store.update(cx, |lsp_store, cx| {
1335 lsp_store.register_buffer_with_language_servers(&buffer, cx)
1336 });
1337 this.registered_buffers
1338 .insert(buffer.read(cx).remote_id(), handle);
1339 }
1340 }
1341 }
1342
1343 this.report_editor_event("open", None, cx);
1344 this
1345 }
1346
1347 pub fn mouse_menu_is_focused(&self, cx: &WindowContext) -> bool {
1348 self.mouse_context_menu
1349 .as_ref()
1350 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(cx))
1351 }
1352
1353 fn key_context(&self, cx: &ViewContext<Self>) -> KeyContext {
1354 let mut key_context = KeyContext::new_with_defaults();
1355 key_context.add("Editor");
1356 let mode = match self.mode {
1357 EditorMode::SingleLine { .. } => "single_line",
1358 EditorMode::AutoHeight { .. } => "auto_height",
1359 EditorMode::Full => "full",
1360 };
1361
1362 if EditorSettings::jupyter_enabled(cx) {
1363 key_context.add("jupyter");
1364 }
1365
1366 key_context.set("mode", mode);
1367 if self.pending_rename.is_some() {
1368 key_context.add("renaming");
1369 }
1370 if self.context_menu_visible() {
1371 match self.context_menu.read().as_ref() {
1372 Some(CodeContextMenu::Completions(_)) => {
1373 key_context.add("menu");
1374 key_context.add("showing_completions")
1375 }
1376 Some(CodeContextMenu::CodeActions(_)) => {
1377 key_context.add("menu");
1378 key_context.add("showing_code_actions")
1379 }
1380 None => {}
1381 }
1382 }
1383
1384 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
1385 if !self.focus_handle(cx).contains_focused(cx)
1386 || (self.is_focused(cx) || self.mouse_menu_is_focused(cx))
1387 {
1388 for addon in self.addons.values() {
1389 addon.extend_key_context(&mut key_context, cx)
1390 }
1391 }
1392
1393 if let Some(extension) = self
1394 .buffer
1395 .read(cx)
1396 .as_singleton()
1397 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
1398 {
1399 key_context.set("extension", extension.to_string());
1400 }
1401
1402 if self.has_active_inline_completion() {
1403 key_context.add("copilot_suggestion");
1404 key_context.add("inline_completion");
1405 }
1406
1407 key_context
1408 }
1409
1410 pub fn new_file(
1411 workspace: &mut Workspace,
1412 _: &workspace::NewFile,
1413 cx: &mut ViewContext<Workspace>,
1414 ) {
1415 Self::new_in_workspace(workspace, cx).detach_and_prompt_err(
1416 "Failed to create buffer",
1417 cx,
1418 |e, _| match e.error_code() {
1419 ErrorCode::RemoteUpgradeRequired => Some(format!(
1420 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1421 e.error_tag("required").unwrap_or("the latest version")
1422 )),
1423 _ => None,
1424 },
1425 );
1426 }
1427
1428 pub fn new_in_workspace(
1429 workspace: &mut Workspace,
1430 cx: &mut ViewContext<Workspace>,
1431 ) -> Task<Result<View<Editor>>> {
1432 let project = workspace.project().clone();
1433 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1434
1435 cx.spawn(|workspace, mut cx| async move {
1436 let buffer = create.await?;
1437 workspace.update(&mut cx, |workspace, cx| {
1438 let editor =
1439 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx));
1440 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
1441 editor
1442 })
1443 })
1444 }
1445
1446 fn new_file_vertical(
1447 workspace: &mut Workspace,
1448 _: &workspace::NewFileSplitVertical,
1449 cx: &mut ViewContext<Workspace>,
1450 ) {
1451 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), cx)
1452 }
1453
1454 fn new_file_horizontal(
1455 workspace: &mut Workspace,
1456 _: &workspace::NewFileSplitHorizontal,
1457 cx: &mut ViewContext<Workspace>,
1458 ) {
1459 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), cx)
1460 }
1461
1462 fn new_file_in_direction(
1463 workspace: &mut Workspace,
1464 direction: SplitDirection,
1465 cx: &mut ViewContext<Workspace>,
1466 ) {
1467 let project = workspace.project().clone();
1468 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1469
1470 cx.spawn(|workspace, mut cx| async move {
1471 let buffer = create.await?;
1472 workspace.update(&mut cx, move |workspace, cx| {
1473 workspace.split_item(
1474 direction,
1475 Box::new(
1476 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)),
1477 ),
1478 cx,
1479 )
1480 })?;
1481 anyhow::Ok(())
1482 })
1483 .detach_and_prompt_err("Failed to create buffer", cx, |e, _| match e.error_code() {
1484 ErrorCode::RemoteUpgradeRequired => Some(format!(
1485 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1486 e.error_tag("required").unwrap_or("the latest version")
1487 )),
1488 _ => None,
1489 });
1490 }
1491
1492 pub fn leader_peer_id(&self) -> Option<PeerId> {
1493 self.leader_peer_id
1494 }
1495
1496 pub fn buffer(&self) -> &Model<MultiBuffer> {
1497 &self.buffer
1498 }
1499
1500 pub fn workspace(&self) -> Option<View<Workspace>> {
1501 self.workspace.as_ref()?.0.upgrade()
1502 }
1503
1504 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
1505 self.buffer().read(cx).title(cx)
1506 }
1507
1508 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
1509 let git_blame_gutter_max_author_length = self
1510 .render_git_blame_gutter(cx)
1511 .then(|| {
1512 if let Some(blame) = self.blame.as_ref() {
1513 let max_author_length =
1514 blame.update(cx, |blame, cx| blame.max_author_length(cx));
1515 Some(max_author_length)
1516 } else {
1517 None
1518 }
1519 })
1520 .flatten();
1521
1522 EditorSnapshot {
1523 mode: self.mode,
1524 show_gutter: self.show_gutter,
1525 show_line_numbers: self.show_line_numbers,
1526 show_git_diff_gutter: self.show_git_diff_gutter,
1527 show_code_actions: self.show_code_actions,
1528 show_runnables: self.show_runnables,
1529 git_blame_gutter_max_author_length,
1530 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1531 scroll_anchor: self.scroll_manager.anchor(),
1532 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1533 placeholder_text: self.placeholder_text.clone(),
1534 diff_map: self.diff_map.snapshot(),
1535 is_focused: self.focus_handle.is_focused(cx),
1536 current_line_highlight: self
1537 .current_line_highlight
1538 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
1539 gutter_hovered: self.gutter_hovered,
1540 }
1541 }
1542
1543 pub fn language_at<T: ToOffset>(&self, point: T, cx: &AppContext) -> Option<Arc<Language>> {
1544 self.buffer.read(cx).language_at(point, cx)
1545 }
1546
1547 pub fn file_at<T: ToOffset>(
1548 &self,
1549 point: T,
1550 cx: &AppContext,
1551 ) -> Option<Arc<dyn language::File>> {
1552 self.buffer.read(cx).read(cx).file_at(point).cloned()
1553 }
1554
1555 pub fn active_excerpt(
1556 &self,
1557 cx: &AppContext,
1558 ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
1559 self.buffer
1560 .read(cx)
1561 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1562 }
1563
1564 pub fn mode(&self) -> EditorMode {
1565 self.mode
1566 }
1567
1568 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1569 self.collaboration_hub.as_deref()
1570 }
1571
1572 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1573 self.collaboration_hub = Some(hub);
1574 }
1575
1576 pub fn set_custom_context_menu(
1577 &mut self,
1578 f: impl 'static
1579 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
1580 ) {
1581 self.custom_context_menu = Some(Box::new(f))
1582 }
1583
1584 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
1585 self.completion_provider = provider;
1586 }
1587
1588 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
1589 self.semantics_provider.clone()
1590 }
1591
1592 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
1593 self.semantics_provider = provider;
1594 }
1595
1596 pub fn set_inline_completion_provider<T>(
1597 &mut self,
1598 provider: Option<Model<T>>,
1599 cx: &mut ViewContext<Self>,
1600 ) where
1601 T: InlineCompletionProvider,
1602 {
1603 self.inline_completion_provider =
1604 provider.map(|provider| RegisteredInlineCompletionProvider {
1605 _subscription: cx.observe(&provider, |this, _, cx| {
1606 if this.focus_handle.is_focused(cx) {
1607 this.update_visible_inline_completion(cx);
1608 }
1609 }),
1610 provider: Arc::new(provider),
1611 });
1612 self.refresh_inline_completion(false, false, cx);
1613 }
1614
1615 pub fn placeholder_text(&self, _cx: &WindowContext) -> Option<&str> {
1616 self.placeholder_text.as_deref()
1617 }
1618
1619 pub fn set_placeholder_text(
1620 &mut self,
1621 placeholder_text: impl Into<Arc<str>>,
1622 cx: &mut ViewContext<Self>,
1623 ) {
1624 let placeholder_text = Some(placeholder_text.into());
1625 if self.placeholder_text != placeholder_text {
1626 self.placeholder_text = placeholder_text;
1627 cx.notify();
1628 }
1629 }
1630
1631 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
1632 self.cursor_shape = cursor_shape;
1633
1634 // Disrupt blink for immediate user feedback that the cursor shape has changed
1635 self.blink_manager.update(cx, BlinkManager::show_cursor);
1636
1637 cx.notify();
1638 }
1639
1640 pub fn set_current_line_highlight(
1641 &mut self,
1642 current_line_highlight: Option<CurrentLineHighlight>,
1643 ) {
1644 self.current_line_highlight = current_line_highlight;
1645 }
1646
1647 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1648 self.collapse_matches = collapse_matches;
1649 }
1650
1651 pub fn register_buffers_with_language_servers(&mut self, cx: &mut ViewContext<Self>) {
1652 let buffers = self.buffer.read(cx).all_buffers();
1653 let Some(lsp_store) = self.lsp_store(cx) else {
1654 return;
1655 };
1656 lsp_store.update(cx, |lsp_store, cx| {
1657 for buffer in buffers {
1658 self.registered_buffers
1659 .entry(buffer.read(cx).remote_id())
1660 .or_insert_with(|| {
1661 lsp_store.register_buffer_with_language_servers(&buffer, cx)
1662 });
1663 }
1664 })
1665 }
1666
1667 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1668 if self.collapse_matches {
1669 return range.start..range.start;
1670 }
1671 range.clone()
1672 }
1673
1674 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
1675 if self.display_map.read(cx).clip_at_line_ends != clip {
1676 self.display_map
1677 .update(cx, |map, _| map.clip_at_line_ends = clip);
1678 }
1679 }
1680
1681 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1682 self.input_enabled = input_enabled;
1683 }
1684
1685 pub fn set_inline_completions_enabled(&mut self, enabled: bool) {
1686 self.enable_inline_completions = enabled;
1687 }
1688
1689 pub fn set_autoindent(&mut self, autoindent: bool) {
1690 if autoindent {
1691 self.autoindent_mode = Some(AutoindentMode::EachLine);
1692 } else {
1693 self.autoindent_mode = None;
1694 }
1695 }
1696
1697 pub fn read_only(&self, cx: &AppContext) -> bool {
1698 self.read_only || self.buffer.read(cx).read_only()
1699 }
1700
1701 pub fn set_read_only(&mut self, read_only: bool) {
1702 self.read_only = read_only;
1703 }
1704
1705 pub fn set_use_autoclose(&mut self, autoclose: bool) {
1706 self.use_autoclose = autoclose;
1707 }
1708
1709 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
1710 self.use_auto_surround = auto_surround;
1711 }
1712
1713 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
1714 self.auto_replace_emoji_shortcode = auto_replace;
1715 }
1716
1717 pub fn toggle_inline_completions(
1718 &mut self,
1719 _: &ToggleInlineCompletions,
1720 cx: &mut ViewContext<Self>,
1721 ) {
1722 if self.show_inline_completions_override.is_some() {
1723 self.set_show_inline_completions(None, cx);
1724 } else {
1725 let cursor = self.selections.newest_anchor().head();
1726 if let Some((buffer, cursor_buffer_position)) =
1727 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
1728 {
1729 let show_inline_completions =
1730 !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx);
1731 self.set_show_inline_completions(Some(show_inline_completions), cx);
1732 }
1733 }
1734 }
1735
1736 pub fn set_show_inline_completions(
1737 &mut self,
1738 show_inline_completions: Option<bool>,
1739 cx: &mut ViewContext<Self>,
1740 ) {
1741 self.show_inline_completions_override = show_inline_completions;
1742 self.refresh_inline_completion(false, true, cx);
1743 }
1744
1745 fn should_show_inline_completions(
1746 &self,
1747 buffer: &Model<Buffer>,
1748 buffer_position: language::Anchor,
1749 cx: &AppContext,
1750 ) -> bool {
1751 if !self.snippet_stack.is_empty() {
1752 return false;
1753 }
1754
1755 if self.inline_completions_disabled_in_scope(buffer, buffer_position, cx) {
1756 return false;
1757 }
1758
1759 if let Some(provider) = self.inline_completion_provider() {
1760 if let Some(show_inline_completions) = self.show_inline_completions_override {
1761 show_inline_completions
1762 } else {
1763 self.mode == EditorMode::Full && provider.is_enabled(buffer, buffer_position, cx)
1764 }
1765 } else {
1766 false
1767 }
1768 }
1769
1770 fn inline_completions_disabled_in_scope(
1771 &self,
1772 buffer: &Model<Buffer>,
1773 buffer_position: language::Anchor,
1774 cx: &AppContext,
1775 ) -> bool {
1776 let snapshot = buffer.read(cx).snapshot();
1777 let settings = snapshot.settings_at(buffer_position, cx);
1778
1779 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
1780 return false;
1781 };
1782
1783 scope.override_name().map_or(false, |scope_name| {
1784 settings
1785 .inline_completions_disabled_in
1786 .iter()
1787 .any(|s| s == scope_name)
1788 })
1789 }
1790
1791 pub fn set_use_modal_editing(&mut self, to: bool) {
1792 self.use_modal_editing = to;
1793 }
1794
1795 pub fn use_modal_editing(&self) -> bool {
1796 self.use_modal_editing
1797 }
1798
1799 fn selections_did_change(
1800 &mut self,
1801 local: bool,
1802 old_cursor_position: &Anchor,
1803 show_completions: bool,
1804 cx: &mut ViewContext<Self>,
1805 ) {
1806 cx.invalidate_character_coordinates();
1807
1808 // Copy selections to primary selection buffer
1809 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
1810 if local {
1811 let selections = self.selections.all::<usize>(cx);
1812 let buffer_handle = self.buffer.read(cx).read(cx);
1813
1814 let mut text = String::new();
1815 for (index, selection) in selections.iter().enumerate() {
1816 let text_for_selection = buffer_handle
1817 .text_for_range(selection.start..selection.end)
1818 .collect::<String>();
1819
1820 text.push_str(&text_for_selection);
1821 if index != selections.len() - 1 {
1822 text.push('\n');
1823 }
1824 }
1825
1826 if !text.is_empty() {
1827 cx.write_to_primary(ClipboardItem::new_string(text));
1828 }
1829 }
1830
1831 if self.focus_handle.is_focused(cx) && self.leader_peer_id.is_none() {
1832 self.buffer.update(cx, |buffer, cx| {
1833 buffer.set_active_selections(
1834 &self.selections.disjoint_anchors(),
1835 self.selections.line_mode,
1836 self.cursor_shape,
1837 cx,
1838 )
1839 });
1840 }
1841 let display_map = self
1842 .display_map
1843 .update(cx, |display_map, cx| display_map.snapshot(cx));
1844 let buffer = &display_map.buffer_snapshot;
1845 self.add_selections_state = None;
1846 self.select_next_state = None;
1847 self.select_prev_state = None;
1848 self.select_larger_syntax_node_stack.clear();
1849 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
1850 self.snippet_stack
1851 .invalidate(&self.selections.disjoint_anchors(), buffer);
1852 self.take_rename(false, cx);
1853
1854 let new_cursor_position = self.selections.newest_anchor().head();
1855
1856 self.push_to_nav_history(
1857 *old_cursor_position,
1858 Some(new_cursor_position.to_point(buffer)),
1859 cx,
1860 );
1861
1862 if local {
1863 let new_cursor_position = self.selections.newest_anchor().head();
1864 let mut context_menu = self.context_menu.write();
1865 let completion_menu = match context_menu.as_ref() {
1866 Some(CodeContextMenu::Completions(menu)) => Some(menu),
1867
1868 _ => {
1869 *context_menu = None;
1870 None
1871 }
1872 };
1873
1874 if let Some(completion_menu) = completion_menu {
1875 let cursor_position = new_cursor_position.to_offset(buffer);
1876 let (word_range, kind) =
1877 buffer.surrounding_word(completion_menu.initial_position, true);
1878 if kind == Some(CharKind::Word)
1879 && word_range.to_inclusive().contains(&cursor_position)
1880 {
1881 let mut completion_menu = completion_menu.clone();
1882 drop(context_menu);
1883
1884 let query = Self::completion_query(buffer, cursor_position);
1885 cx.spawn(move |this, mut cx| async move {
1886 completion_menu
1887 .filter(query.as_deref(), cx.background_executor().clone())
1888 .await;
1889
1890 this.update(&mut cx, |this, cx| {
1891 let mut context_menu = this.context_menu.write();
1892 let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
1893 else {
1894 return;
1895 };
1896
1897 if menu.id > completion_menu.id {
1898 return;
1899 }
1900
1901 *context_menu = Some(CodeContextMenu::Completions(completion_menu));
1902 drop(context_menu);
1903 cx.notify();
1904 })
1905 })
1906 .detach();
1907
1908 if show_completions {
1909 self.show_completions(&ShowCompletions { trigger: None }, cx);
1910 }
1911 } else {
1912 drop(context_menu);
1913 self.hide_context_menu(cx);
1914 }
1915 } else {
1916 drop(context_menu);
1917 }
1918
1919 hide_hover(self, cx);
1920
1921 if old_cursor_position.to_display_point(&display_map).row()
1922 != new_cursor_position.to_display_point(&display_map).row()
1923 {
1924 self.available_code_actions.take();
1925 }
1926 self.refresh_code_actions(cx);
1927 self.refresh_document_highlights(cx);
1928 refresh_matching_bracket_highlights(self, cx);
1929 self.update_visible_inline_completion(cx);
1930 linked_editing_ranges::refresh_linked_ranges(self, cx);
1931 if self.git_blame_inline_enabled {
1932 self.start_inline_blame_timer(cx);
1933 }
1934 }
1935
1936 self.blink_manager.update(cx, BlinkManager::pause_blinking);
1937 cx.emit(EditorEvent::SelectionsChanged { local });
1938
1939 if self.selections.disjoint_anchors().len() == 1 {
1940 cx.emit(SearchEvent::ActiveMatchChanged)
1941 }
1942 cx.notify();
1943 }
1944
1945 pub fn change_selections<R>(
1946 &mut self,
1947 autoscroll: Option<Autoscroll>,
1948 cx: &mut ViewContext<Self>,
1949 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
1950 ) -> R {
1951 self.change_selections_inner(autoscroll, true, cx, change)
1952 }
1953
1954 pub fn change_selections_inner<R>(
1955 &mut self,
1956 autoscroll: Option<Autoscroll>,
1957 request_completions: bool,
1958 cx: &mut ViewContext<Self>,
1959 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
1960 ) -> R {
1961 let old_cursor_position = self.selections.newest_anchor().head();
1962 self.push_to_selection_history();
1963
1964 let (changed, result) = self.selections.change_with(cx, change);
1965
1966 if changed {
1967 if let Some(autoscroll) = autoscroll {
1968 self.request_autoscroll(autoscroll, cx);
1969 }
1970 self.selections_did_change(true, &old_cursor_position, request_completions, cx);
1971
1972 if self.should_open_signature_help_automatically(
1973 &old_cursor_position,
1974 self.signature_help_state.backspace_pressed(),
1975 cx,
1976 ) {
1977 self.show_signature_help(&ShowSignatureHelp, cx);
1978 }
1979 self.signature_help_state.set_backspace_pressed(false);
1980 }
1981
1982 result
1983 }
1984
1985 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1986 where
1987 I: IntoIterator<Item = (Range<S>, T)>,
1988 S: ToOffset,
1989 T: Into<Arc<str>>,
1990 {
1991 if self.read_only(cx) {
1992 return;
1993 }
1994
1995 self.buffer
1996 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
1997 }
1998
1999 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2000 where
2001 I: IntoIterator<Item = (Range<S>, T)>,
2002 S: ToOffset,
2003 T: Into<Arc<str>>,
2004 {
2005 if self.read_only(cx) {
2006 return;
2007 }
2008
2009 self.buffer.update(cx, |buffer, cx| {
2010 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2011 });
2012 }
2013
2014 pub fn edit_with_block_indent<I, S, T>(
2015 &mut self,
2016 edits: I,
2017 original_indent_columns: Vec<u32>,
2018 cx: &mut ViewContext<Self>,
2019 ) where
2020 I: IntoIterator<Item = (Range<S>, T)>,
2021 S: ToOffset,
2022 T: Into<Arc<str>>,
2023 {
2024 if self.read_only(cx) {
2025 return;
2026 }
2027
2028 self.buffer.update(cx, |buffer, cx| {
2029 buffer.edit(
2030 edits,
2031 Some(AutoindentMode::Block {
2032 original_indent_columns,
2033 }),
2034 cx,
2035 )
2036 });
2037 }
2038
2039 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
2040 self.hide_context_menu(cx);
2041
2042 match phase {
2043 SelectPhase::Begin {
2044 position,
2045 add,
2046 click_count,
2047 } => self.begin_selection(position, add, click_count, cx),
2048 SelectPhase::BeginColumnar {
2049 position,
2050 goal_column,
2051 reset,
2052 } => self.begin_columnar_selection(position, goal_column, reset, cx),
2053 SelectPhase::Extend {
2054 position,
2055 click_count,
2056 } => self.extend_selection(position, click_count, cx),
2057 SelectPhase::Update {
2058 position,
2059 goal_column,
2060 scroll_delta,
2061 } => self.update_selection(position, goal_column, scroll_delta, cx),
2062 SelectPhase::End => self.end_selection(cx),
2063 }
2064 }
2065
2066 fn extend_selection(
2067 &mut self,
2068 position: DisplayPoint,
2069 click_count: usize,
2070 cx: &mut ViewContext<Self>,
2071 ) {
2072 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2073 let tail = self.selections.newest::<usize>(cx).tail();
2074 self.begin_selection(position, false, click_count, cx);
2075
2076 let position = position.to_offset(&display_map, Bias::Left);
2077 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2078
2079 let mut pending_selection = self
2080 .selections
2081 .pending_anchor()
2082 .expect("extend_selection not called with pending selection");
2083 if position >= tail {
2084 pending_selection.start = tail_anchor;
2085 } else {
2086 pending_selection.end = tail_anchor;
2087 pending_selection.reversed = true;
2088 }
2089
2090 let mut pending_mode = self.selections.pending_mode().unwrap();
2091 match &mut pending_mode {
2092 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2093 _ => {}
2094 }
2095
2096 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
2097 s.set_pending(pending_selection, pending_mode)
2098 });
2099 }
2100
2101 fn begin_selection(
2102 &mut self,
2103 position: DisplayPoint,
2104 add: bool,
2105 click_count: usize,
2106 cx: &mut ViewContext<Self>,
2107 ) {
2108 if !self.focus_handle.is_focused(cx) {
2109 self.last_focused_descendant = None;
2110 cx.focus(&self.focus_handle);
2111 }
2112
2113 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2114 let buffer = &display_map.buffer_snapshot;
2115 let newest_selection = self.selections.newest_anchor().clone();
2116 let position = display_map.clip_point(position, Bias::Left);
2117
2118 let start;
2119 let end;
2120 let mode;
2121 let mut auto_scroll;
2122 match click_count {
2123 1 => {
2124 start = buffer.anchor_before(position.to_point(&display_map));
2125 end = start;
2126 mode = SelectMode::Character;
2127 auto_scroll = true;
2128 }
2129 2 => {
2130 let range = movement::surrounding_word(&display_map, position);
2131 start = buffer.anchor_before(range.start.to_point(&display_map));
2132 end = buffer.anchor_before(range.end.to_point(&display_map));
2133 mode = SelectMode::Word(start..end);
2134 auto_scroll = true;
2135 }
2136 3 => {
2137 let position = display_map
2138 .clip_point(position, Bias::Left)
2139 .to_point(&display_map);
2140 let line_start = display_map.prev_line_boundary(position).0;
2141 let next_line_start = buffer.clip_point(
2142 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2143 Bias::Left,
2144 );
2145 start = buffer.anchor_before(line_start);
2146 end = buffer.anchor_before(next_line_start);
2147 mode = SelectMode::Line(start..end);
2148 auto_scroll = true;
2149 }
2150 _ => {
2151 start = buffer.anchor_before(0);
2152 end = buffer.anchor_before(buffer.len());
2153 mode = SelectMode::All;
2154 auto_scroll = false;
2155 }
2156 }
2157 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2158
2159 let point_to_delete: Option<usize> = {
2160 let selected_points: Vec<Selection<Point>> =
2161 self.selections.disjoint_in_range(start..end, cx);
2162
2163 if !add || click_count > 1 {
2164 None
2165 } else if !selected_points.is_empty() {
2166 Some(selected_points[0].id)
2167 } else {
2168 let clicked_point_already_selected =
2169 self.selections.disjoint.iter().find(|selection| {
2170 selection.start.to_point(buffer) == start.to_point(buffer)
2171 || selection.end.to_point(buffer) == end.to_point(buffer)
2172 });
2173
2174 clicked_point_already_selected.map(|selection| selection.id)
2175 }
2176 };
2177
2178 let selections_count = self.selections.count();
2179
2180 self.change_selections(auto_scroll.then(Autoscroll::newest), cx, |s| {
2181 if let Some(point_to_delete) = point_to_delete {
2182 s.delete(point_to_delete);
2183
2184 if selections_count == 1 {
2185 s.set_pending_anchor_range(start..end, mode);
2186 }
2187 } else {
2188 if !add {
2189 s.clear_disjoint();
2190 } else if click_count > 1 {
2191 s.delete(newest_selection.id)
2192 }
2193
2194 s.set_pending_anchor_range(start..end, mode);
2195 }
2196 });
2197 }
2198
2199 fn begin_columnar_selection(
2200 &mut self,
2201 position: DisplayPoint,
2202 goal_column: u32,
2203 reset: bool,
2204 cx: &mut ViewContext<Self>,
2205 ) {
2206 if !self.focus_handle.is_focused(cx) {
2207 self.last_focused_descendant = None;
2208 cx.focus(&self.focus_handle);
2209 }
2210
2211 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2212
2213 if reset {
2214 let pointer_position = display_map
2215 .buffer_snapshot
2216 .anchor_before(position.to_point(&display_map));
2217
2218 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
2219 s.clear_disjoint();
2220 s.set_pending_anchor_range(
2221 pointer_position..pointer_position,
2222 SelectMode::Character,
2223 );
2224 });
2225 }
2226
2227 let tail = self.selections.newest::<Point>(cx).tail();
2228 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2229
2230 if !reset {
2231 self.select_columns(
2232 tail.to_display_point(&display_map),
2233 position,
2234 goal_column,
2235 &display_map,
2236 cx,
2237 );
2238 }
2239 }
2240
2241 fn update_selection(
2242 &mut self,
2243 position: DisplayPoint,
2244 goal_column: u32,
2245 scroll_delta: gpui::Point<f32>,
2246 cx: &mut ViewContext<Self>,
2247 ) {
2248 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2249
2250 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2251 let tail = tail.to_display_point(&display_map);
2252 self.select_columns(tail, position, goal_column, &display_map, cx);
2253 } else if let Some(mut pending) = self.selections.pending_anchor() {
2254 let buffer = self.buffer.read(cx).snapshot(cx);
2255 let head;
2256 let tail;
2257 let mode = self.selections.pending_mode().unwrap();
2258 match &mode {
2259 SelectMode::Character => {
2260 head = position.to_point(&display_map);
2261 tail = pending.tail().to_point(&buffer);
2262 }
2263 SelectMode::Word(original_range) => {
2264 let original_display_range = original_range.start.to_display_point(&display_map)
2265 ..original_range.end.to_display_point(&display_map);
2266 let original_buffer_range = original_display_range.start.to_point(&display_map)
2267 ..original_display_range.end.to_point(&display_map);
2268 if movement::is_inside_word(&display_map, position)
2269 || original_display_range.contains(&position)
2270 {
2271 let word_range = movement::surrounding_word(&display_map, position);
2272 if word_range.start < original_display_range.start {
2273 head = word_range.start.to_point(&display_map);
2274 } else {
2275 head = word_range.end.to_point(&display_map);
2276 }
2277 } else {
2278 head = position.to_point(&display_map);
2279 }
2280
2281 if head <= original_buffer_range.start {
2282 tail = original_buffer_range.end;
2283 } else {
2284 tail = original_buffer_range.start;
2285 }
2286 }
2287 SelectMode::Line(original_range) => {
2288 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2289
2290 let position = display_map
2291 .clip_point(position, Bias::Left)
2292 .to_point(&display_map);
2293 let line_start = display_map.prev_line_boundary(position).0;
2294 let next_line_start = buffer.clip_point(
2295 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2296 Bias::Left,
2297 );
2298
2299 if line_start < original_range.start {
2300 head = line_start
2301 } else {
2302 head = next_line_start
2303 }
2304
2305 if head <= original_range.start {
2306 tail = original_range.end;
2307 } else {
2308 tail = original_range.start;
2309 }
2310 }
2311 SelectMode::All => {
2312 return;
2313 }
2314 };
2315
2316 if head < tail {
2317 pending.start = buffer.anchor_before(head);
2318 pending.end = buffer.anchor_before(tail);
2319 pending.reversed = true;
2320 } else {
2321 pending.start = buffer.anchor_before(tail);
2322 pending.end = buffer.anchor_before(head);
2323 pending.reversed = false;
2324 }
2325
2326 self.change_selections(None, cx, |s| {
2327 s.set_pending(pending, mode);
2328 });
2329 } else {
2330 log::error!("update_selection dispatched with no pending selection");
2331 return;
2332 }
2333
2334 self.apply_scroll_delta(scroll_delta, cx);
2335 cx.notify();
2336 }
2337
2338 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
2339 self.columnar_selection_tail.take();
2340 if self.selections.pending_anchor().is_some() {
2341 let selections = self.selections.all::<usize>(cx);
2342 self.change_selections(None, cx, |s| {
2343 s.select(selections);
2344 s.clear_pending();
2345 });
2346 }
2347 }
2348
2349 fn select_columns(
2350 &mut self,
2351 tail: DisplayPoint,
2352 head: DisplayPoint,
2353 goal_column: u32,
2354 display_map: &DisplaySnapshot,
2355 cx: &mut ViewContext<Self>,
2356 ) {
2357 let start_row = cmp::min(tail.row(), head.row());
2358 let end_row = cmp::max(tail.row(), head.row());
2359 let start_column = cmp::min(tail.column(), goal_column);
2360 let end_column = cmp::max(tail.column(), goal_column);
2361 let reversed = start_column < tail.column();
2362
2363 let selection_ranges = (start_row.0..=end_row.0)
2364 .map(DisplayRow)
2365 .filter_map(|row| {
2366 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2367 let start = display_map
2368 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2369 .to_point(display_map);
2370 let end = display_map
2371 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2372 .to_point(display_map);
2373 if reversed {
2374 Some(end..start)
2375 } else {
2376 Some(start..end)
2377 }
2378 } else {
2379 None
2380 }
2381 })
2382 .collect::<Vec<_>>();
2383
2384 self.change_selections(None, cx, |s| {
2385 s.select_ranges(selection_ranges);
2386 });
2387 cx.notify();
2388 }
2389
2390 pub fn has_pending_nonempty_selection(&self) -> bool {
2391 let pending_nonempty_selection = match self.selections.pending_anchor() {
2392 Some(Selection { start, end, .. }) => start != end,
2393 None => false,
2394 };
2395
2396 pending_nonempty_selection
2397 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2398 }
2399
2400 pub fn has_pending_selection(&self) -> bool {
2401 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2402 }
2403
2404 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
2405 if self.clear_expanded_diff_hunks(cx) {
2406 cx.notify();
2407 return;
2408 }
2409 if self.dismiss_menus_and_popups(true, cx) {
2410 return;
2411 }
2412
2413 if self.mode == EditorMode::Full
2414 && self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel())
2415 {
2416 return;
2417 }
2418
2419 cx.propagate();
2420 }
2421
2422 pub fn dismiss_menus_and_popups(
2423 &mut self,
2424 should_report_inline_completion_event: bool,
2425 cx: &mut ViewContext<Self>,
2426 ) -> bool {
2427 if self.take_rename(false, cx).is_some() {
2428 return true;
2429 }
2430
2431 if hide_hover(self, cx) {
2432 return true;
2433 }
2434
2435 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2436 return true;
2437 }
2438
2439 if self.hide_context_menu(cx).is_some() {
2440 return true;
2441 }
2442
2443 if self.mouse_context_menu.take().is_some() {
2444 return true;
2445 }
2446
2447 if self.discard_inline_completion(should_report_inline_completion_event, cx) {
2448 return true;
2449 }
2450
2451 if self.snippet_stack.pop().is_some() {
2452 return true;
2453 }
2454
2455 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
2456 self.dismiss_diagnostics(cx);
2457 return true;
2458 }
2459
2460 false
2461 }
2462
2463 fn linked_editing_ranges_for(
2464 &self,
2465 selection: Range<text::Anchor>,
2466 cx: &AppContext,
2467 ) -> Option<HashMap<Model<Buffer>, Vec<Range<text::Anchor>>>> {
2468 if self.linked_edit_ranges.is_empty() {
2469 return None;
2470 }
2471 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2472 selection.end.buffer_id.and_then(|end_buffer_id| {
2473 if selection.start.buffer_id != Some(end_buffer_id) {
2474 return None;
2475 }
2476 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2477 let snapshot = buffer.read(cx).snapshot();
2478 self.linked_edit_ranges
2479 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2480 .map(|ranges| (ranges, snapshot, buffer))
2481 })?;
2482 use text::ToOffset as TO;
2483 // find offset from the start of current range to current cursor position
2484 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2485
2486 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2487 let start_difference = start_offset - start_byte_offset;
2488 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2489 let end_difference = end_offset - start_byte_offset;
2490 // Current range has associated linked ranges.
2491 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2492 for range in linked_ranges.iter() {
2493 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2494 let end_offset = start_offset + end_difference;
2495 let start_offset = start_offset + start_difference;
2496 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2497 continue;
2498 }
2499 if self.selections.disjoint_anchor_ranges().iter().any(|s| {
2500 if s.start.buffer_id != selection.start.buffer_id
2501 || s.end.buffer_id != selection.end.buffer_id
2502 {
2503 return false;
2504 }
2505 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
2506 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
2507 }) {
2508 continue;
2509 }
2510 let start = buffer_snapshot.anchor_after(start_offset);
2511 let end = buffer_snapshot.anchor_after(end_offset);
2512 linked_edits
2513 .entry(buffer.clone())
2514 .or_default()
2515 .push(start..end);
2516 }
2517 Some(linked_edits)
2518 }
2519
2520 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2521 let text: Arc<str> = text.into();
2522
2523 if self.read_only(cx) {
2524 return;
2525 }
2526
2527 let selections = self.selections.all_adjusted(cx);
2528 let mut bracket_inserted = false;
2529 let mut edits = Vec::new();
2530 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2531 let mut new_selections = Vec::with_capacity(selections.len());
2532 let mut new_autoclose_regions = Vec::new();
2533 let snapshot = self.buffer.read(cx).read(cx);
2534
2535 for (selection, autoclose_region) in
2536 self.selections_with_autoclose_regions(selections, &snapshot)
2537 {
2538 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2539 // Determine if the inserted text matches the opening or closing
2540 // bracket of any of this language's bracket pairs.
2541 let mut bracket_pair = None;
2542 let mut is_bracket_pair_start = false;
2543 let mut is_bracket_pair_end = false;
2544 if !text.is_empty() {
2545 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
2546 // and they are removing the character that triggered IME popup.
2547 for (pair, enabled) in scope.brackets() {
2548 if !pair.close && !pair.surround {
2549 continue;
2550 }
2551
2552 if enabled && pair.start.ends_with(text.as_ref()) {
2553 let prefix_len = pair.start.len() - text.len();
2554 let preceding_text_matches_prefix = prefix_len == 0
2555 || (selection.start.column >= (prefix_len as u32)
2556 && snapshot.contains_str_at(
2557 Point::new(
2558 selection.start.row,
2559 selection.start.column - (prefix_len as u32),
2560 ),
2561 &pair.start[..prefix_len],
2562 ));
2563 if preceding_text_matches_prefix {
2564 bracket_pair = Some(pair.clone());
2565 is_bracket_pair_start = true;
2566 break;
2567 }
2568 }
2569 if pair.end.as_str() == text.as_ref() {
2570 bracket_pair = Some(pair.clone());
2571 is_bracket_pair_end = true;
2572 break;
2573 }
2574 }
2575 }
2576
2577 if let Some(bracket_pair) = bracket_pair {
2578 let snapshot_settings = snapshot.settings_at(selection.start, cx);
2579 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
2580 let auto_surround =
2581 self.use_auto_surround && snapshot_settings.use_auto_surround;
2582 if selection.is_empty() {
2583 if is_bracket_pair_start {
2584 // If the inserted text is a suffix of an opening bracket and the
2585 // selection is preceded by the rest of the opening bracket, then
2586 // insert the closing bracket.
2587 let following_text_allows_autoclose = snapshot
2588 .chars_at(selection.start)
2589 .next()
2590 .map_or(true, |c| scope.should_autoclose_before(c));
2591
2592 let is_closing_quote = if bracket_pair.end == bracket_pair.start
2593 && bracket_pair.start.len() == 1
2594 {
2595 let target = bracket_pair.start.chars().next().unwrap();
2596 let current_line_count = snapshot
2597 .reversed_chars_at(selection.start)
2598 .take_while(|&c| c != '\n')
2599 .filter(|&c| c == target)
2600 .count();
2601 current_line_count % 2 == 1
2602 } else {
2603 false
2604 };
2605
2606 if autoclose
2607 && bracket_pair.close
2608 && following_text_allows_autoclose
2609 && !is_closing_quote
2610 {
2611 let anchor = snapshot.anchor_before(selection.end);
2612 new_selections.push((selection.map(|_| anchor), text.len()));
2613 new_autoclose_regions.push((
2614 anchor,
2615 text.len(),
2616 selection.id,
2617 bracket_pair.clone(),
2618 ));
2619 edits.push((
2620 selection.range(),
2621 format!("{}{}", text, bracket_pair.end).into(),
2622 ));
2623 bracket_inserted = true;
2624 continue;
2625 }
2626 }
2627
2628 if let Some(region) = autoclose_region {
2629 // If the selection is followed by an auto-inserted closing bracket,
2630 // then don't insert that closing bracket again; just move the selection
2631 // past the closing bracket.
2632 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2633 && text.as_ref() == region.pair.end.as_str();
2634 if should_skip {
2635 let anchor = snapshot.anchor_after(selection.end);
2636 new_selections
2637 .push((selection.map(|_| anchor), region.pair.end.len()));
2638 continue;
2639 }
2640 }
2641
2642 let always_treat_brackets_as_autoclosed = snapshot
2643 .settings_at(selection.start, cx)
2644 .always_treat_brackets_as_autoclosed;
2645 if always_treat_brackets_as_autoclosed
2646 && is_bracket_pair_end
2647 && snapshot.contains_str_at(selection.end, text.as_ref())
2648 {
2649 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
2650 // and the inserted text is a closing bracket and the selection is followed
2651 // by the closing bracket then move the selection past the closing bracket.
2652 let anchor = snapshot.anchor_after(selection.end);
2653 new_selections.push((selection.map(|_| anchor), text.len()));
2654 continue;
2655 }
2656 }
2657 // If an opening bracket is 1 character long and is typed while
2658 // text is selected, then surround that text with the bracket pair.
2659 else if auto_surround
2660 && bracket_pair.surround
2661 && is_bracket_pair_start
2662 && bracket_pair.start.chars().count() == 1
2663 {
2664 edits.push((selection.start..selection.start, text.clone()));
2665 edits.push((
2666 selection.end..selection.end,
2667 bracket_pair.end.as_str().into(),
2668 ));
2669 bracket_inserted = true;
2670 new_selections.push((
2671 Selection {
2672 id: selection.id,
2673 start: snapshot.anchor_after(selection.start),
2674 end: snapshot.anchor_before(selection.end),
2675 reversed: selection.reversed,
2676 goal: selection.goal,
2677 },
2678 0,
2679 ));
2680 continue;
2681 }
2682 }
2683 }
2684
2685 if self.auto_replace_emoji_shortcode
2686 && selection.is_empty()
2687 && text.as_ref().ends_with(':')
2688 {
2689 if let Some(possible_emoji_short_code) =
2690 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
2691 {
2692 if !possible_emoji_short_code.is_empty() {
2693 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
2694 let emoji_shortcode_start = Point::new(
2695 selection.start.row,
2696 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
2697 );
2698
2699 // Remove shortcode from buffer
2700 edits.push((
2701 emoji_shortcode_start..selection.start,
2702 "".to_string().into(),
2703 ));
2704 new_selections.push((
2705 Selection {
2706 id: selection.id,
2707 start: snapshot.anchor_after(emoji_shortcode_start),
2708 end: snapshot.anchor_before(selection.start),
2709 reversed: selection.reversed,
2710 goal: selection.goal,
2711 },
2712 0,
2713 ));
2714
2715 // Insert emoji
2716 let selection_start_anchor = snapshot.anchor_after(selection.start);
2717 new_selections.push((selection.map(|_| selection_start_anchor), 0));
2718 edits.push((selection.start..selection.end, emoji.to_string().into()));
2719
2720 continue;
2721 }
2722 }
2723 }
2724 }
2725
2726 // If not handling any auto-close operation, then just replace the selected
2727 // text with the given input and move the selection to the end of the
2728 // newly inserted text.
2729 let anchor = snapshot.anchor_after(selection.end);
2730 if !self.linked_edit_ranges.is_empty() {
2731 let start_anchor = snapshot.anchor_before(selection.start);
2732
2733 let is_word_char = text.chars().next().map_or(true, |char| {
2734 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
2735 classifier.is_word(char)
2736 });
2737
2738 if is_word_char {
2739 if let Some(ranges) = self
2740 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
2741 {
2742 for (buffer, edits) in ranges {
2743 linked_edits
2744 .entry(buffer.clone())
2745 .or_default()
2746 .extend(edits.into_iter().map(|range| (range, text.clone())));
2747 }
2748 }
2749 }
2750 }
2751
2752 new_selections.push((selection.map(|_| anchor), 0));
2753 edits.push((selection.start..selection.end, text.clone()));
2754 }
2755
2756 drop(snapshot);
2757
2758 self.transact(cx, |this, cx| {
2759 this.buffer.update(cx, |buffer, cx| {
2760 buffer.edit(edits, this.autoindent_mode.clone(), cx);
2761 });
2762 for (buffer, edits) in linked_edits {
2763 buffer.update(cx, |buffer, cx| {
2764 let snapshot = buffer.snapshot();
2765 let edits = edits
2766 .into_iter()
2767 .map(|(range, text)| {
2768 use text::ToPoint as TP;
2769 let end_point = TP::to_point(&range.end, &snapshot);
2770 let start_point = TP::to_point(&range.start, &snapshot);
2771 (start_point..end_point, text)
2772 })
2773 .sorted_by_key(|(range, _)| range.start)
2774 .collect::<Vec<_>>();
2775 buffer.edit(edits, None, cx);
2776 })
2777 }
2778 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
2779 let new_selection_deltas = new_selections.iter().map(|e| e.1);
2780 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
2781 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
2782 .zip(new_selection_deltas)
2783 .map(|(selection, delta)| Selection {
2784 id: selection.id,
2785 start: selection.start + delta,
2786 end: selection.end + delta,
2787 reversed: selection.reversed,
2788 goal: SelectionGoal::None,
2789 })
2790 .collect::<Vec<_>>();
2791
2792 let mut i = 0;
2793 for (position, delta, selection_id, pair) in new_autoclose_regions {
2794 let position = position.to_offset(&map.buffer_snapshot) + delta;
2795 let start = map.buffer_snapshot.anchor_before(position);
2796 let end = map.buffer_snapshot.anchor_after(position);
2797 while let Some(existing_state) = this.autoclose_regions.get(i) {
2798 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
2799 Ordering::Less => i += 1,
2800 Ordering::Greater => break,
2801 Ordering::Equal => {
2802 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
2803 Ordering::Less => i += 1,
2804 Ordering::Equal => break,
2805 Ordering::Greater => break,
2806 }
2807 }
2808 }
2809 }
2810 this.autoclose_regions.insert(
2811 i,
2812 AutocloseRegion {
2813 selection_id,
2814 range: start..end,
2815 pair,
2816 },
2817 );
2818 }
2819
2820 let had_active_inline_completion = this.has_active_inline_completion();
2821 this.change_selections_inner(Some(Autoscroll::fit()), false, cx, |s| {
2822 s.select(new_selections)
2823 });
2824
2825 if !bracket_inserted {
2826 if let Some(on_type_format_task) =
2827 this.trigger_on_type_formatting(text.to_string(), cx)
2828 {
2829 on_type_format_task.detach_and_log_err(cx);
2830 }
2831 }
2832
2833 let editor_settings = EditorSettings::get_global(cx);
2834 if bracket_inserted
2835 && (editor_settings.auto_signature_help
2836 || editor_settings.show_signature_help_after_edits)
2837 {
2838 this.show_signature_help(&ShowSignatureHelp, cx);
2839 }
2840
2841 let trigger_in_words = !had_active_inline_completion;
2842 this.trigger_completion_on_input(&text, trigger_in_words, cx);
2843 linked_editing_ranges::refresh_linked_ranges(this, cx);
2844 this.refresh_inline_completion(true, false, cx);
2845 });
2846 }
2847
2848 fn find_possible_emoji_shortcode_at_position(
2849 snapshot: &MultiBufferSnapshot,
2850 position: Point,
2851 ) -> Option<String> {
2852 let mut chars = Vec::new();
2853 let mut found_colon = false;
2854 for char in snapshot.reversed_chars_at(position).take(100) {
2855 // Found a possible emoji shortcode in the middle of the buffer
2856 if found_colon {
2857 if char.is_whitespace() {
2858 chars.reverse();
2859 return Some(chars.iter().collect());
2860 }
2861 // If the previous character is not a whitespace, we are in the middle of a word
2862 // and we only want to complete the shortcode if the word is made up of other emojis
2863 let mut containing_word = String::new();
2864 for ch in snapshot
2865 .reversed_chars_at(position)
2866 .skip(chars.len() + 1)
2867 .take(100)
2868 {
2869 if ch.is_whitespace() {
2870 break;
2871 }
2872 containing_word.push(ch);
2873 }
2874 let containing_word = containing_word.chars().rev().collect::<String>();
2875 if util::word_consists_of_emojis(containing_word.as_str()) {
2876 chars.reverse();
2877 return Some(chars.iter().collect());
2878 }
2879 }
2880
2881 if char.is_whitespace() || !char.is_ascii() {
2882 return None;
2883 }
2884 if char == ':' {
2885 found_colon = true;
2886 } else {
2887 chars.push(char);
2888 }
2889 }
2890 // Found a possible emoji shortcode at the beginning of the buffer
2891 chars.reverse();
2892 Some(chars.iter().collect())
2893 }
2894
2895 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
2896 self.transact(cx, |this, cx| {
2897 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
2898 let selections = this.selections.all::<usize>(cx);
2899 let multi_buffer = this.buffer.read(cx);
2900 let buffer = multi_buffer.snapshot(cx);
2901 selections
2902 .iter()
2903 .map(|selection| {
2904 let start_point = selection.start.to_point(&buffer);
2905 let mut indent =
2906 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
2907 indent.len = cmp::min(indent.len, start_point.column);
2908 let start = selection.start;
2909 let end = selection.end;
2910 let selection_is_empty = start == end;
2911 let language_scope = buffer.language_scope_at(start);
2912 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
2913 &language_scope
2914 {
2915 let leading_whitespace_len = buffer
2916 .reversed_chars_at(start)
2917 .take_while(|c| c.is_whitespace() && *c != '\n')
2918 .map(|c| c.len_utf8())
2919 .sum::<usize>();
2920
2921 let trailing_whitespace_len = buffer
2922 .chars_at(end)
2923 .take_while(|c| c.is_whitespace() && *c != '\n')
2924 .map(|c| c.len_utf8())
2925 .sum::<usize>();
2926
2927 let insert_extra_newline =
2928 language.brackets().any(|(pair, enabled)| {
2929 let pair_start = pair.start.trim_end();
2930 let pair_end = pair.end.trim_start();
2931
2932 enabled
2933 && pair.newline
2934 && buffer.contains_str_at(
2935 end + trailing_whitespace_len,
2936 pair_end,
2937 )
2938 && buffer.contains_str_at(
2939 (start - leading_whitespace_len)
2940 .saturating_sub(pair_start.len()),
2941 pair_start,
2942 )
2943 });
2944
2945 // Comment extension on newline is allowed only for cursor selections
2946 let comment_delimiter = maybe!({
2947 if !selection_is_empty {
2948 return None;
2949 }
2950
2951 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
2952 return None;
2953 }
2954
2955 let delimiters = language.line_comment_prefixes();
2956 let max_len_of_delimiter =
2957 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
2958 let (snapshot, range) =
2959 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
2960
2961 let mut index_of_first_non_whitespace = 0;
2962 let comment_candidate = snapshot
2963 .chars_for_range(range)
2964 .skip_while(|c| {
2965 let should_skip = c.is_whitespace();
2966 if should_skip {
2967 index_of_first_non_whitespace += 1;
2968 }
2969 should_skip
2970 })
2971 .take(max_len_of_delimiter)
2972 .collect::<String>();
2973 let comment_prefix = delimiters.iter().find(|comment_prefix| {
2974 comment_candidate.starts_with(comment_prefix.as_ref())
2975 })?;
2976 let cursor_is_placed_after_comment_marker =
2977 index_of_first_non_whitespace + comment_prefix.len()
2978 <= start_point.column as usize;
2979 if cursor_is_placed_after_comment_marker {
2980 Some(comment_prefix.clone())
2981 } else {
2982 None
2983 }
2984 });
2985 (comment_delimiter, insert_extra_newline)
2986 } else {
2987 (None, false)
2988 };
2989
2990 let capacity_for_delimiter = comment_delimiter
2991 .as_deref()
2992 .map(str::len)
2993 .unwrap_or_default();
2994 let mut new_text =
2995 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
2996 new_text.push('\n');
2997 new_text.extend(indent.chars());
2998 if let Some(delimiter) = &comment_delimiter {
2999 new_text.push_str(delimiter);
3000 }
3001 if insert_extra_newline {
3002 new_text = new_text.repeat(2);
3003 }
3004
3005 let anchor = buffer.anchor_after(end);
3006 let new_selection = selection.map(|_| anchor);
3007 (
3008 (start..end, new_text),
3009 (insert_extra_newline, new_selection),
3010 )
3011 })
3012 .unzip()
3013 };
3014
3015 this.edit_with_autoindent(edits, cx);
3016 let buffer = this.buffer.read(cx).snapshot(cx);
3017 let new_selections = selection_fixup_info
3018 .into_iter()
3019 .map(|(extra_newline_inserted, new_selection)| {
3020 let mut cursor = new_selection.end.to_point(&buffer);
3021 if extra_newline_inserted {
3022 cursor.row -= 1;
3023 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3024 }
3025 new_selection.map(|_| cursor)
3026 })
3027 .collect();
3028
3029 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
3030 this.refresh_inline_completion(true, false, cx);
3031 });
3032 }
3033
3034 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
3035 let buffer = self.buffer.read(cx);
3036 let snapshot = buffer.snapshot(cx);
3037
3038 let mut edits = Vec::new();
3039 let mut rows = Vec::new();
3040
3041 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3042 let cursor = selection.head();
3043 let row = cursor.row;
3044
3045 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3046
3047 let newline = "\n".to_string();
3048 edits.push((start_of_line..start_of_line, newline));
3049
3050 rows.push(row + rows_inserted as u32);
3051 }
3052
3053 self.transact(cx, |editor, cx| {
3054 editor.edit(edits, cx);
3055
3056 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3057 let mut index = 0;
3058 s.move_cursors_with(|map, _, _| {
3059 let row = rows[index];
3060 index += 1;
3061
3062 let point = Point::new(row, 0);
3063 let boundary = map.next_line_boundary(point).1;
3064 let clipped = map.clip_point(boundary, Bias::Left);
3065
3066 (clipped, SelectionGoal::None)
3067 });
3068 });
3069
3070 let mut indent_edits = Vec::new();
3071 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3072 for row in rows {
3073 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3074 for (row, indent) in indents {
3075 if indent.len == 0 {
3076 continue;
3077 }
3078
3079 let text = match indent.kind {
3080 IndentKind::Space => " ".repeat(indent.len as usize),
3081 IndentKind::Tab => "\t".repeat(indent.len as usize),
3082 };
3083 let point = Point::new(row.0, 0);
3084 indent_edits.push((point..point, text));
3085 }
3086 }
3087 editor.edit(indent_edits, cx);
3088 });
3089 }
3090
3091 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
3092 let buffer = self.buffer.read(cx);
3093 let snapshot = buffer.snapshot(cx);
3094
3095 let mut edits = Vec::new();
3096 let mut rows = Vec::new();
3097 let mut rows_inserted = 0;
3098
3099 for selection in self.selections.all_adjusted(cx) {
3100 let cursor = selection.head();
3101 let row = cursor.row;
3102
3103 let point = Point::new(row + 1, 0);
3104 let start_of_line = snapshot.clip_point(point, Bias::Left);
3105
3106 let newline = "\n".to_string();
3107 edits.push((start_of_line..start_of_line, newline));
3108
3109 rows_inserted += 1;
3110 rows.push(row + rows_inserted);
3111 }
3112
3113 self.transact(cx, |editor, cx| {
3114 editor.edit(edits, cx);
3115
3116 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3117 let mut index = 0;
3118 s.move_cursors_with(|map, _, _| {
3119 let row = rows[index];
3120 index += 1;
3121
3122 let point = Point::new(row, 0);
3123 let boundary = map.next_line_boundary(point).1;
3124 let clipped = map.clip_point(boundary, Bias::Left);
3125
3126 (clipped, SelectionGoal::None)
3127 });
3128 });
3129
3130 let mut indent_edits = Vec::new();
3131 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3132 for row in rows {
3133 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3134 for (row, indent) in indents {
3135 if indent.len == 0 {
3136 continue;
3137 }
3138
3139 let text = match indent.kind {
3140 IndentKind::Space => " ".repeat(indent.len as usize),
3141 IndentKind::Tab => "\t".repeat(indent.len as usize),
3142 };
3143 let point = Point::new(row.0, 0);
3144 indent_edits.push((point..point, text));
3145 }
3146 }
3147 editor.edit(indent_edits, cx);
3148 });
3149 }
3150
3151 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3152 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3153 original_indent_columns: Vec::new(),
3154 });
3155 self.insert_with_autoindent_mode(text, autoindent, cx);
3156 }
3157
3158 fn insert_with_autoindent_mode(
3159 &mut self,
3160 text: &str,
3161 autoindent_mode: Option<AutoindentMode>,
3162 cx: &mut ViewContext<Self>,
3163 ) {
3164 if self.read_only(cx) {
3165 return;
3166 }
3167
3168 let text: Arc<str> = text.into();
3169 self.transact(cx, |this, cx| {
3170 let old_selections = this.selections.all_adjusted(cx);
3171 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3172 let anchors = {
3173 let snapshot = buffer.read(cx);
3174 old_selections
3175 .iter()
3176 .map(|s| {
3177 let anchor = snapshot.anchor_after(s.head());
3178 s.map(|_| anchor)
3179 })
3180 .collect::<Vec<_>>()
3181 };
3182 buffer.edit(
3183 old_selections
3184 .iter()
3185 .map(|s| (s.start..s.end, text.clone())),
3186 autoindent_mode,
3187 cx,
3188 );
3189 anchors
3190 });
3191
3192 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3193 s.select_anchors(selection_anchors);
3194 })
3195 });
3196 }
3197
3198 fn trigger_completion_on_input(
3199 &mut self,
3200 text: &str,
3201 trigger_in_words: bool,
3202 cx: &mut ViewContext<Self>,
3203 ) {
3204 if self.is_completion_trigger(text, trigger_in_words, cx) {
3205 self.show_completions(
3206 &ShowCompletions {
3207 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3208 },
3209 cx,
3210 );
3211 } else {
3212 self.hide_context_menu(cx);
3213 }
3214 }
3215
3216 fn is_completion_trigger(
3217 &self,
3218 text: &str,
3219 trigger_in_words: bool,
3220 cx: &mut ViewContext<Self>,
3221 ) -> bool {
3222 let position = self.selections.newest_anchor().head();
3223 let multibuffer = self.buffer.read(cx);
3224 let Some(buffer) = position
3225 .buffer_id
3226 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3227 else {
3228 return false;
3229 };
3230
3231 if let Some(completion_provider) = &self.completion_provider {
3232 completion_provider.is_completion_trigger(
3233 &buffer,
3234 position.text_anchor,
3235 text,
3236 trigger_in_words,
3237 cx,
3238 )
3239 } else {
3240 false
3241 }
3242 }
3243
3244 /// If any empty selections is touching the start of its innermost containing autoclose
3245 /// region, expand it to select the brackets.
3246 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
3247 let selections = self.selections.all::<usize>(cx);
3248 let buffer = self.buffer.read(cx).read(cx);
3249 let new_selections = self
3250 .selections_with_autoclose_regions(selections, &buffer)
3251 .map(|(mut selection, region)| {
3252 if !selection.is_empty() {
3253 return selection;
3254 }
3255
3256 if let Some(region) = region {
3257 let mut range = region.range.to_offset(&buffer);
3258 if selection.start == range.start && range.start >= region.pair.start.len() {
3259 range.start -= region.pair.start.len();
3260 if buffer.contains_str_at(range.start, ®ion.pair.start)
3261 && buffer.contains_str_at(range.end, ®ion.pair.end)
3262 {
3263 range.end += region.pair.end.len();
3264 selection.start = range.start;
3265 selection.end = range.end;
3266
3267 return selection;
3268 }
3269 }
3270 }
3271
3272 let always_treat_brackets_as_autoclosed = buffer
3273 .settings_at(selection.start, cx)
3274 .always_treat_brackets_as_autoclosed;
3275
3276 if !always_treat_brackets_as_autoclosed {
3277 return selection;
3278 }
3279
3280 if let Some(scope) = buffer.language_scope_at(selection.start) {
3281 for (pair, enabled) in scope.brackets() {
3282 if !enabled || !pair.close {
3283 continue;
3284 }
3285
3286 if buffer.contains_str_at(selection.start, &pair.end) {
3287 let pair_start_len = pair.start.len();
3288 if buffer.contains_str_at(
3289 selection.start.saturating_sub(pair_start_len),
3290 &pair.start,
3291 ) {
3292 selection.start -= pair_start_len;
3293 selection.end += pair.end.len();
3294
3295 return selection;
3296 }
3297 }
3298 }
3299 }
3300
3301 selection
3302 })
3303 .collect();
3304
3305 drop(buffer);
3306 self.change_selections(None, cx, |selections| selections.select(new_selections));
3307 }
3308
3309 /// Iterate the given selections, and for each one, find the smallest surrounding
3310 /// autoclose region. This uses the ordering of the selections and the autoclose
3311 /// regions to avoid repeated comparisons.
3312 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3313 &'a self,
3314 selections: impl IntoIterator<Item = Selection<D>>,
3315 buffer: &'a MultiBufferSnapshot,
3316 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3317 let mut i = 0;
3318 let mut regions = self.autoclose_regions.as_slice();
3319 selections.into_iter().map(move |selection| {
3320 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3321
3322 let mut enclosing = None;
3323 while let Some(pair_state) = regions.get(i) {
3324 if pair_state.range.end.to_offset(buffer) < range.start {
3325 regions = ®ions[i + 1..];
3326 i = 0;
3327 } else if pair_state.range.start.to_offset(buffer) > range.end {
3328 break;
3329 } else {
3330 if pair_state.selection_id == selection.id {
3331 enclosing = Some(pair_state);
3332 }
3333 i += 1;
3334 }
3335 }
3336
3337 (selection, enclosing)
3338 })
3339 }
3340
3341 /// Remove any autoclose regions that no longer contain their selection.
3342 fn invalidate_autoclose_regions(
3343 &mut self,
3344 mut selections: &[Selection<Anchor>],
3345 buffer: &MultiBufferSnapshot,
3346 ) {
3347 self.autoclose_regions.retain(|state| {
3348 let mut i = 0;
3349 while let Some(selection) = selections.get(i) {
3350 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3351 selections = &selections[1..];
3352 continue;
3353 }
3354 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3355 break;
3356 }
3357 if selection.id == state.selection_id {
3358 return true;
3359 } else {
3360 i += 1;
3361 }
3362 }
3363 false
3364 });
3365 }
3366
3367 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3368 let offset = position.to_offset(buffer);
3369 let (word_range, kind) = buffer.surrounding_word(offset, true);
3370 if offset > word_range.start && kind == Some(CharKind::Word) {
3371 Some(
3372 buffer
3373 .text_for_range(word_range.start..offset)
3374 .collect::<String>(),
3375 )
3376 } else {
3377 None
3378 }
3379 }
3380
3381 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
3382 self.refresh_inlay_hints(
3383 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
3384 cx,
3385 );
3386 }
3387
3388 pub fn inlay_hints_enabled(&self) -> bool {
3389 self.inlay_hint_cache.enabled
3390 }
3391
3392 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
3393 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
3394 return;
3395 }
3396
3397 let reason_description = reason.description();
3398 let ignore_debounce = matches!(
3399 reason,
3400 InlayHintRefreshReason::SettingsChange(_)
3401 | InlayHintRefreshReason::Toggle(_)
3402 | InlayHintRefreshReason::ExcerptsRemoved(_)
3403 );
3404 let (invalidate_cache, required_languages) = match reason {
3405 InlayHintRefreshReason::Toggle(enabled) => {
3406 self.inlay_hint_cache.enabled = enabled;
3407 if enabled {
3408 (InvalidationStrategy::RefreshRequested, None)
3409 } else {
3410 self.inlay_hint_cache.clear();
3411 self.splice_inlays(
3412 self.visible_inlay_hints(cx)
3413 .iter()
3414 .map(|inlay| inlay.id)
3415 .collect(),
3416 Vec::new(),
3417 cx,
3418 );
3419 return;
3420 }
3421 }
3422 InlayHintRefreshReason::SettingsChange(new_settings) => {
3423 match self.inlay_hint_cache.update_settings(
3424 &self.buffer,
3425 new_settings,
3426 self.visible_inlay_hints(cx),
3427 cx,
3428 ) {
3429 ControlFlow::Break(Some(InlaySplice {
3430 to_remove,
3431 to_insert,
3432 })) => {
3433 self.splice_inlays(to_remove, to_insert, cx);
3434 return;
3435 }
3436 ControlFlow::Break(None) => return,
3437 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3438 }
3439 }
3440 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3441 if let Some(InlaySplice {
3442 to_remove,
3443 to_insert,
3444 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3445 {
3446 self.splice_inlays(to_remove, to_insert, cx);
3447 }
3448 return;
3449 }
3450 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3451 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3452 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3453 }
3454 InlayHintRefreshReason::RefreshRequested => {
3455 (InvalidationStrategy::RefreshRequested, None)
3456 }
3457 };
3458
3459 if let Some(InlaySplice {
3460 to_remove,
3461 to_insert,
3462 }) = self.inlay_hint_cache.spawn_hint_refresh(
3463 reason_description,
3464 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3465 invalidate_cache,
3466 ignore_debounce,
3467 cx,
3468 ) {
3469 self.splice_inlays(to_remove, to_insert, cx);
3470 }
3471 }
3472
3473 fn visible_inlay_hints(&self, cx: &ViewContext<'_, Editor>) -> Vec<Inlay> {
3474 self.display_map
3475 .read(cx)
3476 .current_inlays()
3477 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3478 .cloned()
3479 .collect()
3480 }
3481
3482 pub fn excerpts_for_inlay_hints_query(
3483 &self,
3484 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3485 cx: &mut ViewContext<Editor>,
3486 ) -> HashMap<ExcerptId, (Model<Buffer>, clock::Global, Range<usize>)> {
3487 let Some(project) = self.project.as_ref() else {
3488 return HashMap::default();
3489 };
3490 let project = project.read(cx);
3491 let multi_buffer = self.buffer().read(cx);
3492 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3493 let multi_buffer_visible_start = self
3494 .scroll_manager
3495 .anchor()
3496 .anchor
3497 .to_point(&multi_buffer_snapshot);
3498 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3499 multi_buffer_visible_start
3500 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3501 Bias::Left,
3502 );
3503 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3504 multi_buffer
3505 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
3506 .into_iter()
3507 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3508 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
3509 let buffer = buffer_handle.read(cx);
3510 let buffer_file = project::File::from_dyn(buffer.file())?;
3511 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3512 let worktree_entry = buffer_worktree
3513 .read(cx)
3514 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3515 if worktree_entry.is_ignored {
3516 return None;
3517 }
3518
3519 let language = buffer.language()?;
3520 if let Some(restrict_to_languages) = restrict_to_languages {
3521 if !restrict_to_languages.contains(language) {
3522 return None;
3523 }
3524 }
3525 Some((
3526 excerpt_id,
3527 (
3528 buffer_handle,
3529 buffer.version().clone(),
3530 excerpt_visible_range,
3531 ),
3532 ))
3533 })
3534 .collect()
3535 }
3536
3537 pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
3538 TextLayoutDetails {
3539 text_system: cx.text_system().clone(),
3540 editor_style: self.style.clone().unwrap(),
3541 rem_size: cx.rem_size(),
3542 scroll_anchor: self.scroll_manager.anchor(),
3543 visible_rows: self.visible_line_count(),
3544 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
3545 }
3546 }
3547
3548 fn splice_inlays(
3549 &self,
3550 to_remove: Vec<InlayId>,
3551 to_insert: Vec<Inlay>,
3552 cx: &mut ViewContext<Self>,
3553 ) {
3554 self.display_map.update(cx, |display_map, cx| {
3555 display_map.splice_inlays(to_remove, to_insert, cx)
3556 });
3557 cx.notify();
3558 }
3559
3560 fn trigger_on_type_formatting(
3561 &self,
3562 input: String,
3563 cx: &mut ViewContext<Self>,
3564 ) -> Option<Task<Result<()>>> {
3565 if input.len() != 1 {
3566 return None;
3567 }
3568
3569 let project = self.project.as_ref()?;
3570 let position = self.selections.newest_anchor().head();
3571 let (buffer, buffer_position) = self
3572 .buffer
3573 .read(cx)
3574 .text_anchor_for_position(position, cx)?;
3575
3576 let settings = language_settings::language_settings(
3577 buffer
3578 .read(cx)
3579 .language_at(buffer_position)
3580 .map(|l| l.name()),
3581 buffer.read(cx).file(),
3582 cx,
3583 );
3584 if !settings.use_on_type_format {
3585 return None;
3586 }
3587
3588 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3589 // hence we do LSP request & edit on host side only — add formats to host's history.
3590 let push_to_lsp_host_history = true;
3591 // If this is not the host, append its history with new edits.
3592 let push_to_client_history = project.read(cx).is_via_collab();
3593
3594 let on_type_formatting = project.update(cx, |project, cx| {
3595 project.on_type_format(
3596 buffer.clone(),
3597 buffer_position,
3598 input,
3599 push_to_lsp_host_history,
3600 cx,
3601 )
3602 });
3603 Some(cx.spawn(|editor, mut cx| async move {
3604 if let Some(transaction) = on_type_formatting.await? {
3605 if push_to_client_history {
3606 buffer
3607 .update(&mut cx, |buffer, _| {
3608 buffer.push_transaction(transaction, Instant::now());
3609 })
3610 .ok();
3611 }
3612 editor.update(&mut cx, |editor, cx| {
3613 editor.refresh_document_highlights(cx);
3614 })?;
3615 }
3616 Ok(())
3617 }))
3618 }
3619
3620 pub fn show_completions(&mut self, options: &ShowCompletions, cx: &mut ViewContext<Self>) {
3621 if self.pending_rename.is_some() {
3622 return;
3623 }
3624
3625 let Some(provider) = self.completion_provider.as_ref() else {
3626 return;
3627 };
3628
3629 if !self.snippet_stack.is_empty() && self.context_menu.read().as_ref().is_some() {
3630 return;
3631 }
3632
3633 let position = self.selections.newest_anchor().head();
3634 let (buffer, buffer_position) =
3635 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
3636 output
3637 } else {
3638 return;
3639 };
3640 let show_completion_documentation = buffer
3641 .read(cx)
3642 .snapshot()
3643 .settings_at(buffer_position, cx)
3644 .show_completion_documentation;
3645
3646 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
3647
3648 let aside_was_displayed = match self.context_menu.read().deref() {
3649 Some(CodeContextMenu::Completions(menu)) => menu.aside_was_displayed.get(),
3650 _ => false,
3651 };
3652 let trigger_kind = match &options.trigger {
3653 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
3654 CompletionTriggerKind::TRIGGER_CHARACTER
3655 }
3656 _ => CompletionTriggerKind::INVOKED,
3657 };
3658 let completion_context = CompletionContext {
3659 trigger_character: options.trigger.as_ref().and_then(|trigger| {
3660 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
3661 Some(String::from(trigger))
3662 } else {
3663 None
3664 }
3665 }),
3666 trigger_kind,
3667 };
3668 let completions = provider.completions(&buffer, buffer_position, completion_context, cx);
3669 let sort_completions = provider.sort_completions();
3670
3671 let id = post_inc(&mut self.next_completion_id);
3672 let task = cx.spawn(|editor, mut cx| {
3673 async move {
3674 editor.update(&mut cx, |this, _| {
3675 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
3676 })?;
3677 let completions = completions.await.log_err();
3678 let menu = if let Some(completions) = completions {
3679 let mut menu = CompletionsMenu::new(
3680 id,
3681 sort_completions,
3682 show_completion_documentation,
3683 position,
3684 buffer.clone(),
3685 completions.into(),
3686 aside_was_displayed,
3687 );
3688 menu.filter(query.as_deref(), cx.background_executor().clone())
3689 .await;
3690
3691 if menu.matches.is_empty() {
3692 None
3693 } else {
3694 Some(menu)
3695 }
3696 } else {
3697 None
3698 };
3699
3700 editor.update(&mut cx, |editor, cx| {
3701 let mut context_menu = editor.context_menu.write();
3702 match context_menu.as_ref() {
3703 None => {}
3704
3705 Some(CodeContextMenu::Completions(prev_menu)) => {
3706 if prev_menu.id > id {
3707 return;
3708 }
3709 }
3710
3711 _ => return,
3712 }
3713
3714 if editor.focus_handle.is_focused(cx) && menu.is_some() {
3715 let mut menu = menu.unwrap();
3716 menu.resolve_visible_completions(editor.completion_provider.as_deref(), cx);
3717 *context_menu = Some(CodeContextMenu::Completions(menu));
3718 drop(context_menu);
3719 cx.notify();
3720 } else if editor.completion_tasks.len() <= 1 {
3721 // If there are no more completion tasks and the last menu was
3722 // empty, we should hide it. If it was already hidden, we should
3723 // also show the copilot completion when available.
3724 drop(context_menu);
3725 editor.hide_context_menu(cx);
3726 }
3727 })?;
3728
3729 Ok::<_, anyhow::Error>(())
3730 }
3731 .log_err()
3732 });
3733
3734 self.completion_tasks.push((id, task));
3735 }
3736
3737 pub fn confirm_completion(
3738 &mut self,
3739 action: &ConfirmCompletion,
3740 cx: &mut ViewContext<Self>,
3741 ) -> Option<Task<Result<()>>> {
3742 self.do_completion(action.item_ix, CompletionIntent::Complete, cx)
3743 }
3744
3745 pub fn compose_completion(
3746 &mut self,
3747 action: &ComposeCompletion,
3748 cx: &mut ViewContext<Self>,
3749 ) -> Option<Task<Result<()>>> {
3750 self.do_completion(action.item_ix, CompletionIntent::Compose, cx)
3751 }
3752
3753 fn do_completion(
3754 &mut self,
3755 item_ix: Option<usize>,
3756 intent: CompletionIntent,
3757 cx: &mut ViewContext<Editor>,
3758 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
3759 use language::ToOffset as _;
3760
3761 self.discard_inline_completion(true, cx);
3762 let completions_menu =
3763 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
3764 menu
3765 } else {
3766 return None;
3767 };
3768
3769 let mat = completions_menu
3770 .matches
3771 .get(item_ix.unwrap_or(completions_menu.selected_item))?;
3772 let buffer_handle = completions_menu.buffer;
3773 let completions = completions_menu.completions.read();
3774 let completion = completions.get(mat.candidate_id)?;
3775 cx.stop_propagation();
3776
3777 let snippet;
3778 let text;
3779
3780 if completion.is_snippet() {
3781 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
3782 text = snippet.as_ref().unwrap().text.clone();
3783 } else {
3784 snippet = None;
3785 text = completion.new_text.clone();
3786 };
3787 let selections = self.selections.all::<usize>(cx);
3788 let buffer = buffer_handle.read(cx);
3789 let old_range = completion.old_range.to_offset(buffer);
3790 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
3791
3792 let newest_selection = self.selections.newest_anchor();
3793 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
3794 return None;
3795 }
3796
3797 let lookbehind = newest_selection
3798 .start
3799 .text_anchor
3800 .to_offset(buffer)
3801 .saturating_sub(old_range.start);
3802 let lookahead = old_range
3803 .end
3804 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
3805 let mut common_prefix_len = old_text
3806 .bytes()
3807 .zip(text.bytes())
3808 .take_while(|(a, b)| a == b)
3809 .count();
3810
3811 let snapshot = self.buffer.read(cx).snapshot(cx);
3812 let mut range_to_replace: Option<Range<isize>> = None;
3813 let mut ranges = Vec::new();
3814 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3815 for selection in &selections {
3816 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
3817 let start = selection.start.saturating_sub(lookbehind);
3818 let end = selection.end + lookahead;
3819 if selection.id == newest_selection.id {
3820 range_to_replace = Some(
3821 ((start + common_prefix_len) as isize - selection.start as isize)
3822 ..(end as isize - selection.start as isize),
3823 );
3824 }
3825 ranges.push(start + common_prefix_len..end);
3826 } else {
3827 common_prefix_len = 0;
3828 ranges.clear();
3829 ranges.extend(selections.iter().map(|s| {
3830 if s.id == newest_selection.id {
3831 range_to_replace = Some(
3832 old_range.start.to_offset_utf16(&snapshot).0 as isize
3833 - selection.start as isize
3834 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
3835 - selection.start as isize,
3836 );
3837 old_range.clone()
3838 } else {
3839 s.start..s.end
3840 }
3841 }));
3842 break;
3843 }
3844 if !self.linked_edit_ranges.is_empty() {
3845 let start_anchor = snapshot.anchor_before(selection.head());
3846 let end_anchor = snapshot.anchor_after(selection.tail());
3847 if let Some(ranges) = self
3848 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
3849 {
3850 for (buffer, edits) in ranges {
3851 linked_edits.entry(buffer.clone()).or_default().extend(
3852 edits
3853 .into_iter()
3854 .map(|range| (range, text[common_prefix_len..].to_owned())),
3855 );
3856 }
3857 }
3858 }
3859 }
3860 let text = &text[common_prefix_len..];
3861
3862 cx.emit(EditorEvent::InputHandled {
3863 utf16_range_to_replace: range_to_replace,
3864 text: text.into(),
3865 });
3866
3867 self.transact(cx, |this, cx| {
3868 if let Some(mut snippet) = snippet {
3869 snippet.text = text.to_string();
3870 for tabstop in snippet
3871 .tabstops
3872 .iter_mut()
3873 .flat_map(|tabstop| tabstop.ranges.iter_mut())
3874 {
3875 tabstop.start -= common_prefix_len as isize;
3876 tabstop.end -= common_prefix_len as isize;
3877 }
3878
3879 this.insert_snippet(&ranges, snippet, cx).log_err();
3880 } else {
3881 this.buffer.update(cx, |buffer, cx| {
3882 buffer.edit(
3883 ranges.iter().map(|range| (range.clone(), text)),
3884 this.autoindent_mode.clone(),
3885 cx,
3886 );
3887 });
3888 }
3889 for (buffer, edits) in linked_edits {
3890 buffer.update(cx, |buffer, cx| {
3891 let snapshot = buffer.snapshot();
3892 let edits = edits
3893 .into_iter()
3894 .map(|(range, text)| {
3895 use text::ToPoint as TP;
3896 let end_point = TP::to_point(&range.end, &snapshot);
3897 let start_point = TP::to_point(&range.start, &snapshot);
3898 (start_point..end_point, text)
3899 })
3900 .sorted_by_key(|(range, _)| range.start)
3901 .collect::<Vec<_>>();
3902 buffer.edit(edits, None, cx);
3903 })
3904 }
3905
3906 this.refresh_inline_completion(true, false, cx);
3907 });
3908
3909 let show_new_completions_on_confirm = completion
3910 .confirm
3911 .as_ref()
3912 .map_or(false, |confirm| confirm(intent, cx));
3913 if show_new_completions_on_confirm {
3914 self.show_completions(&ShowCompletions { trigger: None }, cx);
3915 }
3916
3917 let provider = self.completion_provider.as_ref()?;
3918 let apply_edits = provider.apply_additional_edits_for_completion(
3919 buffer_handle,
3920 completion.clone(),
3921 true,
3922 cx,
3923 );
3924
3925 let editor_settings = EditorSettings::get_global(cx);
3926 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
3927 // After the code completion is finished, users often want to know what signatures are needed.
3928 // so we should automatically call signature_help
3929 self.show_signature_help(&ShowSignatureHelp, cx);
3930 }
3931
3932 Some(cx.foreground_executor().spawn(async move {
3933 apply_edits.await?;
3934 Ok(())
3935 }))
3936 }
3937
3938 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
3939 let mut context_menu = self.context_menu.write();
3940 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
3941 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
3942 // Toggle if we're selecting the same one
3943 *context_menu = None;
3944 cx.notify();
3945 return;
3946 } else {
3947 // Otherwise, clear it and start a new one
3948 *context_menu = None;
3949 cx.notify();
3950 }
3951 }
3952 drop(context_menu);
3953 let snapshot = self.snapshot(cx);
3954 let deployed_from_indicator = action.deployed_from_indicator;
3955 let mut task = self.code_actions_task.take();
3956 let action = action.clone();
3957 cx.spawn(|editor, mut cx| async move {
3958 while let Some(prev_task) = task {
3959 prev_task.await.log_err();
3960 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
3961 }
3962
3963 let spawned_test_task = editor.update(&mut cx, |editor, cx| {
3964 if editor.focus_handle.is_focused(cx) {
3965 let multibuffer_point = action
3966 .deployed_from_indicator
3967 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
3968 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
3969 let (buffer, buffer_row) = snapshot
3970 .buffer_snapshot
3971 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
3972 .and_then(|(buffer_snapshot, range)| {
3973 editor
3974 .buffer
3975 .read(cx)
3976 .buffer(buffer_snapshot.remote_id())
3977 .map(|buffer| (buffer, range.start.row))
3978 })?;
3979 let (_, code_actions) = editor
3980 .available_code_actions
3981 .clone()
3982 .and_then(|(location, code_actions)| {
3983 let snapshot = location.buffer.read(cx).snapshot();
3984 let point_range = location.range.to_point(&snapshot);
3985 let point_range = point_range.start.row..=point_range.end.row;
3986 if point_range.contains(&buffer_row) {
3987 Some((location, code_actions))
3988 } else {
3989 None
3990 }
3991 })
3992 .unzip();
3993 let buffer_id = buffer.read(cx).remote_id();
3994 let tasks = editor
3995 .tasks
3996 .get(&(buffer_id, buffer_row))
3997 .map(|t| Arc::new(t.to_owned()));
3998 if tasks.is_none() && code_actions.is_none() {
3999 return None;
4000 }
4001
4002 editor.completion_tasks.clear();
4003 editor.discard_inline_completion(false, cx);
4004 let task_context =
4005 tasks
4006 .as_ref()
4007 .zip(editor.project.clone())
4008 .map(|(tasks, project)| {
4009 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4010 });
4011
4012 Some(cx.spawn(|editor, mut cx| async move {
4013 let task_context = match task_context {
4014 Some(task_context) => task_context.await,
4015 None => None,
4016 };
4017 let resolved_tasks =
4018 tasks.zip(task_context).map(|(tasks, task_context)| {
4019 Arc::new(ResolvedTasks {
4020 templates: tasks.resolve(&task_context).collect(),
4021 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4022 multibuffer_point.row,
4023 tasks.column,
4024 )),
4025 })
4026 });
4027 let spawn_straight_away = resolved_tasks
4028 .as_ref()
4029 .map_or(false, |tasks| tasks.templates.len() == 1)
4030 && code_actions
4031 .as_ref()
4032 .map_or(true, |actions| actions.is_empty());
4033 if let Ok(task) = editor.update(&mut cx, |editor, cx| {
4034 *editor.context_menu.write() =
4035 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4036 buffer,
4037 actions: CodeActionContents {
4038 tasks: resolved_tasks,
4039 actions: code_actions,
4040 },
4041 selected_item: Default::default(),
4042 scroll_handle: UniformListScrollHandle::default(),
4043 deployed_from_indicator,
4044 }));
4045 if spawn_straight_away {
4046 if let Some(task) = editor.confirm_code_action(
4047 &ConfirmCodeAction { item_ix: Some(0) },
4048 cx,
4049 ) {
4050 cx.notify();
4051 return task;
4052 }
4053 }
4054 cx.notify();
4055 Task::ready(Ok(()))
4056 }) {
4057 task.await
4058 } else {
4059 Ok(())
4060 }
4061 }))
4062 } else {
4063 Some(Task::ready(Ok(())))
4064 }
4065 })?;
4066 if let Some(task) = spawned_test_task {
4067 task.await?;
4068 }
4069
4070 Ok::<_, anyhow::Error>(())
4071 })
4072 .detach_and_log_err(cx);
4073 }
4074
4075 pub fn confirm_code_action(
4076 &mut self,
4077 action: &ConfirmCodeAction,
4078 cx: &mut ViewContext<Self>,
4079 ) -> Option<Task<Result<()>>> {
4080 let actions_menu = if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
4081 menu
4082 } else {
4083 return None;
4084 };
4085 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4086 let action = actions_menu.actions.get(action_ix)?;
4087 let title = action.label();
4088 let buffer = actions_menu.buffer;
4089 let workspace = self.workspace()?;
4090
4091 match action {
4092 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4093 workspace.update(cx, |workspace, cx| {
4094 workspace::tasks::schedule_resolved_task(
4095 workspace,
4096 task_source_kind,
4097 resolved_task,
4098 false,
4099 cx,
4100 );
4101
4102 Some(Task::ready(Ok(())))
4103 })
4104 }
4105 CodeActionsItem::CodeAction {
4106 excerpt_id,
4107 action,
4108 provider,
4109 } => {
4110 let apply_code_action =
4111 provider.apply_code_action(buffer, action, excerpt_id, true, cx);
4112 let workspace = workspace.downgrade();
4113 Some(cx.spawn(|editor, cx| async move {
4114 let project_transaction = apply_code_action.await?;
4115 Self::open_project_transaction(
4116 &editor,
4117 workspace,
4118 project_transaction,
4119 title,
4120 cx,
4121 )
4122 .await
4123 }))
4124 }
4125 }
4126 }
4127
4128 pub async fn open_project_transaction(
4129 this: &WeakView<Editor>,
4130 workspace: WeakView<Workspace>,
4131 transaction: ProjectTransaction,
4132 title: String,
4133 mut cx: AsyncWindowContext,
4134 ) -> Result<()> {
4135 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4136 cx.update(|cx| {
4137 entries.sort_unstable_by_key(|(buffer, _)| {
4138 buffer.read(cx).file().map(|f| f.path().clone())
4139 });
4140 })?;
4141
4142 // If the project transaction's edits are all contained within this editor, then
4143 // avoid opening a new editor to display them.
4144
4145 if let Some((buffer, transaction)) = entries.first() {
4146 if entries.len() == 1 {
4147 let excerpt = this.update(&mut cx, |editor, cx| {
4148 editor
4149 .buffer()
4150 .read(cx)
4151 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4152 })?;
4153 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4154 if excerpted_buffer == *buffer {
4155 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4156 let excerpt_range = excerpt_range.to_offset(buffer);
4157 buffer
4158 .edited_ranges_for_transaction::<usize>(transaction)
4159 .all(|range| {
4160 excerpt_range.start <= range.start
4161 && excerpt_range.end >= range.end
4162 })
4163 })?;
4164
4165 if all_edits_within_excerpt {
4166 return Ok(());
4167 }
4168 }
4169 }
4170 }
4171 } else {
4172 return Ok(());
4173 }
4174
4175 let mut ranges_to_highlight = Vec::new();
4176 let excerpt_buffer = cx.new_model(|cx| {
4177 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4178 for (buffer_handle, transaction) in &entries {
4179 let buffer = buffer_handle.read(cx);
4180 ranges_to_highlight.extend(
4181 multibuffer.push_excerpts_with_context_lines(
4182 buffer_handle.clone(),
4183 buffer
4184 .edited_ranges_for_transaction::<usize>(transaction)
4185 .collect(),
4186 DEFAULT_MULTIBUFFER_CONTEXT,
4187 cx,
4188 ),
4189 );
4190 }
4191 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4192 multibuffer
4193 })?;
4194
4195 workspace.update(&mut cx, |workspace, cx| {
4196 let project = workspace.project().clone();
4197 let editor =
4198 cx.new_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, cx));
4199 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
4200 editor.update(cx, |editor, cx| {
4201 editor.highlight_background::<Self>(
4202 &ranges_to_highlight,
4203 |theme| theme.editor_highlighted_line_background,
4204 cx,
4205 );
4206 });
4207 })?;
4208
4209 Ok(())
4210 }
4211
4212 pub fn clear_code_action_providers(&mut self) {
4213 self.code_action_providers.clear();
4214 self.available_code_actions.take();
4215 }
4216
4217 pub fn push_code_action_provider(
4218 &mut self,
4219 provider: Arc<dyn CodeActionProvider>,
4220 cx: &mut ViewContext<Self>,
4221 ) {
4222 self.code_action_providers.push(provider);
4223 self.refresh_code_actions(cx);
4224 }
4225
4226 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4227 let buffer = self.buffer.read(cx);
4228 let newest_selection = self.selections.newest_anchor().clone();
4229 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4230 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4231 if start_buffer != end_buffer {
4232 return None;
4233 }
4234
4235 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
4236 cx.background_executor()
4237 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4238 .await;
4239
4240 let (providers, tasks) = this.update(&mut cx, |this, cx| {
4241 let providers = this.code_action_providers.clone();
4242 let tasks = this
4243 .code_action_providers
4244 .iter()
4245 .map(|provider| provider.code_actions(&start_buffer, start..end, cx))
4246 .collect::<Vec<_>>();
4247 (providers, tasks)
4248 })?;
4249
4250 let mut actions = Vec::new();
4251 for (provider, provider_actions) in
4252 providers.into_iter().zip(future::join_all(tasks).await)
4253 {
4254 if let Some(provider_actions) = provider_actions.log_err() {
4255 actions.extend(provider_actions.into_iter().map(|action| {
4256 AvailableCodeAction {
4257 excerpt_id: newest_selection.start.excerpt_id,
4258 action,
4259 provider: provider.clone(),
4260 }
4261 }));
4262 }
4263 }
4264
4265 this.update(&mut cx, |this, cx| {
4266 this.available_code_actions = if actions.is_empty() {
4267 None
4268 } else {
4269 Some((
4270 Location {
4271 buffer: start_buffer,
4272 range: start..end,
4273 },
4274 actions.into(),
4275 ))
4276 };
4277 cx.notify();
4278 })
4279 }));
4280 None
4281 }
4282
4283 fn start_inline_blame_timer(&mut self, cx: &mut ViewContext<Self>) {
4284 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4285 self.show_git_blame_inline = false;
4286
4287 self.show_git_blame_inline_delay_task = Some(cx.spawn(|this, mut cx| async move {
4288 cx.background_executor().timer(delay).await;
4289
4290 this.update(&mut cx, |this, cx| {
4291 this.show_git_blame_inline = true;
4292 cx.notify();
4293 })
4294 .log_err();
4295 }));
4296 }
4297 }
4298
4299 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4300 if self.pending_rename.is_some() {
4301 return None;
4302 }
4303
4304 let provider = self.semantics_provider.clone()?;
4305 let buffer = self.buffer.read(cx);
4306 let newest_selection = self.selections.newest_anchor().clone();
4307 let cursor_position = newest_selection.head();
4308 let (cursor_buffer, cursor_buffer_position) =
4309 buffer.text_anchor_for_position(cursor_position, cx)?;
4310 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4311 if cursor_buffer != tail_buffer {
4312 return None;
4313 }
4314
4315 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4316 cx.background_executor()
4317 .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT)
4318 .await;
4319
4320 let highlights = if let Some(highlights) = cx
4321 .update(|cx| {
4322 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4323 })
4324 .ok()
4325 .flatten()
4326 {
4327 highlights.await.log_err()
4328 } else {
4329 None
4330 };
4331
4332 if let Some(highlights) = highlights {
4333 this.update(&mut cx, |this, cx| {
4334 if this.pending_rename.is_some() {
4335 return;
4336 }
4337
4338 let buffer_id = cursor_position.buffer_id;
4339 let buffer = this.buffer.read(cx);
4340 if !buffer
4341 .text_anchor_for_position(cursor_position, cx)
4342 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4343 {
4344 return;
4345 }
4346
4347 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4348 let mut write_ranges = Vec::new();
4349 let mut read_ranges = Vec::new();
4350 for highlight in highlights {
4351 for (excerpt_id, excerpt_range) in
4352 buffer.excerpts_for_buffer(&cursor_buffer, cx)
4353 {
4354 let start = highlight
4355 .range
4356 .start
4357 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4358 let end = highlight
4359 .range
4360 .end
4361 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4362 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4363 continue;
4364 }
4365
4366 let range = Anchor {
4367 buffer_id,
4368 excerpt_id,
4369 text_anchor: start,
4370 }..Anchor {
4371 buffer_id,
4372 excerpt_id,
4373 text_anchor: end,
4374 };
4375 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4376 write_ranges.push(range);
4377 } else {
4378 read_ranges.push(range);
4379 }
4380 }
4381 }
4382
4383 this.highlight_background::<DocumentHighlightRead>(
4384 &read_ranges,
4385 |theme| theme.editor_document_highlight_read_background,
4386 cx,
4387 );
4388 this.highlight_background::<DocumentHighlightWrite>(
4389 &write_ranges,
4390 |theme| theme.editor_document_highlight_write_background,
4391 cx,
4392 );
4393 cx.notify();
4394 })
4395 .log_err();
4396 }
4397 }));
4398 None
4399 }
4400
4401 pub fn refresh_inline_completion(
4402 &mut self,
4403 debounce: bool,
4404 user_requested: bool,
4405 cx: &mut ViewContext<Self>,
4406 ) -> Option<()> {
4407 let provider = self.inline_completion_provider()?;
4408 let cursor = self.selections.newest_anchor().head();
4409 let (buffer, cursor_buffer_position) =
4410 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4411
4412 if !user_requested
4413 && (!self.enable_inline_completions
4414 || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
4415 || !self.is_focused(cx))
4416 {
4417 self.discard_inline_completion(false, cx);
4418 return None;
4419 }
4420
4421 self.update_visible_inline_completion(cx);
4422 provider.refresh(buffer, cursor_buffer_position, debounce, cx);
4423 Some(())
4424 }
4425
4426 fn cycle_inline_completion(
4427 &mut self,
4428 direction: Direction,
4429 cx: &mut ViewContext<Self>,
4430 ) -> Option<()> {
4431 let provider = self.inline_completion_provider()?;
4432 let cursor = self.selections.newest_anchor().head();
4433 let (buffer, cursor_buffer_position) =
4434 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4435 if !self.enable_inline_completions
4436 || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
4437 {
4438 return None;
4439 }
4440
4441 provider.cycle(buffer, cursor_buffer_position, direction, cx);
4442 self.update_visible_inline_completion(cx);
4443
4444 Some(())
4445 }
4446
4447 pub fn show_inline_completion(&mut self, _: &ShowInlineCompletion, cx: &mut ViewContext<Self>) {
4448 if !self.has_active_inline_completion() {
4449 self.refresh_inline_completion(false, true, cx);
4450 return;
4451 }
4452
4453 self.update_visible_inline_completion(cx);
4454 }
4455
4456 pub fn display_cursor_names(&mut self, _: &DisplayCursorNames, cx: &mut ViewContext<Self>) {
4457 self.show_cursor_names(cx);
4458 }
4459
4460 fn show_cursor_names(&mut self, cx: &mut ViewContext<Self>) {
4461 self.show_cursor_names = true;
4462 cx.notify();
4463 cx.spawn(|this, mut cx| async move {
4464 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
4465 this.update(&mut cx, |this, cx| {
4466 this.show_cursor_names = false;
4467 cx.notify()
4468 })
4469 .ok()
4470 })
4471 .detach();
4472 }
4473
4474 pub fn next_inline_completion(&mut self, _: &NextInlineCompletion, cx: &mut ViewContext<Self>) {
4475 if self.has_active_inline_completion() {
4476 self.cycle_inline_completion(Direction::Next, cx);
4477 } else {
4478 let is_copilot_disabled = self.refresh_inline_completion(false, true, cx).is_none();
4479 if is_copilot_disabled {
4480 cx.propagate();
4481 }
4482 }
4483 }
4484
4485 pub fn previous_inline_completion(
4486 &mut self,
4487 _: &PreviousInlineCompletion,
4488 cx: &mut ViewContext<Self>,
4489 ) {
4490 if self.has_active_inline_completion() {
4491 self.cycle_inline_completion(Direction::Prev, cx);
4492 } else {
4493 let is_copilot_disabled = self.refresh_inline_completion(false, true, cx).is_none();
4494 if is_copilot_disabled {
4495 cx.propagate();
4496 }
4497 }
4498 }
4499
4500 pub fn accept_inline_completion(
4501 &mut self,
4502 _: &AcceptInlineCompletion,
4503 cx: &mut ViewContext<Self>,
4504 ) {
4505 self.hide_context_menu(cx);
4506
4507 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
4508 return;
4509 };
4510
4511 self.report_inline_completion_event(true, cx);
4512
4513 match &active_inline_completion.completion {
4514 InlineCompletion::Move(position) => {
4515 let position = *position;
4516 self.change_selections(Some(Autoscroll::newest()), cx, |selections| {
4517 selections.select_anchor_ranges([position..position]);
4518 });
4519 }
4520 InlineCompletion::Edit(edits) => {
4521 if let Some(provider) = self.inline_completion_provider() {
4522 provider.accept(cx);
4523 }
4524
4525 let snapshot = self.buffer.read(cx).snapshot(cx);
4526 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
4527
4528 self.buffer.update(cx, |buffer, cx| {
4529 buffer.edit(edits.iter().cloned(), None, cx)
4530 });
4531
4532 self.change_selections(None, cx, |s| {
4533 s.select_anchor_ranges([last_edit_end..last_edit_end])
4534 });
4535
4536 self.update_visible_inline_completion(cx);
4537 if self.active_inline_completion.is_none() {
4538 self.refresh_inline_completion(true, true, cx);
4539 }
4540
4541 cx.notify();
4542 }
4543 }
4544 }
4545
4546 pub fn accept_partial_inline_completion(
4547 &mut self,
4548 _: &AcceptPartialInlineCompletion,
4549 cx: &mut ViewContext<Self>,
4550 ) {
4551 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
4552 return;
4553 };
4554 if self.selections.count() != 1 {
4555 return;
4556 }
4557
4558 self.report_inline_completion_event(true, cx);
4559
4560 match &active_inline_completion.completion {
4561 InlineCompletion::Move(position) => {
4562 let position = *position;
4563 self.change_selections(Some(Autoscroll::newest()), cx, |selections| {
4564 selections.select_anchor_ranges([position..position]);
4565 });
4566 }
4567 InlineCompletion::Edit(edits) => {
4568 if edits.len() == 1 && edits[0].0.start == edits[0].0.end {
4569 let text = edits[0].1.as_str();
4570 let mut partial_completion = text
4571 .chars()
4572 .by_ref()
4573 .take_while(|c| c.is_alphabetic())
4574 .collect::<String>();
4575 if partial_completion.is_empty() {
4576 partial_completion = text
4577 .chars()
4578 .by_ref()
4579 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
4580 .collect::<String>();
4581 }
4582
4583 cx.emit(EditorEvent::InputHandled {
4584 utf16_range_to_replace: None,
4585 text: partial_completion.clone().into(),
4586 });
4587
4588 self.insert_with_autoindent_mode(&partial_completion, None, cx);
4589
4590 self.refresh_inline_completion(true, true, cx);
4591 cx.notify();
4592 }
4593 }
4594 }
4595 }
4596
4597 fn discard_inline_completion(
4598 &mut self,
4599 should_report_inline_completion_event: bool,
4600 cx: &mut ViewContext<Self>,
4601 ) -> bool {
4602 if should_report_inline_completion_event {
4603 self.report_inline_completion_event(false, cx);
4604 }
4605
4606 if let Some(provider) = self.inline_completion_provider() {
4607 provider.discard(cx);
4608 }
4609
4610 self.take_active_inline_completion(cx).is_some()
4611 }
4612
4613 fn report_inline_completion_event(&self, accepted: bool, cx: &AppContext) {
4614 let Some(provider) = self.inline_completion_provider() else {
4615 return;
4616 };
4617 let Some(project) = self.project.as_ref() else {
4618 return;
4619 };
4620 let Some((_, buffer, _)) = self
4621 .buffer
4622 .read(cx)
4623 .excerpt_containing(self.selections.newest_anchor().head(), cx)
4624 else {
4625 return;
4626 };
4627
4628 let project = project.read(cx);
4629 let extension = buffer
4630 .read(cx)
4631 .file()
4632 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
4633 project.client().telemetry().report_inline_completion_event(
4634 provider.name().into(),
4635 accepted,
4636 extension,
4637 );
4638 }
4639
4640 pub fn has_active_inline_completion(&self) -> bool {
4641 self.active_inline_completion.is_some()
4642 }
4643
4644 fn take_active_inline_completion(
4645 &mut self,
4646 cx: &mut ViewContext<Self>,
4647 ) -> Option<InlineCompletion> {
4648 let active_inline_completion = self.active_inline_completion.take()?;
4649 self.splice_inlays(active_inline_completion.inlay_ids, Default::default(), cx);
4650 self.clear_highlights::<InlineCompletionHighlight>(cx);
4651 Some(active_inline_completion.completion)
4652 }
4653
4654 fn update_visible_inline_completion(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4655 let selection = self.selections.newest_anchor();
4656 let cursor = selection.head();
4657 let multibuffer = self.buffer.read(cx).snapshot(cx);
4658 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
4659 let excerpt_id = cursor.excerpt_id;
4660
4661 if !offset_selection.is_empty()
4662 || self
4663 .active_inline_completion
4664 .as_ref()
4665 .map_or(false, |completion| {
4666 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
4667 let invalidation_range = invalidation_range.start..=invalidation_range.end;
4668 !invalidation_range.contains(&offset_selection.head())
4669 })
4670 {
4671 self.discard_inline_completion(false, cx);
4672 return None;
4673 }
4674
4675 self.take_active_inline_completion(cx);
4676 let provider = self.inline_completion_provider()?;
4677
4678 let (buffer, cursor_buffer_position) =
4679 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4680
4681 let completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
4682 let edits = completion
4683 .edits
4684 .into_iter()
4685 .map(|(range, new_text)| {
4686 (
4687 multibuffer
4688 .anchor_in_excerpt(excerpt_id, range.start)
4689 .unwrap()
4690 ..multibuffer
4691 .anchor_in_excerpt(excerpt_id, range.end)
4692 .unwrap(),
4693 new_text,
4694 )
4695 })
4696 .collect::<Vec<_>>();
4697 if edits.is_empty() {
4698 return None;
4699 }
4700
4701 let first_edit_start = edits.first().unwrap().0.start;
4702 let edit_start_row = first_edit_start
4703 .to_point(&multibuffer)
4704 .row
4705 .saturating_sub(2);
4706
4707 let last_edit_end = edits.last().unwrap().0.end;
4708 let edit_end_row = cmp::min(
4709 multibuffer.max_point().row,
4710 last_edit_end.to_point(&multibuffer).row + 2,
4711 );
4712
4713 let cursor_row = cursor.to_point(&multibuffer).row;
4714
4715 let mut inlay_ids = Vec::new();
4716 let invalidation_row_range;
4717 let completion;
4718 if cursor_row < edit_start_row {
4719 invalidation_row_range = cursor_row..edit_end_row;
4720 completion = InlineCompletion::Move(first_edit_start);
4721 } else if cursor_row > edit_end_row {
4722 invalidation_row_range = edit_start_row..cursor_row;
4723 completion = InlineCompletion::Move(first_edit_start);
4724 } else {
4725 if edits
4726 .iter()
4727 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
4728 {
4729 let mut inlays = Vec::new();
4730 for (range, new_text) in &edits {
4731 let inlay = Inlay::suggestion(
4732 post_inc(&mut self.next_inlay_id),
4733 range.start,
4734 new_text.as_str(),
4735 );
4736 inlay_ids.push(inlay.id);
4737 inlays.push(inlay);
4738 }
4739
4740 self.splice_inlays(vec![], inlays, cx);
4741 } else {
4742 let background_color = cx.theme().status().deleted_background;
4743 self.highlight_text::<InlineCompletionHighlight>(
4744 edits.iter().map(|(range, _)| range.clone()).collect(),
4745 HighlightStyle {
4746 background_color: Some(background_color),
4747 ..Default::default()
4748 },
4749 cx,
4750 );
4751 }
4752
4753 invalidation_row_range = edit_start_row..edit_end_row;
4754 completion = InlineCompletion::Edit(edits);
4755 };
4756
4757 let invalidation_range = multibuffer
4758 .anchor_before(Point::new(invalidation_row_range.start, 0))
4759 ..multibuffer.anchor_after(Point::new(
4760 invalidation_row_range.end,
4761 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
4762 ));
4763
4764 self.active_inline_completion = Some(InlineCompletionState {
4765 inlay_ids,
4766 completion,
4767 invalidation_range,
4768 });
4769 cx.notify();
4770
4771 Some(())
4772 }
4773
4774 fn inline_completion_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
4775 Some(self.inline_completion_provider.as_ref()?.provider.clone())
4776 }
4777
4778 fn render_code_actions_indicator(
4779 &self,
4780 _style: &EditorStyle,
4781 row: DisplayRow,
4782 is_active: bool,
4783 cx: &mut ViewContext<Self>,
4784 ) -> Option<IconButton> {
4785 if self.available_code_actions.is_some() {
4786 Some(
4787 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
4788 .shape(ui::IconButtonShape::Square)
4789 .icon_size(IconSize::XSmall)
4790 .icon_color(Color::Muted)
4791 .selected(is_active)
4792 .tooltip({
4793 let focus_handle = self.focus_handle.clone();
4794 move |cx| {
4795 Tooltip::for_action_in(
4796 "Toggle Code Actions",
4797 &ToggleCodeActions {
4798 deployed_from_indicator: None,
4799 },
4800 &focus_handle,
4801 cx,
4802 )
4803 }
4804 })
4805 .on_click(cx.listener(move |editor, _e, cx| {
4806 editor.focus(cx);
4807 editor.toggle_code_actions(
4808 &ToggleCodeActions {
4809 deployed_from_indicator: Some(row),
4810 },
4811 cx,
4812 );
4813 })),
4814 )
4815 } else {
4816 None
4817 }
4818 }
4819
4820 fn clear_tasks(&mut self) {
4821 self.tasks.clear()
4822 }
4823
4824 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
4825 if self.tasks.insert(key, value).is_some() {
4826 // This case should hopefully be rare, but just in case...
4827 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
4828 }
4829 }
4830
4831 fn build_tasks_context(
4832 project: &Model<Project>,
4833 buffer: &Model<Buffer>,
4834 buffer_row: u32,
4835 tasks: &Arc<RunnableTasks>,
4836 cx: &mut ViewContext<Self>,
4837 ) -> Task<Option<task::TaskContext>> {
4838 let position = Point::new(buffer_row, tasks.column);
4839 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
4840 let location = Location {
4841 buffer: buffer.clone(),
4842 range: range_start..range_start,
4843 };
4844 // Fill in the environmental variables from the tree-sitter captures
4845 let mut captured_task_variables = TaskVariables::default();
4846 for (capture_name, value) in tasks.extra_variables.clone() {
4847 captured_task_variables.insert(
4848 task::VariableName::Custom(capture_name.into()),
4849 value.clone(),
4850 );
4851 }
4852 project.update(cx, |project, cx| {
4853 project.task_store().update(cx, |task_store, cx| {
4854 task_store.task_context_for_location(captured_task_variables, location, cx)
4855 })
4856 })
4857 }
4858
4859 pub fn spawn_nearest_task(&mut self, action: &SpawnNearestTask, cx: &mut ViewContext<Self>) {
4860 let Some((workspace, _)) = self.workspace.clone() else {
4861 return;
4862 };
4863 let Some(project) = self.project.clone() else {
4864 return;
4865 };
4866
4867 // Try to find a closest, enclosing node using tree-sitter that has a
4868 // task
4869 let Some((buffer, buffer_row, tasks)) = self
4870 .find_enclosing_node_task(cx)
4871 // Or find the task that's closest in row-distance.
4872 .or_else(|| self.find_closest_task(cx))
4873 else {
4874 return;
4875 };
4876
4877 let reveal_strategy = action.reveal;
4878 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
4879 cx.spawn(|_, mut cx| async move {
4880 let context = task_context.await?;
4881 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
4882
4883 let resolved = resolved_task.resolved.as_mut()?;
4884 resolved.reveal = reveal_strategy;
4885
4886 workspace
4887 .update(&mut cx, |workspace, cx| {
4888 workspace::tasks::schedule_resolved_task(
4889 workspace,
4890 task_source_kind,
4891 resolved_task,
4892 false,
4893 cx,
4894 );
4895 })
4896 .ok()
4897 })
4898 .detach();
4899 }
4900
4901 fn find_closest_task(
4902 &mut self,
4903 cx: &mut ViewContext<Self>,
4904 ) -> Option<(Model<Buffer>, u32, Arc<RunnableTasks>)> {
4905 let cursor_row = self.selections.newest_adjusted(cx).head().row;
4906
4907 let ((buffer_id, row), tasks) = self
4908 .tasks
4909 .iter()
4910 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
4911
4912 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
4913 let tasks = Arc::new(tasks.to_owned());
4914 Some((buffer, *row, tasks))
4915 }
4916
4917 fn find_enclosing_node_task(
4918 &mut self,
4919 cx: &mut ViewContext<Self>,
4920 ) -> Option<(Model<Buffer>, u32, Arc<RunnableTasks>)> {
4921 let snapshot = self.buffer.read(cx).snapshot(cx);
4922 let offset = self.selections.newest::<usize>(cx).head();
4923 let excerpt = snapshot.excerpt_containing(offset..offset)?;
4924 let buffer_id = excerpt.buffer().remote_id();
4925
4926 let layer = excerpt.buffer().syntax_layer_at(offset)?;
4927 let mut cursor = layer.node().walk();
4928
4929 while cursor.goto_first_child_for_byte(offset).is_some() {
4930 if cursor.node().end_byte() == offset {
4931 cursor.goto_next_sibling();
4932 }
4933 }
4934
4935 // Ascend to the smallest ancestor that contains the range and has a task.
4936 loop {
4937 let node = cursor.node();
4938 let node_range = node.byte_range();
4939 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
4940
4941 // Check if this node contains our offset
4942 if node_range.start <= offset && node_range.end >= offset {
4943 // If it contains offset, check for task
4944 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
4945 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
4946 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
4947 }
4948 }
4949
4950 if !cursor.goto_parent() {
4951 break;
4952 }
4953 }
4954 None
4955 }
4956
4957 fn render_run_indicator(
4958 &self,
4959 _style: &EditorStyle,
4960 is_active: bool,
4961 row: DisplayRow,
4962 cx: &mut ViewContext<Self>,
4963 ) -> IconButton {
4964 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
4965 .shape(ui::IconButtonShape::Square)
4966 .icon_size(IconSize::XSmall)
4967 .icon_color(Color::Muted)
4968 .selected(is_active)
4969 .on_click(cx.listener(move |editor, _e, cx| {
4970 editor.focus(cx);
4971 editor.toggle_code_actions(
4972 &ToggleCodeActions {
4973 deployed_from_indicator: Some(row),
4974 },
4975 cx,
4976 );
4977 }))
4978 }
4979
4980 pub fn context_menu_visible(&self) -> bool {
4981 self.context_menu
4982 .read()
4983 .as_ref()
4984 .map_or(false, |menu| menu.visible())
4985 }
4986
4987 fn render_context_menu(
4988 &self,
4989 cursor_position: DisplayPoint,
4990 style: &EditorStyle,
4991 max_height: Pixels,
4992 cx: &mut ViewContext<Editor>,
4993 ) -> Option<(ContextMenuOrigin, AnyElement)> {
4994 self.context_menu.read().as_ref().map(|menu| {
4995 menu.render(
4996 cursor_position,
4997 style,
4998 max_height,
4999 self.workspace.as_ref().map(|(w, _)| w.clone()),
5000 cx,
5001 )
5002 })
5003 }
5004
5005 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<CodeContextMenu> {
5006 cx.notify();
5007 self.completion_tasks.clear();
5008 self.context_menu.write().take()
5009 }
5010
5011 fn show_snippet_choices(
5012 &mut self,
5013 choices: &Vec<String>,
5014 selection: Range<Anchor>,
5015 cx: &mut ViewContext<Self>,
5016 ) {
5017 if selection.start.buffer_id.is_none() {
5018 return;
5019 }
5020 let buffer_id = selection.start.buffer_id.unwrap();
5021 let buffer = self.buffer().read(cx).buffer(buffer_id);
5022 let id = post_inc(&mut self.next_completion_id);
5023
5024 if let Some(buffer) = buffer {
5025 *self.context_menu.write() = Some(CodeContextMenu::Completions(
5026 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
5027 ));
5028 }
5029 }
5030
5031 pub fn insert_snippet(
5032 &mut self,
5033 insertion_ranges: &[Range<usize>],
5034 snippet: Snippet,
5035 cx: &mut ViewContext<Self>,
5036 ) -> Result<()> {
5037 struct Tabstop<T> {
5038 is_end_tabstop: bool,
5039 ranges: Vec<Range<T>>,
5040 choices: Option<Vec<String>>,
5041 }
5042
5043 let tabstops = self.buffer.update(cx, |buffer, cx| {
5044 let snippet_text: Arc<str> = snippet.text.clone().into();
5045 buffer.edit(
5046 insertion_ranges
5047 .iter()
5048 .cloned()
5049 .map(|range| (range, snippet_text.clone())),
5050 Some(AutoindentMode::EachLine),
5051 cx,
5052 );
5053
5054 let snapshot = &*buffer.read(cx);
5055 let snippet = &snippet;
5056 snippet
5057 .tabstops
5058 .iter()
5059 .map(|tabstop| {
5060 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
5061 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
5062 });
5063 let mut tabstop_ranges = tabstop
5064 .ranges
5065 .iter()
5066 .flat_map(|tabstop_range| {
5067 let mut delta = 0_isize;
5068 insertion_ranges.iter().map(move |insertion_range| {
5069 let insertion_start = insertion_range.start as isize + delta;
5070 delta +=
5071 snippet.text.len() as isize - insertion_range.len() as isize;
5072
5073 let start = ((insertion_start + tabstop_range.start) as usize)
5074 .min(snapshot.len());
5075 let end = ((insertion_start + tabstop_range.end) as usize)
5076 .min(snapshot.len());
5077 snapshot.anchor_before(start)..snapshot.anchor_after(end)
5078 })
5079 })
5080 .collect::<Vec<_>>();
5081 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
5082
5083 Tabstop {
5084 is_end_tabstop,
5085 ranges: tabstop_ranges,
5086 choices: tabstop.choices.clone(),
5087 }
5088 })
5089 .collect::<Vec<_>>()
5090 });
5091 if let Some(tabstop) = tabstops.first() {
5092 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5093 s.select_ranges(tabstop.ranges.iter().cloned());
5094 });
5095
5096 if let Some(choices) = &tabstop.choices {
5097 if let Some(selection) = tabstop.ranges.first() {
5098 self.show_snippet_choices(choices, selection.clone(), cx)
5099 }
5100 }
5101
5102 // If we're already at the last tabstop and it's at the end of the snippet,
5103 // we're done, we don't need to keep the state around.
5104 if !tabstop.is_end_tabstop {
5105 let choices = tabstops
5106 .iter()
5107 .map(|tabstop| tabstop.choices.clone())
5108 .collect();
5109
5110 let ranges = tabstops
5111 .into_iter()
5112 .map(|tabstop| tabstop.ranges)
5113 .collect::<Vec<_>>();
5114
5115 self.snippet_stack.push(SnippetState {
5116 active_index: 0,
5117 ranges,
5118 choices,
5119 });
5120 }
5121
5122 // Check whether the just-entered snippet ends with an auto-closable bracket.
5123 if self.autoclose_regions.is_empty() {
5124 let snapshot = self.buffer.read(cx).snapshot(cx);
5125 for selection in &mut self.selections.all::<Point>(cx) {
5126 let selection_head = selection.head();
5127 let Some(scope) = snapshot.language_scope_at(selection_head) else {
5128 continue;
5129 };
5130
5131 let mut bracket_pair = None;
5132 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
5133 let prev_chars = snapshot
5134 .reversed_chars_at(selection_head)
5135 .collect::<String>();
5136 for (pair, enabled) in scope.brackets() {
5137 if enabled
5138 && pair.close
5139 && prev_chars.starts_with(pair.start.as_str())
5140 && next_chars.starts_with(pair.end.as_str())
5141 {
5142 bracket_pair = Some(pair.clone());
5143 break;
5144 }
5145 }
5146 if let Some(pair) = bracket_pair {
5147 let start = snapshot.anchor_after(selection_head);
5148 let end = snapshot.anchor_after(selection_head);
5149 self.autoclose_regions.push(AutocloseRegion {
5150 selection_id: selection.id,
5151 range: start..end,
5152 pair,
5153 });
5154 }
5155 }
5156 }
5157 }
5158 Ok(())
5159 }
5160
5161 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5162 self.move_to_snippet_tabstop(Bias::Right, cx)
5163 }
5164
5165 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5166 self.move_to_snippet_tabstop(Bias::Left, cx)
5167 }
5168
5169 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
5170 if let Some(mut snippet) = self.snippet_stack.pop() {
5171 match bias {
5172 Bias::Left => {
5173 if snippet.active_index > 0 {
5174 snippet.active_index -= 1;
5175 } else {
5176 self.snippet_stack.push(snippet);
5177 return false;
5178 }
5179 }
5180 Bias::Right => {
5181 if snippet.active_index + 1 < snippet.ranges.len() {
5182 snippet.active_index += 1;
5183 } else {
5184 self.snippet_stack.push(snippet);
5185 return false;
5186 }
5187 }
5188 }
5189 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
5190 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5191 s.select_anchor_ranges(current_ranges.iter().cloned())
5192 });
5193
5194 if let Some(choices) = &snippet.choices[snippet.active_index] {
5195 if let Some(selection) = current_ranges.first() {
5196 self.show_snippet_choices(&choices, selection.clone(), cx);
5197 }
5198 }
5199
5200 // If snippet state is not at the last tabstop, push it back on the stack
5201 if snippet.active_index + 1 < snippet.ranges.len() {
5202 self.snippet_stack.push(snippet);
5203 }
5204 return true;
5205 }
5206 }
5207
5208 false
5209 }
5210
5211 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
5212 self.transact(cx, |this, cx| {
5213 this.select_all(&SelectAll, cx);
5214 this.insert("", cx);
5215 });
5216 }
5217
5218 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
5219 self.transact(cx, |this, cx| {
5220 this.select_autoclose_pair(cx);
5221 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
5222 if !this.linked_edit_ranges.is_empty() {
5223 let selections = this.selections.all::<MultiBufferPoint>(cx);
5224 let snapshot = this.buffer.read(cx).snapshot(cx);
5225
5226 for selection in selections.iter() {
5227 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
5228 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
5229 if selection_start.buffer_id != selection_end.buffer_id {
5230 continue;
5231 }
5232 if let Some(ranges) =
5233 this.linked_editing_ranges_for(selection_start..selection_end, cx)
5234 {
5235 for (buffer, entries) in ranges {
5236 linked_ranges.entry(buffer).or_default().extend(entries);
5237 }
5238 }
5239 }
5240 }
5241
5242 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
5243 if !this.selections.line_mode {
5244 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
5245 for selection in &mut selections {
5246 if selection.is_empty() {
5247 let old_head = selection.head();
5248 let mut new_head =
5249 movement::left(&display_map, old_head.to_display_point(&display_map))
5250 .to_point(&display_map);
5251 if let Some((buffer, line_buffer_range)) = display_map
5252 .buffer_snapshot
5253 .buffer_line_for_row(MultiBufferRow(old_head.row))
5254 {
5255 let indent_size =
5256 buffer.indent_size_for_line(line_buffer_range.start.row);
5257 let indent_len = match indent_size.kind {
5258 IndentKind::Space => {
5259 buffer.settings_at(line_buffer_range.start, cx).tab_size
5260 }
5261 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
5262 };
5263 if old_head.column <= indent_size.len && old_head.column > 0 {
5264 let indent_len = indent_len.get();
5265 new_head = cmp::min(
5266 new_head,
5267 MultiBufferPoint::new(
5268 old_head.row,
5269 ((old_head.column - 1) / indent_len) * indent_len,
5270 ),
5271 );
5272 }
5273 }
5274
5275 selection.set_head(new_head, SelectionGoal::None);
5276 }
5277 }
5278 }
5279
5280 this.signature_help_state.set_backspace_pressed(true);
5281 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5282 this.insert("", cx);
5283 let empty_str: Arc<str> = Arc::from("");
5284 for (buffer, edits) in linked_ranges {
5285 let snapshot = buffer.read(cx).snapshot();
5286 use text::ToPoint as TP;
5287
5288 let edits = edits
5289 .into_iter()
5290 .map(|range| {
5291 let end_point = TP::to_point(&range.end, &snapshot);
5292 let mut start_point = TP::to_point(&range.start, &snapshot);
5293
5294 if end_point == start_point {
5295 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
5296 .saturating_sub(1);
5297 start_point = TP::to_point(&offset, &snapshot);
5298 };
5299
5300 (start_point..end_point, empty_str.clone())
5301 })
5302 .sorted_by_key(|(range, _)| range.start)
5303 .collect::<Vec<_>>();
5304 buffer.update(cx, |this, cx| {
5305 this.edit(edits, None, cx);
5306 })
5307 }
5308 this.refresh_inline_completion(true, false, cx);
5309 linked_editing_ranges::refresh_linked_ranges(this, cx);
5310 });
5311 }
5312
5313 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
5314 self.transact(cx, |this, cx| {
5315 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5316 let line_mode = s.line_mode;
5317 s.move_with(|map, selection| {
5318 if selection.is_empty() && !line_mode {
5319 let cursor = movement::right(map, selection.head());
5320 selection.end = cursor;
5321 selection.reversed = true;
5322 selection.goal = SelectionGoal::None;
5323 }
5324 })
5325 });
5326 this.insert("", cx);
5327 this.refresh_inline_completion(true, false, cx);
5328 });
5329 }
5330
5331 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
5332 if self.move_to_prev_snippet_tabstop(cx) {
5333 return;
5334 }
5335
5336 self.outdent(&Outdent, cx);
5337 }
5338
5339 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
5340 if self.move_to_next_snippet_tabstop(cx) || self.read_only(cx) {
5341 return;
5342 }
5343
5344 let mut selections = self.selections.all_adjusted(cx);
5345 let buffer = self.buffer.read(cx);
5346 let snapshot = buffer.snapshot(cx);
5347 let rows_iter = selections.iter().map(|s| s.head().row);
5348 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
5349
5350 let mut edits = Vec::new();
5351 let mut prev_edited_row = 0;
5352 let mut row_delta = 0;
5353 for selection in &mut selections {
5354 if selection.start.row != prev_edited_row {
5355 row_delta = 0;
5356 }
5357 prev_edited_row = selection.end.row;
5358
5359 // If the selection is non-empty, then increase the indentation of the selected lines.
5360 if !selection.is_empty() {
5361 row_delta =
5362 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5363 continue;
5364 }
5365
5366 // If the selection is empty and the cursor is in the leading whitespace before the
5367 // suggested indentation, then auto-indent the line.
5368 let cursor = selection.head();
5369 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
5370 if let Some(suggested_indent) =
5371 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
5372 {
5373 if cursor.column < suggested_indent.len
5374 && cursor.column <= current_indent.len
5375 && current_indent.len <= suggested_indent.len
5376 {
5377 selection.start = Point::new(cursor.row, suggested_indent.len);
5378 selection.end = selection.start;
5379 if row_delta == 0 {
5380 edits.extend(Buffer::edit_for_indent_size_adjustment(
5381 cursor.row,
5382 current_indent,
5383 suggested_indent,
5384 ));
5385 row_delta = suggested_indent.len - current_indent.len;
5386 }
5387 continue;
5388 }
5389 }
5390
5391 // Otherwise, insert a hard or soft tab.
5392 let settings = buffer.settings_at(cursor, cx);
5393 let tab_size = if settings.hard_tabs {
5394 IndentSize::tab()
5395 } else {
5396 let tab_size = settings.tab_size.get();
5397 let char_column = snapshot
5398 .text_for_range(Point::new(cursor.row, 0)..cursor)
5399 .flat_map(str::chars)
5400 .count()
5401 + row_delta as usize;
5402 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
5403 IndentSize::spaces(chars_to_next_tab_stop)
5404 };
5405 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
5406 selection.end = selection.start;
5407 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
5408 row_delta += tab_size.len;
5409 }
5410
5411 self.transact(cx, |this, cx| {
5412 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5413 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5414 this.refresh_inline_completion(true, false, cx);
5415 });
5416 }
5417
5418 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
5419 if self.read_only(cx) {
5420 return;
5421 }
5422 let mut selections = self.selections.all::<Point>(cx);
5423 let mut prev_edited_row = 0;
5424 let mut row_delta = 0;
5425 let mut edits = Vec::new();
5426 let buffer = self.buffer.read(cx);
5427 let snapshot = buffer.snapshot(cx);
5428 for selection in &mut selections {
5429 if selection.start.row != prev_edited_row {
5430 row_delta = 0;
5431 }
5432 prev_edited_row = selection.end.row;
5433
5434 row_delta =
5435 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5436 }
5437
5438 self.transact(cx, |this, cx| {
5439 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5440 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5441 });
5442 }
5443
5444 fn indent_selection(
5445 buffer: &MultiBuffer,
5446 snapshot: &MultiBufferSnapshot,
5447 selection: &mut Selection<Point>,
5448 edits: &mut Vec<(Range<Point>, String)>,
5449 delta_for_start_row: u32,
5450 cx: &AppContext,
5451 ) -> u32 {
5452 let settings = buffer.settings_at(selection.start, cx);
5453 let tab_size = settings.tab_size.get();
5454 let indent_kind = if settings.hard_tabs {
5455 IndentKind::Tab
5456 } else {
5457 IndentKind::Space
5458 };
5459 let mut start_row = selection.start.row;
5460 let mut end_row = selection.end.row + 1;
5461
5462 // If a selection ends at the beginning of a line, don't indent
5463 // that last line.
5464 if selection.end.column == 0 && selection.end.row > selection.start.row {
5465 end_row -= 1;
5466 }
5467
5468 // Avoid re-indenting a row that has already been indented by a
5469 // previous selection, but still update this selection's column
5470 // to reflect that indentation.
5471 if delta_for_start_row > 0 {
5472 start_row += 1;
5473 selection.start.column += delta_for_start_row;
5474 if selection.end.row == selection.start.row {
5475 selection.end.column += delta_for_start_row;
5476 }
5477 }
5478
5479 let mut delta_for_end_row = 0;
5480 let has_multiple_rows = start_row + 1 != end_row;
5481 for row in start_row..end_row {
5482 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
5483 let indent_delta = match (current_indent.kind, indent_kind) {
5484 (IndentKind::Space, IndentKind::Space) => {
5485 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
5486 IndentSize::spaces(columns_to_next_tab_stop)
5487 }
5488 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
5489 (_, IndentKind::Tab) => IndentSize::tab(),
5490 };
5491
5492 let start = if has_multiple_rows || current_indent.len < selection.start.column {
5493 0
5494 } else {
5495 selection.start.column
5496 };
5497 let row_start = Point::new(row, start);
5498 edits.push((
5499 row_start..row_start,
5500 indent_delta.chars().collect::<String>(),
5501 ));
5502
5503 // Update this selection's endpoints to reflect the indentation.
5504 if row == selection.start.row {
5505 selection.start.column += indent_delta.len;
5506 }
5507 if row == selection.end.row {
5508 selection.end.column += indent_delta.len;
5509 delta_for_end_row = indent_delta.len;
5510 }
5511 }
5512
5513 if selection.start.row == selection.end.row {
5514 delta_for_start_row + delta_for_end_row
5515 } else {
5516 delta_for_end_row
5517 }
5518 }
5519
5520 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
5521 if self.read_only(cx) {
5522 return;
5523 }
5524 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5525 let selections = self.selections.all::<Point>(cx);
5526 let mut deletion_ranges = Vec::new();
5527 let mut last_outdent = None;
5528 {
5529 let buffer = self.buffer.read(cx);
5530 let snapshot = buffer.snapshot(cx);
5531 for selection in &selections {
5532 let settings = buffer.settings_at(selection.start, cx);
5533 let tab_size = settings.tab_size.get();
5534 let mut rows = selection.spanned_rows(false, &display_map);
5535
5536 // Avoid re-outdenting a row that has already been outdented by a
5537 // previous selection.
5538 if let Some(last_row) = last_outdent {
5539 if last_row == rows.start {
5540 rows.start = rows.start.next_row();
5541 }
5542 }
5543 let has_multiple_rows = rows.len() > 1;
5544 for row in rows.iter_rows() {
5545 let indent_size = snapshot.indent_size_for_line(row);
5546 if indent_size.len > 0 {
5547 let deletion_len = match indent_size.kind {
5548 IndentKind::Space => {
5549 let columns_to_prev_tab_stop = indent_size.len % tab_size;
5550 if columns_to_prev_tab_stop == 0 {
5551 tab_size
5552 } else {
5553 columns_to_prev_tab_stop
5554 }
5555 }
5556 IndentKind::Tab => 1,
5557 };
5558 let start = if has_multiple_rows
5559 || deletion_len > selection.start.column
5560 || indent_size.len < selection.start.column
5561 {
5562 0
5563 } else {
5564 selection.start.column - deletion_len
5565 };
5566 deletion_ranges.push(
5567 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
5568 );
5569 last_outdent = Some(row);
5570 }
5571 }
5572 }
5573 }
5574
5575 self.transact(cx, |this, cx| {
5576 this.buffer.update(cx, |buffer, cx| {
5577 let empty_str: Arc<str> = Arc::default();
5578 buffer.edit(
5579 deletion_ranges
5580 .into_iter()
5581 .map(|range| (range, empty_str.clone())),
5582 None,
5583 cx,
5584 );
5585 });
5586 let selections = this.selections.all::<usize>(cx);
5587 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5588 });
5589 }
5590
5591 pub fn autoindent(&mut self, _: &AutoIndent, cx: &mut ViewContext<Self>) {
5592 if self.read_only(cx) {
5593 return;
5594 }
5595 let selections = self
5596 .selections
5597 .all::<usize>(cx)
5598 .into_iter()
5599 .map(|s| s.range());
5600
5601 self.transact(cx, |this, cx| {
5602 this.buffer.update(cx, |buffer, cx| {
5603 buffer.autoindent_ranges(selections, cx);
5604 });
5605 let selections = this.selections.all::<usize>(cx);
5606 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5607 });
5608 }
5609
5610 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
5611 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5612 let selections = self.selections.all::<Point>(cx);
5613
5614 let mut new_cursors = Vec::new();
5615 let mut edit_ranges = Vec::new();
5616 let mut selections = selections.iter().peekable();
5617 while let Some(selection) = selections.next() {
5618 let mut rows = selection.spanned_rows(false, &display_map);
5619 let goal_display_column = selection.head().to_display_point(&display_map).column();
5620
5621 // Accumulate contiguous regions of rows that we want to delete.
5622 while let Some(next_selection) = selections.peek() {
5623 let next_rows = next_selection.spanned_rows(false, &display_map);
5624 if next_rows.start <= rows.end {
5625 rows.end = next_rows.end;
5626 selections.next().unwrap();
5627 } else {
5628 break;
5629 }
5630 }
5631
5632 let buffer = &display_map.buffer_snapshot;
5633 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
5634 let edit_end;
5635 let cursor_buffer_row;
5636 if buffer.max_point().row >= rows.end.0 {
5637 // If there's a line after the range, delete the \n from the end of the row range
5638 // and position the cursor on the next line.
5639 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
5640 cursor_buffer_row = rows.end;
5641 } else {
5642 // If there isn't a line after the range, delete the \n from the line before the
5643 // start of the row range and position the cursor there.
5644 edit_start = edit_start.saturating_sub(1);
5645 edit_end = buffer.len();
5646 cursor_buffer_row = rows.start.previous_row();
5647 }
5648
5649 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
5650 *cursor.column_mut() =
5651 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
5652
5653 new_cursors.push((
5654 selection.id,
5655 buffer.anchor_after(cursor.to_point(&display_map)),
5656 ));
5657 edit_ranges.push(edit_start..edit_end);
5658 }
5659
5660 self.transact(cx, |this, cx| {
5661 let buffer = this.buffer.update(cx, |buffer, cx| {
5662 let empty_str: Arc<str> = Arc::default();
5663 buffer.edit(
5664 edit_ranges
5665 .into_iter()
5666 .map(|range| (range, empty_str.clone())),
5667 None,
5668 cx,
5669 );
5670 buffer.snapshot(cx)
5671 });
5672 let new_selections = new_cursors
5673 .into_iter()
5674 .map(|(id, cursor)| {
5675 let cursor = cursor.to_point(&buffer);
5676 Selection {
5677 id,
5678 start: cursor,
5679 end: cursor,
5680 reversed: false,
5681 goal: SelectionGoal::None,
5682 }
5683 })
5684 .collect();
5685
5686 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5687 s.select(new_selections);
5688 });
5689 });
5690 }
5691
5692 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
5693 if self.read_only(cx) {
5694 return;
5695 }
5696 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
5697 for selection in self.selections.all::<Point>(cx) {
5698 let start = MultiBufferRow(selection.start.row);
5699 // Treat single line selections as if they include the next line. Otherwise this action
5700 // would do nothing for single line selections individual cursors.
5701 let end = if selection.start.row == selection.end.row {
5702 MultiBufferRow(selection.start.row + 1)
5703 } else {
5704 MultiBufferRow(selection.end.row)
5705 };
5706
5707 if let Some(last_row_range) = row_ranges.last_mut() {
5708 if start <= last_row_range.end {
5709 last_row_range.end = end;
5710 continue;
5711 }
5712 }
5713 row_ranges.push(start..end);
5714 }
5715
5716 let snapshot = self.buffer.read(cx).snapshot(cx);
5717 let mut cursor_positions = Vec::new();
5718 for row_range in &row_ranges {
5719 let anchor = snapshot.anchor_before(Point::new(
5720 row_range.end.previous_row().0,
5721 snapshot.line_len(row_range.end.previous_row()),
5722 ));
5723 cursor_positions.push(anchor..anchor);
5724 }
5725
5726 self.transact(cx, |this, cx| {
5727 for row_range in row_ranges.into_iter().rev() {
5728 for row in row_range.iter_rows().rev() {
5729 let end_of_line = Point::new(row.0, snapshot.line_len(row));
5730 let next_line_row = row.next_row();
5731 let indent = snapshot.indent_size_for_line(next_line_row);
5732 let start_of_next_line = Point::new(next_line_row.0, indent.len);
5733
5734 let replace = if snapshot.line_len(next_line_row) > indent.len {
5735 " "
5736 } else {
5737 ""
5738 };
5739
5740 this.buffer.update(cx, |buffer, cx| {
5741 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
5742 });
5743 }
5744 }
5745
5746 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5747 s.select_anchor_ranges(cursor_positions)
5748 });
5749 });
5750 }
5751
5752 pub fn sort_lines_case_sensitive(
5753 &mut self,
5754 _: &SortLinesCaseSensitive,
5755 cx: &mut ViewContext<Self>,
5756 ) {
5757 self.manipulate_lines(cx, |lines| lines.sort())
5758 }
5759
5760 pub fn sort_lines_case_insensitive(
5761 &mut self,
5762 _: &SortLinesCaseInsensitive,
5763 cx: &mut ViewContext<Self>,
5764 ) {
5765 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
5766 }
5767
5768 pub fn unique_lines_case_insensitive(
5769 &mut self,
5770 _: &UniqueLinesCaseInsensitive,
5771 cx: &mut ViewContext<Self>,
5772 ) {
5773 self.manipulate_lines(cx, |lines| {
5774 let mut seen = HashSet::default();
5775 lines.retain(|line| seen.insert(line.to_lowercase()));
5776 })
5777 }
5778
5779 pub fn unique_lines_case_sensitive(
5780 &mut self,
5781 _: &UniqueLinesCaseSensitive,
5782 cx: &mut ViewContext<Self>,
5783 ) {
5784 self.manipulate_lines(cx, |lines| {
5785 let mut seen = HashSet::default();
5786 lines.retain(|line| seen.insert(*line));
5787 })
5788 }
5789
5790 pub fn revert_file(&mut self, _: &RevertFile, cx: &mut ViewContext<Self>) {
5791 let mut revert_changes = HashMap::default();
5792 let snapshot = self.snapshot(cx);
5793 for hunk in hunks_for_ranges(
5794 Some(Point::zero()..snapshot.buffer_snapshot.max_point()).into_iter(),
5795 &snapshot,
5796 ) {
5797 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
5798 }
5799 if !revert_changes.is_empty() {
5800 self.transact(cx, |editor, cx| {
5801 editor.revert(revert_changes, cx);
5802 });
5803 }
5804 }
5805
5806 pub fn reload_file(&mut self, _: &ReloadFile, cx: &mut ViewContext<Self>) {
5807 let Some(project) = self.project.clone() else {
5808 return;
5809 };
5810 self.reload(project, cx).detach_and_notify_err(cx);
5811 }
5812
5813 pub fn revert_selected_hunks(&mut self, _: &RevertSelectedHunks, cx: &mut ViewContext<Self>) {
5814 let revert_changes = self.gather_revert_changes(&self.selections.all(cx), cx);
5815 if !revert_changes.is_empty() {
5816 self.transact(cx, |editor, cx| {
5817 editor.revert(revert_changes, cx);
5818 });
5819 }
5820 }
5821
5822 fn revert_hunk(&mut self, hunk: HoveredHunk, cx: &mut ViewContext<Editor>) {
5823 let snapshot = self.buffer.read(cx).read(cx);
5824 if let Some(hunk) = crate::hunk_diff::to_diff_hunk(&hunk, &snapshot) {
5825 drop(snapshot);
5826 let mut revert_changes = HashMap::default();
5827 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
5828 if !revert_changes.is_empty() {
5829 self.revert(revert_changes, cx)
5830 }
5831 }
5832 }
5833
5834 pub fn open_active_item_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
5835 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
5836 let project_path = buffer.read(cx).project_path(cx)?;
5837 let project = self.project.as_ref()?.read(cx);
5838 let entry = project.entry_for_path(&project_path, cx)?;
5839 let parent = match &entry.canonical_path {
5840 Some(canonical_path) => canonical_path.to_path_buf(),
5841 None => project.absolute_path(&project_path, cx)?,
5842 }
5843 .parent()?
5844 .to_path_buf();
5845 Some(parent)
5846 }) {
5847 cx.dispatch_action(OpenTerminal { working_directory }.boxed_clone());
5848 }
5849 }
5850
5851 fn gather_revert_changes(
5852 &mut self,
5853 selections: &[Selection<Point>],
5854 cx: &mut ViewContext<'_, Editor>,
5855 ) -> HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>> {
5856 let mut revert_changes = HashMap::default();
5857 let snapshot = self.snapshot(cx);
5858 for hunk in hunks_for_selections(&snapshot, selections) {
5859 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
5860 }
5861 revert_changes
5862 }
5863
5864 pub fn prepare_revert_change(
5865 &mut self,
5866 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
5867 hunk: &MultiBufferDiffHunk,
5868 cx: &AppContext,
5869 ) -> Option<()> {
5870 let buffer = self.buffer.read(cx).buffer(hunk.buffer_id)?;
5871 let buffer = buffer.read(cx);
5872 let change_set = &self.diff_map.diff_bases.get(&hunk.buffer_id)?.change_set;
5873 let original_text = change_set
5874 .read(cx)
5875 .base_text
5876 .as_ref()?
5877 .read(cx)
5878 .as_rope()
5879 .slice(hunk.diff_base_byte_range.clone());
5880 let buffer_snapshot = buffer.snapshot();
5881 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
5882 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
5883 probe
5884 .0
5885 .start
5886 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
5887 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
5888 }) {
5889 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
5890 Some(())
5891 } else {
5892 None
5893 }
5894 }
5895
5896 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
5897 self.manipulate_lines(cx, |lines| lines.reverse())
5898 }
5899
5900 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
5901 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
5902 }
5903
5904 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
5905 where
5906 Fn: FnMut(&mut Vec<&str>),
5907 {
5908 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5909 let buffer = self.buffer.read(cx).snapshot(cx);
5910
5911 let mut edits = Vec::new();
5912
5913 let selections = self.selections.all::<Point>(cx);
5914 let mut selections = selections.iter().peekable();
5915 let mut contiguous_row_selections = Vec::new();
5916 let mut new_selections = Vec::new();
5917 let mut added_lines = 0;
5918 let mut removed_lines = 0;
5919
5920 while let Some(selection) = selections.next() {
5921 let (start_row, end_row) = consume_contiguous_rows(
5922 &mut contiguous_row_selections,
5923 selection,
5924 &display_map,
5925 &mut selections,
5926 );
5927
5928 let start_point = Point::new(start_row.0, 0);
5929 let end_point = Point::new(
5930 end_row.previous_row().0,
5931 buffer.line_len(end_row.previous_row()),
5932 );
5933 let text = buffer
5934 .text_for_range(start_point..end_point)
5935 .collect::<String>();
5936
5937 let mut lines = text.split('\n').collect_vec();
5938
5939 let lines_before = lines.len();
5940 callback(&mut lines);
5941 let lines_after = lines.len();
5942
5943 edits.push((start_point..end_point, lines.join("\n")));
5944
5945 // Selections must change based on added and removed line count
5946 let start_row =
5947 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
5948 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
5949 new_selections.push(Selection {
5950 id: selection.id,
5951 start: start_row,
5952 end: end_row,
5953 goal: SelectionGoal::None,
5954 reversed: selection.reversed,
5955 });
5956
5957 if lines_after > lines_before {
5958 added_lines += lines_after - lines_before;
5959 } else if lines_before > lines_after {
5960 removed_lines += lines_before - lines_after;
5961 }
5962 }
5963
5964 self.transact(cx, |this, cx| {
5965 let buffer = this.buffer.update(cx, |buffer, cx| {
5966 buffer.edit(edits, None, cx);
5967 buffer.snapshot(cx)
5968 });
5969
5970 // Recalculate offsets on newly edited buffer
5971 let new_selections = new_selections
5972 .iter()
5973 .map(|s| {
5974 let start_point = Point::new(s.start.0, 0);
5975 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
5976 Selection {
5977 id: s.id,
5978 start: buffer.point_to_offset(start_point),
5979 end: buffer.point_to_offset(end_point),
5980 goal: s.goal,
5981 reversed: s.reversed,
5982 }
5983 })
5984 .collect();
5985
5986 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5987 s.select(new_selections);
5988 });
5989
5990 this.request_autoscroll(Autoscroll::fit(), cx);
5991 });
5992 }
5993
5994 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
5995 self.manipulate_text(cx, |text| text.to_uppercase())
5996 }
5997
5998 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
5999 self.manipulate_text(cx, |text| text.to_lowercase())
6000 }
6001
6002 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
6003 self.manipulate_text(cx, |text| {
6004 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6005 // https://github.com/rutrum/convert-case/issues/16
6006 text.split('\n')
6007 .map(|line| line.to_case(Case::Title))
6008 .join("\n")
6009 })
6010 }
6011
6012 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
6013 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
6014 }
6015
6016 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
6017 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
6018 }
6019
6020 pub fn convert_to_upper_camel_case(
6021 &mut self,
6022 _: &ConvertToUpperCamelCase,
6023 cx: &mut ViewContext<Self>,
6024 ) {
6025 self.manipulate_text(cx, |text| {
6026 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6027 // https://github.com/rutrum/convert-case/issues/16
6028 text.split('\n')
6029 .map(|line| line.to_case(Case::UpperCamel))
6030 .join("\n")
6031 })
6032 }
6033
6034 pub fn convert_to_lower_camel_case(
6035 &mut self,
6036 _: &ConvertToLowerCamelCase,
6037 cx: &mut ViewContext<Self>,
6038 ) {
6039 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
6040 }
6041
6042 pub fn convert_to_opposite_case(
6043 &mut self,
6044 _: &ConvertToOppositeCase,
6045 cx: &mut ViewContext<Self>,
6046 ) {
6047 self.manipulate_text(cx, |text| {
6048 text.chars()
6049 .fold(String::with_capacity(text.len()), |mut t, c| {
6050 if c.is_uppercase() {
6051 t.extend(c.to_lowercase());
6052 } else {
6053 t.extend(c.to_uppercase());
6054 }
6055 t
6056 })
6057 })
6058 }
6059
6060 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6061 where
6062 Fn: FnMut(&str) -> String,
6063 {
6064 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6065 let buffer = self.buffer.read(cx).snapshot(cx);
6066
6067 let mut new_selections = Vec::new();
6068 let mut edits = Vec::new();
6069 let mut selection_adjustment = 0i32;
6070
6071 for selection in self.selections.all::<usize>(cx) {
6072 let selection_is_empty = selection.is_empty();
6073
6074 let (start, end) = if selection_is_empty {
6075 let word_range = movement::surrounding_word(
6076 &display_map,
6077 selection.start.to_display_point(&display_map),
6078 );
6079 let start = word_range.start.to_offset(&display_map, Bias::Left);
6080 let end = word_range.end.to_offset(&display_map, Bias::Left);
6081 (start, end)
6082 } else {
6083 (selection.start, selection.end)
6084 };
6085
6086 let text = buffer.text_for_range(start..end).collect::<String>();
6087 let old_length = text.len() as i32;
6088 let text = callback(&text);
6089
6090 new_selections.push(Selection {
6091 start: (start as i32 - selection_adjustment) as usize,
6092 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
6093 goal: SelectionGoal::None,
6094 ..selection
6095 });
6096
6097 selection_adjustment += old_length - text.len() as i32;
6098
6099 edits.push((start..end, text));
6100 }
6101
6102 self.transact(cx, |this, cx| {
6103 this.buffer.update(cx, |buffer, cx| {
6104 buffer.edit(edits, None, cx);
6105 });
6106
6107 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6108 s.select(new_selections);
6109 });
6110
6111 this.request_autoscroll(Autoscroll::fit(), cx);
6112 });
6113 }
6114
6115 pub fn duplicate_line(&mut self, upwards: bool, cx: &mut ViewContext<Self>) {
6116 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6117 let buffer = &display_map.buffer_snapshot;
6118 let selections = self.selections.all::<Point>(cx);
6119
6120 let mut edits = Vec::new();
6121 let mut selections_iter = selections.iter().peekable();
6122 while let Some(selection) = selections_iter.next() {
6123 // Avoid duplicating the same lines twice.
6124 let mut rows = selection.spanned_rows(false, &display_map);
6125
6126 while let Some(next_selection) = selections_iter.peek() {
6127 let next_rows = next_selection.spanned_rows(false, &display_map);
6128 if next_rows.start < rows.end {
6129 rows.end = next_rows.end;
6130 selections_iter.next().unwrap();
6131 } else {
6132 break;
6133 }
6134 }
6135
6136 // Copy the text from the selected row region and splice it either at the start
6137 // or end of the region.
6138 let start = Point::new(rows.start.0, 0);
6139 let end = Point::new(
6140 rows.end.previous_row().0,
6141 buffer.line_len(rows.end.previous_row()),
6142 );
6143 let text = buffer
6144 .text_for_range(start..end)
6145 .chain(Some("\n"))
6146 .collect::<String>();
6147 let insert_location = if upwards {
6148 Point::new(rows.end.0, 0)
6149 } else {
6150 start
6151 };
6152 edits.push((insert_location..insert_location, text));
6153 }
6154
6155 self.transact(cx, |this, cx| {
6156 this.buffer.update(cx, |buffer, cx| {
6157 buffer.edit(edits, None, cx);
6158 });
6159
6160 this.request_autoscroll(Autoscroll::fit(), cx);
6161 });
6162 }
6163
6164 pub fn duplicate_line_up(&mut self, _: &DuplicateLineUp, cx: &mut ViewContext<Self>) {
6165 self.duplicate_line(true, cx);
6166 }
6167
6168 pub fn duplicate_line_down(&mut self, _: &DuplicateLineDown, cx: &mut ViewContext<Self>) {
6169 self.duplicate_line(false, cx);
6170 }
6171
6172 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
6173 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6174 let buffer = self.buffer.read(cx).snapshot(cx);
6175
6176 let mut edits = Vec::new();
6177 let mut unfold_ranges = Vec::new();
6178 let mut refold_creases = Vec::new();
6179
6180 let selections = self.selections.all::<Point>(cx);
6181 let mut selections = selections.iter().peekable();
6182 let mut contiguous_row_selections = Vec::new();
6183 let mut new_selections = Vec::new();
6184
6185 while let Some(selection) = selections.next() {
6186 // Find all the selections that span a contiguous row range
6187 let (start_row, end_row) = consume_contiguous_rows(
6188 &mut contiguous_row_selections,
6189 selection,
6190 &display_map,
6191 &mut selections,
6192 );
6193
6194 // Move the text spanned by the row range to be before the line preceding the row range
6195 if start_row.0 > 0 {
6196 let range_to_move = Point::new(
6197 start_row.previous_row().0,
6198 buffer.line_len(start_row.previous_row()),
6199 )
6200 ..Point::new(
6201 end_row.previous_row().0,
6202 buffer.line_len(end_row.previous_row()),
6203 );
6204 let insertion_point = display_map
6205 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
6206 .0;
6207
6208 // Don't move lines across excerpts
6209 if buffer
6210 .excerpt_boundaries_in_range((
6211 Bound::Excluded(insertion_point),
6212 Bound::Included(range_to_move.end),
6213 ))
6214 .next()
6215 .is_none()
6216 {
6217 let text = buffer
6218 .text_for_range(range_to_move.clone())
6219 .flat_map(|s| s.chars())
6220 .skip(1)
6221 .chain(['\n'])
6222 .collect::<String>();
6223
6224 edits.push((
6225 buffer.anchor_after(range_to_move.start)
6226 ..buffer.anchor_before(range_to_move.end),
6227 String::new(),
6228 ));
6229 let insertion_anchor = buffer.anchor_after(insertion_point);
6230 edits.push((insertion_anchor..insertion_anchor, text));
6231
6232 let row_delta = range_to_move.start.row - insertion_point.row + 1;
6233
6234 // Move selections up
6235 new_selections.extend(contiguous_row_selections.drain(..).map(
6236 |mut selection| {
6237 selection.start.row -= row_delta;
6238 selection.end.row -= row_delta;
6239 selection
6240 },
6241 ));
6242
6243 // Move folds up
6244 unfold_ranges.push(range_to_move.clone());
6245 for fold in display_map.folds_in_range(
6246 buffer.anchor_before(range_to_move.start)
6247 ..buffer.anchor_after(range_to_move.end),
6248 ) {
6249 let mut start = fold.range.start.to_point(&buffer);
6250 let mut end = fold.range.end.to_point(&buffer);
6251 start.row -= row_delta;
6252 end.row -= row_delta;
6253 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
6254 }
6255 }
6256 }
6257
6258 // If we didn't move line(s), preserve the existing selections
6259 new_selections.append(&mut contiguous_row_selections);
6260 }
6261
6262 self.transact(cx, |this, cx| {
6263 this.unfold_ranges(&unfold_ranges, true, true, cx);
6264 this.buffer.update(cx, |buffer, cx| {
6265 for (range, text) in edits {
6266 buffer.edit([(range, text)], None, cx);
6267 }
6268 });
6269 this.fold_creases(refold_creases, true, cx);
6270 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6271 s.select(new_selections);
6272 })
6273 });
6274 }
6275
6276 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
6277 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6278 let buffer = self.buffer.read(cx).snapshot(cx);
6279
6280 let mut edits = Vec::new();
6281 let mut unfold_ranges = Vec::new();
6282 let mut refold_creases = Vec::new();
6283
6284 let selections = self.selections.all::<Point>(cx);
6285 let mut selections = selections.iter().peekable();
6286 let mut contiguous_row_selections = Vec::new();
6287 let mut new_selections = Vec::new();
6288
6289 while let Some(selection) = selections.next() {
6290 // Find all the selections that span a contiguous row range
6291 let (start_row, end_row) = consume_contiguous_rows(
6292 &mut contiguous_row_selections,
6293 selection,
6294 &display_map,
6295 &mut selections,
6296 );
6297
6298 // Move the text spanned by the row range to be after the last line of the row range
6299 if end_row.0 <= buffer.max_point().row {
6300 let range_to_move =
6301 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
6302 let insertion_point = display_map
6303 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
6304 .0;
6305
6306 // Don't move lines across excerpt boundaries
6307 if buffer
6308 .excerpt_boundaries_in_range((
6309 Bound::Excluded(range_to_move.start),
6310 Bound::Included(insertion_point),
6311 ))
6312 .next()
6313 .is_none()
6314 {
6315 let mut text = String::from("\n");
6316 text.extend(buffer.text_for_range(range_to_move.clone()));
6317 text.pop(); // Drop trailing newline
6318 edits.push((
6319 buffer.anchor_after(range_to_move.start)
6320 ..buffer.anchor_before(range_to_move.end),
6321 String::new(),
6322 ));
6323 let insertion_anchor = buffer.anchor_after(insertion_point);
6324 edits.push((insertion_anchor..insertion_anchor, text));
6325
6326 let row_delta = insertion_point.row - range_to_move.end.row + 1;
6327
6328 // Move selections down
6329 new_selections.extend(contiguous_row_selections.drain(..).map(
6330 |mut selection| {
6331 selection.start.row += row_delta;
6332 selection.end.row += row_delta;
6333 selection
6334 },
6335 ));
6336
6337 // Move folds down
6338 unfold_ranges.push(range_to_move.clone());
6339 for fold in display_map.folds_in_range(
6340 buffer.anchor_before(range_to_move.start)
6341 ..buffer.anchor_after(range_to_move.end),
6342 ) {
6343 let mut start = fold.range.start.to_point(&buffer);
6344 let mut end = fold.range.end.to_point(&buffer);
6345 start.row += row_delta;
6346 end.row += row_delta;
6347 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
6348 }
6349 }
6350 }
6351
6352 // If we didn't move line(s), preserve the existing selections
6353 new_selections.append(&mut contiguous_row_selections);
6354 }
6355
6356 self.transact(cx, |this, cx| {
6357 this.unfold_ranges(&unfold_ranges, true, true, cx);
6358 this.buffer.update(cx, |buffer, cx| {
6359 for (range, text) in edits {
6360 buffer.edit([(range, text)], None, cx);
6361 }
6362 });
6363 this.fold_creases(refold_creases, true, cx);
6364 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
6365 });
6366 }
6367
6368 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
6369 let text_layout_details = &self.text_layout_details(cx);
6370 self.transact(cx, |this, cx| {
6371 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6372 let mut edits: Vec<(Range<usize>, String)> = Default::default();
6373 let line_mode = s.line_mode;
6374 s.move_with(|display_map, selection| {
6375 if !selection.is_empty() || line_mode {
6376 return;
6377 }
6378
6379 let mut head = selection.head();
6380 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
6381 if head.column() == display_map.line_len(head.row()) {
6382 transpose_offset = display_map
6383 .buffer_snapshot
6384 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6385 }
6386
6387 if transpose_offset == 0 {
6388 return;
6389 }
6390
6391 *head.column_mut() += 1;
6392 head = display_map.clip_point(head, Bias::Right);
6393 let goal = SelectionGoal::HorizontalPosition(
6394 display_map
6395 .x_for_display_point(head, text_layout_details)
6396 .into(),
6397 );
6398 selection.collapse_to(head, goal);
6399
6400 let transpose_start = display_map
6401 .buffer_snapshot
6402 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6403 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
6404 let transpose_end = display_map
6405 .buffer_snapshot
6406 .clip_offset(transpose_offset + 1, Bias::Right);
6407 if let Some(ch) =
6408 display_map.buffer_snapshot.chars_at(transpose_start).next()
6409 {
6410 edits.push((transpose_start..transpose_offset, String::new()));
6411 edits.push((transpose_end..transpose_end, ch.to_string()));
6412 }
6413 }
6414 });
6415 edits
6416 });
6417 this.buffer
6418 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
6419 let selections = this.selections.all::<usize>(cx);
6420 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6421 s.select(selections);
6422 });
6423 });
6424 }
6425
6426 pub fn rewrap(&mut self, _: &Rewrap, cx: &mut ViewContext<Self>) {
6427 self.rewrap_impl(IsVimMode::No, cx)
6428 }
6429
6430 pub fn rewrap_impl(&mut self, is_vim_mode: IsVimMode, cx: &mut ViewContext<Self>) {
6431 let buffer = self.buffer.read(cx).snapshot(cx);
6432 let selections = self.selections.all::<Point>(cx);
6433 let mut selections = selections.iter().peekable();
6434
6435 let mut edits = Vec::new();
6436 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
6437
6438 while let Some(selection) = selections.next() {
6439 let mut start_row = selection.start.row;
6440 let mut end_row = selection.end.row;
6441
6442 // Skip selections that overlap with a range that has already been rewrapped.
6443 let selection_range = start_row..end_row;
6444 if rewrapped_row_ranges
6445 .iter()
6446 .any(|range| range.overlaps(&selection_range))
6447 {
6448 continue;
6449 }
6450
6451 let mut should_rewrap = is_vim_mode == IsVimMode::Yes;
6452
6453 if let Some(language_scope) = buffer.language_scope_at(selection.head()) {
6454 match language_scope.language_name().0.as_ref() {
6455 "Markdown" | "Plain Text" => {
6456 should_rewrap = true;
6457 }
6458 _ => {}
6459 }
6460 }
6461
6462 let tab_size = buffer.settings_at(selection.head(), cx).tab_size;
6463
6464 // Since not all lines in the selection may be at the same indent
6465 // level, choose the indent size that is the most common between all
6466 // of the lines.
6467 //
6468 // If there is a tie, we use the deepest indent.
6469 let (indent_size, indent_end) = {
6470 let mut indent_size_occurrences = HashMap::default();
6471 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
6472
6473 for row in start_row..=end_row {
6474 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
6475 rows_by_indent_size.entry(indent).or_default().push(row);
6476 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
6477 }
6478
6479 let indent_size = indent_size_occurrences
6480 .into_iter()
6481 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
6482 .map(|(indent, _)| indent)
6483 .unwrap_or_default();
6484 let row = rows_by_indent_size[&indent_size][0];
6485 let indent_end = Point::new(row, indent_size.len);
6486
6487 (indent_size, indent_end)
6488 };
6489
6490 let mut line_prefix = indent_size.chars().collect::<String>();
6491
6492 if let Some(comment_prefix) =
6493 buffer
6494 .language_scope_at(selection.head())
6495 .and_then(|language| {
6496 language
6497 .line_comment_prefixes()
6498 .iter()
6499 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
6500 .cloned()
6501 })
6502 {
6503 line_prefix.push_str(&comment_prefix);
6504 should_rewrap = true;
6505 }
6506
6507 if !should_rewrap {
6508 continue;
6509 }
6510
6511 if selection.is_empty() {
6512 'expand_upwards: while start_row > 0 {
6513 let prev_row = start_row - 1;
6514 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
6515 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
6516 {
6517 start_row = prev_row;
6518 } else {
6519 break 'expand_upwards;
6520 }
6521 }
6522
6523 'expand_downwards: while end_row < buffer.max_point().row {
6524 let next_row = end_row + 1;
6525 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
6526 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
6527 {
6528 end_row = next_row;
6529 } else {
6530 break 'expand_downwards;
6531 }
6532 }
6533 }
6534
6535 let start = Point::new(start_row, 0);
6536 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
6537 let selection_text = buffer.text_for_range(start..end).collect::<String>();
6538 let Some(lines_without_prefixes) = selection_text
6539 .lines()
6540 .map(|line| {
6541 line.strip_prefix(&line_prefix)
6542 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
6543 .ok_or_else(|| {
6544 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
6545 })
6546 })
6547 .collect::<Result<Vec<_>, _>>()
6548 .log_err()
6549 else {
6550 continue;
6551 };
6552
6553 let wrap_column = buffer
6554 .settings_at(Point::new(start_row, 0), cx)
6555 .preferred_line_length as usize;
6556 let wrapped_text = wrap_with_prefix(
6557 line_prefix,
6558 lines_without_prefixes.join(" "),
6559 wrap_column,
6560 tab_size,
6561 );
6562
6563 // TODO: should always use char-based diff while still supporting cursor behavior that
6564 // matches vim.
6565 let diff = match is_vim_mode {
6566 IsVimMode::Yes => TextDiff::from_lines(&selection_text, &wrapped_text),
6567 IsVimMode::No => TextDiff::from_chars(&selection_text, &wrapped_text),
6568 };
6569 let mut offset = start.to_offset(&buffer);
6570 let mut moved_since_edit = true;
6571
6572 for change in diff.iter_all_changes() {
6573 let value = change.value();
6574 match change.tag() {
6575 ChangeTag::Equal => {
6576 offset += value.len();
6577 moved_since_edit = true;
6578 }
6579 ChangeTag::Delete => {
6580 let start = buffer.anchor_after(offset);
6581 let end = buffer.anchor_before(offset + value.len());
6582
6583 if moved_since_edit {
6584 edits.push((start..end, String::new()));
6585 } else {
6586 edits.last_mut().unwrap().0.end = end;
6587 }
6588
6589 offset += value.len();
6590 moved_since_edit = false;
6591 }
6592 ChangeTag::Insert => {
6593 if moved_since_edit {
6594 let anchor = buffer.anchor_after(offset);
6595 edits.push((anchor..anchor, value.to_string()));
6596 } else {
6597 edits.last_mut().unwrap().1.push_str(value);
6598 }
6599
6600 moved_since_edit = false;
6601 }
6602 }
6603 }
6604
6605 rewrapped_row_ranges.push(start_row..=end_row);
6606 }
6607
6608 self.buffer
6609 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
6610 }
6611
6612 pub fn cut_common(&mut self, cx: &mut ViewContext<Self>) -> ClipboardItem {
6613 let mut text = String::new();
6614 let buffer = self.buffer.read(cx).snapshot(cx);
6615 let mut selections = self.selections.all::<Point>(cx);
6616 let mut clipboard_selections = Vec::with_capacity(selections.len());
6617 {
6618 let max_point = buffer.max_point();
6619 let mut is_first = true;
6620 for selection in &mut selections {
6621 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6622 if is_entire_line {
6623 selection.start = Point::new(selection.start.row, 0);
6624 if !selection.is_empty() && selection.end.column == 0 {
6625 selection.end = cmp::min(max_point, selection.end);
6626 } else {
6627 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
6628 }
6629 selection.goal = SelectionGoal::None;
6630 }
6631 if is_first {
6632 is_first = false;
6633 } else {
6634 text += "\n";
6635 }
6636 let mut len = 0;
6637 for chunk in buffer.text_for_range(selection.start..selection.end) {
6638 text.push_str(chunk);
6639 len += chunk.len();
6640 }
6641 clipboard_selections.push(ClipboardSelection {
6642 len,
6643 is_entire_line,
6644 first_line_indent: buffer
6645 .indent_size_for_line(MultiBufferRow(selection.start.row))
6646 .len,
6647 });
6648 }
6649 }
6650
6651 self.transact(cx, |this, cx| {
6652 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6653 s.select(selections);
6654 });
6655 this.insert("", cx);
6656 });
6657 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
6658 }
6659
6660 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
6661 let item = self.cut_common(cx);
6662 cx.write_to_clipboard(item);
6663 }
6664
6665 pub fn kill_ring_cut(&mut self, _: &KillRingCut, cx: &mut ViewContext<Self>) {
6666 self.change_selections(None, cx, |s| {
6667 s.move_with(|snapshot, sel| {
6668 if sel.is_empty() {
6669 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
6670 }
6671 });
6672 });
6673 let item = self.cut_common(cx);
6674 cx.set_global(KillRing(item))
6675 }
6676
6677 pub fn kill_ring_yank(&mut self, _: &KillRingYank, cx: &mut ViewContext<Self>) {
6678 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
6679 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
6680 (kill_ring.text().to_string(), kill_ring.metadata_json())
6681 } else {
6682 return;
6683 }
6684 } else {
6685 return;
6686 };
6687 self.do_paste(&text, metadata, false, cx);
6688 }
6689
6690 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
6691 let selections = self.selections.all::<Point>(cx);
6692 let buffer = self.buffer.read(cx).read(cx);
6693 let mut text = String::new();
6694
6695 let mut clipboard_selections = Vec::with_capacity(selections.len());
6696 {
6697 let max_point = buffer.max_point();
6698 let mut is_first = true;
6699 for selection in selections.iter() {
6700 let mut start = selection.start;
6701 let mut end = selection.end;
6702 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6703 if is_entire_line {
6704 start = Point::new(start.row, 0);
6705 end = cmp::min(max_point, Point::new(end.row + 1, 0));
6706 }
6707 if is_first {
6708 is_first = false;
6709 } else {
6710 text += "\n";
6711 }
6712 let mut len = 0;
6713 for chunk in buffer.text_for_range(start..end) {
6714 text.push_str(chunk);
6715 len += chunk.len();
6716 }
6717 clipboard_selections.push(ClipboardSelection {
6718 len,
6719 is_entire_line,
6720 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
6721 });
6722 }
6723 }
6724
6725 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
6726 text,
6727 clipboard_selections,
6728 ));
6729 }
6730
6731 pub fn do_paste(
6732 &mut self,
6733 text: &String,
6734 clipboard_selections: Option<Vec<ClipboardSelection>>,
6735 handle_entire_lines: bool,
6736 cx: &mut ViewContext<Self>,
6737 ) {
6738 if self.read_only(cx) {
6739 return;
6740 }
6741
6742 let clipboard_text = Cow::Borrowed(text);
6743
6744 self.transact(cx, |this, cx| {
6745 if let Some(mut clipboard_selections) = clipboard_selections {
6746 let old_selections = this.selections.all::<usize>(cx);
6747 let all_selections_were_entire_line =
6748 clipboard_selections.iter().all(|s| s.is_entire_line);
6749 let first_selection_indent_column =
6750 clipboard_selections.first().map(|s| s.first_line_indent);
6751 if clipboard_selections.len() != old_selections.len() {
6752 clipboard_selections.drain(..);
6753 }
6754 let cursor_offset = this.selections.last::<usize>(cx).head();
6755 let mut auto_indent_on_paste = true;
6756
6757 this.buffer.update(cx, |buffer, cx| {
6758 let snapshot = buffer.read(cx);
6759 auto_indent_on_paste =
6760 snapshot.settings_at(cursor_offset, cx).auto_indent_on_paste;
6761
6762 let mut start_offset = 0;
6763 let mut edits = Vec::new();
6764 let mut original_indent_columns = Vec::new();
6765 for (ix, selection) in old_selections.iter().enumerate() {
6766 let to_insert;
6767 let entire_line;
6768 let original_indent_column;
6769 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
6770 let end_offset = start_offset + clipboard_selection.len;
6771 to_insert = &clipboard_text[start_offset..end_offset];
6772 entire_line = clipboard_selection.is_entire_line;
6773 start_offset = end_offset + 1;
6774 original_indent_column = Some(clipboard_selection.first_line_indent);
6775 } else {
6776 to_insert = clipboard_text.as_str();
6777 entire_line = all_selections_were_entire_line;
6778 original_indent_column = first_selection_indent_column
6779 }
6780
6781 // If the corresponding selection was empty when this slice of the
6782 // clipboard text was written, then the entire line containing the
6783 // selection was copied. If this selection is also currently empty,
6784 // then paste the line before the current line of the buffer.
6785 let range = if selection.is_empty() && handle_entire_lines && entire_line {
6786 let column = selection.start.to_point(&snapshot).column as usize;
6787 let line_start = selection.start - column;
6788 line_start..line_start
6789 } else {
6790 selection.range()
6791 };
6792
6793 edits.push((range, to_insert));
6794 original_indent_columns.extend(original_indent_column);
6795 }
6796 drop(snapshot);
6797
6798 buffer.edit(
6799 edits,
6800 if auto_indent_on_paste {
6801 Some(AutoindentMode::Block {
6802 original_indent_columns,
6803 })
6804 } else {
6805 None
6806 },
6807 cx,
6808 );
6809 });
6810
6811 let selections = this.selections.all::<usize>(cx);
6812 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6813 } else {
6814 this.insert(&clipboard_text, cx);
6815 }
6816 });
6817 }
6818
6819 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
6820 if let Some(item) = cx.read_from_clipboard() {
6821 let entries = item.entries();
6822
6823 match entries.first() {
6824 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
6825 // of all the pasted entries.
6826 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
6827 .do_paste(
6828 clipboard_string.text(),
6829 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
6830 true,
6831 cx,
6832 ),
6833 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, cx),
6834 }
6835 }
6836 }
6837
6838 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
6839 if self.read_only(cx) {
6840 return;
6841 }
6842
6843 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
6844 if let Some((selections, _)) =
6845 self.selection_history.transaction(transaction_id).cloned()
6846 {
6847 self.change_selections(None, cx, |s| {
6848 s.select_anchors(selections.to_vec());
6849 });
6850 }
6851 self.request_autoscroll(Autoscroll::fit(), cx);
6852 self.unmark_text(cx);
6853 self.refresh_inline_completion(true, false, cx);
6854 cx.emit(EditorEvent::Edited { transaction_id });
6855 cx.emit(EditorEvent::TransactionUndone { transaction_id });
6856 }
6857 }
6858
6859 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
6860 if self.read_only(cx) {
6861 return;
6862 }
6863
6864 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
6865 if let Some((_, Some(selections))) =
6866 self.selection_history.transaction(transaction_id).cloned()
6867 {
6868 self.change_selections(None, cx, |s| {
6869 s.select_anchors(selections.to_vec());
6870 });
6871 }
6872 self.request_autoscroll(Autoscroll::fit(), cx);
6873 self.unmark_text(cx);
6874 self.refresh_inline_completion(true, false, cx);
6875 cx.emit(EditorEvent::Edited { transaction_id });
6876 }
6877 }
6878
6879 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
6880 self.buffer
6881 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
6882 }
6883
6884 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut ViewContext<Self>) {
6885 self.buffer
6886 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
6887 }
6888
6889 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
6890 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6891 let line_mode = s.line_mode;
6892 s.move_with(|map, selection| {
6893 let cursor = if selection.is_empty() && !line_mode {
6894 movement::left(map, selection.start)
6895 } else {
6896 selection.start
6897 };
6898 selection.collapse_to(cursor, SelectionGoal::None);
6899 });
6900 })
6901 }
6902
6903 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
6904 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6905 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
6906 })
6907 }
6908
6909 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
6910 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6911 let line_mode = s.line_mode;
6912 s.move_with(|map, selection| {
6913 let cursor = if selection.is_empty() && !line_mode {
6914 movement::right(map, selection.end)
6915 } else {
6916 selection.end
6917 };
6918 selection.collapse_to(cursor, SelectionGoal::None)
6919 });
6920 })
6921 }
6922
6923 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
6924 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6925 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
6926 })
6927 }
6928
6929 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
6930 if self.take_rename(true, cx).is_some() {
6931 return;
6932 }
6933
6934 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6935 cx.propagate();
6936 return;
6937 }
6938
6939 let text_layout_details = &self.text_layout_details(cx);
6940 let selection_count = self.selections.count();
6941 let first_selection = self.selections.first_anchor();
6942
6943 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6944 let line_mode = s.line_mode;
6945 s.move_with(|map, selection| {
6946 if !selection.is_empty() && !line_mode {
6947 selection.goal = SelectionGoal::None;
6948 }
6949 let (cursor, goal) = movement::up(
6950 map,
6951 selection.start,
6952 selection.goal,
6953 false,
6954 text_layout_details,
6955 );
6956 selection.collapse_to(cursor, goal);
6957 });
6958 });
6959
6960 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
6961 {
6962 cx.propagate();
6963 }
6964 }
6965
6966 pub fn move_up_by_lines(&mut self, action: &MoveUpByLines, cx: &mut ViewContext<Self>) {
6967 if self.take_rename(true, cx).is_some() {
6968 return;
6969 }
6970
6971 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6972 cx.propagate();
6973 return;
6974 }
6975
6976 let text_layout_details = &self.text_layout_details(cx);
6977
6978 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6979 let line_mode = s.line_mode;
6980 s.move_with(|map, selection| {
6981 if !selection.is_empty() && !line_mode {
6982 selection.goal = SelectionGoal::None;
6983 }
6984 let (cursor, goal) = movement::up_by_rows(
6985 map,
6986 selection.start,
6987 action.lines,
6988 selection.goal,
6989 false,
6990 text_layout_details,
6991 );
6992 selection.collapse_to(cursor, goal);
6993 });
6994 })
6995 }
6996
6997 pub fn move_down_by_lines(&mut self, action: &MoveDownByLines, cx: &mut ViewContext<Self>) {
6998 if self.take_rename(true, cx).is_some() {
6999 return;
7000 }
7001
7002 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7003 cx.propagate();
7004 return;
7005 }
7006
7007 let text_layout_details = &self.text_layout_details(cx);
7008
7009 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7010 let line_mode = s.line_mode;
7011 s.move_with(|map, selection| {
7012 if !selection.is_empty() && !line_mode {
7013 selection.goal = SelectionGoal::None;
7014 }
7015 let (cursor, goal) = movement::down_by_rows(
7016 map,
7017 selection.start,
7018 action.lines,
7019 selection.goal,
7020 false,
7021 text_layout_details,
7022 );
7023 selection.collapse_to(cursor, goal);
7024 });
7025 })
7026 }
7027
7028 pub fn select_down_by_lines(&mut self, action: &SelectDownByLines, cx: &mut ViewContext<Self>) {
7029 let text_layout_details = &self.text_layout_details(cx);
7030 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7031 s.move_heads_with(|map, head, goal| {
7032 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
7033 })
7034 })
7035 }
7036
7037 pub fn select_up_by_lines(&mut self, action: &SelectUpByLines, cx: &mut ViewContext<Self>) {
7038 let text_layout_details = &self.text_layout_details(cx);
7039 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7040 s.move_heads_with(|map, head, goal| {
7041 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
7042 })
7043 })
7044 }
7045
7046 pub fn select_page_up(&mut self, _: &SelectPageUp, cx: &mut ViewContext<Self>) {
7047 let Some(row_count) = self.visible_row_count() else {
7048 return;
7049 };
7050
7051 let text_layout_details = &self.text_layout_details(cx);
7052
7053 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7054 s.move_heads_with(|map, head, goal| {
7055 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
7056 })
7057 })
7058 }
7059
7060 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
7061 if self.take_rename(true, cx).is_some() {
7062 return;
7063 }
7064
7065 if self
7066 .context_menu
7067 .write()
7068 .as_mut()
7069 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
7070 .unwrap_or(false)
7071 {
7072 return;
7073 }
7074
7075 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7076 cx.propagate();
7077 return;
7078 }
7079
7080 let Some(row_count) = self.visible_row_count() else {
7081 return;
7082 };
7083
7084 let autoscroll = if action.center_cursor {
7085 Autoscroll::center()
7086 } else {
7087 Autoscroll::fit()
7088 };
7089
7090 let text_layout_details = &self.text_layout_details(cx);
7091
7092 self.change_selections(Some(autoscroll), cx, |s| {
7093 let line_mode = s.line_mode;
7094 s.move_with(|map, selection| {
7095 if !selection.is_empty() && !line_mode {
7096 selection.goal = SelectionGoal::None;
7097 }
7098 let (cursor, goal) = movement::up_by_rows(
7099 map,
7100 selection.end,
7101 row_count,
7102 selection.goal,
7103 false,
7104 text_layout_details,
7105 );
7106 selection.collapse_to(cursor, goal);
7107 });
7108 });
7109 }
7110
7111 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
7112 let text_layout_details = &self.text_layout_details(cx);
7113 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7114 s.move_heads_with(|map, head, goal| {
7115 movement::up(map, head, goal, false, text_layout_details)
7116 })
7117 })
7118 }
7119
7120 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
7121 self.take_rename(true, cx);
7122
7123 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7124 cx.propagate();
7125 return;
7126 }
7127
7128 let text_layout_details = &self.text_layout_details(cx);
7129 let selection_count = self.selections.count();
7130 let first_selection = self.selections.first_anchor();
7131
7132 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7133 let line_mode = s.line_mode;
7134 s.move_with(|map, selection| {
7135 if !selection.is_empty() && !line_mode {
7136 selection.goal = SelectionGoal::None;
7137 }
7138 let (cursor, goal) = movement::down(
7139 map,
7140 selection.end,
7141 selection.goal,
7142 false,
7143 text_layout_details,
7144 );
7145 selection.collapse_to(cursor, goal);
7146 });
7147 });
7148
7149 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7150 {
7151 cx.propagate();
7152 }
7153 }
7154
7155 pub fn select_page_down(&mut self, _: &SelectPageDown, cx: &mut ViewContext<Self>) {
7156 let Some(row_count) = self.visible_row_count() else {
7157 return;
7158 };
7159
7160 let text_layout_details = &self.text_layout_details(cx);
7161
7162 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7163 s.move_heads_with(|map, head, goal| {
7164 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
7165 })
7166 })
7167 }
7168
7169 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
7170 if self.take_rename(true, cx).is_some() {
7171 return;
7172 }
7173
7174 if self
7175 .context_menu
7176 .write()
7177 .as_mut()
7178 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
7179 .unwrap_or(false)
7180 {
7181 return;
7182 }
7183
7184 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7185 cx.propagate();
7186 return;
7187 }
7188
7189 let Some(row_count) = self.visible_row_count() else {
7190 return;
7191 };
7192
7193 let autoscroll = if action.center_cursor {
7194 Autoscroll::center()
7195 } else {
7196 Autoscroll::fit()
7197 };
7198
7199 let text_layout_details = &self.text_layout_details(cx);
7200 self.change_selections(Some(autoscroll), cx, |s| {
7201 let line_mode = s.line_mode;
7202 s.move_with(|map, selection| {
7203 if !selection.is_empty() && !line_mode {
7204 selection.goal = SelectionGoal::None;
7205 }
7206 let (cursor, goal) = movement::down_by_rows(
7207 map,
7208 selection.end,
7209 row_count,
7210 selection.goal,
7211 false,
7212 text_layout_details,
7213 );
7214 selection.collapse_to(cursor, goal);
7215 });
7216 });
7217 }
7218
7219 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
7220 let text_layout_details = &self.text_layout_details(cx);
7221 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7222 s.move_heads_with(|map, head, goal| {
7223 movement::down(map, head, goal, false, text_layout_details)
7224 })
7225 });
7226 }
7227
7228 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
7229 if let Some(context_menu) = self.context_menu.write().as_mut() {
7230 context_menu.select_first(self.completion_provider.as_deref(), cx);
7231 }
7232 }
7233
7234 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
7235 if let Some(context_menu) = self.context_menu.write().as_mut() {
7236 context_menu.select_prev(self.completion_provider.as_deref(), cx);
7237 }
7238 }
7239
7240 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
7241 if let Some(context_menu) = self.context_menu.write().as_mut() {
7242 context_menu.select_next(self.completion_provider.as_deref(), cx);
7243 }
7244 }
7245
7246 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
7247 if let Some(context_menu) = self.context_menu.write().as_mut() {
7248 context_menu.select_last(self.completion_provider.as_deref(), cx);
7249 }
7250 }
7251
7252 pub fn move_to_previous_word_start(
7253 &mut self,
7254 _: &MoveToPreviousWordStart,
7255 cx: &mut ViewContext<Self>,
7256 ) {
7257 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7258 s.move_cursors_with(|map, head, _| {
7259 (
7260 movement::previous_word_start(map, head),
7261 SelectionGoal::None,
7262 )
7263 });
7264 })
7265 }
7266
7267 pub fn move_to_previous_subword_start(
7268 &mut self,
7269 _: &MoveToPreviousSubwordStart,
7270 cx: &mut ViewContext<Self>,
7271 ) {
7272 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7273 s.move_cursors_with(|map, head, _| {
7274 (
7275 movement::previous_subword_start(map, head),
7276 SelectionGoal::None,
7277 )
7278 });
7279 })
7280 }
7281
7282 pub fn select_to_previous_word_start(
7283 &mut self,
7284 _: &SelectToPreviousWordStart,
7285 cx: &mut ViewContext<Self>,
7286 ) {
7287 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7288 s.move_heads_with(|map, head, _| {
7289 (
7290 movement::previous_word_start(map, head),
7291 SelectionGoal::None,
7292 )
7293 });
7294 })
7295 }
7296
7297 pub fn select_to_previous_subword_start(
7298 &mut self,
7299 _: &SelectToPreviousSubwordStart,
7300 cx: &mut ViewContext<Self>,
7301 ) {
7302 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7303 s.move_heads_with(|map, head, _| {
7304 (
7305 movement::previous_subword_start(map, head),
7306 SelectionGoal::None,
7307 )
7308 });
7309 })
7310 }
7311
7312 pub fn delete_to_previous_word_start(
7313 &mut self,
7314 action: &DeleteToPreviousWordStart,
7315 cx: &mut ViewContext<Self>,
7316 ) {
7317 self.transact(cx, |this, cx| {
7318 this.select_autoclose_pair(cx);
7319 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7320 let line_mode = s.line_mode;
7321 s.move_with(|map, selection| {
7322 if selection.is_empty() && !line_mode {
7323 let cursor = if action.ignore_newlines {
7324 movement::previous_word_start(map, selection.head())
7325 } else {
7326 movement::previous_word_start_or_newline(map, selection.head())
7327 };
7328 selection.set_head(cursor, SelectionGoal::None);
7329 }
7330 });
7331 });
7332 this.insert("", cx);
7333 });
7334 }
7335
7336 pub fn delete_to_previous_subword_start(
7337 &mut self,
7338 _: &DeleteToPreviousSubwordStart,
7339 cx: &mut ViewContext<Self>,
7340 ) {
7341 self.transact(cx, |this, cx| {
7342 this.select_autoclose_pair(cx);
7343 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7344 let line_mode = s.line_mode;
7345 s.move_with(|map, selection| {
7346 if selection.is_empty() && !line_mode {
7347 let cursor = movement::previous_subword_start(map, selection.head());
7348 selection.set_head(cursor, SelectionGoal::None);
7349 }
7350 });
7351 });
7352 this.insert("", cx);
7353 });
7354 }
7355
7356 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
7357 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7358 s.move_cursors_with(|map, head, _| {
7359 (movement::next_word_end(map, head), SelectionGoal::None)
7360 });
7361 })
7362 }
7363
7364 pub fn move_to_next_subword_end(
7365 &mut self,
7366 _: &MoveToNextSubwordEnd,
7367 cx: &mut ViewContext<Self>,
7368 ) {
7369 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7370 s.move_cursors_with(|map, head, _| {
7371 (movement::next_subword_end(map, head), SelectionGoal::None)
7372 });
7373 })
7374 }
7375
7376 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
7377 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7378 s.move_heads_with(|map, head, _| {
7379 (movement::next_word_end(map, head), SelectionGoal::None)
7380 });
7381 })
7382 }
7383
7384 pub fn select_to_next_subword_end(
7385 &mut self,
7386 _: &SelectToNextSubwordEnd,
7387 cx: &mut ViewContext<Self>,
7388 ) {
7389 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7390 s.move_heads_with(|map, head, _| {
7391 (movement::next_subword_end(map, head), SelectionGoal::None)
7392 });
7393 })
7394 }
7395
7396 pub fn delete_to_next_word_end(
7397 &mut self,
7398 action: &DeleteToNextWordEnd,
7399 cx: &mut ViewContext<Self>,
7400 ) {
7401 self.transact(cx, |this, cx| {
7402 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7403 let line_mode = s.line_mode;
7404 s.move_with(|map, selection| {
7405 if selection.is_empty() && !line_mode {
7406 let cursor = if action.ignore_newlines {
7407 movement::next_word_end(map, selection.head())
7408 } else {
7409 movement::next_word_end_or_newline(map, selection.head())
7410 };
7411 selection.set_head(cursor, SelectionGoal::None);
7412 }
7413 });
7414 });
7415 this.insert("", cx);
7416 });
7417 }
7418
7419 pub fn delete_to_next_subword_end(
7420 &mut self,
7421 _: &DeleteToNextSubwordEnd,
7422 cx: &mut ViewContext<Self>,
7423 ) {
7424 self.transact(cx, |this, cx| {
7425 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7426 s.move_with(|map, selection| {
7427 if selection.is_empty() {
7428 let cursor = movement::next_subword_end(map, selection.head());
7429 selection.set_head(cursor, SelectionGoal::None);
7430 }
7431 });
7432 });
7433 this.insert("", cx);
7434 });
7435 }
7436
7437 pub fn move_to_beginning_of_line(
7438 &mut self,
7439 action: &MoveToBeginningOfLine,
7440 cx: &mut ViewContext<Self>,
7441 ) {
7442 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7443 s.move_cursors_with(|map, head, _| {
7444 (
7445 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7446 SelectionGoal::None,
7447 )
7448 });
7449 })
7450 }
7451
7452 pub fn select_to_beginning_of_line(
7453 &mut self,
7454 action: &SelectToBeginningOfLine,
7455 cx: &mut ViewContext<Self>,
7456 ) {
7457 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7458 s.move_heads_with(|map, head, _| {
7459 (
7460 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7461 SelectionGoal::None,
7462 )
7463 });
7464 });
7465 }
7466
7467 pub fn delete_to_beginning_of_line(
7468 &mut self,
7469 _: &DeleteToBeginningOfLine,
7470 cx: &mut ViewContext<Self>,
7471 ) {
7472 self.transact(cx, |this, cx| {
7473 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7474 s.move_with(|_, selection| {
7475 selection.reversed = true;
7476 });
7477 });
7478
7479 this.select_to_beginning_of_line(
7480 &SelectToBeginningOfLine {
7481 stop_at_soft_wraps: false,
7482 },
7483 cx,
7484 );
7485 this.backspace(&Backspace, cx);
7486 });
7487 }
7488
7489 pub fn move_to_end_of_line(&mut self, action: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
7490 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7491 s.move_cursors_with(|map, head, _| {
7492 (
7493 movement::line_end(map, head, action.stop_at_soft_wraps),
7494 SelectionGoal::None,
7495 )
7496 });
7497 })
7498 }
7499
7500 pub fn select_to_end_of_line(
7501 &mut self,
7502 action: &SelectToEndOfLine,
7503 cx: &mut ViewContext<Self>,
7504 ) {
7505 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7506 s.move_heads_with(|map, head, _| {
7507 (
7508 movement::line_end(map, head, action.stop_at_soft_wraps),
7509 SelectionGoal::None,
7510 )
7511 });
7512 })
7513 }
7514
7515 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
7516 self.transact(cx, |this, cx| {
7517 this.select_to_end_of_line(
7518 &SelectToEndOfLine {
7519 stop_at_soft_wraps: false,
7520 },
7521 cx,
7522 );
7523 this.delete(&Delete, cx);
7524 });
7525 }
7526
7527 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
7528 self.transact(cx, |this, cx| {
7529 this.select_to_end_of_line(
7530 &SelectToEndOfLine {
7531 stop_at_soft_wraps: false,
7532 },
7533 cx,
7534 );
7535 this.cut(&Cut, cx);
7536 });
7537 }
7538
7539 pub fn move_to_start_of_paragraph(
7540 &mut self,
7541 _: &MoveToStartOfParagraph,
7542 cx: &mut ViewContext<Self>,
7543 ) {
7544 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7545 cx.propagate();
7546 return;
7547 }
7548
7549 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7550 s.move_with(|map, selection| {
7551 selection.collapse_to(
7552 movement::start_of_paragraph(map, selection.head(), 1),
7553 SelectionGoal::None,
7554 )
7555 });
7556 })
7557 }
7558
7559 pub fn move_to_end_of_paragraph(
7560 &mut self,
7561 _: &MoveToEndOfParagraph,
7562 cx: &mut ViewContext<Self>,
7563 ) {
7564 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7565 cx.propagate();
7566 return;
7567 }
7568
7569 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7570 s.move_with(|map, selection| {
7571 selection.collapse_to(
7572 movement::end_of_paragraph(map, selection.head(), 1),
7573 SelectionGoal::None,
7574 )
7575 });
7576 })
7577 }
7578
7579 pub fn select_to_start_of_paragraph(
7580 &mut self,
7581 _: &SelectToStartOfParagraph,
7582 cx: &mut ViewContext<Self>,
7583 ) {
7584 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7585 cx.propagate();
7586 return;
7587 }
7588
7589 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7590 s.move_heads_with(|map, head, _| {
7591 (
7592 movement::start_of_paragraph(map, head, 1),
7593 SelectionGoal::None,
7594 )
7595 });
7596 })
7597 }
7598
7599 pub fn select_to_end_of_paragraph(
7600 &mut self,
7601 _: &SelectToEndOfParagraph,
7602 cx: &mut ViewContext<Self>,
7603 ) {
7604 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7605 cx.propagate();
7606 return;
7607 }
7608
7609 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7610 s.move_heads_with(|map, head, _| {
7611 (
7612 movement::end_of_paragraph(map, head, 1),
7613 SelectionGoal::None,
7614 )
7615 });
7616 })
7617 }
7618
7619 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
7620 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7621 cx.propagate();
7622 return;
7623 }
7624
7625 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7626 s.select_ranges(vec![0..0]);
7627 });
7628 }
7629
7630 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
7631 let mut selection = self.selections.last::<Point>(cx);
7632 selection.set_head(Point::zero(), SelectionGoal::None);
7633
7634 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7635 s.select(vec![selection]);
7636 });
7637 }
7638
7639 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
7640 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7641 cx.propagate();
7642 return;
7643 }
7644
7645 let cursor = self.buffer.read(cx).read(cx).len();
7646 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7647 s.select_ranges(vec![cursor..cursor])
7648 });
7649 }
7650
7651 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
7652 self.nav_history = nav_history;
7653 }
7654
7655 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
7656 self.nav_history.as_ref()
7657 }
7658
7659 fn push_to_nav_history(
7660 &mut self,
7661 cursor_anchor: Anchor,
7662 new_position: Option<Point>,
7663 cx: &mut ViewContext<Self>,
7664 ) {
7665 if let Some(nav_history) = self.nav_history.as_mut() {
7666 let buffer = self.buffer.read(cx).read(cx);
7667 let cursor_position = cursor_anchor.to_point(&buffer);
7668 let scroll_state = self.scroll_manager.anchor();
7669 let scroll_top_row = scroll_state.top_row(&buffer);
7670 drop(buffer);
7671
7672 if let Some(new_position) = new_position {
7673 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
7674 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
7675 return;
7676 }
7677 }
7678
7679 nav_history.push(
7680 Some(NavigationData {
7681 cursor_anchor,
7682 cursor_position,
7683 scroll_anchor: scroll_state,
7684 scroll_top_row,
7685 }),
7686 cx,
7687 );
7688 }
7689 }
7690
7691 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
7692 let buffer = self.buffer.read(cx).snapshot(cx);
7693 let mut selection = self.selections.first::<usize>(cx);
7694 selection.set_head(buffer.len(), SelectionGoal::None);
7695 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7696 s.select(vec![selection]);
7697 });
7698 }
7699
7700 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
7701 let end = self.buffer.read(cx).read(cx).len();
7702 self.change_selections(None, cx, |s| {
7703 s.select_ranges(vec![0..end]);
7704 });
7705 }
7706
7707 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
7708 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7709 let mut selections = self.selections.all::<Point>(cx);
7710 let max_point = display_map.buffer_snapshot.max_point();
7711 for selection in &mut selections {
7712 let rows = selection.spanned_rows(true, &display_map);
7713 selection.start = Point::new(rows.start.0, 0);
7714 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
7715 selection.reversed = false;
7716 }
7717 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7718 s.select(selections);
7719 });
7720 }
7721
7722 pub fn split_selection_into_lines(
7723 &mut self,
7724 _: &SplitSelectionIntoLines,
7725 cx: &mut ViewContext<Self>,
7726 ) {
7727 let mut to_unfold = Vec::new();
7728 let mut new_selection_ranges = Vec::new();
7729 {
7730 let selections = self.selections.all::<Point>(cx);
7731 let buffer = self.buffer.read(cx).read(cx);
7732 for selection in selections {
7733 for row in selection.start.row..selection.end.row {
7734 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
7735 new_selection_ranges.push(cursor..cursor);
7736 }
7737 new_selection_ranges.push(selection.end..selection.end);
7738 to_unfold.push(selection.start..selection.end);
7739 }
7740 }
7741 self.unfold_ranges(&to_unfold, true, true, cx);
7742 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7743 s.select_ranges(new_selection_ranges);
7744 });
7745 }
7746
7747 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
7748 self.add_selection(true, cx);
7749 }
7750
7751 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
7752 self.add_selection(false, cx);
7753 }
7754
7755 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
7756 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7757 let mut selections = self.selections.all::<Point>(cx);
7758 let text_layout_details = self.text_layout_details(cx);
7759 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
7760 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
7761 let range = oldest_selection.display_range(&display_map).sorted();
7762
7763 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
7764 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
7765 let positions = start_x.min(end_x)..start_x.max(end_x);
7766
7767 selections.clear();
7768 let mut stack = Vec::new();
7769 for row in range.start.row().0..=range.end.row().0 {
7770 if let Some(selection) = self.selections.build_columnar_selection(
7771 &display_map,
7772 DisplayRow(row),
7773 &positions,
7774 oldest_selection.reversed,
7775 &text_layout_details,
7776 ) {
7777 stack.push(selection.id);
7778 selections.push(selection);
7779 }
7780 }
7781
7782 if above {
7783 stack.reverse();
7784 }
7785
7786 AddSelectionsState { above, stack }
7787 });
7788
7789 let last_added_selection = *state.stack.last().unwrap();
7790 let mut new_selections = Vec::new();
7791 if above == state.above {
7792 let end_row = if above {
7793 DisplayRow(0)
7794 } else {
7795 display_map.max_point().row()
7796 };
7797
7798 'outer: for selection in selections {
7799 if selection.id == last_added_selection {
7800 let range = selection.display_range(&display_map).sorted();
7801 debug_assert_eq!(range.start.row(), range.end.row());
7802 let mut row = range.start.row();
7803 let positions =
7804 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
7805 px(start)..px(end)
7806 } else {
7807 let start_x =
7808 display_map.x_for_display_point(range.start, &text_layout_details);
7809 let end_x =
7810 display_map.x_for_display_point(range.end, &text_layout_details);
7811 start_x.min(end_x)..start_x.max(end_x)
7812 };
7813
7814 while row != end_row {
7815 if above {
7816 row.0 -= 1;
7817 } else {
7818 row.0 += 1;
7819 }
7820
7821 if let Some(new_selection) = self.selections.build_columnar_selection(
7822 &display_map,
7823 row,
7824 &positions,
7825 selection.reversed,
7826 &text_layout_details,
7827 ) {
7828 state.stack.push(new_selection.id);
7829 if above {
7830 new_selections.push(new_selection);
7831 new_selections.push(selection);
7832 } else {
7833 new_selections.push(selection);
7834 new_selections.push(new_selection);
7835 }
7836
7837 continue 'outer;
7838 }
7839 }
7840 }
7841
7842 new_selections.push(selection);
7843 }
7844 } else {
7845 new_selections = selections;
7846 new_selections.retain(|s| s.id != last_added_selection);
7847 state.stack.pop();
7848 }
7849
7850 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7851 s.select(new_selections);
7852 });
7853 if state.stack.len() > 1 {
7854 self.add_selections_state = Some(state);
7855 }
7856 }
7857
7858 pub fn select_next_match_internal(
7859 &mut self,
7860 display_map: &DisplaySnapshot,
7861 replace_newest: bool,
7862 autoscroll: Option<Autoscroll>,
7863 cx: &mut ViewContext<Self>,
7864 ) -> Result<()> {
7865 fn select_next_match_ranges(
7866 this: &mut Editor,
7867 range: Range<usize>,
7868 replace_newest: bool,
7869 auto_scroll: Option<Autoscroll>,
7870 cx: &mut ViewContext<Editor>,
7871 ) {
7872 this.unfold_ranges(&[range.clone()], false, true, cx);
7873 this.change_selections(auto_scroll, cx, |s| {
7874 if replace_newest {
7875 s.delete(s.newest_anchor().id);
7876 }
7877 s.insert_range(range.clone());
7878 });
7879 }
7880
7881 let buffer = &display_map.buffer_snapshot;
7882 let mut selections = self.selections.all::<usize>(cx);
7883 if let Some(mut select_next_state) = self.select_next_state.take() {
7884 let query = &select_next_state.query;
7885 if !select_next_state.done {
7886 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
7887 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
7888 let mut next_selected_range = None;
7889
7890 let bytes_after_last_selection =
7891 buffer.bytes_in_range(last_selection.end..buffer.len());
7892 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
7893 let query_matches = query
7894 .stream_find_iter(bytes_after_last_selection)
7895 .map(|result| (last_selection.end, result))
7896 .chain(
7897 query
7898 .stream_find_iter(bytes_before_first_selection)
7899 .map(|result| (0, result)),
7900 );
7901
7902 for (start_offset, query_match) in query_matches {
7903 let query_match = query_match.unwrap(); // can only fail due to I/O
7904 let offset_range =
7905 start_offset + query_match.start()..start_offset + query_match.end();
7906 let display_range = offset_range.start.to_display_point(display_map)
7907 ..offset_range.end.to_display_point(display_map);
7908
7909 if !select_next_state.wordwise
7910 || (!movement::is_inside_word(display_map, display_range.start)
7911 && !movement::is_inside_word(display_map, display_range.end))
7912 {
7913 // TODO: This is n^2, because we might check all the selections
7914 if !selections
7915 .iter()
7916 .any(|selection| selection.range().overlaps(&offset_range))
7917 {
7918 next_selected_range = Some(offset_range);
7919 break;
7920 }
7921 }
7922 }
7923
7924 if let Some(next_selected_range) = next_selected_range {
7925 select_next_match_ranges(
7926 self,
7927 next_selected_range,
7928 replace_newest,
7929 autoscroll,
7930 cx,
7931 );
7932 } else {
7933 select_next_state.done = true;
7934 }
7935 }
7936
7937 self.select_next_state = Some(select_next_state);
7938 } else {
7939 let mut only_carets = true;
7940 let mut same_text_selected = true;
7941 let mut selected_text = None;
7942
7943 let mut selections_iter = selections.iter().peekable();
7944 while let Some(selection) = selections_iter.next() {
7945 if selection.start != selection.end {
7946 only_carets = false;
7947 }
7948
7949 if same_text_selected {
7950 if selected_text.is_none() {
7951 selected_text =
7952 Some(buffer.text_for_range(selection.range()).collect::<String>());
7953 }
7954
7955 if let Some(next_selection) = selections_iter.peek() {
7956 if next_selection.range().len() == selection.range().len() {
7957 let next_selected_text = buffer
7958 .text_for_range(next_selection.range())
7959 .collect::<String>();
7960 if Some(next_selected_text) != selected_text {
7961 same_text_selected = false;
7962 selected_text = None;
7963 }
7964 } else {
7965 same_text_selected = false;
7966 selected_text = None;
7967 }
7968 }
7969 }
7970 }
7971
7972 if only_carets {
7973 for selection in &mut selections {
7974 let word_range = movement::surrounding_word(
7975 display_map,
7976 selection.start.to_display_point(display_map),
7977 );
7978 selection.start = word_range.start.to_offset(display_map, Bias::Left);
7979 selection.end = word_range.end.to_offset(display_map, Bias::Left);
7980 selection.goal = SelectionGoal::None;
7981 selection.reversed = false;
7982 select_next_match_ranges(
7983 self,
7984 selection.start..selection.end,
7985 replace_newest,
7986 autoscroll,
7987 cx,
7988 );
7989 }
7990
7991 if selections.len() == 1 {
7992 let selection = selections
7993 .last()
7994 .expect("ensured that there's only one selection");
7995 let query = buffer
7996 .text_for_range(selection.start..selection.end)
7997 .collect::<String>();
7998 let is_empty = query.is_empty();
7999 let select_state = SelectNextState {
8000 query: AhoCorasick::new(&[query])?,
8001 wordwise: true,
8002 done: is_empty,
8003 };
8004 self.select_next_state = Some(select_state);
8005 } else {
8006 self.select_next_state = None;
8007 }
8008 } else if let Some(selected_text) = selected_text {
8009 self.select_next_state = Some(SelectNextState {
8010 query: AhoCorasick::new(&[selected_text])?,
8011 wordwise: false,
8012 done: false,
8013 });
8014 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
8015 }
8016 }
8017 Ok(())
8018 }
8019
8020 pub fn select_all_matches(
8021 &mut self,
8022 _action: &SelectAllMatches,
8023 cx: &mut ViewContext<Self>,
8024 ) -> Result<()> {
8025 self.push_to_selection_history();
8026 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8027
8028 self.select_next_match_internal(&display_map, false, None, cx)?;
8029 let Some(select_next_state) = self.select_next_state.as_mut() else {
8030 return Ok(());
8031 };
8032 if select_next_state.done {
8033 return Ok(());
8034 }
8035
8036 let mut new_selections = self.selections.all::<usize>(cx);
8037
8038 let buffer = &display_map.buffer_snapshot;
8039 let query_matches = select_next_state
8040 .query
8041 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
8042
8043 for query_match in query_matches {
8044 let query_match = query_match.unwrap(); // can only fail due to I/O
8045 let offset_range = query_match.start()..query_match.end();
8046 let display_range = offset_range.start.to_display_point(&display_map)
8047 ..offset_range.end.to_display_point(&display_map);
8048
8049 if !select_next_state.wordwise
8050 || (!movement::is_inside_word(&display_map, display_range.start)
8051 && !movement::is_inside_word(&display_map, display_range.end))
8052 {
8053 self.selections.change_with(cx, |selections| {
8054 new_selections.push(Selection {
8055 id: selections.new_selection_id(),
8056 start: offset_range.start,
8057 end: offset_range.end,
8058 reversed: false,
8059 goal: SelectionGoal::None,
8060 });
8061 });
8062 }
8063 }
8064
8065 new_selections.sort_by_key(|selection| selection.start);
8066 let mut ix = 0;
8067 while ix + 1 < new_selections.len() {
8068 let current_selection = &new_selections[ix];
8069 let next_selection = &new_selections[ix + 1];
8070 if current_selection.range().overlaps(&next_selection.range()) {
8071 if current_selection.id < next_selection.id {
8072 new_selections.remove(ix + 1);
8073 } else {
8074 new_selections.remove(ix);
8075 }
8076 } else {
8077 ix += 1;
8078 }
8079 }
8080
8081 select_next_state.done = true;
8082 self.unfold_ranges(
8083 &new_selections
8084 .iter()
8085 .map(|selection| selection.range())
8086 .collect::<Vec<_>>(),
8087 false,
8088 false,
8089 cx,
8090 );
8091 self.change_selections(Some(Autoscroll::fit()), cx, |selections| {
8092 selections.select(new_selections)
8093 });
8094
8095 Ok(())
8096 }
8097
8098 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
8099 self.push_to_selection_history();
8100 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8101 self.select_next_match_internal(
8102 &display_map,
8103 action.replace_newest,
8104 Some(Autoscroll::newest()),
8105 cx,
8106 )?;
8107 Ok(())
8108 }
8109
8110 pub fn select_previous(
8111 &mut self,
8112 action: &SelectPrevious,
8113 cx: &mut ViewContext<Self>,
8114 ) -> Result<()> {
8115 self.push_to_selection_history();
8116 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8117 let buffer = &display_map.buffer_snapshot;
8118 let mut selections = self.selections.all::<usize>(cx);
8119 if let Some(mut select_prev_state) = self.select_prev_state.take() {
8120 let query = &select_prev_state.query;
8121 if !select_prev_state.done {
8122 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
8123 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
8124 let mut next_selected_range = None;
8125 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
8126 let bytes_before_last_selection =
8127 buffer.reversed_bytes_in_range(0..last_selection.start);
8128 let bytes_after_first_selection =
8129 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
8130 let query_matches = query
8131 .stream_find_iter(bytes_before_last_selection)
8132 .map(|result| (last_selection.start, result))
8133 .chain(
8134 query
8135 .stream_find_iter(bytes_after_first_selection)
8136 .map(|result| (buffer.len(), result)),
8137 );
8138 for (end_offset, query_match) in query_matches {
8139 let query_match = query_match.unwrap(); // can only fail due to I/O
8140 let offset_range =
8141 end_offset - query_match.end()..end_offset - query_match.start();
8142 let display_range = offset_range.start.to_display_point(&display_map)
8143 ..offset_range.end.to_display_point(&display_map);
8144
8145 if !select_prev_state.wordwise
8146 || (!movement::is_inside_word(&display_map, display_range.start)
8147 && !movement::is_inside_word(&display_map, display_range.end))
8148 {
8149 next_selected_range = Some(offset_range);
8150 break;
8151 }
8152 }
8153
8154 if let Some(next_selected_range) = next_selected_range {
8155 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
8156 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8157 if action.replace_newest {
8158 s.delete(s.newest_anchor().id);
8159 }
8160 s.insert_range(next_selected_range);
8161 });
8162 } else {
8163 select_prev_state.done = true;
8164 }
8165 }
8166
8167 self.select_prev_state = Some(select_prev_state);
8168 } else {
8169 let mut only_carets = true;
8170 let mut same_text_selected = true;
8171 let mut selected_text = None;
8172
8173 let mut selections_iter = selections.iter().peekable();
8174 while let Some(selection) = selections_iter.next() {
8175 if selection.start != selection.end {
8176 only_carets = false;
8177 }
8178
8179 if same_text_selected {
8180 if selected_text.is_none() {
8181 selected_text =
8182 Some(buffer.text_for_range(selection.range()).collect::<String>());
8183 }
8184
8185 if let Some(next_selection) = selections_iter.peek() {
8186 if next_selection.range().len() == selection.range().len() {
8187 let next_selected_text = buffer
8188 .text_for_range(next_selection.range())
8189 .collect::<String>();
8190 if Some(next_selected_text) != selected_text {
8191 same_text_selected = false;
8192 selected_text = None;
8193 }
8194 } else {
8195 same_text_selected = false;
8196 selected_text = None;
8197 }
8198 }
8199 }
8200 }
8201
8202 if only_carets {
8203 for selection in &mut selections {
8204 let word_range = movement::surrounding_word(
8205 &display_map,
8206 selection.start.to_display_point(&display_map),
8207 );
8208 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
8209 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
8210 selection.goal = SelectionGoal::None;
8211 selection.reversed = false;
8212 }
8213 if selections.len() == 1 {
8214 let selection = selections
8215 .last()
8216 .expect("ensured that there's only one selection");
8217 let query = buffer
8218 .text_for_range(selection.start..selection.end)
8219 .collect::<String>();
8220 let is_empty = query.is_empty();
8221 let select_state = SelectNextState {
8222 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
8223 wordwise: true,
8224 done: is_empty,
8225 };
8226 self.select_prev_state = Some(select_state);
8227 } else {
8228 self.select_prev_state = None;
8229 }
8230
8231 self.unfold_ranges(
8232 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
8233 false,
8234 true,
8235 cx,
8236 );
8237 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8238 s.select(selections);
8239 });
8240 } else if let Some(selected_text) = selected_text {
8241 self.select_prev_state = Some(SelectNextState {
8242 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
8243 wordwise: false,
8244 done: false,
8245 });
8246 self.select_previous(action, cx)?;
8247 }
8248 }
8249 Ok(())
8250 }
8251
8252 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
8253 if self.read_only(cx) {
8254 return;
8255 }
8256 let text_layout_details = &self.text_layout_details(cx);
8257 self.transact(cx, |this, cx| {
8258 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
8259 let mut edits = Vec::new();
8260 let mut selection_edit_ranges = Vec::new();
8261 let mut last_toggled_row = None;
8262 let snapshot = this.buffer.read(cx).read(cx);
8263 let empty_str: Arc<str> = Arc::default();
8264 let mut suffixes_inserted = Vec::new();
8265 let ignore_indent = action.ignore_indent;
8266
8267 fn comment_prefix_range(
8268 snapshot: &MultiBufferSnapshot,
8269 row: MultiBufferRow,
8270 comment_prefix: &str,
8271 comment_prefix_whitespace: &str,
8272 ignore_indent: bool,
8273 ) -> Range<Point> {
8274 let indent_size = if ignore_indent {
8275 0
8276 } else {
8277 snapshot.indent_size_for_line(row).len
8278 };
8279
8280 let start = Point::new(row.0, indent_size);
8281
8282 let mut line_bytes = snapshot
8283 .bytes_in_range(start..snapshot.max_point())
8284 .flatten()
8285 .copied();
8286
8287 // If this line currently begins with the line comment prefix, then record
8288 // the range containing the prefix.
8289 if line_bytes
8290 .by_ref()
8291 .take(comment_prefix.len())
8292 .eq(comment_prefix.bytes())
8293 {
8294 // Include any whitespace that matches the comment prefix.
8295 let matching_whitespace_len = line_bytes
8296 .zip(comment_prefix_whitespace.bytes())
8297 .take_while(|(a, b)| a == b)
8298 .count() as u32;
8299 let end = Point::new(
8300 start.row,
8301 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
8302 );
8303 start..end
8304 } else {
8305 start..start
8306 }
8307 }
8308
8309 fn comment_suffix_range(
8310 snapshot: &MultiBufferSnapshot,
8311 row: MultiBufferRow,
8312 comment_suffix: &str,
8313 comment_suffix_has_leading_space: bool,
8314 ) -> Range<Point> {
8315 let end = Point::new(row.0, snapshot.line_len(row));
8316 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
8317
8318 let mut line_end_bytes = snapshot
8319 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
8320 .flatten()
8321 .copied();
8322
8323 let leading_space_len = if suffix_start_column > 0
8324 && line_end_bytes.next() == Some(b' ')
8325 && comment_suffix_has_leading_space
8326 {
8327 1
8328 } else {
8329 0
8330 };
8331
8332 // If this line currently begins with the line comment prefix, then record
8333 // the range containing the prefix.
8334 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
8335 let start = Point::new(end.row, suffix_start_column - leading_space_len);
8336 start..end
8337 } else {
8338 end..end
8339 }
8340 }
8341
8342 // TODO: Handle selections that cross excerpts
8343 for selection in &mut selections {
8344 let start_column = snapshot
8345 .indent_size_for_line(MultiBufferRow(selection.start.row))
8346 .len;
8347 let language = if let Some(language) =
8348 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
8349 {
8350 language
8351 } else {
8352 continue;
8353 };
8354
8355 selection_edit_ranges.clear();
8356
8357 // If multiple selections contain a given row, avoid processing that
8358 // row more than once.
8359 let mut start_row = MultiBufferRow(selection.start.row);
8360 if last_toggled_row == Some(start_row) {
8361 start_row = start_row.next_row();
8362 }
8363 let end_row =
8364 if selection.end.row > selection.start.row && selection.end.column == 0 {
8365 MultiBufferRow(selection.end.row - 1)
8366 } else {
8367 MultiBufferRow(selection.end.row)
8368 };
8369 last_toggled_row = Some(end_row);
8370
8371 if start_row > end_row {
8372 continue;
8373 }
8374
8375 // If the language has line comments, toggle those.
8376 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
8377
8378 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
8379 if ignore_indent {
8380 full_comment_prefixes = full_comment_prefixes
8381 .into_iter()
8382 .map(|s| Arc::from(s.trim_end()))
8383 .collect();
8384 }
8385
8386 if !full_comment_prefixes.is_empty() {
8387 let first_prefix = full_comment_prefixes
8388 .first()
8389 .expect("prefixes is non-empty");
8390 let prefix_trimmed_lengths = full_comment_prefixes
8391 .iter()
8392 .map(|p| p.trim_end_matches(' ').len())
8393 .collect::<SmallVec<[usize; 4]>>();
8394
8395 let mut all_selection_lines_are_comments = true;
8396
8397 for row in start_row.0..=end_row.0 {
8398 let row = MultiBufferRow(row);
8399 if start_row < end_row && snapshot.is_line_blank(row) {
8400 continue;
8401 }
8402
8403 let prefix_range = full_comment_prefixes
8404 .iter()
8405 .zip(prefix_trimmed_lengths.iter().copied())
8406 .map(|(prefix, trimmed_prefix_len)| {
8407 comment_prefix_range(
8408 snapshot.deref(),
8409 row,
8410 &prefix[..trimmed_prefix_len],
8411 &prefix[trimmed_prefix_len..],
8412 ignore_indent,
8413 )
8414 })
8415 .max_by_key(|range| range.end.column - range.start.column)
8416 .expect("prefixes is non-empty");
8417
8418 if prefix_range.is_empty() {
8419 all_selection_lines_are_comments = false;
8420 }
8421
8422 selection_edit_ranges.push(prefix_range);
8423 }
8424
8425 if all_selection_lines_are_comments {
8426 edits.extend(
8427 selection_edit_ranges
8428 .iter()
8429 .cloned()
8430 .map(|range| (range, empty_str.clone())),
8431 );
8432 } else {
8433 let min_column = selection_edit_ranges
8434 .iter()
8435 .map(|range| range.start.column)
8436 .min()
8437 .unwrap_or(0);
8438 edits.extend(selection_edit_ranges.iter().map(|range| {
8439 let position = Point::new(range.start.row, min_column);
8440 (position..position, first_prefix.clone())
8441 }));
8442 }
8443 } else if let Some((full_comment_prefix, comment_suffix)) =
8444 language.block_comment_delimiters()
8445 {
8446 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
8447 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
8448 let prefix_range = comment_prefix_range(
8449 snapshot.deref(),
8450 start_row,
8451 comment_prefix,
8452 comment_prefix_whitespace,
8453 ignore_indent,
8454 );
8455 let suffix_range = comment_suffix_range(
8456 snapshot.deref(),
8457 end_row,
8458 comment_suffix.trim_start_matches(' '),
8459 comment_suffix.starts_with(' '),
8460 );
8461
8462 if prefix_range.is_empty() || suffix_range.is_empty() {
8463 edits.push((
8464 prefix_range.start..prefix_range.start,
8465 full_comment_prefix.clone(),
8466 ));
8467 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
8468 suffixes_inserted.push((end_row, comment_suffix.len()));
8469 } else {
8470 edits.push((prefix_range, empty_str.clone()));
8471 edits.push((suffix_range, empty_str.clone()));
8472 }
8473 } else {
8474 continue;
8475 }
8476 }
8477
8478 drop(snapshot);
8479 this.buffer.update(cx, |buffer, cx| {
8480 buffer.edit(edits, None, cx);
8481 });
8482
8483 // Adjust selections so that they end before any comment suffixes that
8484 // were inserted.
8485 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
8486 let mut selections = this.selections.all::<Point>(cx);
8487 let snapshot = this.buffer.read(cx).read(cx);
8488 for selection in &mut selections {
8489 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
8490 match row.cmp(&MultiBufferRow(selection.end.row)) {
8491 Ordering::Less => {
8492 suffixes_inserted.next();
8493 continue;
8494 }
8495 Ordering::Greater => break,
8496 Ordering::Equal => {
8497 if selection.end.column == snapshot.line_len(row) {
8498 if selection.is_empty() {
8499 selection.start.column -= suffix_len as u32;
8500 }
8501 selection.end.column -= suffix_len as u32;
8502 }
8503 break;
8504 }
8505 }
8506 }
8507 }
8508
8509 drop(snapshot);
8510 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
8511
8512 let selections = this.selections.all::<Point>(cx);
8513 let selections_on_single_row = selections.windows(2).all(|selections| {
8514 selections[0].start.row == selections[1].start.row
8515 && selections[0].end.row == selections[1].end.row
8516 && selections[0].start.row == selections[0].end.row
8517 });
8518 let selections_selecting = selections
8519 .iter()
8520 .any(|selection| selection.start != selection.end);
8521 let advance_downwards = action.advance_downwards
8522 && selections_on_single_row
8523 && !selections_selecting
8524 && !matches!(this.mode, EditorMode::SingleLine { .. });
8525
8526 if advance_downwards {
8527 let snapshot = this.buffer.read(cx).snapshot(cx);
8528
8529 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8530 s.move_cursors_with(|display_snapshot, display_point, _| {
8531 let mut point = display_point.to_point(display_snapshot);
8532 point.row += 1;
8533 point = snapshot.clip_point(point, Bias::Left);
8534 let display_point = point.to_display_point(display_snapshot);
8535 let goal = SelectionGoal::HorizontalPosition(
8536 display_snapshot
8537 .x_for_display_point(display_point, text_layout_details)
8538 .into(),
8539 );
8540 (display_point, goal)
8541 })
8542 });
8543 }
8544 });
8545 }
8546
8547 pub fn select_enclosing_symbol(
8548 &mut self,
8549 _: &SelectEnclosingSymbol,
8550 cx: &mut ViewContext<Self>,
8551 ) {
8552 let buffer = self.buffer.read(cx).snapshot(cx);
8553 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8554
8555 fn update_selection(
8556 selection: &Selection<usize>,
8557 buffer_snap: &MultiBufferSnapshot,
8558 ) -> Option<Selection<usize>> {
8559 let cursor = selection.head();
8560 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
8561 for symbol in symbols.iter().rev() {
8562 let start = symbol.range.start.to_offset(buffer_snap);
8563 let end = symbol.range.end.to_offset(buffer_snap);
8564 let new_range = start..end;
8565 if start < selection.start || end > selection.end {
8566 return Some(Selection {
8567 id: selection.id,
8568 start: new_range.start,
8569 end: new_range.end,
8570 goal: SelectionGoal::None,
8571 reversed: selection.reversed,
8572 });
8573 }
8574 }
8575 None
8576 }
8577
8578 let mut selected_larger_symbol = false;
8579 let new_selections = old_selections
8580 .iter()
8581 .map(|selection| match update_selection(selection, &buffer) {
8582 Some(new_selection) => {
8583 if new_selection.range() != selection.range() {
8584 selected_larger_symbol = true;
8585 }
8586 new_selection
8587 }
8588 None => selection.clone(),
8589 })
8590 .collect::<Vec<_>>();
8591
8592 if selected_larger_symbol {
8593 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8594 s.select(new_selections);
8595 });
8596 }
8597 }
8598
8599 pub fn select_larger_syntax_node(
8600 &mut self,
8601 _: &SelectLargerSyntaxNode,
8602 cx: &mut ViewContext<Self>,
8603 ) {
8604 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8605 let buffer = self.buffer.read(cx).snapshot(cx);
8606 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8607
8608 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8609 let mut selected_larger_node = false;
8610 let new_selections = old_selections
8611 .iter()
8612 .map(|selection| {
8613 let old_range = selection.start..selection.end;
8614 let mut new_range = old_range.clone();
8615 while let Some(containing_range) =
8616 buffer.range_for_syntax_ancestor(new_range.clone())
8617 {
8618 new_range = containing_range;
8619 if !display_map.intersects_fold(new_range.start)
8620 && !display_map.intersects_fold(new_range.end)
8621 {
8622 break;
8623 }
8624 }
8625
8626 selected_larger_node |= new_range != old_range;
8627 Selection {
8628 id: selection.id,
8629 start: new_range.start,
8630 end: new_range.end,
8631 goal: SelectionGoal::None,
8632 reversed: selection.reversed,
8633 }
8634 })
8635 .collect::<Vec<_>>();
8636
8637 if selected_larger_node {
8638 stack.push(old_selections);
8639 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8640 s.select(new_selections);
8641 });
8642 }
8643 self.select_larger_syntax_node_stack = stack;
8644 }
8645
8646 pub fn select_smaller_syntax_node(
8647 &mut self,
8648 _: &SelectSmallerSyntaxNode,
8649 cx: &mut ViewContext<Self>,
8650 ) {
8651 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8652 if let Some(selections) = stack.pop() {
8653 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8654 s.select(selections.to_vec());
8655 });
8656 }
8657 self.select_larger_syntax_node_stack = stack;
8658 }
8659
8660 fn refresh_runnables(&mut self, cx: &mut ViewContext<Self>) -> Task<()> {
8661 if !EditorSettings::get_global(cx).gutter.runnables {
8662 self.clear_tasks();
8663 return Task::ready(());
8664 }
8665 let project = self.project.as_ref().map(Model::downgrade);
8666 cx.spawn(|this, mut cx| async move {
8667 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
8668 let Some(project) = project.and_then(|p| p.upgrade()) else {
8669 return;
8670 };
8671 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
8672 this.display_map.update(cx, |map, cx| map.snapshot(cx))
8673 }) else {
8674 return;
8675 };
8676
8677 let hide_runnables = project
8678 .update(&mut cx, |project, cx| {
8679 // Do not display any test indicators in non-dev server remote projects.
8680 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
8681 })
8682 .unwrap_or(true);
8683 if hide_runnables {
8684 return;
8685 }
8686 let new_rows =
8687 cx.background_executor()
8688 .spawn({
8689 let snapshot = display_snapshot.clone();
8690 async move {
8691 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
8692 }
8693 })
8694 .await;
8695 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
8696
8697 this.update(&mut cx, |this, _| {
8698 this.clear_tasks();
8699 for (key, value) in rows {
8700 this.insert_tasks(key, value);
8701 }
8702 })
8703 .ok();
8704 })
8705 }
8706 fn fetch_runnable_ranges(
8707 snapshot: &DisplaySnapshot,
8708 range: Range<Anchor>,
8709 ) -> Vec<language::RunnableRange> {
8710 snapshot.buffer_snapshot.runnable_ranges(range).collect()
8711 }
8712
8713 fn runnable_rows(
8714 project: Model<Project>,
8715 snapshot: DisplaySnapshot,
8716 runnable_ranges: Vec<RunnableRange>,
8717 mut cx: AsyncWindowContext,
8718 ) -> Vec<((BufferId, u32), RunnableTasks)> {
8719 runnable_ranges
8720 .into_iter()
8721 .filter_map(|mut runnable| {
8722 let tasks = cx
8723 .update(|cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
8724 .ok()?;
8725 if tasks.is_empty() {
8726 return None;
8727 }
8728
8729 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
8730
8731 let row = snapshot
8732 .buffer_snapshot
8733 .buffer_line_for_row(MultiBufferRow(point.row))?
8734 .1
8735 .start
8736 .row;
8737
8738 let context_range =
8739 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
8740 Some((
8741 (runnable.buffer_id, row),
8742 RunnableTasks {
8743 templates: tasks,
8744 offset: MultiBufferOffset(runnable.run_range.start),
8745 context_range,
8746 column: point.column,
8747 extra_variables: runnable.extra_captures,
8748 },
8749 ))
8750 })
8751 .collect()
8752 }
8753
8754 fn templates_with_tags(
8755 project: &Model<Project>,
8756 runnable: &mut Runnable,
8757 cx: &WindowContext<'_>,
8758 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
8759 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
8760 let (worktree_id, file) = project
8761 .buffer_for_id(runnable.buffer, cx)
8762 .and_then(|buffer| buffer.read(cx).file())
8763 .map(|file| (file.worktree_id(cx), file.clone()))
8764 .unzip();
8765
8766 (
8767 project.task_store().read(cx).task_inventory().cloned(),
8768 worktree_id,
8769 file,
8770 )
8771 });
8772
8773 let tags = mem::take(&mut runnable.tags);
8774 let mut tags: Vec<_> = tags
8775 .into_iter()
8776 .flat_map(|tag| {
8777 let tag = tag.0.clone();
8778 inventory
8779 .as_ref()
8780 .into_iter()
8781 .flat_map(|inventory| {
8782 inventory.read(cx).list_tasks(
8783 file.clone(),
8784 Some(runnable.language.clone()),
8785 worktree_id,
8786 cx,
8787 )
8788 })
8789 .filter(move |(_, template)| {
8790 template.tags.iter().any(|source_tag| source_tag == &tag)
8791 })
8792 })
8793 .sorted_by_key(|(kind, _)| kind.to_owned())
8794 .collect();
8795 if let Some((leading_tag_source, _)) = tags.first() {
8796 // Strongest source wins; if we have worktree tag binding, prefer that to
8797 // global and language bindings;
8798 // if we have a global binding, prefer that to language binding.
8799 let first_mismatch = tags
8800 .iter()
8801 .position(|(tag_source, _)| tag_source != leading_tag_source);
8802 if let Some(index) = first_mismatch {
8803 tags.truncate(index);
8804 }
8805 }
8806
8807 tags
8808 }
8809
8810 pub fn move_to_enclosing_bracket(
8811 &mut self,
8812 _: &MoveToEnclosingBracket,
8813 cx: &mut ViewContext<Self>,
8814 ) {
8815 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8816 s.move_offsets_with(|snapshot, selection| {
8817 let Some(enclosing_bracket_ranges) =
8818 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
8819 else {
8820 return;
8821 };
8822
8823 let mut best_length = usize::MAX;
8824 let mut best_inside = false;
8825 let mut best_in_bracket_range = false;
8826 let mut best_destination = None;
8827 for (open, close) in enclosing_bracket_ranges {
8828 let close = close.to_inclusive();
8829 let length = close.end() - open.start;
8830 let inside = selection.start >= open.end && selection.end <= *close.start();
8831 let in_bracket_range = open.to_inclusive().contains(&selection.head())
8832 || close.contains(&selection.head());
8833
8834 // If best is next to a bracket and current isn't, skip
8835 if !in_bracket_range && best_in_bracket_range {
8836 continue;
8837 }
8838
8839 // Prefer smaller lengths unless best is inside and current isn't
8840 if length > best_length && (best_inside || !inside) {
8841 continue;
8842 }
8843
8844 best_length = length;
8845 best_inside = inside;
8846 best_in_bracket_range = in_bracket_range;
8847 best_destination = Some(
8848 if close.contains(&selection.start) && close.contains(&selection.end) {
8849 if inside {
8850 open.end
8851 } else {
8852 open.start
8853 }
8854 } else if inside {
8855 *close.start()
8856 } else {
8857 *close.end()
8858 },
8859 );
8860 }
8861
8862 if let Some(destination) = best_destination {
8863 selection.collapse_to(destination, SelectionGoal::None);
8864 }
8865 })
8866 });
8867 }
8868
8869 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
8870 self.end_selection(cx);
8871 self.selection_history.mode = SelectionHistoryMode::Undoing;
8872 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
8873 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8874 self.select_next_state = entry.select_next_state;
8875 self.select_prev_state = entry.select_prev_state;
8876 self.add_selections_state = entry.add_selections_state;
8877 self.request_autoscroll(Autoscroll::newest(), cx);
8878 }
8879 self.selection_history.mode = SelectionHistoryMode::Normal;
8880 }
8881
8882 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
8883 self.end_selection(cx);
8884 self.selection_history.mode = SelectionHistoryMode::Redoing;
8885 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
8886 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8887 self.select_next_state = entry.select_next_state;
8888 self.select_prev_state = entry.select_prev_state;
8889 self.add_selections_state = entry.add_selections_state;
8890 self.request_autoscroll(Autoscroll::newest(), cx);
8891 }
8892 self.selection_history.mode = SelectionHistoryMode::Normal;
8893 }
8894
8895 pub fn expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
8896 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
8897 }
8898
8899 pub fn expand_excerpts_down(
8900 &mut self,
8901 action: &ExpandExcerptsDown,
8902 cx: &mut ViewContext<Self>,
8903 ) {
8904 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
8905 }
8906
8907 pub fn expand_excerpts_up(&mut self, action: &ExpandExcerptsUp, cx: &mut ViewContext<Self>) {
8908 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
8909 }
8910
8911 pub fn expand_excerpts_for_direction(
8912 &mut self,
8913 lines: u32,
8914 direction: ExpandExcerptDirection,
8915 cx: &mut ViewContext<Self>,
8916 ) {
8917 let selections = self.selections.disjoint_anchors();
8918
8919 let lines = if lines == 0 {
8920 EditorSettings::get_global(cx).expand_excerpt_lines
8921 } else {
8922 lines
8923 };
8924
8925 self.buffer.update(cx, |buffer, cx| {
8926 buffer.expand_excerpts(
8927 selections
8928 .iter()
8929 .map(|selection| selection.head().excerpt_id)
8930 .dedup(),
8931 lines,
8932 direction,
8933 cx,
8934 )
8935 })
8936 }
8937
8938 pub fn expand_excerpt(
8939 &mut self,
8940 excerpt: ExcerptId,
8941 direction: ExpandExcerptDirection,
8942 cx: &mut ViewContext<Self>,
8943 ) {
8944 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
8945 self.buffer.update(cx, |buffer, cx| {
8946 buffer.expand_excerpts([excerpt], lines, direction, cx)
8947 })
8948 }
8949
8950 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
8951 self.go_to_diagnostic_impl(Direction::Next, cx)
8952 }
8953
8954 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
8955 self.go_to_diagnostic_impl(Direction::Prev, cx)
8956 }
8957
8958 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
8959 let buffer = self.buffer.read(cx).snapshot(cx);
8960 let selection = self.selections.newest::<usize>(cx);
8961
8962 // If there is an active Diagnostic Popover jump to its diagnostic instead.
8963 if direction == Direction::Next {
8964 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
8965 let (group_id, jump_to) = popover.activation_info();
8966 if self.activate_diagnostics(group_id, cx) {
8967 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8968 let mut new_selection = s.newest_anchor().clone();
8969 new_selection.collapse_to(jump_to, SelectionGoal::None);
8970 s.select_anchors(vec![new_selection.clone()]);
8971 });
8972 }
8973 return;
8974 }
8975 }
8976
8977 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
8978 active_diagnostics
8979 .primary_range
8980 .to_offset(&buffer)
8981 .to_inclusive()
8982 });
8983 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
8984 if active_primary_range.contains(&selection.head()) {
8985 *active_primary_range.start()
8986 } else {
8987 selection.head()
8988 }
8989 } else {
8990 selection.head()
8991 };
8992 let snapshot = self.snapshot(cx);
8993 loop {
8994 let diagnostics = if direction == Direction::Prev {
8995 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
8996 } else {
8997 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
8998 }
8999 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start));
9000 let group = diagnostics
9001 // relies on diagnostics_in_range to return diagnostics with the same starting range to
9002 // be sorted in a stable way
9003 // skip until we are at current active diagnostic, if it exists
9004 .skip_while(|entry| {
9005 (match direction {
9006 Direction::Prev => entry.range.start >= search_start,
9007 Direction::Next => entry.range.start <= search_start,
9008 }) && self
9009 .active_diagnostics
9010 .as_ref()
9011 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
9012 })
9013 .find_map(|entry| {
9014 if entry.diagnostic.is_primary
9015 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
9016 && !entry.range.is_empty()
9017 // if we match with the active diagnostic, skip it
9018 && Some(entry.diagnostic.group_id)
9019 != self.active_diagnostics.as_ref().map(|d| d.group_id)
9020 {
9021 Some((entry.range, entry.diagnostic.group_id))
9022 } else {
9023 None
9024 }
9025 });
9026
9027 if let Some((primary_range, group_id)) = group {
9028 if self.activate_diagnostics(group_id, cx) {
9029 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9030 s.select(vec![Selection {
9031 id: selection.id,
9032 start: primary_range.start,
9033 end: primary_range.start,
9034 reversed: false,
9035 goal: SelectionGoal::None,
9036 }]);
9037 });
9038 }
9039 break;
9040 } else {
9041 // Cycle around to the start of the buffer, potentially moving back to the start of
9042 // the currently active diagnostic.
9043 active_primary_range.take();
9044 if direction == Direction::Prev {
9045 if search_start == buffer.len() {
9046 break;
9047 } else {
9048 search_start = buffer.len();
9049 }
9050 } else if search_start == 0 {
9051 break;
9052 } else {
9053 search_start = 0;
9054 }
9055 }
9056 }
9057 }
9058
9059 fn go_to_next_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
9060 let snapshot = self.snapshot(cx);
9061 let selection = self.selections.newest::<Point>(cx);
9062 self.go_to_hunk_after_position(&snapshot, selection.head(), cx);
9063 }
9064
9065 fn go_to_hunk_after_position(
9066 &mut self,
9067 snapshot: &EditorSnapshot,
9068 position: Point,
9069 cx: &mut ViewContext<'_, Editor>,
9070 ) -> Option<MultiBufferDiffHunk> {
9071 for (ix, position) in [position, Point::zero()].into_iter().enumerate() {
9072 if let Some(hunk) = self.go_to_next_hunk_in_direction(
9073 snapshot,
9074 position,
9075 ix > 0,
9076 snapshot.diff_map.diff_hunks_in_range(
9077 position + Point::new(1, 0)..snapshot.buffer_snapshot.max_point(),
9078 &snapshot.buffer_snapshot,
9079 ),
9080 cx,
9081 ) {
9082 return Some(hunk);
9083 }
9084 }
9085 None
9086 }
9087
9088 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
9089 let snapshot = self.snapshot(cx);
9090 let selection = self.selections.newest::<Point>(cx);
9091 self.go_to_hunk_before_position(&snapshot, selection.head(), cx);
9092 }
9093
9094 fn go_to_hunk_before_position(
9095 &mut self,
9096 snapshot: &EditorSnapshot,
9097 position: Point,
9098 cx: &mut ViewContext<'_, Editor>,
9099 ) -> Option<MultiBufferDiffHunk> {
9100 for (ix, position) in [position, snapshot.buffer_snapshot.max_point()]
9101 .into_iter()
9102 .enumerate()
9103 {
9104 if let Some(hunk) = self.go_to_next_hunk_in_direction(
9105 snapshot,
9106 position,
9107 ix > 0,
9108 snapshot
9109 .diff_map
9110 .diff_hunks_in_range_rev(Point::zero()..position, &snapshot.buffer_snapshot),
9111 cx,
9112 ) {
9113 return Some(hunk);
9114 }
9115 }
9116 None
9117 }
9118
9119 fn go_to_next_hunk_in_direction(
9120 &mut self,
9121 snapshot: &DisplaySnapshot,
9122 initial_point: Point,
9123 is_wrapped: bool,
9124 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
9125 cx: &mut ViewContext<Editor>,
9126 ) -> Option<MultiBufferDiffHunk> {
9127 let display_point = initial_point.to_display_point(snapshot);
9128 let mut hunks = hunks
9129 .map(|hunk| (diff_hunk_to_display(&hunk, snapshot), hunk))
9130 .filter(|(display_hunk, _)| {
9131 is_wrapped || !display_hunk.contains_display_row(display_point.row())
9132 })
9133 .dedup();
9134
9135 if let Some((display_hunk, hunk)) = hunks.next() {
9136 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9137 let row = display_hunk.start_display_row();
9138 let point = DisplayPoint::new(row, 0);
9139 s.select_display_ranges([point..point]);
9140 });
9141
9142 Some(hunk)
9143 } else {
9144 None
9145 }
9146 }
9147
9148 pub fn go_to_definition(
9149 &mut self,
9150 _: &GoToDefinition,
9151 cx: &mut ViewContext<Self>,
9152 ) -> Task<Result<Navigated>> {
9153 let definition = self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
9154 cx.spawn(|editor, mut cx| async move {
9155 if definition.await? == Navigated::Yes {
9156 return Ok(Navigated::Yes);
9157 }
9158 match editor.update(&mut cx, |editor, cx| {
9159 editor.find_all_references(&FindAllReferences, cx)
9160 })? {
9161 Some(references) => references.await,
9162 None => Ok(Navigated::No),
9163 }
9164 })
9165 }
9166
9167 pub fn go_to_declaration(
9168 &mut self,
9169 _: &GoToDeclaration,
9170 cx: &mut ViewContext<Self>,
9171 ) -> Task<Result<Navigated>> {
9172 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, cx)
9173 }
9174
9175 pub fn go_to_declaration_split(
9176 &mut self,
9177 _: &GoToDeclaration,
9178 cx: &mut ViewContext<Self>,
9179 ) -> Task<Result<Navigated>> {
9180 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, cx)
9181 }
9182
9183 pub fn go_to_implementation(
9184 &mut self,
9185 _: &GoToImplementation,
9186 cx: &mut ViewContext<Self>,
9187 ) -> Task<Result<Navigated>> {
9188 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx)
9189 }
9190
9191 pub fn go_to_implementation_split(
9192 &mut self,
9193 _: &GoToImplementationSplit,
9194 cx: &mut ViewContext<Self>,
9195 ) -> Task<Result<Navigated>> {
9196 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx)
9197 }
9198
9199 pub fn go_to_type_definition(
9200 &mut self,
9201 _: &GoToTypeDefinition,
9202 cx: &mut ViewContext<Self>,
9203 ) -> Task<Result<Navigated>> {
9204 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx)
9205 }
9206
9207 pub fn go_to_definition_split(
9208 &mut self,
9209 _: &GoToDefinitionSplit,
9210 cx: &mut ViewContext<Self>,
9211 ) -> Task<Result<Navigated>> {
9212 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx)
9213 }
9214
9215 pub fn go_to_type_definition_split(
9216 &mut self,
9217 _: &GoToTypeDefinitionSplit,
9218 cx: &mut ViewContext<Self>,
9219 ) -> Task<Result<Navigated>> {
9220 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx)
9221 }
9222
9223 fn go_to_definition_of_kind(
9224 &mut self,
9225 kind: GotoDefinitionKind,
9226 split: bool,
9227 cx: &mut ViewContext<Self>,
9228 ) -> Task<Result<Navigated>> {
9229 let Some(provider) = self.semantics_provider.clone() else {
9230 return Task::ready(Ok(Navigated::No));
9231 };
9232 let head = self.selections.newest::<usize>(cx).head();
9233 let buffer = self.buffer.read(cx);
9234 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
9235 text_anchor
9236 } else {
9237 return Task::ready(Ok(Navigated::No));
9238 };
9239
9240 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
9241 return Task::ready(Ok(Navigated::No));
9242 };
9243
9244 cx.spawn(|editor, mut cx| async move {
9245 let definitions = definitions.await?;
9246 let navigated = editor
9247 .update(&mut cx, |editor, cx| {
9248 editor.navigate_to_hover_links(
9249 Some(kind),
9250 definitions
9251 .into_iter()
9252 .filter(|location| {
9253 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
9254 })
9255 .map(HoverLink::Text)
9256 .collect::<Vec<_>>(),
9257 split,
9258 cx,
9259 )
9260 })?
9261 .await?;
9262 anyhow::Ok(navigated)
9263 })
9264 }
9265
9266 pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext<Self>) {
9267 let position = self.selections.newest_anchor().head();
9268 let Some((buffer, buffer_position)) =
9269 self.buffer.read(cx).text_anchor_for_position(position, cx)
9270 else {
9271 return;
9272 };
9273
9274 cx.spawn(|editor, mut cx| async move {
9275 if let Some((_, url)) = find_url(&buffer, buffer_position, cx.clone()) {
9276 editor.update(&mut cx, |_, cx| {
9277 cx.open_url(&url);
9278 })
9279 } else {
9280 Ok(())
9281 }
9282 })
9283 .detach();
9284 }
9285
9286 pub fn open_file(&mut self, _: &OpenFile, cx: &mut ViewContext<Self>) {
9287 let Some(workspace) = self.workspace() else {
9288 return;
9289 };
9290
9291 let position = self.selections.newest_anchor().head();
9292
9293 let Some((buffer, buffer_position)) =
9294 self.buffer.read(cx).text_anchor_for_position(position, cx)
9295 else {
9296 return;
9297 };
9298
9299 let project = self.project.clone();
9300
9301 cx.spawn(|_, mut cx| async move {
9302 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
9303
9304 if let Some((_, path)) = result {
9305 workspace
9306 .update(&mut cx, |workspace, cx| {
9307 workspace.open_resolved_path(path, cx)
9308 })?
9309 .await?;
9310 }
9311 anyhow::Ok(())
9312 })
9313 .detach();
9314 }
9315
9316 pub(crate) fn navigate_to_hover_links(
9317 &mut self,
9318 kind: Option<GotoDefinitionKind>,
9319 mut definitions: Vec<HoverLink>,
9320 split: bool,
9321 cx: &mut ViewContext<Editor>,
9322 ) -> Task<Result<Navigated>> {
9323 // If there is one definition, just open it directly
9324 if definitions.len() == 1 {
9325 let definition = definitions.pop().unwrap();
9326
9327 enum TargetTaskResult {
9328 Location(Option<Location>),
9329 AlreadyNavigated,
9330 }
9331
9332 let target_task = match definition {
9333 HoverLink::Text(link) => {
9334 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
9335 }
9336 HoverLink::InlayHint(lsp_location, server_id) => {
9337 let computation = self.compute_target_location(lsp_location, server_id, cx);
9338 cx.background_executor().spawn(async move {
9339 let location = computation.await?;
9340 Ok(TargetTaskResult::Location(location))
9341 })
9342 }
9343 HoverLink::Url(url) => {
9344 cx.open_url(&url);
9345 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
9346 }
9347 HoverLink::File(path) => {
9348 if let Some(workspace) = self.workspace() {
9349 cx.spawn(|_, mut cx| async move {
9350 workspace
9351 .update(&mut cx, |workspace, cx| {
9352 workspace.open_resolved_path(path, cx)
9353 })?
9354 .await
9355 .map(|_| TargetTaskResult::AlreadyNavigated)
9356 })
9357 } else {
9358 Task::ready(Ok(TargetTaskResult::Location(None)))
9359 }
9360 }
9361 };
9362 cx.spawn(|editor, mut cx| async move {
9363 let target = match target_task.await.context("target resolution task")? {
9364 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
9365 TargetTaskResult::Location(None) => return Ok(Navigated::No),
9366 TargetTaskResult::Location(Some(target)) => target,
9367 };
9368
9369 editor.update(&mut cx, |editor, cx| {
9370 let Some(workspace) = editor.workspace() else {
9371 return Navigated::No;
9372 };
9373 let pane = workspace.read(cx).active_pane().clone();
9374
9375 let range = target.range.to_offset(target.buffer.read(cx));
9376 let range = editor.range_for_match(&range);
9377
9378 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
9379 let buffer = target.buffer.read(cx);
9380 let range = check_multiline_range(buffer, range);
9381 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
9382 s.select_ranges([range]);
9383 });
9384 } else {
9385 cx.window_context().defer(move |cx| {
9386 let target_editor: View<Self> =
9387 workspace.update(cx, |workspace, cx| {
9388 let pane = if split {
9389 workspace.adjacent_pane(cx)
9390 } else {
9391 workspace.active_pane().clone()
9392 };
9393
9394 workspace.open_project_item(
9395 pane,
9396 target.buffer.clone(),
9397 true,
9398 true,
9399 cx,
9400 )
9401 });
9402 target_editor.update(cx, |target_editor, cx| {
9403 // When selecting a definition in a different buffer, disable the nav history
9404 // to avoid creating a history entry at the previous cursor location.
9405 pane.update(cx, |pane, _| pane.disable_history());
9406 let buffer = target.buffer.read(cx);
9407 let range = check_multiline_range(buffer, range);
9408 target_editor.change_selections(
9409 Some(Autoscroll::focused()),
9410 cx,
9411 |s| {
9412 s.select_ranges([range]);
9413 },
9414 );
9415 pane.update(cx, |pane, _| pane.enable_history());
9416 });
9417 });
9418 }
9419 Navigated::Yes
9420 })
9421 })
9422 } else if !definitions.is_empty() {
9423 cx.spawn(|editor, mut cx| async move {
9424 let (title, location_tasks, workspace) = editor
9425 .update(&mut cx, |editor, cx| {
9426 let tab_kind = match kind {
9427 Some(GotoDefinitionKind::Implementation) => "Implementations",
9428 _ => "Definitions",
9429 };
9430 let title = definitions
9431 .iter()
9432 .find_map(|definition| match definition {
9433 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
9434 let buffer = origin.buffer.read(cx);
9435 format!(
9436 "{} for {}",
9437 tab_kind,
9438 buffer
9439 .text_for_range(origin.range.clone())
9440 .collect::<String>()
9441 )
9442 }),
9443 HoverLink::InlayHint(_, _) => None,
9444 HoverLink::Url(_) => None,
9445 HoverLink::File(_) => None,
9446 })
9447 .unwrap_or(tab_kind.to_string());
9448 let location_tasks = definitions
9449 .into_iter()
9450 .map(|definition| match definition {
9451 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
9452 HoverLink::InlayHint(lsp_location, server_id) => {
9453 editor.compute_target_location(lsp_location, server_id, cx)
9454 }
9455 HoverLink::Url(_) => Task::ready(Ok(None)),
9456 HoverLink::File(_) => Task::ready(Ok(None)),
9457 })
9458 .collect::<Vec<_>>();
9459 (title, location_tasks, editor.workspace().clone())
9460 })
9461 .context("location tasks preparation")?;
9462
9463 let locations = future::join_all(location_tasks)
9464 .await
9465 .into_iter()
9466 .filter_map(|location| location.transpose())
9467 .collect::<Result<_>>()
9468 .context("location tasks")?;
9469
9470 let Some(workspace) = workspace else {
9471 return Ok(Navigated::No);
9472 };
9473 let opened = workspace
9474 .update(&mut cx, |workspace, cx| {
9475 Self::open_locations_in_multibuffer(workspace, locations, title, split, cx)
9476 })
9477 .ok();
9478
9479 anyhow::Ok(Navigated::from_bool(opened.is_some()))
9480 })
9481 } else {
9482 Task::ready(Ok(Navigated::No))
9483 }
9484 }
9485
9486 fn compute_target_location(
9487 &self,
9488 lsp_location: lsp::Location,
9489 server_id: LanguageServerId,
9490 cx: &mut ViewContext<Self>,
9491 ) -> Task<anyhow::Result<Option<Location>>> {
9492 let Some(project) = self.project.clone() else {
9493 return Task::Ready(Some(Ok(None)));
9494 };
9495
9496 cx.spawn(move |editor, mut cx| async move {
9497 let location_task = editor.update(&mut cx, |_, cx| {
9498 project.update(cx, |project, cx| {
9499 let language_server_name = project
9500 .language_server_statuses(cx)
9501 .find(|(id, _)| server_id == *id)
9502 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
9503 language_server_name.map(|language_server_name| {
9504 project.open_local_buffer_via_lsp(
9505 lsp_location.uri.clone(),
9506 server_id,
9507 language_server_name,
9508 cx,
9509 )
9510 })
9511 })
9512 })?;
9513 let location = match location_task {
9514 Some(task) => Some({
9515 let target_buffer_handle = task.await.context("open local buffer")?;
9516 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
9517 let target_start = target_buffer
9518 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
9519 let target_end = target_buffer
9520 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
9521 target_buffer.anchor_after(target_start)
9522 ..target_buffer.anchor_before(target_end)
9523 })?;
9524 Location {
9525 buffer: target_buffer_handle,
9526 range,
9527 }
9528 }),
9529 None => None,
9530 };
9531 Ok(location)
9532 })
9533 }
9534
9535 pub fn find_all_references(
9536 &mut self,
9537 _: &FindAllReferences,
9538 cx: &mut ViewContext<Self>,
9539 ) -> Option<Task<Result<Navigated>>> {
9540 let selection = self.selections.newest::<usize>(cx);
9541 let multi_buffer = self.buffer.read(cx);
9542 let head = selection.head();
9543
9544 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
9545 let head_anchor = multi_buffer_snapshot.anchor_at(
9546 head,
9547 if head < selection.tail() {
9548 Bias::Right
9549 } else {
9550 Bias::Left
9551 },
9552 );
9553
9554 match self
9555 .find_all_references_task_sources
9556 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
9557 {
9558 Ok(_) => {
9559 log::info!(
9560 "Ignoring repeated FindAllReferences invocation with the position of already running task"
9561 );
9562 return None;
9563 }
9564 Err(i) => {
9565 self.find_all_references_task_sources.insert(i, head_anchor);
9566 }
9567 }
9568
9569 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
9570 let workspace = self.workspace()?;
9571 let project = workspace.read(cx).project().clone();
9572 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
9573 Some(cx.spawn(|editor, mut cx| async move {
9574 let _cleanup = defer({
9575 let mut cx = cx.clone();
9576 move || {
9577 let _ = editor.update(&mut cx, |editor, _| {
9578 if let Ok(i) =
9579 editor
9580 .find_all_references_task_sources
9581 .binary_search_by(|anchor| {
9582 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
9583 })
9584 {
9585 editor.find_all_references_task_sources.remove(i);
9586 }
9587 });
9588 }
9589 });
9590
9591 let locations = references.await?;
9592 if locations.is_empty() {
9593 return anyhow::Ok(Navigated::No);
9594 }
9595
9596 workspace.update(&mut cx, |workspace, cx| {
9597 let title = locations
9598 .first()
9599 .as_ref()
9600 .map(|location| {
9601 let buffer = location.buffer.read(cx);
9602 format!(
9603 "References to `{}`",
9604 buffer
9605 .text_for_range(location.range.clone())
9606 .collect::<String>()
9607 )
9608 })
9609 .unwrap();
9610 Self::open_locations_in_multibuffer(workspace, locations, title, false, cx);
9611 Navigated::Yes
9612 })
9613 }))
9614 }
9615
9616 /// Opens a multibuffer with the given project locations in it
9617 pub fn open_locations_in_multibuffer(
9618 workspace: &mut Workspace,
9619 mut locations: Vec<Location>,
9620 title: String,
9621 split: bool,
9622 cx: &mut ViewContext<Workspace>,
9623 ) {
9624 // If there are multiple definitions, open them in a multibuffer
9625 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
9626 let mut locations = locations.into_iter().peekable();
9627 let mut ranges_to_highlight = Vec::new();
9628 let capability = workspace.project().read(cx).capability();
9629
9630 let excerpt_buffer = cx.new_model(|cx| {
9631 let mut multibuffer = MultiBuffer::new(capability);
9632 while let Some(location) = locations.next() {
9633 let buffer = location.buffer.read(cx);
9634 let mut ranges_for_buffer = Vec::new();
9635 let range = location.range.to_offset(buffer);
9636 ranges_for_buffer.push(range.clone());
9637
9638 while let Some(next_location) = locations.peek() {
9639 if next_location.buffer == location.buffer {
9640 ranges_for_buffer.push(next_location.range.to_offset(buffer));
9641 locations.next();
9642 } else {
9643 break;
9644 }
9645 }
9646
9647 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
9648 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
9649 location.buffer.clone(),
9650 ranges_for_buffer,
9651 DEFAULT_MULTIBUFFER_CONTEXT,
9652 cx,
9653 ))
9654 }
9655
9656 multibuffer.with_title(title)
9657 });
9658
9659 let editor = cx.new_view(|cx| {
9660 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), true, cx)
9661 });
9662 editor.update(cx, |editor, cx| {
9663 if let Some(first_range) = ranges_to_highlight.first() {
9664 editor.change_selections(None, cx, |selections| {
9665 selections.clear_disjoint();
9666 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
9667 });
9668 }
9669 editor.highlight_background::<Self>(
9670 &ranges_to_highlight,
9671 |theme| theme.editor_highlighted_line_background,
9672 cx,
9673 );
9674 editor.register_buffers_with_language_servers(cx);
9675 });
9676
9677 let item = Box::new(editor);
9678 let item_id = item.item_id();
9679
9680 if split {
9681 workspace.split_item(SplitDirection::Right, item.clone(), cx);
9682 } else {
9683 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
9684 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
9685 pane.close_current_preview_item(cx)
9686 } else {
9687 None
9688 }
9689 });
9690 workspace.add_item_to_active_pane(item.clone(), destination_index, true, cx);
9691 }
9692 workspace.active_pane().update(cx, |pane, cx| {
9693 pane.set_preview_item_id(Some(item_id), cx);
9694 });
9695 }
9696
9697 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9698 use language::ToOffset as _;
9699
9700 let provider = self.semantics_provider.clone()?;
9701 let selection = self.selections.newest_anchor().clone();
9702 let (cursor_buffer, cursor_buffer_position) = self
9703 .buffer
9704 .read(cx)
9705 .text_anchor_for_position(selection.head(), cx)?;
9706 let (tail_buffer, cursor_buffer_position_end) = self
9707 .buffer
9708 .read(cx)
9709 .text_anchor_for_position(selection.tail(), cx)?;
9710 if tail_buffer != cursor_buffer {
9711 return None;
9712 }
9713
9714 let snapshot = cursor_buffer.read(cx).snapshot();
9715 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
9716 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
9717 let prepare_rename = provider
9718 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
9719 .unwrap_or_else(|| Task::ready(Ok(None)));
9720 drop(snapshot);
9721
9722 Some(cx.spawn(|this, mut cx| async move {
9723 let rename_range = if let Some(range) = prepare_rename.await? {
9724 Some(range)
9725 } else {
9726 this.update(&mut cx, |this, cx| {
9727 let buffer = this.buffer.read(cx).snapshot(cx);
9728 let mut buffer_highlights = this
9729 .document_highlights_for_position(selection.head(), &buffer)
9730 .filter(|highlight| {
9731 highlight.start.excerpt_id == selection.head().excerpt_id
9732 && highlight.end.excerpt_id == selection.head().excerpt_id
9733 });
9734 buffer_highlights
9735 .next()
9736 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
9737 })?
9738 };
9739 if let Some(rename_range) = rename_range {
9740 this.update(&mut cx, |this, cx| {
9741 let snapshot = cursor_buffer.read(cx).snapshot();
9742 let rename_buffer_range = rename_range.to_offset(&snapshot);
9743 let cursor_offset_in_rename_range =
9744 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
9745 let cursor_offset_in_rename_range_end =
9746 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
9747
9748 this.take_rename(false, cx);
9749 let buffer = this.buffer.read(cx).read(cx);
9750 let cursor_offset = selection.head().to_offset(&buffer);
9751 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
9752 let rename_end = rename_start + rename_buffer_range.len();
9753 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
9754 let mut old_highlight_id = None;
9755 let old_name: Arc<str> = buffer
9756 .chunks(rename_start..rename_end, true)
9757 .map(|chunk| {
9758 if old_highlight_id.is_none() {
9759 old_highlight_id = chunk.syntax_highlight_id;
9760 }
9761 chunk.text
9762 })
9763 .collect::<String>()
9764 .into();
9765
9766 drop(buffer);
9767
9768 // Position the selection in the rename editor so that it matches the current selection.
9769 this.show_local_selections = false;
9770 let rename_editor = cx.new_view(|cx| {
9771 let mut editor = Editor::single_line(cx);
9772 editor.buffer.update(cx, |buffer, cx| {
9773 buffer.edit([(0..0, old_name.clone())], None, cx)
9774 });
9775 let rename_selection_range = match cursor_offset_in_rename_range
9776 .cmp(&cursor_offset_in_rename_range_end)
9777 {
9778 Ordering::Equal => {
9779 editor.select_all(&SelectAll, cx);
9780 return editor;
9781 }
9782 Ordering::Less => {
9783 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
9784 }
9785 Ordering::Greater => {
9786 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
9787 }
9788 };
9789 if rename_selection_range.end > old_name.len() {
9790 editor.select_all(&SelectAll, cx);
9791 } else {
9792 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
9793 s.select_ranges([rename_selection_range]);
9794 });
9795 }
9796 editor
9797 });
9798 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
9799 if e == &EditorEvent::Focused {
9800 cx.emit(EditorEvent::FocusedIn)
9801 }
9802 })
9803 .detach();
9804
9805 let write_highlights =
9806 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
9807 let read_highlights =
9808 this.clear_background_highlights::<DocumentHighlightRead>(cx);
9809 let ranges = write_highlights
9810 .iter()
9811 .flat_map(|(_, ranges)| ranges.iter())
9812 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
9813 .cloned()
9814 .collect();
9815
9816 this.highlight_text::<Rename>(
9817 ranges,
9818 HighlightStyle {
9819 fade_out: Some(0.6),
9820 ..Default::default()
9821 },
9822 cx,
9823 );
9824 let rename_focus_handle = rename_editor.focus_handle(cx);
9825 cx.focus(&rename_focus_handle);
9826 let block_id = this.insert_blocks(
9827 [BlockProperties {
9828 style: BlockStyle::Flex,
9829 placement: BlockPlacement::Below(range.start),
9830 height: 1,
9831 render: Arc::new({
9832 let rename_editor = rename_editor.clone();
9833 move |cx: &mut BlockContext| {
9834 let mut text_style = cx.editor_style.text.clone();
9835 if let Some(highlight_style) = old_highlight_id
9836 .and_then(|h| h.style(&cx.editor_style.syntax))
9837 {
9838 text_style = text_style.highlight(highlight_style);
9839 }
9840 div()
9841 .block_mouse_down()
9842 .pl(cx.anchor_x)
9843 .child(EditorElement::new(
9844 &rename_editor,
9845 EditorStyle {
9846 background: cx.theme().system().transparent,
9847 local_player: cx.editor_style.local_player,
9848 text: text_style,
9849 scrollbar_width: cx.editor_style.scrollbar_width,
9850 syntax: cx.editor_style.syntax.clone(),
9851 status: cx.editor_style.status.clone(),
9852 inlay_hints_style: HighlightStyle {
9853 font_weight: Some(FontWeight::BOLD),
9854 ..make_inlay_hints_style(cx)
9855 },
9856 suggestions_style: HighlightStyle {
9857 color: Some(cx.theme().status().predictive),
9858 ..HighlightStyle::default()
9859 },
9860 ..EditorStyle::default()
9861 },
9862 ))
9863 .into_any_element()
9864 }
9865 }),
9866 priority: 0,
9867 }],
9868 Some(Autoscroll::fit()),
9869 cx,
9870 )[0];
9871 this.pending_rename = Some(RenameState {
9872 range,
9873 old_name,
9874 editor: rename_editor,
9875 block_id,
9876 });
9877 })?;
9878 }
9879
9880 Ok(())
9881 }))
9882 }
9883
9884 pub fn confirm_rename(
9885 &mut self,
9886 _: &ConfirmRename,
9887 cx: &mut ViewContext<Self>,
9888 ) -> Option<Task<Result<()>>> {
9889 let rename = self.take_rename(false, cx)?;
9890 let workspace = self.workspace()?.downgrade();
9891 let (buffer, start) = self
9892 .buffer
9893 .read(cx)
9894 .text_anchor_for_position(rename.range.start, cx)?;
9895 let (end_buffer, _) = self
9896 .buffer
9897 .read(cx)
9898 .text_anchor_for_position(rename.range.end, cx)?;
9899 if buffer != end_buffer {
9900 return None;
9901 }
9902
9903 let old_name = rename.old_name;
9904 let new_name = rename.editor.read(cx).text(cx);
9905
9906 let rename = self.semantics_provider.as_ref()?.perform_rename(
9907 &buffer,
9908 start,
9909 new_name.clone(),
9910 cx,
9911 )?;
9912
9913 Some(cx.spawn(|editor, mut cx| async move {
9914 let project_transaction = rename.await?;
9915 Self::open_project_transaction(
9916 &editor,
9917 workspace,
9918 project_transaction,
9919 format!("Rename: {} → {}", old_name, new_name),
9920 cx.clone(),
9921 )
9922 .await?;
9923
9924 editor.update(&mut cx, |editor, cx| {
9925 editor.refresh_document_highlights(cx);
9926 })?;
9927 Ok(())
9928 }))
9929 }
9930
9931 fn take_rename(
9932 &mut self,
9933 moving_cursor: bool,
9934 cx: &mut ViewContext<Self>,
9935 ) -> Option<RenameState> {
9936 let rename = self.pending_rename.take()?;
9937 if rename.editor.focus_handle(cx).is_focused(cx) {
9938 cx.focus(&self.focus_handle);
9939 }
9940
9941 self.remove_blocks(
9942 [rename.block_id].into_iter().collect(),
9943 Some(Autoscroll::fit()),
9944 cx,
9945 );
9946 self.clear_highlights::<Rename>(cx);
9947 self.show_local_selections = true;
9948
9949 if moving_cursor {
9950 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
9951 editor.selections.newest::<usize>(cx).head()
9952 });
9953
9954 // Update the selection to match the position of the selection inside
9955 // the rename editor.
9956 let snapshot = self.buffer.read(cx).read(cx);
9957 let rename_range = rename.range.to_offset(&snapshot);
9958 let cursor_in_editor = snapshot
9959 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
9960 .min(rename_range.end);
9961 drop(snapshot);
9962
9963 self.change_selections(None, cx, |s| {
9964 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
9965 });
9966 } else {
9967 self.refresh_document_highlights(cx);
9968 }
9969
9970 Some(rename)
9971 }
9972
9973 pub fn pending_rename(&self) -> Option<&RenameState> {
9974 self.pending_rename.as_ref()
9975 }
9976
9977 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9978 let project = match &self.project {
9979 Some(project) => project.clone(),
9980 None => return None,
9981 };
9982
9983 Some(self.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffer, cx))
9984 }
9985
9986 fn format_selections(
9987 &mut self,
9988 _: &FormatSelections,
9989 cx: &mut ViewContext<Self>,
9990 ) -> Option<Task<Result<()>>> {
9991 let project = match &self.project {
9992 Some(project) => project.clone(),
9993 None => return None,
9994 };
9995
9996 let selections = self
9997 .selections
9998 .all_adjusted(cx)
9999 .into_iter()
10000 .filter(|s| !s.is_empty())
10001 .collect_vec();
10002
10003 Some(self.perform_format(
10004 project,
10005 FormatTrigger::Manual,
10006 FormatTarget::Ranges(selections),
10007 cx,
10008 ))
10009 }
10010
10011 fn perform_format(
10012 &mut self,
10013 project: Model<Project>,
10014 trigger: FormatTrigger,
10015 target: FormatTarget,
10016 cx: &mut ViewContext<Self>,
10017 ) -> Task<Result<()>> {
10018 let buffer = self.buffer().clone();
10019 let mut buffers = buffer.read(cx).all_buffers();
10020 if trigger == FormatTrigger::Save {
10021 buffers.retain(|buffer| buffer.read(cx).is_dirty());
10022 }
10023
10024 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
10025 let format = project.update(cx, |project, cx| {
10026 project.format(buffers, true, trigger, target, cx)
10027 });
10028
10029 cx.spawn(|_, mut cx| async move {
10030 let transaction = futures::select_biased! {
10031 () = timeout => {
10032 log::warn!("timed out waiting for formatting");
10033 None
10034 }
10035 transaction = format.log_err().fuse() => transaction,
10036 };
10037
10038 buffer
10039 .update(&mut cx, |buffer, cx| {
10040 if let Some(transaction) = transaction {
10041 if !buffer.is_singleton() {
10042 buffer.push_transaction(&transaction.0, cx);
10043 }
10044 }
10045
10046 cx.notify();
10047 })
10048 .ok();
10049
10050 Ok(())
10051 })
10052 }
10053
10054 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
10055 if let Some(project) = self.project.clone() {
10056 self.buffer.update(cx, |multi_buffer, cx| {
10057 project.update(cx, |project, cx| {
10058 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
10059 });
10060 })
10061 }
10062 }
10063
10064 fn cancel_language_server_work(
10065 &mut self,
10066 _: &actions::CancelLanguageServerWork,
10067 cx: &mut ViewContext<Self>,
10068 ) {
10069 if let Some(project) = self.project.clone() {
10070 self.buffer.update(cx, |multi_buffer, cx| {
10071 project.update(cx, |project, cx| {
10072 project.cancel_language_server_work_for_buffers(multi_buffer.all_buffers(), cx);
10073 });
10074 })
10075 }
10076 }
10077
10078 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
10079 cx.show_character_palette();
10080 }
10081
10082 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
10083 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
10084 let buffer = self.buffer.read(cx).snapshot(cx);
10085 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
10086 let is_valid = buffer
10087 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
10088 .any(|entry| {
10089 entry.diagnostic.is_primary
10090 && !entry.range.is_empty()
10091 && entry.range.start == primary_range_start
10092 && entry.diagnostic.message == active_diagnostics.primary_message
10093 });
10094
10095 if is_valid != active_diagnostics.is_valid {
10096 active_diagnostics.is_valid = is_valid;
10097 let mut new_styles = HashMap::default();
10098 for (block_id, diagnostic) in &active_diagnostics.blocks {
10099 new_styles.insert(
10100 *block_id,
10101 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
10102 );
10103 }
10104 self.display_map.update(cx, |display_map, _cx| {
10105 display_map.replace_blocks(new_styles)
10106 });
10107 }
10108 }
10109 }
10110
10111 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
10112 self.dismiss_diagnostics(cx);
10113 let snapshot = self.snapshot(cx);
10114 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
10115 let buffer = self.buffer.read(cx).snapshot(cx);
10116
10117 let mut primary_range = None;
10118 let mut primary_message = None;
10119 let mut group_end = Point::zero();
10120 let diagnostic_group = buffer
10121 .diagnostic_group::<MultiBufferPoint>(group_id)
10122 .filter_map(|entry| {
10123 if snapshot.is_line_folded(MultiBufferRow(entry.range.start.row))
10124 && (entry.range.start.row == entry.range.end.row
10125 || snapshot.is_line_folded(MultiBufferRow(entry.range.end.row)))
10126 {
10127 return None;
10128 }
10129 if entry.range.end > group_end {
10130 group_end = entry.range.end;
10131 }
10132 if entry.diagnostic.is_primary {
10133 primary_range = Some(entry.range.clone());
10134 primary_message = Some(entry.diagnostic.message.clone());
10135 }
10136 Some(entry)
10137 })
10138 .collect::<Vec<_>>();
10139 let primary_range = primary_range?;
10140 let primary_message = primary_message?;
10141 let primary_range =
10142 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
10143
10144 let blocks = display_map
10145 .insert_blocks(
10146 diagnostic_group.iter().map(|entry| {
10147 let diagnostic = entry.diagnostic.clone();
10148 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
10149 BlockProperties {
10150 style: BlockStyle::Fixed,
10151 placement: BlockPlacement::Below(
10152 buffer.anchor_after(entry.range.start),
10153 ),
10154 height: message_height,
10155 render: diagnostic_block_renderer(diagnostic, None, true, true),
10156 priority: 0,
10157 }
10158 }),
10159 cx,
10160 )
10161 .into_iter()
10162 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
10163 .collect();
10164
10165 Some(ActiveDiagnosticGroup {
10166 primary_range,
10167 primary_message,
10168 group_id,
10169 blocks,
10170 is_valid: true,
10171 })
10172 });
10173 self.active_diagnostics.is_some()
10174 }
10175
10176 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
10177 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
10178 self.display_map.update(cx, |display_map, cx| {
10179 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
10180 });
10181 cx.notify();
10182 }
10183 }
10184
10185 pub fn set_selections_from_remote(
10186 &mut self,
10187 selections: Vec<Selection<Anchor>>,
10188 pending_selection: Option<Selection<Anchor>>,
10189 cx: &mut ViewContext<Self>,
10190 ) {
10191 let old_cursor_position = self.selections.newest_anchor().head();
10192 self.selections.change_with(cx, |s| {
10193 s.select_anchors(selections);
10194 if let Some(pending_selection) = pending_selection {
10195 s.set_pending(pending_selection, SelectMode::Character);
10196 } else {
10197 s.clear_pending();
10198 }
10199 });
10200 self.selections_did_change(false, &old_cursor_position, true, cx);
10201 }
10202
10203 fn push_to_selection_history(&mut self) {
10204 self.selection_history.push(SelectionHistoryEntry {
10205 selections: self.selections.disjoint_anchors(),
10206 select_next_state: self.select_next_state.clone(),
10207 select_prev_state: self.select_prev_state.clone(),
10208 add_selections_state: self.add_selections_state.clone(),
10209 });
10210 }
10211
10212 pub fn transact(
10213 &mut self,
10214 cx: &mut ViewContext<Self>,
10215 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
10216 ) -> Option<TransactionId> {
10217 self.start_transaction_at(Instant::now(), cx);
10218 update(self, cx);
10219 self.end_transaction_at(Instant::now(), cx)
10220 }
10221
10222 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
10223 self.end_selection(cx);
10224 if let Some(tx_id) = self
10225 .buffer
10226 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
10227 {
10228 self.selection_history
10229 .insert_transaction(tx_id, self.selections.disjoint_anchors());
10230 cx.emit(EditorEvent::TransactionBegun {
10231 transaction_id: tx_id,
10232 })
10233 }
10234 }
10235
10236 fn end_transaction_at(
10237 &mut self,
10238 now: Instant,
10239 cx: &mut ViewContext<Self>,
10240 ) -> Option<TransactionId> {
10241 if let Some(transaction_id) = self
10242 .buffer
10243 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
10244 {
10245 if let Some((_, end_selections)) =
10246 self.selection_history.transaction_mut(transaction_id)
10247 {
10248 *end_selections = Some(self.selections.disjoint_anchors());
10249 } else {
10250 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
10251 }
10252
10253 cx.emit(EditorEvent::Edited { transaction_id });
10254 Some(transaction_id)
10255 } else {
10256 None
10257 }
10258 }
10259
10260 pub fn toggle_fold(&mut self, _: &actions::ToggleFold, cx: &mut ViewContext<Self>) {
10261 let selection = self.selections.newest::<Point>(cx);
10262
10263 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10264 let range = if selection.is_empty() {
10265 let point = selection.head().to_display_point(&display_map);
10266 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
10267 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
10268 .to_point(&display_map);
10269 start..end
10270 } else {
10271 selection.range()
10272 };
10273 if display_map.folds_in_range(range).next().is_some() {
10274 self.unfold_lines(&Default::default(), cx)
10275 } else {
10276 self.fold(&Default::default(), cx)
10277 }
10278 }
10279
10280 pub fn toggle_fold_recursive(
10281 &mut self,
10282 _: &actions::ToggleFoldRecursive,
10283 cx: &mut ViewContext<Self>,
10284 ) {
10285 let selection = self.selections.newest::<Point>(cx);
10286
10287 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10288 let range = if selection.is_empty() {
10289 let point = selection.head().to_display_point(&display_map);
10290 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
10291 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
10292 .to_point(&display_map);
10293 start..end
10294 } else {
10295 selection.range()
10296 };
10297 if display_map.folds_in_range(range).next().is_some() {
10298 self.unfold_recursive(&Default::default(), cx)
10299 } else {
10300 self.fold_recursive(&Default::default(), cx)
10301 }
10302 }
10303
10304 pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
10305 let mut to_fold = Vec::new();
10306 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10307 let selections = self.selections.all_adjusted(cx);
10308
10309 for selection in selections {
10310 let range = selection.range().sorted();
10311 let buffer_start_row = range.start.row;
10312
10313 if range.start.row != range.end.row {
10314 let mut found = false;
10315 let mut row = range.start.row;
10316 while row <= range.end.row {
10317 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
10318 found = true;
10319 row = crease.range().end.row + 1;
10320 to_fold.push(crease);
10321 } else {
10322 row += 1
10323 }
10324 }
10325 if found {
10326 continue;
10327 }
10328 }
10329
10330 for row in (0..=range.start.row).rev() {
10331 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
10332 if crease.range().end.row >= buffer_start_row {
10333 to_fold.push(crease);
10334 if row <= range.start.row {
10335 break;
10336 }
10337 }
10338 }
10339 }
10340 }
10341
10342 self.fold_creases(to_fold, true, cx);
10343 }
10344
10345 fn fold_at_level(&mut self, fold_at: &FoldAtLevel, cx: &mut ViewContext<Self>) {
10346 if !self.buffer.read(cx).is_singleton() {
10347 return;
10348 }
10349
10350 let fold_at_level = fold_at.level;
10351 let snapshot = self.buffer.read(cx).snapshot(cx);
10352 let mut to_fold = Vec::new();
10353 let mut stack = vec![(0, snapshot.max_row().0, 1)];
10354
10355 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
10356 while start_row < end_row {
10357 match self
10358 .snapshot(cx)
10359 .crease_for_buffer_row(MultiBufferRow(start_row))
10360 {
10361 Some(crease) => {
10362 let nested_start_row = crease.range().start.row + 1;
10363 let nested_end_row = crease.range().end.row;
10364
10365 if current_level < fold_at_level {
10366 stack.push((nested_start_row, nested_end_row, current_level + 1));
10367 } else if current_level == fold_at_level {
10368 to_fold.push(crease);
10369 }
10370
10371 start_row = nested_end_row + 1;
10372 }
10373 None => start_row += 1,
10374 }
10375 }
10376 }
10377
10378 self.fold_creases(to_fold, true, cx);
10379 }
10380
10381 pub fn fold_all(&mut self, _: &actions::FoldAll, cx: &mut ViewContext<Self>) {
10382 if !self.buffer.read(cx).is_singleton() {
10383 return;
10384 }
10385
10386 let mut fold_ranges = Vec::new();
10387 let snapshot = self.buffer.read(cx).snapshot(cx);
10388
10389 for row in 0..snapshot.max_row().0 {
10390 if let Some(foldable_range) =
10391 self.snapshot(cx).crease_for_buffer_row(MultiBufferRow(row))
10392 {
10393 fold_ranges.push(foldable_range);
10394 }
10395 }
10396
10397 self.fold_creases(fold_ranges, true, cx);
10398 }
10399
10400 pub fn fold_function_bodies(
10401 &mut self,
10402 _: &actions::FoldFunctionBodies,
10403 cx: &mut ViewContext<Self>,
10404 ) {
10405 let snapshot = self.buffer.read(cx).snapshot(cx);
10406 let Some((_, _, buffer)) = snapshot.as_singleton() else {
10407 return;
10408 };
10409 let creases = buffer
10410 .function_body_fold_ranges(0..buffer.len())
10411 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
10412 .collect();
10413
10414 self.fold_creases(creases, true, cx);
10415 }
10416
10417 pub fn fold_recursive(&mut self, _: &actions::FoldRecursive, cx: &mut ViewContext<Self>) {
10418 let mut to_fold = Vec::new();
10419 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10420 let selections = self.selections.all_adjusted(cx);
10421
10422 for selection in selections {
10423 let range = selection.range().sorted();
10424 let buffer_start_row = range.start.row;
10425
10426 if range.start.row != range.end.row {
10427 let mut found = false;
10428 for row in range.start.row..=range.end.row {
10429 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
10430 found = true;
10431 to_fold.push(crease);
10432 }
10433 }
10434 if found {
10435 continue;
10436 }
10437 }
10438
10439 for row in (0..=range.start.row).rev() {
10440 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
10441 if crease.range().end.row >= buffer_start_row {
10442 to_fold.push(crease);
10443 } else {
10444 break;
10445 }
10446 }
10447 }
10448 }
10449
10450 self.fold_creases(to_fold, true, cx);
10451 }
10452
10453 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
10454 let buffer_row = fold_at.buffer_row;
10455 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10456
10457 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
10458 let autoscroll = self
10459 .selections
10460 .all::<Point>(cx)
10461 .iter()
10462 .any(|selection| crease.range().overlaps(&selection.range()));
10463
10464 self.fold_creases(vec![crease], autoscroll, cx);
10465 }
10466 }
10467
10468 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
10469 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10470 let buffer = &display_map.buffer_snapshot;
10471 let selections = self.selections.all::<Point>(cx);
10472 let ranges = selections
10473 .iter()
10474 .map(|s| {
10475 let range = s.display_range(&display_map).sorted();
10476 let mut start = range.start.to_point(&display_map);
10477 let mut end = range.end.to_point(&display_map);
10478 start.column = 0;
10479 end.column = buffer.line_len(MultiBufferRow(end.row));
10480 start..end
10481 })
10482 .collect::<Vec<_>>();
10483
10484 self.unfold_ranges(&ranges, true, true, cx);
10485 }
10486
10487 pub fn unfold_recursive(&mut self, _: &UnfoldRecursive, cx: &mut ViewContext<Self>) {
10488 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10489 let selections = self.selections.all::<Point>(cx);
10490 let ranges = selections
10491 .iter()
10492 .map(|s| {
10493 let mut range = s.display_range(&display_map).sorted();
10494 *range.start.column_mut() = 0;
10495 *range.end.column_mut() = display_map.line_len(range.end.row());
10496 let start = range.start.to_point(&display_map);
10497 let end = range.end.to_point(&display_map);
10498 start..end
10499 })
10500 .collect::<Vec<_>>();
10501
10502 self.unfold_ranges(&ranges, true, true, cx);
10503 }
10504
10505 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
10506 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10507
10508 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
10509 ..Point::new(
10510 unfold_at.buffer_row.0,
10511 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
10512 );
10513
10514 let autoscroll = self
10515 .selections
10516 .all::<Point>(cx)
10517 .iter()
10518 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
10519
10520 self.unfold_ranges(&[intersection_range], true, autoscroll, cx)
10521 }
10522
10523 pub fn unfold_all(&mut self, _: &actions::UnfoldAll, cx: &mut ViewContext<Self>) {
10524 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10525 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
10526 }
10527
10528 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
10529 let selections = self.selections.all::<Point>(cx);
10530 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10531 let line_mode = self.selections.line_mode;
10532 let ranges = selections
10533 .into_iter()
10534 .map(|s| {
10535 if line_mode {
10536 let start = Point::new(s.start.row, 0);
10537 let end = Point::new(
10538 s.end.row,
10539 display_map
10540 .buffer_snapshot
10541 .line_len(MultiBufferRow(s.end.row)),
10542 );
10543 Crease::simple(start..end, display_map.fold_placeholder.clone())
10544 } else {
10545 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
10546 }
10547 })
10548 .collect::<Vec<_>>();
10549 self.fold_creases(ranges, true, cx);
10550 }
10551
10552 pub fn fold_creases<T: ToOffset + Clone>(
10553 &mut self,
10554 creases: Vec<Crease<T>>,
10555 auto_scroll: bool,
10556 cx: &mut ViewContext<Self>,
10557 ) {
10558 if creases.is_empty() {
10559 return;
10560 }
10561
10562 let mut buffers_affected = HashSet::default();
10563 let multi_buffer = self.buffer().read(cx);
10564 for crease in &creases {
10565 if let Some((_, buffer, _)) =
10566 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
10567 {
10568 buffers_affected.insert(buffer.read(cx).remote_id());
10569 };
10570 }
10571
10572 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
10573
10574 if auto_scroll {
10575 self.request_autoscroll(Autoscroll::fit(), cx);
10576 }
10577
10578 for buffer_id in buffers_affected {
10579 Self::sync_expanded_diff_hunks(&mut self.diff_map, buffer_id, cx);
10580 }
10581
10582 cx.notify();
10583
10584 if let Some(active_diagnostics) = self.active_diagnostics.take() {
10585 // Clear diagnostics block when folding a range that contains it.
10586 let snapshot = self.snapshot(cx);
10587 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
10588 drop(snapshot);
10589 self.active_diagnostics = Some(active_diagnostics);
10590 self.dismiss_diagnostics(cx);
10591 } else {
10592 self.active_diagnostics = Some(active_diagnostics);
10593 }
10594 }
10595
10596 self.scrollbar_marker_state.dirty = true;
10597 }
10598
10599 /// Removes any folds whose ranges intersect any of the given ranges.
10600 pub fn unfold_ranges<T: ToOffset + Clone>(
10601 &mut self,
10602 ranges: &[Range<T>],
10603 inclusive: bool,
10604 auto_scroll: bool,
10605 cx: &mut ViewContext<Self>,
10606 ) {
10607 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
10608 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
10609 });
10610 }
10611
10612 /// Removes any folds with the given ranges.
10613 pub fn remove_folds_with_type<T: ToOffset + Clone>(
10614 &mut self,
10615 ranges: &[Range<T>],
10616 type_id: TypeId,
10617 auto_scroll: bool,
10618 cx: &mut ViewContext<Self>,
10619 ) {
10620 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
10621 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
10622 });
10623 }
10624
10625 fn remove_folds_with<T: ToOffset + Clone>(
10626 &mut self,
10627 ranges: &[Range<T>],
10628 auto_scroll: bool,
10629 cx: &mut ViewContext<Self>,
10630 update: impl FnOnce(&mut DisplayMap, &mut ModelContext<DisplayMap>),
10631 ) {
10632 if ranges.is_empty() {
10633 return;
10634 }
10635
10636 let mut buffers_affected = HashSet::default();
10637 let multi_buffer = self.buffer().read(cx);
10638 for range in ranges {
10639 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
10640 buffers_affected.insert(buffer.read(cx).remote_id());
10641 };
10642 }
10643
10644 self.display_map.update(cx, update);
10645
10646 if auto_scroll {
10647 self.request_autoscroll(Autoscroll::fit(), cx);
10648 }
10649
10650 for buffer_id in buffers_affected {
10651 Self::sync_expanded_diff_hunks(&mut self.diff_map, buffer_id, cx);
10652 }
10653
10654 cx.notify();
10655 self.scrollbar_marker_state.dirty = true;
10656 self.active_indent_guides_state.dirty = true;
10657 }
10658
10659 pub fn default_fold_placeholder(&self, cx: &AppContext) -> FoldPlaceholder {
10660 self.display_map.read(cx).fold_placeholder.clone()
10661 }
10662
10663 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
10664 if hovered != self.gutter_hovered {
10665 self.gutter_hovered = hovered;
10666 cx.notify();
10667 }
10668 }
10669
10670 pub fn insert_blocks(
10671 &mut self,
10672 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
10673 autoscroll: Option<Autoscroll>,
10674 cx: &mut ViewContext<Self>,
10675 ) -> Vec<CustomBlockId> {
10676 let blocks = self
10677 .display_map
10678 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
10679 if let Some(autoscroll) = autoscroll {
10680 self.request_autoscroll(autoscroll, cx);
10681 }
10682 cx.notify();
10683 blocks
10684 }
10685
10686 pub fn resize_blocks(
10687 &mut self,
10688 heights: HashMap<CustomBlockId, u32>,
10689 autoscroll: Option<Autoscroll>,
10690 cx: &mut ViewContext<Self>,
10691 ) {
10692 self.display_map
10693 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
10694 if let Some(autoscroll) = autoscroll {
10695 self.request_autoscroll(autoscroll, cx);
10696 }
10697 cx.notify();
10698 }
10699
10700 pub fn replace_blocks(
10701 &mut self,
10702 renderers: HashMap<CustomBlockId, RenderBlock>,
10703 autoscroll: Option<Autoscroll>,
10704 cx: &mut ViewContext<Self>,
10705 ) {
10706 self.display_map
10707 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
10708 if let Some(autoscroll) = autoscroll {
10709 self.request_autoscroll(autoscroll, cx);
10710 }
10711 cx.notify();
10712 }
10713
10714 pub fn remove_blocks(
10715 &mut self,
10716 block_ids: HashSet<CustomBlockId>,
10717 autoscroll: Option<Autoscroll>,
10718 cx: &mut ViewContext<Self>,
10719 ) {
10720 self.display_map.update(cx, |display_map, cx| {
10721 display_map.remove_blocks(block_ids, cx)
10722 });
10723 if let Some(autoscroll) = autoscroll {
10724 self.request_autoscroll(autoscroll, cx);
10725 }
10726 cx.notify();
10727 }
10728
10729 pub fn row_for_block(
10730 &self,
10731 block_id: CustomBlockId,
10732 cx: &mut ViewContext<Self>,
10733 ) -> Option<DisplayRow> {
10734 self.display_map
10735 .update(cx, |map, cx| map.row_for_block(block_id, cx))
10736 }
10737
10738 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
10739 self.focused_block = Some(focused_block);
10740 }
10741
10742 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
10743 self.focused_block.take()
10744 }
10745
10746 pub fn insert_creases(
10747 &mut self,
10748 creases: impl IntoIterator<Item = Crease<Anchor>>,
10749 cx: &mut ViewContext<Self>,
10750 ) -> Vec<CreaseId> {
10751 self.display_map
10752 .update(cx, |map, cx| map.insert_creases(creases, cx))
10753 }
10754
10755 pub fn remove_creases(
10756 &mut self,
10757 ids: impl IntoIterator<Item = CreaseId>,
10758 cx: &mut ViewContext<Self>,
10759 ) {
10760 self.display_map
10761 .update(cx, |map, cx| map.remove_creases(ids, cx));
10762 }
10763
10764 pub fn longest_row(&self, cx: &mut AppContext) -> DisplayRow {
10765 self.display_map
10766 .update(cx, |map, cx| map.snapshot(cx))
10767 .longest_row()
10768 }
10769
10770 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
10771 self.display_map
10772 .update(cx, |map, cx| map.snapshot(cx))
10773 .max_point()
10774 }
10775
10776 pub fn text(&self, cx: &AppContext) -> String {
10777 self.buffer.read(cx).read(cx).text()
10778 }
10779
10780 pub fn text_option(&self, cx: &AppContext) -> Option<String> {
10781 let text = self.text(cx);
10782 let text = text.trim();
10783
10784 if text.is_empty() {
10785 return None;
10786 }
10787
10788 Some(text.to_string())
10789 }
10790
10791 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
10792 self.transact(cx, |this, cx| {
10793 this.buffer
10794 .read(cx)
10795 .as_singleton()
10796 .expect("you can only call set_text on editors for singleton buffers")
10797 .update(cx, |buffer, cx| buffer.set_text(text, cx));
10798 });
10799 }
10800
10801 pub fn display_text(&self, cx: &mut AppContext) -> String {
10802 self.display_map
10803 .update(cx, |map, cx| map.snapshot(cx))
10804 .text()
10805 }
10806
10807 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
10808 let mut wrap_guides = smallvec::smallvec![];
10809
10810 if self.show_wrap_guides == Some(false) {
10811 return wrap_guides;
10812 }
10813
10814 let settings = self.buffer.read(cx).settings_at(0, cx);
10815 if settings.show_wrap_guides {
10816 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
10817 wrap_guides.push((soft_wrap as usize, true));
10818 } else if let SoftWrap::Bounded(soft_wrap) = self.soft_wrap_mode(cx) {
10819 wrap_guides.push((soft_wrap as usize, true));
10820 }
10821 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
10822 }
10823
10824 wrap_guides
10825 }
10826
10827 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
10828 let settings = self.buffer.read(cx).settings_at(0, cx);
10829 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
10830 match mode {
10831 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
10832 SoftWrap::None
10833 }
10834 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
10835 language_settings::SoftWrap::PreferredLineLength => {
10836 SoftWrap::Column(settings.preferred_line_length)
10837 }
10838 language_settings::SoftWrap::Bounded => {
10839 SoftWrap::Bounded(settings.preferred_line_length)
10840 }
10841 }
10842 }
10843
10844 pub fn set_soft_wrap_mode(
10845 &mut self,
10846 mode: language_settings::SoftWrap,
10847 cx: &mut ViewContext<Self>,
10848 ) {
10849 self.soft_wrap_mode_override = Some(mode);
10850 cx.notify();
10851 }
10852
10853 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
10854 self.text_style_refinement = Some(style);
10855 }
10856
10857 /// called by the Element so we know what style we were most recently rendered with.
10858 pub(crate) fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
10859 let rem_size = cx.rem_size();
10860 self.display_map.update(cx, |map, cx| {
10861 map.set_font(
10862 style.text.font(),
10863 style.text.font_size.to_pixels(rem_size),
10864 cx,
10865 )
10866 });
10867 self.style = Some(style);
10868 }
10869
10870 pub fn style(&self) -> Option<&EditorStyle> {
10871 self.style.as_ref()
10872 }
10873
10874 // Called by the element. This method is not designed to be called outside of the editor
10875 // element's layout code because it does not notify when rewrapping is computed synchronously.
10876 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
10877 self.display_map
10878 .update(cx, |map, cx| map.set_wrap_width(width, cx))
10879 }
10880
10881 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
10882 if self.soft_wrap_mode_override.is_some() {
10883 self.soft_wrap_mode_override.take();
10884 } else {
10885 let soft_wrap = match self.soft_wrap_mode(cx) {
10886 SoftWrap::GitDiff => return,
10887 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
10888 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
10889 language_settings::SoftWrap::None
10890 }
10891 };
10892 self.soft_wrap_mode_override = Some(soft_wrap);
10893 }
10894 cx.notify();
10895 }
10896
10897 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, cx: &mut ViewContext<Self>) {
10898 let Some(workspace) = self.workspace() else {
10899 return;
10900 };
10901 let fs = workspace.read(cx).app_state().fs.clone();
10902 let current_show = TabBarSettings::get_global(cx).show;
10903 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
10904 setting.show = Some(!current_show);
10905 });
10906 }
10907
10908 pub fn toggle_indent_guides(&mut self, _: &ToggleIndentGuides, cx: &mut ViewContext<Self>) {
10909 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
10910 self.buffer
10911 .read(cx)
10912 .settings_at(0, cx)
10913 .indent_guides
10914 .enabled
10915 });
10916 self.show_indent_guides = Some(!currently_enabled);
10917 cx.notify();
10918 }
10919
10920 fn should_show_indent_guides(&self) -> Option<bool> {
10921 self.show_indent_guides
10922 }
10923
10924 pub fn toggle_line_numbers(&mut self, _: &ToggleLineNumbers, cx: &mut ViewContext<Self>) {
10925 let mut editor_settings = EditorSettings::get_global(cx).clone();
10926 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
10927 EditorSettings::override_global(editor_settings, cx);
10928 }
10929
10930 pub fn should_use_relative_line_numbers(&self, cx: &WindowContext) -> bool {
10931 self.use_relative_line_numbers
10932 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
10933 }
10934
10935 pub fn toggle_relative_line_numbers(
10936 &mut self,
10937 _: &ToggleRelativeLineNumbers,
10938 cx: &mut ViewContext<Self>,
10939 ) {
10940 let is_relative = self.should_use_relative_line_numbers(cx);
10941 self.set_relative_line_number(Some(!is_relative), cx)
10942 }
10943
10944 pub fn set_relative_line_number(
10945 &mut self,
10946 is_relative: Option<bool>,
10947 cx: &mut ViewContext<Self>,
10948 ) {
10949 self.use_relative_line_numbers = is_relative;
10950 cx.notify();
10951 }
10952
10953 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
10954 self.show_gutter = show_gutter;
10955 cx.notify();
10956 }
10957
10958 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut ViewContext<Self>) {
10959 self.show_line_numbers = Some(show_line_numbers);
10960 cx.notify();
10961 }
10962
10963 pub fn set_show_git_diff_gutter(
10964 &mut self,
10965 show_git_diff_gutter: bool,
10966 cx: &mut ViewContext<Self>,
10967 ) {
10968 self.show_git_diff_gutter = Some(show_git_diff_gutter);
10969 cx.notify();
10970 }
10971
10972 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut ViewContext<Self>) {
10973 self.show_code_actions = Some(show_code_actions);
10974 cx.notify();
10975 }
10976
10977 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut ViewContext<Self>) {
10978 self.show_runnables = Some(show_runnables);
10979 cx.notify();
10980 }
10981
10982 pub fn set_masked(&mut self, masked: bool, cx: &mut ViewContext<Self>) {
10983 if self.display_map.read(cx).masked != masked {
10984 self.display_map.update(cx, |map, _| map.masked = masked);
10985 }
10986 cx.notify()
10987 }
10988
10989 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut ViewContext<Self>) {
10990 self.show_wrap_guides = Some(show_wrap_guides);
10991 cx.notify();
10992 }
10993
10994 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut ViewContext<Self>) {
10995 self.show_indent_guides = Some(show_indent_guides);
10996 cx.notify();
10997 }
10998
10999 pub fn working_directory(&self, cx: &WindowContext) -> Option<PathBuf> {
11000 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
11001 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
11002 if let Some(dir) = file.abs_path(cx).parent() {
11003 return Some(dir.to_owned());
11004 }
11005 }
11006
11007 if let Some(project_path) = buffer.read(cx).project_path(cx) {
11008 return Some(project_path.path.to_path_buf());
11009 }
11010 }
11011
11012 None
11013 }
11014
11015 fn target_file<'a>(&self, cx: &'a AppContext) -> Option<&'a dyn language::LocalFile> {
11016 self.active_excerpt(cx)?
11017 .1
11018 .read(cx)
11019 .file()
11020 .and_then(|f| f.as_local())
11021 }
11022
11023 pub fn reveal_in_finder(&mut self, _: &RevealInFileManager, cx: &mut ViewContext<Self>) {
11024 if let Some(target) = self.target_file(cx) {
11025 cx.reveal_path(&target.abs_path(cx));
11026 }
11027 }
11028
11029 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
11030 if let Some(file) = self.target_file(cx) {
11031 if let Some(path) = file.abs_path(cx).to_str() {
11032 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
11033 }
11034 }
11035 }
11036
11037 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
11038 if let Some(file) = self.target_file(cx) {
11039 if let Some(path) = file.path().to_str() {
11040 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
11041 }
11042 }
11043 }
11044
11045 pub fn toggle_git_blame(&mut self, _: &ToggleGitBlame, cx: &mut ViewContext<Self>) {
11046 self.show_git_blame_gutter = !self.show_git_blame_gutter;
11047
11048 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
11049 self.start_git_blame(true, cx);
11050 }
11051
11052 cx.notify();
11053 }
11054
11055 pub fn toggle_git_blame_inline(
11056 &mut self,
11057 _: &ToggleGitBlameInline,
11058 cx: &mut ViewContext<Self>,
11059 ) {
11060 self.toggle_git_blame_inline_internal(true, cx);
11061 cx.notify();
11062 }
11063
11064 pub fn git_blame_inline_enabled(&self) -> bool {
11065 self.git_blame_inline_enabled
11066 }
11067
11068 pub fn toggle_selection_menu(&mut self, _: &ToggleSelectionMenu, cx: &mut ViewContext<Self>) {
11069 self.show_selection_menu = self
11070 .show_selection_menu
11071 .map(|show_selections_menu| !show_selections_menu)
11072 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
11073
11074 cx.notify();
11075 }
11076
11077 pub fn selection_menu_enabled(&self, cx: &AppContext) -> bool {
11078 self.show_selection_menu
11079 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
11080 }
11081
11082 fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
11083 if let Some(project) = self.project.as_ref() {
11084 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
11085 return;
11086 };
11087
11088 if buffer.read(cx).file().is_none() {
11089 return;
11090 }
11091
11092 let focused = self.focus_handle(cx).contains_focused(cx);
11093
11094 let project = project.clone();
11095 let blame =
11096 cx.new_model(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
11097 self.blame_subscription = Some(cx.observe(&blame, |_, _, cx| cx.notify()));
11098 self.blame = Some(blame);
11099 }
11100 }
11101
11102 fn toggle_git_blame_inline_internal(
11103 &mut self,
11104 user_triggered: bool,
11105 cx: &mut ViewContext<Self>,
11106 ) {
11107 if self.git_blame_inline_enabled {
11108 self.git_blame_inline_enabled = false;
11109 self.show_git_blame_inline = false;
11110 self.show_git_blame_inline_delay_task.take();
11111 } else {
11112 self.git_blame_inline_enabled = true;
11113 self.start_git_blame_inline(user_triggered, cx);
11114 }
11115
11116 cx.notify();
11117 }
11118
11119 fn start_git_blame_inline(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
11120 self.start_git_blame(user_triggered, cx);
11121
11122 if ProjectSettings::get_global(cx)
11123 .git
11124 .inline_blame_delay()
11125 .is_some()
11126 {
11127 self.start_inline_blame_timer(cx);
11128 } else {
11129 self.show_git_blame_inline = true
11130 }
11131 }
11132
11133 pub fn blame(&self) -> Option<&Model<GitBlame>> {
11134 self.blame.as_ref()
11135 }
11136
11137 pub fn show_git_blame_gutter(&self) -> bool {
11138 self.show_git_blame_gutter
11139 }
11140
11141 pub fn render_git_blame_gutter(&mut self, cx: &mut WindowContext) -> bool {
11142 self.show_git_blame_gutter && self.has_blame_entries(cx)
11143 }
11144
11145 pub fn render_git_blame_inline(&mut self, cx: &mut WindowContext) -> bool {
11146 self.show_git_blame_inline
11147 && self.focus_handle.is_focused(cx)
11148 && !self.newest_selection_head_on_empty_line(cx)
11149 && self.has_blame_entries(cx)
11150 }
11151
11152 fn has_blame_entries(&self, cx: &mut WindowContext) -> bool {
11153 self.blame()
11154 .map_or(false, |blame| blame.read(cx).has_generated_entries())
11155 }
11156
11157 fn newest_selection_head_on_empty_line(&mut self, cx: &mut WindowContext) -> bool {
11158 let cursor_anchor = self.selections.newest_anchor().head();
11159
11160 let snapshot = self.buffer.read(cx).snapshot(cx);
11161 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
11162
11163 snapshot.line_len(buffer_row) == 0
11164 }
11165
11166 fn get_permalink_to_line(&mut self, cx: &mut ViewContext<Self>) -> Task<Result<url::Url>> {
11167 let buffer_and_selection = maybe!({
11168 let selection = self.selections.newest::<Point>(cx);
11169 let selection_range = selection.range();
11170
11171 let (buffer, selection) = if let Some(buffer) = self.buffer().read(cx).as_singleton() {
11172 (buffer, selection_range.start.row..selection_range.end.row)
11173 } else {
11174 let buffer_ranges = self
11175 .buffer()
11176 .read(cx)
11177 .range_to_buffer_ranges(selection_range, cx);
11178
11179 let (buffer, range, _) = if selection.reversed {
11180 buffer_ranges.first()
11181 } else {
11182 buffer_ranges.last()
11183 }?;
11184
11185 let snapshot = buffer.read(cx).snapshot();
11186 let selection = text::ToPoint::to_point(&range.start, &snapshot).row
11187 ..text::ToPoint::to_point(&range.end, &snapshot).row;
11188 (buffer.clone(), selection)
11189 };
11190
11191 Some((buffer, selection))
11192 });
11193
11194 let Some((buffer, selection)) = buffer_and_selection else {
11195 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
11196 };
11197
11198 let Some(project) = self.project.as_ref() else {
11199 return Task::ready(Err(anyhow!("editor does not have project")));
11200 };
11201
11202 project.update(cx, |project, cx| {
11203 project.get_permalink_to_line(&buffer, selection, cx)
11204 })
11205 }
11206
11207 pub fn copy_permalink_to_line(&mut self, _: &CopyPermalinkToLine, cx: &mut ViewContext<Self>) {
11208 let permalink_task = self.get_permalink_to_line(cx);
11209 let workspace = self.workspace();
11210
11211 cx.spawn(|_, mut cx| async move {
11212 match permalink_task.await {
11213 Ok(permalink) => {
11214 cx.update(|cx| {
11215 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
11216 })
11217 .ok();
11218 }
11219 Err(err) => {
11220 let message = format!("Failed to copy permalink: {err}");
11221
11222 Err::<(), anyhow::Error>(err).log_err();
11223
11224 if let Some(workspace) = workspace {
11225 workspace
11226 .update(&mut cx, |workspace, cx| {
11227 struct CopyPermalinkToLine;
11228
11229 workspace.show_toast(
11230 Toast::new(
11231 NotificationId::unique::<CopyPermalinkToLine>(),
11232 message,
11233 ),
11234 cx,
11235 )
11236 })
11237 .ok();
11238 }
11239 }
11240 }
11241 })
11242 .detach();
11243 }
11244
11245 pub fn copy_file_location(&mut self, _: &CopyFileLocation, cx: &mut ViewContext<Self>) {
11246 let selection = self.selections.newest::<Point>(cx).start.row + 1;
11247 if let Some(file) = self.target_file(cx) {
11248 if let Some(path) = file.path().to_str() {
11249 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
11250 }
11251 }
11252 }
11253
11254 pub fn open_permalink_to_line(&mut self, _: &OpenPermalinkToLine, cx: &mut ViewContext<Self>) {
11255 let permalink_task = self.get_permalink_to_line(cx);
11256 let workspace = self.workspace();
11257
11258 cx.spawn(|_, mut cx| async move {
11259 match permalink_task.await {
11260 Ok(permalink) => {
11261 cx.update(|cx| {
11262 cx.open_url(permalink.as_ref());
11263 })
11264 .ok();
11265 }
11266 Err(err) => {
11267 let message = format!("Failed to open permalink: {err}");
11268
11269 Err::<(), anyhow::Error>(err).log_err();
11270
11271 if let Some(workspace) = workspace {
11272 workspace
11273 .update(&mut cx, |workspace, cx| {
11274 struct OpenPermalinkToLine;
11275
11276 workspace.show_toast(
11277 Toast::new(
11278 NotificationId::unique::<OpenPermalinkToLine>(),
11279 message,
11280 ),
11281 cx,
11282 )
11283 })
11284 .ok();
11285 }
11286 }
11287 }
11288 })
11289 .detach();
11290 }
11291
11292 pub fn insert_uuid_v4(&mut self, _: &InsertUuidV4, cx: &mut ViewContext<Self>) {
11293 self.insert_uuid(UuidVersion::V4, cx);
11294 }
11295
11296 pub fn insert_uuid_v7(&mut self, _: &InsertUuidV7, cx: &mut ViewContext<Self>) {
11297 self.insert_uuid(UuidVersion::V7, cx);
11298 }
11299
11300 fn insert_uuid(&mut self, version: UuidVersion, cx: &mut ViewContext<Self>) {
11301 self.transact(cx, |this, cx| {
11302 let edits = this
11303 .selections
11304 .all::<Point>(cx)
11305 .into_iter()
11306 .map(|selection| {
11307 let uuid = match version {
11308 UuidVersion::V4 => uuid::Uuid::new_v4(),
11309 UuidVersion::V7 => uuid::Uuid::now_v7(),
11310 };
11311
11312 (selection.range(), uuid.to_string())
11313 });
11314 this.edit(edits, cx);
11315 this.refresh_inline_completion(true, false, cx);
11316 });
11317 }
11318
11319 /// Adds a row highlight for the given range. If a row has multiple highlights, the
11320 /// last highlight added will be used.
11321 ///
11322 /// If the range ends at the beginning of a line, then that line will not be highlighted.
11323 pub fn highlight_rows<T: 'static>(
11324 &mut self,
11325 range: Range<Anchor>,
11326 color: Hsla,
11327 should_autoscroll: bool,
11328 cx: &mut ViewContext<Self>,
11329 ) {
11330 let snapshot = self.buffer().read(cx).snapshot(cx);
11331 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
11332 let ix = row_highlights.binary_search_by(|highlight| {
11333 Ordering::Equal
11334 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
11335 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
11336 });
11337
11338 if let Err(mut ix) = ix {
11339 let index = post_inc(&mut self.highlight_order);
11340
11341 // If this range intersects with the preceding highlight, then merge it with
11342 // the preceding highlight. Otherwise insert a new highlight.
11343 let mut merged = false;
11344 if ix > 0 {
11345 let prev_highlight = &mut row_highlights[ix - 1];
11346 if prev_highlight
11347 .range
11348 .end
11349 .cmp(&range.start, &snapshot)
11350 .is_ge()
11351 {
11352 ix -= 1;
11353 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
11354 prev_highlight.range.end = range.end;
11355 }
11356 merged = true;
11357 prev_highlight.index = index;
11358 prev_highlight.color = color;
11359 prev_highlight.should_autoscroll = should_autoscroll;
11360 }
11361 }
11362
11363 if !merged {
11364 row_highlights.insert(
11365 ix,
11366 RowHighlight {
11367 range: range.clone(),
11368 index,
11369 color,
11370 should_autoscroll,
11371 },
11372 );
11373 }
11374
11375 // If any of the following highlights intersect with this one, merge them.
11376 while let Some(next_highlight) = row_highlights.get(ix + 1) {
11377 let highlight = &row_highlights[ix];
11378 if next_highlight
11379 .range
11380 .start
11381 .cmp(&highlight.range.end, &snapshot)
11382 .is_le()
11383 {
11384 if next_highlight
11385 .range
11386 .end
11387 .cmp(&highlight.range.end, &snapshot)
11388 .is_gt()
11389 {
11390 row_highlights[ix].range.end = next_highlight.range.end;
11391 }
11392 row_highlights.remove(ix + 1);
11393 } else {
11394 break;
11395 }
11396 }
11397 }
11398 }
11399
11400 /// Remove any highlighted row ranges of the given type that intersect the
11401 /// given ranges.
11402 pub fn remove_highlighted_rows<T: 'static>(
11403 &mut self,
11404 ranges_to_remove: Vec<Range<Anchor>>,
11405 cx: &mut ViewContext<Self>,
11406 ) {
11407 let snapshot = self.buffer().read(cx).snapshot(cx);
11408 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
11409 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
11410 row_highlights.retain(|highlight| {
11411 while let Some(range_to_remove) = ranges_to_remove.peek() {
11412 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
11413 Ordering::Less | Ordering::Equal => {
11414 ranges_to_remove.next();
11415 }
11416 Ordering::Greater => {
11417 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
11418 Ordering::Less | Ordering::Equal => {
11419 return false;
11420 }
11421 Ordering::Greater => break,
11422 }
11423 }
11424 }
11425 }
11426
11427 true
11428 })
11429 }
11430
11431 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
11432 pub fn clear_row_highlights<T: 'static>(&mut self) {
11433 self.highlighted_rows.remove(&TypeId::of::<T>());
11434 }
11435
11436 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
11437 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
11438 self.highlighted_rows
11439 .get(&TypeId::of::<T>())
11440 .map_or(&[] as &[_], |vec| vec.as_slice())
11441 .iter()
11442 .map(|highlight| (highlight.range.clone(), highlight.color))
11443 }
11444
11445 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
11446 /// Rerturns a map of display rows that are highlighted and their corresponding highlight color.
11447 /// Allows to ignore certain kinds of highlights.
11448 pub fn highlighted_display_rows(
11449 &mut self,
11450 cx: &mut WindowContext,
11451 ) -> BTreeMap<DisplayRow, Hsla> {
11452 let snapshot = self.snapshot(cx);
11453 let mut used_highlight_orders = HashMap::default();
11454 self.highlighted_rows
11455 .iter()
11456 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
11457 .fold(
11458 BTreeMap::<DisplayRow, Hsla>::new(),
11459 |mut unique_rows, highlight| {
11460 let start = highlight.range.start.to_display_point(&snapshot);
11461 let end = highlight.range.end.to_display_point(&snapshot);
11462 let start_row = start.row().0;
11463 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
11464 && end.column() == 0
11465 {
11466 end.row().0.saturating_sub(1)
11467 } else {
11468 end.row().0
11469 };
11470 for row in start_row..=end_row {
11471 let used_index =
11472 used_highlight_orders.entry(row).or_insert(highlight.index);
11473 if highlight.index >= *used_index {
11474 *used_index = highlight.index;
11475 unique_rows.insert(DisplayRow(row), highlight.color);
11476 }
11477 }
11478 unique_rows
11479 },
11480 )
11481 }
11482
11483 pub fn highlighted_display_row_for_autoscroll(
11484 &self,
11485 snapshot: &DisplaySnapshot,
11486 ) -> Option<DisplayRow> {
11487 self.highlighted_rows
11488 .values()
11489 .flat_map(|highlighted_rows| highlighted_rows.iter())
11490 .filter_map(|highlight| {
11491 if highlight.should_autoscroll {
11492 Some(highlight.range.start.to_display_point(snapshot).row())
11493 } else {
11494 None
11495 }
11496 })
11497 .min()
11498 }
11499
11500 pub fn set_search_within_ranges(
11501 &mut self,
11502 ranges: &[Range<Anchor>],
11503 cx: &mut ViewContext<Self>,
11504 ) {
11505 self.highlight_background::<SearchWithinRange>(
11506 ranges,
11507 |colors| colors.editor_document_highlight_read_background,
11508 cx,
11509 )
11510 }
11511
11512 pub fn set_breadcrumb_header(&mut self, new_header: String) {
11513 self.breadcrumb_header = Some(new_header);
11514 }
11515
11516 pub fn clear_search_within_ranges(&mut self, cx: &mut ViewContext<Self>) {
11517 self.clear_background_highlights::<SearchWithinRange>(cx);
11518 }
11519
11520 pub fn highlight_background<T: 'static>(
11521 &mut self,
11522 ranges: &[Range<Anchor>],
11523 color_fetcher: fn(&ThemeColors) -> Hsla,
11524 cx: &mut ViewContext<Self>,
11525 ) {
11526 self.background_highlights
11527 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
11528 self.scrollbar_marker_state.dirty = true;
11529 cx.notify();
11530 }
11531
11532 pub fn clear_background_highlights<T: 'static>(
11533 &mut self,
11534 cx: &mut ViewContext<Self>,
11535 ) -> Option<BackgroundHighlight> {
11536 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
11537 if !text_highlights.1.is_empty() {
11538 self.scrollbar_marker_state.dirty = true;
11539 cx.notify();
11540 }
11541 Some(text_highlights)
11542 }
11543
11544 pub fn highlight_gutter<T: 'static>(
11545 &mut self,
11546 ranges: &[Range<Anchor>],
11547 color_fetcher: fn(&AppContext) -> Hsla,
11548 cx: &mut ViewContext<Self>,
11549 ) {
11550 self.gutter_highlights
11551 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
11552 cx.notify();
11553 }
11554
11555 pub fn clear_gutter_highlights<T: 'static>(
11556 &mut self,
11557 cx: &mut ViewContext<Self>,
11558 ) -> Option<GutterHighlight> {
11559 cx.notify();
11560 self.gutter_highlights.remove(&TypeId::of::<T>())
11561 }
11562
11563 #[cfg(feature = "test-support")]
11564 pub fn all_text_background_highlights(
11565 &mut self,
11566 cx: &mut ViewContext<Self>,
11567 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11568 let snapshot = self.snapshot(cx);
11569 let buffer = &snapshot.buffer_snapshot;
11570 let start = buffer.anchor_before(0);
11571 let end = buffer.anchor_after(buffer.len());
11572 let theme = cx.theme().colors();
11573 self.background_highlights_in_range(start..end, &snapshot, theme)
11574 }
11575
11576 #[cfg(feature = "test-support")]
11577 pub fn search_background_highlights(
11578 &mut self,
11579 cx: &mut ViewContext<Self>,
11580 ) -> Vec<Range<Point>> {
11581 let snapshot = self.buffer().read(cx).snapshot(cx);
11582
11583 let highlights = self
11584 .background_highlights
11585 .get(&TypeId::of::<items::BufferSearchHighlights>());
11586
11587 if let Some((_color, ranges)) = highlights {
11588 ranges
11589 .iter()
11590 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
11591 .collect_vec()
11592 } else {
11593 vec![]
11594 }
11595 }
11596
11597 fn document_highlights_for_position<'a>(
11598 &'a self,
11599 position: Anchor,
11600 buffer: &'a MultiBufferSnapshot,
11601 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
11602 let read_highlights = self
11603 .background_highlights
11604 .get(&TypeId::of::<DocumentHighlightRead>())
11605 .map(|h| &h.1);
11606 let write_highlights = self
11607 .background_highlights
11608 .get(&TypeId::of::<DocumentHighlightWrite>())
11609 .map(|h| &h.1);
11610 let left_position = position.bias_left(buffer);
11611 let right_position = position.bias_right(buffer);
11612 read_highlights
11613 .into_iter()
11614 .chain(write_highlights)
11615 .flat_map(move |ranges| {
11616 let start_ix = match ranges.binary_search_by(|probe| {
11617 let cmp = probe.end.cmp(&left_position, buffer);
11618 if cmp.is_ge() {
11619 Ordering::Greater
11620 } else {
11621 Ordering::Less
11622 }
11623 }) {
11624 Ok(i) | Err(i) => i,
11625 };
11626
11627 ranges[start_ix..]
11628 .iter()
11629 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
11630 })
11631 }
11632
11633 pub fn has_background_highlights<T: 'static>(&self) -> bool {
11634 self.background_highlights
11635 .get(&TypeId::of::<T>())
11636 .map_or(false, |(_, highlights)| !highlights.is_empty())
11637 }
11638
11639 pub fn background_highlights_in_range(
11640 &self,
11641 search_range: Range<Anchor>,
11642 display_snapshot: &DisplaySnapshot,
11643 theme: &ThemeColors,
11644 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11645 let mut results = Vec::new();
11646 for (color_fetcher, ranges) in self.background_highlights.values() {
11647 let color = color_fetcher(theme);
11648 let start_ix = match ranges.binary_search_by(|probe| {
11649 let cmp = probe
11650 .end
11651 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11652 if cmp.is_gt() {
11653 Ordering::Greater
11654 } else {
11655 Ordering::Less
11656 }
11657 }) {
11658 Ok(i) | Err(i) => i,
11659 };
11660 for range in &ranges[start_ix..] {
11661 if range
11662 .start
11663 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11664 .is_ge()
11665 {
11666 break;
11667 }
11668
11669 let start = range.start.to_display_point(display_snapshot);
11670 let end = range.end.to_display_point(display_snapshot);
11671 results.push((start..end, color))
11672 }
11673 }
11674 results
11675 }
11676
11677 pub fn background_highlight_row_ranges<T: 'static>(
11678 &self,
11679 search_range: Range<Anchor>,
11680 display_snapshot: &DisplaySnapshot,
11681 count: usize,
11682 ) -> Vec<RangeInclusive<DisplayPoint>> {
11683 let mut results = Vec::new();
11684 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
11685 return vec![];
11686 };
11687
11688 let start_ix = match ranges.binary_search_by(|probe| {
11689 let cmp = probe
11690 .end
11691 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11692 if cmp.is_gt() {
11693 Ordering::Greater
11694 } else {
11695 Ordering::Less
11696 }
11697 }) {
11698 Ok(i) | Err(i) => i,
11699 };
11700 let mut push_region = |start: Option<Point>, end: Option<Point>| {
11701 if let (Some(start_display), Some(end_display)) = (start, end) {
11702 results.push(
11703 start_display.to_display_point(display_snapshot)
11704 ..=end_display.to_display_point(display_snapshot),
11705 );
11706 }
11707 };
11708 let mut start_row: Option<Point> = None;
11709 let mut end_row: Option<Point> = None;
11710 if ranges.len() > count {
11711 return Vec::new();
11712 }
11713 for range in &ranges[start_ix..] {
11714 if range
11715 .start
11716 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11717 .is_ge()
11718 {
11719 break;
11720 }
11721 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
11722 if let Some(current_row) = &end_row {
11723 if end.row == current_row.row {
11724 continue;
11725 }
11726 }
11727 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
11728 if start_row.is_none() {
11729 assert_eq!(end_row, None);
11730 start_row = Some(start);
11731 end_row = Some(end);
11732 continue;
11733 }
11734 if let Some(current_end) = end_row.as_mut() {
11735 if start.row > current_end.row + 1 {
11736 push_region(start_row, end_row);
11737 start_row = Some(start);
11738 end_row = Some(end);
11739 } else {
11740 // Merge two hunks.
11741 *current_end = end;
11742 }
11743 } else {
11744 unreachable!();
11745 }
11746 }
11747 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
11748 push_region(start_row, end_row);
11749 results
11750 }
11751
11752 pub fn gutter_highlights_in_range(
11753 &self,
11754 search_range: Range<Anchor>,
11755 display_snapshot: &DisplaySnapshot,
11756 cx: &AppContext,
11757 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11758 let mut results = Vec::new();
11759 for (color_fetcher, ranges) in self.gutter_highlights.values() {
11760 let color = color_fetcher(cx);
11761 let start_ix = match ranges.binary_search_by(|probe| {
11762 let cmp = probe
11763 .end
11764 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11765 if cmp.is_gt() {
11766 Ordering::Greater
11767 } else {
11768 Ordering::Less
11769 }
11770 }) {
11771 Ok(i) | Err(i) => i,
11772 };
11773 for range in &ranges[start_ix..] {
11774 if range
11775 .start
11776 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11777 .is_ge()
11778 {
11779 break;
11780 }
11781
11782 let start = range.start.to_display_point(display_snapshot);
11783 let end = range.end.to_display_point(display_snapshot);
11784 results.push((start..end, color))
11785 }
11786 }
11787 results
11788 }
11789
11790 /// Get the text ranges corresponding to the redaction query
11791 pub fn redacted_ranges(
11792 &self,
11793 search_range: Range<Anchor>,
11794 display_snapshot: &DisplaySnapshot,
11795 cx: &WindowContext,
11796 ) -> Vec<Range<DisplayPoint>> {
11797 display_snapshot
11798 .buffer_snapshot
11799 .redacted_ranges(search_range, |file| {
11800 if let Some(file) = file {
11801 file.is_private()
11802 && EditorSettings::get(
11803 Some(SettingsLocation {
11804 worktree_id: file.worktree_id(cx),
11805 path: file.path().as_ref(),
11806 }),
11807 cx,
11808 )
11809 .redact_private_values
11810 } else {
11811 false
11812 }
11813 })
11814 .map(|range| {
11815 range.start.to_display_point(display_snapshot)
11816 ..range.end.to_display_point(display_snapshot)
11817 })
11818 .collect()
11819 }
11820
11821 pub fn highlight_text<T: 'static>(
11822 &mut self,
11823 ranges: Vec<Range<Anchor>>,
11824 style: HighlightStyle,
11825 cx: &mut ViewContext<Self>,
11826 ) {
11827 self.display_map.update(cx, |map, _| {
11828 map.highlight_text(TypeId::of::<T>(), ranges, style)
11829 });
11830 cx.notify();
11831 }
11832
11833 pub(crate) fn highlight_inlays<T: 'static>(
11834 &mut self,
11835 highlights: Vec<InlayHighlight>,
11836 style: HighlightStyle,
11837 cx: &mut ViewContext<Self>,
11838 ) {
11839 self.display_map.update(cx, |map, _| {
11840 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
11841 });
11842 cx.notify();
11843 }
11844
11845 pub fn text_highlights<'a, T: 'static>(
11846 &'a self,
11847 cx: &'a AppContext,
11848 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
11849 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
11850 }
11851
11852 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
11853 let cleared = self
11854 .display_map
11855 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
11856 if cleared {
11857 cx.notify();
11858 }
11859 }
11860
11861 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
11862 (self.read_only(cx) || self.blink_manager.read(cx).visible())
11863 && self.focus_handle.is_focused(cx)
11864 }
11865
11866 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut ViewContext<Self>) {
11867 self.show_cursor_when_unfocused = is_enabled;
11868 cx.notify();
11869 }
11870
11871 pub fn lsp_store(&self, cx: &AppContext) -> Option<Model<LspStore>> {
11872 self.project
11873 .as_ref()
11874 .map(|project| project.read(cx).lsp_store())
11875 }
11876
11877 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
11878 cx.notify();
11879 }
11880
11881 fn on_buffer_event(
11882 &mut self,
11883 multibuffer: Model<MultiBuffer>,
11884 event: &multi_buffer::Event,
11885 cx: &mut ViewContext<Self>,
11886 ) {
11887 match event {
11888 multi_buffer::Event::Edited {
11889 singleton_buffer_edited,
11890 edited_buffer: buffer_edited,
11891 } => {
11892 self.scrollbar_marker_state.dirty = true;
11893 self.active_indent_guides_state.dirty = true;
11894 self.refresh_active_diagnostics(cx);
11895 self.refresh_code_actions(cx);
11896 if self.has_active_inline_completion() {
11897 self.update_visible_inline_completion(cx);
11898 }
11899 if let Some(buffer) = buffer_edited {
11900 let buffer_id = buffer.read(cx).remote_id();
11901 if !self.registered_buffers.contains_key(&buffer_id) {
11902 if let Some(lsp_store) = self.lsp_store(cx) {
11903 lsp_store.update(cx, |lsp_store, cx| {
11904 self.registered_buffers.insert(
11905 buffer_id,
11906 lsp_store.register_buffer_with_language_servers(&buffer, cx),
11907 );
11908 })
11909 }
11910 }
11911 }
11912 cx.emit(EditorEvent::BufferEdited);
11913 cx.emit(SearchEvent::MatchesInvalidated);
11914 if *singleton_buffer_edited {
11915 if let Some(project) = &self.project {
11916 let project = project.read(cx);
11917 #[allow(clippy::mutable_key_type)]
11918 let languages_affected = multibuffer
11919 .read(cx)
11920 .all_buffers()
11921 .into_iter()
11922 .filter_map(|buffer| {
11923 let buffer = buffer.read(cx);
11924 let language = buffer.language()?;
11925 if project.is_local()
11926 && project
11927 .language_servers_for_local_buffer(buffer, cx)
11928 .count()
11929 == 0
11930 {
11931 None
11932 } else {
11933 Some(language)
11934 }
11935 })
11936 .cloned()
11937 .collect::<HashSet<_>>();
11938 if !languages_affected.is_empty() {
11939 self.refresh_inlay_hints(
11940 InlayHintRefreshReason::BufferEdited(languages_affected),
11941 cx,
11942 );
11943 }
11944 }
11945 }
11946
11947 let Some(project) = &self.project else { return };
11948 let (telemetry, is_via_ssh) = {
11949 let project = project.read(cx);
11950 let telemetry = project.client().telemetry().clone();
11951 let is_via_ssh = project.is_via_ssh();
11952 (telemetry, is_via_ssh)
11953 };
11954 refresh_linked_ranges(self, cx);
11955 telemetry.log_edit_event("editor", is_via_ssh);
11956 }
11957 multi_buffer::Event::ExcerptsAdded {
11958 buffer,
11959 predecessor,
11960 excerpts,
11961 } => {
11962 self.tasks_update_task = Some(self.refresh_runnables(cx));
11963 let buffer_id = buffer.read(cx).remote_id();
11964 if !self.diff_map.diff_bases.contains_key(&buffer_id) {
11965 if let Some(project) = &self.project {
11966 get_unstaged_changes_for_buffers(project, [buffer.clone()], cx);
11967 }
11968 }
11969 cx.emit(EditorEvent::ExcerptsAdded {
11970 buffer: buffer.clone(),
11971 predecessor: *predecessor,
11972 excerpts: excerpts.clone(),
11973 });
11974 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
11975 }
11976 multi_buffer::Event::ExcerptsRemoved { ids } => {
11977 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
11978 let buffer = self.buffer.read(cx);
11979 self.registered_buffers
11980 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
11981 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
11982 }
11983 multi_buffer::Event::ExcerptsEdited { ids } => {
11984 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
11985 }
11986 multi_buffer::Event::ExcerptsExpanded { ids } => {
11987 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
11988 }
11989 multi_buffer::Event::Reparsed(buffer_id) => {
11990 self.tasks_update_task = Some(self.refresh_runnables(cx));
11991
11992 cx.emit(EditorEvent::Reparsed(*buffer_id));
11993 }
11994 multi_buffer::Event::LanguageChanged(buffer_id) => {
11995 linked_editing_ranges::refresh_linked_ranges(self, cx);
11996 cx.emit(EditorEvent::Reparsed(*buffer_id));
11997 cx.notify();
11998 }
11999 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
12000 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
12001 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
12002 cx.emit(EditorEvent::TitleChanged)
12003 }
12004 // multi_buffer::Event::DiffBaseChanged => {
12005 // self.scrollbar_marker_state.dirty = true;
12006 // cx.emit(EditorEvent::DiffBaseChanged);
12007 // cx.notify();
12008 // }
12009 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
12010 multi_buffer::Event::DiagnosticsUpdated => {
12011 self.refresh_active_diagnostics(cx);
12012 self.scrollbar_marker_state.dirty = true;
12013 cx.notify();
12014 }
12015 _ => {}
12016 };
12017 }
12018
12019 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
12020 cx.notify();
12021 }
12022
12023 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
12024 self.tasks_update_task = Some(self.refresh_runnables(cx));
12025 self.refresh_inline_completion(true, false, cx);
12026 self.refresh_inlay_hints(
12027 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
12028 self.selections.newest_anchor().head(),
12029 &self.buffer.read(cx).snapshot(cx),
12030 cx,
12031 )),
12032 cx,
12033 );
12034
12035 let old_cursor_shape = self.cursor_shape;
12036
12037 {
12038 let editor_settings = EditorSettings::get_global(cx);
12039 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
12040 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
12041 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
12042 }
12043
12044 if old_cursor_shape != self.cursor_shape {
12045 cx.emit(EditorEvent::CursorShapeChanged);
12046 }
12047
12048 let project_settings = ProjectSettings::get_global(cx);
12049 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
12050
12051 if self.mode == EditorMode::Full {
12052 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
12053 if self.git_blame_inline_enabled != inline_blame_enabled {
12054 self.toggle_git_blame_inline_internal(false, cx);
12055 }
12056 }
12057
12058 cx.notify();
12059 }
12060
12061 pub fn set_searchable(&mut self, searchable: bool) {
12062 self.searchable = searchable;
12063 }
12064
12065 pub fn searchable(&self) -> bool {
12066 self.searchable
12067 }
12068
12069 fn open_proposed_changes_editor(
12070 &mut self,
12071 _: &OpenProposedChangesEditor,
12072 cx: &mut ViewContext<Self>,
12073 ) {
12074 let Some(workspace) = self.workspace() else {
12075 cx.propagate();
12076 return;
12077 };
12078
12079 let selections = self.selections.all::<usize>(cx);
12080 let buffer = self.buffer.read(cx);
12081 let mut new_selections_by_buffer = HashMap::default();
12082 for selection in selections {
12083 for (buffer, range, _) in
12084 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
12085 {
12086 let mut range = range.to_point(buffer.read(cx));
12087 range.start.column = 0;
12088 range.end.column = buffer.read(cx).line_len(range.end.row);
12089 new_selections_by_buffer
12090 .entry(buffer)
12091 .or_insert(Vec::new())
12092 .push(range)
12093 }
12094 }
12095
12096 let proposed_changes_buffers = new_selections_by_buffer
12097 .into_iter()
12098 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
12099 .collect::<Vec<_>>();
12100 let proposed_changes_editor = cx.new_view(|cx| {
12101 ProposedChangesEditor::new(
12102 "Proposed changes",
12103 proposed_changes_buffers,
12104 self.project.clone(),
12105 cx,
12106 )
12107 });
12108
12109 cx.window_context().defer(move |cx| {
12110 workspace.update(cx, |workspace, cx| {
12111 workspace.active_pane().update(cx, |pane, cx| {
12112 pane.add_item(Box::new(proposed_changes_editor), true, true, None, cx);
12113 });
12114 });
12115 });
12116 }
12117
12118 pub fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext<Self>) {
12119 self.open_excerpts_common(None, true, cx)
12120 }
12121
12122 pub fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
12123 self.open_excerpts_common(None, false, cx)
12124 }
12125
12126 fn open_excerpts_common(
12127 &mut self,
12128 jump_data: Option<JumpData>,
12129 split: bool,
12130 cx: &mut ViewContext<Self>,
12131 ) {
12132 let Some(workspace) = self.workspace() else {
12133 cx.propagate();
12134 return;
12135 };
12136
12137 if self.buffer.read(cx).is_singleton() {
12138 cx.propagate();
12139 return;
12140 }
12141
12142 let mut new_selections_by_buffer = HashMap::default();
12143 match &jump_data {
12144 Some(jump_data) => {
12145 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12146 if let Some(buffer) = multi_buffer_snapshot
12147 .buffer_id_for_excerpt(jump_data.excerpt_id)
12148 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
12149 {
12150 let buffer_snapshot = buffer.read(cx).snapshot();
12151 let jump_to_point = if buffer_snapshot.can_resolve(&jump_data.anchor) {
12152 language::ToPoint::to_point(&jump_data.anchor, &buffer_snapshot)
12153 } else {
12154 buffer_snapshot.clip_point(jump_data.position, Bias::Left)
12155 };
12156 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
12157 new_selections_by_buffer.insert(
12158 buffer,
12159 (
12160 vec![jump_to_offset..jump_to_offset],
12161 Some(jump_data.line_offset_from_top),
12162 ),
12163 );
12164 }
12165 }
12166 None => {
12167 let selections = self.selections.all::<usize>(cx);
12168 let buffer = self.buffer.read(cx);
12169 for selection in selections {
12170 for (mut buffer_handle, mut range, _) in
12171 buffer.range_to_buffer_ranges(selection.range(), cx)
12172 {
12173 // When editing branch buffers, jump to the corresponding location
12174 // in their base buffer.
12175 let buffer = buffer_handle.read(cx);
12176 if let Some(base_buffer) = buffer.base_buffer() {
12177 range = buffer.range_to_version(range, &base_buffer.read(cx).version());
12178 buffer_handle = base_buffer;
12179 }
12180
12181 if selection.reversed {
12182 mem::swap(&mut range.start, &mut range.end);
12183 }
12184 new_selections_by_buffer
12185 .entry(buffer_handle)
12186 .or_insert((Vec::new(), None))
12187 .0
12188 .push(range)
12189 }
12190 }
12191 }
12192 }
12193
12194 if new_selections_by_buffer.is_empty() {
12195 return;
12196 }
12197
12198 // We defer the pane interaction because we ourselves are a workspace item
12199 // and activating a new item causes the pane to call a method on us reentrantly,
12200 // which panics if we're on the stack.
12201 cx.window_context().defer(move |cx| {
12202 workspace.update(cx, |workspace, cx| {
12203 let pane = if split {
12204 workspace.adjacent_pane(cx)
12205 } else {
12206 workspace.active_pane().clone()
12207 };
12208
12209 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
12210 let editor = buffer
12211 .read(cx)
12212 .file()
12213 .is_none()
12214 .then(|| {
12215 // Handle file-less buffers separately: those are not really the project items, so won't have a paroject path or entity id,
12216 // so `workspace.open_project_item` will never find them, always opening a new editor.
12217 // Instead, we try to activate the existing editor in the pane first.
12218 let (editor, pane_item_index) =
12219 pane.read(cx).items().enumerate().find_map(|(i, item)| {
12220 let editor = item.downcast::<Editor>()?;
12221 let singleton_buffer =
12222 editor.read(cx).buffer().read(cx).as_singleton()?;
12223 if singleton_buffer == buffer {
12224 Some((editor, i))
12225 } else {
12226 None
12227 }
12228 })?;
12229 pane.update(cx, |pane, cx| {
12230 pane.activate_item(pane_item_index, true, true, cx)
12231 });
12232 Some(editor)
12233 })
12234 .flatten()
12235 .unwrap_or_else(|| {
12236 workspace.open_project_item::<Self>(
12237 pane.clone(),
12238 buffer,
12239 true,
12240 true,
12241 cx,
12242 )
12243 });
12244
12245 editor.update(cx, |editor, cx| {
12246 let autoscroll = match scroll_offset {
12247 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
12248 None => Autoscroll::newest(),
12249 };
12250 let nav_history = editor.nav_history.take();
12251 editor.change_selections(Some(autoscroll), cx, |s| {
12252 s.select_ranges(ranges);
12253 });
12254 editor.nav_history = nav_history;
12255 });
12256 }
12257 })
12258 });
12259 }
12260
12261 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
12262 let snapshot = self.buffer.read(cx).read(cx);
12263 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
12264 Some(
12265 ranges
12266 .iter()
12267 .map(move |range| {
12268 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
12269 })
12270 .collect(),
12271 )
12272 }
12273
12274 fn selection_replacement_ranges(
12275 &self,
12276 range: Range<OffsetUtf16>,
12277 cx: &mut AppContext,
12278 ) -> Vec<Range<OffsetUtf16>> {
12279 let selections = self.selections.all::<OffsetUtf16>(cx);
12280 let newest_selection = selections
12281 .iter()
12282 .max_by_key(|selection| selection.id)
12283 .unwrap();
12284 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
12285 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
12286 let snapshot = self.buffer.read(cx).read(cx);
12287 selections
12288 .into_iter()
12289 .map(|mut selection| {
12290 selection.start.0 =
12291 (selection.start.0 as isize).saturating_add(start_delta) as usize;
12292 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
12293 snapshot.clip_offset_utf16(selection.start, Bias::Left)
12294 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
12295 })
12296 .collect()
12297 }
12298
12299 fn report_editor_event(
12300 &self,
12301 operation: &'static str,
12302 file_extension: Option<String>,
12303 cx: &AppContext,
12304 ) {
12305 if cfg!(any(test, feature = "test-support")) {
12306 return;
12307 }
12308
12309 let Some(project) = &self.project else { return };
12310
12311 // If None, we are in a file without an extension
12312 let file = self
12313 .buffer
12314 .read(cx)
12315 .as_singleton()
12316 .and_then(|b| b.read(cx).file());
12317 let file_extension = file_extension.or(file
12318 .as_ref()
12319 .and_then(|file| Path::new(file.file_name(cx)).extension())
12320 .and_then(|e| e.to_str())
12321 .map(|a| a.to_string()));
12322
12323 let vim_mode = cx
12324 .global::<SettingsStore>()
12325 .raw_user_settings()
12326 .get("vim_mode")
12327 == Some(&serde_json::Value::Bool(true));
12328
12329 let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
12330 == language::language_settings::InlineCompletionProvider::Copilot;
12331 let copilot_enabled_for_language = self
12332 .buffer
12333 .read(cx)
12334 .settings_at(0, cx)
12335 .show_inline_completions;
12336
12337 let project = project.read(cx);
12338 let telemetry = project.client().telemetry().clone();
12339 telemetry.report_editor_event(
12340 file_extension,
12341 vim_mode,
12342 operation,
12343 copilot_enabled,
12344 copilot_enabled_for_language,
12345 project.is_via_ssh(),
12346 )
12347 }
12348
12349 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
12350 /// with each line being an array of {text, highlight} objects.
12351 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
12352 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
12353 return;
12354 };
12355
12356 #[derive(Serialize)]
12357 struct Chunk<'a> {
12358 text: String,
12359 highlight: Option<&'a str>,
12360 }
12361
12362 let snapshot = buffer.read(cx).snapshot();
12363 let range = self
12364 .selected_text_range(false, cx)
12365 .and_then(|selection| {
12366 if selection.range.is_empty() {
12367 None
12368 } else {
12369 Some(selection.range)
12370 }
12371 })
12372 .unwrap_or_else(|| 0..snapshot.len());
12373
12374 let chunks = snapshot.chunks(range, true);
12375 let mut lines = Vec::new();
12376 let mut line: VecDeque<Chunk> = VecDeque::new();
12377
12378 let Some(style) = self.style.as_ref() else {
12379 return;
12380 };
12381
12382 for chunk in chunks {
12383 let highlight = chunk
12384 .syntax_highlight_id
12385 .and_then(|id| id.name(&style.syntax));
12386 let mut chunk_lines = chunk.text.split('\n').peekable();
12387 while let Some(text) = chunk_lines.next() {
12388 let mut merged_with_last_token = false;
12389 if let Some(last_token) = line.back_mut() {
12390 if last_token.highlight == highlight {
12391 last_token.text.push_str(text);
12392 merged_with_last_token = true;
12393 }
12394 }
12395
12396 if !merged_with_last_token {
12397 line.push_back(Chunk {
12398 text: text.into(),
12399 highlight,
12400 });
12401 }
12402
12403 if chunk_lines.peek().is_some() {
12404 if line.len() > 1 && line.front().unwrap().text.is_empty() {
12405 line.pop_front();
12406 }
12407 if line.len() > 1 && line.back().unwrap().text.is_empty() {
12408 line.pop_back();
12409 }
12410
12411 lines.push(mem::take(&mut line));
12412 }
12413 }
12414 }
12415
12416 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
12417 return;
12418 };
12419 cx.write_to_clipboard(ClipboardItem::new_string(lines));
12420 }
12421
12422 pub fn open_context_menu(&mut self, _: &OpenContextMenu, cx: &mut ViewContext<Self>) {
12423 self.request_autoscroll(Autoscroll::newest(), cx);
12424 let position = self.selections.newest_display(cx).start;
12425 mouse_context_menu::deploy_context_menu(self, None, position, cx);
12426 }
12427
12428 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
12429 &self.inlay_hint_cache
12430 }
12431
12432 pub fn replay_insert_event(
12433 &mut self,
12434 text: &str,
12435 relative_utf16_range: Option<Range<isize>>,
12436 cx: &mut ViewContext<Self>,
12437 ) {
12438 if !self.input_enabled {
12439 cx.emit(EditorEvent::InputIgnored { text: text.into() });
12440 return;
12441 }
12442 if let Some(relative_utf16_range) = relative_utf16_range {
12443 let selections = self.selections.all::<OffsetUtf16>(cx);
12444 self.change_selections(None, cx, |s| {
12445 let new_ranges = selections.into_iter().map(|range| {
12446 let start = OffsetUtf16(
12447 range
12448 .head()
12449 .0
12450 .saturating_add_signed(relative_utf16_range.start),
12451 );
12452 let end = OffsetUtf16(
12453 range
12454 .head()
12455 .0
12456 .saturating_add_signed(relative_utf16_range.end),
12457 );
12458 start..end
12459 });
12460 s.select_ranges(new_ranges);
12461 });
12462 }
12463
12464 self.handle_input(text, cx);
12465 }
12466
12467 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
12468 let Some(provider) = self.semantics_provider.as_ref() else {
12469 return false;
12470 };
12471
12472 let mut supports = false;
12473 self.buffer().read(cx).for_each_buffer(|buffer| {
12474 supports |= provider.supports_inlay_hints(buffer, cx);
12475 });
12476 supports
12477 }
12478
12479 pub fn focus(&self, cx: &mut WindowContext) {
12480 cx.focus(&self.focus_handle)
12481 }
12482
12483 pub fn is_focused(&self, cx: &WindowContext) -> bool {
12484 self.focus_handle.is_focused(cx)
12485 }
12486
12487 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
12488 cx.emit(EditorEvent::Focused);
12489
12490 if let Some(descendant) = self
12491 .last_focused_descendant
12492 .take()
12493 .and_then(|descendant| descendant.upgrade())
12494 {
12495 cx.focus(&descendant);
12496 } else {
12497 if let Some(blame) = self.blame.as_ref() {
12498 blame.update(cx, GitBlame::focus)
12499 }
12500
12501 self.blink_manager.update(cx, BlinkManager::enable);
12502 self.show_cursor_names(cx);
12503 self.buffer.update(cx, |buffer, cx| {
12504 buffer.finalize_last_transaction(cx);
12505 if self.leader_peer_id.is_none() {
12506 buffer.set_active_selections(
12507 &self.selections.disjoint_anchors(),
12508 self.selections.line_mode,
12509 self.cursor_shape,
12510 cx,
12511 );
12512 }
12513 });
12514 }
12515 }
12516
12517 fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
12518 cx.emit(EditorEvent::FocusedIn)
12519 }
12520
12521 fn handle_focus_out(&mut self, event: FocusOutEvent, _cx: &mut ViewContext<Self>) {
12522 if event.blurred != self.focus_handle {
12523 self.last_focused_descendant = Some(event.blurred);
12524 }
12525 }
12526
12527 pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
12528 self.blink_manager.update(cx, BlinkManager::disable);
12529 self.buffer
12530 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
12531
12532 if let Some(blame) = self.blame.as_ref() {
12533 blame.update(cx, GitBlame::blur)
12534 }
12535 if !self.hover_state.focused(cx) {
12536 hide_hover(self, cx);
12537 }
12538
12539 self.hide_context_menu(cx);
12540 cx.emit(EditorEvent::Blurred);
12541 cx.notify();
12542 }
12543
12544 pub fn register_action<A: Action>(
12545 &mut self,
12546 listener: impl Fn(&A, &mut WindowContext) + 'static,
12547 ) -> Subscription {
12548 let id = self.next_editor_action_id.post_inc();
12549 let listener = Arc::new(listener);
12550 self.editor_actions.borrow_mut().insert(
12551 id,
12552 Box::new(move |cx| {
12553 let cx = cx.window_context();
12554 let listener = listener.clone();
12555 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
12556 let action = action.downcast_ref().unwrap();
12557 if phase == DispatchPhase::Bubble {
12558 listener(action, cx)
12559 }
12560 })
12561 }),
12562 );
12563
12564 let editor_actions = self.editor_actions.clone();
12565 Subscription::new(move || {
12566 editor_actions.borrow_mut().remove(&id);
12567 })
12568 }
12569
12570 pub fn file_header_size(&self) -> u32 {
12571 FILE_HEADER_HEIGHT
12572 }
12573
12574 pub fn revert(
12575 &mut self,
12576 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
12577 cx: &mut ViewContext<Self>,
12578 ) {
12579 self.buffer().update(cx, |multi_buffer, cx| {
12580 for (buffer_id, changes) in revert_changes {
12581 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
12582 buffer.update(cx, |buffer, cx| {
12583 buffer.edit(
12584 changes.into_iter().map(|(range, text)| {
12585 (range, text.to_string().map(Arc::<str>::from))
12586 }),
12587 None,
12588 cx,
12589 );
12590 });
12591 }
12592 }
12593 });
12594 self.change_selections(None, cx, |selections| selections.refresh());
12595 }
12596
12597 pub fn to_pixel_point(
12598 &mut self,
12599 source: multi_buffer::Anchor,
12600 editor_snapshot: &EditorSnapshot,
12601 cx: &mut ViewContext<Self>,
12602 ) -> Option<gpui::Point<Pixels>> {
12603 let source_point = source.to_display_point(editor_snapshot);
12604 self.display_to_pixel_point(source_point, editor_snapshot, cx)
12605 }
12606
12607 pub fn display_to_pixel_point(
12608 &self,
12609 source: DisplayPoint,
12610 editor_snapshot: &EditorSnapshot,
12611 cx: &WindowContext,
12612 ) -> Option<gpui::Point<Pixels>> {
12613 let line_height = self.style()?.text.line_height_in_pixels(cx.rem_size());
12614 let text_layout_details = self.text_layout_details(cx);
12615 let scroll_top = text_layout_details
12616 .scroll_anchor
12617 .scroll_position(editor_snapshot)
12618 .y;
12619
12620 if source.row().as_f32() < scroll_top.floor() {
12621 return None;
12622 }
12623 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
12624 let source_y = line_height * (source.row().as_f32() - scroll_top);
12625 Some(gpui::Point::new(source_x, source_y))
12626 }
12627
12628 pub fn has_active_completions_menu(&self) -> bool {
12629 self.context_menu.read().as_ref().map_or(false, |menu| {
12630 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
12631 })
12632 }
12633
12634 pub fn register_addon<T: Addon>(&mut self, instance: T) {
12635 self.addons
12636 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
12637 }
12638
12639 pub fn unregister_addon<T: Addon>(&mut self) {
12640 self.addons.remove(&std::any::TypeId::of::<T>());
12641 }
12642
12643 pub fn addon<T: Addon>(&self) -> Option<&T> {
12644 let type_id = std::any::TypeId::of::<T>();
12645 self.addons
12646 .get(&type_id)
12647 .and_then(|item| item.to_any().downcast_ref::<T>())
12648 }
12649
12650 fn character_size(&self, cx: &mut ViewContext<Self>) -> gpui::Point<Pixels> {
12651 let text_layout_details = self.text_layout_details(cx);
12652 let style = &text_layout_details.editor_style;
12653 let font_id = cx.text_system().resolve_font(&style.text.font());
12654 let font_size = style.text.font_size.to_pixels(cx.rem_size());
12655 let line_height = style.text.line_height_in_pixels(cx.rem_size());
12656
12657 let em_width = cx
12658 .text_system()
12659 .typographic_bounds(font_id, font_size, 'm')
12660 .unwrap()
12661 .size
12662 .width;
12663
12664 gpui::Point::new(em_width, line_height)
12665 }
12666}
12667
12668fn get_unstaged_changes_for_buffers(
12669 project: &Model<Project>,
12670 buffers: impl IntoIterator<Item = Model<Buffer>>,
12671 cx: &mut ViewContext<Editor>,
12672) {
12673 let mut tasks = Vec::new();
12674 project.update(cx, |project, cx| {
12675 for buffer in buffers {
12676 tasks.push(project.open_unstaged_changes(buffer.clone(), cx))
12677 }
12678 });
12679 cx.spawn(|this, mut cx| async move {
12680 let change_sets = futures::future::join_all(tasks).await;
12681 this.update(&mut cx, |this, cx| {
12682 for change_set in change_sets {
12683 if let Some(change_set) = change_set.log_err() {
12684 this.diff_map.add_change_set(change_set, cx);
12685 }
12686 }
12687 })
12688 .ok();
12689 })
12690 .detach();
12691}
12692
12693fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
12694 let tab_size = tab_size.get() as usize;
12695 let mut width = offset;
12696
12697 for ch in text.chars() {
12698 width += if ch == '\t' {
12699 tab_size - (width % tab_size)
12700 } else {
12701 1
12702 };
12703 }
12704
12705 width - offset
12706}
12707
12708#[cfg(test)]
12709mod tests {
12710 use super::*;
12711
12712 #[test]
12713 fn test_string_size_with_expanded_tabs() {
12714 let nz = |val| NonZeroU32::new(val).unwrap();
12715 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
12716 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
12717 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
12718 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
12719 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
12720 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
12721 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
12722 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
12723 }
12724}
12725
12726/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
12727struct WordBreakingTokenizer<'a> {
12728 input: &'a str,
12729}
12730
12731impl<'a> WordBreakingTokenizer<'a> {
12732 fn new(input: &'a str) -> Self {
12733 Self { input }
12734 }
12735}
12736
12737fn is_char_ideographic(ch: char) -> bool {
12738 use unicode_script::Script::*;
12739 use unicode_script::UnicodeScript;
12740 matches!(ch.script(), Han | Tangut | Yi)
12741}
12742
12743fn is_grapheme_ideographic(text: &str) -> bool {
12744 text.chars().any(is_char_ideographic)
12745}
12746
12747fn is_grapheme_whitespace(text: &str) -> bool {
12748 text.chars().any(|x| x.is_whitespace())
12749}
12750
12751fn should_stay_with_preceding_ideograph(text: &str) -> bool {
12752 text.chars().next().map_or(false, |ch| {
12753 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
12754 })
12755}
12756
12757#[derive(PartialEq, Eq, Debug, Clone, Copy)]
12758struct WordBreakToken<'a> {
12759 token: &'a str,
12760 grapheme_len: usize,
12761 is_whitespace: bool,
12762}
12763
12764impl<'a> Iterator for WordBreakingTokenizer<'a> {
12765 /// Yields a span, the count of graphemes in the token, and whether it was
12766 /// whitespace. Note that it also breaks at word boundaries.
12767 type Item = WordBreakToken<'a>;
12768
12769 fn next(&mut self) -> Option<Self::Item> {
12770 use unicode_segmentation::UnicodeSegmentation;
12771 if self.input.is_empty() {
12772 return None;
12773 }
12774
12775 let mut iter = self.input.graphemes(true).peekable();
12776 let mut offset = 0;
12777 let mut graphemes = 0;
12778 if let Some(first_grapheme) = iter.next() {
12779 let is_whitespace = is_grapheme_whitespace(first_grapheme);
12780 offset += first_grapheme.len();
12781 graphemes += 1;
12782 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
12783 if let Some(grapheme) = iter.peek().copied() {
12784 if should_stay_with_preceding_ideograph(grapheme) {
12785 offset += grapheme.len();
12786 graphemes += 1;
12787 }
12788 }
12789 } else {
12790 let mut words = self.input[offset..].split_word_bound_indices().peekable();
12791 let mut next_word_bound = words.peek().copied();
12792 if next_word_bound.map_or(false, |(i, _)| i == 0) {
12793 next_word_bound = words.next();
12794 }
12795 while let Some(grapheme) = iter.peek().copied() {
12796 if next_word_bound.map_or(false, |(i, _)| i == offset) {
12797 break;
12798 };
12799 if is_grapheme_whitespace(grapheme) != is_whitespace {
12800 break;
12801 };
12802 offset += grapheme.len();
12803 graphemes += 1;
12804 iter.next();
12805 }
12806 }
12807 let token = &self.input[..offset];
12808 self.input = &self.input[offset..];
12809 if is_whitespace {
12810 Some(WordBreakToken {
12811 token: " ",
12812 grapheme_len: 1,
12813 is_whitespace: true,
12814 })
12815 } else {
12816 Some(WordBreakToken {
12817 token,
12818 grapheme_len: graphemes,
12819 is_whitespace: false,
12820 })
12821 }
12822 } else {
12823 None
12824 }
12825 }
12826}
12827
12828#[test]
12829fn test_word_breaking_tokenizer() {
12830 let tests: &[(&str, &[(&str, usize, bool)])] = &[
12831 ("", &[]),
12832 (" ", &[(" ", 1, true)]),
12833 ("Ʒ", &[("Ʒ", 1, false)]),
12834 ("Ǽ", &[("Ǽ", 1, false)]),
12835 ("⋑", &[("⋑", 1, false)]),
12836 ("⋑⋑", &[("⋑⋑", 2, false)]),
12837 (
12838 "原理,进而",
12839 &[
12840 ("原", 1, false),
12841 ("理,", 2, false),
12842 ("进", 1, false),
12843 ("而", 1, false),
12844 ],
12845 ),
12846 (
12847 "hello world",
12848 &[("hello", 5, false), (" ", 1, true), ("world", 5, false)],
12849 ),
12850 (
12851 "hello, world",
12852 &[("hello,", 6, false), (" ", 1, true), ("world", 5, false)],
12853 ),
12854 (
12855 " hello world",
12856 &[
12857 (" ", 1, true),
12858 ("hello", 5, false),
12859 (" ", 1, true),
12860 ("world", 5, false),
12861 ],
12862 ),
12863 (
12864 "这是什么 \n 钢笔",
12865 &[
12866 ("这", 1, false),
12867 ("是", 1, false),
12868 ("什", 1, false),
12869 ("么", 1, false),
12870 (" ", 1, true),
12871 ("钢", 1, false),
12872 ("笔", 1, false),
12873 ],
12874 ),
12875 (" mutton", &[(" ", 1, true), ("mutton", 6, false)]),
12876 ];
12877
12878 for (input, result) in tests {
12879 assert_eq!(
12880 WordBreakingTokenizer::new(input).collect::<Vec<_>>(),
12881 result
12882 .iter()
12883 .copied()
12884 .map(|(token, grapheme_len, is_whitespace)| WordBreakToken {
12885 token,
12886 grapheme_len,
12887 is_whitespace,
12888 })
12889 .collect::<Vec<_>>()
12890 );
12891 }
12892}
12893
12894fn wrap_with_prefix(
12895 line_prefix: String,
12896 unwrapped_text: String,
12897 wrap_column: usize,
12898 tab_size: NonZeroU32,
12899) -> String {
12900 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
12901 let mut wrapped_text = String::new();
12902 let mut current_line = line_prefix.clone();
12903
12904 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
12905 let mut current_line_len = line_prefix_len;
12906 for WordBreakToken {
12907 token,
12908 grapheme_len,
12909 is_whitespace,
12910 } in tokenizer
12911 {
12912 if current_line_len + grapheme_len > wrap_column && current_line_len != line_prefix_len {
12913 wrapped_text.push_str(current_line.trim_end());
12914 wrapped_text.push('\n');
12915 current_line.truncate(line_prefix.len());
12916 current_line_len = line_prefix_len;
12917 if !is_whitespace {
12918 current_line.push_str(token);
12919 current_line_len += grapheme_len;
12920 }
12921 } else if !is_whitespace {
12922 current_line.push_str(token);
12923 current_line_len += grapheme_len;
12924 } else if current_line_len != line_prefix_len {
12925 current_line.push(' ');
12926 current_line_len += 1;
12927 }
12928 }
12929
12930 if !current_line.is_empty() {
12931 wrapped_text.push_str(¤t_line);
12932 }
12933 wrapped_text
12934}
12935
12936#[test]
12937fn test_wrap_with_prefix() {
12938 assert_eq!(
12939 wrap_with_prefix(
12940 "# ".to_string(),
12941 "abcdefg".to_string(),
12942 4,
12943 NonZeroU32::new(4).unwrap()
12944 ),
12945 "# abcdefg"
12946 );
12947 assert_eq!(
12948 wrap_with_prefix(
12949 "".to_string(),
12950 "\thello world".to_string(),
12951 8,
12952 NonZeroU32::new(4).unwrap()
12953 ),
12954 "hello\nworld"
12955 );
12956 assert_eq!(
12957 wrap_with_prefix(
12958 "// ".to_string(),
12959 "xx \nyy zz aa bb cc".to_string(),
12960 12,
12961 NonZeroU32::new(4).unwrap()
12962 ),
12963 "// xx yy zz\n// aa bb cc"
12964 );
12965 assert_eq!(
12966 wrap_with_prefix(
12967 String::new(),
12968 "这是什么 \n 钢笔".to_string(),
12969 3,
12970 NonZeroU32::new(4).unwrap()
12971 ),
12972 "这是什\n么 钢\n笔"
12973 );
12974}
12975
12976fn hunks_for_selections(
12977 snapshot: &EditorSnapshot,
12978 selections: &[Selection<Point>],
12979) -> Vec<MultiBufferDiffHunk> {
12980 hunks_for_ranges(
12981 selections.iter().map(|selection| selection.range()),
12982 snapshot,
12983 )
12984}
12985
12986pub fn hunks_for_ranges(
12987 ranges: impl Iterator<Item = Range<Point>>,
12988 snapshot: &EditorSnapshot,
12989) -> Vec<MultiBufferDiffHunk> {
12990 let mut hunks = Vec::new();
12991 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
12992 HashMap::default();
12993 for query_range in ranges {
12994 let query_rows =
12995 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
12996 for hunk in snapshot.diff_map.diff_hunks_in_range(
12997 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
12998 &snapshot.buffer_snapshot,
12999 ) {
13000 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
13001 // when the caret is just above or just below the deleted hunk.
13002 let allow_adjacent = hunk_status(&hunk) == DiffHunkStatus::Removed;
13003 let related_to_selection = if allow_adjacent {
13004 hunk.row_range.overlaps(&query_rows)
13005 || hunk.row_range.start == query_rows.end
13006 || hunk.row_range.end == query_rows.start
13007 } else {
13008 hunk.row_range.overlaps(&query_rows)
13009 };
13010 if related_to_selection {
13011 if !processed_buffer_rows
13012 .entry(hunk.buffer_id)
13013 .or_default()
13014 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
13015 {
13016 continue;
13017 }
13018 hunks.push(hunk);
13019 }
13020 }
13021 }
13022
13023 hunks
13024}
13025
13026pub trait CollaborationHub {
13027 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
13028 fn user_participant_indices<'a>(
13029 &self,
13030 cx: &'a AppContext,
13031 ) -> &'a HashMap<u64, ParticipantIndex>;
13032 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
13033}
13034
13035impl CollaborationHub for Model<Project> {
13036 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
13037 self.read(cx).collaborators()
13038 }
13039
13040 fn user_participant_indices<'a>(
13041 &self,
13042 cx: &'a AppContext,
13043 ) -> &'a HashMap<u64, ParticipantIndex> {
13044 self.read(cx).user_store().read(cx).participant_indices()
13045 }
13046
13047 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
13048 let this = self.read(cx);
13049 let user_ids = this.collaborators().values().map(|c| c.user_id);
13050 this.user_store().read_with(cx, |user_store, cx| {
13051 user_store.participant_names(user_ids, cx)
13052 })
13053 }
13054}
13055
13056pub trait SemanticsProvider {
13057 fn hover(
13058 &self,
13059 buffer: &Model<Buffer>,
13060 position: text::Anchor,
13061 cx: &mut AppContext,
13062 ) -> Option<Task<Vec<project::Hover>>>;
13063
13064 fn inlay_hints(
13065 &self,
13066 buffer_handle: Model<Buffer>,
13067 range: Range<text::Anchor>,
13068 cx: &mut AppContext,
13069 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
13070
13071 fn resolve_inlay_hint(
13072 &self,
13073 hint: InlayHint,
13074 buffer_handle: Model<Buffer>,
13075 server_id: LanguageServerId,
13076 cx: &mut AppContext,
13077 ) -> Option<Task<anyhow::Result<InlayHint>>>;
13078
13079 fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool;
13080
13081 fn document_highlights(
13082 &self,
13083 buffer: &Model<Buffer>,
13084 position: text::Anchor,
13085 cx: &mut AppContext,
13086 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
13087
13088 fn definitions(
13089 &self,
13090 buffer: &Model<Buffer>,
13091 position: text::Anchor,
13092 kind: GotoDefinitionKind,
13093 cx: &mut AppContext,
13094 ) -> Option<Task<Result<Vec<LocationLink>>>>;
13095
13096 fn range_for_rename(
13097 &self,
13098 buffer: &Model<Buffer>,
13099 position: text::Anchor,
13100 cx: &mut AppContext,
13101 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
13102
13103 fn perform_rename(
13104 &self,
13105 buffer: &Model<Buffer>,
13106 position: text::Anchor,
13107 new_name: String,
13108 cx: &mut AppContext,
13109 ) -> Option<Task<Result<ProjectTransaction>>>;
13110}
13111
13112pub trait CompletionProvider {
13113 fn completions(
13114 &self,
13115 buffer: &Model<Buffer>,
13116 buffer_position: text::Anchor,
13117 trigger: CompletionContext,
13118 cx: &mut ViewContext<Editor>,
13119 ) -> Task<Result<Vec<Completion>>>;
13120
13121 fn resolve_completions(
13122 &self,
13123 buffer: Model<Buffer>,
13124 completion_indices: Vec<usize>,
13125 completions: Arc<RwLock<Box<[Completion]>>>,
13126 cx: &mut ViewContext<Editor>,
13127 ) -> Task<Result<bool>>;
13128
13129 fn apply_additional_edits_for_completion(
13130 &self,
13131 buffer: Model<Buffer>,
13132 completion: Completion,
13133 push_to_history: bool,
13134 cx: &mut ViewContext<Editor>,
13135 ) -> Task<Result<Option<language::Transaction>>>;
13136
13137 fn is_completion_trigger(
13138 &self,
13139 buffer: &Model<Buffer>,
13140 position: language::Anchor,
13141 text: &str,
13142 trigger_in_words: bool,
13143 cx: &mut ViewContext<Editor>,
13144 ) -> bool;
13145
13146 fn sort_completions(&self) -> bool {
13147 true
13148 }
13149}
13150
13151pub trait CodeActionProvider {
13152 fn code_actions(
13153 &self,
13154 buffer: &Model<Buffer>,
13155 range: Range<text::Anchor>,
13156 cx: &mut WindowContext,
13157 ) -> Task<Result<Vec<CodeAction>>>;
13158
13159 fn apply_code_action(
13160 &self,
13161 buffer_handle: Model<Buffer>,
13162 action: CodeAction,
13163 excerpt_id: ExcerptId,
13164 push_to_history: bool,
13165 cx: &mut WindowContext,
13166 ) -> Task<Result<ProjectTransaction>>;
13167}
13168
13169impl CodeActionProvider for Model<Project> {
13170 fn code_actions(
13171 &self,
13172 buffer: &Model<Buffer>,
13173 range: Range<text::Anchor>,
13174 cx: &mut WindowContext,
13175 ) -> Task<Result<Vec<CodeAction>>> {
13176 self.update(cx, |project, cx| {
13177 project.code_actions(buffer, range, None, cx)
13178 })
13179 }
13180
13181 fn apply_code_action(
13182 &self,
13183 buffer_handle: Model<Buffer>,
13184 action: CodeAction,
13185 _excerpt_id: ExcerptId,
13186 push_to_history: bool,
13187 cx: &mut WindowContext,
13188 ) -> Task<Result<ProjectTransaction>> {
13189 self.update(cx, |project, cx| {
13190 project.apply_code_action(buffer_handle, action, push_to_history, cx)
13191 })
13192 }
13193}
13194
13195fn snippet_completions(
13196 project: &Project,
13197 buffer: &Model<Buffer>,
13198 buffer_position: text::Anchor,
13199 cx: &mut AppContext,
13200) -> Task<Result<Vec<Completion>>> {
13201 let language = buffer.read(cx).language_at(buffer_position);
13202 let language_name = language.as_ref().map(|language| language.lsp_id());
13203 let snippet_store = project.snippets().read(cx);
13204 let snippets = snippet_store.snippets_for(language_name, cx);
13205
13206 if snippets.is_empty() {
13207 return Task::ready(Ok(vec![]));
13208 }
13209 let snapshot = buffer.read(cx).text_snapshot();
13210 let chars: String = snapshot
13211 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
13212 .collect();
13213
13214 let scope = language.map(|language| language.default_scope());
13215 let executor = cx.background_executor().clone();
13216
13217 cx.background_executor().spawn(async move {
13218 let classifier = CharClassifier::new(scope).for_completion(true);
13219 let mut last_word = chars
13220 .chars()
13221 .take_while(|c| classifier.is_word(*c))
13222 .collect::<String>();
13223 last_word = last_word.chars().rev().collect();
13224
13225 if last_word.is_empty() {
13226 return Ok(vec![]);
13227 }
13228
13229 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
13230 let to_lsp = |point: &text::Anchor| {
13231 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
13232 point_to_lsp(end)
13233 };
13234 let lsp_end = to_lsp(&buffer_position);
13235
13236 let candidates = snippets
13237 .iter()
13238 .enumerate()
13239 .flat_map(|(ix, snippet)| {
13240 snippet
13241 .prefix
13242 .iter()
13243 .map(move |prefix| StringMatchCandidate::new(ix, prefix.clone()))
13244 })
13245 .collect::<Vec<StringMatchCandidate>>();
13246
13247 let mut matches = fuzzy::match_strings(
13248 &candidates,
13249 &last_word,
13250 last_word.chars().any(|c| c.is_uppercase()),
13251 100,
13252 &Default::default(),
13253 executor,
13254 )
13255 .await;
13256
13257 // Remove all candidates where the query's start does not match the start of any word in the candidate
13258 if let Some(query_start) = last_word.chars().next() {
13259 matches.retain(|string_match| {
13260 split_words(&string_match.string).any(|word| {
13261 // Check that the first codepoint of the word as lowercase matches the first
13262 // codepoint of the query as lowercase
13263 word.chars()
13264 .flat_map(|codepoint| codepoint.to_lowercase())
13265 .zip(query_start.to_lowercase())
13266 .all(|(word_cp, query_cp)| word_cp == query_cp)
13267 })
13268 });
13269 }
13270
13271 let matched_strings = matches
13272 .into_iter()
13273 .map(|m| m.string)
13274 .collect::<HashSet<_>>();
13275
13276 let result: Vec<Completion> = snippets
13277 .into_iter()
13278 .filter_map(|snippet| {
13279 let matching_prefix = snippet
13280 .prefix
13281 .iter()
13282 .find(|prefix| matched_strings.contains(*prefix))?;
13283 let start = as_offset - last_word.len();
13284 let start = snapshot.anchor_before(start);
13285 let range = start..buffer_position;
13286 let lsp_start = to_lsp(&start);
13287 let lsp_range = lsp::Range {
13288 start: lsp_start,
13289 end: lsp_end,
13290 };
13291 Some(Completion {
13292 old_range: range,
13293 new_text: snippet.body.clone(),
13294 label: CodeLabel {
13295 text: matching_prefix.clone(),
13296 runs: vec![],
13297 filter_range: 0..matching_prefix.len(),
13298 },
13299 server_id: LanguageServerId(usize::MAX),
13300 documentation: snippet.description.clone().map(Documentation::SingleLine),
13301 lsp_completion: lsp::CompletionItem {
13302 label: snippet.prefix.first().unwrap().clone(),
13303 kind: Some(CompletionItemKind::SNIPPET),
13304 label_details: snippet.description.as_ref().map(|description| {
13305 lsp::CompletionItemLabelDetails {
13306 detail: Some(description.clone()),
13307 description: None,
13308 }
13309 }),
13310 insert_text_format: Some(InsertTextFormat::SNIPPET),
13311 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13312 lsp::InsertReplaceEdit {
13313 new_text: snippet.body.clone(),
13314 insert: lsp_range,
13315 replace: lsp_range,
13316 },
13317 )),
13318 filter_text: Some(snippet.body.clone()),
13319 sort_text: Some(char::MAX.to_string()),
13320 ..Default::default()
13321 },
13322 confirm: None,
13323 })
13324 })
13325 .collect();
13326
13327 Ok(result)
13328 })
13329}
13330
13331impl CompletionProvider for Model<Project> {
13332 fn completions(
13333 &self,
13334 buffer: &Model<Buffer>,
13335 buffer_position: text::Anchor,
13336 options: CompletionContext,
13337 cx: &mut ViewContext<Editor>,
13338 ) -> Task<Result<Vec<Completion>>> {
13339 self.update(cx, |project, cx| {
13340 let snippets = snippet_completions(project, buffer, buffer_position, cx);
13341 let project_completions = project.completions(buffer, buffer_position, options, cx);
13342 cx.background_executor().spawn(async move {
13343 let mut completions = project_completions.await?;
13344 let snippets_completions = snippets.await?;
13345 completions.extend(snippets_completions);
13346 Ok(completions)
13347 })
13348 })
13349 }
13350
13351 fn resolve_completions(
13352 &self,
13353 buffer: Model<Buffer>,
13354 completion_indices: Vec<usize>,
13355 completions: Arc<RwLock<Box<[Completion]>>>,
13356 cx: &mut ViewContext<Editor>,
13357 ) -> Task<Result<bool>> {
13358 self.update(cx, |project, cx| {
13359 project.resolve_completions(buffer, completion_indices, completions, cx)
13360 })
13361 }
13362
13363 fn apply_additional_edits_for_completion(
13364 &self,
13365 buffer: Model<Buffer>,
13366 completion: Completion,
13367 push_to_history: bool,
13368 cx: &mut ViewContext<Editor>,
13369 ) -> Task<Result<Option<language::Transaction>>> {
13370 self.update(cx, |project, cx| {
13371 project.apply_additional_edits_for_completion(buffer, completion, push_to_history, cx)
13372 })
13373 }
13374
13375 fn is_completion_trigger(
13376 &self,
13377 buffer: &Model<Buffer>,
13378 position: language::Anchor,
13379 text: &str,
13380 trigger_in_words: bool,
13381 cx: &mut ViewContext<Editor>,
13382 ) -> bool {
13383 let mut chars = text.chars();
13384 let char = if let Some(char) = chars.next() {
13385 char
13386 } else {
13387 return false;
13388 };
13389 if chars.next().is_some() {
13390 return false;
13391 }
13392
13393 let buffer = buffer.read(cx);
13394 let snapshot = buffer.snapshot();
13395 if !snapshot.settings_at(position, cx).show_completions_on_input {
13396 return false;
13397 }
13398 let classifier = snapshot.char_classifier_at(position).for_completion(true);
13399 if trigger_in_words && classifier.is_word(char) {
13400 return true;
13401 }
13402
13403 buffer.completion_triggers().contains(text)
13404 }
13405}
13406
13407impl SemanticsProvider for Model<Project> {
13408 fn hover(
13409 &self,
13410 buffer: &Model<Buffer>,
13411 position: text::Anchor,
13412 cx: &mut AppContext,
13413 ) -> Option<Task<Vec<project::Hover>>> {
13414 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
13415 }
13416
13417 fn document_highlights(
13418 &self,
13419 buffer: &Model<Buffer>,
13420 position: text::Anchor,
13421 cx: &mut AppContext,
13422 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
13423 Some(self.update(cx, |project, cx| {
13424 project.document_highlights(buffer, position, cx)
13425 }))
13426 }
13427
13428 fn definitions(
13429 &self,
13430 buffer: &Model<Buffer>,
13431 position: text::Anchor,
13432 kind: GotoDefinitionKind,
13433 cx: &mut AppContext,
13434 ) -> Option<Task<Result<Vec<LocationLink>>>> {
13435 Some(self.update(cx, |project, cx| match kind {
13436 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
13437 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
13438 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
13439 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
13440 }))
13441 }
13442
13443 fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool {
13444 // TODO: make this work for remote projects
13445 self.read(cx)
13446 .language_servers_for_local_buffer(buffer.read(cx), cx)
13447 .any(
13448 |(_, server)| match server.capabilities().inlay_hint_provider {
13449 Some(lsp::OneOf::Left(enabled)) => enabled,
13450 Some(lsp::OneOf::Right(_)) => true,
13451 None => false,
13452 },
13453 )
13454 }
13455
13456 fn inlay_hints(
13457 &self,
13458 buffer_handle: Model<Buffer>,
13459 range: Range<text::Anchor>,
13460 cx: &mut AppContext,
13461 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
13462 Some(self.update(cx, |project, cx| {
13463 project.inlay_hints(buffer_handle, range, cx)
13464 }))
13465 }
13466
13467 fn resolve_inlay_hint(
13468 &self,
13469 hint: InlayHint,
13470 buffer_handle: Model<Buffer>,
13471 server_id: LanguageServerId,
13472 cx: &mut AppContext,
13473 ) -> Option<Task<anyhow::Result<InlayHint>>> {
13474 Some(self.update(cx, |project, cx| {
13475 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
13476 }))
13477 }
13478
13479 fn range_for_rename(
13480 &self,
13481 buffer: &Model<Buffer>,
13482 position: text::Anchor,
13483 cx: &mut AppContext,
13484 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
13485 Some(self.update(cx, |project, cx| {
13486 project.prepare_rename(buffer.clone(), position, cx)
13487 }))
13488 }
13489
13490 fn perform_rename(
13491 &self,
13492 buffer: &Model<Buffer>,
13493 position: text::Anchor,
13494 new_name: String,
13495 cx: &mut AppContext,
13496 ) -> Option<Task<Result<ProjectTransaction>>> {
13497 Some(self.update(cx, |project, cx| {
13498 project.perform_rename(buffer.clone(), position, new_name, cx)
13499 }))
13500 }
13501}
13502
13503fn inlay_hint_settings(
13504 location: Anchor,
13505 snapshot: &MultiBufferSnapshot,
13506 cx: &mut ViewContext<'_, Editor>,
13507) -> InlayHintSettings {
13508 let file = snapshot.file_at(location);
13509 let language = snapshot.language_at(location).map(|l| l.name());
13510 language_settings(language, file, cx).inlay_hints
13511}
13512
13513fn consume_contiguous_rows(
13514 contiguous_row_selections: &mut Vec<Selection<Point>>,
13515 selection: &Selection<Point>,
13516 display_map: &DisplaySnapshot,
13517 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
13518) -> (MultiBufferRow, MultiBufferRow) {
13519 contiguous_row_selections.push(selection.clone());
13520 let start_row = MultiBufferRow(selection.start.row);
13521 let mut end_row = ending_row(selection, display_map);
13522
13523 while let Some(next_selection) = selections.peek() {
13524 if next_selection.start.row <= end_row.0 {
13525 end_row = ending_row(next_selection, display_map);
13526 contiguous_row_selections.push(selections.next().unwrap().clone());
13527 } else {
13528 break;
13529 }
13530 }
13531 (start_row, end_row)
13532}
13533
13534fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
13535 if next_selection.end.column > 0 || next_selection.is_empty() {
13536 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
13537 } else {
13538 MultiBufferRow(next_selection.end.row)
13539 }
13540}
13541
13542impl EditorSnapshot {
13543 pub fn remote_selections_in_range<'a>(
13544 &'a self,
13545 range: &'a Range<Anchor>,
13546 collaboration_hub: &dyn CollaborationHub,
13547 cx: &'a AppContext,
13548 ) -> impl 'a + Iterator<Item = RemoteSelection> {
13549 let participant_names = collaboration_hub.user_names(cx);
13550 let participant_indices = collaboration_hub.user_participant_indices(cx);
13551 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
13552 let collaborators_by_replica_id = collaborators_by_peer_id
13553 .iter()
13554 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
13555 .collect::<HashMap<_, _>>();
13556 self.buffer_snapshot
13557 .selections_in_range(range, false)
13558 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
13559 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
13560 let participant_index = participant_indices.get(&collaborator.user_id).copied();
13561 let user_name = participant_names.get(&collaborator.user_id).cloned();
13562 Some(RemoteSelection {
13563 replica_id,
13564 selection,
13565 cursor_shape,
13566 line_mode,
13567 participant_index,
13568 peer_id: collaborator.peer_id,
13569 user_name,
13570 })
13571 })
13572 }
13573
13574 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
13575 self.display_snapshot.buffer_snapshot.language_at(position)
13576 }
13577
13578 pub fn is_focused(&self) -> bool {
13579 self.is_focused
13580 }
13581
13582 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
13583 self.placeholder_text.as_ref()
13584 }
13585
13586 pub fn scroll_position(&self) -> gpui::Point<f32> {
13587 self.scroll_anchor.scroll_position(&self.display_snapshot)
13588 }
13589
13590 fn gutter_dimensions(
13591 &self,
13592 font_id: FontId,
13593 font_size: Pixels,
13594 em_width: Pixels,
13595 em_advance: Pixels,
13596 max_line_number_width: Pixels,
13597 cx: &AppContext,
13598 ) -> GutterDimensions {
13599 if !self.show_gutter {
13600 return GutterDimensions::default();
13601 }
13602 let descent = cx.text_system().descent(font_id, font_size);
13603
13604 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
13605 matches!(
13606 ProjectSettings::get_global(cx).git.git_gutter,
13607 Some(GitGutterSetting::TrackedFiles)
13608 )
13609 });
13610 let gutter_settings = EditorSettings::get_global(cx).gutter;
13611 let show_line_numbers = self
13612 .show_line_numbers
13613 .unwrap_or(gutter_settings.line_numbers);
13614 let line_gutter_width = if show_line_numbers {
13615 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
13616 let min_width_for_number_on_gutter = em_advance * 4.0;
13617 max_line_number_width.max(min_width_for_number_on_gutter)
13618 } else {
13619 0.0.into()
13620 };
13621
13622 let show_code_actions = self
13623 .show_code_actions
13624 .unwrap_or(gutter_settings.code_actions);
13625
13626 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
13627
13628 let git_blame_entries_width =
13629 self.git_blame_gutter_max_author_length
13630 .map(|max_author_length| {
13631 // Length of the author name, but also space for the commit hash,
13632 // the spacing and the timestamp.
13633 let max_char_count = max_author_length
13634 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
13635 + 7 // length of commit sha
13636 + 14 // length of max relative timestamp ("60 minutes ago")
13637 + 4; // gaps and margins
13638
13639 em_advance * max_char_count
13640 });
13641
13642 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
13643 left_padding += if show_code_actions || show_runnables {
13644 em_width * 3.0
13645 } else if show_git_gutter && show_line_numbers {
13646 em_width * 2.0
13647 } else if show_git_gutter || show_line_numbers {
13648 em_width
13649 } else {
13650 px(0.)
13651 };
13652
13653 let right_padding = if gutter_settings.folds && show_line_numbers {
13654 em_width * 4.0
13655 } else if gutter_settings.folds {
13656 em_width * 3.0
13657 } else if show_line_numbers {
13658 em_width
13659 } else {
13660 px(0.)
13661 };
13662
13663 GutterDimensions {
13664 left_padding,
13665 right_padding,
13666 width: line_gutter_width + left_padding + right_padding,
13667 margin: -descent,
13668 git_blame_entries_width,
13669 }
13670 }
13671
13672 pub fn render_crease_toggle(
13673 &self,
13674 buffer_row: MultiBufferRow,
13675 row_contains_cursor: bool,
13676 editor: View<Editor>,
13677 cx: &mut WindowContext,
13678 ) -> Option<AnyElement> {
13679 let folded = self.is_line_folded(buffer_row);
13680 let mut is_foldable = false;
13681
13682 if let Some(crease) = self
13683 .crease_snapshot
13684 .query_row(buffer_row, &self.buffer_snapshot)
13685 {
13686 is_foldable = true;
13687 match crease {
13688 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
13689 if let Some(render_toggle) = render_toggle {
13690 let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
13691 if folded {
13692 editor.update(cx, |editor, cx| {
13693 editor.fold_at(&crate::FoldAt { buffer_row }, cx)
13694 });
13695 } else {
13696 editor.update(cx, |editor, cx| {
13697 editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
13698 });
13699 }
13700 });
13701 return Some((render_toggle)(buffer_row, folded, toggle_callback, cx));
13702 }
13703 }
13704 }
13705 }
13706
13707 is_foldable |= self.starts_indent(buffer_row);
13708
13709 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
13710 Some(
13711 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
13712 .selected(folded)
13713 .on_click(cx.listener_for(&editor, move |this, _e, cx| {
13714 if folded {
13715 this.unfold_at(&UnfoldAt { buffer_row }, cx);
13716 } else {
13717 this.fold_at(&FoldAt { buffer_row }, cx);
13718 }
13719 }))
13720 .into_any_element(),
13721 )
13722 } else {
13723 None
13724 }
13725 }
13726
13727 pub fn render_crease_trailer(
13728 &self,
13729 buffer_row: MultiBufferRow,
13730 cx: &mut WindowContext,
13731 ) -> Option<AnyElement> {
13732 let folded = self.is_line_folded(buffer_row);
13733 if let Crease::Inline { render_trailer, .. } = self
13734 .crease_snapshot
13735 .query_row(buffer_row, &self.buffer_snapshot)?
13736 {
13737 let render_trailer = render_trailer.as_ref()?;
13738 Some(render_trailer(buffer_row, folded, cx))
13739 } else {
13740 None
13741 }
13742 }
13743}
13744
13745impl Deref for EditorSnapshot {
13746 type Target = DisplaySnapshot;
13747
13748 fn deref(&self) -> &Self::Target {
13749 &self.display_snapshot
13750 }
13751}
13752
13753#[derive(Clone, Debug, PartialEq, Eq)]
13754pub enum EditorEvent {
13755 InputIgnored {
13756 text: Arc<str>,
13757 },
13758 InputHandled {
13759 utf16_range_to_replace: Option<Range<isize>>,
13760 text: Arc<str>,
13761 },
13762 ExcerptsAdded {
13763 buffer: Model<Buffer>,
13764 predecessor: ExcerptId,
13765 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
13766 },
13767 ExcerptsRemoved {
13768 ids: Vec<ExcerptId>,
13769 },
13770 ExcerptsEdited {
13771 ids: Vec<ExcerptId>,
13772 },
13773 ExcerptsExpanded {
13774 ids: Vec<ExcerptId>,
13775 },
13776 BufferEdited,
13777 Edited {
13778 transaction_id: clock::Lamport,
13779 },
13780 Reparsed(BufferId),
13781 Focused,
13782 FocusedIn,
13783 Blurred,
13784 DirtyChanged,
13785 Saved,
13786 TitleChanged,
13787 DiffBaseChanged,
13788 SelectionsChanged {
13789 local: bool,
13790 },
13791 ScrollPositionChanged {
13792 local: bool,
13793 autoscroll: bool,
13794 },
13795 Closed,
13796 TransactionUndone {
13797 transaction_id: clock::Lamport,
13798 },
13799 TransactionBegun {
13800 transaction_id: clock::Lamport,
13801 },
13802 Reloaded,
13803 CursorShapeChanged,
13804}
13805
13806impl EventEmitter<EditorEvent> for Editor {}
13807
13808impl FocusableView for Editor {
13809 fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
13810 self.focus_handle.clone()
13811 }
13812}
13813
13814impl Render for Editor {
13815 fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
13816 let settings = ThemeSettings::get_global(cx);
13817
13818 let mut text_style = match self.mode {
13819 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
13820 color: cx.theme().colors().editor_foreground,
13821 font_family: settings.ui_font.family.clone(),
13822 font_features: settings.ui_font.features.clone(),
13823 font_fallbacks: settings.ui_font.fallbacks.clone(),
13824 font_size: rems(0.875).into(),
13825 font_weight: settings.ui_font.weight,
13826 line_height: relative(settings.buffer_line_height.value()),
13827 ..Default::default()
13828 },
13829 EditorMode::Full => TextStyle {
13830 color: cx.theme().colors().editor_foreground,
13831 font_family: settings.buffer_font.family.clone(),
13832 font_features: settings.buffer_font.features.clone(),
13833 font_fallbacks: settings.buffer_font.fallbacks.clone(),
13834 font_size: settings.buffer_font_size(cx).into(),
13835 font_weight: settings.buffer_font.weight,
13836 line_height: relative(settings.buffer_line_height.value()),
13837 ..Default::default()
13838 },
13839 };
13840 if let Some(text_style_refinement) = &self.text_style_refinement {
13841 text_style.refine(text_style_refinement)
13842 }
13843
13844 let background = match self.mode {
13845 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
13846 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
13847 EditorMode::Full => cx.theme().colors().editor_background,
13848 };
13849
13850 EditorElement::new(
13851 cx.view(),
13852 EditorStyle {
13853 background,
13854 local_player: cx.theme().players().local(),
13855 text: text_style,
13856 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
13857 syntax: cx.theme().syntax().clone(),
13858 status: cx.theme().status().clone(),
13859 inlay_hints_style: make_inlay_hints_style(cx),
13860 suggestions_style: HighlightStyle {
13861 color: Some(cx.theme().status().predictive),
13862 ..HighlightStyle::default()
13863 },
13864 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
13865 },
13866 )
13867 }
13868}
13869
13870impl ViewInputHandler for Editor {
13871 fn text_for_range(
13872 &mut self,
13873 range_utf16: Range<usize>,
13874 adjusted_range: &mut Option<Range<usize>>,
13875 cx: &mut ViewContext<Self>,
13876 ) -> Option<String> {
13877 let snapshot = self.buffer.read(cx).read(cx);
13878 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
13879 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
13880 if (start.0..end.0) != range_utf16 {
13881 adjusted_range.replace(start.0..end.0);
13882 }
13883 Some(snapshot.text_for_range(start..end).collect())
13884 }
13885
13886 fn selected_text_range(
13887 &mut self,
13888 ignore_disabled_input: bool,
13889 cx: &mut ViewContext<Self>,
13890 ) -> Option<UTF16Selection> {
13891 // Prevent the IME menu from appearing when holding down an alphabetic key
13892 // while input is disabled.
13893 if !ignore_disabled_input && !self.input_enabled {
13894 return None;
13895 }
13896
13897 let selection = self.selections.newest::<OffsetUtf16>(cx);
13898 let range = selection.range();
13899
13900 Some(UTF16Selection {
13901 range: range.start.0..range.end.0,
13902 reversed: selection.reversed,
13903 })
13904 }
13905
13906 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
13907 let snapshot = self.buffer.read(cx).read(cx);
13908 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
13909 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
13910 }
13911
13912 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
13913 self.clear_highlights::<InputComposition>(cx);
13914 self.ime_transaction.take();
13915 }
13916
13917 fn replace_text_in_range(
13918 &mut self,
13919 range_utf16: Option<Range<usize>>,
13920 text: &str,
13921 cx: &mut ViewContext<Self>,
13922 ) {
13923 if !self.input_enabled {
13924 cx.emit(EditorEvent::InputIgnored { text: text.into() });
13925 return;
13926 }
13927
13928 self.transact(cx, |this, cx| {
13929 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
13930 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
13931 Some(this.selection_replacement_ranges(range_utf16, cx))
13932 } else {
13933 this.marked_text_ranges(cx)
13934 };
13935
13936 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
13937 let newest_selection_id = this.selections.newest_anchor().id;
13938 this.selections
13939 .all::<OffsetUtf16>(cx)
13940 .iter()
13941 .zip(ranges_to_replace.iter())
13942 .find_map(|(selection, range)| {
13943 if selection.id == newest_selection_id {
13944 Some(
13945 (range.start.0 as isize - selection.head().0 as isize)
13946 ..(range.end.0 as isize - selection.head().0 as isize),
13947 )
13948 } else {
13949 None
13950 }
13951 })
13952 });
13953
13954 cx.emit(EditorEvent::InputHandled {
13955 utf16_range_to_replace: range_to_replace,
13956 text: text.into(),
13957 });
13958
13959 if let Some(new_selected_ranges) = new_selected_ranges {
13960 this.change_selections(None, cx, |selections| {
13961 selections.select_ranges(new_selected_ranges)
13962 });
13963 this.backspace(&Default::default(), cx);
13964 }
13965
13966 this.handle_input(text, cx);
13967 });
13968
13969 if let Some(transaction) = self.ime_transaction {
13970 self.buffer.update(cx, |buffer, cx| {
13971 buffer.group_until_transaction(transaction, cx);
13972 });
13973 }
13974
13975 self.unmark_text(cx);
13976 }
13977
13978 fn replace_and_mark_text_in_range(
13979 &mut self,
13980 range_utf16: Option<Range<usize>>,
13981 text: &str,
13982 new_selected_range_utf16: Option<Range<usize>>,
13983 cx: &mut ViewContext<Self>,
13984 ) {
13985 if !self.input_enabled {
13986 return;
13987 }
13988
13989 let transaction = self.transact(cx, |this, cx| {
13990 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
13991 let snapshot = this.buffer.read(cx).read(cx);
13992 if let Some(relative_range_utf16) = range_utf16.as_ref() {
13993 for marked_range in &mut marked_ranges {
13994 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
13995 marked_range.start.0 += relative_range_utf16.start;
13996 marked_range.start =
13997 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
13998 marked_range.end =
13999 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
14000 }
14001 }
14002 Some(marked_ranges)
14003 } else if let Some(range_utf16) = range_utf16 {
14004 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
14005 Some(this.selection_replacement_ranges(range_utf16, cx))
14006 } else {
14007 None
14008 };
14009
14010 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
14011 let newest_selection_id = this.selections.newest_anchor().id;
14012 this.selections
14013 .all::<OffsetUtf16>(cx)
14014 .iter()
14015 .zip(ranges_to_replace.iter())
14016 .find_map(|(selection, range)| {
14017 if selection.id == newest_selection_id {
14018 Some(
14019 (range.start.0 as isize - selection.head().0 as isize)
14020 ..(range.end.0 as isize - selection.head().0 as isize),
14021 )
14022 } else {
14023 None
14024 }
14025 })
14026 });
14027
14028 cx.emit(EditorEvent::InputHandled {
14029 utf16_range_to_replace: range_to_replace,
14030 text: text.into(),
14031 });
14032
14033 if let Some(ranges) = ranges_to_replace {
14034 this.change_selections(None, cx, |s| s.select_ranges(ranges));
14035 }
14036
14037 let marked_ranges = {
14038 let snapshot = this.buffer.read(cx).read(cx);
14039 this.selections
14040 .disjoint_anchors()
14041 .iter()
14042 .map(|selection| {
14043 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
14044 })
14045 .collect::<Vec<_>>()
14046 };
14047
14048 if text.is_empty() {
14049 this.unmark_text(cx);
14050 } else {
14051 this.highlight_text::<InputComposition>(
14052 marked_ranges.clone(),
14053 HighlightStyle {
14054 underline: Some(UnderlineStyle {
14055 thickness: px(1.),
14056 color: None,
14057 wavy: false,
14058 }),
14059 ..Default::default()
14060 },
14061 cx,
14062 );
14063 }
14064
14065 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
14066 let use_autoclose = this.use_autoclose;
14067 let use_auto_surround = this.use_auto_surround;
14068 this.set_use_autoclose(false);
14069 this.set_use_auto_surround(false);
14070 this.handle_input(text, cx);
14071 this.set_use_autoclose(use_autoclose);
14072 this.set_use_auto_surround(use_auto_surround);
14073
14074 if let Some(new_selected_range) = new_selected_range_utf16 {
14075 let snapshot = this.buffer.read(cx).read(cx);
14076 let new_selected_ranges = marked_ranges
14077 .into_iter()
14078 .map(|marked_range| {
14079 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
14080 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
14081 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
14082 snapshot.clip_offset_utf16(new_start, Bias::Left)
14083 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
14084 })
14085 .collect::<Vec<_>>();
14086
14087 drop(snapshot);
14088 this.change_selections(None, cx, |selections| {
14089 selections.select_ranges(new_selected_ranges)
14090 });
14091 }
14092 });
14093
14094 self.ime_transaction = self.ime_transaction.or(transaction);
14095 if let Some(transaction) = self.ime_transaction {
14096 self.buffer.update(cx, |buffer, cx| {
14097 buffer.group_until_transaction(transaction, cx);
14098 });
14099 }
14100
14101 if self.text_highlights::<InputComposition>(cx).is_none() {
14102 self.ime_transaction.take();
14103 }
14104 }
14105
14106 fn bounds_for_range(
14107 &mut self,
14108 range_utf16: Range<usize>,
14109 element_bounds: gpui::Bounds<Pixels>,
14110 cx: &mut ViewContext<Self>,
14111 ) -> Option<gpui::Bounds<Pixels>> {
14112 let text_layout_details = self.text_layout_details(cx);
14113 let gpui::Point {
14114 x: em_width,
14115 y: line_height,
14116 } = self.character_size(cx);
14117
14118 let snapshot = self.snapshot(cx);
14119 let scroll_position = snapshot.scroll_position();
14120 let scroll_left = scroll_position.x * em_width;
14121
14122 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
14123 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
14124 + self.gutter_dimensions.width
14125 + self.gutter_dimensions.margin;
14126 let y = line_height * (start.row().as_f32() - scroll_position.y);
14127
14128 Some(Bounds {
14129 origin: element_bounds.origin + point(x, y),
14130 size: size(em_width, line_height),
14131 })
14132 }
14133}
14134
14135trait SelectionExt {
14136 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
14137 fn spanned_rows(
14138 &self,
14139 include_end_if_at_line_start: bool,
14140 map: &DisplaySnapshot,
14141 ) -> Range<MultiBufferRow>;
14142}
14143
14144impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
14145 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
14146 let start = self
14147 .start
14148 .to_point(&map.buffer_snapshot)
14149 .to_display_point(map);
14150 let end = self
14151 .end
14152 .to_point(&map.buffer_snapshot)
14153 .to_display_point(map);
14154 if self.reversed {
14155 end..start
14156 } else {
14157 start..end
14158 }
14159 }
14160
14161 fn spanned_rows(
14162 &self,
14163 include_end_if_at_line_start: bool,
14164 map: &DisplaySnapshot,
14165 ) -> Range<MultiBufferRow> {
14166 let start = self.start.to_point(&map.buffer_snapshot);
14167 let mut end = self.end.to_point(&map.buffer_snapshot);
14168 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
14169 end.row -= 1;
14170 }
14171
14172 let buffer_start = map.prev_line_boundary(start).0;
14173 let buffer_end = map.next_line_boundary(end).0;
14174 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
14175 }
14176}
14177
14178impl<T: InvalidationRegion> InvalidationStack<T> {
14179 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
14180 where
14181 S: Clone + ToOffset,
14182 {
14183 while let Some(region) = self.last() {
14184 let all_selections_inside_invalidation_ranges =
14185 if selections.len() == region.ranges().len() {
14186 selections
14187 .iter()
14188 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
14189 .all(|(selection, invalidation_range)| {
14190 let head = selection.head().to_offset(buffer);
14191 invalidation_range.start <= head && invalidation_range.end >= head
14192 })
14193 } else {
14194 false
14195 };
14196
14197 if all_selections_inside_invalidation_ranges {
14198 break;
14199 } else {
14200 self.pop();
14201 }
14202 }
14203 }
14204}
14205
14206impl<T> Default for InvalidationStack<T> {
14207 fn default() -> Self {
14208 Self(Default::default())
14209 }
14210}
14211
14212impl<T> Deref for InvalidationStack<T> {
14213 type Target = Vec<T>;
14214
14215 fn deref(&self) -> &Self::Target {
14216 &self.0
14217 }
14218}
14219
14220impl<T> DerefMut for InvalidationStack<T> {
14221 fn deref_mut(&mut self) -> &mut Self::Target {
14222 &mut self.0
14223 }
14224}
14225
14226impl InvalidationRegion for SnippetState {
14227 fn ranges(&self) -> &[Range<Anchor>] {
14228 &self.ranges[self.active_index]
14229 }
14230}
14231
14232pub fn diagnostic_block_renderer(
14233 diagnostic: Diagnostic,
14234 max_message_rows: Option<u8>,
14235 allow_closing: bool,
14236 _is_valid: bool,
14237) -> RenderBlock {
14238 let (text_without_backticks, code_ranges) =
14239 highlight_diagnostic_message(&diagnostic, max_message_rows);
14240
14241 Arc::new(move |cx: &mut BlockContext| {
14242 let group_id: SharedString = cx.block_id.to_string().into();
14243
14244 let mut text_style = cx.text_style().clone();
14245 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
14246 let theme_settings = ThemeSettings::get_global(cx);
14247 text_style.font_family = theme_settings.buffer_font.family.clone();
14248 text_style.font_style = theme_settings.buffer_font.style;
14249 text_style.font_features = theme_settings.buffer_font.features.clone();
14250 text_style.font_weight = theme_settings.buffer_font.weight;
14251
14252 let multi_line_diagnostic = diagnostic.message.contains('\n');
14253
14254 let buttons = |diagnostic: &Diagnostic| {
14255 if multi_line_diagnostic {
14256 v_flex()
14257 } else {
14258 h_flex()
14259 }
14260 .when(allow_closing, |div| {
14261 div.children(diagnostic.is_primary.then(|| {
14262 IconButton::new("close-block", IconName::XCircle)
14263 .icon_color(Color::Muted)
14264 .size(ButtonSize::Compact)
14265 .style(ButtonStyle::Transparent)
14266 .visible_on_hover(group_id.clone())
14267 .on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
14268 .tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
14269 }))
14270 })
14271 .child(
14272 IconButton::new("copy-block", IconName::Copy)
14273 .icon_color(Color::Muted)
14274 .size(ButtonSize::Compact)
14275 .style(ButtonStyle::Transparent)
14276 .visible_on_hover(group_id.clone())
14277 .on_click({
14278 let message = diagnostic.message.clone();
14279 move |_click, cx| {
14280 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
14281 }
14282 })
14283 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
14284 )
14285 };
14286
14287 let icon_size = buttons(&diagnostic)
14288 .into_any_element()
14289 .layout_as_root(AvailableSpace::min_size(), cx);
14290
14291 h_flex()
14292 .id(cx.block_id)
14293 .group(group_id.clone())
14294 .relative()
14295 .size_full()
14296 .block_mouse_down()
14297 .pl(cx.gutter_dimensions.width)
14298 .w(cx.max_width - cx.gutter_dimensions.full_width())
14299 .child(
14300 div()
14301 .flex()
14302 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
14303 .flex_shrink(),
14304 )
14305 .child(buttons(&diagnostic))
14306 .child(div().flex().flex_shrink_0().child(
14307 StyledText::new(text_without_backticks.clone()).with_highlights(
14308 &text_style,
14309 code_ranges.iter().map(|range| {
14310 (
14311 range.clone(),
14312 HighlightStyle {
14313 font_weight: Some(FontWeight::BOLD),
14314 ..Default::default()
14315 },
14316 )
14317 }),
14318 ),
14319 ))
14320 .into_any_element()
14321 })
14322}
14323
14324pub fn highlight_diagnostic_message(
14325 diagnostic: &Diagnostic,
14326 mut max_message_rows: Option<u8>,
14327) -> (SharedString, Vec<Range<usize>>) {
14328 let mut text_without_backticks = String::new();
14329 let mut code_ranges = Vec::new();
14330
14331 if let Some(source) = &diagnostic.source {
14332 text_without_backticks.push_str(source);
14333 code_ranges.push(0..source.len());
14334 text_without_backticks.push_str(": ");
14335 }
14336
14337 let mut prev_offset = 0;
14338 let mut in_code_block = false;
14339 let has_row_limit = max_message_rows.is_some();
14340 let mut newline_indices = diagnostic
14341 .message
14342 .match_indices('\n')
14343 .filter(|_| has_row_limit)
14344 .map(|(ix, _)| ix)
14345 .fuse()
14346 .peekable();
14347
14348 for (quote_ix, _) in diagnostic
14349 .message
14350 .match_indices('`')
14351 .chain([(diagnostic.message.len(), "")])
14352 {
14353 let mut first_newline_ix = None;
14354 let mut last_newline_ix = None;
14355 while let Some(newline_ix) = newline_indices.peek() {
14356 if *newline_ix < quote_ix {
14357 if first_newline_ix.is_none() {
14358 first_newline_ix = Some(*newline_ix);
14359 }
14360 last_newline_ix = Some(*newline_ix);
14361
14362 if let Some(rows_left) = &mut max_message_rows {
14363 if *rows_left == 0 {
14364 break;
14365 } else {
14366 *rows_left -= 1;
14367 }
14368 }
14369 let _ = newline_indices.next();
14370 } else {
14371 break;
14372 }
14373 }
14374 let prev_len = text_without_backticks.len();
14375 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
14376 text_without_backticks.push_str(new_text);
14377 if in_code_block {
14378 code_ranges.push(prev_len..text_without_backticks.len());
14379 }
14380 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
14381 in_code_block = !in_code_block;
14382 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
14383 text_without_backticks.push_str("...");
14384 break;
14385 }
14386 }
14387
14388 (text_without_backticks.into(), code_ranges)
14389}
14390
14391fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
14392 match severity {
14393 DiagnosticSeverity::ERROR => colors.error,
14394 DiagnosticSeverity::WARNING => colors.warning,
14395 DiagnosticSeverity::INFORMATION => colors.info,
14396 DiagnosticSeverity::HINT => colors.info,
14397 _ => colors.ignored,
14398 }
14399}
14400
14401pub fn styled_runs_for_code_label<'a>(
14402 label: &'a CodeLabel,
14403 syntax_theme: &'a theme::SyntaxTheme,
14404) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
14405 let fade_out = HighlightStyle {
14406 fade_out: Some(0.35),
14407 ..Default::default()
14408 };
14409
14410 let mut prev_end = label.filter_range.end;
14411 label
14412 .runs
14413 .iter()
14414 .enumerate()
14415 .flat_map(move |(ix, (range, highlight_id))| {
14416 let style = if let Some(style) = highlight_id.style(syntax_theme) {
14417 style
14418 } else {
14419 return Default::default();
14420 };
14421 let mut muted_style = style;
14422 muted_style.highlight(fade_out);
14423
14424 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
14425 if range.start >= label.filter_range.end {
14426 if range.start > prev_end {
14427 runs.push((prev_end..range.start, fade_out));
14428 }
14429 runs.push((range.clone(), muted_style));
14430 } else if range.end <= label.filter_range.end {
14431 runs.push((range.clone(), style));
14432 } else {
14433 runs.push((range.start..label.filter_range.end, style));
14434 runs.push((label.filter_range.end..range.end, muted_style));
14435 }
14436 prev_end = cmp::max(prev_end, range.end);
14437
14438 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
14439 runs.push((prev_end..label.text.len(), fade_out));
14440 }
14441
14442 runs
14443 })
14444}
14445
14446pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
14447 let mut prev_index = 0;
14448 let mut prev_codepoint: Option<char> = None;
14449 text.char_indices()
14450 .chain([(text.len(), '\0')])
14451 .filter_map(move |(index, codepoint)| {
14452 let prev_codepoint = prev_codepoint.replace(codepoint)?;
14453 let is_boundary = index == text.len()
14454 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
14455 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
14456 if is_boundary {
14457 let chunk = &text[prev_index..index];
14458 prev_index = index;
14459 Some(chunk)
14460 } else {
14461 None
14462 }
14463 })
14464}
14465
14466pub trait RangeToAnchorExt: Sized {
14467 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
14468
14469 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
14470 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
14471 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
14472 }
14473}
14474
14475impl<T: ToOffset> RangeToAnchorExt for Range<T> {
14476 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
14477 let start_offset = self.start.to_offset(snapshot);
14478 let end_offset = self.end.to_offset(snapshot);
14479 if start_offset == end_offset {
14480 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
14481 } else {
14482 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
14483 }
14484 }
14485}
14486
14487pub trait RowExt {
14488 fn as_f32(&self) -> f32;
14489
14490 fn next_row(&self) -> Self;
14491
14492 fn previous_row(&self) -> Self;
14493
14494 fn minus(&self, other: Self) -> u32;
14495}
14496
14497impl RowExt for DisplayRow {
14498 fn as_f32(&self) -> f32 {
14499 self.0 as f32
14500 }
14501
14502 fn next_row(&self) -> Self {
14503 Self(self.0 + 1)
14504 }
14505
14506 fn previous_row(&self) -> Self {
14507 Self(self.0.saturating_sub(1))
14508 }
14509
14510 fn minus(&self, other: Self) -> u32 {
14511 self.0 - other.0
14512 }
14513}
14514
14515impl RowExt for MultiBufferRow {
14516 fn as_f32(&self) -> f32 {
14517 self.0 as f32
14518 }
14519
14520 fn next_row(&self) -> Self {
14521 Self(self.0 + 1)
14522 }
14523
14524 fn previous_row(&self) -> Self {
14525 Self(self.0.saturating_sub(1))
14526 }
14527
14528 fn minus(&self, other: Self) -> u32 {
14529 self.0 - other.0
14530 }
14531}
14532
14533trait RowRangeExt {
14534 type Row;
14535
14536 fn len(&self) -> usize;
14537
14538 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
14539}
14540
14541impl RowRangeExt for Range<MultiBufferRow> {
14542 type Row = MultiBufferRow;
14543
14544 fn len(&self) -> usize {
14545 (self.end.0 - self.start.0) as usize
14546 }
14547
14548 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
14549 (self.start.0..self.end.0).map(MultiBufferRow)
14550 }
14551}
14552
14553impl RowRangeExt for Range<DisplayRow> {
14554 type Row = DisplayRow;
14555
14556 fn len(&self) -> usize {
14557 (self.end.0 - self.start.0) as usize
14558 }
14559
14560 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
14561 (self.start.0..self.end.0).map(DisplayRow)
14562 }
14563}
14564
14565fn hunk_status(hunk: &MultiBufferDiffHunk) -> DiffHunkStatus {
14566 if hunk.diff_base_byte_range.is_empty() {
14567 DiffHunkStatus::Added
14568 } else if hunk.row_range.is_empty() {
14569 DiffHunkStatus::Removed
14570 } else {
14571 DiffHunkStatus::Modified
14572 }
14573}
14574
14575/// If select range has more than one line, we
14576/// just point the cursor to range.start.
14577fn check_multiline_range(buffer: &Buffer, range: Range<usize>) -> Range<usize> {
14578 if buffer.offset_to_point(range.start).row == buffer.offset_to_point(range.end).row {
14579 range
14580 } else {
14581 range.start..range.start
14582 }
14583}
14584
14585pub struct KillRing(ClipboardItem);
14586impl Global for KillRing {}
14587
14588const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);