1#![allow(rustdoc::private_intra_doc_links)]
2//! This is the place where everything editor-related is stored (data-wise) and displayed (ui-wise).
3//! The main point of interest in this crate is [`Editor`] type, which is used in every other Zed part as a user input element.
4//! It comes in different flavors: single line, multiline and a fixed height one.
5//!
6//! Editor contains of multiple large submodules:
7//! * [`element`] — the place where all rendering happens
8//! * [`display_map`] - chunks up text in the editor into the logical blocks, establishes coordinates and mapping between each of them.
9//! Contains all metadata related to text transformations (folds, fake inlay text insertions, soft wraps, tab markup, etc.).
10//! * [`inlay_hint_cache`] - is a storage of inlay hints out of LSP requests, responsible for querying LSP and updating `display_map`'s state accordingly.
11//!
12//! All other submodules and structs are mostly concerned with holding editor data about the way it displays current buffer region(s).
13//!
14//! If you're looking to improve Vim mode, you should check out Vim crate that wraps Editor and overrides its behavior.
15pub mod actions;
16mod blame_entry_tooltip;
17mod blink_manager;
18mod clangd_ext;
19mod debounced_delay;
20pub mod display_map;
21mod editor_settings;
22mod editor_settings_controls;
23mod element;
24mod git;
25mod highlight_matching_bracket;
26mod hover_links;
27mod hover_popover;
28mod hunk_diff;
29mod indent_guides;
30mod inlay_hint_cache;
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;
45mod signature_help;
46#[cfg(any(test, feature = "test-support"))]
47pub mod test;
48
49use ::git::diff::DiffHunkStatus;
50pub(crate) use actions::*;
51pub use actions::{OpenExcerpts, OpenExcerptsSplit};
52use aho_corasick::AhoCorasick;
53use anyhow::{anyhow, Context as _, Result};
54use blink_manager::BlinkManager;
55use client::{Collaborator, ParticipantIndex};
56use clock::ReplicaId;
57use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
58use convert_case::{Case, Casing};
59use debounced_delay::DebouncedDelay;
60use display_map::*;
61pub use display_map::{DisplayPoint, FoldPlaceholder};
62pub use editor_settings::{
63 CurrentLineHighlight, EditorSettings, ScrollBeyondLastLine, SearchSettings, ShowScrollbar,
64};
65pub use editor_settings_controls::*;
66use element::LineWithInvisibles;
67pub use element::{
68 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
69};
70use futures::{future, FutureExt};
71use fuzzy::{StringMatch, StringMatchCandidate};
72use git::blame::GitBlame;
73use gpui::{
74 div, impl_actions, point, prelude::*, px, relative, size, uniform_list, Action, AnyElement,
75 AppContext, AsyncWindowContext, AvailableSpace, BackgroundExecutor, Bounds, ClipboardEntry,
76 ClipboardItem, Context, DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusOutEvent,
77 FocusableView, FontId, FontWeight, Global, HighlightStyle, Hsla, InteractiveText, KeyContext,
78 ListSizingBehavior, Model, ModelContext, MouseButton, PaintQuad, ParentElement, Pixels, Render,
79 ScrollStrategy, SharedString, Size, StrikethroughStyle, Styled, StyledText, Subscription, Task,
80 TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle, UniformListScrollHandle, View,
81 ViewContext, ViewInputHandler, VisualContext, WeakFocusHandle, WeakView, WindowContext,
82};
83use highlight_matching_bracket::refresh_matching_bracket_highlights;
84use hover_popover::{hide_hover, HoverState};
85pub(crate) use hunk_diff::HoveredHunk;
86use hunk_diff::{diff_hunk_to_display, DiffMap, DiffMapSnapshot};
87use indent_guides::ActiveIndentGuidesState;
88use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
89pub use inline_completion::Direction;
90use inline_completion::{InlayProposal, InlineCompletionProvider, InlineCompletionProviderHandle};
91pub use items::MAX_TAB_TITLE_LEN;
92use itertools::Itertools;
93use language::{
94 language_settings::{self, all_language_settings, language_settings, InlayHintSettings},
95 markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
96 CursorShape, Diagnostic, Documentation, IndentKind, IndentSize, Language, OffsetRangeExt,
97 Point, Selection, SelectionGoal, TransactionId,
98};
99use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
100use linked_editing_ranges::refresh_linked_ranges;
101pub use proposed_changes_editor::{
102 ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar,
103};
104use similar::{ChangeTag, TextDiff};
105use std::iter::Peekable;
106use task::{ResolvedTask, TaskTemplate, TaskVariables};
107
108use hover_links::{find_file, HoverLink, HoveredLinkState, InlayHighlight};
109pub use lsp::CompletionContext;
110use lsp::{
111 CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity, InsertTextFormat,
112 LanguageServerId, LanguageServerName,
113};
114use mouse_context_menu::MouseContextMenu;
115use movement::TextLayoutDetails;
116pub use multi_buffer::{
117 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
118 ToPoint,
119};
120use multi_buffer::{
121 ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow, ToOffsetUtf16,
122};
123use ordered_float::OrderedFloat;
124use parking_lot::{Mutex, RwLock};
125use project::{
126 lsp_store::{FormatTarget, FormatTrigger},
127 project_settings::{GitGutterSetting, ProjectSettings},
128 CodeAction, Completion, CompletionIntent, DocumentHighlight, InlayHint, Location, LocationLink,
129 Project, ProjectItem, ProjectTransaction, TaskSourceKind,
130};
131use rand::prelude::*;
132use rpc::{proto::*, ErrorExt};
133use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
134use selections_collection::{
135 resolve_selections, MutableSelectionsCollection, SelectionsCollection,
136};
137use serde::{Deserialize, Serialize};
138use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
139use smallvec::SmallVec;
140use snippet::Snippet;
141use std::{
142 any::TypeId,
143 borrow::Cow,
144 cell::RefCell,
145 cmp::{self, Ordering, Reverse},
146 mem,
147 num::NonZeroU32,
148 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
149 path::{Path, PathBuf},
150 rc::Rc,
151 sync::Arc,
152 time::{Duration, Instant},
153};
154pub use sum_tree::Bias;
155use sum_tree::TreeMap;
156use text::{BufferId, OffsetUtf16, Rope};
157use theme::{
158 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
159 ThemeColors, ThemeSettings,
160};
161use ui::{
162 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize,
163 ListItem, Popover, PopoverMenuHandle, Tooltip,
164};
165use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
166use workspace::item::{ItemHandle, PreviewTabsSettings};
167use workspace::notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt};
168use workspace::{
169 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
170};
171use workspace::{Item as WorkspaceItem, OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
172
173use crate::hover_links::find_url;
174use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
175
176pub const FILE_HEADER_HEIGHT: u32 = 2;
177pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
178pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u32 = 1;
179pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
180const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
181const MAX_LINE_LEN: usize = 1024;
182const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
183const MAX_SELECTION_HISTORY_LEN: usize = 1024;
184pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
185#[doc(hidden)]
186pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
187#[doc(hidden)]
188pub const DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
189
190pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
191pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
192
193pub fn render_parsed_markdown(
194 element_id: impl Into<ElementId>,
195 parsed: &language::ParsedMarkdown,
196 editor_style: &EditorStyle,
197 workspace: Option<WeakView<Workspace>>,
198 cx: &mut WindowContext,
199) -> InteractiveText {
200 let code_span_background_color = cx
201 .theme()
202 .colors()
203 .editor_document_highlight_read_background;
204
205 let highlights = gpui::combine_highlights(
206 parsed.highlights.iter().filter_map(|(range, highlight)| {
207 let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
208 Some((range.clone(), highlight))
209 }),
210 parsed
211 .regions
212 .iter()
213 .zip(&parsed.region_ranges)
214 .filter_map(|(region, range)| {
215 if region.code {
216 Some((
217 range.clone(),
218 HighlightStyle {
219 background_color: Some(code_span_background_color),
220 ..Default::default()
221 },
222 ))
223 } else {
224 None
225 }
226 }),
227 );
228
229 let mut links = Vec::new();
230 let mut link_ranges = Vec::new();
231 for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
232 if let Some(link) = region.link.clone() {
233 links.push(link);
234 link_ranges.push(range.clone());
235 }
236 }
237
238 InteractiveText::new(
239 element_id,
240 StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
241 )
242 .on_click(link_ranges, move |clicked_range_ix, cx| {
243 match &links[clicked_range_ix] {
244 markdown::Link::Web { url } => cx.open_url(url),
245 markdown::Link::Path { path } => {
246 if let Some(workspace) = &workspace {
247 _ = workspace.update(cx, |workspace, cx| {
248 workspace.open_abs_path(path.clone(), false, cx).detach();
249 });
250 }
251 }
252 }
253 })
254}
255
256#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
257pub(crate) enum InlayId {
258 Suggestion(usize),
259 Hint(usize),
260}
261
262impl InlayId {
263 fn id(&self) -> usize {
264 match self {
265 Self::Suggestion(id) => *id,
266 Self::Hint(id) => *id,
267 }
268 }
269}
270
271enum DiffRowHighlight {}
272enum DocumentHighlightRead {}
273enum DocumentHighlightWrite {}
274enum InputComposition {}
275
276#[derive(Debug, Copy, Clone, PartialEq, Eq)]
277pub enum Navigated {
278 Yes,
279 No,
280}
281
282impl Navigated {
283 pub fn from_bool(yes: bool) -> Navigated {
284 if yes {
285 Navigated::Yes
286 } else {
287 Navigated::No
288 }
289 }
290}
291
292pub fn init_settings(cx: &mut AppContext) {
293 EditorSettings::register(cx);
294}
295
296pub fn init(cx: &mut AppContext) {
297 init_settings(cx);
298
299 workspace::register_project_item::<Editor>(cx);
300 workspace::FollowableViewRegistry::register::<Editor>(cx);
301 workspace::register_serializable_item::<Editor>(cx);
302
303 cx.observe_new_views(
304 |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
305 workspace.register_action(Editor::new_file);
306 workspace.register_action(Editor::new_file_vertical);
307 workspace.register_action(Editor::new_file_horizontal);
308 },
309 )
310 .detach();
311
312 cx.on_action(move |_: &workspace::NewFile, cx| {
313 let app_state = workspace::AppState::global(cx);
314 if let Some(app_state) = app_state.upgrade() {
315 workspace::open_new(Default::default(), app_state, cx, |workspace, cx| {
316 Editor::new_file(workspace, &Default::default(), cx)
317 })
318 .detach();
319 }
320 });
321 cx.on_action(move |_: &workspace::NewWindow, cx| {
322 let app_state = workspace::AppState::global(cx);
323 if let Some(app_state) = app_state.upgrade() {
324 workspace::open_new(Default::default(), app_state, cx, |workspace, cx| {
325 Editor::new_file(workspace, &Default::default(), cx)
326 })
327 .detach();
328 }
329 });
330 git::project_diff::init(cx);
331}
332
333pub struct SearchWithinRange;
334
335trait InvalidationRegion {
336 fn ranges(&self) -> &[Range<Anchor>];
337}
338
339#[derive(Clone, Debug, PartialEq)]
340pub enum SelectPhase {
341 Begin {
342 position: DisplayPoint,
343 add: bool,
344 click_count: usize,
345 },
346 BeginColumnar {
347 position: DisplayPoint,
348 reset: bool,
349 goal_column: u32,
350 },
351 Extend {
352 position: DisplayPoint,
353 click_count: usize,
354 },
355 Update {
356 position: DisplayPoint,
357 goal_column: u32,
358 scroll_delta: gpui::Point<f32>,
359 },
360 End,
361}
362
363#[derive(Clone, Debug)]
364pub enum SelectMode {
365 Character,
366 Word(Range<Anchor>),
367 Line(Range<Anchor>),
368 All,
369}
370
371#[derive(Copy, Clone, PartialEq, Eq, Debug)]
372pub enum EditorMode {
373 SingleLine { auto_width: bool },
374 AutoHeight { max_lines: usize },
375 Full,
376}
377
378#[derive(Copy, Clone, Debug)]
379pub enum SoftWrap {
380 /// Prefer not to wrap at all.
381 ///
382 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
383 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
384 GitDiff,
385 /// Prefer a single line generally, unless an overly long line is encountered.
386 None,
387 /// Soft wrap lines that exceed the editor width.
388 EditorWidth,
389 /// Soft wrap lines at the preferred line length.
390 Column(u32),
391 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
392 Bounded(u32),
393}
394
395#[derive(Clone)]
396pub struct EditorStyle {
397 pub background: Hsla,
398 pub local_player: PlayerColor,
399 pub text: TextStyle,
400 pub scrollbar_width: Pixels,
401 pub syntax: Arc<SyntaxTheme>,
402 pub status: StatusColors,
403 pub inlay_hints_style: HighlightStyle,
404 pub suggestions_style: HighlightStyle,
405 pub unnecessary_code_fade: f32,
406}
407
408impl Default for EditorStyle {
409 fn default() -> Self {
410 Self {
411 background: Hsla::default(),
412 local_player: PlayerColor::default(),
413 text: TextStyle::default(),
414 scrollbar_width: Pixels::default(),
415 syntax: Default::default(),
416 // HACK: Status colors don't have a real default.
417 // We should look into removing the status colors from the editor
418 // style and retrieve them directly from the theme.
419 status: StatusColors::dark(),
420 inlay_hints_style: HighlightStyle::default(),
421 suggestions_style: HighlightStyle::default(),
422 unnecessary_code_fade: Default::default(),
423 }
424 }
425}
426
427pub fn make_inlay_hints_style(cx: &WindowContext) -> HighlightStyle {
428 let show_background = language_settings::language_settings(None, None, cx)
429 .inlay_hints
430 .show_background;
431
432 HighlightStyle {
433 color: Some(cx.theme().status().hint),
434 background_color: show_background.then(|| cx.theme().status().hint_background),
435 ..HighlightStyle::default()
436 }
437}
438
439type CompletionId = usize;
440
441#[derive(Clone, Debug)]
442struct CompletionState {
443 // render_inlay_ids represents the inlay hints that are inserted
444 // for rendering the inline completions. They may be discontinuous
445 // in the event that the completion provider returns some intersection
446 // with the existing content.
447 render_inlay_ids: Vec<InlayId>,
448 // text is the resulting rope that is inserted when the user accepts a completion.
449 text: Rope,
450 // position is the position of the cursor when the completion was triggered.
451 position: multi_buffer::Anchor,
452 // delete_range is the range of text that this completion state covers.
453 // if the completion is accepted, this range should be deleted.
454 delete_range: Option<Range<multi_buffer::Anchor>>,
455}
456
457#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
458struct EditorActionId(usize);
459
460impl EditorActionId {
461 pub fn post_inc(&mut self) -> Self {
462 let answer = self.0;
463
464 *self = Self(answer + 1);
465
466 Self(answer)
467 }
468}
469
470// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
471// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
472
473type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
474type GutterHighlight = (fn(&AppContext) -> Hsla, Arc<[Range<Anchor>]>);
475
476#[derive(Default)]
477struct ScrollbarMarkerState {
478 scrollbar_size: Size<Pixels>,
479 dirty: bool,
480 markers: Arc<[PaintQuad]>,
481 pending_refresh: Option<Task<Result<()>>>,
482}
483
484impl ScrollbarMarkerState {
485 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
486 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
487 }
488}
489
490#[derive(Clone, Debug)]
491struct RunnableTasks {
492 templates: Vec<(TaskSourceKind, TaskTemplate)>,
493 offset: MultiBufferOffset,
494 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
495 column: u32,
496 // Values of all named captures, including those starting with '_'
497 extra_variables: HashMap<String, String>,
498 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
499 context_range: Range<BufferOffset>,
500}
501
502impl RunnableTasks {
503 fn resolve<'a>(
504 &'a self,
505 cx: &'a task::TaskContext,
506 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
507 self.templates.iter().filter_map(|(kind, template)| {
508 template
509 .resolve_task(&kind.to_id_base(), cx)
510 .map(|task| (kind.clone(), task))
511 })
512 }
513}
514
515#[derive(Clone)]
516struct ResolvedTasks {
517 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
518 position: Anchor,
519}
520#[derive(Copy, Clone, Debug)]
521struct MultiBufferOffset(usize);
522#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
523struct BufferOffset(usize);
524
525// Addons allow storing per-editor state in other crates (e.g. Vim)
526pub trait Addon: 'static {
527 fn extend_key_context(&self, _: &mut KeyContext, _: &AppContext) {}
528
529 fn to_any(&self) -> &dyn std::any::Any;
530}
531
532#[derive(Debug, Copy, Clone, PartialEq, Eq)]
533pub enum IsVimMode {
534 Yes,
535 No,
536}
537
538/// Zed's primary text input `View`, allowing users to edit a [`MultiBuffer`]
539///
540/// See the [module level documentation](self) for more information.
541pub struct Editor {
542 focus_handle: FocusHandle,
543 last_focused_descendant: Option<WeakFocusHandle>,
544 /// The text buffer being edited
545 buffer: Model<MultiBuffer>,
546 /// Map of how text in the buffer should be displayed.
547 /// Handles soft wraps, folds, fake inlay text insertions, etc.
548 pub display_map: Model<DisplayMap>,
549 pub selections: SelectionsCollection,
550 pub scroll_manager: ScrollManager,
551 /// When inline assist editors are linked, they all render cursors because
552 /// typing enters text into each of them, even the ones that aren't focused.
553 pub(crate) show_cursor_when_unfocused: bool,
554 columnar_selection_tail: Option<Anchor>,
555 add_selections_state: Option<AddSelectionsState>,
556 select_next_state: Option<SelectNextState>,
557 select_prev_state: Option<SelectNextState>,
558 selection_history: SelectionHistory,
559 autoclose_regions: Vec<AutocloseRegion>,
560 snippet_stack: InvalidationStack<SnippetState>,
561 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
562 ime_transaction: Option<TransactionId>,
563 active_diagnostics: Option<ActiveDiagnosticGroup>,
564 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
565
566 project: Option<Model<Project>>,
567 semantics_provider: Option<Rc<dyn SemanticsProvider>>,
568 completion_provider: Option<Box<dyn CompletionProvider>>,
569 collaboration_hub: Option<Box<dyn CollaborationHub>>,
570 blink_manager: Model<BlinkManager>,
571 show_cursor_names: bool,
572 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
573 pub show_local_selections: bool,
574 mode: EditorMode,
575 show_breadcrumbs: bool,
576 show_gutter: bool,
577 show_line_numbers: Option<bool>,
578 use_relative_line_numbers: Option<bool>,
579 show_git_diff_gutter: Option<bool>,
580 show_code_actions: Option<bool>,
581 show_runnables: Option<bool>,
582 show_wrap_guides: Option<bool>,
583 show_indent_guides: Option<bool>,
584 placeholder_text: Option<Arc<str>>,
585 highlight_order: usize,
586 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
587 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
588 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
589 scrollbar_marker_state: ScrollbarMarkerState,
590 active_indent_guides_state: ActiveIndentGuidesState,
591 nav_history: Option<ItemNavHistory>,
592 context_menu: RwLock<Option<ContextMenu>>,
593 mouse_context_menu: Option<MouseContextMenu>,
594 hunk_controls_menu_handle: PopoverMenuHandle<ui::ContextMenu>,
595 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
596 signature_help_state: SignatureHelpState,
597 auto_signature_help: Option<bool>,
598 find_all_references_task_sources: Vec<Anchor>,
599 next_completion_id: CompletionId,
600 available_code_actions: Option<(Location, Arc<[AvailableCodeAction]>)>,
601 code_actions_task: Option<Task<Result<()>>>,
602 document_highlights_task: Option<Task<()>>,
603 linked_editing_range_task: Option<Task<Option<()>>>,
604 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
605 pending_rename: Option<RenameState>,
606 searchable: bool,
607 cursor_shape: CursorShape,
608 current_line_highlight: Option<CurrentLineHighlight>,
609 collapse_matches: bool,
610 autoindent_mode: Option<AutoindentMode>,
611 workspace: Option<(WeakView<Workspace>, Option<WorkspaceId>)>,
612 input_enabled: bool,
613 use_modal_editing: bool,
614 read_only: bool,
615 leader_peer_id: Option<PeerId>,
616 remote_id: Option<ViewId>,
617 hover_state: HoverState,
618 gutter_hovered: bool,
619 hovered_link_state: Option<HoveredLinkState>,
620 inline_completion_provider: Option<RegisteredInlineCompletionProvider>,
621 code_action_providers: Vec<Arc<dyn CodeActionProvider>>,
622 active_inline_completion: Option<CompletionState>,
623 // enable_inline_completions is a switch that Vim can use to disable
624 // inline completions based on its mode.
625 enable_inline_completions: bool,
626 show_inline_completions_override: Option<bool>,
627 inlay_hint_cache: InlayHintCache,
628 diff_map: DiffMap,
629 next_inlay_id: usize,
630 _subscriptions: Vec<Subscription>,
631 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
632 gutter_dimensions: GutterDimensions,
633 style: Option<EditorStyle>,
634 text_style_refinement: Option<TextStyleRefinement>,
635 next_editor_action_id: EditorActionId,
636 editor_actions: Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut ViewContext<Self>)>>>>,
637 use_autoclose: bool,
638 use_auto_surround: bool,
639 auto_replace_emoji_shortcode: bool,
640 show_git_blame_gutter: bool,
641 show_git_blame_inline: bool,
642 show_git_blame_inline_delay_task: Option<Task<()>>,
643 git_blame_inline_enabled: bool,
644 serialize_dirty_buffers: bool,
645 show_selection_menu: Option<bool>,
646 blame: Option<Model<GitBlame>>,
647 blame_subscription: Option<Subscription>,
648 custom_context_menu: Option<
649 Box<
650 dyn 'static
651 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
652 >,
653 >,
654 last_bounds: Option<Bounds<Pixels>>,
655 expect_bounds_change: Option<Bounds<Pixels>>,
656 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
657 tasks_update_task: Option<Task<()>>,
658 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
659 breadcrumb_header: Option<String>,
660 focused_block: Option<FocusedBlock>,
661 next_scroll_position: NextScrollCursorCenterTopBottom,
662 addons: HashMap<TypeId, Box<dyn Addon>>,
663 _scroll_cursor_center_top_bottom_task: Task<()>,
664}
665
666#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
667enum NextScrollCursorCenterTopBottom {
668 #[default]
669 Center,
670 Top,
671 Bottom,
672}
673
674impl NextScrollCursorCenterTopBottom {
675 fn next(&self) -> Self {
676 match self {
677 Self::Center => Self::Top,
678 Self::Top => Self::Bottom,
679 Self::Bottom => Self::Center,
680 }
681 }
682}
683
684#[derive(Clone)]
685pub struct EditorSnapshot {
686 pub mode: EditorMode,
687 show_gutter: bool,
688 show_line_numbers: Option<bool>,
689 show_git_diff_gutter: Option<bool>,
690 show_code_actions: Option<bool>,
691 show_runnables: Option<bool>,
692 git_blame_gutter_max_author_length: Option<usize>,
693 pub display_snapshot: DisplaySnapshot,
694 pub placeholder_text: Option<Arc<str>>,
695 diff_map: DiffMapSnapshot,
696 is_focused: bool,
697 scroll_anchor: ScrollAnchor,
698 ongoing_scroll: OngoingScroll,
699 current_line_highlight: CurrentLineHighlight,
700 gutter_hovered: bool,
701}
702
703const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
704
705#[derive(Default, Debug, Clone, Copy)]
706pub struct GutterDimensions {
707 pub left_padding: Pixels,
708 pub right_padding: Pixels,
709 pub width: Pixels,
710 pub margin: Pixels,
711 pub git_blame_entries_width: Option<Pixels>,
712}
713
714impl GutterDimensions {
715 /// The full width of the space taken up by the gutter.
716 pub fn full_width(&self) -> Pixels {
717 self.margin + self.width
718 }
719
720 /// The width of the space reserved for the fold indicators,
721 /// use alongside 'justify_end' and `gutter_width` to
722 /// right align content with the line numbers
723 pub fn fold_area_width(&self) -> Pixels {
724 self.margin + self.right_padding
725 }
726}
727
728#[derive(Debug)]
729pub struct RemoteSelection {
730 pub replica_id: ReplicaId,
731 pub selection: Selection<Anchor>,
732 pub cursor_shape: CursorShape,
733 pub peer_id: PeerId,
734 pub line_mode: bool,
735 pub participant_index: Option<ParticipantIndex>,
736 pub user_name: Option<SharedString>,
737}
738
739#[derive(Clone, Debug)]
740struct SelectionHistoryEntry {
741 selections: Arc<[Selection<Anchor>]>,
742 select_next_state: Option<SelectNextState>,
743 select_prev_state: Option<SelectNextState>,
744 add_selections_state: Option<AddSelectionsState>,
745}
746
747enum SelectionHistoryMode {
748 Normal,
749 Undoing,
750 Redoing,
751}
752
753#[derive(Clone, PartialEq, Eq, Hash)]
754struct HoveredCursor {
755 replica_id: u16,
756 selection_id: usize,
757}
758
759impl Default for SelectionHistoryMode {
760 fn default() -> Self {
761 Self::Normal
762 }
763}
764
765#[derive(Default)]
766struct SelectionHistory {
767 #[allow(clippy::type_complexity)]
768 selections_by_transaction:
769 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
770 mode: SelectionHistoryMode,
771 undo_stack: VecDeque<SelectionHistoryEntry>,
772 redo_stack: VecDeque<SelectionHistoryEntry>,
773}
774
775impl SelectionHistory {
776 fn insert_transaction(
777 &mut self,
778 transaction_id: TransactionId,
779 selections: Arc<[Selection<Anchor>]>,
780 ) {
781 self.selections_by_transaction
782 .insert(transaction_id, (selections, None));
783 }
784
785 #[allow(clippy::type_complexity)]
786 fn transaction(
787 &self,
788 transaction_id: TransactionId,
789 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
790 self.selections_by_transaction.get(&transaction_id)
791 }
792
793 #[allow(clippy::type_complexity)]
794 fn transaction_mut(
795 &mut self,
796 transaction_id: TransactionId,
797 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
798 self.selections_by_transaction.get_mut(&transaction_id)
799 }
800
801 fn push(&mut self, entry: SelectionHistoryEntry) {
802 if !entry.selections.is_empty() {
803 match self.mode {
804 SelectionHistoryMode::Normal => {
805 self.push_undo(entry);
806 self.redo_stack.clear();
807 }
808 SelectionHistoryMode::Undoing => self.push_redo(entry),
809 SelectionHistoryMode::Redoing => self.push_undo(entry),
810 }
811 }
812 }
813
814 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
815 if self
816 .undo_stack
817 .back()
818 .map_or(true, |e| e.selections != entry.selections)
819 {
820 self.undo_stack.push_back(entry);
821 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
822 self.undo_stack.pop_front();
823 }
824 }
825 }
826
827 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
828 if self
829 .redo_stack
830 .back()
831 .map_or(true, |e| e.selections != entry.selections)
832 {
833 self.redo_stack.push_back(entry);
834 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
835 self.redo_stack.pop_front();
836 }
837 }
838 }
839}
840
841struct RowHighlight {
842 index: usize,
843 range: Range<Anchor>,
844 color: Hsla,
845 should_autoscroll: bool,
846}
847
848#[derive(Clone, Debug)]
849struct AddSelectionsState {
850 above: bool,
851 stack: Vec<usize>,
852}
853
854#[derive(Clone)]
855struct SelectNextState {
856 query: AhoCorasick,
857 wordwise: bool,
858 done: bool,
859}
860
861impl std::fmt::Debug for SelectNextState {
862 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
863 f.debug_struct(std::any::type_name::<Self>())
864 .field("wordwise", &self.wordwise)
865 .field("done", &self.done)
866 .finish()
867 }
868}
869
870#[derive(Debug)]
871struct AutocloseRegion {
872 selection_id: usize,
873 range: Range<Anchor>,
874 pair: BracketPair,
875}
876
877#[derive(Debug)]
878struct SnippetState {
879 ranges: Vec<Vec<Range<Anchor>>>,
880 active_index: usize,
881 choices: Vec<Option<Vec<String>>>,
882}
883
884#[doc(hidden)]
885pub struct RenameState {
886 pub range: Range<Anchor>,
887 pub old_name: Arc<str>,
888 pub editor: View<Editor>,
889 block_id: CustomBlockId,
890}
891
892struct InvalidationStack<T>(Vec<T>);
893
894struct RegisteredInlineCompletionProvider {
895 provider: Arc<dyn InlineCompletionProviderHandle>,
896 _subscription: Subscription,
897}
898
899enum ContextMenu {
900 Completions(CompletionsMenu),
901 CodeActions(CodeActionsMenu),
902}
903
904impl ContextMenu {
905 fn select_first(
906 &mut self,
907 provider: Option<&dyn CompletionProvider>,
908 cx: &mut ViewContext<Editor>,
909 ) -> bool {
910 if self.visible() {
911 match self {
912 ContextMenu::Completions(menu) => menu.select_first(provider, cx),
913 ContextMenu::CodeActions(menu) => menu.select_first(cx),
914 }
915 true
916 } else {
917 false
918 }
919 }
920
921 fn select_prev(
922 &mut self,
923 provider: Option<&dyn CompletionProvider>,
924 cx: &mut ViewContext<Editor>,
925 ) -> bool {
926 if self.visible() {
927 match self {
928 ContextMenu::Completions(menu) => menu.select_prev(provider, cx),
929 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
930 }
931 true
932 } else {
933 false
934 }
935 }
936
937 fn select_next(
938 &mut self,
939 provider: Option<&dyn CompletionProvider>,
940 cx: &mut ViewContext<Editor>,
941 ) -> bool {
942 if self.visible() {
943 match self {
944 ContextMenu::Completions(menu) => menu.select_next(provider, cx),
945 ContextMenu::CodeActions(menu) => menu.select_next(cx),
946 }
947 true
948 } else {
949 false
950 }
951 }
952
953 fn select_last(
954 &mut self,
955 provider: Option<&dyn CompletionProvider>,
956 cx: &mut ViewContext<Editor>,
957 ) -> bool {
958 if self.visible() {
959 match self {
960 ContextMenu::Completions(menu) => menu.select_last(provider, cx),
961 ContextMenu::CodeActions(menu) => menu.select_last(cx),
962 }
963 true
964 } else {
965 false
966 }
967 }
968
969 fn visible(&self) -> bool {
970 match self {
971 ContextMenu::Completions(menu) => menu.visible(),
972 ContextMenu::CodeActions(menu) => menu.visible(),
973 }
974 }
975
976 fn render(
977 &self,
978 cursor_position: DisplayPoint,
979 style: &EditorStyle,
980 max_height: Pixels,
981 workspace: Option<WeakView<Workspace>>,
982 cx: &mut ViewContext<Editor>,
983 ) -> (ContextMenuOrigin, AnyElement) {
984 match self {
985 ContextMenu::Completions(menu) => (
986 ContextMenuOrigin::EditorPoint(cursor_position),
987 menu.render(style, max_height, workspace, cx),
988 ),
989 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, max_height, cx),
990 }
991 }
992}
993
994enum ContextMenuOrigin {
995 EditorPoint(DisplayPoint),
996 GutterIndicator(DisplayRow),
997}
998
999#[derive(Clone, Debug)]
1000struct CompletionsMenu {
1001 id: CompletionId,
1002 sort_completions: bool,
1003 initial_position: Anchor,
1004 buffer: Model<Buffer>,
1005 completions: Arc<RwLock<Box<[Completion]>>>,
1006 match_candidates: Arc<[StringMatchCandidate]>,
1007 matches: Arc<[StringMatch]>,
1008 selected_item: usize,
1009 scroll_handle: UniformListScrollHandle,
1010 selected_completion_resolve_debounce: Option<Arc<Mutex<DebouncedDelay>>>,
1011}
1012
1013impl CompletionsMenu {
1014 fn new(
1015 id: CompletionId,
1016 sort_completions: bool,
1017 initial_position: Anchor,
1018 buffer: Model<Buffer>,
1019 completions: Box<[Completion]>,
1020 ) -> Self {
1021 let match_candidates = completions
1022 .iter()
1023 .enumerate()
1024 .map(|(id, completion)| {
1025 StringMatchCandidate::new(
1026 id,
1027 completion.label.text[completion.label.filter_range.clone()].into(),
1028 )
1029 })
1030 .collect();
1031
1032 Self {
1033 id,
1034 sort_completions,
1035 initial_position,
1036 buffer,
1037 completions: Arc::new(RwLock::new(completions)),
1038 match_candidates,
1039 matches: Vec::new().into(),
1040 selected_item: 0,
1041 scroll_handle: UniformListScrollHandle::new(),
1042 selected_completion_resolve_debounce: Some(Arc::new(Mutex::new(DebouncedDelay::new()))),
1043 }
1044 }
1045
1046 fn new_snippet_choices(
1047 id: CompletionId,
1048 sort_completions: bool,
1049 choices: &Vec<String>,
1050 selection: Range<Anchor>,
1051 buffer: Model<Buffer>,
1052 ) -> Self {
1053 let completions = choices
1054 .iter()
1055 .map(|choice| Completion {
1056 old_range: selection.start.text_anchor..selection.end.text_anchor,
1057 new_text: choice.to_string(),
1058 label: CodeLabel {
1059 text: choice.to_string(),
1060 runs: Default::default(),
1061 filter_range: Default::default(),
1062 },
1063 server_id: LanguageServerId(usize::MAX),
1064 documentation: None,
1065 lsp_completion: Default::default(),
1066 confirm: None,
1067 })
1068 .collect();
1069
1070 let match_candidates = choices
1071 .iter()
1072 .enumerate()
1073 .map(|(id, completion)| StringMatchCandidate::new(id, completion.to_string()))
1074 .collect();
1075 let matches = choices
1076 .iter()
1077 .enumerate()
1078 .map(|(id, completion)| StringMatch {
1079 candidate_id: id,
1080 score: 1.,
1081 positions: vec![],
1082 string: completion.clone(),
1083 })
1084 .collect();
1085 Self {
1086 id,
1087 sort_completions,
1088 initial_position: selection.start,
1089 buffer,
1090 completions: Arc::new(RwLock::new(completions)),
1091 match_candidates,
1092 matches,
1093 selected_item: 0,
1094 scroll_handle: UniformListScrollHandle::new(),
1095 selected_completion_resolve_debounce: Some(Arc::new(Mutex::new(DebouncedDelay::new()))),
1096 }
1097 }
1098
1099 fn suppress_documentation_resolution(mut self) -> Self {
1100 self.selected_completion_resolve_debounce.take();
1101 self
1102 }
1103
1104 fn select_first(
1105 &mut self,
1106 provider: Option<&dyn CompletionProvider>,
1107 cx: &mut ViewContext<Editor>,
1108 ) {
1109 self.selected_item = 0;
1110 self.scroll_handle
1111 .scroll_to_item(self.selected_item, ScrollStrategy::Top);
1112 self.resolve_selected_completion(provider, cx);
1113 cx.notify();
1114 }
1115
1116 fn select_prev(
1117 &mut self,
1118 provider: Option<&dyn CompletionProvider>,
1119 cx: &mut ViewContext<Editor>,
1120 ) {
1121 if self.selected_item > 0 {
1122 self.selected_item -= 1;
1123 } else {
1124 self.selected_item = self.matches.len() - 1;
1125 }
1126 self.scroll_handle
1127 .scroll_to_item(self.selected_item, ScrollStrategy::Top);
1128 self.resolve_selected_completion(provider, cx);
1129 cx.notify();
1130 }
1131
1132 fn select_next(
1133 &mut self,
1134 provider: Option<&dyn CompletionProvider>,
1135 cx: &mut ViewContext<Editor>,
1136 ) {
1137 if self.selected_item + 1 < self.matches.len() {
1138 self.selected_item += 1;
1139 } else {
1140 self.selected_item = 0;
1141 }
1142 self.scroll_handle
1143 .scroll_to_item(self.selected_item, ScrollStrategy::Top);
1144 self.resolve_selected_completion(provider, cx);
1145 cx.notify();
1146 }
1147
1148 fn select_last(
1149 &mut self,
1150 provider: Option<&dyn CompletionProvider>,
1151 cx: &mut ViewContext<Editor>,
1152 ) {
1153 self.selected_item = self.matches.len() - 1;
1154 self.scroll_handle
1155 .scroll_to_item(self.selected_item, ScrollStrategy::Top);
1156 self.resolve_selected_completion(provider, cx);
1157 cx.notify();
1158 }
1159
1160 fn resolve_selected_completion(
1161 &mut self,
1162 provider: Option<&dyn CompletionProvider>,
1163 cx: &mut ViewContext<Editor>,
1164 ) {
1165 let completion_index = self.matches[self.selected_item].candidate_id;
1166 let Some(provider) = provider else {
1167 return;
1168 };
1169 let Some(completion_resolve) = self.selected_completion_resolve_debounce.as_ref() else {
1170 return;
1171 };
1172
1173 let resolve_task = provider.resolve_completions(
1174 self.buffer.clone(),
1175 vec![completion_index],
1176 self.completions.clone(),
1177 cx,
1178 );
1179
1180 let delay_ms =
1181 EditorSettings::get_global(cx).completion_documentation_secondary_query_debounce;
1182 let delay = Duration::from_millis(delay_ms);
1183
1184 completion_resolve.lock().fire_new(delay, cx, |_, cx| {
1185 cx.spawn(move |editor, mut cx| async move {
1186 if let Some(true) = resolve_task.await.log_err() {
1187 editor.update(&mut cx, |_, cx| cx.notify()).ok();
1188 }
1189 })
1190 });
1191 }
1192
1193 fn visible(&self) -> bool {
1194 !self.matches.is_empty()
1195 }
1196
1197 fn render(
1198 &self,
1199 style: &EditorStyle,
1200 max_height: Pixels,
1201 workspace: Option<WeakView<Workspace>>,
1202 cx: &mut ViewContext<Editor>,
1203 ) -> AnyElement {
1204 let settings = EditorSettings::get_global(cx);
1205 let show_completion_documentation = settings.show_completion_documentation;
1206
1207 let widest_completion_ix = self
1208 .matches
1209 .iter()
1210 .enumerate()
1211 .max_by_key(|(_, mat)| {
1212 let completions = self.completions.read();
1213 let completion = &completions[mat.candidate_id];
1214 let documentation = &completion.documentation;
1215
1216 let mut len = completion.label.text.chars().count();
1217 if let Some(Documentation::SingleLine(text)) = documentation {
1218 if show_completion_documentation {
1219 len += text.chars().count();
1220 }
1221 }
1222
1223 len
1224 })
1225 .map(|(ix, _)| ix);
1226
1227 let completions = self.completions.clone();
1228 let matches = self.matches.clone();
1229 let selected_item = self.selected_item;
1230 let style = style.clone();
1231
1232 let multiline_docs = if show_completion_documentation {
1233 let mat = &self.matches[selected_item];
1234 let multiline_docs = match &self.completions.read()[mat.candidate_id].documentation {
1235 Some(Documentation::MultiLinePlainText(text)) => {
1236 Some(div().child(SharedString::from(text.clone())))
1237 }
1238 Some(Documentation::MultiLineMarkdown(parsed)) if !parsed.text.is_empty() => {
1239 Some(div().child(render_parsed_markdown(
1240 "completions_markdown",
1241 parsed,
1242 &style,
1243 workspace,
1244 cx,
1245 )))
1246 }
1247 _ => None,
1248 };
1249 multiline_docs.map(|div| {
1250 div.id("multiline_docs")
1251 .max_h(max_height)
1252 .flex_1()
1253 .px_1p5()
1254 .py_1()
1255 .min_w(px(260.))
1256 .max_w(px(640.))
1257 .w(px(500.))
1258 .overflow_y_scroll()
1259 .occlude()
1260 })
1261 } else {
1262 None
1263 };
1264
1265 let list = uniform_list(
1266 cx.view().clone(),
1267 "completions",
1268 matches.len(),
1269 move |_editor, range, cx| {
1270 let start_ix = range.start;
1271 let completions_guard = completions.read();
1272
1273 matches[range]
1274 .iter()
1275 .enumerate()
1276 .map(|(ix, mat)| {
1277 let item_ix = start_ix + ix;
1278 let candidate_id = mat.candidate_id;
1279 let completion = &completions_guard[candidate_id];
1280
1281 let documentation = if show_completion_documentation {
1282 &completion.documentation
1283 } else {
1284 &None
1285 };
1286
1287 let highlights = gpui::combine_highlights(
1288 mat.ranges().map(|range| (range, FontWeight::BOLD.into())),
1289 styled_runs_for_code_label(&completion.label, &style.syntax).map(
1290 |(range, mut highlight)| {
1291 // Ignore font weight for syntax highlighting, as we'll use it
1292 // for fuzzy matches.
1293 highlight.font_weight = None;
1294
1295 if completion.lsp_completion.deprecated.unwrap_or(false) {
1296 highlight.strikethrough = Some(StrikethroughStyle {
1297 thickness: 1.0.into(),
1298 ..Default::default()
1299 });
1300 highlight.color = Some(cx.theme().colors().text_muted);
1301 }
1302
1303 (range, highlight)
1304 },
1305 ),
1306 );
1307 let completion_label = StyledText::new(completion.label.text.clone())
1308 .with_highlights(&style.text, highlights);
1309 let documentation_label =
1310 if let Some(Documentation::SingleLine(text)) = documentation {
1311 if text.trim().is_empty() {
1312 None
1313 } else {
1314 Some(
1315 Label::new(text.clone())
1316 .ml_4()
1317 .size(LabelSize::Small)
1318 .color(Color::Muted),
1319 )
1320 }
1321 } else {
1322 None
1323 };
1324
1325 let color_swatch = completion
1326 .color()
1327 .map(|color| div().size_4().bg(color).rounded_sm());
1328
1329 div().min_w(px(220.)).max_w(px(540.)).child(
1330 ListItem::new(mat.candidate_id)
1331 .inset(true)
1332 .selected(item_ix == selected_item)
1333 .on_click(cx.listener(move |editor, _event, cx| {
1334 cx.stop_propagation();
1335 if let Some(task) = editor.confirm_completion(
1336 &ConfirmCompletion {
1337 item_ix: Some(item_ix),
1338 },
1339 cx,
1340 ) {
1341 task.detach_and_log_err(cx)
1342 }
1343 }))
1344 .start_slot::<Div>(color_swatch)
1345 .child(h_flex().overflow_hidden().child(completion_label))
1346 .end_slot::<Label>(documentation_label),
1347 )
1348 })
1349 .collect()
1350 },
1351 )
1352 .occlude()
1353 .max_h(max_height)
1354 .track_scroll(self.scroll_handle.clone())
1355 .with_width_from_item(widest_completion_ix)
1356 .with_sizing_behavior(ListSizingBehavior::Infer);
1357
1358 Popover::new()
1359 .child(list)
1360 .when_some(multiline_docs, |popover, multiline_docs| {
1361 popover.aside(multiline_docs)
1362 })
1363 .into_any_element()
1364 }
1365
1366 pub async fn filter(&mut self, query: Option<&str>, executor: BackgroundExecutor) {
1367 let mut matches = if let Some(query) = query {
1368 fuzzy::match_strings(
1369 &self.match_candidates,
1370 query,
1371 query.chars().any(|c| c.is_uppercase()),
1372 100,
1373 &Default::default(),
1374 executor,
1375 )
1376 .await
1377 } else {
1378 self.match_candidates
1379 .iter()
1380 .enumerate()
1381 .map(|(candidate_id, candidate)| StringMatch {
1382 candidate_id,
1383 score: Default::default(),
1384 positions: Default::default(),
1385 string: candidate.string.clone(),
1386 })
1387 .collect()
1388 };
1389
1390 // Remove all candidates where the query's start does not match the start of any word in the candidate
1391 if let Some(query) = query {
1392 if let Some(query_start) = query.chars().next() {
1393 matches.retain(|string_match| {
1394 split_words(&string_match.string).any(|word| {
1395 // Check that the first codepoint of the word as lowercase matches the first
1396 // codepoint of the query as lowercase
1397 word.chars()
1398 .flat_map(|codepoint| codepoint.to_lowercase())
1399 .zip(query_start.to_lowercase())
1400 .all(|(word_cp, query_cp)| word_cp == query_cp)
1401 })
1402 });
1403 }
1404 }
1405
1406 let completions = self.completions.read();
1407 if self.sort_completions {
1408 matches.sort_unstable_by_key(|mat| {
1409 // We do want to strike a balance here between what the language server tells us
1410 // to sort by (the sort_text) and what are "obvious" good matches (i.e. when you type
1411 // `Creat` and there is a local variable called `CreateComponent`).
1412 // So what we do is: we bucket all matches into two buckets
1413 // - Strong matches
1414 // - Weak matches
1415 // Strong matches are the ones with a high fuzzy-matcher score (the "obvious" matches)
1416 // and the Weak matches are the rest.
1417 //
1418 // For the strong matches, we sort by our fuzzy-finder score first and for the weak
1419 // matches, we prefer language-server sort_text first.
1420 //
1421 // The thinking behind that: we want to show strong matches first in order of relevance(fuzzy score).
1422 // Rest of the matches(weak) can be sorted as language-server expects.
1423
1424 #[derive(PartialEq, Eq, PartialOrd, Ord)]
1425 enum MatchScore<'a> {
1426 Strong {
1427 score: Reverse<OrderedFloat<f64>>,
1428 sort_text: Option<&'a str>,
1429 sort_key: (usize, &'a str),
1430 },
1431 Weak {
1432 sort_text: Option<&'a str>,
1433 score: Reverse<OrderedFloat<f64>>,
1434 sort_key: (usize, &'a str),
1435 },
1436 }
1437
1438 let completion = &completions[mat.candidate_id];
1439 let sort_key = completion.sort_key();
1440 let sort_text = completion.lsp_completion.sort_text.as_deref();
1441 let score = Reverse(OrderedFloat(mat.score));
1442
1443 if mat.score >= 0.2 {
1444 MatchScore::Strong {
1445 score,
1446 sort_text,
1447 sort_key,
1448 }
1449 } else {
1450 MatchScore::Weak {
1451 sort_text,
1452 score,
1453 sort_key,
1454 }
1455 }
1456 });
1457 }
1458
1459 for mat in &mut matches {
1460 let completion = &completions[mat.candidate_id];
1461 mat.string.clone_from(&completion.label.text);
1462 for position in &mut mat.positions {
1463 *position += completion.label.filter_range.start;
1464 }
1465 }
1466 drop(completions);
1467
1468 self.matches = matches.into();
1469 self.selected_item = 0;
1470 }
1471}
1472
1473#[derive(Clone)]
1474struct AvailableCodeAction {
1475 excerpt_id: ExcerptId,
1476 action: CodeAction,
1477 provider: Arc<dyn CodeActionProvider>,
1478}
1479
1480#[derive(Clone)]
1481struct CodeActionContents {
1482 tasks: Option<Arc<ResolvedTasks>>,
1483 actions: Option<Arc<[AvailableCodeAction]>>,
1484}
1485
1486impl CodeActionContents {
1487 fn len(&self) -> usize {
1488 match (&self.tasks, &self.actions) {
1489 (Some(tasks), Some(actions)) => actions.len() + tasks.templates.len(),
1490 (Some(tasks), None) => tasks.templates.len(),
1491 (None, Some(actions)) => actions.len(),
1492 (None, None) => 0,
1493 }
1494 }
1495
1496 fn is_empty(&self) -> bool {
1497 match (&self.tasks, &self.actions) {
1498 (Some(tasks), Some(actions)) => actions.is_empty() && tasks.templates.is_empty(),
1499 (Some(tasks), None) => tasks.templates.is_empty(),
1500 (None, Some(actions)) => actions.is_empty(),
1501 (None, None) => true,
1502 }
1503 }
1504
1505 fn iter(&self) -> impl Iterator<Item = CodeActionsItem> + '_ {
1506 self.tasks
1507 .iter()
1508 .flat_map(|tasks| {
1509 tasks
1510 .templates
1511 .iter()
1512 .map(|(kind, task)| CodeActionsItem::Task(kind.clone(), task.clone()))
1513 })
1514 .chain(self.actions.iter().flat_map(|actions| {
1515 actions.iter().map(|available| CodeActionsItem::CodeAction {
1516 excerpt_id: available.excerpt_id,
1517 action: available.action.clone(),
1518 provider: available.provider.clone(),
1519 })
1520 }))
1521 }
1522 fn get(&self, index: usize) -> Option<CodeActionsItem> {
1523 match (&self.tasks, &self.actions) {
1524 (Some(tasks), Some(actions)) => {
1525 if index < tasks.templates.len() {
1526 tasks
1527 .templates
1528 .get(index)
1529 .cloned()
1530 .map(|(kind, task)| CodeActionsItem::Task(kind, task))
1531 } else {
1532 actions.get(index - tasks.templates.len()).map(|available| {
1533 CodeActionsItem::CodeAction {
1534 excerpt_id: available.excerpt_id,
1535 action: available.action.clone(),
1536 provider: available.provider.clone(),
1537 }
1538 })
1539 }
1540 }
1541 (Some(tasks), None) => tasks
1542 .templates
1543 .get(index)
1544 .cloned()
1545 .map(|(kind, task)| CodeActionsItem::Task(kind, task)),
1546 (None, Some(actions)) => {
1547 actions
1548 .get(index)
1549 .map(|available| CodeActionsItem::CodeAction {
1550 excerpt_id: available.excerpt_id,
1551 action: available.action.clone(),
1552 provider: available.provider.clone(),
1553 })
1554 }
1555 (None, None) => None,
1556 }
1557 }
1558}
1559
1560#[allow(clippy::large_enum_variant)]
1561#[derive(Clone)]
1562enum CodeActionsItem {
1563 Task(TaskSourceKind, ResolvedTask),
1564 CodeAction {
1565 excerpt_id: ExcerptId,
1566 action: CodeAction,
1567 provider: Arc<dyn CodeActionProvider>,
1568 },
1569}
1570
1571impl CodeActionsItem {
1572 fn as_task(&self) -> Option<&ResolvedTask> {
1573 let Self::Task(_, task) = self else {
1574 return None;
1575 };
1576 Some(task)
1577 }
1578 fn as_code_action(&self) -> Option<&CodeAction> {
1579 let Self::CodeAction { action, .. } = self else {
1580 return None;
1581 };
1582 Some(action)
1583 }
1584 fn label(&self) -> String {
1585 match self {
1586 Self::CodeAction { action, .. } => action.lsp_action.title.clone(),
1587 Self::Task(_, task) => task.resolved_label.clone(),
1588 }
1589 }
1590}
1591
1592struct CodeActionsMenu {
1593 actions: CodeActionContents,
1594 buffer: Model<Buffer>,
1595 selected_item: usize,
1596 scroll_handle: UniformListScrollHandle,
1597 deployed_from_indicator: Option<DisplayRow>,
1598}
1599
1600impl CodeActionsMenu {
1601 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
1602 self.selected_item = 0;
1603 self.scroll_handle
1604 .scroll_to_item(self.selected_item, ScrollStrategy::Top);
1605 cx.notify()
1606 }
1607
1608 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
1609 if self.selected_item > 0 {
1610 self.selected_item -= 1;
1611 } else {
1612 self.selected_item = self.actions.len() - 1;
1613 }
1614 self.scroll_handle
1615 .scroll_to_item(self.selected_item, ScrollStrategy::Top);
1616 cx.notify();
1617 }
1618
1619 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
1620 if self.selected_item + 1 < self.actions.len() {
1621 self.selected_item += 1;
1622 } else {
1623 self.selected_item = 0;
1624 }
1625 self.scroll_handle
1626 .scroll_to_item(self.selected_item, ScrollStrategy::Top);
1627 cx.notify();
1628 }
1629
1630 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
1631 self.selected_item = self.actions.len() - 1;
1632 self.scroll_handle
1633 .scroll_to_item(self.selected_item, ScrollStrategy::Top);
1634 cx.notify()
1635 }
1636
1637 fn visible(&self) -> bool {
1638 !self.actions.is_empty()
1639 }
1640
1641 fn render(
1642 &self,
1643 cursor_position: DisplayPoint,
1644 _style: &EditorStyle,
1645 max_height: Pixels,
1646 cx: &mut ViewContext<Editor>,
1647 ) -> (ContextMenuOrigin, AnyElement) {
1648 let actions = self.actions.clone();
1649 let selected_item = self.selected_item;
1650 let element = uniform_list(
1651 cx.view().clone(),
1652 "code_actions_menu",
1653 self.actions.len(),
1654 move |_this, range, cx| {
1655 actions
1656 .iter()
1657 .skip(range.start)
1658 .take(range.end - range.start)
1659 .enumerate()
1660 .map(|(ix, action)| {
1661 let item_ix = range.start + ix;
1662 let selected = selected_item == item_ix;
1663 let colors = cx.theme().colors();
1664 div()
1665 .px_1()
1666 .rounded_md()
1667 .text_color(colors.text)
1668 .when(selected, |style| {
1669 style
1670 .bg(colors.element_active)
1671 .text_color(colors.text_accent)
1672 })
1673 .hover(|style| {
1674 style
1675 .bg(colors.element_hover)
1676 .text_color(colors.text_accent)
1677 })
1678 .whitespace_nowrap()
1679 .when_some(action.as_code_action(), |this, action| {
1680 this.on_mouse_down(
1681 MouseButton::Left,
1682 cx.listener(move |editor, _, cx| {
1683 cx.stop_propagation();
1684 if let Some(task) = editor.confirm_code_action(
1685 &ConfirmCodeAction {
1686 item_ix: Some(item_ix),
1687 },
1688 cx,
1689 ) {
1690 task.detach_and_log_err(cx)
1691 }
1692 }),
1693 )
1694 // TASK: It would be good to make lsp_action.title a SharedString to avoid allocating here.
1695 .child(SharedString::from(
1696 action.lsp_action.title.replace("\n", ""),
1697 ))
1698 })
1699 .when_some(action.as_task(), |this, task| {
1700 this.on_mouse_down(
1701 MouseButton::Left,
1702 cx.listener(move |editor, _, cx| {
1703 cx.stop_propagation();
1704 if let Some(task) = editor.confirm_code_action(
1705 &ConfirmCodeAction {
1706 item_ix: Some(item_ix),
1707 },
1708 cx,
1709 ) {
1710 task.detach_and_log_err(cx)
1711 }
1712 }),
1713 )
1714 .child(SharedString::from(task.resolved_label.replace("\n", "")))
1715 })
1716 })
1717 .collect()
1718 },
1719 )
1720 .elevation_1(cx)
1721 .p_1()
1722 .max_h(max_height)
1723 .occlude()
1724 .track_scroll(self.scroll_handle.clone())
1725 .with_width_from_item(
1726 self.actions
1727 .iter()
1728 .enumerate()
1729 .max_by_key(|(_, action)| match action {
1730 CodeActionsItem::Task(_, task) => task.resolved_label.chars().count(),
1731 CodeActionsItem::CodeAction { action, .. } => {
1732 action.lsp_action.title.chars().count()
1733 }
1734 })
1735 .map(|(ix, _)| ix),
1736 )
1737 .with_sizing_behavior(ListSizingBehavior::Infer)
1738 .into_any_element();
1739
1740 let cursor_position = if let Some(row) = self.deployed_from_indicator {
1741 ContextMenuOrigin::GutterIndicator(row)
1742 } else {
1743 ContextMenuOrigin::EditorPoint(cursor_position)
1744 };
1745
1746 (cursor_position, element)
1747 }
1748}
1749
1750#[derive(Debug)]
1751struct ActiveDiagnosticGroup {
1752 primary_range: Range<Anchor>,
1753 primary_message: String,
1754 group_id: usize,
1755 blocks: HashMap<CustomBlockId, Diagnostic>,
1756 is_valid: bool,
1757}
1758
1759#[derive(Serialize, Deserialize, Clone, Debug)]
1760pub struct ClipboardSelection {
1761 pub len: usize,
1762 pub is_entire_line: bool,
1763 pub first_line_indent: u32,
1764}
1765
1766#[derive(Debug)]
1767pub(crate) struct NavigationData {
1768 cursor_anchor: Anchor,
1769 cursor_position: Point,
1770 scroll_anchor: ScrollAnchor,
1771 scroll_top_row: u32,
1772}
1773
1774#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1775pub enum GotoDefinitionKind {
1776 Symbol,
1777 Declaration,
1778 Type,
1779 Implementation,
1780}
1781
1782#[derive(Debug, Clone)]
1783enum InlayHintRefreshReason {
1784 Toggle(bool),
1785 SettingsChange(InlayHintSettings),
1786 NewLinesShown,
1787 BufferEdited(HashSet<Arc<Language>>),
1788 RefreshRequested,
1789 ExcerptsRemoved(Vec<ExcerptId>),
1790}
1791
1792impl InlayHintRefreshReason {
1793 fn description(&self) -> &'static str {
1794 match self {
1795 Self::Toggle(_) => "toggle",
1796 Self::SettingsChange(_) => "settings change",
1797 Self::NewLinesShown => "new lines shown",
1798 Self::BufferEdited(_) => "buffer edited",
1799 Self::RefreshRequested => "refresh requested",
1800 Self::ExcerptsRemoved(_) => "excerpts removed",
1801 }
1802 }
1803}
1804
1805pub(crate) struct FocusedBlock {
1806 id: BlockId,
1807 focus_handle: WeakFocusHandle,
1808}
1809
1810#[derive(Clone)]
1811struct JumpData {
1812 excerpt_id: ExcerptId,
1813 position: Point,
1814 anchor: text::Anchor,
1815 path: Option<project::ProjectPath>,
1816 line_offset_from_top: u32,
1817}
1818
1819impl Editor {
1820 pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
1821 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1822 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1823 Self::new(
1824 EditorMode::SingleLine { auto_width: false },
1825 buffer,
1826 None,
1827 false,
1828 cx,
1829 )
1830 }
1831
1832 pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
1833 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1834 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1835 Self::new(EditorMode::Full, buffer, None, false, cx)
1836 }
1837
1838 pub fn auto_width(cx: &mut ViewContext<Self>) -> Self {
1839 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1840 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1841 Self::new(
1842 EditorMode::SingleLine { auto_width: true },
1843 buffer,
1844 None,
1845 false,
1846 cx,
1847 )
1848 }
1849
1850 pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
1851 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1852 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1853 Self::new(
1854 EditorMode::AutoHeight { max_lines },
1855 buffer,
1856 None,
1857 false,
1858 cx,
1859 )
1860 }
1861
1862 pub fn for_buffer(
1863 buffer: Model<Buffer>,
1864 project: Option<Model<Project>>,
1865 cx: &mut ViewContext<Self>,
1866 ) -> Self {
1867 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1868 Self::new(EditorMode::Full, buffer, project, false, cx)
1869 }
1870
1871 pub fn for_multibuffer(
1872 buffer: Model<MultiBuffer>,
1873 project: Option<Model<Project>>,
1874 show_excerpt_controls: bool,
1875 cx: &mut ViewContext<Self>,
1876 ) -> Self {
1877 Self::new(EditorMode::Full, buffer, project, show_excerpt_controls, cx)
1878 }
1879
1880 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1881 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1882 let mut clone = Self::new(
1883 self.mode,
1884 self.buffer.clone(),
1885 self.project.clone(),
1886 show_excerpt_controls,
1887 cx,
1888 );
1889 self.display_map.update(cx, |display_map, cx| {
1890 let snapshot = display_map.snapshot(cx);
1891 clone.display_map.update(cx, |display_map, cx| {
1892 display_map.set_state(&snapshot, cx);
1893 });
1894 });
1895 clone.selections.clone_state(&self.selections);
1896 clone.scroll_manager.clone_state(&self.scroll_manager);
1897 clone.searchable = self.searchable;
1898 clone
1899 }
1900
1901 pub fn new(
1902 mode: EditorMode,
1903 buffer: Model<MultiBuffer>,
1904 project: Option<Model<Project>>,
1905 show_excerpt_controls: bool,
1906 cx: &mut ViewContext<Self>,
1907 ) -> Self {
1908 let style = cx.text_style();
1909 let font_size = style.font_size.to_pixels(cx.rem_size());
1910 let editor = cx.view().downgrade();
1911 let fold_placeholder = FoldPlaceholder {
1912 constrain_width: true,
1913 render: Arc::new(move |fold_id, fold_range, cx| {
1914 let editor = editor.clone();
1915 div()
1916 .id(fold_id)
1917 .bg(cx.theme().colors().ghost_element_background)
1918 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1919 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1920 .rounded_sm()
1921 .size_full()
1922 .cursor_pointer()
1923 .child("⋯")
1924 .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
1925 .on_click(move |_, cx| {
1926 editor
1927 .update(cx, |editor, cx| {
1928 editor.unfold_ranges(
1929 &[fold_range.start..fold_range.end],
1930 true,
1931 false,
1932 cx,
1933 );
1934 cx.stop_propagation();
1935 })
1936 .ok();
1937 })
1938 .into_any()
1939 }),
1940 merge_adjacent: true,
1941 ..Default::default()
1942 };
1943 let display_map = cx.new_model(|cx| {
1944 DisplayMap::new(
1945 buffer.clone(),
1946 style.font(),
1947 font_size,
1948 None,
1949 show_excerpt_controls,
1950 FILE_HEADER_HEIGHT,
1951 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1952 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1953 fold_placeholder,
1954 cx,
1955 )
1956 });
1957
1958 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1959
1960 let blink_manager = cx.new_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1961
1962 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1963 .then(|| language_settings::SoftWrap::None);
1964
1965 let mut project_subscriptions = Vec::new();
1966 if mode == EditorMode::Full {
1967 if let Some(project) = project.as_ref() {
1968 if buffer.read(cx).is_singleton() {
1969 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1970 cx.emit(EditorEvent::TitleChanged);
1971 }));
1972 }
1973 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1974 if let project::Event::RefreshInlayHints = event {
1975 editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1976 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1977 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1978 let focus_handle = editor.focus_handle(cx);
1979 if focus_handle.is_focused(cx) {
1980 let snapshot = buffer.read(cx).snapshot();
1981 for (range, snippet) in snippet_edits {
1982 let editor_range =
1983 language::range_from_lsp(*range).to_offset(&snapshot);
1984 editor
1985 .insert_snippet(&[editor_range], snippet.clone(), cx)
1986 .ok();
1987 }
1988 }
1989 }
1990 }
1991 }));
1992 if let Some(task_inventory) = project
1993 .read(cx)
1994 .task_store()
1995 .read(cx)
1996 .task_inventory()
1997 .cloned()
1998 {
1999 project_subscriptions.push(cx.observe(&task_inventory, |editor, _, cx| {
2000 editor.tasks_update_task = Some(editor.refresh_runnables(cx));
2001 }));
2002 }
2003 }
2004 }
2005
2006 let buffer_snapshot = buffer.read(cx).snapshot(cx);
2007
2008 let inlay_hint_settings =
2009 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
2010 let focus_handle = cx.focus_handle();
2011 cx.on_focus(&focus_handle, Self::handle_focus).detach();
2012 cx.on_focus_in(&focus_handle, Self::handle_focus_in)
2013 .detach();
2014 cx.on_focus_out(&focus_handle, Self::handle_focus_out)
2015 .detach();
2016 cx.on_blur(&focus_handle, Self::handle_blur).detach();
2017
2018 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
2019 Some(false)
2020 } else {
2021 None
2022 };
2023
2024 let mut code_action_providers = Vec::new();
2025 if let Some(project) = project.clone() {
2026 let mut tasks = Vec::new();
2027 buffer.update(cx, |multibuffer, cx| {
2028 project.update(cx, |project, cx| {
2029 multibuffer.for_each_buffer(|buffer| {
2030 tasks.push(project.open_unstaged_changes(buffer.clone(), cx))
2031 });
2032 });
2033 });
2034
2035 cx.spawn(|this, mut cx| async move {
2036 let change_sets = futures::future::join_all(tasks).await;
2037 this.update(&mut cx, |this, cx| {
2038 for change_set in change_sets {
2039 if let Some(change_set) = change_set.log_err() {
2040 this.diff_map.add_change_set(change_set, cx);
2041 }
2042 }
2043 })
2044 .ok();
2045 })
2046 .detach();
2047
2048 code_action_providers.push(Arc::new(project) as Arc<_>);
2049 }
2050
2051 let mut this = Self {
2052 focus_handle,
2053 show_cursor_when_unfocused: false,
2054 last_focused_descendant: None,
2055 buffer: buffer.clone(),
2056 display_map: display_map.clone(),
2057 selections,
2058 scroll_manager: ScrollManager::new(cx),
2059 columnar_selection_tail: None,
2060 add_selections_state: None,
2061 select_next_state: None,
2062 select_prev_state: None,
2063 selection_history: Default::default(),
2064 autoclose_regions: Default::default(),
2065 snippet_stack: Default::default(),
2066 select_larger_syntax_node_stack: Vec::new(),
2067 ime_transaction: Default::default(),
2068 active_diagnostics: None,
2069 soft_wrap_mode_override,
2070 completion_provider: project.clone().map(|project| Box::new(project) as _),
2071 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
2072 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
2073 project,
2074 blink_manager: blink_manager.clone(),
2075 show_local_selections: true,
2076 mode,
2077 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
2078 show_gutter: mode == EditorMode::Full,
2079 show_line_numbers: None,
2080 use_relative_line_numbers: None,
2081 show_git_diff_gutter: None,
2082 show_code_actions: None,
2083 show_runnables: None,
2084 show_wrap_guides: None,
2085 show_indent_guides,
2086 placeholder_text: None,
2087 highlight_order: 0,
2088 highlighted_rows: HashMap::default(),
2089 background_highlights: Default::default(),
2090 gutter_highlights: TreeMap::default(),
2091 scrollbar_marker_state: ScrollbarMarkerState::default(),
2092 active_indent_guides_state: ActiveIndentGuidesState::default(),
2093 nav_history: None,
2094 context_menu: RwLock::new(None),
2095 mouse_context_menu: None,
2096 hunk_controls_menu_handle: PopoverMenuHandle::default(),
2097 completion_tasks: Default::default(),
2098 signature_help_state: SignatureHelpState::default(),
2099 auto_signature_help: None,
2100 find_all_references_task_sources: Vec::new(),
2101 next_completion_id: 0,
2102 next_inlay_id: 0,
2103 code_action_providers,
2104 available_code_actions: Default::default(),
2105 code_actions_task: Default::default(),
2106 document_highlights_task: Default::default(),
2107 linked_editing_range_task: Default::default(),
2108 pending_rename: Default::default(),
2109 searchable: true,
2110 cursor_shape: EditorSettings::get_global(cx)
2111 .cursor_shape
2112 .unwrap_or_default(),
2113 current_line_highlight: None,
2114 autoindent_mode: Some(AutoindentMode::EachLine),
2115 collapse_matches: false,
2116 workspace: None,
2117 input_enabled: true,
2118 use_modal_editing: mode == EditorMode::Full,
2119 read_only: false,
2120 use_autoclose: true,
2121 use_auto_surround: true,
2122 auto_replace_emoji_shortcode: false,
2123 leader_peer_id: None,
2124 remote_id: None,
2125 hover_state: Default::default(),
2126 hovered_link_state: Default::default(),
2127 inline_completion_provider: None,
2128 active_inline_completion: None,
2129 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
2130 diff_map: DiffMap::default(),
2131 gutter_hovered: false,
2132 pixel_position_of_newest_cursor: None,
2133 last_bounds: None,
2134 expect_bounds_change: None,
2135 gutter_dimensions: GutterDimensions::default(),
2136 style: None,
2137 show_cursor_names: false,
2138 hovered_cursors: Default::default(),
2139 next_editor_action_id: EditorActionId::default(),
2140 editor_actions: Rc::default(),
2141 show_inline_completions_override: None,
2142 enable_inline_completions: true,
2143 custom_context_menu: None,
2144 show_git_blame_gutter: false,
2145 show_git_blame_inline: false,
2146 show_selection_menu: None,
2147 show_git_blame_inline_delay_task: None,
2148 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
2149 serialize_dirty_buffers: ProjectSettings::get_global(cx)
2150 .session
2151 .restore_unsaved_buffers,
2152 blame: None,
2153 blame_subscription: None,
2154 tasks: Default::default(),
2155 _subscriptions: vec![
2156 cx.observe(&buffer, Self::on_buffer_changed),
2157 cx.subscribe(&buffer, Self::on_buffer_event),
2158 cx.observe(&display_map, Self::on_display_map_changed),
2159 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
2160 cx.observe_global::<SettingsStore>(Self::settings_changed),
2161 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
2162 cx.observe_window_activation(|editor, cx| {
2163 let active = cx.is_window_active();
2164 editor.blink_manager.update(cx, |blink_manager, cx| {
2165 if active {
2166 blink_manager.enable(cx);
2167 } else {
2168 blink_manager.disable(cx);
2169 }
2170 });
2171 }),
2172 ],
2173 tasks_update_task: None,
2174 linked_edit_ranges: Default::default(),
2175 previous_search_ranges: None,
2176 breadcrumb_header: None,
2177 focused_block: None,
2178 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
2179 addons: HashMap::default(),
2180 _scroll_cursor_center_top_bottom_task: Task::ready(()),
2181 text_style_refinement: None,
2182 };
2183 this.tasks_update_task = Some(this.refresh_runnables(cx));
2184 this._subscriptions.extend(project_subscriptions);
2185
2186 this.end_selection(cx);
2187 this.scroll_manager.show_scrollbar(cx);
2188
2189 if mode == EditorMode::Full {
2190 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
2191 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
2192
2193 if this.git_blame_inline_enabled {
2194 this.git_blame_inline_enabled = true;
2195 this.start_git_blame_inline(false, cx);
2196 }
2197 }
2198
2199 this.report_editor_event("open", None, cx);
2200 this
2201 }
2202
2203 pub fn mouse_menu_is_focused(&self, cx: &WindowContext) -> bool {
2204 self.mouse_context_menu
2205 .as_ref()
2206 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(cx))
2207 }
2208
2209 fn key_context(&self, cx: &ViewContext<Self>) -> KeyContext {
2210 let mut key_context = KeyContext::new_with_defaults();
2211 key_context.add("Editor");
2212 let mode = match self.mode {
2213 EditorMode::SingleLine { .. } => "single_line",
2214 EditorMode::AutoHeight { .. } => "auto_height",
2215 EditorMode::Full => "full",
2216 };
2217
2218 if EditorSettings::jupyter_enabled(cx) {
2219 key_context.add("jupyter");
2220 }
2221
2222 key_context.set("mode", mode);
2223 if self.pending_rename.is_some() {
2224 key_context.add("renaming");
2225 }
2226 if self.context_menu_visible() {
2227 match self.context_menu.read().as_ref() {
2228 Some(ContextMenu::Completions(_)) => {
2229 key_context.add("menu");
2230 key_context.add("showing_completions")
2231 }
2232 Some(ContextMenu::CodeActions(_)) => {
2233 key_context.add("menu");
2234 key_context.add("showing_code_actions")
2235 }
2236 None => {}
2237 }
2238 }
2239
2240 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
2241 if !self.focus_handle(cx).contains_focused(cx)
2242 || (self.is_focused(cx) || self.mouse_menu_is_focused(cx))
2243 {
2244 for addon in self.addons.values() {
2245 addon.extend_key_context(&mut key_context, cx)
2246 }
2247 }
2248
2249 if let Some(extension) = self
2250 .buffer
2251 .read(cx)
2252 .as_singleton()
2253 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
2254 {
2255 key_context.set("extension", extension.to_string());
2256 }
2257
2258 if self.has_active_inline_completion(cx) {
2259 key_context.add("copilot_suggestion");
2260 key_context.add("inline_completion");
2261 }
2262
2263 key_context
2264 }
2265
2266 pub fn new_file(
2267 workspace: &mut Workspace,
2268 _: &workspace::NewFile,
2269 cx: &mut ViewContext<Workspace>,
2270 ) {
2271 Self::new_in_workspace(workspace, cx).detach_and_prompt_err(
2272 "Failed to create buffer",
2273 cx,
2274 |e, _| match e.error_code() {
2275 ErrorCode::RemoteUpgradeRequired => Some(format!(
2276 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2277 e.error_tag("required").unwrap_or("the latest version")
2278 )),
2279 _ => None,
2280 },
2281 );
2282 }
2283
2284 pub fn new_in_workspace(
2285 workspace: &mut Workspace,
2286 cx: &mut ViewContext<Workspace>,
2287 ) -> Task<Result<View<Editor>>> {
2288 let project = workspace.project().clone();
2289 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2290
2291 cx.spawn(|workspace, mut cx| async move {
2292 let buffer = create.await?;
2293 workspace.update(&mut cx, |workspace, cx| {
2294 let editor =
2295 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx));
2296 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
2297 editor
2298 })
2299 })
2300 }
2301
2302 fn new_file_vertical(
2303 workspace: &mut Workspace,
2304 _: &workspace::NewFileSplitVertical,
2305 cx: &mut ViewContext<Workspace>,
2306 ) {
2307 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), cx)
2308 }
2309
2310 fn new_file_horizontal(
2311 workspace: &mut Workspace,
2312 _: &workspace::NewFileSplitHorizontal,
2313 cx: &mut ViewContext<Workspace>,
2314 ) {
2315 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), cx)
2316 }
2317
2318 fn new_file_in_direction(
2319 workspace: &mut Workspace,
2320 direction: SplitDirection,
2321 cx: &mut ViewContext<Workspace>,
2322 ) {
2323 let project = workspace.project().clone();
2324 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2325
2326 cx.spawn(|workspace, mut cx| async move {
2327 let buffer = create.await?;
2328 workspace.update(&mut cx, move |workspace, cx| {
2329 workspace.split_item(
2330 direction,
2331 Box::new(
2332 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)),
2333 ),
2334 cx,
2335 )
2336 })?;
2337 anyhow::Ok(())
2338 })
2339 .detach_and_prompt_err("Failed to create buffer", cx, |e, _| match e.error_code() {
2340 ErrorCode::RemoteUpgradeRequired => Some(format!(
2341 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2342 e.error_tag("required").unwrap_or("the latest version")
2343 )),
2344 _ => None,
2345 });
2346 }
2347
2348 pub fn leader_peer_id(&self) -> Option<PeerId> {
2349 self.leader_peer_id
2350 }
2351
2352 pub fn buffer(&self) -> &Model<MultiBuffer> {
2353 &self.buffer
2354 }
2355
2356 pub fn workspace(&self) -> Option<View<Workspace>> {
2357 self.workspace.as_ref()?.0.upgrade()
2358 }
2359
2360 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
2361 self.buffer().read(cx).title(cx)
2362 }
2363
2364 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
2365 let git_blame_gutter_max_author_length = self
2366 .render_git_blame_gutter(cx)
2367 .then(|| {
2368 if let Some(blame) = self.blame.as_ref() {
2369 let max_author_length =
2370 blame.update(cx, |blame, cx| blame.max_author_length(cx));
2371 Some(max_author_length)
2372 } else {
2373 None
2374 }
2375 })
2376 .flatten();
2377
2378 EditorSnapshot {
2379 mode: self.mode,
2380 show_gutter: self.show_gutter,
2381 show_line_numbers: self.show_line_numbers,
2382 show_git_diff_gutter: self.show_git_diff_gutter,
2383 show_code_actions: self.show_code_actions,
2384 show_runnables: self.show_runnables,
2385 git_blame_gutter_max_author_length,
2386 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
2387 scroll_anchor: self.scroll_manager.anchor(),
2388 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
2389 placeholder_text: self.placeholder_text.clone(),
2390 diff_map: self.diff_map.snapshot(),
2391 is_focused: self.focus_handle.is_focused(cx),
2392 current_line_highlight: self
2393 .current_line_highlight
2394 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
2395 gutter_hovered: self.gutter_hovered,
2396 }
2397 }
2398
2399 pub fn language_at<T: ToOffset>(&self, point: T, cx: &AppContext) -> Option<Arc<Language>> {
2400 self.buffer.read(cx).language_at(point, cx)
2401 }
2402
2403 pub fn file_at<T: ToOffset>(
2404 &self,
2405 point: T,
2406 cx: &AppContext,
2407 ) -> Option<Arc<dyn language::File>> {
2408 self.buffer.read(cx).read(cx).file_at(point).cloned()
2409 }
2410
2411 pub fn active_excerpt(
2412 &self,
2413 cx: &AppContext,
2414 ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
2415 self.buffer
2416 .read(cx)
2417 .excerpt_containing(self.selections.newest_anchor().head(), cx)
2418 }
2419
2420 pub fn mode(&self) -> EditorMode {
2421 self.mode
2422 }
2423
2424 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
2425 self.collaboration_hub.as_deref()
2426 }
2427
2428 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
2429 self.collaboration_hub = Some(hub);
2430 }
2431
2432 pub fn set_custom_context_menu(
2433 &mut self,
2434 f: impl 'static
2435 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
2436 ) {
2437 self.custom_context_menu = Some(Box::new(f))
2438 }
2439
2440 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
2441 self.completion_provider = provider;
2442 }
2443
2444 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
2445 self.semantics_provider.clone()
2446 }
2447
2448 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
2449 self.semantics_provider = provider;
2450 }
2451
2452 pub fn set_inline_completion_provider<T>(
2453 &mut self,
2454 provider: Option<Model<T>>,
2455 cx: &mut ViewContext<Self>,
2456 ) where
2457 T: InlineCompletionProvider,
2458 {
2459 self.inline_completion_provider =
2460 provider.map(|provider| RegisteredInlineCompletionProvider {
2461 _subscription: cx.observe(&provider, |this, _, cx| {
2462 if this.focus_handle.is_focused(cx) {
2463 this.update_visible_inline_completion(cx);
2464 }
2465 }),
2466 provider: Arc::new(provider),
2467 });
2468 self.refresh_inline_completion(false, false, cx);
2469 }
2470
2471 pub fn placeholder_text(&self, _cx: &WindowContext) -> Option<&str> {
2472 self.placeholder_text.as_deref()
2473 }
2474
2475 pub fn set_placeholder_text(
2476 &mut self,
2477 placeholder_text: impl Into<Arc<str>>,
2478 cx: &mut ViewContext<Self>,
2479 ) {
2480 let placeholder_text = Some(placeholder_text.into());
2481 if self.placeholder_text != placeholder_text {
2482 self.placeholder_text = placeholder_text;
2483 cx.notify();
2484 }
2485 }
2486
2487 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
2488 self.cursor_shape = cursor_shape;
2489
2490 // Disrupt blink for immediate user feedback that the cursor shape has changed
2491 self.blink_manager.update(cx, BlinkManager::show_cursor);
2492
2493 cx.notify();
2494 }
2495
2496 pub fn set_current_line_highlight(
2497 &mut self,
2498 current_line_highlight: Option<CurrentLineHighlight>,
2499 ) {
2500 self.current_line_highlight = current_line_highlight;
2501 }
2502
2503 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
2504 self.collapse_matches = collapse_matches;
2505 }
2506
2507 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
2508 if self.collapse_matches {
2509 return range.start..range.start;
2510 }
2511 range.clone()
2512 }
2513
2514 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
2515 if self.display_map.read(cx).clip_at_line_ends != clip {
2516 self.display_map
2517 .update(cx, |map, _| map.clip_at_line_ends = clip);
2518 }
2519 }
2520
2521 pub fn set_input_enabled(&mut self, input_enabled: bool) {
2522 self.input_enabled = input_enabled;
2523 }
2524
2525 pub fn set_inline_completions_enabled(&mut self, enabled: bool) {
2526 self.enable_inline_completions = enabled;
2527 }
2528
2529 pub fn set_autoindent(&mut self, autoindent: bool) {
2530 if autoindent {
2531 self.autoindent_mode = Some(AutoindentMode::EachLine);
2532 } else {
2533 self.autoindent_mode = None;
2534 }
2535 }
2536
2537 pub fn read_only(&self, cx: &AppContext) -> bool {
2538 self.read_only || self.buffer.read(cx).read_only()
2539 }
2540
2541 pub fn set_read_only(&mut self, read_only: bool) {
2542 self.read_only = read_only;
2543 }
2544
2545 pub fn set_use_autoclose(&mut self, autoclose: bool) {
2546 self.use_autoclose = autoclose;
2547 }
2548
2549 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
2550 self.use_auto_surround = auto_surround;
2551 }
2552
2553 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
2554 self.auto_replace_emoji_shortcode = auto_replace;
2555 }
2556
2557 pub fn toggle_inline_completions(
2558 &mut self,
2559 _: &ToggleInlineCompletions,
2560 cx: &mut ViewContext<Self>,
2561 ) {
2562 if self.show_inline_completions_override.is_some() {
2563 self.set_show_inline_completions(None, cx);
2564 } else {
2565 let cursor = self.selections.newest_anchor().head();
2566 if let Some((buffer, cursor_buffer_position)) =
2567 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
2568 {
2569 let show_inline_completions =
2570 !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx);
2571 self.set_show_inline_completions(Some(show_inline_completions), cx);
2572 }
2573 }
2574 }
2575
2576 pub fn set_show_inline_completions(
2577 &mut self,
2578 show_inline_completions: Option<bool>,
2579 cx: &mut ViewContext<Self>,
2580 ) {
2581 self.show_inline_completions_override = show_inline_completions;
2582 self.refresh_inline_completion(false, true, cx);
2583 }
2584
2585 fn should_show_inline_completions(
2586 &self,
2587 buffer: &Model<Buffer>,
2588 buffer_position: language::Anchor,
2589 cx: &AppContext,
2590 ) -> bool {
2591 if !self.snippet_stack.is_empty() {
2592 return false;
2593 }
2594
2595 if self.inline_completions_disabled_in_scope(buffer, buffer_position, cx) {
2596 return false;
2597 }
2598
2599 if let Some(provider) = self.inline_completion_provider() {
2600 if let Some(show_inline_completions) = self.show_inline_completions_override {
2601 show_inline_completions
2602 } else {
2603 self.mode == EditorMode::Full && provider.is_enabled(buffer, buffer_position, cx)
2604 }
2605 } else {
2606 false
2607 }
2608 }
2609
2610 fn inline_completions_disabled_in_scope(
2611 &self,
2612 buffer: &Model<Buffer>,
2613 buffer_position: language::Anchor,
2614 cx: &AppContext,
2615 ) -> bool {
2616 let snapshot = buffer.read(cx).snapshot();
2617 let settings = snapshot.settings_at(buffer_position, cx);
2618
2619 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
2620 return false;
2621 };
2622
2623 scope.override_name().map_or(false, |scope_name| {
2624 settings
2625 .inline_completions_disabled_in
2626 .iter()
2627 .any(|s| s == scope_name)
2628 })
2629 }
2630
2631 pub fn set_use_modal_editing(&mut self, to: bool) {
2632 self.use_modal_editing = to;
2633 }
2634
2635 pub fn use_modal_editing(&self) -> bool {
2636 self.use_modal_editing
2637 }
2638
2639 fn selections_did_change(
2640 &mut self,
2641 local: bool,
2642 old_cursor_position: &Anchor,
2643 show_completions: bool,
2644 cx: &mut ViewContext<Self>,
2645 ) {
2646 cx.invalidate_character_coordinates();
2647
2648 // Copy selections to primary selection buffer
2649 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
2650 if local {
2651 let selections = self.selections.all::<usize>(cx);
2652 let buffer_handle = self.buffer.read(cx).read(cx);
2653
2654 let mut text = String::new();
2655 for (index, selection) in selections.iter().enumerate() {
2656 let text_for_selection = buffer_handle
2657 .text_for_range(selection.start..selection.end)
2658 .collect::<String>();
2659
2660 text.push_str(&text_for_selection);
2661 if index != selections.len() - 1 {
2662 text.push('\n');
2663 }
2664 }
2665
2666 if !text.is_empty() {
2667 cx.write_to_primary(ClipboardItem::new_string(text));
2668 }
2669 }
2670
2671 if self.focus_handle.is_focused(cx) && self.leader_peer_id.is_none() {
2672 self.buffer.update(cx, |buffer, cx| {
2673 buffer.set_active_selections(
2674 &self.selections.disjoint_anchors(),
2675 self.selections.line_mode,
2676 self.cursor_shape,
2677 cx,
2678 )
2679 });
2680 }
2681 let display_map = self
2682 .display_map
2683 .update(cx, |display_map, cx| display_map.snapshot(cx));
2684 let buffer = &display_map.buffer_snapshot;
2685 self.add_selections_state = None;
2686 self.select_next_state = None;
2687 self.select_prev_state = None;
2688 self.select_larger_syntax_node_stack.clear();
2689 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2690 self.snippet_stack
2691 .invalidate(&self.selections.disjoint_anchors(), buffer);
2692 self.take_rename(false, cx);
2693
2694 let new_cursor_position = self.selections.newest_anchor().head();
2695
2696 self.push_to_nav_history(
2697 *old_cursor_position,
2698 Some(new_cursor_position.to_point(buffer)),
2699 cx,
2700 );
2701
2702 if local {
2703 let new_cursor_position = self.selections.newest_anchor().head();
2704 let mut context_menu = self.context_menu.write();
2705 let completion_menu = match context_menu.as_ref() {
2706 Some(ContextMenu::Completions(menu)) => Some(menu),
2707
2708 _ => {
2709 *context_menu = None;
2710 None
2711 }
2712 };
2713
2714 if let Some(completion_menu) = completion_menu {
2715 let cursor_position = new_cursor_position.to_offset(buffer);
2716 let (word_range, kind) =
2717 buffer.surrounding_word(completion_menu.initial_position, true);
2718 if kind == Some(CharKind::Word)
2719 && word_range.to_inclusive().contains(&cursor_position)
2720 {
2721 let mut completion_menu = completion_menu.clone();
2722 drop(context_menu);
2723
2724 let query = Self::completion_query(buffer, cursor_position);
2725 cx.spawn(move |this, mut cx| async move {
2726 completion_menu
2727 .filter(query.as_deref(), cx.background_executor().clone())
2728 .await;
2729
2730 this.update(&mut cx, |this, cx| {
2731 let mut context_menu = this.context_menu.write();
2732 let Some(ContextMenu::Completions(menu)) = context_menu.as_ref() else {
2733 return;
2734 };
2735
2736 if menu.id > completion_menu.id {
2737 return;
2738 }
2739
2740 *context_menu = Some(ContextMenu::Completions(completion_menu));
2741 drop(context_menu);
2742 cx.notify();
2743 })
2744 })
2745 .detach();
2746
2747 if show_completions {
2748 self.show_completions(&ShowCompletions { trigger: None }, cx);
2749 }
2750 } else {
2751 drop(context_menu);
2752 self.hide_context_menu(cx);
2753 }
2754 } else {
2755 drop(context_menu);
2756 }
2757
2758 hide_hover(self, cx);
2759
2760 if old_cursor_position.to_display_point(&display_map).row()
2761 != new_cursor_position.to_display_point(&display_map).row()
2762 {
2763 self.available_code_actions.take();
2764 }
2765 self.refresh_code_actions(cx);
2766 self.refresh_document_highlights(cx);
2767 refresh_matching_bracket_highlights(self, cx);
2768 self.discard_inline_completion(false, cx);
2769 linked_editing_ranges::refresh_linked_ranges(self, cx);
2770 if self.git_blame_inline_enabled {
2771 self.start_inline_blame_timer(cx);
2772 }
2773 }
2774
2775 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2776 cx.emit(EditorEvent::SelectionsChanged { local });
2777
2778 if self.selections.disjoint_anchors().len() == 1 {
2779 cx.emit(SearchEvent::ActiveMatchChanged)
2780 }
2781 cx.notify();
2782 }
2783
2784 pub fn change_selections<R>(
2785 &mut self,
2786 autoscroll: Option<Autoscroll>,
2787 cx: &mut ViewContext<Self>,
2788 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2789 ) -> R {
2790 self.change_selections_inner(autoscroll, true, cx, change)
2791 }
2792
2793 pub fn change_selections_inner<R>(
2794 &mut self,
2795 autoscroll: Option<Autoscroll>,
2796 request_completions: bool,
2797 cx: &mut ViewContext<Self>,
2798 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2799 ) -> R {
2800 let old_cursor_position = self.selections.newest_anchor().head();
2801 self.push_to_selection_history();
2802
2803 let (changed, result) = self.selections.change_with(cx, change);
2804
2805 if changed {
2806 if let Some(autoscroll) = autoscroll {
2807 self.request_autoscroll(autoscroll, cx);
2808 }
2809 self.selections_did_change(true, &old_cursor_position, request_completions, cx);
2810
2811 if self.should_open_signature_help_automatically(
2812 &old_cursor_position,
2813 self.signature_help_state.backspace_pressed(),
2814 cx,
2815 ) {
2816 self.show_signature_help(&ShowSignatureHelp, cx);
2817 }
2818 self.signature_help_state.set_backspace_pressed(false);
2819 }
2820
2821 result
2822 }
2823
2824 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2825 where
2826 I: IntoIterator<Item = (Range<S>, T)>,
2827 S: ToOffset,
2828 T: Into<Arc<str>>,
2829 {
2830 if self.read_only(cx) {
2831 return;
2832 }
2833
2834 self.buffer
2835 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2836 }
2837
2838 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2839 where
2840 I: IntoIterator<Item = (Range<S>, T)>,
2841 S: ToOffset,
2842 T: Into<Arc<str>>,
2843 {
2844 if self.read_only(cx) {
2845 return;
2846 }
2847
2848 self.buffer.update(cx, |buffer, cx| {
2849 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2850 });
2851 }
2852
2853 pub fn edit_with_block_indent<I, S, T>(
2854 &mut self,
2855 edits: I,
2856 original_indent_columns: Vec<u32>,
2857 cx: &mut ViewContext<Self>,
2858 ) where
2859 I: IntoIterator<Item = (Range<S>, T)>,
2860 S: ToOffset,
2861 T: Into<Arc<str>>,
2862 {
2863 if self.read_only(cx) {
2864 return;
2865 }
2866
2867 self.buffer.update(cx, |buffer, cx| {
2868 buffer.edit(
2869 edits,
2870 Some(AutoindentMode::Block {
2871 original_indent_columns,
2872 }),
2873 cx,
2874 )
2875 });
2876 }
2877
2878 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
2879 self.hide_context_menu(cx);
2880
2881 match phase {
2882 SelectPhase::Begin {
2883 position,
2884 add,
2885 click_count,
2886 } => self.begin_selection(position, add, click_count, cx),
2887 SelectPhase::BeginColumnar {
2888 position,
2889 goal_column,
2890 reset,
2891 } => self.begin_columnar_selection(position, goal_column, reset, cx),
2892 SelectPhase::Extend {
2893 position,
2894 click_count,
2895 } => self.extend_selection(position, click_count, cx),
2896 SelectPhase::Update {
2897 position,
2898 goal_column,
2899 scroll_delta,
2900 } => self.update_selection(position, goal_column, scroll_delta, cx),
2901 SelectPhase::End => self.end_selection(cx),
2902 }
2903 }
2904
2905 fn extend_selection(
2906 &mut self,
2907 position: DisplayPoint,
2908 click_count: usize,
2909 cx: &mut ViewContext<Self>,
2910 ) {
2911 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2912 let tail = self.selections.newest::<usize>(cx).tail();
2913 self.begin_selection(position, false, click_count, cx);
2914
2915 let position = position.to_offset(&display_map, Bias::Left);
2916 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2917
2918 let mut pending_selection = self
2919 .selections
2920 .pending_anchor()
2921 .expect("extend_selection not called with pending selection");
2922 if position >= tail {
2923 pending_selection.start = tail_anchor;
2924 } else {
2925 pending_selection.end = tail_anchor;
2926 pending_selection.reversed = true;
2927 }
2928
2929 let mut pending_mode = self.selections.pending_mode().unwrap();
2930 match &mut pending_mode {
2931 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2932 _ => {}
2933 }
2934
2935 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
2936 s.set_pending(pending_selection, pending_mode)
2937 });
2938 }
2939
2940 fn begin_selection(
2941 &mut self,
2942 position: DisplayPoint,
2943 add: bool,
2944 click_count: usize,
2945 cx: &mut ViewContext<Self>,
2946 ) {
2947 if !self.focus_handle.is_focused(cx) {
2948 self.last_focused_descendant = None;
2949 cx.focus(&self.focus_handle);
2950 }
2951
2952 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2953 let buffer = &display_map.buffer_snapshot;
2954 let newest_selection = self.selections.newest_anchor().clone();
2955 let position = display_map.clip_point(position, Bias::Left);
2956
2957 let start;
2958 let end;
2959 let mode;
2960 let mut auto_scroll;
2961 match click_count {
2962 1 => {
2963 start = buffer.anchor_before(position.to_point(&display_map));
2964 end = start;
2965 mode = SelectMode::Character;
2966 auto_scroll = true;
2967 }
2968 2 => {
2969 let range = movement::surrounding_word(&display_map, position);
2970 start = buffer.anchor_before(range.start.to_point(&display_map));
2971 end = buffer.anchor_before(range.end.to_point(&display_map));
2972 mode = SelectMode::Word(start..end);
2973 auto_scroll = true;
2974 }
2975 3 => {
2976 let position = display_map
2977 .clip_point(position, Bias::Left)
2978 .to_point(&display_map);
2979 let line_start = display_map.prev_line_boundary(position).0;
2980 let next_line_start = buffer.clip_point(
2981 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2982 Bias::Left,
2983 );
2984 start = buffer.anchor_before(line_start);
2985 end = buffer.anchor_before(next_line_start);
2986 mode = SelectMode::Line(start..end);
2987 auto_scroll = true;
2988 }
2989 _ => {
2990 start = buffer.anchor_before(0);
2991 end = buffer.anchor_before(buffer.len());
2992 mode = SelectMode::All;
2993 auto_scroll = false;
2994 }
2995 }
2996 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2997
2998 let point_to_delete: Option<usize> = {
2999 let selected_points: Vec<Selection<Point>> =
3000 self.selections.disjoint_in_range(start..end, cx);
3001
3002 if !add || click_count > 1 {
3003 None
3004 } else if !selected_points.is_empty() {
3005 Some(selected_points[0].id)
3006 } else {
3007 let clicked_point_already_selected =
3008 self.selections.disjoint.iter().find(|selection| {
3009 selection.start.to_point(buffer) == start.to_point(buffer)
3010 || selection.end.to_point(buffer) == end.to_point(buffer)
3011 });
3012
3013 clicked_point_already_selected.map(|selection| selection.id)
3014 }
3015 };
3016
3017 let selections_count = self.selections.count();
3018
3019 self.change_selections(auto_scroll.then(Autoscroll::newest), cx, |s| {
3020 if let Some(point_to_delete) = point_to_delete {
3021 s.delete(point_to_delete);
3022
3023 if selections_count == 1 {
3024 s.set_pending_anchor_range(start..end, mode);
3025 }
3026 } else {
3027 if !add {
3028 s.clear_disjoint();
3029 } else if click_count > 1 {
3030 s.delete(newest_selection.id)
3031 }
3032
3033 s.set_pending_anchor_range(start..end, mode);
3034 }
3035 });
3036 }
3037
3038 fn begin_columnar_selection(
3039 &mut self,
3040 position: DisplayPoint,
3041 goal_column: u32,
3042 reset: bool,
3043 cx: &mut ViewContext<Self>,
3044 ) {
3045 if !self.focus_handle.is_focused(cx) {
3046 self.last_focused_descendant = None;
3047 cx.focus(&self.focus_handle);
3048 }
3049
3050 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3051
3052 if reset {
3053 let pointer_position = display_map
3054 .buffer_snapshot
3055 .anchor_before(position.to_point(&display_map));
3056
3057 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
3058 s.clear_disjoint();
3059 s.set_pending_anchor_range(
3060 pointer_position..pointer_position,
3061 SelectMode::Character,
3062 );
3063 });
3064 }
3065
3066 let tail = self.selections.newest::<Point>(cx).tail();
3067 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
3068
3069 if !reset {
3070 self.select_columns(
3071 tail.to_display_point(&display_map),
3072 position,
3073 goal_column,
3074 &display_map,
3075 cx,
3076 );
3077 }
3078 }
3079
3080 fn update_selection(
3081 &mut self,
3082 position: DisplayPoint,
3083 goal_column: u32,
3084 scroll_delta: gpui::Point<f32>,
3085 cx: &mut ViewContext<Self>,
3086 ) {
3087 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3088
3089 if let Some(tail) = self.columnar_selection_tail.as_ref() {
3090 let tail = tail.to_display_point(&display_map);
3091 self.select_columns(tail, position, goal_column, &display_map, cx);
3092 } else if let Some(mut pending) = self.selections.pending_anchor() {
3093 let buffer = self.buffer.read(cx).snapshot(cx);
3094 let head;
3095 let tail;
3096 let mode = self.selections.pending_mode().unwrap();
3097 match &mode {
3098 SelectMode::Character => {
3099 head = position.to_point(&display_map);
3100 tail = pending.tail().to_point(&buffer);
3101 }
3102 SelectMode::Word(original_range) => {
3103 let original_display_range = original_range.start.to_display_point(&display_map)
3104 ..original_range.end.to_display_point(&display_map);
3105 let original_buffer_range = original_display_range.start.to_point(&display_map)
3106 ..original_display_range.end.to_point(&display_map);
3107 if movement::is_inside_word(&display_map, position)
3108 || original_display_range.contains(&position)
3109 {
3110 let word_range = movement::surrounding_word(&display_map, position);
3111 if word_range.start < original_display_range.start {
3112 head = word_range.start.to_point(&display_map);
3113 } else {
3114 head = word_range.end.to_point(&display_map);
3115 }
3116 } else {
3117 head = position.to_point(&display_map);
3118 }
3119
3120 if head <= original_buffer_range.start {
3121 tail = original_buffer_range.end;
3122 } else {
3123 tail = original_buffer_range.start;
3124 }
3125 }
3126 SelectMode::Line(original_range) => {
3127 let original_range = original_range.to_point(&display_map.buffer_snapshot);
3128
3129 let position = display_map
3130 .clip_point(position, Bias::Left)
3131 .to_point(&display_map);
3132 let line_start = display_map.prev_line_boundary(position).0;
3133 let next_line_start = buffer.clip_point(
3134 display_map.next_line_boundary(position).0 + Point::new(1, 0),
3135 Bias::Left,
3136 );
3137
3138 if line_start < original_range.start {
3139 head = line_start
3140 } else {
3141 head = next_line_start
3142 }
3143
3144 if head <= original_range.start {
3145 tail = original_range.end;
3146 } else {
3147 tail = original_range.start;
3148 }
3149 }
3150 SelectMode::All => {
3151 return;
3152 }
3153 };
3154
3155 if head < tail {
3156 pending.start = buffer.anchor_before(head);
3157 pending.end = buffer.anchor_before(tail);
3158 pending.reversed = true;
3159 } else {
3160 pending.start = buffer.anchor_before(tail);
3161 pending.end = buffer.anchor_before(head);
3162 pending.reversed = false;
3163 }
3164
3165 self.change_selections(None, cx, |s| {
3166 s.set_pending(pending, mode);
3167 });
3168 } else {
3169 log::error!("update_selection dispatched with no pending selection");
3170 return;
3171 }
3172
3173 self.apply_scroll_delta(scroll_delta, cx);
3174 cx.notify();
3175 }
3176
3177 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
3178 self.columnar_selection_tail.take();
3179 if self.selections.pending_anchor().is_some() {
3180 let selections = self.selections.all::<usize>(cx);
3181 self.change_selections(None, cx, |s| {
3182 s.select(selections);
3183 s.clear_pending();
3184 });
3185 }
3186 }
3187
3188 fn select_columns(
3189 &mut self,
3190 tail: DisplayPoint,
3191 head: DisplayPoint,
3192 goal_column: u32,
3193 display_map: &DisplaySnapshot,
3194 cx: &mut ViewContext<Self>,
3195 ) {
3196 let start_row = cmp::min(tail.row(), head.row());
3197 let end_row = cmp::max(tail.row(), head.row());
3198 let start_column = cmp::min(tail.column(), goal_column);
3199 let end_column = cmp::max(tail.column(), goal_column);
3200 let reversed = start_column < tail.column();
3201
3202 let selection_ranges = (start_row.0..=end_row.0)
3203 .map(DisplayRow)
3204 .filter_map(|row| {
3205 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
3206 let start = display_map
3207 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
3208 .to_point(display_map);
3209 let end = display_map
3210 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
3211 .to_point(display_map);
3212 if reversed {
3213 Some(end..start)
3214 } else {
3215 Some(start..end)
3216 }
3217 } else {
3218 None
3219 }
3220 })
3221 .collect::<Vec<_>>();
3222
3223 self.change_selections(None, cx, |s| {
3224 s.select_ranges(selection_ranges);
3225 });
3226 cx.notify();
3227 }
3228
3229 pub fn has_pending_nonempty_selection(&self) -> bool {
3230 let pending_nonempty_selection = match self.selections.pending_anchor() {
3231 Some(Selection { start, end, .. }) => start != end,
3232 None => false,
3233 };
3234
3235 pending_nonempty_selection
3236 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
3237 }
3238
3239 pub fn has_pending_selection(&self) -> bool {
3240 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
3241 }
3242
3243 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
3244 if self.clear_expanded_diff_hunks(cx) {
3245 cx.notify();
3246 return;
3247 }
3248 if self.dismiss_menus_and_popups(true, cx) {
3249 return;
3250 }
3251
3252 if self.mode == EditorMode::Full
3253 && self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel())
3254 {
3255 return;
3256 }
3257
3258 cx.propagate();
3259 }
3260
3261 pub fn dismiss_menus_and_popups(
3262 &mut self,
3263 should_report_inline_completion_event: bool,
3264 cx: &mut ViewContext<Self>,
3265 ) -> bool {
3266 if self.take_rename(false, cx).is_some() {
3267 return true;
3268 }
3269
3270 if hide_hover(self, cx) {
3271 return true;
3272 }
3273
3274 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
3275 return true;
3276 }
3277
3278 if self.hide_context_menu(cx).is_some() {
3279 return true;
3280 }
3281
3282 if self.mouse_context_menu.take().is_some() {
3283 return true;
3284 }
3285
3286 if self.discard_inline_completion(should_report_inline_completion_event, cx) {
3287 return true;
3288 }
3289
3290 if self.snippet_stack.pop().is_some() {
3291 return true;
3292 }
3293
3294 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
3295 self.dismiss_diagnostics(cx);
3296 return true;
3297 }
3298
3299 false
3300 }
3301
3302 fn linked_editing_ranges_for(
3303 &self,
3304 selection: Range<text::Anchor>,
3305 cx: &AppContext,
3306 ) -> Option<HashMap<Model<Buffer>, Vec<Range<text::Anchor>>>> {
3307 if self.linked_edit_ranges.is_empty() {
3308 return None;
3309 }
3310 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
3311 selection.end.buffer_id.and_then(|end_buffer_id| {
3312 if selection.start.buffer_id != Some(end_buffer_id) {
3313 return None;
3314 }
3315 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
3316 let snapshot = buffer.read(cx).snapshot();
3317 self.linked_edit_ranges
3318 .get(end_buffer_id, selection.start..selection.end, &snapshot)
3319 .map(|ranges| (ranges, snapshot, buffer))
3320 })?;
3321 use text::ToOffset as TO;
3322 // find offset from the start of current range to current cursor position
3323 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
3324
3325 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
3326 let start_difference = start_offset - start_byte_offset;
3327 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
3328 let end_difference = end_offset - start_byte_offset;
3329 // Current range has associated linked ranges.
3330 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3331 for range in linked_ranges.iter() {
3332 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
3333 let end_offset = start_offset + end_difference;
3334 let start_offset = start_offset + start_difference;
3335 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
3336 continue;
3337 }
3338 if self.selections.disjoint_anchor_ranges().iter().any(|s| {
3339 if s.start.buffer_id != selection.start.buffer_id
3340 || s.end.buffer_id != selection.end.buffer_id
3341 {
3342 return false;
3343 }
3344 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
3345 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
3346 }) {
3347 continue;
3348 }
3349 let start = buffer_snapshot.anchor_after(start_offset);
3350 let end = buffer_snapshot.anchor_after(end_offset);
3351 linked_edits
3352 .entry(buffer.clone())
3353 .or_default()
3354 .push(start..end);
3355 }
3356 Some(linked_edits)
3357 }
3358
3359 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3360 let text: Arc<str> = text.into();
3361
3362 if self.read_only(cx) {
3363 return;
3364 }
3365
3366 let selections = self.selections.all_adjusted(cx);
3367 let mut bracket_inserted = false;
3368 let mut edits = Vec::new();
3369 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3370 let mut new_selections = Vec::with_capacity(selections.len());
3371 let mut new_autoclose_regions = Vec::new();
3372 let snapshot = self.buffer.read(cx).read(cx);
3373
3374 for (selection, autoclose_region) in
3375 self.selections_with_autoclose_regions(selections, &snapshot)
3376 {
3377 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
3378 // Determine if the inserted text matches the opening or closing
3379 // bracket of any of this language's bracket pairs.
3380 let mut bracket_pair = None;
3381 let mut is_bracket_pair_start = false;
3382 let mut is_bracket_pair_end = false;
3383 if !text.is_empty() {
3384 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
3385 // and they are removing the character that triggered IME popup.
3386 for (pair, enabled) in scope.brackets() {
3387 if !pair.close && !pair.surround {
3388 continue;
3389 }
3390
3391 if enabled && pair.start.ends_with(text.as_ref()) {
3392 let prefix_len = pair.start.len() - text.len();
3393 let preceding_text_matches_prefix = prefix_len == 0
3394 || (selection.start.column >= (prefix_len as u32)
3395 && snapshot.contains_str_at(
3396 Point::new(
3397 selection.start.row,
3398 selection.start.column - (prefix_len as u32),
3399 ),
3400 &pair.start[..prefix_len],
3401 ));
3402 if preceding_text_matches_prefix {
3403 bracket_pair = Some(pair.clone());
3404 is_bracket_pair_start = true;
3405 break;
3406 }
3407 }
3408 if pair.end.as_str() == text.as_ref() {
3409 bracket_pair = Some(pair.clone());
3410 is_bracket_pair_end = true;
3411 break;
3412 }
3413 }
3414 }
3415
3416 if let Some(bracket_pair) = bracket_pair {
3417 let snapshot_settings = snapshot.settings_at(selection.start, cx);
3418 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
3419 let auto_surround =
3420 self.use_auto_surround && snapshot_settings.use_auto_surround;
3421 if selection.is_empty() {
3422 if is_bracket_pair_start {
3423 // If the inserted text is a suffix of an opening bracket and the
3424 // selection is preceded by the rest of the opening bracket, then
3425 // insert the closing bracket.
3426 let following_text_allows_autoclose = snapshot
3427 .chars_at(selection.start)
3428 .next()
3429 .map_or(true, |c| scope.should_autoclose_before(c));
3430
3431 let is_closing_quote = if bracket_pair.end == bracket_pair.start
3432 && bracket_pair.start.len() == 1
3433 {
3434 let target = bracket_pair.start.chars().next().unwrap();
3435 let current_line_count = snapshot
3436 .reversed_chars_at(selection.start)
3437 .take_while(|&c| c != '\n')
3438 .filter(|&c| c == target)
3439 .count();
3440 current_line_count % 2 == 1
3441 } else {
3442 false
3443 };
3444
3445 if autoclose
3446 && bracket_pair.close
3447 && following_text_allows_autoclose
3448 && !is_closing_quote
3449 {
3450 let anchor = snapshot.anchor_before(selection.end);
3451 new_selections.push((selection.map(|_| anchor), text.len()));
3452 new_autoclose_regions.push((
3453 anchor,
3454 text.len(),
3455 selection.id,
3456 bracket_pair.clone(),
3457 ));
3458 edits.push((
3459 selection.range(),
3460 format!("{}{}", text, bracket_pair.end).into(),
3461 ));
3462 bracket_inserted = true;
3463 continue;
3464 }
3465 }
3466
3467 if let Some(region) = autoclose_region {
3468 // If the selection is followed by an auto-inserted closing bracket,
3469 // then don't insert that closing bracket again; just move the selection
3470 // past the closing bracket.
3471 let should_skip = selection.end == region.range.end.to_point(&snapshot)
3472 && text.as_ref() == region.pair.end.as_str();
3473 if should_skip {
3474 let anchor = snapshot.anchor_after(selection.end);
3475 new_selections
3476 .push((selection.map(|_| anchor), region.pair.end.len()));
3477 continue;
3478 }
3479 }
3480
3481 let always_treat_brackets_as_autoclosed = snapshot
3482 .settings_at(selection.start, cx)
3483 .always_treat_brackets_as_autoclosed;
3484 if always_treat_brackets_as_autoclosed
3485 && is_bracket_pair_end
3486 && snapshot.contains_str_at(selection.end, text.as_ref())
3487 {
3488 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
3489 // and the inserted text is a closing bracket and the selection is followed
3490 // by the closing bracket then move the selection past the closing bracket.
3491 let anchor = snapshot.anchor_after(selection.end);
3492 new_selections.push((selection.map(|_| anchor), text.len()));
3493 continue;
3494 }
3495 }
3496 // If an opening bracket is 1 character long and is typed while
3497 // text is selected, then surround that text with the bracket pair.
3498 else if auto_surround
3499 && bracket_pair.surround
3500 && is_bracket_pair_start
3501 && bracket_pair.start.chars().count() == 1
3502 {
3503 edits.push((selection.start..selection.start, text.clone()));
3504 edits.push((
3505 selection.end..selection.end,
3506 bracket_pair.end.as_str().into(),
3507 ));
3508 bracket_inserted = true;
3509 new_selections.push((
3510 Selection {
3511 id: selection.id,
3512 start: snapshot.anchor_after(selection.start),
3513 end: snapshot.anchor_before(selection.end),
3514 reversed: selection.reversed,
3515 goal: selection.goal,
3516 },
3517 0,
3518 ));
3519 continue;
3520 }
3521 }
3522 }
3523
3524 if self.auto_replace_emoji_shortcode
3525 && selection.is_empty()
3526 && text.as_ref().ends_with(':')
3527 {
3528 if let Some(possible_emoji_short_code) =
3529 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
3530 {
3531 if !possible_emoji_short_code.is_empty() {
3532 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
3533 let emoji_shortcode_start = Point::new(
3534 selection.start.row,
3535 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
3536 );
3537
3538 // Remove shortcode from buffer
3539 edits.push((
3540 emoji_shortcode_start..selection.start,
3541 "".to_string().into(),
3542 ));
3543 new_selections.push((
3544 Selection {
3545 id: selection.id,
3546 start: snapshot.anchor_after(emoji_shortcode_start),
3547 end: snapshot.anchor_before(selection.start),
3548 reversed: selection.reversed,
3549 goal: selection.goal,
3550 },
3551 0,
3552 ));
3553
3554 // Insert emoji
3555 let selection_start_anchor = snapshot.anchor_after(selection.start);
3556 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3557 edits.push((selection.start..selection.end, emoji.to_string().into()));
3558
3559 continue;
3560 }
3561 }
3562 }
3563 }
3564
3565 // If not handling any auto-close operation, then just replace the selected
3566 // text with the given input and move the selection to the end of the
3567 // newly inserted text.
3568 let anchor = snapshot.anchor_after(selection.end);
3569 if !self.linked_edit_ranges.is_empty() {
3570 let start_anchor = snapshot.anchor_before(selection.start);
3571
3572 let is_word_char = text.chars().next().map_or(true, |char| {
3573 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
3574 classifier.is_word(char)
3575 });
3576
3577 if is_word_char {
3578 if let Some(ranges) = self
3579 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3580 {
3581 for (buffer, edits) in ranges {
3582 linked_edits
3583 .entry(buffer.clone())
3584 .or_default()
3585 .extend(edits.into_iter().map(|range| (range, text.clone())));
3586 }
3587 }
3588 }
3589 }
3590
3591 new_selections.push((selection.map(|_| anchor), 0));
3592 edits.push((selection.start..selection.end, text.clone()));
3593 }
3594
3595 drop(snapshot);
3596
3597 self.transact(cx, |this, cx| {
3598 this.buffer.update(cx, |buffer, cx| {
3599 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3600 });
3601 for (buffer, edits) in linked_edits {
3602 buffer.update(cx, |buffer, cx| {
3603 let snapshot = buffer.snapshot();
3604 let edits = edits
3605 .into_iter()
3606 .map(|(range, text)| {
3607 use text::ToPoint as TP;
3608 let end_point = TP::to_point(&range.end, &snapshot);
3609 let start_point = TP::to_point(&range.start, &snapshot);
3610 (start_point..end_point, text)
3611 })
3612 .sorted_by_key(|(range, _)| range.start)
3613 .collect::<Vec<_>>();
3614 buffer.edit(edits, None, cx);
3615 })
3616 }
3617 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3618 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3619 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3620 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
3621 .zip(new_selection_deltas)
3622 .map(|(selection, delta)| Selection {
3623 id: selection.id,
3624 start: selection.start + delta,
3625 end: selection.end + delta,
3626 reversed: selection.reversed,
3627 goal: SelectionGoal::None,
3628 })
3629 .collect::<Vec<_>>();
3630
3631 let mut i = 0;
3632 for (position, delta, selection_id, pair) in new_autoclose_regions {
3633 let position = position.to_offset(&map.buffer_snapshot) + delta;
3634 let start = map.buffer_snapshot.anchor_before(position);
3635 let end = map.buffer_snapshot.anchor_after(position);
3636 while let Some(existing_state) = this.autoclose_regions.get(i) {
3637 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
3638 Ordering::Less => i += 1,
3639 Ordering::Greater => break,
3640 Ordering::Equal => {
3641 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
3642 Ordering::Less => i += 1,
3643 Ordering::Equal => break,
3644 Ordering::Greater => break,
3645 }
3646 }
3647 }
3648 }
3649 this.autoclose_regions.insert(
3650 i,
3651 AutocloseRegion {
3652 selection_id,
3653 range: start..end,
3654 pair,
3655 },
3656 );
3657 }
3658
3659 let had_active_inline_completion = this.has_active_inline_completion(cx);
3660 this.change_selections_inner(Some(Autoscroll::fit()), false, cx, |s| {
3661 s.select(new_selections)
3662 });
3663
3664 if !bracket_inserted {
3665 if let Some(on_type_format_task) =
3666 this.trigger_on_type_formatting(text.to_string(), cx)
3667 {
3668 on_type_format_task.detach_and_log_err(cx);
3669 }
3670 }
3671
3672 let editor_settings = EditorSettings::get_global(cx);
3673 if bracket_inserted
3674 && (editor_settings.auto_signature_help
3675 || editor_settings.show_signature_help_after_edits)
3676 {
3677 this.show_signature_help(&ShowSignatureHelp, cx);
3678 }
3679
3680 let trigger_in_words = !had_active_inline_completion;
3681 this.trigger_completion_on_input(&text, trigger_in_words, cx);
3682 linked_editing_ranges::refresh_linked_ranges(this, cx);
3683 this.refresh_inline_completion(true, false, cx);
3684 });
3685 }
3686
3687 fn find_possible_emoji_shortcode_at_position(
3688 snapshot: &MultiBufferSnapshot,
3689 position: Point,
3690 ) -> Option<String> {
3691 let mut chars = Vec::new();
3692 let mut found_colon = false;
3693 for char in snapshot.reversed_chars_at(position).take(100) {
3694 // Found a possible emoji shortcode in the middle of the buffer
3695 if found_colon {
3696 if char.is_whitespace() {
3697 chars.reverse();
3698 return Some(chars.iter().collect());
3699 }
3700 // If the previous character is not a whitespace, we are in the middle of a word
3701 // and we only want to complete the shortcode if the word is made up of other emojis
3702 let mut containing_word = String::new();
3703 for ch in snapshot
3704 .reversed_chars_at(position)
3705 .skip(chars.len() + 1)
3706 .take(100)
3707 {
3708 if ch.is_whitespace() {
3709 break;
3710 }
3711 containing_word.push(ch);
3712 }
3713 let containing_word = containing_word.chars().rev().collect::<String>();
3714 if util::word_consists_of_emojis(containing_word.as_str()) {
3715 chars.reverse();
3716 return Some(chars.iter().collect());
3717 }
3718 }
3719
3720 if char.is_whitespace() || !char.is_ascii() {
3721 return None;
3722 }
3723 if char == ':' {
3724 found_colon = true;
3725 } else {
3726 chars.push(char);
3727 }
3728 }
3729 // Found a possible emoji shortcode at the beginning of the buffer
3730 chars.reverse();
3731 Some(chars.iter().collect())
3732 }
3733
3734 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
3735 self.transact(cx, |this, cx| {
3736 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3737 let selections = this.selections.all::<usize>(cx);
3738 let multi_buffer = this.buffer.read(cx);
3739 let buffer = multi_buffer.snapshot(cx);
3740 selections
3741 .iter()
3742 .map(|selection| {
3743 let start_point = selection.start.to_point(&buffer);
3744 let mut indent =
3745 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3746 indent.len = cmp::min(indent.len, start_point.column);
3747 let start = selection.start;
3748 let end = selection.end;
3749 let selection_is_empty = start == end;
3750 let language_scope = buffer.language_scope_at(start);
3751 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3752 &language_scope
3753 {
3754 let leading_whitespace_len = buffer
3755 .reversed_chars_at(start)
3756 .take_while(|c| c.is_whitespace() && *c != '\n')
3757 .map(|c| c.len_utf8())
3758 .sum::<usize>();
3759
3760 let trailing_whitespace_len = buffer
3761 .chars_at(end)
3762 .take_while(|c| c.is_whitespace() && *c != '\n')
3763 .map(|c| c.len_utf8())
3764 .sum::<usize>();
3765
3766 let insert_extra_newline =
3767 language.brackets().any(|(pair, enabled)| {
3768 let pair_start = pair.start.trim_end();
3769 let pair_end = pair.end.trim_start();
3770
3771 enabled
3772 && pair.newline
3773 && buffer.contains_str_at(
3774 end + trailing_whitespace_len,
3775 pair_end,
3776 )
3777 && buffer.contains_str_at(
3778 (start - leading_whitespace_len)
3779 .saturating_sub(pair_start.len()),
3780 pair_start,
3781 )
3782 });
3783
3784 // Comment extension on newline is allowed only for cursor selections
3785 let comment_delimiter = maybe!({
3786 if !selection_is_empty {
3787 return None;
3788 }
3789
3790 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
3791 return None;
3792 }
3793
3794 let delimiters = language.line_comment_prefixes();
3795 let max_len_of_delimiter =
3796 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3797 let (snapshot, range) =
3798 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3799
3800 let mut index_of_first_non_whitespace = 0;
3801 let comment_candidate = snapshot
3802 .chars_for_range(range)
3803 .skip_while(|c| {
3804 let should_skip = c.is_whitespace();
3805 if should_skip {
3806 index_of_first_non_whitespace += 1;
3807 }
3808 should_skip
3809 })
3810 .take(max_len_of_delimiter)
3811 .collect::<String>();
3812 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3813 comment_candidate.starts_with(comment_prefix.as_ref())
3814 })?;
3815 let cursor_is_placed_after_comment_marker =
3816 index_of_first_non_whitespace + comment_prefix.len()
3817 <= start_point.column as usize;
3818 if cursor_is_placed_after_comment_marker {
3819 Some(comment_prefix.clone())
3820 } else {
3821 None
3822 }
3823 });
3824 (comment_delimiter, insert_extra_newline)
3825 } else {
3826 (None, false)
3827 };
3828
3829 let capacity_for_delimiter = comment_delimiter
3830 .as_deref()
3831 .map(str::len)
3832 .unwrap_or_default();
3833 let mut new_text =
3834 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3835 new_text.push('\n');
3836 new_text.extend(indent.chars());
3837 if let Some(delimiter) = &comment_delimiter {
3838 new_text.push_str(delimiter);
3839 }
3840 if insert_extra_newline {
3841 new_text = new_text.repeat(2);
3842 }
3843
3844 let anchor = buffer.anchor_after(end);
3845 let new_selection = selection.map(|_| anchor);
3846 (
3847 (start..end, new_text),
3848 (insert_extra_newline, new_selection),
3849 )
3850 })
3851 .unzip()
3852 };
3853
3854 this.edit_with_autoindent(edits, cx);
3855 let buffer = this.buffer.read(cx).snapshot(cx);
3856 let new_selections = selection_fixup_info
3857 .into_iter()
3858 .map(|(extra_newline_inserted, new_selection)| {
3859 let mut cursor = new_selection.end.to_point(&buffer);
3860 if extra_newline_inserted {
3861 cursor.row -= 1;
3862 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3863 }
3864 new_selection.map(|_| cursor)
3865 })
3866 .collect();
3867
3868 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
3869 this.refresh_inline_completion(true, false, cx);
3870 });
3871 }
3872
3873 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
3874 let buffer = self.buffer.read(cx);
3875 let snapshot = buffer.snapshot(cx);
3876
3877 let mut edits = Vec::new();
3878 let mut rows = Vec::new();
3879
3880 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3881 let cursor = selection.head();
3882 let row = cursor.row;
3883
3884 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3885
3886 let newline = "\n".to_string();
3887 edits.push((start_of_line..start_of_line, newline));
3888
3889 rows.push(row + rows_inserted as u32);
3890 }
3891
3892 self.transact(cx, |editor, cx| {
3893 editor.edit(edits, cx);
3894
3895 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3896 let mut index = 0;
3897 s.move_cursors_with(|map, _, _| {
3898 let row = rows[index];
3899 index += 1;
3900
3901 let point = Point::new(row, 0);
3902 let boundary = map.next_line_boundary(point).1;
3903 let clipped = map.clip_point(boundary, Bias::Left);
3904
3905 (clipped, SelectionGoal::None)
3906 });
3907 });
3908
3909 let mut indent_edits = Vec::new();
3910 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3911 for row in rows {
3912 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3913 for (row, indent) in indents {
3914 if indent.len == 0 {
3915 continue;
3916 }
3917
3918 let text = match indent.kind {
3919 IndentKind::Space => " ".repeat(indent.len as usize),
3920 IndentKind::Tab => "\t".repeat(indent.len as usize),
3921 };
3922 let point = Point::new(row.0, 0);
3923 indent_edits.push((point..point, text));
3924 }
3925 }
3926 editor.edit(indent_edits, cx);
3927 });
3928 }
3929
3930 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
3931 let buffer = self.buffer.read(cx);
3932 let snapshot = buffer.snapshot(cx);
3933
3934 let mut edits = Vec::new();
3935 let mut rows = Vec::new();
3936 let mut rows_inserted = 0;
3937
3938 for selection in self.selections.all_adjusted(cx) {
3939 let cursor = selection.head();
3940 let row = cursor.row;
3941
3942 let point = Point::new(row + 1, 0);
3943 let start_of_line = snapshot.clip_point(point, Bias::Left);
3944
3945 let newline = "\n".to_string();
3946 edits.push((start_of_line..start_of_line, newline));
3947
3948 rows_inserted += 1;
3949 rows.push(row + rows_inserted);
3950 }
3951
3952 self.transact(cx, |editor, cx| {
3953 editor.edit(edits, cx);
3954
3955 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3956 let mut index = 0;
3957 s.move_cursors_with(|map, _, _| {
3958 let row = rows[index];
3959 index += 1;
3960
3961 let point = Point::new(row, 0);
3962 let boundary = map.next_line_boundary(point).1;
3963 let clipped = map.clip_point(boundary, Bias::Left);
3964
3965 (clipped, SelectionGoal::None)
3966 });
3967 });
3968
3969 let mut indent_edits = Vec::new();
3970 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3971 for row in rows {
3972 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3973 for (row, indent) in indents {
3974 if indent.len == 0 {
3975 continue;
3976 }
3977
3978 let text = match indent.kind {
3979 IndentKind::Space => " ".repeat(indent.len as usize),
3980 IndentKind::Tab => "\t".repeat(indent.len as usize),
3981 };
3982 let point = Point::new(row.0, 0);
3983 indent_edits.push((point..point, text));
3984 }
3985 }
3986 editor.edit(indent_edits, cx);
3987 });
3988 }
3989
3990 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3991 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3992 original_indent_columns: Vec::new(),
3993 });
3994 self.insert_with_autoindent_mode(text, autoindent, cx);
3995 }
3996
3997 fn insert_with_autoindent_mode(
3998 &mut self,
3999 text: &str,
4000 autoindent_mode: Option<AutoindentMode>,
4001 cx: &mut ViewContext<Self>,
4002 ) {
4003 if self.read_only(cx) {
4004 return;
4005 }
4006
4007 let text: Arc<str> = text.into();
4008 self.transact(cx, |this, cx| {
4009 let old_selections = this.selections.all_adjusted(cx);
4010 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
4011 let anchors = {
4012 let snapshot = buffer.read(cx);
4013 old_selections
4014 .iter()
4015 .map(|s| {
4016 let anchor = snapshot.anchor_after(s.head());
4017 s.map(|_| anchor)
4018 })
4019 .collect::<Vec<_>>()
4020 };
4021 buffer.edit(
4022 old_selections
4023 .iter()
4024 .map(|s| (s.start..s.end, text.clone())),
4025 autoindent_mode,
4026 cx,
4027 );
4028 anchors
4029 });
4030
4031 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4032 s.select_anchors(selection_anchors);
4033 })
4034 });
4035 }
4036
4037 fn trigger_completion_on_input(
4038 &mut self,
4039 text: &str,
4040 trigger_in_words: bool,
4041 cx: &mut ViewContext<Self>,
4042 ) {
4043 if self.is_completion_trigger(text, trigger_in_words, cx) {
4044 self.show_completions(
4045 &ShowCompletions {
4046 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
4047 },
4048 cx,
4049 );
4050 } else {
4051 self.hide_context_menu(cx);
4052 }
4053 }
4054
4055 fn is_completion_trigger(
4056 &self,
4057 text: &str,
4058 trigger_in_words: bool,
4059 cx: &mut ViewContext<Self>,
4060 ) -> bool {
4061 let position = self.selections.newest_anchor().head();
4062 let multibuffer = self.buffer.read(cx);
4063 let Some(buffer) = position
4064 .buffer_id
4065 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
4066 else {
4067 return false;
4068 };
4069
4070 if let Some(completion_provider) = &self.completion_provider {
4071 completion_provider.is_completion_trigger(
4072 &buffer,
4073 position.text_anchor,
4074 text,
4075 trigger_in_words,
4076 cx,
4077 )
4078 } else {
4079 false
4080 }
4081 }
4082
4083 /// If any empty selections is touching the start of its innermost containing autoclose
4084 /// region, expand it to select the brackets.
4085 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
4086 let selections = self.selections.all::<usize>(cx);
4087 let buffer = self.buffer.read(cx).read(cx);
4088 let new_selections = self
4089 .selections_with_autoclose_regions(selections, &buffer)
4090 .map(|(mut selection, region)| {
4091 if !selection.is_empty() {
4092 return selection;
4093 }
4094
4095 if let Some(region) = region {
4096 let mut range = region.range.to_offset(&buffer);
4097 if selection.start == range.start && range.start >= region.pair.start.len() {
4098 range.start -= region.pair.start.len();
4099 if buffer.contains_str_at(range.start, ®ion.pair.start)
4100 && buffer.contains_str_at(range.end, ®ion.pair.end)
4101 {
4102 range.end += region.pair.end.len();
4103 selection.start = range.start;
4104 selection.end = range.end;
4105
4106 return selection;
4107 }
4108 }
4109 }
4110
4111 let always_treat_brackets_as_autoclosed = buffer
4112 .settings_at(selection.start, cx)
4113 .always_treat_brackets_as_autoclosed;
4114
4115 if !always_treat_brackets_as_autoclosed {
4116 return selection;
4117 }
4118
4119 if let Some(scope) = buffer.language_scope_at(selection.start) {
4120 for (pair, enabled) in scope.brackets() {
4121 if !enabled || !pair.close {
4122 continue;
4123 }
4124
4125 if buffer.contains_str_at(selection.start, &pair.end) {
4126 let pair_start_len = pair.start.len();
4127 if buffer.contains_str_at(
4128 selection.start.saturating_sub(pair_start_len),
4129 &pair.start,
4130 ) {
4131 selection.start -= pair_start_len;
4132 selection.end += pair.end.len();
4133
4134 return selection;
4135 }
4136 }
4137 }
4138 }
4139
4140 selection
4141 })
4142 .collect();
4143
4144 drop(buffer);
4145 self.change_selections(None, cx, |selections| selections.select(new_selections));
4146 }
4147
4148 /// Iterate the given selections, and for each one, find the smallest surrounding
4149 /// autoclose region. This uses the ordering of the selections and the autoclose
4150 /// regions to avoid repeated comparisons.
4151 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
4152 &'a self,
4153 selections: impl IntoIterator<Item = Selection<D>>,
4154 buffer: &'a MultiBufferSnapshot,
4155 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
4156 let mut i = 0;
4157 let mut regions = self.autoclose_regions.as_slice();
4158 selections.into_iter().map(move |selection| {
4159 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
4160
4161 let mut enclosing = None;
4162 while let Some(pair_state) = regions.get(i) {
4163 if pair_state.range.end.to_offset(buffer) < range.start {
4164 regions = ®ions[i + 1..];
4165 i = 0;
4166 } else if pair_state.range.start.to_offset(buffer) > range.end {
4167 break;
4168 } else {
4169 if pair_state.selection_id == selection.id {
4170 enclosing = Some(pair_state);
4171 }
4172 i += 1;
4173 }
4174 }
4175
4176 (selection, enclosing)
4177 })
4178 }
4179
4180 /// Remove any autoclose regions that no longer contain their selection.
4181 fn invalidate_autoclose_regions(
4182 &mut self,
4183 mut selections: &[Selection<Anchor>],
4184 buffer: &MultiBufferSnapshot,
4185 ) {
4186 self.autoclose_regions.retain(|state| {
4187 let mut i = 0;
4188 while let Some(selection) = selections.get(i) {
4189 if selection.end.cmp(&state.range.start, buffer).is_lt() {
4190 selections = &selections[1..];
4191 continue;
4192 }
4193 if selection.start.cmp(&state.range.end, buffer).is_gt() {
4194 break;
4195 }
4196 if selection.id == state.selection_id {
4197 return true;
4198 } else {
4199 i += 1;
4200 }
4201 }
4202 false
4203 });
4204 }
4205
4206 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
4207 let offset = position.to_offset(buffer);
4208 let (word_range, kind) = buffer.surrounding_word(offset, true);
4209 if offset > word_range.start && kind == Some(CharKind::Word) {
4210 Some(
4211 buffer
4212 .text_for_range(word_range.start..offset)
4213 .collect::<String>(),
4214 )
4215 } else {
4216 None
4217 }
4218 }
4219
4220 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
4221 self.refresh_inlay_hints(
4222 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
4223 cx,
4224 );
4225 }
4226
4227 pub fn inlay_hints_enabled(&self) -> bool {
4228 self.inlay_hint_cache.enabled
4229 }
4230
4231 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
4232 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
4233 return;
4234 }
4235
4236 let reason_description = reason.description();
4237 let ignore_debounce = matches!(
4238 reason,
4239 InlayHintRefreshReason::SettingsChange(_)
4240 | InlayHintRefreshReason::Toggle(_)
4241 | InlayHintRefreshReason::ExcerptsRemoved(_)
4242 );
4243 let (invalidate_cache, required_languages) = match reason {
4244 InlayHintRefreshReason::Toggle(enabled) => {
4245 self.inlay_hint_cache.enabled = enabled;
4246 if enabled {
4247 (InvalidationStrategy::RefreshRequested, None)
4248 } else {
4249 self.inlay_hint_cache.clear();
4250 self.splice_inlays(
4251 self.visible_inlay_hints(cx)
4252 .iter()
4253 .map(|inlay| inlay.id)
4254 .collect(),
4255 Vec::new(),
4256 cx,
4257 );
4258 return;
4259 }
4260 }
4261 InlayHintRefreshReason::SettingsChange(new_settings) => {
4262 match self.inlay_hint_cache.update_settings(
4263 &self.buffer,
4264 new_settings,
4265 self.visible_inlay_hints(cx),
4266 cx,
4267 ) {
4268 ControlFlow::Break(Some(InlaySplice {
4269 to_remove,
4270 to_insert,
4271 })) => {
4272 self.splice_inlays(to_remove, to_insert, cx);
4273 return;
4274 }
4275 ControlFlow::Break(None) => return,
4276 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
4277 }
4278 }
4279 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
4280 if let Some(InlaySplice {
4281 to_remove,
4282 to_insert,
4283 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
4284 {
4285 self.splice_inlays(to_remove, to_insert, cx);
4286 }
4287 return;
4288 }
4289 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
4290 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
4291 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
4292 }
4293 InlayHintRefreshReason::RefreshRequested => {
4294 (InvalidationStrategy::RefreshRequested, None)
4295 }
4296 };
4297
4298 if let Some(InlaySplice {
4299 to_remove,
4300 to_insert,
4301 }) = self.inlay_hint_cache.spawn_hint_refresh(
4302 reason_description,
4303 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
4304 invalidate_cache,
4305 ignore_debounce,
4306 cx,
4307 ) {
4308 self.splice_inlays(to_remove, to_insert, cx);
4309 }
4310 }
4311
4312 fn visible_inlay_hints(&self, cx: &ViewContext<'_, Editor>) -> Vec<Inlay> {
4313 self.display_map
4314 .read(cx)
4315 .current_inlays()
4316 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
4317 .cloned()
4318 .collect()
4319 }
4320
4321 pub fn excerpts_for_inlay_hints_query(
4322 &self,
4323 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
4324 cx: &mut ViewContext<Editor>,
4325 ) -> HashMap<ExcerptId, (Model<Buffer>, clock::Global, Range<usize>)> {
4326 let Some(project) = self.project.as_ref() else {
4327 return HashMap::default();
4328 };
4329 let project = project.read(cx);
4330 let multi_buffer = self.buffer().read(cx);
4331 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
4332 let multi_buffer_visible_start = self
4333 .scroll_manager
4334 .anchor()
4335 .anchor
4336 .to_point(&multi_buffer_snapshot);
4337 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
4338 multi_buffer_visible_start
4339 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
4340 Bias::Left,
4341 );
4342 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
4343 multi_buffer
4344 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
4345 .into_iter()
4346 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
4347 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
4348 let buffer = buffer_handle.read(cx);
4349 let buffer_file = project::File::from_dyn(buffer.file())?;
4350 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
4351 let worktree_entry = buffer_worktree
4352 .read(cx)
4353 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
4354 if worktree_entry.is_ignored {
4355 return None;
4356 }
4357
4358 let language = buffer.language()?;
4359 if let Some(restrict_to_languages) = restrict_to_languages {
4360 if !restrict_to_languages.contains(language) {
4361 return None;
4362 }
4363 }
4364 Some((
4365 excerpt_id,
4366 (
4367 buffer_handle,
4368 buffer.version().clone(),
4369 excerpt_visible_range,
4370 ),
4371 ))
4372 })
4373 .collect()
4374 }
4375
4376 pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
4377 TextLayoutDetails {
4378 text_system: cx.text_system().clone(),
4379 editor_style: self.style.clone().unwrap(),
4380 rem_size: cx.rem_size(),
4381 scroll_anchor: self.scroll_manager.anchor(),
4382 visible_rows: self.visible_line_count(),
4383 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
4384 }
4385 }
4386
4387 fn splice_inlays(
4388 &self,
4389 to_remove: Vec<InlayId>,
4390 to_insert: Vec<Inlay>,
4391 cx: &mut ViewContext<Self>,
4392 ) {
4393 self.display_map.update(cx, |display_map, cx| {
4394 display_map.splice_inlays(to_remove, to_insert, cx);
4395 });
4396 cx.notify();
4397 }
4398
4399 fn trigger_on_type_formatting(
4400 &self,
4401 input: String,
4402 cx: &mut ViewContext<Self>,
4403 ) -> Option<Task<Result<()>>> {
4404 if input.len() != 1 {
4405 return None;
4406 }
4407
4408 let project = self.project.as_ref()?;
4409 let position = self.selections.newest_anchor().head();
4410 let (buffer, buffer_position) = self
4411 .buffer
4412 .read(cx)
4413 .text_anchor_for_position(position, cx)?;
4414
4415 let settings = language_settings::language_settings(
4416 buffer
4417 .read(cx)
4418 .language_at(buffer_position)
4419 .map(|l| l.name()),
4420 buffer.read(cx).file(),
4421 cx,
4422 );
4423 if !settings.use_on_type_format {
4424 return None;
4425 }
4426
4427 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
4428 // hence we do LSP request & edit on host side only — add formats to host's history.
4429 let push_to_lsp_host_history = true;
4430 // If this is not the host, append its history with new edits.
4431 let push_to_client_history = project.read(cx).is_via_collab();
4432
4433 let on_type_formatting = project.update(cx, |project, cx| {
4434 project.on_type_format(
4435 buffer.clone(),
4436 buffer_position,
4437 input,
4438 push_to_lsp_host_history,
4439 cx,
4440 )
4441 });
4442 Some(cx.spawn(|editor, mut cx| async move {
4443 if let Some(transaction) = on_type_formatting.await? {
4444 if push_to_client_history {
4445 buffer
4446 .update(&mut cx, |buffer, _| {
4447 buffer.push_transaction(transaction, Instant::now());
4448 })
4449 .ok();
4450 }
4451 editor.update(&mut cx, |editor, cx| {
4452 editor.refresh_document_highlights(cx);
4453 })?;
4454 }
4455 Ok(())
4456 }))
4457 }
4458
4459 pub fn show_completions(&mut self, options: &ShowCompletions, cx: &mut ViewContext<Self>) {
4460 if self.pending_rename.is_some() {
4461 return;
4462 }
4463
4464 let Some(provider) = self.completion_provider.as_ref() else {
4465 return;
4466 };
4467
4468 if !self.snippet_stack.is_empty() && self.context_menu.read().as_ref().is_some() {
4469 return;
4470 }
4471
4472 let position = self.selections.newest_anchor().head();
4473 let (buffer, buffer_position) =
4474 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
4475 output
4476 } else {
4477 return;
4478 };
4479
4480 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
4481 let is_followup_invoke = {
4482 let context_menu_state = self.context_menu.read();
4483 matches!(
4484 context_menu_state.deref(),
4485 Some(ContextMenu::Completions(_))
4486 )
4487 };
4488 let trigger_kind = match (&options.trigger, is_followup_invoke) {
4489 (_, true) => CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS,
4490 (Some(trigger), _) if buffer.read(cx).completion_triggers().contains(trigger) => {
4491 CompletionTriggerKind::TRIGGER_CHARACTER
4492 }
4493
4494 _ => CompletionTriggerKind::INVOKED,
4495 };
4496 let completion_context = CompletionContext {
4497 trigger_character: options.trigger.as_ref().and_then(|trigger| {
4498 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
4499 Some(String::from(trigger))
4500 } else {
4501 None
4502 }
4503 }),
4504 trigger_kind,
4505 };
4506 let completions = provider.completions(&buffer, buffer_position, completion_context, cx);
4507 let sort_completions = provider.sort_completions();
4508
4509 let id = post_inc(&mut self.next_completion_id);
4510 let task = cx.spawn(|editor, mut cx| {
4511 async move {
4512 editor.update(&mut cx, |this, _| {
4513 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
4514 })?;
4515 let completions = completions.await.log_err();
4516 let menu = if let Some(completions) = completions {
4517 let mut menu = CompletionsMenu::new(
4518 id,
4519 sort_completions,
4520 position,
4521 buffer.clone(),
4522 completions.into(),
4523 );
4524 menu.filter(query.as_deref(), cx.background_executor().clone())
4525 .await;
4526
4527 if menu.matches.is_empty() {
4528 None
4529 } else {
4530 Some(menu)
4531 }
4532 } else {
4533 None
4534 };
4535
4536 editor.update(&mut cx, |editor, cx| {
4537 let mut context_menu = editor.context_menu.write();
4538 match context_menu.as_ref() {
4539 None => {}
4540
4541 Some(ContextMenu::Completions(prev_menu)) => {
4542 if prev_menu.id > id {
4543 return;
4544 }
4545 }
4546
4547 _ => return,
4548 }
4549
4550 if editor.focus_handle.is_focused(cx) && menu.is_some() {
4551 let mut menu = menu.unwrap();
4552 menu.resolve_selected_completion(editor.completion_provider.as_deref(), cx);
4553 *context_menu = Some(ContextMenu::Completions(menu));
4554 drop(context_menu);
4555 editor.discard_inline_completion(false, cx);
4556 cx.notify();
4557 } else if editor.completion_tasks.len() <= 1 {
4558 // If there are no more completion tasks and the last menu was
4559 // empty, we should hide it. If it was already hidden, we should
4560 // also show the copilot completion when available.
4561 drop(context_menu);
4562 if editor.hide_context_menu(cx).is_none() {
4563 editor.update_visible_inline_completion(cx);
4564 }
4565 }
4566 })?;
4567
4568 Ok::<_, anyhow::Error>(())
4569 }
4570 .log_err()
4571 });
4572
4573 self.completion_tasks.push((id, task));
4574 }
4575
4576 pub fn confirm_completion(
4577 &mut self,
4578 action: &ConfirmCompletion,
4579 cx: &mut ViewContext<Self>,
4580 ) -> Option<Task<Result<()>>> {
4581 self.do_completion(action.item_ix, CompletionIntent::Complete, cx)
4582 }
4583
4584 pub fn compose_completion(
4585 &mut self,
4586 action: &ComposeCompletion,
4587 cx: &mut ViewContext<Self>,
4588 ) -> Option<Task<Result<()>>> {
4589 self.do_completion(action.item_ix, CompletionIntent::Compose, cx)
4590 }
4591
4592 fn do_completion(
4593 &mut self,
4594 item_ix: Option<usize>,
4595 intent: CompletionIntent,
4596 cx: &mut ViewContext<Editor>,
4597 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
4598 use language::ToOffset as _;
4599
4600 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
4601 menu
4602 } else {
4603 return None;
4604 };
4605
4606 let mat = completions_menu
4607 .matches
4608 .get(item_ix.unwrap_or(completions_menu.selected_item))?;
4609 let buffer_handle = completions_menu.buffer;
4610 let completions = completions_menu.completions.read();
4611 let completion = completions.get(mat.candidate_id)?;
4612 cx.stop_propagation();
4613
4614 let snippet;
4615 let text;
4616
4617 if completion.is_snippet() {
4618 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4619 text = snippet.as_ref().unwrap().text.clone();
4620 } else {
4621 snippet = None;
4622 text = completion.new_text.clone();
4623 };
4624 let selections = self.selections.all::<usize>(cx);
4625 let buffer = buffer_handle.read(cx);
4626 let old_range = completion.old_range.to_offset(buffer);
4627 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4628
4629 let newest_selection = self.selections.newest_anchor();
4630 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4631 return None;
4632 }
4633
4634 let lookbehind = newest_selection
4635 .start
4636 .text_anchor
4637 .to_offset(buffer)
4638 .saturating_sub(old_range.start);
4639 let lookahead = old_range
4640 .end
4641 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4642 let mut common_prefix_len = old_text
4643 .bytes()
4644 .zip(text.bytes())
4645 .take_while(|(a, b)| a == b)
4646 .count();
4647
4648 let snapshot = self.buffer.read(cx).snapshot(cx);
4649 let mut range_to_replace: Option<Range<isize>> = None;
4650 let mut ranges = Vec::new();
4651 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4652 for selection in &selections {
4653 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4654 let start = selection.start.saturating_sub(lookbehind);
4655 let end = selection.end + lookahead;
4656 if selection.id == newest_selection.id {
4657 range_to_replace = Some(
4658 ((start + common_prefix_len) as isize - selection.start as isize)
4659 ..(end as isize - selection.start as isize),
4660 );
4661 }
4662 ranges.push(start + common_prefix_len..end);
4663 } else {
4664 common_prefix_len = 0;
4665 ranges.clear();
4666 ranges.extend(selections.iter().map(|s| {
4667 if s.id == newest_selection.id {
4668 range_to_replace = Some(
4669 old_range.start.to_offset_utf16(&snapshot).0 as isize
4670 - selection.start as isize
4671 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4672 - selection.start as isize,
4673 );
4674 old_range.clone()
4675 } else {
4676 s.start..s.end
4677 }
4678 }));
4679 break;
4680 }
4681 if !self.linked_edit_ranges.is_empty() {
4682 let start_anchor = snapshot.anchor_before(selection.head());
4683 let end_anchor = snapshot.anchor_after(selection.tail());
4684 if let Some(ranges) = self
4685 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4686 {
4687 for (buffer, edits) in ranges {
4688 linked_edits.entry(buffer.clone()).or_default().extend(
4689 edits
4690 .into_iter()
4691 .map(|range| (range, text[common_prefix_len..].to_owned())),
4692 );
4693 }
4694 }
4695 }
4696 }
4697 let text = &text[common_prefix_len..];
4698
4699 cx.emit(EditorEvent::InputHandled {
4700 utf16_range_to_replace: range_to_replace,
4701 text: text.into(),
4702 });
4703
4704 self.transact(cx, |this, cx| {
4705 if let Some(mut snippet) = snippet {
4706 snippet.text = text.to_string();
4707 for tabstop in snippet
4708 .tabstops
4709 .iter_mut()
4710 .flat_map(|tabstop| tabstop.ranges.iter_mut())
4711 {
4712 tabstop.start -= common_prefix_len as isize;
4713 tabstop.end -= common_prefix_len as isize;
4714 }
4715
4716 this.insert_snippet(&ranges, snippet, cx).log_err();
4717 } else {
4718 this.buffer.update(cx, |buffer, cx| {
4719 buffer.edit(
4720 ranges.iter().map(|range| (range.clone(), text)),
4721 this.autoindent_mode.clone(),
4722 cx,
4723 );
4724 });
4725 }
4726 for (buffer, edits) in linked_edits {
4727 buffer.update(cx, |buffer, cx| {
4728 let snapshot = buffer.snapshot();
4729 let edits = edits
4730 .into_iter()
4731 .map(|(range, text)| {
4732 use text::ToPoint as TP;
4733 let end_point = TP::to_point(&range.end, &snapshot);
4734 let start_point = TP::to_point(&range.start, &snapshot);
4735 (start_point..end_point, text)
4736 })
4737 .sorted_by_key(|(range, _)| range.start)
4738 .collect::<Vec<_>>();
4739 buffer.edit(edits, None, cx);
4740 })
4741 }
4742
4743 this.refresh_inline_completion(true, false, cx);
4744 });
4745
4746 let show_new_completions_on_confirm = completion
4747 .confirm
4748 .as_ref()
4749 .map_or(false, |confirm| confirm(intent, cx));
4750 if show_new_completions_on_confirm {
4751 self.show_completions(&ShowCompletions { trigger: None }, cx);
4752 }
4753
4754 let provider = self.completion_provider.as_ref()?;
4755 let apply_edits = provider.apply_additional_edits_for_completion(
4756 buffer_handle,
4757 completion.clone(),
4758 true,
4759 cx,
4760 );
4761
4762 let editor_settings = EditorSettings::get_global(cx);
4763 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4764 // After the code completion is finished, users often want to know what signatures are needed.
4765 // so we should automatically call signature_help
4766 self.show_signature_help(&ShowSignatureHelp, cx);
4767 }
4768
4769 Some(cx.foreground_executor().spawn(async move {
4770 apply_edits.await?;
4771 Ok(())
4772 }))
4773 }
4774
4775 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
4776 let mut context_menu = self.context_menu.write();
4777 if let Some(ContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4778 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4779 // Toggle if we're selecting the same one
4780 *context_menu = None;
4781 cx.notify();
4782 return;
4783 } else {
4784 // Otherwise, clear it and start a new one
4785 *context_menu = None;
4786 cx.notify();
4787 }
4788 }
4789 drop(context_menu);
4790 let snapshot = self.snapshot(cx);
4791 let deployed_from_indicator = action.deployed_from_indicator;
4792 let mut task = self.code_actions_task.take();
4793 let action = action.clone();
4794 cx.spawn(|editor, mut cx| async move {
4795 while let Some(prev_task) = task {
4796 prev_task.await.log_err();
4797 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4798 }
4799
4800 let spawned_test_task = editor.update(&mut cx, |editor, cx| {
4801 if editor.focus_handle.is_focused(cx) {
4802 let multibuffer_point = action
4803 .deployed_from_indicator
4804 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4805 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4806 let (buffer, buffer_row) = snapshot
4807 .buffer_snapshot
4808 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4809 .and_then(|(buffer_snapshot, range)| {
4810 editor
4811 .buffer
4812 .read(cx)
4813 .buffer(buffer_snapshot.remote_id())
4814 .map(|buffer| (buffer, range.start.row))
4815 })?;
4816 let (_, code_actions) = editor
4817 .available_code_actions
4818 .clone()
4819 .and_then(|(location, code_actions)| {
4820 let snapshot = location.buffer.read(cx).snapshot();
4821 let point_range = location.range.to_point(&snapshot);
4822 let point_range = point_range.start.row..=point_range.end.row;
4823 if point_range.contains(&buffer_row) {
4824 Some((location, code_actions))
4825 } else {
4826 None
4827 }
4828 })
4829 .unzip();
4830 let buffer_id = buffer.read(cx).remote_id();
4831 let tasks = editor
4832 .tasks
4833 .get(&(buffer_id, buffer_row))
4834 .map(|t| Arc::new(t.to_owned()));
4835 if tasks.is_none() && code_actions.is_none() {
4836 return None;
4837 }
4838
4839 editor.completion_tasks.clear();
4840 editor.discard_inline_completion(false, cx);
4841 let task_context =
4842 tasks
4843 .as_ref()
4844 .zip(editor.project.clone())
4845 .map(|(tasks, project)| {
4846 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4847 });
4848
4849 Some(cx.spawn(|editor, mut cx| async move {
4850 let task_context = match task_context {
4851 Some(task_context) => task_context.await,
4852 None => None,
4853 };
4854 let resolved_tasks =
4855 tasks.zip(task_context).map(|(tasks, task_context)| {
4856 Arc::new(ResolvedTasks {
4857 templates: tasks.resolve(&task_context).collect(),
4858 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4859 multibuffer_point.row,
4860 tasks.column,
4861 )),
4862 })
4863 });
4864 let spawn_straight_away = resolved_tasks
4865 .as_ref()
4866 .map_or(false, |tasks| tasks.templates.len() == 1)
4867 && code_actions
4868 .as_ref()
4869 .map_or(true, |actions| actions.is_empty());
4870 if let Ok(task) = editor.update(&mut cx, |editor, cx| {
4871 *editor.context_menu.write() =
4872 Some(ContextMenu::CodeActions(CodeActionsMenu {
4873 buffer,
4874 actions: CodeActionContents {
4875 tasks: resolved_tasks,
4876 actions: code_actions,
4877 },
4878 selected_item: Default::default(),
4879 scroll_handle: UniformListScrollHandle::default(),
4880 deployed_from_indicator,
4881 }));
4882 if spawn_straight_away {
4883 if let Some(task) = editor.confirm_code_action(
4884 &ConfirmCodeAction { item_ix: Some(0) },
4885 cx,
4886 ) {
4887 cx.notify();
4888 return task;
4889 }
4890 }
4891 cx.notify();
4892 Task::ready(Ok(()))
4893 }) {
4894 task.await
4895 } else {
4896 Ok(())
4897 }
4898 }))
4899 } else {
4900 Some(Task::ready(Ok(())))
4901 }
4902 })?;
4903 if let Some(task) = spawned_test_task {
4904 task.await?;
4905 }
4906
4907 Ok::<_, anyhow::Error>(())
4908 })
4909 .detach_and_log_err(cx);
4910 }
4911
4912 pub fn confirm_code_action(
4913 &mut self,
4914 action: &ConfirmCodeAction,
4915 cx: &mut ViewContext<Self>,
4916 ) -> Option<Task<Result<()>>> {
4917 let actions_menu = if let ContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
4918 menu
4919 } else {
4920 return None;
4921 };
4922 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4923 let action = actions_menu.actions.get(action_ix)?;
4924 let title = action.label();
4925 let buffer = actions_menu.buffer;
4926 let workspace = self.workspace()?;
4927
4928 match action {
4929 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4930 workspace.update(cx, |workspace, cx| {
4931 workspace::tasks::schedule_resolved_task(
4932 workspace,
4933 task_source_kind,
4934 resolved_task,
4935 false,
4936 cx,
4937 );
4938
4939 Some(Task::ready(Ok(())))
4940 })
4941 }
4942 CodeActionsItem::CodeAction {
4943 excerpt_id,
4944 action,
4945 provider,
4946 } => {
4947 let apply_code_action =
4948 provider.apply_code_action(buffer, action, excerpt_id, true, cx);
4949 let workspace = workspace.downgrade();
4950 Some(cx.spawn(|editor, cx| async move {
4951 let project_transaction = apply_code_action.await?;
4952 Self::open_project_transaction(
4953 &editor,
4954 workspace,
4955 project_transaction,
4956 title,
4957 cx,
4958 )
4959 .await
4960 }))
4961 }
4962 }
4963 }
4964
4965 pub async fn open_project_transaction(
4966 this: &WeakView<Editor>,
4967 workspace: WeakView<Workspace>,
4968 transaction: ProjectTransaction,
4969 title: String,
4970 mut cx: AsyncWindowContext,
4971 ) -> Result<()> {
4972 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4973 cx.update(|cx| {
4974 entries.sort_unstable_by_key(|(buffer, _)| {
4975 buffer.read(cx).file().map(|f| f.path().clone())
4976 });
4977 })?;
4978
4979 // If the project transaction's edits are all contained within this editor, then
4980 // avoid opening a new editor to display them.
4981
4982 if let Some((buffer, transaction)) = entries.first() {
4983 if entries.len() == 1 {
4984 let excerpt = this.update(&mut cx, |editor, cx| {
4985 editor
4986 .buffer()
4987 .read(cx)
4988 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4989 })?;
4990 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4991 if excerpted_buffer == *buffer {
4992 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4993 let excerpt_range = excerpt_range.to_offset(buffer);
4994 buffer
4995 .edited_ranges_for_transaction::<usize>(transaction)
4996 .all(|range| {
4997 excerpt_range.start <= range.start
4998 && excerpt_range.end >= range.end
4999 })
5000 })?;
5001
5002 if all_edits_within_excerpt {
5003 return Ok(());
5004 }
5005 }
5006 }
5007 }
5008 } else {
5009 return Ok(());
5010 }
5011
5012 let mut ranges_to_highlight = Vec::new();
5013 let excerpt_buffer = cx.new_model(|cx| {
5014 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
5015 for (buffer_handle, transaction) in &entries {
5016 let buffer = buffer_handle.read(cx);
5017 ranges_to_highlight.extend(
5018 multibuffer.push_excerpts_with_context_lines(
5019 buffer_handle.clone(),
5020 buffer
5021 .edited_ranges_for_transaction::<usize>(transaction)
5022 .collect(),
5023 DEFAULT_MULTIBUFFER_CONTEXT,
5024 cx,
5025 ),
5026 );
5027 }
5028 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
5029 multibuffer
5030 })?;
5031
5032 workspace.update(&mut cx, |workspace, cx| {
5033 let project = workspace.project().clone();
5034 let editor =
5035 cx.new_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, cx));
5036 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
5037 editor.update(cx, |editor, cx| {
5038 editor.highlight_background::<Self>(
5039 &ranges_to_highlight,
5040 |theme| theme.editor_highlighted_line_background,
5041 cx,
5042 );
5043 });
5044 })?;
5045
5046 Ok(())
5047 }
5048
5049 pub fn clear_code_action_providers(&mut self) {
5050 self.code_action_providers.clear();
5051 self.available_code_actions.take();
5052 }
5053
5054 pub fn push_code_action_provider(
5055 &mut self,
5056 provider: Arc<dyn CodeActionProvider>,
5057 cx: &mut ViewContext<Self>,
5058 ) {
5059 self.code_action_providers.push(provider);
5060 self.refresh_code_actions(cx);
5061 }
5062
5063 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
5064 let buffer = self.buffer.read(cx);
5065 let newest_selection = self.selections.newest_anchor().clone();
5066 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
5067 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
5068 if start_buffer != end_buffer {
5069 return None;
5070 }
5071
5072 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
5073 cx.background_executor()
5074 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
5075 .await;
5076
5077 let (providers, tasks) = this.update(&mut cx, |this, cx| {
5078 let providers = this.code_action_providers.clone();
5079 let tasks = this
5080 .code_action_providers
5081 .iter()
5082 .map(|provider| provider.code_actions(&start_buffer, start..end, cx))
5083 .collect::<Vec<_>>();
5084 (providers, tasks)
5085 })?;
5086
5087 let mut actions = Vec::new();
5088 for (provider, provider_actions) in
5089 providers.into_iter().zip(future::join_all(tasks).await)
5090 {
5091 if let Some(provider_actions) = provider_actions.log_err() {
5092 actions.extend(provider_actions.into_iter().map(|action| {
5093 AvailableCodeAction {
5094 excerpt_id: newest_selection.start.excerpt_id,
5095 action,
5096 provider: provider.clone(),
5097 }
5098 }));
5099 }
5100 }
5101
5102 this.update(&mut cx, |this, cx| {
5103 this.available_code_actions = if actions.is_empty() {
5104 None
5105 } else {
5106 Some((
5107 Location {
5108 buffer: start_buffer,
5109 range: start..end,
5110 },
5111 actions.into(),
5112 ))
5113 };
5114 cx.notify();
5115 })
5116 }));
5117 None
5118 }
5119
5120 fn start_inline_blame_timer(&mut self, cx: &mut ViewContext<Self>) {
5121 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
5122 self.show_git_blame_inline = false;
5123
5124 self.show_git_blame_inline_delay_task = Some(cx.spawn(|this, mut cx| async move {
5125 cx.background_executor().timer(delay).await;
5126
5127 this.update(&mut cx, |this, cx| {
5128 this.show_git_blame_inline = true;
5129 cx.notify();
5130 })
5131 .log_err();
5132 }));
5133 }
5134 }
5135
5136 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
5137 if self.pending_rename.is_some() {
5138 return None;
5139 }
5140
5141 let provider = self.semantics_provider.clone()?;
5142 let buffer = self.buffer.read(cx);
5143 let newest_selection = self.selections.newest_anchor().clone();
5144 let cursor_position = newest_selection.head();
5145 let (cursor_buffer, cursor_buffer_position) =
5146 buffer.text_anchor_for_position(cursor_position, cx)?;
5147 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
5148 if cursor_buffer != tail_buffer {
5149 return None;
5150 }
5151
5152 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
5153 cx.background_executor()
5154 .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT)
5155 .await;
5156
5157 let highlights = if let Some(highlights) = cx
5158 .update(|cx| {
5159 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
5160 })
5161 .ok()
5162 .flatten()
5163 {
5164 highlights.await.log_err()
5165 } else {
5166 None
5167 };
5168
5169 if let Some(highlights) = highlights {
5170 this.update(&mut cx, |this, cx| {
5171 if this.pending_rename.is_some() {
5172 return;
5173 }
5174
5175 let buffer_id = cursor_position.buffer_id;
5176 let buffer = this.buffer.read(cx);
5177 if !buffer
5178 .text_anchor_for_position(cursor_position, cx)
5179 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
5180 {
5181 return;
5182 }
5183
5184 let cursor_buffer_snapshot = cursor_buffer.read(cx);
5185 let mut write_ranges = Vec::new();
5186 let mut read_ranges = Vec::new();
5187 for highlight in highlights {
5188 for (excerpt_id, excerpt_range) in
5189 buffer.excerpts_for_buffer(&cursor_buffer, cx)
5190 {
5191 let start = highlight
5192 .range
5193 .start
5194 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
5195 let end = highlight
5196 .range
5197 .end
5198 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
5199 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
5200 continue;
5201 }
5202
5203 let range = Anchor {
5204 buffer_id,
5205 excerpt_id,
5206 text_anchor: start,
5207 }..Anchor {
5208 buffer_id,
5209 excerpt_id,
5210 text_anchor: end,
5211 };
5212 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
5213 write_ranges.push(range);
5214 } else {
5215 read_ranges.push(range);
5216 }
5217 }
5218 }
5219
5220 this.highlight_background::<DocumentHighlightRead>(
5221 &read_ranges,
5222 |theme| theme.editor_document_highlight_read_background,
5223 cx,
5224 );
5225 this.highlight_background::<DocumentHighlightWrite>(
5226 &write_ranges,
5227 |theme| theme.editor_document_highlight_write_background,
5228 cx,
5229 );
5230 cx.notify();
5231 })
5232 .log_err();
5233 }
5234 }));
5235 None
5236 }
5237
5238 pub fn refresh_inline_completion(
5239 &mut self,
5240 debounce: bool,
5241 user_requested: bool,
5242 cx: &mut ViewContext<Self>,
5243 ) -> Option<()> {
5244 let provider = self.inline_completion_provider()?;
5245 let cursor = self.selections.newest_anchor().head();
5246 let (buffer, cursor_buffer_position) =
5247 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5248
5249 if !user_requested
5250 && (!self.enable_inline_completions
5251 || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx))
5252 {
5253 self.discard_inline_completion(false, cx);
5254 return None;
5255 }
5256
5257 self.update_visible_inline_completion(cx);
5258 provider.refresh(buffer, cursor_buffer_position, debounce, cx);
5259 Some(())
5260 }
5261
5262 fn cycle_inline_completion(
5263 &mut self,
5264 direction: Direction,
5265 cx: &mut ViewContext<Self>,
5266 ) -> Option<()> {
5267 let provider = self.inline_completion_provider()?;
5268 let cursor = self.selections.newest_anchor().head();
5269 let (buffer, cursor_buffer_position) =
5270 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5271 if !self.enable_inline_completions
5272 || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
5273 {
5274 return None;
5275 }
5276
5277 provider.cycle(buffer, cursor_buffer_position, direction, cx);
5278 self.update_visible_inline_completion(cx);
5279
5280 Some(())
5281 }
5282
5283 pub fn show_inline_completion(&mut self, _: &ShowInlineCompletion, cx: &mut ViewContext<Self>) {
5284 if !self.has_active_inline_completion(cx) {
5285 self.refresh_inline_completion(false, true, cx);
5286 return;
5287 }
5288
5289 self.update_visible_inline_completion(cx);
5290 }
5291
5292 pub fn display_cursor_names(&mut self, _: &DisplayCursorNames, cx: &mut ViewContext<Self>) {
5293 self.show_cursor_names(cx);
5294 }
5295
5296 fn show_cursor_names(&mut self, cx: &mut ViewContext<Self>) {
5297 self.show_cursor_names = true;
5298 cx.notify();
5299 cx.spawn(|this, mut cx| async move {
5300 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5301 this.update(&mut cx, |this, cx| {
5302 this.show_cursor_names = false;
5303 cx.notify()
5304 })
5305 .ok()
5306 })
5307 .detach();
5308 }
5309
5310 pub fn next_inline_completion(&mut self, _: &NextInlineCompletion, cx: &mut ViewContext<Self>) {
5311 if self.has_active_inline_completion(cx) {
5312 self.cycle_inline_completion(Direction::Next, cx);
5313 } else {
5314 let is_copilot_disabled = self.refresh_inline_completion(false, true, cx).is_none();
5315 if is_copilot_disabled {
5316 cx.propagate();
5317 }
5318 }
5319 }
5320
5321 pub fn previous_inline_completion(
5322 &mut self,
5323 _: &PreviousInlineCompletion,
5324 cx: &mut ViewContext<Self>,
5325 ) {
5326 if self.has_active_inline_completion(cx) {
5327 self.cycle_inline_completion(Direction::Prev, cx);
5328 } else {
5329 let is_copilot_disabled = self.refresh_inline_completion(false, true, cx).is_none();
5330 if is_copilot_disabled {
5331 cx.propagate();
5332 }
5333 }
5334 }
5335
5336 pub fn accept_inline_completion(
5337 &mut self,
5338 _: &AcceptInlineCompletion,
5339 cx: &mut ViewContext<Self>,
5340 ) {
5341 let Some(completion) = self.take_active_inline_completion(cx) else {
5342 return;
5343 };
5344 if let Some(provider) = self.inline_completion_provider() {
5345 provider.accept(cx);
5346 }
5347
5348 cx.emit(EditorEvent::InputHandled {
5349 utf16_range_to_replace: None,
5350 text: completion.text.to_string().into(),
5351 });
5352
5353 if let Some(range) = completion.delete_range {
5354 self.change_selections(None, cx, |s| s.select_ranges([range]))
5355 }
5356 self.insert_with_autoindent_mode(&completion.text.to_string(), None, cx);
5357 self.refresh_inline_completion(true, true, cx);
5358 cx.notify();
5359 }
5360
5361 pub fn accept_partial_inline_completion(
5362 &mut self,
5363 _: &AcceptPartialInlineCompletion,
5364 cx: &mut ViewContext<Self>,
5365 ) {
5366 if self.selections.count() == 1 && self.has_active_inline_completion(cx) {
5367 if let Some(completion) = self.take_active_inline_completion(cx) {
5368 let mut partial_completion = completion
5369 .text
5370 .chars()
5371 .by_ref()
5372 .take_while(|c| c.is_alphabetic())
5373 .collect::<String>();
5374 if partial_completion.is_empty() {
5375 partial_completion = completion
5376 .text
5377 .chars()
5378 .by_ref()
5379 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5380 .collect::<String>();
5381 }
5382
5383 cx.emit(EditorEvent::InputHandled {
5384 utf16_range_to_replace: None,
5385 text: partial_completion.clone().into(),
5386 });
5387
5388 if let Some(range) = completion.delete_range {
5389 self.change_selections(None, cx, |s| s.select_ranges([range]))
5390 }
5391 self.insert_with_autoindent_mode(&partial_completion, None, cx);
5392
5393 self.refresh_inline_completion(true, true, cx);
5394 cx.notify();
5395 }
5396 }
5397 }
5398
5399 fn discard_inline_completion(
5400 &mut self,
5401 should_report_inline_completion_event: bool,
5402 cx: &mut ViewContext<Self>,
5403 ) -> bool {
5404 if let Some(provider) = self.inline_completion_provider() {
5405 provider.discard(should_report_inline_completion_event, cx);
5406 }
5407
5408 self.take_active_inline_completion(cx).is_some()
5409 }
5410
5411 pub fn has_active_inline_completion(&self, cx: &AppContext) -> bool {
5412 if let Some(completion) = self.active_inline_completion.as_ref() {
5413 let buffer = self.buffer.read(cx).read(cx);
5414 completion.position.is_valid(&buffer)
5415 } else {
5416 false
5417 }
5418 }
5419
5420 fn take_active_inline_completion(
5421 &mut self,
5422 cx: &mut ViewContext<Self>,
5423 ) -> Option<CompletionState> {
5424 let completion = self.active_inline_completion.take()?;
5425 let render_inlay_ids = completion.render_inlay_ids.clone();
5426 self.display_map.update(cx, |map, cx| {
5427 map.splice_inlays(render_inlay_ids, Default::default(), cx);
5428 });
5429 let buffer = self.buffer.read(cx).read(cx);
5430
5431 if completion.position.is_valid(&buffer) {
5432 Some(completion)
5433 } else {
5434 None
5435 }
5436 }
5437
5438 fn update_visible_inline_completion(&mut self, cx: &mut ViewContext<Self>) {
5439 let selection = self.selections.newest_anchor();
5440 let cursor = selection.head();
5441
5442 let excerpt_id = cursor.excerpt_id;
5443
5444 if self.context_menu.read().is_none()
5445 && self.completion_tasks.is_empty()
5446 && selection.start == selection.end
5447 {
5448 if let Some(provider) = self.inline_completion_provider() {
5449 if let Some((buffer, cursor_buffer_position)) =
5450 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5451 {
5452 if let Some(proposal) =
5453 provider.active_completion_text(&buffer, cursor_buffer_position, cx)
5454 {
5455 let mut to_remove = Vec::new();
5456 if let Some(completion) = self.active_inline_completion.take() {
5457 to_remove.extend(completion.render_inlay_ids.iter());
5458 }
5459
5460 let to_add = proposal
5461 .inlays
5462 .iter()
5463 .filter_map(|inlay| {
5464 let snapshot = self.buffer.read(cx).snapshot(cx);
5465 let id = post_inc(&mut self.next_inlay_id);
5466 match inlay {
5467 InlayProposal::Hint(position, hint) => {
5468 let position =
5469 snapshot.anchor_in_excerpt(excerpt_id, *position)?;
5470 Some(Inlay::hint(id, position, hint))
5471 }
5472 InlayProposal::Suggestion(position, text) => {
5473 let position =
5474 snapshot.anchor_in_excerpt(excerpt_id, *position)?;
5475 Some(Inlay::suggestion(id, position, text.clone()))
5476 }
5477 }
5478 })
5479 .collect_vec();
5480
5481 self.active_inline_completion = Some(CompletionState {
5482 position: cursor,
5483 text: proposal.text,
5484 delete_range: proposal.delete_range.and_then(|range| {
5485 let snapshot = self.buffer.read(cx).snapshot(cx);
5486 let start = snapshot.anchor_in_excerpt(excerpt_id, range.start);
5487 let end = snapshot.anchor_in_excerpt(excerpt_id, range.end);
5488 Some(start?..end?)
5489 }),
5490 render_inlay_ids: to_add.iter().map(|i| i.id).collect(),
5491 });
5492
5493 self.display_map
5494 .update(cx, move |map, cx| map.splice_inlays(to_remove, to_add, cx));
5495
5496 cx.notify();
5497 return;
5498 }
5499 }
5500 }
5501 }
5502
5503 self.discard_inline_completion(false, cx);
5504 }
5505
5506 fn inline_completion_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5507 Some(self.inline_completion_provider.as_ref()?.provider.clone())
5508 }
5509
5510 fn render_code_actions_indicator(
5511 &self,
5512 _style: &EditorStyle,
5513 row: DisplayRow,
5514 is_active: bool,
5515 cx: &mut ViewContext<Self>,
5516 ) -> Option<IconButton> {
5517 if self.available_code_actions.is_some() {
5518 Some(
5519 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5520 .shape(ui::IconButtonShape::Square)
5521 .icon_size(IconSize::XSmall)
5522 .icon_color(Color::Muted)
5523 .selected(is_active)
5524 .tooltip({
5525 let focus_handle = self.focus_handle.clone();
5526 move |cx| {
5527 Tooltip::for_action_in(
5528 "Toggle Code Actions",
5529 &ToggleCodeActions {
5530 deployed_from_indicator: None,
5531 },
5532 &focus_handle,
5533 cx,
5534 )
5535 }
5536 })
5537 .on_click(cx.listener(move |editor, _e, cx| {
5538 editor.focus(cx);
5539 editor.toggle_code_actions(
5540 &ToggleCodeActions {
5541 deployed_from_indicator: Some(row),
5542 },
5543 cx,
5544 );
5545 })),
5546 )
5547 } else {
5548 None
5549 }
5550 }
5551
5552 fn clear_tasks(&mut self) {
5553 self.tasks.clear()
5554 }
5555
5556 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5557 if self.tasks.insert(key, value).is_some() {
5558 // This case should hopefully be rare, but just in case...
5559 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5560 }
5561 }
5562
5563 fn build_tasks_context(
5564 project: &Model<Project>,
5565 buffer: &Model<Buffer>,
5566 buffer_row: u32,
5567 tasks: &Arc<RunnableTasks>,
5568 cx: &mut ViewContext<Self>,
5569 ) -> Task<Option<task::TaskContext>> {
5570 let position = Point::new(buffer_row, tasks.column);
5571 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
5572 let location = Location {
5573 buffer: buffer.clone(),
5574 range: range_start..range_start,
5575 };
5576 // Fill in the environmental variables from the tree-sitter captures
5577 let mut captured_task_variables = TaskVariables::default();
5578 for (capture_name, value) in tasks.extra_variables.clone() {
5579 captured_task_variables.insert(
5580 task::VariableName::Custom(capture_name.into()),
5581 value.clone(),
5582 );
5583 }
5584 project.update(cx, |project, cx| {
5585 project.task_store().update(cx, |task_store, cx| {
5586 task_store.task_context_for_location(captured_task_variables, location, cx)
5587 })
5588 })
5589 }
5590
5591 pub fn spawn_nearest_task(&mut self, action: &SpawnNearestTask, cx: &mut ViewContext<Self>) {
5592 let Some((workspace, _)) = self.workspace.clone() else {
5593 return;
5594 };
5595 let Some(project) = self.project.clone() else {
5596 return;
5597 };
5598
5599 // Try to find a closest, enclosing node using tree-sitter that has a
5600 // task
5601 let Some((buffer, buffer_row, tasks)) = self
5602 .find_enclosing_node_task(cx)
5603 // Or find the task that's closest in row-distance.
5604 .or_else(|| self.find_closest_task(cx))
5605 else {
5606 return;
5607 };
5608
5609 let reveal_strategy = action.reveal;
5610 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
5611 cx.spawn(|_, mut cx| async move {
5612 let context = task_context.await?;
5613 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
5614
5615 let resolved = resolved_task.resolved.as_mut()?;
5616 resolved.reveal = reveal_strategy;
5617
5618 workspace
5619 .update(&mut cx, |workspace, cx| {
5620 workspace::tasks::schedule_resolved_task(
5621 workspace,
5622 task_source_kind,
5623 resolved_task,
5624 false,
5625 cx,
5626 );
5627 })
5628 .ok()
5629 })
5630 .detach();
5631 }
5632
5633 fn find_closest_task(
5634 &mut self,
5635 cx: &mut ViewContext<Self>,
5636 ) -> Option<(Model<Buffer>, u32, Arc<RunnableTasks>)> {
5637 let cursor_row = self.selections.newest_adjusted(cx).head().row;
5638
5639 let ((buffer_id, row), tasks) = self
5640 .tasks
5641 .iter()
5642 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
5643
5644 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
5645 let tasks = Arc::new(tasks.to_owned());
5646 Some((buffer, *row, tasks))
5647 }
5648
5649 fn find_enclosing_node_task(
5650 &mut self,
5651 cx: &mut ViewContext<Self>,
5652 ) -> Option<(Model<Buffer>, u32, Arc<RunnableTasks>)> {
5653 let snapshot = self.buffer.read(cx).snapshot(cx);
5654 let offset = self.selections.newest::<usize>(cx).head();
5655 let excerpt = snapshot.excerpt_containing(offset..offset)?;
5656 let buffer_id = excerpt.buffer().remote_id();
5657
5658 let layer = excerpt.buffer().syntax_layer_at(offset)?;
5659 let mut cursor = layer.node().walk();
5660
5661 while cursor.goto_first_child_for_byte(offset).is_some() {
5662 if cursor.node().end_byte() == offset {
5663 cursor.goto_next_sibling();
5664 }
5665 }
5666
5667 // Ascend to the smallest ancestor that contains the range and has a task.
5668 loop {
5669 let node = cursor.node();
5670 let node_range = node.byte_range();
5671 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
5672
5673 // Check if this node contains our offset
5674 if node_range.start <= offset && node_range.end >= offset {
5675 // If it contains offset, check for task
5676 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
5677 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
5678 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
5679 }
5680 }
5681
5682 if !cursor.goto_parent() {
5683 break;
5684 }
5685 }
5686 None
5687 }
5688
5689 fn render_run_indicator(
5690 &self,
5691 _style: &EditorStyle,
5692 is_active: bool,
5693 row: DisplayRow,
5694 cx: &mut ViewContext<Self>,
5695 ) -> IconButton {
5696 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5697 .shape(ui::IconButtonShape::Square)
5698 .icon_size(IconSize::XSmall)
5699 .icon_color(Color::Muted)
5700 .selected(is_active)
5701 .on_click(cx.listener(move |editor, _e, cx| {
5702 editor.focus(cx);
5703 editor.toggle_code_actions(
5704 &ToggleCodeActions {
5705 deployed_from_indicator: Some(row),
5706 },
5707 cx,
5708 );
5709 }))
5710 }
5711
5712 pub fn context_menu_visible(&self) -> bool {
5713 self.context_menu
5714 .read()
5715 .as_ref()
5716 .map_or(false, |menu| menu.visible())
5717 }
5718
5719 fn render_context_menu(
5720 &self,
5721 cursor_position: DisplayPoint,
5722 style: &EditorStyle,
5723 max_height: Pixels,
5724 cx: &mut ViewContext<Editor>,
5725 ) -> Option<(ContextMenuOrigin, AnyElement)> {
5726 self.context_menu.read().as_ref().map(|menu| {
5727 menu.render(
5728 cursor_position,
5729 style,
5730 max_height,
5731 self.workspace.as_ref().map(|(w, _)| w.clone()),
5732 cx,
5733 )
5734 })
5735 }
5736
5737 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
5738 cx.notify();
5739 self.completion_tasks.clear();
5740 let context_menu = self.context_menu.write().take();
5741 if context_menu.is_some() {
5742 self.update_visible_inline_completion(cx);
5743 }
5744 context_menu
5745 }
5746
5747 fn show_snippet_choices(
5748 &mut self,
5749 choices: &Vec<String>,
5750 selection: Range<Anchor>,
5751 cx: &mut ViewContext<Self>,
5752 ) {
5753 if selection.start.buffer_id.is_none() {
5754 return;
5755 }
5756 let buffer_id = selection.start.buffer_id.unwrap();
5757 let buffer = self.buffer().read(cx).buffer(buffer_id);
5758 let id = post_inc(&mut self.next_completion_id);
5759
5760 if let Some(buffer) = buffer {
5761 *self.context_menu.write() = Some(ContextMenu::Completions(
5762 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer)
5763 .suppress_documentation_resolution(),
5764 ));
5765 }
5766 }
5767
5768 pub fn insert_snippet(
5769 &mut self,
5770 insertion_ranges: &[Range<usize>],
5771 snippet: Snippet,
5772 cx: &mut ViewContext<Self>,
5773 ) -> Result<()> {
5774 struct Tabstop<T> {
5775 is_end_tabstop: bool,
5776 ranges: Vec<Range<T>>,
5777 choices: Option<Vec<String>>,
5778 }
5779
5780 let tabstops = self.buffer.update(cx, |buffer, cx| {
5781 let snippet_text: Arc<str> = snippet.text.clone().into();
5782 buffer.edit(
5783 insertion_ranges
5784 .iter()
5785 .cloned()
5786 .map(|range| (range, snippet_text.clone())),
5787 Some(AutoindentMode::EachLine),
5788 cx,
5789 );
5790
5791 let snapshot = &*buffer.read(cx);
5792 let snippet = &snippet;
5793 snippet
5794 .tabstops
5795 .iter()
5796 .map(|tabstop| {
5797 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
5798 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
5799 });
5800 let mut tabstop_ranges = tabstop
5801 .ranges
5802 .iter()
5803 .flat_map(|tabstop_range| {
5804 let mut delta = 0_isize;
5805 insertion_ranges.iter().map(move |insertion_range| {
5806 let insertion_start = insertion_range.start as isize + delta;
5807 delta +=
5808 snippet.text.len() as isize - insertion_range.len() as isize;
5809
5810 let start = ((insertion_start + tabstop_range.start) as usize)
5811 .min(snapshot.len());
5812 let end = ((insertion_start + tabstop_range.end) as usize)
5813 .min(snapshot.len());
5814 snapshot.anchor_before(start)..snapshot.anchor_after(end)
5815 })
5816 })
5817 .collect::<Vec<_>>();
5818 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
5819
5820 Tabstop {
5821 is_end_tabstop,
5822 ranges: tabstop_ranges,
5823 choices: tabstop.choices.clone(),
5824 }
5825 })
5826 .collect::<Vec<_>>()
5827 });
5828 if let Some(tabstop) = tabstops.first() {
5829 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5830 s.select_ranges(tabstop.ranges.iter().cloned());
5831 });
5832
5833 if let Some(choices) = &tabstop.choices {
5834 if let Some(selection) = tabstop.ranges.first() {
5835 self.show_snippet_choices(choices, selection.clone(), cx)
5836 }
5837 }
5838
5839 // If we're already at the last tabstop and it's at the end of the snippet,
5840 // we're done, we don't need to keep the state around.
5841 if !tabstop.is_end_tabstop {
5842 let choices = tabstops
5843 .iter()
5844 .map(|tabstop| tabstop.choices.clone())
5845 .collect();
5846
5847 let ranges = tabstops
5848 .into_iter()
5849 .map(|tabstop| tabstop.ranges)
5850 .collect::<Vec<_>>();
5851
5852 self.snippet_stack.push(SnippetState {
5853 active_index: 0,
5854 ranges,
5855 choices,
5856 });
5857 }
5858
5859 // Check whether the just-entered snippet ends with an auto-closable bracket.
5860 if self.autoclose_regions.is_empty() {
5861 let snapshot = self.buffer.read(cx).snapshot(cx);
5862 for selection in &mut self.selections.all::<Point>(cx) {
5863 let selection_head = selection.head();
5864 let Some(scope) = snapshot.language_scope_at(selection_head) else {
5865 continue;
5866 };
5867
5868 let mut bracket_pair = None;
5869 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
5870 let prev_chars = snapshot
5871 .reversed_chars_at(selection_head)
5872 .collect::<String>();
5873 for (pair, enabled) in scope.brackets() {
5874 if enabled
5875 && pair.close
5876 && prev_chars.starts_with(pair.start.as_str())
5877 && next_chars.starts_with(pair.end.as_str())
5878 {
5879 bracket_pair = Some(pair.clone());
5880 break;
5881 }
5882 }
5883 if let Some(pair) = bracket_pair {
5884 let start = snapshot.anchor_after(selection_head);
5885 let end = snapshot.anchor_after(selection_head);
5886 self.autoclose_regions.push(AutocloseRegion {
5887 selection_id: selection.id,
5888 range: start..end,
5889 pair,
5890 });
5891 }
5892 }
5893 }
5894 }
5895 Ok(())
5896 }
5897
5898 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5899 self.move_to_snippet_tabstop(Bias::Right, cx)
5900 }
5901
5902 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5903 self.move_to_snippet_tabstop(Bias::Left, cx)
5904 }
5905
5906 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
5907 if let Some(mut snippet) = self.snippet_stack.pop() {
5908 match bias {
5909 Bias::Left => {
5910 if snippet.active_index > 0 {
5911 snippet.active_index -= 1;
5912 } else {
5913 self.snippet_stack.push(snippet);
5914 return false;
5915 }
5916 }
5917 Bias::Right => {
5918 if snippet.active_index + 1 < snippet.ranges.len() {
5919 snippet.active_index += 1;
5920 } else {
5921 self.snippet_stack.push(snippet);
5922 return false;
5923 }
5924 }
5925 }
5926 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
5927 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5928 s.select_anchor_ranges(current_ranges.iter().cloned())
5929 });
5930
5931 if let Some(choices) = &snippet.choices[snippet.active_index] {
5932 if let Some(selection) = current_ranges.first() {
5933 self.show_snippet_choices(&choices, selection.clone(), cx);
5934 }
5935 }
5936
5937 // If snippet state is not at the last tabstop, push it back on the stack
5938 if snippet.active_index + 1 < snippet.ranges.len() {
5939 self.snippet_stack.push(snippet);
5940 }
5941 return true;
5942 }
5943 }
5944
5945 false
5946 }
5947
5948 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
5949 self.transact(cx, |this, cx| {
5950 this.select_all(&SelectAll, cx);
5951 this.insert("", cx);
5952 });
5953 }
5954
5955 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
5956 self.transact(cx, |this, cx| {
5957 this.select_autoclose_pair(cx);
5958 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
5959 if !this.linked_edit_ranges.is_empty() {
5960 let selections = this.selections.all::<MultiBufferPoint>(cx);
5961 let snapshot = this.buffer.read(cx).snapshot(cx);
5962
5963 for selection in selections.iter() {
5964 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
5965 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
5966 if selection_start.buffer_id != selection_end.buffer_id {
5967 continue;
5968 }
5969 if let Some(ranges) =
5970 this.linked_editing_ranges_for(selection_start..selection_end, cx)
5971 {
5972 for (buffer, entries) in ranges {
5973 linked_ranges.entry(buffer).or_default().extend(entries);
5974 }
5975 }
5976 }
5977 }
5978
5979 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
5980 if !this.selections.line_mode {
5981 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
5982 for selection in &mut selections {
5983 if selection.is_empty() {
5984 let old_head = selection.head();
5985 let mut new_head =
5986 movement::left(&display_map, old_head.to_display_point(&display_map))
5987 .to_point(&display_map);
5988 if let Some((buffer, line_buffer_range)) = display_map
5989 .buffer_snapshot
5990 .buffer_line_for_row(MultiBufferRow(old_head.row))
5991 {
5992 let indent_size =
5993 buffer.indent_size_for_line(line_buffer_range.start.row);
5994 let indent_len = match indent_size.kind {
5995 IndentKind::Space => {
5996 buffer.settings_at(line_buffer_range.start, cx).tab_size
5997 }
5998 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
5999 };
6000 if old_head.column <= indent_size.len && old_head.column > 0 {
6001 let indent_len = indent_len.get();
6002 new_head = cmp::min(
6003 new_head,
6004 MultiBufferPoint::new(
6005 old_head.row,
6006 ((old_head.column - 1) / indent_len) * indent_len,
6007 ),
6008 );
6009 }
6010 }
6011
6012 selection.set_head(new_head, SelectionGoal::None);
6013 }
6014 }
6015 }
6016
6017 this.signature_help_state.set_backspace_pressed(true);
6018 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6019 this.insert("", cx);
6020 let empty_str: Arc<str> = Arc::from("");
6021 for (buffer, edits) in linked_ranges {
6022 let snapshot = buffer.read(cx).snapshot();
6023 use text::ToPoint as TP;
6024
6025 let edits = edits
6026 .into_iter()
6027 .map(|range| {
6028 let end_point = TP::to_point(&range.end, &snapshot);
6029 let mut start_point = TP::to_point(&range.start, &snapshot);
6030
6031 if end_point == start_point {
6032 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
6033 .saturating_sub(1);
6034 start_point = TP::to_point(&offset, &snapshot);
6035 };
6036
6037 (start_point..end_point, empty_str.clone())
6038 })
6039 .sorted_by_key(|(range, _)| range.start)
6040 .collect::<Vec<_>>();
6041 buffer.update(cx, |this, cx| {
6042 this.edit(edits, None, cx);
6043 })
6044 }
6045 this.refresh_inline_completion(true, false, cx);
6046 linked_editing_ranges::refresh_linked_ranges(this, cx);
6047 });
6048 }
6049
6050 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
6051 self.transact(cx, |this, cx| {
6052 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6053 let line_mode = s.line_mode;
6054 s.move_with(|map, selection| {
6055 if selection.is_empty() && !line_mode {
6056 let cursor = movement::right(map, selection.head());
6057 selection.end = cursor;
6058 selection.reversed = true;
6059 selection.goal = SelectionGoal::None;
6060 }
6061 })
6062 });
6063 this.insert("", cx);
6064 this.refresh_inline_completion(true, false, cx);
6065 });
6066 }
6067
6068 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
6069 if self.move_to_prev_snippet_tabstop(cx) {
6070 return;
6071 }
6072
6073 self.outdent(&Outdent, cx);
6074 }
6075
6076 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
6077 if self.move_to_next_snippet_tabstop(cx) || self.read_only(cx) {
6078 return;
6079 }
6080
6081 let mut selections = self.selections.all_adjusted(cx);
6082 let buffer = self.buffer.read(cx);
6083 let snapshot = buffer.snapshot(cx);
6084 let rows_iter = selections.iter().map(|s| s.head().row);
6085 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
6086
6087 let mut edits = Vec::new();
6088 let mut prev_edited_row = 0;
6089 let mut row_delta = 0;
6090 for selection in &mut selections {
6091 if selection.start.row != prev_edited_row {
6092 row_delta = 0;
6093 }
6094 prev_edited_row = selection.end.row;
6095
6096 // If the selection is non-empty, then increase the indentation of the selected lines.
6097 if !selection.is_empty() {
6098 row_delta =
6099 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
6100 continue;
6101 }
6102
6103 // If the selection is empty and the cursor is in the leading whitespace before the
6104 // suggested indentation, then auto-indent the line.
6105 let cursor = selection.head();
6106 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
6107 if let Some(suggested_indent) =
6108 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
6109 {
6110 if cursor.column < suggested_indent.len
6111 && cursor.column <= current_indent.len
6112 && current_indent.len <= suggested_indent.len
6113 {
6114 selection.start = Point::new(cursor.row, suggested_indent.len);
6115 selection.end = selection.start;
6116 if row_delta == 0 {
6117 edits.extend(Buffer::edit_for_indent_size_adjustment(
6118 cursor.row,
6119 current_indent,
6120 suggested_indent,
6121 ));
6122 row_delta = suggested_indent.len - current_indent.len;
6123 }
6124 continue;
6125 }
6126 }
6127
6128 // Otherwise, insert a hard or soft tab.
6129 let settings = buffer.settings_at(cursor, cx);
6130 let tab_size = if settings.hard_tabs {
6131 IndentSize::tab()
6132 } else {
6133 let tab_size = settings.tab_size.get();
6134 let char_column = snapshot
6135 .text_for_range(Point::new(cursor.row, 0)..cursor)
6136 .flat_map(str::chars)
6137 .count()
6138 + row_delta as usize;
6139 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
6140 IndentSize::spaces(chars_to_next_tab_stop)
6141 };
6142 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
6143 selection.end = selection.start;
6144 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
6145 row_delta += tab_size.len;
6146 }
6147
6148 self.transact(cx, |this, cx| {
6149 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
6150 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6151 this.refresh_inline_completion(true, false, cx);
6152 });
6153 }
6154
6155 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
6156 if self.read_only(cx) {
6157 return;
6158 }
6159 let mut selections = self.selections.all::<Point>(cx);
6160 let mut prev_edited_row = 0;
6161 let mut row_delta = 0;
6162 let mut edits = Vec::new();
6163 let buffer = self.buffer.read(cx);
6164 let snapshot = buffer.snapshot(cx);
6165 for selection in &mut selections {
6166 if selection.start.row != prev_edited_row {
6167 row_delta = 0;
6168 }
6169 prev_edited_row = selection.end.row;
6170
6171 row_delta =
6172 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
6173 }
6174
6175 self.transact(cx, |this, cx| {
6176 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
6177 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6178 });
6179 }
6180
6181 fn indent_selection(
6182 buffer: &MultiBuffer,
6183 snapshot: &MultiBufferSnapshot,
6184 selection: &mut Selection<Point>,
6185 edits: &mut Vec<(Range<Point>, String)>,
6186 delta_for_start_row: u32,
6187 cx: &AppContext,
6188 ) -> u32 {
6189 let settings = buffer.settings_at(selection.start, cx);
6190 let tab_size = settings.tab_size.get();
6191 let indent_kind = if settings.hard_tabs {
6192 IndentKind::Tab
6193 } else {
6194 IndentKind::Space
6195 };
6196 let mut start_row = selection.start.row;
6197 let mut end_row = selection.end.row + 1;
6198
6199 // If a selection ends at the beginning of a line, don't indent
6200 // that last line.
6201 if selection.end.column == 0 && selection.end.row > selection.start.row {
6202 end_row -= 1;
6203 }
6204
6205 // Avoid re-indenting a row that has already been indented by a
6206 // previous selection, but still update this selection's column
6207 // to reflect that indentation.
6208 if delta_for_start_row > 0 {
6209 start_row += 1;
6210 selection.start.column += delta_for_start_row;
6211 if selection.end.row == selection.start.row {
6212 selection.end.column += delta_for_start_row;
6213 }
6214 }
6215
6216 let mut delta_for_end_row = 0;
6217 let has_multiple_rows = start_row + 1 != end_row;
6218 for row in start_row..end_row {
6219 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
6220 let indent_delta = match (current_indent.kind, indent_kind) {
6221 (IndentKind::Space, IndentKind::Space) => {
6222 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
6223 IndentSize::spaces(columns_to_next_tab_stop)
6224 }
6225 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
6226 (_, IndentKind::Tab) => IndentSize::tab(),
6227 };
6228
6229 let start = if has_multiple_rows || current_indent.len < selection.start.column {
6230 0
6231 } else {
6232 selection.start.column
6233 };
6234 let row_start = Point::new(row, start);
6235 edits.push((
6236 row_start..row_start,
6237 indent_delta.chars().collect::<String>(),
6238 ));
6239
6240 // Update this selection's endpoints to reflect the indentation.
6241 if row == selection.start.row {
6242 selection.start.column += indent_delta.len;
6243 }
6244 if row == selection.end.row {
6245 selection.end.column += indent_delta.len;
6246 delta_for_end_row = indent_delta.len;
6247 }
6248 }
6249
6250 if selection.start.row == selection.end.row {
6251 delta_for_start_row + delta_for_end_row
6252 } else {
6253 delta_for_end_row
6254 }
6255 }
6256
6257 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
6258 if self.read_only(cx) {
6259 return;
6260 }
6261 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6262 let selections = self.selections.all::<Point>(cx);
6263 let mut deletion_ranges = Vec::new();
6264 let mut last_outdent = None;
6265 {
6266 let buffer = self.buffer.read(cx);
6267 let snapshot = buffer.snapshot(cx);
6268 for selection in &selections {
6269 let settings = buffer.settings_at(selection.start, cx);
6270 let tab_size = settings.tab_size.get();
6271 let mut rows = selection.spanned_rows(false, &display_map);
6272
6273 // Avoid re-outdenting a row that has already been outdented by a
6274 // previous selection.
6275 if let Some(last_row) = last_outdent {
6276 if last_row == rows.start {
6277 rows.start = rows.start.next_row();
6278 }
6279 }
6280 let has_multiple_rows = rows.len() > 1;
6281 for row in rows.iter_rows() {
6282 let indent_size = snapshot.indent_size_for_line(row);
6283 if indent_size.len > 0 {
6284 let deletion_len = match indent_size.kind {
6285 IndentKind::Space => {
6286 let columns_to_prev_tab_stop = indent_size.len % tab_size;
6287 if columns_to_prev_tab_stop == 0 {
6288 tab_size
6289 } else {
6290 columns_to_prev_tab_stop
6291 }
6292 }
6293 IndentKind::Tab => 1,
6294 };
6295 let start = if has_multiple_rows
6296 || deletion_len > selection.start.column
6297 || indent_size.len < selection.start.column
6298 {
6299 0
6300 } else {
6301 selection.start.column - deletion_len
6302 };
6303 deletion_ranges.push(
6304 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
6305 );
6306 last_outdent = Some(row);
6307 }
6308 }
6309 }
6310 }
6311
6312 self.transact(cx, |this, cx| {
6313 this.buffer.update(cx, |buffer, cx| {
6314 let empty_str: Arc<str> = Arc::default();
6315 buffer.edit(
6316 deletion_ranges
6317 .into_iter()
6318 .map(|range| (range, empty_str.clone())),
6319 None,
6320 cx,
6321 );
6322 });
6323 let selections = this.selections.all::<usize>(cx);
6324 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6325 });
6326 }
6327
6328 pub fn autoindent(&mut self, _: &AutoIndent, cx: &mut ViewContext<Self>) {
6329 if self.read_only(cx) {
6330 return;
6331 }
6332 let selections = self
6333 .selections
6334 .all::<usize>(cx)
6335 .into_iter()
6336 .map(|s| s.range());
6337
6338 self.transact(cx, |this, cx| {
6339 this.buffer.update(cx, |buffer, cx| {
6340 buffer.autoindent_ranges(selections, cx);
6341 });
6342 let selections = this.selections.all::<usize>(cx);
6343 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6344 });
6345 }
6346
6347 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
6348 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6349 let selections = self.selections.all::<Point>(cx);
6350
6351 let mut new_cursors = Vec::new();
6352 let mut edit_ranges = Vec::new();
6353 let mut selections = selections.iter().peekable();
6354 while let Some(selection) = selections.next() {
6355 let mut rows = selection.spanned_rows(false, &display_map);
6356 let goal_display_column = selection.head().to_display_point(&display_map).column();
6357
6358 // Accumulate contiguous regions of rows that we want to delete.
6359 while let Some(next_selection) = selections.peek() {
6360 let next_rows = next_selection.spanned_rows(false, &display_map);
6361 if next_rows.start <= rows.end {
6362 rows.end = next_rows.end;
6363 selections.next().unwrap();
6364 } else {
6365 break;
6366 }
6367 }
6368
6369 let buffer = &display_map.buffer_snapshot;
6370 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
6371 let edit_end;
6372 let cursor_buffer_row;
6373 if buffer.max_point().row >= rows.end.0 {
6374 // If there's a line after the range, delete the \n from the end of the row range
6375 // and position the cursor on the next line.
6376 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
6377 cursor_buffer_row = rows.end;
6378 } else {
6379 // If there isn't a line after the range, delete the \n from the line before the
6380 // start of the row range and position the cursor there.
6381 edit_start = edit_start.saturating_sub(1);
6382 edit_end = buffer.len();
6383 cursor_buffer_row = rows.start.previous_row();
6384 }
6385
6386 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
6387 *cursor.column_mut() =
6388 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
6389
6390 new_cursors.push((
6391 selection.id,
6392 buffer.anchor_after(cursor.to_point(&display_map)),
6393 ));
6394 edit_ranges.push(edit_start..edit_end);
6395 }
6396
6397 self.transact(cx, |this, cx| {
6398 let buffer = this.buffer.update(cx, |buffer, cx| {
6399 let empty_str: Arc<str> = Arc::default();
6400 buffer.edit(
6401 edit_ranges
6402 .into_iter()
6403 .map(|range| (range, empty_str.clone())),
6404 None,
6405 cx,
6406 );
6407 buffer.snapshot(cx)
6408 });
6409 let new_selections = new_cursors
6410 .into_iter()
6411 .map(|(id, cursor)| {
6412 let cursor = cursor.to_point(&buffer);
6413 Selection {
6414 id,
6415 start: cursor,
6416 end: cursor,
6417 reversed: false,
6418 goal: SelectionGoal::None,
6419 }
6420 })
6421 .collect();
6422
6423 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6424 s.select(new_selections);
6425 });
6426 });
6427 }
6428
6429 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
6430 if self.read_only(cx) {
6431 return;
6432 }
6433 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
6434 for selection in self.selections.all::<Point>(cx) {
6435 let start = MultiBufferRow(selection.start.row);
6436 // Treat single line selections as if they include the next line. Otherwise this action
6437 // would do nothing for single line selections individual cursors.
6438 let end = if selection.start.row == selection.end.row {
6439 MultiBufferRow(selection.start.row + 1)
6440 } else {
6441 MultiBufferRow(selection.end.row)
6442 };
6443
6444 if let Some(last_row_range) = row_ranges.last_mut() {
6445 if start <= last_row_range.end {
6446 last_row_range.end = end;
6447 continue;
6448 }
6449 }
6450 row_ranges.push(start..end);
6451 }
6452
6453 let snapshot = self.buffer.read(cx).snapshot(cx);
6454 let mut cursor_positions = Vec::new();
6455 for row_range in &row_ranges {
6456 let anchor = snapshot.anchor_before(Point::new(
6457 row_range.end.previous_row().0,
6458 snapshot.line_len(row_range.end.previous_row()),
6459 ));
6460 cursor_positions.push(anchor..anchor);
6461 }
6462
6463 self.transact(cx, |this, cx| {
6464 for row_range in row_ranges.into_iter().rev() {
6465 for row in row_range.iter_rows().rev() {
6466 let end_of_line = Point::new(row.0, snapshot.line_len(row));
6467 let next_line_row = row.next_row();
6468 let indent = snapshot.indent_size_for_line(next_line_row);
6469 let start_of_next_line = Point::new(next_line_row.0, indent.len);
6470
6471 let replace = if snapshot.line_len(next_line_row) > indent.len {
6472 " "
6473 } else {
6474 ""
6475 };
6476
6477 this.buffer.update(cx, |buffer, cx| {
6478 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
6479 });
6480 }
6481 }
6482
6483 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6484 s.select_anchor_ranges(cursor_positions)
6485 });
6486 });
6487 }
6488
6489 pub fn sort_lines_case_sensitive(
6490 &mut self,
6491 _: &SortLinesCaseSensitive,
6492 cx: &mut ViewContext<Self>,
6493 ) {
6494 self.manipulate_lines(cx, |lines| lines.sort())
6495 }
6496
6497 pub fn sort_lines_case_insensitive(
6498 &mut self,
6499 _: &SortLinesCaseInsensitive,
6500 cx: &mut ViewContext<Self>,
6501 ) {
6502 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
6503 }
6504
6505 pub fn unique_lines_case_insensitive(
6506 &mut self,
6507 _: &UniqueLinesCaseInsensitive,
6508 cx: &mut ViewContext<Self>,
6509 ) {
6510 self.manipulate_lines(cx, |lines| {
6511 let mut seen = HashSet::default();
6512 lines.retain(|line| seen.insert(line.to_lowercase()));
6513 })
6514 }
6515
6516 pub fn unique_lines_case_sensitive(
6517 &mut self,
6518 _: &UniqueLinesCaseSensitive,
6519 cx: &mut ViewContext<Self>,
6520 ) {
6521 self.manipulate_lines(cx, |lines| {
6522 let mut seen = HashSet::default();
6523 lines.retain(|line| seen.insert(*line));
6524 })
6525 }
6526
6527 pub fn revert_file(&mut self, _: &RevertFile, cx: &mut ViewContext<Self>) {
6528 let mut revert_changes = HashMap::default();
6529 let snapshot = self.snapshot(cx);
6530 for hunk in hunks_for_ranges(
6531 Some(Point::zero()..snapshot.buffer_snapshot.max_point()).into_iter(),
6532 &snapshot,
6533 ) {
6534 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
6535 }
6536 if !revert_changes.is_empty() {
6537 self.transact(cx, |editor, cx| {
6538 editor.revert(revert_changes, cx);
6539 });
6540 }
6541 }
6542
6543 pub fn reload_file(&mut self, _: &ReloadFile, cx: &mut ViewContext<Self>) {
6544 let Some(project) = self.project.clone() else {
6545 return;
6546 };
6547 self.reload(project, cx).detach_and_notify_err(cx);
6548 }
6549
6550 pub fn revert_selected_hunks(&mut self, _: &RevertSelectedHunks, cx: &mut ViewContext<Self>) {
6551 let revert_changes = self.gather_revert_changes(&self.selections.all(cx), cx);
6552 if !revert_changes.is_empty() {
6553 self.transact(cx, |editor, cx| {
6554 editor.revert(revert_changes, cx);
6555 });
6556 }
6557 }
6558
6559 fn revert_hunk(&mut self, hunk: HoveredHunk, cx: &mut ViewContext<Editor>) {
6560 let snapshot = self.buffer.read(cx).read(cx);
6561 if let Some(hunk) = crate::hunk_diff::to_diff_hunk(&hunk, &snapshot) {
6562 drop(snapshot);
6563 let mut revert_changes = HashMap::default();
6564 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
6565 if !revert_changes.is_empty() {
6566 self.revert(revert_changes, cx)
6567 }
6568 }
6569 }
6570
6571 pub fn open_active_item_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
6572 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
6573 let project_path = buffer.read(cx).project_path(cx)?;
6574 let project = self.project.as_ref()?.read(cx);
6575 let entry = project.entry_for_path(&project_path, cx)?;
6576 let parent = match &entry.canonical_path {
6577 Some(canonical_path) => canonical_path.to_path_buf(),
6578 None => project.absolute_path(&project_path, cx)?,
6579 }
6580 .parent()?
6581 .to_path_buf();
6582 Some(parent)
6583 }) {
6584 cx.dispatch_action(OpenTerminal { working_directory }.boxed_clone());
6585 }
6586 }
6587
6588 fn gather_revert_changes(
6589 &mut self,
6590 selections: &[Selection<Point>],
6591 cx: &mut ViewContext<'_, Editor>,
6592 ) -> HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>> {
6593 let mut revert_changes = HashMap::default();
6594 let snapshot = self.snapshot(cx);
6595 for hunk in hunks_for_selections(&snapshot, selections) {
6596 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
6597 }
6598 revert_changes
6599 }
6600
6601 pub fn prepare_revert_change(
6602 &mut self,
6603 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
6604 hunk: &MultiBufferDiffHunk,
6605 cx: &AppContext,
6606 ) -> Option<()> {
6607 let buffer = self.buffer.read(cx).buffer(hunk.buffer_id)?;
6608 let buffer = buffer.read(cx);
6609 let change_set = &self.diff_map.diff_bases.get(&hunk.buffer_id)?.change_set;
6610 let original_text = change_set
6611 .read(cx)
6612 .base_text
6613 .as_ref()?
6614 .read(cx)
6615 .as_rope()
6616 .slice(hunk.diff_base_byte_range.clone());
6617 let buffer_snapshot = buffer.snapshot();
6618 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
6619 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
6620 probe
6621 .0
6622 .start
6623 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
6624 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
6625 }) {
6626 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
6627 Some(())
6628 } else {
6629 None
6630 }
6631 }
6632
6633 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
6634 self.manipulate_lines(cx, |lines| lines.reverse())
6635 }
6636
6637 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
6638 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
6639 }
6640
6641 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6642 where
6643 Fn: FnMut(&mut Vec<&str>),
6644 {
6645 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6646 let buffer = self.buffer.read(cx).snapshot(cx);
6647
6648 let mut edits = Vec::new();
6649
6650 let selections = self.selections.all::<Point>(cx);
6651 let mut selections = selections.iter().peekable();
6652 let mut contiguous_row_selections = Vec::new();
6653 let mut new_selections = Vec::new();
6654 let mut added_lines = 0;
6655 let mut removed_lines = 0;
6656
6657 while let Some(selection) = selections.next() {
6658 let (start_row, end_row) = consume_contiguous_rows(
6659 &mut contiguous_row_selections,
6660 selection,
6661 &display_map,
6662 &mut selections,
6663 );
6664
6665 let start_point = Point::new(start_row.0, 0);
6666 let end_point = Point::new(
6667 end_row.previous_row().0,
6668 buffer.line_len(end_row.previous_row()),
6669 );
6670 let text = buffer
6671 .text_for_range(start_point..end_point)
6672 .collect::<String>();
6673
6674 let mut lines = text.split('\n').collect_vec();
6675
6676 let lines_before = lines.len();
6677 callback(&mut lines);
6678 let lines_after = lines.len();
6679
6680 edits.push((start_point..end_point, lines.join("\n")));
6681
6682 // Selections must change based on added and removed line count
6683 let start_row =
6684 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
6685 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
6686 new_selections.push(Selection {
6687 id: selection.id,
6688 start: start_row,
6689 end: end_row,
6690 goal: SelectionGoal::None,
6691 reversed: selection.reversed,
6692 });
6693
6694 if lines_after > lines_before {
6695 added_lines += lines_after - lines_before;
6696 } else if lines_before > lines_after {
6697 removed_lines += lines_before - lines_after;
6698 }
6699 }
6700
6701 self.transact(cx, |this, cx| {
6702 let buffer = this.buffer.update(cx, |buffer, cx| {
6703 buffer.edit(edits, None, cx);
6704 buffer.snapshot(cx)
6705 });
6706
6707 // Recalculate offsets on newly edited buffer
6708 let new_selections = new_selections
6709 .iter()
6710 .map(|s| {
6711 let start_point = Point::new(s.start.0, 0);
6712 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
6713 Selection {
6714 id: s.id,
6715 start: buffer.point_to_offset(start_point),
6716 end: buffer.point_to_offset(end_point),
6717 goal: s.goal,
6718 reversed: s.reversed,
6719 }
6720 })
6721 .collect();
6722
6723 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6724 s.select(new_selections);
6725 });
6726
6727 this.request_autoscroll(Autoscroll::fit(), cx);
6728 });
6729 }
6730
6731 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
6732 self.manipulate_text(cx, |text| text.to_uppercase())
6733 }
6734
6735 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
6736 self.manipulate_text(cx, |text| text.to_lowercase())
6737 }
6738
6739 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
6740 self.manipulate_text(cx, |text| {
6741 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6742 // https://github.com/rutrum/convert-case/issues/16
6743 text.split('\n')
6744 .map(|line| line.to_case(Case::Title))
6745 .join("\n")
6746 })
6747 }
6748
6749 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
6750 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
6751 }
6752
6753 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
6754 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
6755 }
6756
6757 pub fn convert_to_upper_camel_case(
6758 &mut self,
6759 _: &ConvertToUpperCamelCase,
6760 cx: &mut ViewContext<Self>,
6761 ) {
6762 self.manipulate_text(cx, |text| {
6763 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6764 // https://github.com/rutrum/convert-case/issues/16
6765 text.split('\n')
6766 .map(|line| line.to_case(Case::UpperCamel))
6767 .join("\n")
6768 })
6769 }
6770
6771 pub fn convert_to_lower_camel_case(
6772 &mut self,
6773 _: &ConvertToLowerCamelCase,
6774 cx: &mut ViewContext<Self>,
6775 ) {
6776 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
6777 }
6778
6779 pub fn convert_to_opposite_case(
6780 &mut self,
6781 _: &ConvertToOppositeCase,
6782 cx: &mut ViewContext<Self>,
6783 ) {
6784 self.manipulate_text(cx, |text| {
6785 text.chars()
6786 .fold(String::with_capacity(text.len()), |mut t, c| {
6787 if c.is_uppercase() {
6788 t.extend(c.to_lowercase());
6789 } else {
6790 t.extend(c.to_uppercase());
6791 }
6792 t
6793 })
6794 })
6795 }
6796
6797 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6798 where
6799 Fn: FnMut(&str) -> String,
6800 {
6801 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6802 let buffer = self.buffer.read(cx).snapshot(cx);
6803
6804 let mut new_selections = Vec::new();
6805 let mut edits = Vec::new();
6806 let mut selection_adjustment = 0i32;
6807
6808 for selection in self.selections.all::<usize>(cx) {
6809 let selection_is_empty = selection.is_empty();
6810
6811 let (start, end) = if selection_is_empty {
6812 let word_range = movement::surrounding_word(
6813 &display_map,
6814 selection.start.to_display_point(&display_map),
6815 );
6816 let start = word_range.start.to_offset(&display_map, Bias::Left);
6817 let end = word_range.end.to_offset(&display_map, Bias::Left);
6818 (start, end)
6819 } else {
6820 (selection.start, selection.end)
6821 };
6822
6823 let text = buffer.text_for_range(start..end).collect::<String>();
6824 let old_length = text.len() as i32;
6825 let text = callback(&text);
6826
6827 new_selections.push(Selection {
6828 start: (start as i32 - selection_adjustment) as usize,
6829 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
6830 goal: SelectionGoal::None,
6831 ..selection
6832 });
6833
6834 selection_adjustment += old_length - text.len() as i32;
6835
6836 edits.push((start..end, text));
6837 }
6838
6839 self.transact(cx, |this, cx| {
6840 this.buffer.update(cx, |buffer, cx| {
6841 buffer.edit(edits, None, cx);
6842 });
6843
6844 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6845 s.select(new_selections);
6846 });
6847
6848 this.request_autoscroll(Autoscroll::fit(), cx);
6849 });
6850 }
6851
6852 pub fn duplicate_line(&mut self, upwards: bool, cx: &mut ViewContext<Self>) {
6853 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6854 let buffer = &display_map.buffer_snapshot;
6855 let selections = self.selections.all::<Point>(cx);
6856
6857 let mut edits = Vec::new();
6858 let mut selections_iter = selections.iter().peekable();
6859 while let Some(selection) = selections_iter.next() {
6860 // Avoid duplicating the same lines twice.
6861 let mut rows = selection.spanned_rows(false, &display_map);
6862
6863 while let Some(next_selection) = selections_iter.peek() {
6864 let next_rows = next_selection.spanned_rows(false, &display_map);
6865 if next_rows.start < rows.end {
6866 rows.end = next_rows.end;
6867 selections_iter.next().unwrap();
6868 } else {
6869 break;
6870 }
6871 }
6872
6873 // Copy the text from the selected row region and splice it either at the start
6874 // or end of the region.
6875 let start = Point::new(rows.start.0, 0);
6876 let end = Point::new(
6877 rows.end.previous_row().0,
6878 buffer.line_len(rows.end.previous_row()),
6879 );
6880 let text = buffer
6881 .text_for_range(start..end)
6882 .chain(Some("\n"))
6883 .collect::<String>();
6884 let insert_location = if upwards {
6885 Point::new(rows.end.0, 0)
6886 } else {
6887 start
6888 };
6889 edits.push((insert_location..insert_location, text));
6890 }
6891
6892 self.transact(cx, |this, cx| {
6893 this.buffer.update(cx, |buffer, cx| {
6894 buffer.edit(edits, None, cx);
6895 });
6896
6897 this.request_autoscroll(Autoscroll::fit(), cx);
6898 });
6899 }
6900
6901 pub fn duplicate_line_up(&mut self, _: &DuplicateLineUp, cx: &mut ViewContext<Self>) {
6902 self.duplicate_line(true, cx);
6903 }
6904
6905 pub fn duplicate_line_down(&mut self, _: &DuplicateLineDown, cx: &mut ViewContext<Self>) {
6906 self.duplicate_line(false, cx);
6907 }
6908
6909 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
6910 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6911 let buffer = self.buffer.read(cx).snapshot(cx);
6912
6913 let mut edits = Vec::new();
6914 let mut unfold_ranges = Vec::new();
6915 let mut refold_creases = Vec::new();
6916
6917 let selections = self.selections.all::<Point>(cx);
6918 let mut selections = selections.iter().peekable();
6919 let mut contiguous_row_selections = Vec::new();
6920 let mut new_selections = Vec::new();
6921
6922 while let Some(selection) = selections.next() {
6923 // Find all the selections that span a contiguous row range
6924 let (start_row, end_row) = consume_contiguous_rows(
6925 &mut contiguous_row_selections,
6926 selection,
6927 &display_map,
6928 &mut selections,
6929 );
6930
6931 // Move the text spanned by the row range to be before the line preceding the row range
6932 if start_row.0 > 0 {
6933 let range_to_move = Point::new(
6934 start_row.previous_row().0,
6935 buffer.line_len(start_row.previous_row()),
6936 )
6937 ..Point::new(
6938 end_row.previous_row().0,
6939 buffer.line_len(end_row.previous_row()),
6940 );
6941 let insertion_point = display_map
6942 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
6943 .0;
6944
6945 // Don't move lines across excerpts
6946 if buffer
6947 .excerpt_boundaries_in_range((
6948 Bound::Excluded(insertion_point),
6949 Bound::Included(range_to_move.end),
6950 ))
6951 .next()
6952 .is_none()
6953 {
6954 let text = buffer
6955 .text_for_range(range_to_move.clone())
6956 .flat_map(|s| s.chars())
6957 .skip(1)
6958 .chain(['\n'])
6959 .collect::<String>();
6960
6961 edits.push((
6962 buffer.anchor_after(range_to_move.start)
6963 ..buffer.anchor_before(range_to_move.end),
6964 String::new(),
6965 ));
6966 let insertion_anchor = buffer.anchor_after(insertion_point);
6967 edits.push((insertion_anchor..insertion_anchor, text));
6968
6969 let row_delta = range_to_move.start.row - insertion_point.row + 1;
6970
6971 // Move selections up
6972 new_selections.extend(contiguous_row_selections.drain(..).map(
6973 |mut selection| {
6974 selection.start.row -= row_delta;
6975 selection.end.row -= row_delta;
6976 selection
6977 },
6978 ));
6979
6980 // Move folds up
6981 unfold_ranges.push(range_to_move.clone());
6982 for fold in display_map.folds_in_range(
6983 buffer.anchor_before(range_to_move.start)
6984 ..buffer.anchor_after(range_to_move.end),
6985 ) {
6986 let mut start = fold.range.start.to_point(&buffer);
6987 let mut end = fold.range.end.to_point(&buffer);
6988 start.row -= row_delta;
6989 end.row -= row_delta;
6990 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
6991 }
6992 }
6993 }
6994
6995 // If we didn't move line(s), preserve the existing selections
6996 new_selections.append(&mut contiguous_row_selections);
6997 }
6998
6999 self.transact(cx, |this, cx| {
7000 this.unfold_ranges(&unfold_ranges, true, true, cx);
7001 this.buffer.update(cx, |buffer, cx| {
7002 for (range, text) in edits {
7003 buffer.edit([(range, text)], None, cx);
7004 }
7005 });
7006 this.fold_creases(refold_creases, true, cx);
7007 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7008 s.select(new_selections);
7009 })
7010 });
7011 }
7012
7013 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
7014 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7015 let buffer = self.buffer.read(cx).snapshot(cx);
7016
7017 let mut edits = Vec::new();
7018 let mut unfold_ranges = Vec::new();
7019 let mut refold_creases = Vec::new();
7020
7021 let selections = self.selections.all::<Point>(cx);
7022 let mut selections = selections.iter().peekable();
7023 let mut contiguous_row_selections = Vec::new();
7024 let mut new_selections = Vec::new();
7025
7026 while let Some(selection) = selections.next() {
7027 // Find all the selections that span a contiguous row range
7028 let (start_row, end_row) = consume_contiguous_rows(
7029 &mut contiguous_row_selections,
7030 selection,
7031 &display_map,
7032 &mut selections,
7033 );
7034
7035 // Move the text spanned by the row range to be after the last line of the row range
7036 if end_row.0 <= buffer.max_point().row {
7037 let range_to_move =
7038 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
7039 let insertion_point = display_map
7040 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
7041 .0;
7042
7043 // Don't move lines across excerpt boundaries
7044 if buffer
7045 .excerpt_boundaries_in_range((
7046 Bound::Excluded(range_to_move.start),
7047 Bound::Included(insertion_point),
7048 ))
7049 .next()
7050 .is_none()
7051 {
7052 let mut text = String::from("\n");
7053 text.extend(buffer.text_for_range(range_to_move.clone()));
7054 text.pop(); // Drop trailing newline
7055 edits.push((
7056 buffer.anchor_after(range_to_move.start)
7057 ..buffer.anchor_before(range_to_move.end),
7058 String::new(),
7059 ));
7060 let insertion_anchor = buffer.anchor_after(insertion_point);
7061 edits.push((insertion_anchor..insertion_anchor, text));
7062
7063 let row_delta = insertion_point.row - range_to_move.end.row + 1;
7064
7065 // Move selections down
7066 new_selections.extend(contiguous_row_selections.drain(..).map(
7067 |mut selection| {
7068 selection.start.row += row_delta;
7069 selection.end.row += row_delta;
7070 selection
7071 },
7072 ));
7073
7074 // Move folds down
7075 unfold_ranges.push(range_to_move.clone());
7076 for fold in display_map.folds_in_range(
7077 buffer.anchor_before(range_to_move.start)
7078 ..buffer.anchor_after(range_to_move.end),
7079 ) {
7080 let mut start = fold.range.start.to_point(&buffer);
7081 let mut end = fold.range.end.to_point(&buffer);
7082 start.row += row_delta;
7083 end.row += row_delta;
7084 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
7085 }
7086 }
7087 }
7088
7089 // If we didn't move line(s), preserve the existing selections
7090 new_selections.append(&mut contiguous_row_selections);
7091 }
7092
7093 self.transact(cx, |this, cx| {
7094 this.unfold_ranges(&unfold_ranges, true, true, cx);
7095 this.buffer.update(cx, |buffer, cx| {
7096 for (range, text) in edits {
7097 buffer.edit([(range, text)], None, cx);
7098 }
7099 });
7100 this.fold_creases(refold_creases, true, cx);
7101 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
7102 });
7103 }
7104
7105 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
7106 let text_layout_details = &self.text_layout_details(cx);
7107 self.transact(cx, |this, cx| {
7108 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7109 let mut edits: Vec<(Range<usize>, String)> = Default::default();
7110 let line_mode = s.line_mode;
7111 s.move_with(|display_map, selection| {
7112 if !selection.is_empty() || line_mode {
7113 return;
7114 }
7115
7116 let mut head = selection.head();
7117 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
7118 if head.column() == display_map.line_len(head.row()) {
7119 transpose_offset = display_map
7120 .buffer_snapshot
7121 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
7122 }
7123
7124 if transpose_offset == 0 {
7125 return;
7126 }
7127
7128 *head.column_mut() += 1;
7129 head = display_map.clip_point(head, Bias::Right);
7130 let goal = SelectionGoal::HorizontalPosition(
7131 display_map
7132 .x_for_display_point(head, text_layout_details)
7133 .into(),
7134 );
7135 selection.collapse_to(head, goal);
7136
7137 let transpose_start = display_map
7138 .buffer_snapshot
7139 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
7140 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
7141 let transpose_end = display_map
7142 .buffer_snapshot
7143 .clip_offset(transpose_offset + 1, Bias::Right);
7144 if let Some(ch) =
7145 display_map.buffer_snapshot.chars_at(transpose_start).next()
7146 {
7147 edits.push((transpose_start..transpose_offset, String::new()));
7148 edits.push((transpose_end..transpose_end, ch.to_string()));
7149 }
7150 }
7151 });
7152 edits
7153 });
7154 this.buffer
7155 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
7156 let selections = this.selections.all::<usize>(cx);
7157 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7158 s.select(selections);
7159 });
7160 });
7161 }
7162
7163 pub fn rewrap(&mut self, _: &Rewrap, cx: &mut ViewContext<Self>) {
7164 self.rewrap_impl(IsVimMode::No, cx)
7165 }
7166
7167 pub fn rewrap_impl(&mut self, is_vim_mode: IsVimMode, cx: &mut ViewContext<Self>) {
7168 let buffer = self.buffer.read(cx).snapshot(cx);
7169 let selections = self.selections.all::<Point>(cx);
7170 let mut selections = selections.iter().peekable();
7171
7172 let mut edits = Vec::new();
7173 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
7174
7175 while let Some(selection) = selections.next() {
7176 let mut start_row = selection.start.row;
7177 let mut end_row = selection.end.row;
7178
7179 // Skip selections that overlap with a range that has already been rewrapped.
7180 let selection_range = start_row..end_row;
7181 if rewrapped_row_ranges
7182 .iter()
7183 .any(|range| range.overlaps(&selection_range))
7184 {
7185 continue;
7186 }
7187
7188 let mut should_rewrap = is_vim_mode == IsVimMode::Yes;
7189
7190 if let Some(language_scope) = buffer.language_scope_at(selection.head()) {
7191 match language_scope.language_name().0.as_ref() {
7192 "Markdown" | "Plain Text" => {
7193 should_rewrap = true;
7194 }
7195 _ => {}
7196 }
7197 }
7198
7199 let tab_size = buffer.settings_at(selection.head(), cx).tab_size;
7200
7201 // Since not all lines in the selection may be at the same indent
7202 // level, choose the indent size that is the most common between all
7203 // of the lines.
7204 //
7205 // If there is a tie, we use the deepest indent.
7206 let (indent_size, indent_end) = {
7207 let mut indent_size_occurrences = HashMap::default();
7208 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
7209
7210 for row in start_row..=end_row {
7211 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
7212 rows_by_indent_size.entry(indent).or_default().push(row);
7213 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
7214 }
7215
7216 let indent_size = indent_size_occurrences
7217 .into_iter()
7218 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
7219 .map(|(indent, _)| indent)
7220 .unwrap_or_default();
7221 let row = rows_by_indent_size[&indent_size][0];
7222 let indent_end = Point::new(row, indent_size.len);
7223
7224 (indent_size, indent_end)
7225 };
7226
7227 let mut line_prefix = indent_size.chars().collect::<String>();
7228
7229 if let Some(comment_prefix) =
7230 buffer
7231 .language_scope_at(selection.head())
7232 .and_then(|language| {
7233 language
7234 .line_comment_prefixes()
7235 .iter()
7236 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
7237 .cloned()
7238 })
7239 {
7240 line_prefix.push_str(&comment_prefix);
7241 should_rewrap = true;
7242 }
7243
7244 if !should_rewrap {
7245 continue;
7246 }
7247
7248 if selection.is_empty() {
7249 'expand_upwards: while start_row > 0 {
7250 let prev_row = start_row - 1;
7251 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
7252 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
7253 {
7254 start_row = prev_row;
7255 } else {
7256 break 'expand_upwards;
7257 }
7258 }
7259
7260 'expand_downwards: while end_row < buffer.max_point().row {
7261 let next_row = end_row + 1;
7262 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
7263 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
7264 {
7265 end_row = next_row;
7266 } else {
7267 break 'expand_downwards;
7268 }
7269 }
7270 }
7271
7272 let start = Point::new(start_row, 0);
7273 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
7274 let selection_text = buffer.text_for_range(start..end).collect::<String>();
7275 let Some(lines_without_prefixes) = selection_text
7276 .lines()
7277 .map(|line| {
7278 line.strip_prefix(&line_prefix)
7279 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
7280 .ok_or_else(|| {
7281 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
7282 })
7283 })
7284 .collect::<Result<Vec<_>, _>>()
7285 .log_err()
7286 else {
7287 continue;
7288 };
7289
7290 let wrap_column = buffer
7291 .settings_at(Point::new(start_row, 0), cx)
7292 .preferred_line_length as usize;
7293 let wrapped_text = wrap_with_prefix(
7294 line_prefix,
7295 lines_without_prefixes.join(" "),
7296 wrap_column,
7297 tab_size,
7298 );
7299
7300 // TODO: should always use char-based diff while still supporting cursor behavior that
7301 // matches vim.
7302 let diff = match is_vim_mode {
7303 IsVimMode::Yes => TextDiff::from_lines(&selection_text, &wrapped_text),
7304 IsVimMode::No => TextDiff::from_chars(&selection_text, &wrapped_text),
7305 };
7306 let mut offset = start.to_offset(&buffer);
7307 let mut moved_since_edit = true;
7308
7309 for change in diff.iter_all_changes() {
7310 let value = change.value();
7311 match change.tag() {
7312 ChangeTag::Equal => {
7313 offset += value.len();
7314 moved_since_edit = true;
7315 }
7316 ChangeTag::Delete => {
7317 let start = buffer.anchor_after(offset);
7318 let end = buffer.anchor_before(offset + value.len());
7319
7320 if moved_since_edit {
7321 edits.push((start..end, String::new()));
7322 } else {
7323 edits.last_mut().unwrap().0.end = end;
7324 }
7325
7326 offset += value.len();
7327 moved_since_edit = false;
7328 }
7329 ChangeTag::Insert => {
7330 if moved_since_edit {
7331 let anchor = buffer.anchor_after(offset);
7332 edits.push((anchor..anchor, value.to_string()));
7333 } else {
7334 edits.last_mut().unwrap().1.push_str(value);
7335 }
7336
7337 moved_since_edit = false;
7338 }
7339 }
7340 }
7341
7342 rewrapped_row_ranges.push(start_row..=end_row);
7343 }
7344
7345 self.buffer
7346 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
7347 }
7348
7349 pub fn cut_common(&mut self, cx: &mut ViewContext<Self>) -> ClipboardItem {
7350 let mut text = String::new();
7351 let buffer = self.buffer.read(cx).snapshot(cx);
7352 let mut selections = self.selections.all::<Point>(cx);
7353 let mut clipboard_selections = Vec::with_capacity(selections.len());
7354 {
7355 let max_point = buffer.max_point();
7356 let mut is_first = true;
7357 for selection in &mut selections {
7358 let is_entire_line = selection.is_empty() || self.selections.line_mode;
7359 if is_entire_line {
7360 selection.start = Point::new(selection.start.row, 0);
7361 if !selection.is_empty() && selection.end.column == 0 {
7362 selection.end = cmp::min(max_point, selection.end);
7363 } else {
7364 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
7365 }
7366 selection.goal = SelectionGoal::None;
7367 }
7368 if is_first {
7369 is_first = false;
7370 } else {
7371 text += "\n";
7372 }
7373 let mut len = 0;
7374 for chunk in buffer.text_for_range(selection.start..selection.end) {
7375 text.push_str(chunk);
7376 len += chunk.len();
7377 }
7378 clipboard_selections.push(ClipboardSelection {
7379 len,
7380 is_entire_line,
7381 first_line_indent: buffer
7382 .indent_size_for_line(MultiBufferRow(selection.start.row))
7383 .len,
7384 });
7385 }
7386 }
7387
7388 self.transact(cx, |this, cx| {
7389 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7390 s.select(selections);
7391 });
7392 this.insert("", cx);
7393 });
7394 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
7395 }
7396
7397 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
7398 let item = self.cut_common(cx);
7399 cx.write_to_clipboard(item);
7400 }
7401
7402 pub fn kill_ring_cut(&mut self, _: &KillRingCut, cx: &mut ViewContext<Self>) {
7403 self.change_selections(None, cx, |s| {
7404 s.move_with(|snapshot, sel| {
7405 if sel.is_empty() {
7406 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
7407 }
7408 });
7409 });
7410 let item = self.cut_common(cx);
7411 cx.set_global(KillRing(item))
7412 }
7413
7414 pub fn kill_ring_yank(&mut self, _: &KillRingYank, cx: &mut ViewContext<Self>) {
7415 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
7416 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
7417 (kill_ring.text().to_string(), kill_ring.metadata_json())
7418 } else {
7419 return;
7420 }
7421 } else {
7422 return;
7423 };
7424 self.do_paste(&text, metadata, false, cx);
7425 }
7426
7427 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
7428 let selections = self.selections.all::<Point>(cx);
7429 let buffer = self.buffer.read(cx).read(cx);
7430 let mut text = String::new();
7431
7432 let mut clipboard_selections = Vec::with_capacity(selections.len());
7433 {
7434 let max_point = buffer.max_point();
7435 let mut is_first = true;
7436 for selection in selections.iter() {
7437 let mut start = selection.start;
7438 let mut end = selection.end;
7439 let is_entire_line = selection.is_empty() || self.selections.line_mode;
7440 if is_entire_line {
7441 start = Point::new(start.row, 0);
7442 end = cmp::min(max_point, Point::new(end.row + 1, 0));
7443 }
7444 if is_first {
7445 is_first = false;
7446 } else {
7447 text += "\n";
7448 }
7449 let mut len = 0;
7450 for chunk in buffer.text_for_range(start..end) {
7451 text.push_str(chunk);
7452 len += chunk.len();
7453 }
7454 clipboard_selections.push(ClipboardSelection {
7455 len,
7456 is_entire_line,
7457 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
7458 });
7459 }
7460 }
7461
7462 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
7463 text,
7464 clipboard_selections,
7465 ));
7466 }
7467
7468 pub fn do_paste(
7469 &mut self,
7470 text: &String,
7471 clipboard_selections: Option<Vec<ClipboardSelection>>,
7472 handle_entire_lines: bool,
7473 cx: &mut ViewContext<Self>,
7474 ) {
7475 if self.read_only(cx) {
7476 return;
7477 }
7478
7479 let clipboard_text = Cow::Borrowed(text);
7480
7481 self.transact(cx, |this, cx| {
7482 if let Some(mut clipboard_selections) = clipboard_selections {
7483 let old_selections = this.selections.all::<usize>(cx);
7484 let all_selections_were_entire_line =
7485 clipboard_selections.iter().all(|s| s.is_entire_line);
7486 let first_selection_indent_column =
7487 clipboard_selections.first().map(|s| s.first_line_indent);
7488 if clipboard_selections.len() != old_selections.len() {
7489 clipboard_selections.drain(..);
7490 }
7491 let cursor_offset = this.selections.last::<usize>(cx).head();
7492 let mut auto_indent_on_paste = true;
7493
7494 this.buffer.update(cx, |buffer, cx| {
7495 let snapshot = buffer.read(cx);
7496 auto_indent_on_paste =
7497 snapshot.settings_at(cursor_offset, cx).auto_indent_on_paste;
7498
7499 let mut start_offset = 0;
7500 let mut edits = Vec::new();
7501 let mut original_indent_columns = Vec::new();
7502 for (ix, selection) in old_selections.iter().enumerate() {
7503 let to_insert;
7504 let entire_line;
7505 let original_indent_column;
7506 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
7507 let end_offset = start_offset + clipboard_selection.len;
7508 to_insert = &clipboard_text[start_offset..end_offset];
7509 entire_line = clipboard_selection.is_entire_line;
7510 start_offset = end_offset + 1;
7511 original_indent_column = Some(clipboard_selection.first_line_indent);
7512 } else {
7513 to_insert = clipboard_text.as_str();
7514 entire_line = all_selections_were_entire_line;
7515 original_indent_column = first_selection_indent_column
7516 }
7517
7518 // If the corresponding selection was empty when this slice of the
7519 // clipboard text was written, then the entire line containing the
7520 // selection was copied. If this selection is also currently empty,
7521 // then paste the line before the current line of the buffer.
7522 let range = if selection.is_empty() && handle_entire_lines && entire_line {
7523 let column = selection.start.to_point(&snapshot).column as usize;
7524 let line_start = selection.start - column;
7525 line_start..line_start
7526 } else {
7527 selection.range()
7528 };
7529
7530 edits.push((range, to_insert));
7531 original_indent_columns.extend(original_indent_column);
7532 }
7533 drop(snapshot);
7534
7535 buffer.edit(
7536 edits,
7537 if auto_indent_on_paste {
7538 Some(AutoindentMode::Block {
7539 original_indent_columns,
7540 })
7541 } else {
7542 None
7543 },
7544 cx,
7545 );
7546 });
7547
7548 let selections = this.selections.all::<usize>(cx);
7549 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
7550 } else {
7551 this.insert(&clipboard_text, cx);
7552 }
7553 });
7554 }
7555
7556 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
7557 if let Some(item) = cx.read_from_clipboard() {
7558 let entries = item.entries();
7559
7560 match entries.first() {
7561 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
7562 // of all the pasted entries.
7563 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
7564 .do_paste(
7565 clipboard_string.text(),
7566 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
7567 true,
7568 cx,
7569 ),
7570 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, cx),
7571 }
7572 }
7573 }
7574
7575 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
7576 if self.read_only(cx) {
7577 return;
7578 }
7579
7580 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
7581 if let Some((selections, _)) =
7582 self.selection_history.transaction(transaction_id).cloned()
7583 {
7584 self.change_selections(None, cx, |s| {
7585 s.select_anchors(selections.to_vec());
7586 });
7587 }
7588 self.request_autoscroll(Autoscroll::fit(), cx);
7589 self.unmark_text(cx);
7590 self.refresh_inline_completion(true, false, cx);
7591 cx.emit(EditorEvent::Edited { transaction_id });
7592 cx.emit(EditorEvent::TransactionUndone { transaction_id });
7593 }
7594 }
7595
7596 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
7597 if self.read_only(cx) {
7598 return;
7599 }
7600
7601 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
7602 if let Some((_, Some(selections))) =
7603 self.selection_history.transaction(transaction_id).cloned()
7604 {
7605 self.change_selections(None, cx, |s| {
7606 s.select_anchors(selections.to_vec());
7607 });
7608 }
7609 self.request_autoscroll(Autoscroll::fit(), cx);
7610 self.unmark_text(cx);
7611 self.refresh_inline_completion(true, false, cx);
7612 cx.emit(EditorEvent::Edited { transaction_id });
7613 }
7614 }
7615
7616 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
7617 self.buffer
7618 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
7619 }
7620
7621 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut ViewContext<Self>) {
7622 self.buffer
7623 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
7624 }
7625
7626 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
7627 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7628 let line_mode = s.line_mode;
7629 s.move_with(|map, selection| {
7630 let cursor = if selection.is_empty() && !line_mode {
7631 movement::left(map, selection.start)
7632 } else {
7633 selection.start
7634 };
7635 selection.collapse_to(cursor, SelectionGoal::None);
7636 });
7637 })
7638 }
7639
7640 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
7641 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7642 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
7643 })
7644 }
7645
7646 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
7647 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7648 let line_mode = s.line_mode;
7649 s.move_with(|map, selection| {
7650 let cursor = if selection.is_empty() && !line_mode {
7651 movement::right(map, selection.end)
7652 } else {
7653 selection.end
7654 };
7655 selection.collapse_to(cursor, SelectionGoal::None)
7656 });
7657 })
7658 }
7659
7660 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
7661 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7662 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
7663 })
7664 }
7665
7666 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
7667 if self.take_rename(true, cx).is_some() {
7668 return;
7669 }
7670
7671 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7672 cx.propagate();
7673 return;
7674 }
7675
7676 let text_layout_details = &self.text_layout_details(cx);
7677 let selection_count = self.selections.count();
7678 let first_selection = self.selections.first_anchor();
7679
7680 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7681 let line_mode = s.line_mode;
7682 s.move_with(|map, selection| {
7683 if !selection.is_empty() && !line_mode {
7684 selection.goal = SelectionGoal::None;
7685 }
7686 let (cursor, goal) = movement::up(
7687 map,
7688 selection.start,
7689 selection.goal,
7690 false,
7691 text_layout_details,
7692 );
7693 selection.collapse_to(cursor, goal);
7694 });
7695 });
7696
7697 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7698 {
7699 cx.propagate();
7700 }
7701 }
7702
7703 pub fn move_up_by_lines(&mut self, action: &MoveUpByLines, cx: &mut ViewContext<Self>) {
7704 if self.take_rename(true, cx).is_some() {
7705 return;
7706 }
7707
7708 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7709 cx.propagate();
7710 return;
7711 }
7712
7713 let text_layout_details = &self.text_layout_details(cx);
7714
7715 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7716 let line_mode = s.line_mode;
7717 s.move_with(|map, selection| {
7718 if !selection.is_empty() && !line_mode {
7719 selection.goal = SelectionGoal::None;
7720 }
7721 let (cursor, goal) = movement::up_by_rows(
7722 map,
7723 selection.start,
7724 action.lines,
7725 selection.goal,
7726 false,
7727 text_layout_details,
7728 );
7729 selection.collapse_to(cursor, goal);
7730 });
7731 })
7732 }
7733
7734 pub fn move_down_by_lines(&mut self, action: &MoveDownByLines, cx: &mut ViewContext<Self>) {
7735 if self.take_rename(true, cx).is_some() {
7736 return;
7737 }
7738
7739 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7740 cx.propagate();
7741 return;
7742 }
7743
7744 let text_layout_details = &self.text_layout_details(cx);
7745
7746 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7747 let line_mode = s.line_mode;
7748 s.move_with(|map, selection| {
7749 if !selection.is_empty() && !line_mode {
7750 selection.goal = SelectionGoal::None;
7751 }
7752 let (cursor, goal) = movement::down_by_rows(
7753 map,
7754 selection.start,
7755 action.lines,
7756 selection.goal,
7757 false,
7758 text_layout_details,
7759 );
7760 selection.collapse_to(cursor, goal);
7761 });
7762 })
7763 }
7764
7765 pub fn select_down_by_lines(&mut self, action: &SelectDownByLines, cx: &mut ViewContext<Self>) {
7766 let text_layout_details = &self.text_layout_details(cx);
7767 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7768 s.move_heads_with(|map, head, goal| {
7769 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
7770 })
7771 })
7772 }
7773
7774 pub fn select_up_by_lines(&mut self, action: &SelectUpByLines, cx: &mut ViewContext<Self>) {
7775 let text_layout_details = &self.text_layout_details(cx);
7776 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7777 s.move_heads_with(|map, head, goal| {
7778 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
7779 })
7780 })
7781 }
7782
7783 pub fn select_page_up(&mut self, _: &SelectPageUp, cx: &mut ViewContext<Self>) {
7784 let Some(row_count) = self.visible_row_count() else {
7785 return;
7786 };
7787
7788 let text_layout_details = &self.text_layout_details(cx);
7789
7790 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7791 s.move_heads_with(|map, head, goal| {
7792 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
7793 })
7794 })
7795 }
7796
7797 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
7798 if self.take_rename(true, cx).is_some() {
7799 return;
7800 }
7801
7802 if self
7803 .context_menu
7804 .write()
7805 .as_mut()
7806 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
7807 .unwrap_or(false)
7808 {
7809 return;
7810 }
7811
7812 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7813 cx.propagate();
7814 return;
7815 }
7816
7817 let Some(row_count) = self.visible_row_count() else {
7818 return;
7819 };
7820
7821 let autoscroll = if action.center_cursor {
7822 Autoscroll::center()
7823 } else {
7824 Autoscroll::fit()
7825 };
7826
7827 let text_layout_details = &self.text_layout_details(cx);
7828
7829 self.change_selections(Some(autoscroll), cx, |s| {
7830 let line_mode = s.line_mode;
7831 s.move_with(|map, selection| {
7832 if !selection.is_empty() && !line_mode {
7833 selection.goal = SelectionGoal::None;
7834 }
7835 let (cursor, goal) = movement::up_by_rows(
7836 map,
7837 selection.end,
7838 row_count,
7839 selection.goal,
7840 false,
7841 text_layout_details,
7842 );
7843 selection.collapse_to(cursor, goal);
7844 });
7845 });
7846 }
7847
7848 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
7849 let text_layout_details = &self.text_layout_details(cx);
7850 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7851 s.move_heads_with(|map, head, goal| {
7852 movement::up(map, head, goal, false, text_layout_details)
7853 })
7854 })
7855 }
7856
7857 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
7858 self.take_rename(true, cx);
7859
7860 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7861 cx.propagate();
7862 return;
7863 }
7864
7865 let text_layout_details = &self.text_layout_details(cx);
7866 let selection_count = self.selections.count();
7867 let first_selection = self.selections.first_anchor();
7868
7869 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7870 let line_mode = s.line_mode;
7871 s.move_with(|map, selection| {
7872 if !selection.is_empty() && !line_mode {
7873 selection.goal = SelectionGoal::None;
7874 }
7875 let (cursor, goal) = movement::down(
7876 map,
7877 selection.end,
7878 selection.goal,
7879 false,
7880 text_layout_details,
7881 );
7882 selection.collapse_to(cursor, goal);
7883 });
7884 });
7885
7886 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7887 {
7888 cx.propagate();
7889 }
7890 }
7891
7892 pub fn select_page_down(&mut self, _: &SelectPageDown, cx: &mut ViewContext<Self>) {
7893 let Some(row_count) = self.visible_row_count() else {
7894 return;
7895 };
7896
7897 let text_layout_details = &self.text_layout_details(cx);
7898
7899 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7900 s.move_heads_with(|map, head, goal| {
7901 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
7902 })
7903 })
7904 }
7905
7906 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
7907 if self.take_rename(true, cx).is_some() {
7908 return;
7909 }
7910
7911 if self
7912 .context_menu
7913 .write()
7914 .as_mut()
7915 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
7916 .unwrap_or(false)
7917 {
7918 return;
7919 }
7920
7921 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7922 cx.propagate();
7923 return;
7924 }
7925
7926 let Some(row_count) = self.visible_row_count() else {
7927 return;
7928 };
7929
7930 let autoscroll = if action.center_cursor {
7931 Autoscroll::center()
7932 } else {
7933 Autoscroll::fit()
7934 };
7935
7936 let text_layout_details = &self.text_layout_details(cx);
7937 self.change_selections(Some(autoscroll), cx, |s| {
7938 let line_mode = s.line_mode;
7939 s.move_with(|map, selection| {
7940 if !selection.is_empty() && !line_mode {
7941 selection.goal = SelectionGoal::None;
7942 }
7943 let (cursor, goal) = movement::down_by_rows(
7944 map,
7945 selection.end,
7946 row_count,
7947 selection.goal,
7948 false,
7949 text_layout_details,
7950 );
7951 selection.collapse_to(cursor, goal);
7952 });
7953 });
7954 }
7955
7956 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
7957 let text_layout_details = &self.text_layout_details(cx);
7958 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7959 s.move_heads_with(|map, head, goal| {
7960 movement::down(map, head, goal, false, text_layout_details)
7961 })
7962 });
7963 }
7964
7965 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
7966 if let Some(context_menu) = self.context_menu.write().as_mut() {
7967 context_menu.select_first(self.completion_provider.as_deref(), cx);
7968 }
7969 }
7970
7971 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
7972 if let Some(context_menu) = self.context_menu.write().as_mut() {
7973 context_menu.select_prev(self.completion_provider.as_deref(), cx);
7974 }
7975 }
7976
7977 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
7978 if let Some(context_menu) = self.context_menu.write().as_mut() {
7979 context_menu.select_next(self.completion_provider.as_deref(), cx);
7980 }
7981 }
7982
7983 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
7984 if let Some(context_menu) = self.context_menu.write().as_mut() {
7985 context_menu.select_last(self.completion_provider.as_deref(), cx);
7986 }
7987 }
7988
7989 pub fn move_to_previous_word_start(
7990 &mut self,
7991 _: &MoveToPreviousWordStart,
7992 cx: &mut ViewContext<Self>,
7993 ) {
7994 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7995 s.move_cursors_with(|map, head, _| {
7996 (
7997 movement::previous_word_start(map, head),
7998 SelectionGoal::None,
7999 )
8000 });
8001 })
8002 }
8003
8004 pub fn move_to_previous_subword_start(
8005 &mut self,
8006 _: &MoveToPreviousSubwordStart,
8007 cx: &mut ViewContext<Self>,
8008 ) {
8009 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8010 s.move_cursors_with(|map, head, _| {
8011 (
8012 movement::previous_subword_start(map, head),
8013 SelectionGoal::None,
8014 )
8015 });
8016 })
8017 }
8018
8019 pub fn select_to_previous_word_start(
8020 &mut self,
8021 _: &SelectToPreviousWordStart,
8022 cx: &mut ViewContext<Self>,
8023 ) {
8024 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8025 s.move_heads_with(|map, head, _| {
8026 (
8027 movement::previous_word_start(map, head),
8028 SelectionGoal::None,
8029 )
8030 });
8031 })
8032 }
8033
8034 pub fn select_to_previous_subword_start(
8035 &mut self,
8036 _: &SelectToPreviousSubwordStart,
8037 cx: &mut ViewContext<Self>,
8038 ) {
8039 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8040 s.move_heads_with(|map, head, _| {
8041 (
8042 movement::previous_subword_start(map, head),
8043 SelectionGoal::None,
8044 )
8045 });
8046 })
8047 }
8048
8049 pub fn delete_to_previous_word_start(
8050 &mut self,
8051 action: &DeleteToPreviousWordStart,
8052 cx: &mut ViewContext<Self>,
8053 ) {
8054 self.transact(cx, |this, cx| {
8055 this.select_autoclose_pair(cx);
8056 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8057 let line_mode = s.line_mode;
8058 s.move_with(|map, selection| {
8059 if selection.is_empty() && !line_mode {
8060 let cursor = if action.ignore_newlines {
8061 movement::previous_word_start(map, selection.head())
8062 } else {
8063 movement::previous_word_start_or_newline(map, selection.head())
8064 };
8065 selection.set_head(cursor, SelectionGoal::None);
8066 }
8067 });
8068 });
8069 this.insert("", cx);
8070 });
8071 }
8072
8073 pub fn delete_to_previous_subword_start(
8074 &mut self,
8075 _: &DeleteToPreviousSubwordStart,
8076 cx: &mut ViewContext<Self>,
8077 ) {
8078 self.transact(cx, |this, cx| {
8079 this.select_autoclose_pair(cx);
8080 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8081 let line_mode = s.line_mode;
8082 s.move_with(|map, selection| {
8083 if selection.is_empty() && !line_mode {
8084 let cursor = movement::previous_subword_start(map, selection.head());
8085 selection.set_head(cursor, SelectionGoal::None);
8086 }
8087 });
8088 });
8089 this.insert("", cx);
8090 });
8091 }
8092
8093 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
8094 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8095 s.move_cursors_with(|map, head, _| {
8096 (movement::next_word_end(map, head), SelectionGoal::None)
8097 });
8098 })
8099 }
8100
8101 pub fn move_to_next_subword_end(
8102 &mut self,
8103 _: &MoveToNextSubwordEnd,
8104 cx: &mut ViewContext<Self>,
8105 ) {
8106 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8107 s.move_cursors_with(|map, head, _| {
8108 (movement::next_subword_end(map, head), SelectionGoal::None)
8109 });
8110 })
8111 }
8112
8113 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
8114 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8115 s.move_heads_with(|map, head, _| {
8116 (movement::next_word_end(map, head), SelectionGoal::None)
8117 });
8118 })
8119 }
8120
8121 pub fn select_to_next_subword_end(
8122 &mut self,
8123 _: &SelectToNextSubwordEnd,
8124 cx: &mut ViewContext<Self>,
8125 ) {
8126 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8127 s.move_heads_with(|map, head, _| {
8128 (movement::next_subword_end(map, head), SelectionGoal::None)
8129 });
8130 })
8131 }
8132
8133 pub fn delete_to_next_word_end(
8134 &mut self,
8135 action: &DeleteToNextWordEnd,
8136 cx: &mut ViewContext<Self>,
8137 ) {
8138 self.transact(cx, |this, cx| {
8139 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8140 let line_mode = s.line_mode;
8141 s.move_with(|map, selection| {
8142 if selection.is_empty() && !line_mode {
8143 let cursor = if action.ignore_newlines {
8144 movement::next_word_end(map, selection.head())
8145 } else {
8146 movement::next_word_end_or_newline(map, selection.head())
8147 };
8148 selection.set_head(cursor, SelectionGoal::None);
8149 }
8150 });
8151 });
8152 this.insert("", cx);
8153 });
8154 }
8155
8156 pub fn delete_to_next_subword_end(
8157 &mut self,
8158 _: &DeleteToNextSubwordEnd,
8159 cx: &mut ViewContext<Self>,
8160 ) {
8161 self.transact(cx, |this, cx| {
8162 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8163 s.move_with(|map, selection| {
8164 if selection.is_empty() {
8165 let cursor = movement::next_subword_end(map, selection.head());
8166 selection.set_head(cursor, SelectionGoal::None);
8167 }
8168 });
8169 });
8170 this.insert("", cx);
8171 });
8172 }
8173
8174 pub fn move_to_beginning_of_line(
8175 &mut self,
8176 action: &MoveToBeginningOfLine,
8177 cx: &mut ViewContext<Self>,
8178 ) {
8179 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8180 s.move_cursors_with(|map, head, _| {
8181 (
8182 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
8183 SelectionGoal::None,
8184 )
8185 });
8186 })
8187 }
8188
8189 pub fn select_to_beginning_of_line(
8190 &mut self,
8191 action: &SelectToBeginningOfLine,
8192 cx: &mut ViewContext<Self>,
8193 ) {
8194 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8195 s.move_heads_with(|map, head, _| {
8196 (
8197 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
8198 SelectionGoal::None,
8199 )
8200 });
8201 });
8202 }
8203
8204 pub fn delete_to_beginning_of_line(
8205 &mut self,
8206 _: &DeleteToBeginningOfLine,
8207 cx: &mut ViewContext<Self>,
8208 ) {
8209 self.transact(cx, |this, cx| {
8210 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8211 s.move_with(|_, selection| {
8212 selection.reversed = true;
8213 });
8214 });
8215
8216 this.select_to_beginning_of_line(
8217 &SelectToBeginningOfLine {
8218 stop_at_soft_wraps: false,
8219 },
8220 cx,
8221 );
8222 this.backspace(&Backspace, cx);
8223 });
8224 }
8225
8226 pub fn move_to_end_of_line(&mut self, action: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
8227 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8228 s.move_cursors_with(|map, head, _| {
8229 (
8230 movement::line_end(map, head, action.stop_at_soft_wraps),
8231 SelectionGoal::None,
8232 )
8233 });
8234 })
8235 }
8236
8237 pub fn select_to_end_of_line(
8238 &mut self,
8239 action: &SelectToEndOfLine,
8240 cx: &mut ViewContext<Self>,
8241 ) {
8242 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8243 s.move_heads_with(|map, head, _| {
8244 (
8245 movement::line_end(map, head, action.stop_at_soft_wraps),
8246 SelectionGoal::None,
8247 )
8248 });
8249 })
8250 }
8251
8252 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
8253 self.transact(cx, |this, cx| {
8254 this.select_to_end_of_line(
8255 &SelectToEndOfLine {
8256 stop_at_soft_wraps: false,
8257 },
8258 cx,
8259 );
8260 this.delete(&Delete, cx);
8261 });
8262 }
8263
8264 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
8265 self.transact(cx, |this, cx| {
8266 this.select_to_end_of_line(
8267 &SelectToEndOfLine {
8268 stop_at_soft_wraps: false,
8269 },
8270 cx,
8271 );
8272 this.cut(&Cut, cx);
8273 });
8274 }
8275
8276 pub fn move_to_start_of_paragraph(
8277 &mut self,
8278 _: &MoveToStartOfParagraph,
8279 cx: &mut ViewContext<Self>,
8280 ) {
8281 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8282 cx.propagate();
8283 return;
8284 }
8285
8286 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8287 s.move_with(|map, selection| {
8288 selection.collapse_to(
8289 movement::start_of_paragraph(map, selection.head(), 1),
8290 SelectionGoal::None,
8291 )
8292 });
8293 })
8294 }
8295
8296 pub fn move_to_end_of_paragraph(
8297 &mut self,
8298 _: &MoveToEndOfParagraph,
8299 cx: &mut ViewContext<Self>,
8300 ) {
8301 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8302 cx.propagate();
8303 return;
8304 }
8305
8306 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8307 s.move_with(|map, selection| {
8308 selection.collapse_to(
8309 movement::end_of_paragraph(map, selection.head(), 1),
8310 SelectionGoal::None,
8311 )
8312 });
8313 })
8314 }
8315
8316 pub fn select_to_start_of_paragraph(
8317 &mut self,
8318 _: &SelectToStartOfParagraph,
8319 cx: &mut ViewContext<Self>,
8320 ) {
8321 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8322 cx.propagate();
8323 return;
8324 }
8325
8326 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8327 s.move_heads_with(|map, head, _| {
8328 (
8329 movement::start_of_paragraph(map, head, 1),
8330 SelectionGoal::None,
8331 )
8332 });
8333 })
8334 }
8335
8336 pub fn select_to_end_of_paragraph(
8337 &mut self,
8338 _: &SelectToEndOfParagraph,
8339 cx: &mut ViewContext<Self>,
8340 ) {
8341 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8342 cx.propagate();
8343 return;
8344 }
8345
8346 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8347 s.move_heads_with(|map, head, _| {
8348 (
8349 movement::end_of_paragraph(map, head, 1),
8350 SelectionGoal::None,
8351 )
8352 });
8353 })
8354 }
8355
8356 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
8357 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8358 cx.propagate();
8359 return;
8360 }
8361
8362 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8363 s.select_ranges(vec![0..0]);
8364 });
8365 }
8366
8367 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
8368 let mut selection = self.selections.last::<Point>(cx);
8369 selection.set_head(Point::zero(), SelectionGoal::None);
8370
8371 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8372 s.select(vec![selection]);
8373 });
8374 }
8375
8376 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
8377 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8378 cx.propagate();
8379 return;
8380 }
8381
8382 let cursor = self.buffer.read(cx).read(cx).len();
8383 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8384 s.select_ranges(vec![cursor..cursor])
8385 });
8386 }
8387
8388 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
8389 self.nav_history = nav_history;
8390 }
8391
8392 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
8393 self.nav_history.as_ref()
8394 }
8395
8396 fn push_to_nav_history(
8397 &mut self,
8398 cursor_anchor: Anchor,
8399 new_position: Option<Point>,
8400 cx: &mut ViewContext<Self>,
8401 ) {
8402 if let Some(nav_history) = self.nav_history.as_mut() {
8403 let buffer = self.buffer.read(cx).read(cx);
8404 let cursor_position = cursor_anchor.to_point(&buffer);
8405 let scroll_state = self.scroll_manager.anchor();
8406 let scroll_top_row = scroll_state.top_row(&buffer);
8407 drop(buffer);
8408
8409 if let Some(new_position) = new_position {
8410 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
8411 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
8412 return;
8413 }
8414 }
8415
8416 nav_history.push(
8417 Some(NavigationData {
8418 cursor_anchor,
8419 cursor_position,
8420 scroll_anchor: scroll_state,
8421 scroll_top_row,
8422 }),
8423 cx,
8424 );
8425 }
8426 }
8427
8428 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
8429 let buffer = self.buffer.read(cx).snapshot(cx);
8430 let mut selection = self.selections.first::<usize>(cx);
8431 selection.set_head(buffer.len(), SelectionGoal::None);
8432 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8433 s.select(vec![selection]);
8434 });
8435 }
8436
8437 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
8438 let end = self.buffer.read(cx).read(cx).len();
8439 self.change_selections(None, cx, |s| {
8440 s.select_ranges(vec![0..end]);
8441 });
8442 }
8443
8444 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
8445 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8446 let mut selections = self.selections.all::<Point>(cx);
8447 let max_point = display_map.buffer_snapshot.max_point();
8448 for selection in &mut selections {
8449 let rows = selection.spanned_rows(true, &display_map);
8450 selection.start = Point::new(rows.start.0, 0);
8451 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
8452 selection.reversed = false;
8453 }
8454 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8455 s.select(selections);
8456 });
8457 }
8458
8459 pub fn split_selection_into_lines(
8460 &mut self,
8461 _: &SplitSelectionIntoLines,
8462 cx: &mut ViewContext<Self>,
8463 ) {
8464 let mut to_unfold = Vec::new();
8465 let mut new_selection_ranges = Vec::new();
8466 {
8467 let selections = self.selections.all::<Point>(cx);
8468 let buffer = self.buffer.read(cx).read(cx);
8469 for selection in selections {
8470 for row in selection.start.row..selection.end.row {
8471 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
8472 new_selection_ranges.push(cursor..cursor);
8473 }
8474 new_selection_ranges.push(selection.end..selection.end);
8475 to_unfold.push(selection.start..selection.end);
8476 }
8477 }
8478 self.unfold_ranges(&to_unfold, true, true, cx);
8479 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8480 s.select_ranges(new_selection_ranges);
8481 });
8482 }
8483
8484 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
8485 self.add_selection(true, cx);
8486 }
8487
8488 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
8489 self.add_selection(false, cx);
8490 }
8491
8492 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
8493 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8494 let mut selections = self.selections.all::<Point>(cx);
8495 let text_layout_details = self.text_layout_details(cx);
8496 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
8497 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
8498 let range = oldest_selection.display_range(&display_map).sorted();
8499
8500 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
8501 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
8502 let positions = start_x.min(end_x)..start_x.max(end_x);
8503
8504 selections.clear();
8505 let mut stack = Vec::new();
8506 for row in range.start.row().0..=range.end.row().0 {
8507 if let Some(selection) = self.selections.build_columnar_selection(
8508 &display_map,
8509 DisplayRow(row),
8510 &positions,
8511 oldest_selection.reversed,
8512 &text_layout_details,
8513 ) {
8514 stack.push(selection.id);
8515 selections.push(selection);
8516 }
8517 }
8518
8519 if above {
8520 stack.reverse();
8521 }
8522
8523 AddSelectionsState { above, stack }
8524 });
8525
8526 let last_added_selection = *state.stack.last().unwrap();
8527 let mut new_selections = Vec::new();
8528 if above == state.above {
8529 let end_row = if above {
8530 DisplayRow(0)
8531 } else {
8532 display_map.max_point().row()
8533 };
8534
8535 'outer: for selection in selections {
8536 if selection.id == last_added_selection {
8537 let range = selection.display_range(&display_map).sorted();
8538 debug_assert_eq!(range.start.row(), range.end.row());
8539 let mut row = range.start.row();
8540 let positions =
8541 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
8542 px(start)..px(end)
8543 } else {
8544 let start_x =
8545 display_map.x_for_display_point(range.start, &text_layout_details);
8546 let end_x =
8547 display_map.x_for_display_point(range.end, &text_layout_details);
8548 start_x.min(end_x)..start_x.max(end_x)
8549 };
8550
8551 while row != end_row {
8552 if above {
8553 row.0 -= 1;
8554 } else {
8555 row.0 += 1;
8556 }
8557
8558 if let Some(new_selection) = self.selections.build_columnar_selection(
8559 &display_map,
8560 row,
8561 &positions,
8562 selection.reversed,
8563 &text_layout_details,
8564 ) {
8565 state.stack.push(new_selection.id);
8566 if above {
8567 new_selections.push(new_selection);
8568 new_selections.push(selection);
8569 } else {
8570 new_selections.push(selection);
8571 new_selections.push(new_selection);
8572 }
8573
8574 continue 'outer;
8575 }
8576 }
8577 }
8578
8579 new_selections.push(selection);
8580 }
8581 } else {
8582 new_selections = selections;
8583 new_selections.retain(|s| s.id != last_added_selection);
8584 state.stack.pop();
8585 }
8586
8587 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8588 s.select(new_selections);
8589 });
8590 if state.stack.len() > 1 {
8591 self.add_selections_state = Some(state);
8592 }
8593 }
8594
8595 pub fn select_next_match_internal(
8596 &mut self,
8597 display_map: &DisplaySnapshot,
8598 replace_newest: bool,
8599 autoscroll: Option<Autoscroll>,
8600 cx: &mut ViewContext<Self>,
8601 ) -> Result<()> {
8602 fn select_next_match_ranges(
8603 this: &mut Editor,
8604 range: Range<usize>,
8605 replace_newest: bool,
8606 auto_scroll: Option<Autoscroll>,
8607 cx: &mut ViewContext<Editor>,
8608 ) {
8609 this.unfold_ranges(&[range.clone()], false, true, cx);
8610 this.change_selections(auto_scroll, cx, |s| {
8611 if replace_newest {
8612 s.delete(s.newest_anchor().id);
8613 }
8614 s.insert_range(range.clone());
8615 });
8616 }
8617
8618 let buffer = &display_map.buffer_snapshot;
8619 let mut selections = self.selections.all::<usize>(cx);
8620 if let Some(mut select_next_state) = self.select_next_state.take() {
8621 let query = &select_next_state.query;
8622 if !select_next_state.done {
8623 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
8624 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
8625 let mut next_selected_range = None;
8626
8627 let bytes_after_last_selection =
8628 buffer.bytes_in_range(last_selection.end..buffer.len());
8629 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
8630 let query_matches = query
8631 .stream_find_iter(bytes_after_last_selection)
8632 .map(|result| (last_selection.end, result))
8633 .chain(
8634 query
8635 .stream_find_iter(bytes_before_first_selection)
8636 .map(|result| (0, result)),
8637 );
8638
8639 for (start_offset, query_match) in query_matches {
8640 let query_match = query_match.unwrap(); // can only fail due to I/O
8641 let offset_range =
8642 start_offset + query_match.start()..start_offset + query_match.end();
8643 let display_range = offset_range.start.to_display_point(display_map)
8644 ..offset_range.end.to_display_point(display_map);
8645
8646 if !select_next_state.wordwise
8647 || (!movement::is_inside_word(display_map, display_range.start)
8648 && !movement::is_inside_word(display_map, display_range.end))
8649 {
8650 // TODO: This is n^2, because we might check all the selections
8651 if !selections
8652 .iter()
8653 .any(|selection| selection.range().overlaps(&offset_range))
8654 {
8655 next_selected_range = Some(offset_range);
8656 break;
8657 }
8658 }
8659 }
8660
8661 if let Some(next_selected_range) = next_selected_range {
8662 select_next_match_ranges(
8663 self,
8664 next_selected_range,
8665 replace_newest,
8666 autoscroll,
8667 cx,
8668 );
8669 } else {
8670 select_next_state.done = true;
8671 }
8672 }
8673
8674 self.select_next_state = Some(select_next_state);
8675 } else {
8676 let mut only_carets = true;
8677 let mut same_text_selected = true;
8678 let mut selected_text = None;
8679
8680 let mut selections_iter = selections.iter().peekable();
8681 while let Some(selection) = selections_iter.next() {
8682 if selection.start != selection.end {
8683 only_carets = false;
8684 }
8685
8686 if same_text_selected {
8687 if selected_text.is_none() {
8688 selected_text =
8689 Some(buffer.text_for_range(selection.range()).collect::<String>());
8690 }
8691
8692 if let Some(next_selection) = selections_iter.peek() {
8693 if next_selection.range().len() == selection.range().len() {
8694 let next_selected_text = buffer
8695 .text_for_range(next_selection.range())
8696 .collect::<String>();
8697 if Some(next_selected_text) != selected_text {
8698 same_text_selected = false;
8699 selected_text = None;
8700 }
8701 } else {
8702 same_text_selected = false;
8703 selected_text = None;
8704 }
8705 }
8706 }
8707 }
8708
8709 if only_carets {
8710 for selection in &mut selections {
8711 let word_range = movement::surrounding_word(
8712 display_map,
8713 selection.start.to_display_point(display_map),
8714 );
8715 selection.start = word_range.start.to_offset(display_map, Bias::Left);
8716 selection.end = word_range.end.to_offset(display_map, Bias::Left);
8717 selection.goal = SelectionGoal::None;
8718 selection.reversed = false;
8719 select_next_match_ranges(
8720 self,
8721 selection.start..selection.end,
8722 replace_newest,
8723 autoscroll,
8724 cx,
8725 );
8726 }
8727
8728 if selections.len() == 1 {
8729 let selection = selections
8730 .last()
8731 .expect("ensured that there's only one selection");
8732 let query = buffer
8733 .text_for_range(selection.start..selection.end)
8734 .collect::<String>();
8735 let is_empty = query.is_empty();
8736 let select_state = SelectNextState {
8737 query: AhoCorasick::new(&[query])?,
8738 wordwise: true,
8739 done: is_empty,
8740 };
8741 self.select_next_state = Some(select_state);
8742 } else {
8743 self.select_next_state = None;
8744 }
8745 } else if let Some(selected_text) = selected_text {
8746 self.select_next_state = Some(SelectNextState {
8747 query: AhoCorasick::new(&[selected_text])?,
8748 wordwise: false,
8749 done: false,
8750 });
8751 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
8752 }
8753 }
8754 Ok(())
8755 }
8756
8757 pub fn select_all_matches(
8758 &mut self,
8759 _action: &SelectAllMatches,
8760 cx: &mut ViewContext<Self>,
8761 ) -> Result<()> {
8762 self.push_to_selection_history();
8763 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8764
8765 self.select_next_match_internal(&display_map, false, None, cx)?;
8766 let Some(select_next_state) = self.select_next_state.as_mut() else {
8767 return Ok(());
8768 };
8769 if select_next_state.done {
8770 return Ok(());
8771 }
8772
8773 let mut new_selections = self.selections.all::<usize>(cx);
8774
8775 let buffer = &display_map.buffer_snapshot;
8776 let query_matches = select_next_state
8777 .query
8778 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
8779
8780 for query_match in query_matches {
8781 let query_match = query_match.unwrap(); // can only fail due to I/O
8782 let offset_range = query_match.start()..query_match.end();
8783 let display_range = offset_range.start.to_display_point(&display_map)
8784 ..offset_range.end.to_display_point(&display_map);
8785
8786 if !select_next_state.wordwise
8787 || (!movement::is_inside_word(&display_map, display_range.start)
8788 && !movement::is_inside_word(&display_map, display_range.end))
8789 {
8790 self.selections.change_with(cx, |selections| {
8791 new_selections.push(Selection {
8792 id: selections.new_selection_id(),
8793 start: offset_range.start,
8794 end: offset_range.end,
8795 reversed: false,
8796 goal: SelectionGoal::None,
8797 });
8798 });
8799 }
8800 }
8801
8802 new_selections.sort_by_key(|selection| selection.start);
8803 let mut ix = 0;
8804 while ix + 1 < new_selections.len() {
8805 let current_selection = &new_selections[ix];
8806 let next_selection = &new_selections[ix + 1];
8807 if current_selection.range().overlaps(&next_selection.range()) {
8808 if current_selection.id < next_selection.id {
8809 new_selections.remove(ix + 1);
8810 } else {
8811 new_selections.remove(ix);
8812 }
8813 } else {
8814 ix += 1;
8815 }
8816 }
8817
8818 select_next_state.done = true;
8819 self.unfold_ranges(
8820 &new_selections
8821 .iter()
8822 .map(|selection| selection.range())
8823 .collect::<Vec<_>>(),
8824 false,
8825 false,
8826 cx,
8827 );
8828 self.change_selections(Some(Autoscroll::fit()), cx, |selections| {
8829 selections.select(new_selections)
8830 });
8831
8832 Ok(())
8833 }
8834
8835 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
8836 self.push_to_selection_history();
8837 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8838 self.select_next_match_internal(
8839 &display_map,
8840 action.replace_newest,
8841 Some(Autoscroll::newest()),
8842 cx,
8843 )?;
8844 Ok(())
8845 }
8846
8847 pub fn select_previous(
8848 &mut self,
8849 action: &SelectPrevious,
8850 cx: &mut ViewContext<Self>,
8851 ) -> Result<()> {
8852 self.push_to_selection_history();
8853 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8854 let buffer = &display_map.buffer_snapshot;
8855 let mut selections = self.selections.all::<usize>(cx);
8856 if let Some(mut select_prev_state) = self.select_prev_state.take() {
8857 let query = &select_prev_state.query;
8858 if !select_prev_state.done {
8859 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
8860 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
8861 let mut next_selected_range = None;
8862 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
8863 let bytes_before_last_selection =
8864 buffer.reversed_bytes_in_range(0..last_selection.start);
8865 let bytes_after_first_selection =
8866 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
8867 let query_matches = query
8868 .stream_find_iter(bytes_before_last_selection)
8869 .map(|result| (last_selection.start, result))
8870 .chain(
8871 query
8872 .stream_find_iter(bytes_after_first_selection)
8873 .map(|result| (buffer.len(), result)),
8874 );
8875 for (end_offset, query_match) in query_matches {
8876 let query_match = query_match.unwrap(); // can only fail due to I/O
8877 let offset_range =
8878 end_offset - query_match.end()..end_offset - query_match.start();
8879 let display_range = offset_range.start.to_display_point(&display_map)
8880 ..offset_range.end.to_display_point(&display_map);
8881
8882 if !select_prev_state.wordwise
8883 || (!movement::is_inside_word(&display_map, display_range.start)
8884 && !movement::is_inside_word(&display_map, display_range.end))
8885 {
8886 next_selected_range = Some(offset_range);
8887 break;
8888 }
8889 }
8890
8891 if let Some(next_selected_range) = next_selected_range {
8892 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
8893 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8894 if action.replace_newest {
8895 s.delete(s.newest_anchor().id);
8896 }
8897 s.insert_range(next_selected_range);
8898 });
8899 } else {
8900 select_prev_state.done = true;
8901 }
8902 }
8903
8904 self.select_prev_state = Some(select_prev_state);
8905 } else {
8906 let mut only_carets = true;
8907 let mut same_text_selected = true;
8908 let mut selected_text = None;
8909
8910 let mut selections_iter = selections.iter().peekable();
8911 while let Some(selection) = selections_iter.next() {
8912 if selection.start != selection.end {
8913 only_carets = false;
8914 }
8915
8916 if same_text_selected {
8917 if selected_text.is_none() {
8918 selected_text =
8919 Some(buffer.text_for_range(selection.range()).collect::<String>());
8920 }
8921
8922 if let Some(next_selection) = selections_iter.peek() {
8923 if next_selection.range().len() == selection.range().len() {
8924 let next_selected_text = buffer
8925 .text_for_range(next_selection.range())
8926 .collect::<String>();
8927 if Some(next_selected_text) != selected_text {
8928 same_text_selected = false;
8929 selected_text = None;
8930 }
8931 } else {
8932 same_text_selected = false;
8933 selected_text = None;
8934 }
8935 }
8936 }
8937 }
8938
8939 if only_carets {
8940 for selection in &mut selections {
8941 let word_range = movement::surrounding_word(
8942 &display_map,
8943 selection.start.to_display_point(&display_map),
8944 );
8945 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
8946 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
8947 selection.goal = SelectionGoal::None;
8948 selection.reversed = false;
8949 }
8950 if selections.len() == 1 {
8951 let selection = selections
8952 .last()
8953 .expect("ensured that there's only one selection");
8954 let query = buffer
8955 .text_for_range(selection.start..selection.end)
8956 .collect::<String>();
8957 let is_empty = query.is_empty();
8958 let select_state = SelectNextState {
8959 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
8960 wordwise: true,
8961 done: is_empty,
8962 };
8963 self.select_prev_state = Some(select_state);
8964 } else {
8965 self.select_prev_state = None;
8966 }
8967
8968 self.unfold_ranges(
8969 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
8970 false,
8971 true,
8972 cx,
8973 );
8974 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8975 s.select(selections);
8976 });
8977 } else if let Some(selected_text) = selected_text {
8978 self.select_prev_state = Some(SelectNextState {
8979 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
8980 wordwise: false,
8981 done: false,
8982 });
8983 self.select_previous(action, cx)?;
8984 }
8985 }
8986 Ok(())
8987 }
8988
8989 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
8990 if self.read_only(cx) {
8991 return;
8992 }
8993 let text_layout_details = &self.text_layout_details(cx);
8994 self.transact(cx, |this, cx| {
8995 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
8996 let mut edits = Vec::new();
8997 let mut selection_edit_ranges = Vec::new();
8998 let mut last_toggled_row = None;
8999 let snapshot = this.buffer.read(cx).read(cx);
9000 let empty_str: Arc<str> = Arc::default();
9001 let mut suffixes_inserted = Vec::new();
9002 let ignore_indent = action.ignore_indent;
9003
9004 fn comment_prefix_range(
9005 snapshot: &MultiBufferSnapshot,
9006 row: MultiBufferRow,
9007 comment_prefix: &str,
9008 comment_prefix_whitespace: &str,
9009 ignore_indent: bool,
9010 ) -> Range<Point> {
9011 let indent_size = if ignore_indent {
9012 0
9013 } else {
9014 snapshot.indent_size_for_line(row).len
9015 };
9016
9017 let start = Point::new(row.0, indent_size);
9018
9019 let mut line_bytes = snapshot
9020 .bytes_in_range(start..snapshot.max_point())
9021 .flatten()
9022 .copied();
9023
9024 // If this line currently begins with the line comment prefix, then record
9025 // the range containing the prefix.
9026 if line_bytes
9027 .by_ref()
9028 .take(comment_prefix.len())
9029 .eq(comment_prefix.bytes())
9030 {
9031 // Include any whitespace that matches the comment prefix.
9032 let matching_whitespace_len = line_bytes
9033 .zip(comment_prefix_whitespace.bytes())
9034 .take_while(|(a, b)| a == b)
9035 .count() as u32;
9036 let end = Point::new(
9037 start.row,
9038 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
9039 );
9040 start..end
9041 } else {
9042 start..start
9043 }
9044 }
9045
9046 fn comment_suffix_range(
9047 snapshot: &MultiBufferSnapshot,
9048 row: MultiBufferRow,
9049 comment_suffix: &str,
9050 comment_suffix_has_leading_space: bool,
9051 ) -> Range<Point> {
9052 let end = Point::new(row.0, snapshot.line_len(row));
9053 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
9054
9055 let mut line_end_bytes = snapshot
9056 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
9057 .flatten()
9058 .copied();
9059
9060 let leading_space_len = if suffix_start_column > 0
9061 && line_end_bytes.next() == Some(b' ')
9062 && comment_suffix_has_leading_space
9063 {
9064 1
9065 } else {
9066 0
9067 };
9068
9069 // If this line currently begins with the line comment prefix, then record
9070 // the range containing the prefix.
9071 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
9072 let start = Point::new(end.row, suffix_start_column - leading_space_len);
9073 start..end
9074 } else {
9075 end..end
9076 }
9077 }
9078
9079 // TODO: Handle selections that cross excerpts
9080 for selection in &mut selections {
9081 let start_column = snapshot
9082 .indent_size_for_line(MultiBufferRow(selection.start.row))
9083 .len;
9084 let language = if let Some(language) =
9085 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
9086 {
9087 language
9088 } else {
9089 continue;
9090 };
9091
9092 selection_edit_ranges.clear();
9093
9094 // If multiple selections contain a given row, avoid processing that
9095 // row more than once.
9096 let mut start_row = MultiBufferRow(selection.start.row);
9097 if last_toggled_row == Some(start_row) {
9098 start_row = start_row.next_row();
9099 }
9100 let end_row =
9101 if selection.end.row > selection.start.row && selection.end.column == 0 {
9102 MultiBufferRow(selection.end.row - 1)
9103 } else {
9104 MultiBufferRow(selection.end.row)
9105 };
9106 last_toggled_row = Some(end_row);
9107
9108 if start_row > end_row {
9109 continue;
9110 }
9111
9112 // If the language has line comments, toggle those.
9113 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
9114
9115 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
9116 if ignore_indent {
9117 full_comment_prefixes = full_comment_prefixes
9118 .into_iter()
9119 .map(|s| Arc::from(s.trim_end()))
9120 .collect();
9121 }
9122
9123 if !full_comment_prefixes.is_empty() {
9124 let first_prefix = full_comment_prefixes
9125 .first()
9126 .expect("prefixes is non-empty");
9127 let prefix_trimmed_lengths = full_comment_prefixes
9128 .iter()
9129 .map(|p| p.trim_end_matches(' ').len())
9130 .collect::<SmallVec<[usize; 4]>>();
9131
9132 let mut all_selection_lines_are_comments = true;
9133
9134 for row in start_row.0..=end_row.0 {
9135 let row = MultiBufferRow(row);
9136 if start_row < end_row && snapshot.is_line_blank(row) {
9137 continue;
9138 }
9139
9140 let prefix_range = full_comment_prefixes
9141 .iter()
9142 .zip(prefix_trimmed_lengths.iter().copied())
9143 .map(|(prefix, trimmed_prefix_len)| {
9144 comment_prefix_range(
9145 snapshot.deref(),
9146 row,
9147 &prefix[..trimmed_prefix_len],
9148 &prefix[trimmed_prefix_len..],
9149 ignore_indent,
9150 )
9151 })
9152 .max_by_key(|range| range.end.column - range.start.column)
9153 .expect("prefixes is non-empty");
9154
9155 if prefix_range.is_empty() {
9156 all_selection_lines_are_comments = false;
9157 }
9158
9159 selection_edit_ranges.push(prefix_range);
9160 }
9161
9162 if all_selection_lines_are_comments {
9163 edits.extend(
9164 selection_edit_ranges
9165 .iter()
9166 .cloned()
9167 .map(|range| (range, empty_str.clone())),
9168 );
9169 } else {
9170 let min_column = selection_edit_ranges
9171 .iter()
9172 .map(|range| range.start.column)
9173 .min()
9174 .unwrap_or(0);
9175 edits.extend(selection_edit_ranges.iter().map(|range| {
9176 let position = Point::new(range.start.row, min_column);
9177 (position..position, first_prefix.clone())
9178 }));
9179 }
9180 } else if let Some((full_comment_prefix, comment_suffix)) =
9181 language.block_comment_delimiters()
9182 {
9183 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
9184 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
9185 let prefix_range = comment_prefix_range(
9186 snapshot.deref(),
9187 start_row,
9188 comment_prefix,
9189 comment_prefix_whitespace,
9190 ignore_indent,
9191 );
9192 let suffix_range = comment_suffix_range(
9193 snapshot.deref(),
9194 end_row,
9195 comment_suffix.trim_start_matches(' '),
9196 comment_suffix.starts_with(' '),
9197 );
9198
9199 if prefix_range.is_empty() || suffix_range.is_empty() {
9200 edits.push((
9201 prefix_range.start..prefix_range.start,
9202 full_comment_prefix.clone(),
9203 ));
9204 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
9205 suffixes_inserted.push((end_row, comment_suffix.len()));
9206 } else {
9207 edits.push((prefix_range, empty_str.clone()));
9208 edits.push((suffix_range, empty_str.clone()));
9209 }
9210 } else {
9211 continue;
9212 }
9213 }
9214
9215 drop(snapshot);
9216 this.buffer.update(cx, |buffer, cx| {
9217 buffer.edit(edits, None, cx);
9218 });
9219
9220 // Adjust selections so that they end before any comment suffixes that
9221 // were inserted.
9222 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
9223 let mut selections = this.selections.all::<Point>(cx);
9224 let snapshot = this.buffer.read(cx).read(cx);
9225 for selection in &mut selections {
9226 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
9227 match row.cmp(&MultiBufferRow(selection.end.row)) {
9228 Ordering::Less => {
9229 suffixes_inserted.next();
9230 continue;
9231 }
9232 Ordering::Greater => break,
9233 Ordering::Equal => {
9234 if selection.end.column == snapshot.line_len(row) {
9235 if selection.is_empty() {
9236 selection.start.column -= suffix_len as u32;
9237 }
9238 selection.end.column -= suffix_len as u32;
9239 }
9240 break;
9241 }
9242 }
9243 }
9244 }
9245
9246 drop(snapshot);
9247 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
9248
9249 let selections = this.selections.all::<Point>(cx);
9250 let selections_on_single_row = selections.windows(2).all(|selections| {
9251 selections[0].start.row == selections[1].start.row
9252 && selections[0].end.row == selections[1].end.row
9253 && selections[0].start.row == selections[0].end.row
9254 });
9255 let selections_selecting = selections
9256 .iter()
9257 .any(|selection| selection.start != selection.end);
9258 let advance_downwards = action.advance_downwards
9259 && selections_on_single_row
9260 && !selections_selecting
9261 && !matches!(this.mode, EditorMode::SingleLine { .. });
9262
9263 if advance_downwards {
9264 let snapshot = this.buffer.read(cx).snapshot(cx);
9265
9266 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
9267 s.move_cursors_with(|display_snapshot, display_point, _| {
9268 let mut point = display_point.to_point(display_snapshot);
9269 point.row += 1;
9270 point = snapshot.clip_point(point, Bias::Left);
9271 let display_point = point.to_display_point(display_snapshot);
9272 let goal = SelectionGoal::HorizontalPosition(
9273 display_snapshot
9274 .x_for_display_point(display_point, text_layout_details)
9275 .into(),
9276 );
9277 (display_point, goal)
9278 })
9279 });
9280 }
9281 });
9282 }
9283
9284 pub fn select_enclosing_symbol(
9285 &mut self,
9286 _: &SelectEnclosingSymbol,
9287 cx: &mut ViewContext<Self>,
9288 ) {
9289 let buffer = self.buffer.read(cx).snapshot(cx);
9290 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
9291
9292 fn update_selection(
9293 selection: &Selection<usize>,
9294 buffer_snap: &MultiBufferSnapshot,
9295 ) -> Option<Selection<usize>> {
9296 let cursor = selection.head();
9297 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
9298 for symbol in symbols.iter().rev() {
9299 let start = symbol.range.start.to_offset(buffer_snap);
9300 let end = symbol.range.end.to_offset(buffer_snap);
9301 let new_range = start..end;
9302 if start < selection.start || end > selection.end {
9303 return Some(Selection {
9304 id: selection.id,
9305 start: new_range.start,
9306 end: new_range.end,
9307 goal: SelectionGoal::None,
9308 reversed: selection.reversed,
9309 });
9310 }
9311 }
9312 None
9313 }
9314
9315 let mut selected_larger_symbol = false;
9316 let new_selections = old_selections
9317 .iter()
9318 .map(|selection| match update_selection(selection, &buffer) {
9319 Some(new_selection) => {
9320 if new_selection.range() != selection.range() {
9321 selected_larger_symbol = true;
9322 }
9323 new_selection
9324 }
9325 None => selection.clone(),
9326 })
9327 .collect::<Vec<_>>();
9328
9329 if selected_larger_symbol {
9330 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9331 s.select(new_selections);
9332 });
9333 }
9334 }
9335
9336 pub fn select_larger_syntax_node(
9337 &mut self,
9338 _: &SelectLargerSyntaxNode,
9339 cx: &mut ViewContext<Self>,
9340 ) {
9341 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9342 let buffer = self.buffer.read(cx).snapshot(cx);
9343 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
9344
9345 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
9346 let mut selected_larger_node = false;
9347 let new_selections = old_selections
9348 .iter()
9349 .map(|selection| {
9350 let old_range = selection.start..selection.end;
9351 let mut new_range = old_range.clone();
9352 while let Some(containing_range) =
9353 buffer.range_for_syntax_ancestor(new_range.clone())
9354 {
9355 new_range = containing_range;
9356 if !display_map.intersects_fold(new_range.start)
9357 && !display_map.intersects_fold(new_range.end)
9358 {
9359 break;
9360 }
9361 }
9362
9363 selected_larger_node |= new_range != old_range;
9364 Selection {
9365 id: selection.id,
9366 start: new_range.start,
9367 end: new_range.end,
9368 goal: SelectionGoal::None,
9369 reversed: selection.reversed,
9370 }
9371 })
9372 .collect::<Vec<_>>();
9373
9374 if selected_larger_node {
9375 stack.push(old_selections);
9376 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9377 s.select(new_selections);
9378 });
9379 }
9380 self.select_larger_syntax_node_stack = stack;
9381 }
9382
9383 pub fn select_smaller_syntax_node(
9384 &mut self,
9385 _: &SelectSmallerSyntaxNode,
9386 cx: &mut ViewContext<Self>,
9387 ) {
9388 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
9389 if let Some(selections) = stack.pop() {
9390 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9391 s.select(selections.to_vec());
9392 });
9393 }
9394 self.select_larger_syntax_node_stack = stack;
9395 }
9396
9397 fn refresh_runnables(&mut self, cx: &mut ViewContext<Self>) -> Task<()> {
9398 if !EditorSettings::get_global(cx).gutter.runnables {
9399 self.clear_tasks();
9400 return Task::ready(());
9401 }
9402 let project = self.project.as_ref().map(Model::downgrade);
9403 cx.spawn(|this, mut cx| async move {
9404 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
9405 let Some(project) = project.and_then(|p| p.upgrade()) else {
9406 return;
9407 };
9408 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
9409 this.display_map.update(cx, |map, cx| map.snapshot(cx))
9410 }) else {
9411 return;
9412 };
9413
9414 let hide_runnables = project
9415 .update(&mut cx, |project, cx| {
9416 // Do not display any test indicators in non-dev server remote projects.
9417 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
9418 })
9419 .unwrap_or(true);
9420 if hide_runnables {
9421 return;
9422 }
9423 let new_rows =
9424 cx.background_executor()
9425 .spawn({
9426 let snapshot = display_snapshot.clone();
9427 async move {
9428 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
9429 }
9430 })
9431 .await;
9432 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
9433
9434 this.update(&mut cx, |this, _| {
9435 this.clear_tasks();
9436 for (key, value) in rows {
9437 this.insert_tasks(key, value);
9438 }
9439 })
9440 .ok();
9441 })
9442 }
9443 fn fetch_runnable_ranges(
9444 snapshot: &DisplaySnapshot,
9445 range: Range<Anchor>,
9446 ) -> Vec<language::RunnableRange> {
9447 snapshot.buffer_snapshot.runnable_ranges(range).collect()
9448 }
9449
9450 fn runnable_rows(
9451 project: Model<Project>,
9452 snapshot: DisplaySnapshot,
9453 runnable_ranges: Vec<RunnableRange>,
9454 mut cx: AsyncWindowContext,
9455 ) -> Vec<((BufferId, u32), RunnableTasks)> {
9456 runnable_ranges
9457 .into_iter()
9458 .filter_map(|mut runnable| {
9459 let tasks = cx
9460 .update(|cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
9461 .ok()?;
9462 if tasks.is_empty() {
9463 return None;
9464 }
9465
9466 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
9467
9468 let row = snapshot
9469 .buffer_snapshot
9470 .buffer_line_for_row(MultiBufferRow(point.row))?
9471 .1
9472 .start
9473 .row;
9474
9475 let context_range =
9476 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
9477 Some((
9478 (runnable.buffer_id, row),
9479 RunnableTasks {
9480 templates: tasks,
9481 offset: MultiBufferOffset(runnable.run_range.start),
9482 context_range,
9483 column: point.column,
9484 extra_variables: runnable.extra_captures,
9485 },
9486 ))
9487 })
9488 .collect()
9489 }
9490
9491 fn templates_with_tags(
9492 project: &Model<Project>,
9493 runnable: &mut Runnable,
9494 cx: &WindowContext<'_>,
9495 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
9496 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
9497 let (worktree_id, file) = project
9498 .buffer_for_id(runnable.buffer, cx)
9499 .and_then(|buffer| buffer.read(cx).file())
9500 .map(|file| (file.worktree_id(cx), file.clone()))
9501 .unzip();
9502
9503 (
9504 project.task_store().read(cx).task_inventory().cloned(),
9505 worktree_id,
9506 file,
9507 )
9508 });
9509
9510 let tags = mem::take(&mut runnable.tags);
9511 let mut tags: Vec<_> = tags
9512 .into_iter()
9513 .flat_map(|tag| {
9514 let tag = tag.0.clone();
9515 inventory
9516 .as_ref()
9517 .into_iter()
9518 .flat_map(|inventory| {
9519 inventory.read(cx).list_tasks(
9520 file.clone(),
9521 Some(runnable.language.clone()),
9522 worktree_id,
9523 cx,
9524 )
9525 })
9526 .filter(move |(_, template)| {
9527 template.tags.iter().any(|source_tag| source_tag == &tag)
9528 })
9529 })
9530 .sorted_by_key(|(kind, _)| kind.to_owned())
9531 .collect();
9532 if let Some((leading_tag_source, _)) = tags.first() {
9533 // Strongest source wins; if we have worktree tag binding, prefer that to
9534 // global and language bindings;
9535 // if we have a global binding, prefer that to language binding.
9536 let first_mismatch = tags
9537 .iter()
9538 .position(|(tag_source, _)| tag_source != leading_tag_source);
9539 if let Some(index) = first_mismatch {
9540 tags.truncate(index);
9541 }
9542 }
9543
9544 tags
9545 }
9546
9547 pub fn move_to_enclosing_bracket(
9548 &mut self,
9549 _: &MoveToEnclosingBracket,
9550 cx: &mut ViewContext<Self>,
9551 ) {
9552 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9553 s.move_offsets_with(|snapshot, selection| {
9554 let Some(enclosing_bracket_ranges) =
9555 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
9556 else {
9557 return;
9558 };
9559
9560 let mut best_length = usize::MAX;
9561 let mut best_inside = false;
9562 let mut best_in_bracket_range = false;
9563 let mut best_destination = None;
9564 for (open, close) in enclosing_bracket_ranges {
9565 let close = close.to_inclusive();
9566 let length = close.end() - open.start;
9567 let inside = selection.start >= open.end && selection.end <= *close.start();
9568 let in_bracket_range = open.to_inclusive().contains(&selection.head())
9569 || close.contains(&selection.head());
9570
9571 // If best is next to a bracket and current isn't, skip
9572 if !in_bracket_range && best_in_bracket_range {
9573 continue;
9574 }
9575
9576 // Prefer smaller lengths unless best is inside and current isn't
9577 if length > best_length && (best_inside || !inside) {
9578 continue;
9579 }
9580
9581 best_length = length;
9582 best_inside = inside;
9583 best_in_bracket_range = in_bracket_range;
9584 best_destination = Some(
9585 if close.contains(&selection.start) && close.contains(&selection.end) {
9586 if inside {
9587 open.end
9588 } else {
9589 open.start
9590 }
9591 } else if inside {
9592 *close.start()
9593 } else {
9594 *close.end()
9595 },
9596 );
9597 }
9598
9599 if let Some(destination) = best_destination {
9600 selection.collapse_to(destination, SelectionGoal::None);
9601 }
9602 })
9603 });
9604 }
9605
9606 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
9607 self.end_selection(cx);
9608 self.selection_history.mode = SelectionHistoryMode::Undoing;
9609 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
9610 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
9611 self.select_next_state = entry.select_next_state;
9612 self.select_prev_state = entry.select_prev_state;
9613 self.add_selections_state = entry.add_selections_state;
9614 self.request_autoscroll(Autoscroll::newest(), cx);
9615 }
9616 self.selection_history.mode = SelectionHistoryMode::Normal;
9617 }
9618
9619 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
9620 self.end_selection(cx);
9621 self.selection_history.mode = SelectionHistoryMode::Redoing;
9622 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
9623 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
9624 self.select_next_state = entry.select_next_state;
9625 self.select_prev_state = entry.select_prev_state;
9626 self.add_selections_state = entry.add_selections_state;
9627 self.request_autoscroll(Autoscroll::newest(), cx);
9628 }
9629 self.selection_history.mode = SelectionHistoryMode::Normal;
9630 }
9631
9632 pub fn expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
9633 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
9634 }
9635
9636 pub fn expand_excerpts_down(
9637 &mut self,
9638 action: &ExpandExcerptsDown,
9639 cx: &mut ViewContext<Self>,
9640 ) {
9641 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
9642 }
9643
9644 pub fn expand_excerpts_up(&mut self, action: &ExpandExcerptsUp, cx: &mut ViewContext<Self>) {
9645 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
9646 }
9647
9648 pub fn expand_excerpts_for_direction(
9649 &mut self,
9650 lines: u32,
9651 direction: ExpandExcerptDirection,
9652 cx: &mut ViewContext<Self>,
9653 ) {
9654 let selections = self.selections.disjoint_anchors();
9655
9656 let lines = if lines == 0 {
9657 EditorSettings::get_global(cx).expand_excerpt_lines
9658 } else {
9659 lines
9660 };
9661
9662 self.buffer.update(cx, |buffer, cx| {
9663 buffer.expand_excerpts(
9664 selections
9665 .iter()
9666 .map(|selection| selection.head().excerpt_id)
9667 .dedup(),
9668 lines,
9669 direction,
9670 cx,
9671 )
9672 })
9673 }
9674
9675 pub fn expand_excerpt(
9676 &mut self,
9677 excerpt: ExcerptId,
9678 direction: ExpandExcerptDirection,
9679 cx: &mut ViewContext<Self>,
9680 ) {
9681 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
9682 self.buffer.update(cx, |buffer, cx| {
9683 buffer.expand_excerpts([excerpt], lines, direction, cx)
9684 })
9685 }
9686
9687 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
9688 self.go_to_diagnostic_impl(Direction::Next, cx)
9689 }
9690
9691 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
9692 self.go_to_diagnostic_impl(Direction::Prev, cx)
9693 }
9694
9695 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
9696 let buffer = self.buffer.read(cx).snapshot(cx);
9697 let selection = self.selections.newest::<usize>(cx);
9698
9699 // If there is an active Diagnostic Popover jump to its diagnostic instead.
9700 if direction == Direction::Next {
9701 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
9702 let (group_id, jump_to) = popover.activation_info();
9703 if self.activate_diagnostics(group_id, cx) {
9704 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9705 let mut new_selection = s.newest_anchor().clone();
9706 new_selection.collapse_to(jump_to, SelectionGoal::None);
9707 s.select_anchors(vec![new_selection.clone()]);
9708 });
9709 }
9710 return;
9711 }
9712 }
9713
9714 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
9715 active_diagnostics
9716 .primary_range
9717 .to_offset(&buffer)
9718 .to_inclusive()
9719 });
9720 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
9721 if active_primary_range.contains(&selection.head()) {
9722 *active_primary_range.start()
9723 } else {
9724 selection.head()
9725 }
9726 } else {
9727 selection.head()
9728 };
9729 let snapshot = self.snapshot(cx);
9730 loop {
9731 let diagnostics = if direction == Direction::Prev {
9732 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
9733 } else {
9734 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
9735 }
9736 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start));
9737 let group = diagnostics
9738 // relies on diagnostics_in_range to return diagnostics with the same starting range to
9739 // be sorted in a stable way
9740 // skip until we are at current active diagnostic, if it exists
9741 .skip_while(|entry| {
9742 (match direction {
9743 Direction::Prev => entry.range.start >= search_start,
9744 Direction::Next => entry.range.start <= search_start,
9745 }) && self
9746 .active_diagnostics
9747 .as_ref()
9748 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
9749 })
9750 .find_map(|entry| {
9751 if entry.diagnostic.is_primary
9752 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
9753 && !entry.range.is_empty()
9754 // if we match with the active diagnostic, skip it
9755 && Some(entry.diagnostic.group_id)
9756 != self.active_diagnostics.as_ref().map(|d| d.group_id)
9757 {
9758 Some((entry.range, entry.diagnostic.group_id))
9759 } else {
9760 None
9761 }
9762 });
9763
9764 if let Some((primary_range, group_id)) = group {
9765 if self.activate_diagnostics(group_id, cx) {
9766 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9767 s.select(vec![Selection {
9768 id: selection.id,
9769 start: primary_range.start,
9770 end: primary_range.start,
9771 reversed: false,
9772 goal: SelectionGoal::None,
9773 }]);
9774 });
9775 }
9776 break;
9777 } else {
9778 // Cycle around to the start of the buffer, potentially moving back to the start of
9779 // the currently active diagnostic.
9780 active_primary_range.take();
9781 if direction == Direction::Prev {
9782 if search_start == buffer.len() {
9783 break;
9784 } else {
9785 search_start = buffer.len();
9786 }
9787 } else if search_start == 0 {
9788 break;
9789 } else {
9790 search_start = 0;
9791 }
9792 }
9793 }
9794 }
9795
9796 fn go_to_next_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
9797 let snapshot = self.snapshot(cx);
9798 let selection = self.selections.newest::<Point>(cx);
9799 self.go_to_hunk_after_position(&snapshot, selection.head(), cx);
9800 }
9801
9802 fn go_to_hunk_after_position(
9803 &mut self,
9804 snapshot: &EditorSnapshot,
9805 position: Point,
9806 cx: &mut ViewContext<'_, Editor>,
9807 ) -> Option<MultiBufferDiffHunk> {
9808 for (ix, position) in [position, Point::zero()].into_iter().enumerate() {
9809 if let Some(hunk) = self.go_to_next_hunk_in_direction(
9810 snapshot,
9811 position,
9812 ix > 0,
9813 snapshot.diff_map.diff_hunks_in_range(
9814 position + Point::new(1, 0)..snapshot.buffer_snapshot.max_point(),
9815 &snapshot.buffer_snapshot,
9816 ),
9817 cx,
9818 ) {
9819 return Some(hunk);
9820 }
9821 }
9822 None
9823 }
9824
9825 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
9826 let snapshot = self.snapshot(cx);
9827 let selection = self.selections.newest::<Point>(cx);
9828 self.go_to_hunk_before_position(&snapshot, selection.head(), cx);
9829 }
9830
9831 fn go_to_hunk_before_position(
9832 &mut self,
9833 snapshot: &EditorSnapshot,
9834 position: Point,
9835 cx: &mut ViewContext<'_, Editor>,
9836 ) -> Option<MultiBufferDiffHunk> {
9837 for (ix, position) in [position, snapshot.buffer_snapshot.max_point()]
9838 .into_iter()
9839 .enumerate()
9840 {
9841 if let Some(hunk) = self.go_to_next_hunk_in_direction(
9842 snapshot,
9843 position,
9844 ix > 0,
9845 snapshot
9846 .diff_map
9847 .diff_hunks_in_range_rev(Point::zero()..position, &snapshot.buffer_snapshot),
9848 cx,
9849 ) {
9850 return Some(hunk);
9851 }
9852 }
9853 None
9854 }
9855
9856 fn go_to_next_hunk_in_direction(
9857 &mut self,
9858 snapshot: &DisplaySnapshot,
9859 initial_point: Point,
9860 is_wrapped: bool,
9861 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
9862 cx: &mut ViewContext<Editor>,
9863 ) -> Option<MultiBufferDiffHunk> {
9864 let display_point = initial_point.to_display_point(snapshot);
9865 let mut hunks = hunks
9866 .map(|hunk| (diff_hunk_to_display(&hunk, snapshot), hunk))
9867 .filter(|(display_hunk, _)| {
9868 is_wrapped || !display_hunk.contains_display_row(display_point.row())
9869 })
9870 .dedup();
9871
9872 if let Some((display_hunk, hunk)) = hunks.next() {
9873 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9874 let row = display_hunk.start_display_row();
9875 let point = DisplayPoint::new(row, 0);
9876 s.select_display_ranges([point..point]);
9877 });
9878
9879 Some(hunk)
9880 } else {
9881 None
9882 }
9883 }
9884
9885 pub fn go_to_definition(
9886 &mut self,
9887 _: &GoToDefinition,
9888 cx: &mut ViewContext<Self>,
9889 ) -> Task<Result<Navigated>> {
9890 let definition = self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
9891 cx.spawn(|editor, mut cx| async move {
9892 if definition.await? == Navigated::Yes {
9893 return Ok(Navigated::Yes);
9894 }
9895 match editor.update(&mut cx, |editor, cx| {
9896 editor.find_all_references(&FindAllReferences, cx)
9897 })? {
9898 Some(references) => references.await,
9899 None => Ok(Navigated::No),
9900 }
9901 })
9902 }
9903
9904 pub fn go_to_declaration(
9905 &mut self,
9906 _: &GoToDeclaration,
9907 cx: &mut ViewContext<Self>,
9908 ) -> Task<Result<Navigated>> {
9909 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, cx)
9910 }
9911
9912 pub fn go_to_declaration_split(
9913 &mut self,
9914 _: &GoToDeclaration,
9915 cx: &mut ViewContext<Self>,
9916 ) -> Task<Result<Navigated>> {
9917 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, cx)
9918 }
9919
9920 pub fn go_to_implementation(
9921 &mut self,
9922 _: &GoToImplementation,
9923 cx: &mut ViewContext<Self>,
9924 ) -> Task<Result<Navigated>> {
9925 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx)
9926 }
9927
9928 pub fn go_to_implementation_split(
9929 &mut self,
9930 _: &GoToImplementationSplit,
9931 cx: &mut ViewContext<Self>,
9932 ) -> Task<Result<Navigated>> {
9933 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx)
9934 }
9935
9936 pub fn go_to_type_definition(
9937 &mut self,
9938 _: &GoToTypeDefinition,
9939 cx: &mut ViewContext<Self>,
9940 ) -> Task<Result<Navigated>> {
9941 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx)
9942 }
9943
9944 pub fn go_to_definition_split(
9945 &mut self,
9946 _: &GoToDefinitionSplit,
9947 cx: &mut ViewContext<Self>,
9948 ) -> Task<Result<Navigated>> {
9949 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx)
9950 }
9951
9952 pub fn go_to_type_definition_split(
9953 &mut self,
9954 _: &GoToTypeDefinitionSplit,
9955 cx: &mut ViewContext<Self>,
9956 ) -> Task<Result<Navigated>> {
9957 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx)
9958 }
9959
9960 fn go_to_definition_of_kind(
9961 &mut self,
9962 kind: GotoDefinitionKind,
9963 split: bool,
9964 cx: &mut ViewContext<Self>,
9965 ) -> Task<Result<Navigated>> {
9966 let Some(provider) = self.semantics_provider.clone() else {
9967 return Task::ready(Ok(Navigated::No));
9968 };
9969 let head = self.selections.newest::<usize>(cx).head();
9970 let buffer = self.buffer.read(cx);
9971 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
9972 text_anchor
9973 } else {
9974 return Task::ready(Ok(Navigated::No));
9975 };
9976
9977 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
9978 return Task::ready(Ok(Navigated::No));
9979 };
9980
9981 cx.spawn(|editor, mut cx| async move {
9982 let definitions = definitions.await?;
9983 let navigated = editor
9984 .update(&mut cx, |editor, cx| {
9985 editor.navigate_to_hover_links(
9986 Some(kind),
9987 definitions
9988 .into_iter()
9989 .filter(|location| {
9990 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
9991 })
9992 .map(HoverLink::Text)
9993 .collect::<Vec<_>>(),
9994 split,
9995 cx,
9996 )
9997 })?
9998 .await?;
9999 anyhow::Ok(navigated)
10000 })
10001 }
10002
10003 pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext<Self>) {
10004 let position = self.selections.newest_anchor().head();
10005 let Some((buffer, buffer_position)) =
10006 self.buffer.read(cx).text_anchor_for_position(position, cx)
10007 else {
10008 return;
10009 };
10010
10011 cx.spawn(|editor, mut cx| async move {
10012 if let Some((_, url)) = find_url(&buffer, buffer_position, cx.clone()) {
10013 editor.update(&mut cx, |_, cx| {
10014 cx.open_url(&url);
10015 })
10016 } else {
10017 Ok(())
10018 }
10019 })
10020 .detach();
10021 }
10022
10023 pub fn open_file(&mut self, _: &OpenFile, cx: &mut ViewContext<Self>) {
10024 let Some(workspace) = self.workspace() else {
10025 return;
10026 };
10027
10028 let position = self.selections.newest_anchor().head();
10029
10030 let Some((buffer, buffer_position)) =
10031 self.buffer.read(cx).text_anchor_for_position(position, cx)
10032 else {
10033 return;
10034 };
10035
10036 let project = self.project.clone();
10037
10038 cx.spawn(|_, mut cx| async move {
10039 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
10040
10041 if let Some((_, path)) = result {
10042 workspace
10043 .update(&mut cx, |workspace, cx| {
10044 workspace.open_resolved_path(path, cx)
10045 })?
10046 .await?;
10047 }
10048 anyhow::Ok(())
10049 })
10050 .detach();
10051 }
10052
10053 pub(crate) fn navigate_to_hover_links(
10054 &mut self,
10055 kind: Option<GotoDefinitionKind>,
10056 mut definitions: Vec<HoverLink>,
10057 split: bool,
10058 cx: &mut ViewContext<Editor>,
10059 ) -> Task<Result<Navigated>> {
10060 // If there is one definition, just open it directly
10061 if definitions.len() == 1 {
10062 let definition = definitions.pop().unwrap();
10063
10064 enum TargetTaskResult {
10065 Location(Option<Location>),
10066 AlreadyNavigated,
10067 }
10068
10069 let target_task = match definition {
10070 HoverLink::Text(link) => {
10071 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
10072 }
10073 HoverLink::InlayHint(lsp_location, server_id) => {
10074 let computation = self.compute_target_location(lsp_location, server_id, cx);
10075 cx.background_executor().spawn(async move {
10076 let location = computation.await?;
10077 Ok(TargetTaskResult::Location(location))
10078 })
10079 }
10080 HoverLink::Url(url) => {
10081 cx.open_url(&url);
10082 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
10083 }
10084 HoverLink::File(path) => {
10085 if let Some(workspace) = self.workspace() {
10086 cx.spawn(|_, mut cx| async move {
10087 workspace
10088 .update(&mut cx, |workspace, cx| {
10089 workspace.open_resolved_path(path, cx)
10090 })?
10091 .await
10092 .map(|_| TargetTaskResult::AlreadyNavigated)
10093 })
10094 } else {
10095 Task::ready(Ok(TargetTaskResult::Location(None)))
10096 }
10097 }
10098 };
10099 cx.spawn(|editor, mut cx| async move {
10100 let target = match target_task.await.context("target resolution task")? {
10101 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
10102 TargetTaskResult::Location(None) => return Ok(Navigated::No),
10103 TargetTaskResult::Location(Some(target)) => target,
10104 };
10105
10106 editor.update(&mut cx, |editor, cx| {
10107 let Some(workspace) = editor.workspace() else {
10108 return Navigated::No;
10109 };
10110 let pane = workspace.read(cx).active_pane().clone();
10111
10112 let range = target.range.to_offset(target.buffer.read(cx));
10113 let range = editor.range_for_match(&range);
10114
10115 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
10116 let buffer = target.buffer.read(cx);
10117 let range = check_multiline_range(buffer, range);
10118 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
10119 s.select_ranges([range]);
10120 });
10121 } else {
10122 cx.window_context().defer(move |cx| {
10123 let target_editor: View<Self> =
10124 workspace.update(cx, |workspace, cx| {
10125 let pane = if split {
10126 workspace.adjacent_pane(cx)
10127 } else {
10128 workspace.active_pane().clone()
10129 };
10130
10131 workspace.open_project_item(
10132 pane,
10133 target.buffer.clone(),
10134 true,
10135 true,
10136 cx,
10137 )
10138 });
10139 target_editor.update(cx, |target_editor, cx| {
10140 // When selecting a definition in a different buffer, disable the nav history
10141 // to avoid creating a history entry at the previous cursor location.
10142 pane.update(cx, |pane, _| pane.disable_history());
10143 let buffer = target.buffer.read(cx);
10144 let range = check_multiline_range(buffer, range);
10145 target_editor.change_selections(
10146 Some(Autoscroll::focused()),
10147 cx,
10148 |s| {
10149 s.select_ranges([range]);
10150 },
10151 );
10152 pane.update(cx, |pane, _| pane.enable_history());
10153 });
10154 });
10155 }
10156 Navigated::Yes
10157 })
10158 })
10159 } else if !definitions.is_empty() {
10160 cx.spawn(|editor, mut cx| async move {
10161 let (title, location_tasks, workspace) = editor
10162 .update(&mut cx, |editor, cx| {
10163 let tab_kind = match kind {
10164 Some(GotoDefinitionKind::Implementation) => "Implementations",
10165 _ => "Definitions",
10166 };
10167 let title = definitions
10168 .iter()
10169 .find_map(|definition| match definition {
10170 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
10171 let buffer = origin.buffer.read(cx);
10172 format!(
10173 "{} for {}",
10174 tab_kind,
10175 buffer
10176 .text_for_range(origin.range.clone())
10177 .collect::<String>()
10178 )
10179 }),
10180 HoverLink::InlayHint(_, _) => None,
10181 HoverLink::Url(_) => None,
10182 HoverLink::File(_) => None,
10183 })
10184 .unwrap_or(tab_kind.to_string());
10185 let location_tasks = definitions
10186 .into_iter()
10187 .map(|definition| match definition {
10188 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
10189 HoverLink::InlayHint(lsp_location, server_id) => {
10190 editor.compute_target_location(lsp_location, server_id, cx)
10191 }
10192 HoverLink::Url(_) => Task::ready(Ok(None)),
10193 HoverLink::File(_) => Task::ready(Ok(None)),
10194 })
10195 .collect::<Vec<_>>();
10196 (title, location_tasks, editor.workspace().clone())
10197 })
10198 .context("location tasks preparation")?;
10199
10200 let locations = future::join_all(location_tasks)
10201 .await
10202 .into_iter()
10203 .filter_map(|location| location.transpose())
10204 .collect::<Result<_>>()
10205 .context("location tasks")?;
10206
10207 let Some(workspace) = workspace else {
10208 return Ok(Navigated::No);
10209 };
10210 let opened = workspace
10211 .update(&mut cx, |workspace, cx| {
10212 Self::open_locations_in_multibuffer(workspace, locations, title, split, cx)
10213 })
10214 .ok();
10215
10216 anyhow::Ok(Navigated::from_bool(opened.is_some()))
10217 })
10218 } else {
10219 Task::ready(Ok(Navigated::No))
10220 }
10221 }
10222
10223 fn compute_target_location(
10224 &self,
10225 lsp_location: lsp::Location,
10226 server_id: LanguageServerId,
10227 cx: &mut ViewContext<Self>,
10228 ) -> Task<anyhow::Result<Option<Location>>> {
10229 let Some(project) = self.project.clone() else {
10230 return Task::Ready(Some(Ok(None)));
10231 };
10232
10233 cx.spawn(move |editor, mut cx| async move {
10234 let location_task = editor.update(&mut cx, |_, cx| {
10235 project.update(cx, |project, cx| {
10236 let language_server_name = project
10237 .language_server_statuses(cx)
10238 .find(|(id, _)| server_id == *id)
10239 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
10240 language_server_name.map(|language_server_name| {
10241 project.open_local_buffer_via_lsp(
10242 lsp_location.uri.clone(),
10243 server_id,
10244 language_server_name,
10245 cx,
10246 )
10247 })
10248 })
10249 })?;
10250 let location = match location_task {
10251 Some(task) => Some({
10252 let target_buffer_handle = task.await.context("open local buffer")?;
10253 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
10254 let target_start = target_buffer
10255 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
10256 let target_end = target_buffer
10257 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
10258 target_buffer.anchor_after(target_start)
10259 ..target_buffer.anchor_before(target_end)
10260 })?;
10261 Location {
10262 buffer: target_buffer_handle,
10263 range,
10264 }
10265 }),
10266 None => None,
10267 };
10268 Ok(location)
10269 })
10270 }
10271
10272 pub fn find_all_references(
10273 &mut self,
10274 _: &FindAllReferences,
10275 cx: &mut ViewContext<Self>,
10276 ) -> Option<Task<Result<Navigated>>> {
10277 let selection = self.selections.newest::<usize>(cx);
10278 let multi_buffer = self.buffer.read(cx);
10279 let head = selection.head();
10280
10281 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
10282 let head_anchor = multi_buffer_snapshot.anchor_at(
10283 head,
10284 if head < selection.tail() {
10285 Bias::Right
10286 } else {
10287 Bias::Left
10288 },
10289 );
10290
10291 match self
10292 .find_all_references_task_sources
10293 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
10294 {
10295 Ok(_) => {
10296 log::info!(
10297 "Ignoring repeated FindAllReferences invocation with the position of already running task"
10298 );
10299 return None;
10300 }
10301 Err(i) => {
10302 self.find_all_references_task_sources.insert(i, head_anchor);
10303 }
10304 }
10305
10306 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
10307 let workspace = self.workspace()?;
10308 let project = workspace.read(cx).project().clone();
10309 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
10310 Some(cx.spawn(|editor, mut cx| async move {
10311 let _cleanup = defer({
10312 let mut cx = cx.clone();
10313 move || {
10314 let _ = editor.update(&mut cx, |editor, _| {
10315 if let Ok(i) =
10316 editor
10317 .find_all_references_task_sources
10318 .binary_search_by(|anchor| {
10319 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
10320 })
10321 {
10322 editor.find_all_references_task_sources.remove(i);
10323 }
10324 });
10325 }
10326 });
10327
10328 let locations = references.await?;
10329 if locations.is_empty() {
10330 return anyhow::Ok(Navigated::No);
10331 }
10332
10333 workspace.update(&mut cx, |workspace, cx| {
10334 let title = locations
10335 .first()
10336 .as_ref()
10337 .map(|location| {
10338 let buffer = location.buffer.read(cx);
10339 format!(
10340 "References to `{}`",
10341 buffer
10342 .text_for_range(location.range.clone())
10343 .collect::<String>()
10344 )
10345 })
10346 .unwrap();
10347 Self::open_locations_in_multibuffer(workspace, locations, title, false, cx);
10348 Navigated::Yes
10349 })
10350 }))
10351 }
10352
10353 /// Opens a multibuffer with the given project locations in it
10354 pub fn open_locations_in_multibuffer(
10355 workspace: &mut Workspace,
10356 mut locations: Vec<Location>,
10357 title: String,
10358 split: bool,
10359 cx: &mut ViewContext<Workspace>,
10360 ) {
10361 // If there are multiple definitions, open them in a multibuffer
10362 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
10363 let mut locations = locations.into_iter().peekable();
10364 let mut ranges_to_highlight = Vec::new();
10365 let capability = workspace.project().read(cx).capability();
10366
10367 let excerpt_buffer = cx.new_model(|cx| {
10368 let mut multibuffer = MultiBuffer::new(capability);
10369 while let Some(location) = locations.next() {
10370 let buffer = location.buffer.read(cx);
10371 let mut ranges_for_buffer = Vec::new();
10372 let range = location.range.to_offset(buffer);
10373 ranges_for_buffer.push(range.clone());
10374
10375 while let Some(next_location) = locations.peek() {
10376 if next_location.buffer == location.buffer {
10377 ranges_for_buffer.push(next_location.range.to_offset(buffer));
10378 locations.next();
10379 } else {
10380 break;
10381 }
10382 }
10383
10384 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
10385 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
10386 location.buffer.clone(),
10387 ranges_for_buffer,
10388 DEFAULT_MULTIBUFFER_CONTEXT,
10389 cx,
10390 ))
10391 }
10392
10393 multibuffer.with_title(title)
10394 });
10395
10396 let editor = cx.new_view(|cx| {
10397 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), true, cx)
10398 });
10399 editor.update(cx, |editor, cx| {
10400 if let Some(first_range) = ranges_to_highlight.first() {
10401 editor.change_selections(None, cx, |selections| {
10402 selections.clear_disjoint();
10403 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
10404 });
10405 }
10406 editor.highlight_background::<Self>(
10407 &ranges_to_highlight,
10408 |theme| theme.editor_highlighted_line_background,
10409 cx,
10410 );
10411 });
10412
10413 let item = Box::new(editor);
10414 let item_id = item.item_id();
10415
10416 if split {
10417 workspace.split_item(SplitDirection::Right, item.clone(), cx);
10418 } else {
10419 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
10420 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
10421 pane.close_current_preview_item(cx)
10422 } else {
10423 None
10424 }
10425 });
10426 workspace.add_item_to_active_pane(item.clone(), destination_index, true, cx);
10427 }
10428 workspace.active_pane().update(cx, |pane, cx| {
10429 pane.set_preview_item_id(Some(item_id), cx);
10430 });
10431 }
10432
10433 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
10434 use language::ToOffset as _;
10435
10436 let provider = self.semantics_provider.clone()?;
10437 let selection = self.selections.newest_anchor().clone();
10438 let (cursor_buffer, cursor_buffer_position) = self
10439 .buffer
10440 .read(cx)
10441 .text_anchor_for_position(selection.head(), cx)?;
10442 let (tail_buffer, cursor_buffer_position_end) = self
10443 .buffer
10444 .read(cx)
10445 .text_anchor_for_position(selection.tail(), cx)?;
10446 if tail_buffer != cursor_buffer {
10447 return None;
10448 }
10449
10450 let snapshot = cursor_buffer.read(cx).snapshot();
10451 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
10452 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
10453 let prepare_rename = provider
10454 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
10455 .unwrap_or_else(|| Task::ready(Ok(None)));
10456 drop(snapshot);
10457
10458 Some(cx.spawn(|this, mut cx| async move {
10459 let rename_range = if let Some(range) = prepare_rename.await? {
10460 Some(range)
10461 } else {
10462 this.update(&mut cx, |this, cx| {
10463 let buffer = this.buffer.read(cx).snapshot(cx);
10464 let mut buffer_highlights = this
10465 .document_highlights_for_position(selection.head(), &buffer)
10466 .filter(|highlight| {
10467 highlight.start.excerpt_id == selection.head().excerpt_id
10468 && highlight.end.excerpt_id == selection.head().excerpt_id
10469 });
10470 buffer_highlights
10471 .next()
10472 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
10473 })?
10474 };
10475 if let Some(rename_range) = rename_range {
10476 this.update(&mut cx, |this, cx| {
10477 let snapshot = cursor_buffer.read(cx).snapshot();
10478 let rename_buffer_range = rename_range.to_offset(&snapshot);
10479 let cursor_offset_in_rename_range =
10480 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
10481 let cursor_offset_in_rename_range_end =
10482 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
10483
10484 this.take_rename(false, cx);
10485 let buffer = this.buffer.read(cx).read(cx);
10486 let cursor_offset = selection.head().to_offset(&buffer);
10487 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
10488 let rename_end = rename_start + rename_buffer_range.len();
10489 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
10490 let mut old_highlight_id = None;
10491 let old_name: Arc<str> = buffer
10492 .chunks(rename_start..rename_end, true)
10493 .map(|chunk| {
10494 if old_highlight_id.is_none() {
10495 old_highlight_id = chunk.syntax_highlight_id;
10496 }
10497 chunk.text
10498 })
10499 .collect::<String>()
10500 .into();
10501
10502 drop(buffer);
10503
10504 // Position the selection in the rename editor so that it matches the current selection.
10505 this.show_local_selections = false;
10506 let rename_editor = cx.new_view(|cx| {
10507 let mut editor = Editor::single_line(cx);
10508 editor.buffer.update(cx, |buffer, cx| {
10509 buffer.edit([(0..0, old_name.clone())], None, cx)
10510 });
10511 let rename_selection_range = match cursor_offset_in_rename_range
10512 .cmp(&cursor_offset_in_rename_range_end)
10513 {
10514 Ordering::Equal => {
10515 editor.select_all(&SelectAll, cx);
10516 return editor;
10517 }
10518 Ordering::Less => {
10519 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
10520 }
10521 Ordering::Greater => {
10522 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
10523 }
10524 };
10525 if rename_selection_range.end > old_name.len() {
10526 editor.select_all(&SelectAll, cx);
10527 } else {
10528 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
10529 s.select_ranges([rename_selection_range]);
10530 });
10531 }
10532 editor
10533 });
10534 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
10535 if e == &EditorEvent::Focused {
10536 cx.emit(EditorEvent::FocusedIn)
10537 }
10538 })
10539 .detach();
10540
10541 let write_highlights =
10542 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
10543 let read_highlights =
10544 this.clear_background_highlights::<DocumentHighlightRead>(cx);
10545 let ranges = write_highlights
10546 .iter()
10547 .flat_map(|(_, ranges)| ranges.iter())
10548 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
10549 .cloned()
10550 .collect();
10551
10552 this.highlight_text::<Rename>(
10553 ranges,
10554 HighlightStyle {
10555 fade_out: Some(0.6),
10556 ..Default::default()
10557 },
10558 cx,
10559 );
10560 let rename_focus_handle = rename_editor.focus_handle(cx);
10561 cx.focus(&rename_focus_handle);
10562 let block_id = this.insert_blocks(
10563 [BlockProperties {
10564 style: BlockStyle::Flex,
10565 placement: BlockPlacement::Below(range.start),
10566 height: 1,
10567 render: Arc::new({
10568 let rename_editor = rename_editor.clone();
10569 move |cx: &mut BlockContext| {
10570 let mut text_style = cx.editor_style.text.clone();
10571 if let Some(highlight_style) = old_highlight_id
10572 .and_then(|h| h.style(&cx.editor_style.syntax))
10573 {
10574 text_style = text_style.highlight(highlight_style);
10575 }
10576 div()
10577 .block_mouse_down()
10578 .pl(cx.anchor_x)
10579 .child(EditorElement::new(
10580 &rename_editor,
10581 EditorStyle {
10582 background: cx.theme().system().transparent,
10583 local_player: cx.editor_style.local_player,
10584 text: text_style,
10585 scrollbar_width: cx.editor_style.scrollbar_width,
10586 syntax: cx.editor_style.syntax.clone(),
10587 status: cx.editor_style.status.clone(),
10588 inlay_hints_style: HighlightStyle {
10589 font_weight: Some(FontWeight::BOLD),
10590 ..make_inlay_hints_style(cx)
10591 },
10592 suggestions_style: HighlightStyle {
10593 color: Some(cx.theme().status().predictive),
10594 ..HighlightStyle::default()
10595 },
10596 ..EditorStyle::default()
10597 },
10598 ))
10599 .into_any_element()
10600 }
10601 }),
10602 priority: 0,
10603 }],
10604 Some(Autoscroll::fit()),
10605 cx,
10606 )[0];
10607 this.pending_rename = Some(RenameState {
10608 range,
10609 old_name,
10610 editor: rename_editor,
10611 block_id,
10612 });
10613 })?;
10614 }
10615
10616 Ok(())
10617 }))
10618 }
10619
10620 pub fn confirm_rename(
10621 &mut self,
10622 _: &ConfirmRename,
10623 cx: &mut ViewContext<Self>,
10624 ) -> Option<Task<Result<()>>> {
10625 let rename = self.take_rename(false, cx)?;
10626 let workspace = self.workspace()?.downgrade();
10627 let (buffer, start) = self
10628 .buffer
10629 .read(cx)
10630 .text_anchor_for_position(rename.range.start, cx)?;
10631 let (end_buffer, _) = self
10632 .buffer
10633 .read(cx)
10634 .text_anchor_for_position(rename.range.end, cx)?;
10635 if buffer != end_buffer {
10636 return None;
10637 }
10638
10639 let old_name = rename.old_name;
10640 let new_name = rename.editor.read(cx).text(cx);
10641
10642 let rename = self.semantics_provider.as_ref()?.perform_rename(
10643 &buffer,
10644 start,
10645 new_name.clone(),
10646 cx,
10647 )?;
10648
10649 Some(cx.spawn(|editor, mut cx| async move {
10650 let project_transaction = rename.await?;
10651 Self::open_project_transaction(
10652 &editor,
10653 workspace,
10654 project_transaction,
10655 format!("Rename: {} → {}", old_name, new_name),
10656 cx.clone(),
10657 )
10658 .await?;
10659
10660 editor.update(&mut cx, |editor, cx| {
10661 editor.refresh_document_highlights(cx);
10662 })?;
10663 Ok(())
10664 }))
10665 }
10666
10667 fn take_rename(
10668 &mut self,
10669 moving_cursor: bool,
10670 cx: &mut ViewContext<Self>,
10671 ) -> Option<RenameState> {
10672 let rename = self.pending_rename.take()?;
10673 if rename.editor.focus_handle(cx).is_focused(cx) {
10674 cx.focus(&self.focus_handle);
10675 }
10676
10677 self.remove_blocks(
10678 [rename.block_id].into_iter().collect(),
10679 Some(Autoscroll::fit()),
10680 cx,
10681 );
10682 self.clear_highlights::<Rename>(cx);
10683 self.show_local_selections = true;
10684
10685 if moving_cursor {
10686 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
10687 editor.selections.newest::<usize>(cx).head()
10688 });
10689
10690 // Update the selection to match the position of the selection inside
10691 // the rename editor.
10692 let snapshot = self.buffer.read(cx).read(cx);
10693 let rename_range = rename.range.to_offset(&snapshot);
10694 let cursor_in_editor = snapshot
10695 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
10696 .min(rename_range.end);
10697 drop(snapshot);
10698
10699 self.change_selections(None, cx, |s| {
10700 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
10701 });
10702 } else {
10703 self.refresh_document_highlights(cx);
10704 }
10705
10706 Some(rename)
10707 }
10708
10709 pub fn pending_rename(&self) -> Option<&RenameState> {
10710 self.pending_rename.as_ref()
10711 }
10712
10713 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
10714 let project = match &self.project {
10715 Some(project) => project.clone(),
10716 None => return None,
10717 };
10718
10719 Some(self.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffer, cx))
10720 }
10721
10722 fn format_selections(
10723 &mut self,
10724 _: &FormatSelections,
10725 cx: &mut ViewContext<Self>,
10726 ) -> Option<Task<Result<()>>> {
10727 let project = match &self.project {
10728 Some(project) => project.clone(),
10729 None => return None,
10730 };
10731
10732 let selections = self
10733 .selections
10734 .all_adjusted(cx)
10735 .into_iter()
10736 .filter(|s| !s.is_empty())
10737 .collect_vec();
10738
10739 Some(self.perform_format(
10740 project,
10741 FormatTrigger::Manual,
10742 FormatTarget::Ranges(selections),
10743 cx,
10744 ))
10745 }
10746
10747 fn perform_format(
10748 &mut self,
10749 project: Model<Project>,
10750 trigger: FormatTrigger,
10751 target: FormatTarget,
10752 cx: &mut ViewContext<Self>,
10753 ) -> Task<Result<()>> {
10754 let buffer = self.buffer().clone();
10755 let mut buffers = buffer.read(cx).all_buffers();
10756 if trigger == FormatTrigger::Save {
10757 buffers.retain(|buffer| buffer.read(cx).is_dirty());
10758 }
10759
10760 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
10761 let format = project.update(cx, |project, cx| {
10762 project.format(buffers, true, trigger, target, cx)
10763 });
10764
10765 cx.spawn(|_, mut cx| async move {
10766 let transaction = futures::select_biased! {
10767 () = timeout => {
10768 log::warn!("timed out waiting for formatting");
10769 None
10770 }
10771 transaction = format.log_err().fuse() => transaction,
10772 };
10773
10774 buffer
10775 .update(&mut cx, |buffer, cx| {
10776 if let Some(transaction) = transaction {
10777 if !buffer.is_singleton() {
10778 buffer.push_transaction(&transaction.0, cx);
10779 }
10780 }
10781
10782 cx.notify();
10783 })
10784 .ok();
10785
10786 Ok(())
10787 })
10788 }
10789
10790 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
10791 if let Some(project) = self.project.clone() {
10792 self.buffer.update(cx, |multi_buffer, cx| {
10793 project.update(cx, |project, cx| {
10794 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
10795 });
10796 })
10797 }
10798 }
10799
10800 fn cancel_language_server_work(
10801 &mut self,
10802 _: &actions::CancelLanguageServerWork,
10803 cx: &mut ViewContext<Self>,
10804 ) {
10805 if let Some(project) = self.project.clone() {
10806 self.buffer.update(cx, |multi_buffer, cx| {
10807 project.update(cx, |project, cx| {
10808 project.cancel_language_server_work_for_buffers(multi_buffer.all_buffers(), cx);
10809 });
10810 })
10811 }
10812 }
10813
10814 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
10815 cx.show_character_palette();
10816 }
10817
10818 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
10819 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
10820 let buffer = self.buffer.read(cx).snapshot(cx);
10821 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
10822 let is_valid = buffer
10823 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
10824 .any(|entry| {
10825 entry.diagnostic.is_primary
10826 && !entry.range.is_empty()
10827 && entry.range.start == primary_range_start
10828 && entry.diagnostic.message == active_diagnostics.primary_message
10829 });
10830
10831 if is_valid != active_diagnostics.is_valid {
10832 active_diagnostics.is_valid = is_valid;
10833 let mut new_styles = HashMap::default();
10834 for (block_id, diagnostic) in &active_diagnostics.blocks {
10835 new_styles.insert(
10836 *block_id,
10837 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
10838 );
10839 }
10840 self.display_map.update(cx, |display_map, _cx| {
10841 display_map.replace_blocks(new_styles)
10842 });
10843 }
10844 }
10845 }
10846
10847 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
10848 self.dismiss_diagnostics(cx);
10849 let snapshot = self.snapshot(cx);
10850 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
10851 let buffer = self.buffer.read(cx).snapshot(cx);
10852
10853 let mut primary_range = None;
10854 let mut primary_message = None;
10855 let mut group_end = Point::zero();
10856 let diagnostic_group = buffer
10857 .diagnostic_group::<MultiBufferPoint>(group_id)
10858 .filter_map(|entry| {
10859 if snapshot.is_line_folded(MultiBufferRow(entry.range.start.row))
10860 && (entry.range.start.row == entry.range.end.row
10861 || snapshot.is_line_folded(MultiBufferRow(entry.range.end.row)))
10862 {
10863 return None;
10864 }
10865 if entry.range.end > group_end {
10866 group_end = entry.range.end;
10867 }
10868 if entry.diagnostic.is_primary {
10869 primary_range = Some(entry.range.clone());
10870 primary_message = Some(entry.diagnostic.message.clone());
10871 }
10872 Some(entry)
10873 })
10874 .collect::<Vec<_>>();
10875 let primary_range = primary_range?;
10876 let primary_message = primary_message?;
10877 let primary_range =
10878 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
10879
10880 let blocks = display_map
10881 .insert_blocks(
10882 diagnostic_group.iter().map(|entry| {
10883 let diagnostic = entry.diagnostic.clone();
10884 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
10885 BlockProperties {
10886 style: BlockStyle::Fixed,
10887 placement: BlockPlacement::Below(
10888 buffer.anchor_after(entry.range.start),
10889 ),
10890 height: message_height,
10891 render: diagnostic_block_renderer(diagnostic, None, true, true),
10892 priority: 0,
10893 }
10894 }),
10895 cx,
10896 )
10897 .into_iter()
10898 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
10899 .collect();
10900
10901 Some(ActiveDiagnosticGroup {
10902 primary_range,
10903 primary_message,
10904 group_id,
10905 blocks,
10906 is_valid: true,
10907 })
10908 });
10909 self.active_diagnostics.is_some()
10910 }
10911
10912 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
10913 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
10914 self.display_map.update(cx, |display_map, cx| {
10915 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
10916 });
10917 cx.notify();
10918 }
10919 }
10920
10921 pub fn set_selections_from_remote(
10922 &mut self,
10923 selections: Vec<Selection<Anchor>>,
10924 pending_selection: Option<Selection<Anchor>>,
10925 cx: &mut ViewContext<Self>,
10926 ) {
10927 let old_cursor_position = self.selections.newest_anchor().head();
10928 self.selections.change_with(cx, |s| {
10929 s.select_anchors(selections);
10930 if let Some(pending_selection) = pending_selection {
10931 s.set_pending(pending_selection, SelectMode::Character);
10932 } else {
10933 s.clear_pending();
10934 }
10935 });
10936 self.selections_did_change(false, &old_cursor_position, true, cx);
10937 }
10938
10939 fn push_to_selection_history(&mut self) {
10940 self.selection_history.push(SelectionHistoryEntry {
10941 selections: self.selections.disjoint_anchors(),
10942 select_next_state: self.select_next_state.clone(),
10943 select_prev_state: self.select_prev_state.clone(),
10944 add_selections_state: self.add_selections_state.clone(),
10945 });
10946 }
10947
10948 pub fn transact(
10949 &mut self,
10950 cx: &mut ViewContext<Self>,
10951 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
10952 ) -> Option<TransactionId> {
10953 self.start_transaction_at(Instant::now(), cx);
10954 update(self, cx);
10955 self.end_transaction_at(Instant::now(), cx)
10956 }
10957
10958 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
10959 self.end_selection(cx);
10960 if let Some(tx_id) = self
10961 .buffer
10962 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
10963 {
10964 self.selection_history
10965 .insert_transaction(tx_id, self.selections.disjoint_anchors());
10966 cx.emit(EditorEvent::TransactionBegun {
10967 transaction_id: tx_id,
10968 })
10969 }
10970 }
10971
10972 fn end_transaction_at(
10973 &mut self,
10974 now: Instant,
10975 cx: &mut ViewContext<Self>,
10976 ) -> Option<TransactionId> {
10977 if let Some(transaction_id) = self
10978 .buffer
10979 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
10980 {
10981 if let Some((_, end_selections)) =
10982 self.selection_history.transaction_mut(transaction_id)
10983 {
10984 *end_selections = Some(self.selections.disjoint_anchors());
10985 } else {
10986 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
10987 }
10988
10989 cx.emit(EditorEvent::Edited { transaction_id });
10990 Some(transaction_id)
10991 } else {
10992 None
10993 }
10994 }
10995
10996 pub fn toggle_fold(&mut self, _: &actions::ToggleFold, cx: &mut ViewContext<Self>) {
10997 let selection = self.selections.newest::<Point>(cx);
10998
10999 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11000 let range = if selection.is_empty() {
11001 let point = selection.head().to_display_point(&display_map);
11002 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
11003 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
11004 .to_point(&display_map);
11005 start..end
11006 } else {
11007 selection.range()
11008 };
11009 if display_map.folds_in_range(range).next().is_some() {
11010 self.unfold_lines(&Default::default(), cx)
11011 } else {
11012 self.fold(&Default::default(), cx)
11013 }
11014 }
11015
11016 pub fn toggle_fold_recursive(
11017 &mut self,
11018 _: &actions::ToggleFoldRecursive,
11019 cx: &mut ViewContext<Self>,
11020 ) {
11021 let selection = self.selections.newest::<Point>(cx);
11022
11023 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11024 let range = if selection.is_empty() {
11025 let point = selection.head().to_display_point(&display_map);
11026 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
11027 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
11028 .to_point(&display_map);
11029 start..end
11030 } else {
11031 selection.range()
11032 };
11033 if display_map.folds_in_range(range).next().is_some() {
11034 self.unfold_recursive(&Default::default(), cx)
11035 } else {
11036 self.fold_recursive(&Default::default(), cx)
11037 }
11038 }
11039
11040 pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
11041 let mut to_fold = Vec::new();
11042 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11043 let selections = self.selections.all_adjusted(cx);
11044
11045 for selection in selections {
11046 let range = selection.range().sorted();
11047 let buffer_start_row = range.start.row;
11048
11049 if range.start.row != range.end.row {
11050 let mut found = false;
11051 let mut row = range.start.row;
11052 while row <= range.end.row {
11053 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
11054 found = true;
11055 row = crease.range().end.row + 1;
11056 to_fold.push(crease);
11057 } else {
11058 row += 1
11059 }
11060 }
11061 if found {
11062 continue;
11063 }
11064 }
11065
11066 for row in (0..=range.start.row).rev() {
11067 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
11068 if crease.range().end.row >= buffer_start_row {
11069 to_fold.push(crease);
11070 if row <= range.start.row {
11071 break;
11072 }
11073 }
11074 }
11075 }
11076 }
11077
11078 self.fold_creases(to_fold, true, cx);
11079 }
11080
11081 fn fold_at_level(&mut self, fold_at: &FoldAtLevel, cx: &mut ViewContext<Self>) {
11082 if !self.buffer.read(cx).is_singleton() {
11083 return;
11084 }
11085
11086 let fold_at_level = fold_at.level;
11087 let snapshot = self.buffer.read(cx).snapshot(cx);
11088 let mut to_fold = Vec::new();
11089 let mut stack = vec![(0, snapshot.max_row().0, 1)];
11090
11091 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
11092 while start_row < end_row {
11093 match self
11094 .snapshot(cx)
11095 .crease_for_buffer_row(MultiBufferRow(start_row))
11096 {
11097 Some(crease) => {
11098 let nested_start_row = crease.range().start.row + 1;
11099 let nested_end_row = crease.range().end.row;
11100
11101 if current_level < fold_at_level {
11102 stack.push((nested_start_row, nested_end_row, current_level + 1));
11103 } else if current_level == fold_at_level {
11104 to_fold.push(crease);
11105 }
11106
11107 start_row = nested_end_row + 1;
11108 }
11109 None => start_row += 1,
11110 }
11111 }
11112 }
11113
11114 self.fold_creases(to_fold, true, cx);
11115 }
11116
11117 pub fn fold_all(&mut self, _: &actions::FoldAll, cx: &mut ViewContext<Self>) {
11118 if !self.buffer.read(cx).is_singleton() {
11119 return;
11120 }
11121
11122 let mut fold_ranges = Vec::new();
11123 let snapshot = self.buffer.read(cx).snapshot(cx);
11124
11125 for row in 0..snapshot.max_row().0 {
11126 if let Some(foldable_range) =
11127 self.snapshot(cx).crease_for_buffer_row(MultiBufferRow(row))
11128 {
11129 fold_ranges.push(foldable_range);
11130 }
11131 }
11132
11133 self.fold_creases(fold_ranges, true, cx);
11134 }
11135
11136 pub fn fold_function_bodies(
11137 &mut self,
11138 _: &actions::FoldFunctionBodies,
11139 cx: &mut ViewContext<Self>,
11140 ) {
11141 let snapshot = self.buffer.read(cx).snapshot(cx);
11142 let Some((_, _, buffer)) = snapshot.as_singleton() else {
11143 return;
11144 };
11145 let creases = buffer
11146 .function_body_fold_ranges(0..buffer.len())
11147 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
11148 .collect();
11149
11150 self.fold_creases(creases, true, cx);
11151 }
11152
11153 pub fn fold_recursive(&mut self, _: &actions::FoldRecursive, cx: &mut ViewContext<Self>) {
11154 let mut to_fold = Vec::new();
11155 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11156 let selections = self.selections.all_adjusted(cx);
11157
11158 for selection in selections {
11159 let range = selection.range().sorted();
11160 let buffer_start_row = range.start.row;
11161
11162 if range.start.row != range.end.row {
11163 let mut found = false;
11164 for row in range.start.row..=range.end.row {
11165 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
11166 found = true;
11167 to_fold.push(crease);
11168 }
11169 }
11170 if found {
11171 continue;
11172 }
11173 }
11174
11175 for row in (0..=range.start.row).rev() {
11176 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
11177 if crease.range().end.row >= buffer_start_row {
11178 to_fold.push(crease);
11179 } else {
11180 break;
11181 }
11182 }
11183 }
11184 }
11185
11186 self.fold_creases(to_fold, true, cx);
11187 }
11188
11189 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
11190 let buffer_row = fold_at.buffer_row;
11191 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11192
11193 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
11194 let autoscroll = self
11195 .selections
11196 .all::<Point>(cx)
11197 .iter()
11198 .any(|selection| crease.range().overlaps(&selection.range()));
11199
11200 self.fold_creases(vec![crease], autoscroll, cx);
11201 }
11202 }
11203
11204 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
11205 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11206 let buffer = &display_map.buffer_snapshot;
11207 let selections = self.selections.all::<Point>(cx);
11208 let ranges = selections
11209 .iter()
11210 .map(|s| {
11211 let range = s.display_range(&display_map).sorted();
11212 let mut start = range.start.to_point(&display_map);
11213 let mut end = range.end.to_point(&display_map);
11214 start.column = 0;
11215 end.column = buffer.line_len(MultiBufferRow(end.row));
11216 start..end
11217 })
11218 .collect::<Vec<_>>();
11219
11220 self.unfold_ranges(&ranges, true, true, cx);
11221 }
11222
11223 pub fn unfold_recursive(&mut self, _: &UnfoldRecursive, cx: &mut ViewContext<Self>) {
11224 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11225 let selections = self.selections.all::<Point>(cx);
11226 let ranges = selections
11227 .iter()
11228 .map(|s| {
11229 let mut range = s.display_range(&display_map).sorted();
11230 *range.start.column_mut() = 0;
11231 *range.end.column_mut() = display_map.line_len(range.end.row());
11232 let start = range.start.to_point(&display_map);
11233 let end = range.end.to_point(&display_map);
11234 start..end
11235 })
11236 .collect::<Vec<_>>();
11237
11238 self.unfold_ranges(&ranges, true, true, cx);
11239 }
11240
11241 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
11242 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11243
11244 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
11245 ..Point::new(
11246 unfold_at.buffer_row.0,
11247 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
11248 );
11249
11250 let autoscroll = self
11251 .selections
11252 .all::<Point>(cx)
11253 .iter()
11254 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
11255
11256 self.unfold_ranges(&[intersection_range], true, autoscroll, cx)
11257 }
11258
11259 pub fn unfold_all(&mut self, _: &actions::UnfoldAll, cx: &mut ViewContext<Self>) {
11260 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11261 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
11262 }
11263
11264 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
11265 let selections = self.selections.all::<Point>(cx);
11266 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11267 let line_mode = self.selections.line_mode;
11268 let ranges = selections
11269 .into_iter()
11270 .map(|s| {
11271 if line_mode {
11272 let start = Point::new(s.start.row, 0);
11273 let end = Point::new(
11274 s.end.row,
11275 display_map
11276 .buffer_snapshot
11277 .line_len(MultiBufferRow(s.end.row)),
11278 );
11279 Crease::simple(start..end, display_map.fold_placeholder.clone())
11280 } else {
11281 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
11282 }
11283 })
11284 .collect::<Vec<_>>();
11285 self.fold_creases(ranges, true, cx);
11286 }
11287
11288 pub fn fold_creases<T: ToOffset + Clone>(
11289 &mut self,
11290 creases: Vec<Crease<T>>,
11291 auto_scroll: bool,
11292 cx: &mut ViewContext<Self>,
11293 ) {
11294 if creases.is_empty() {
11295 return;
11296 }
11297
11298 let mut buffers_affected = HashSet::default();
11299 let multi_buffer = self.buffer().read(cx);
11300 for crease in &creases {
11301 if let Some((_, buffer, _)) =
11302 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
11303 {
11304 buffers_affected.insert(buffer.read(cx).remote_id());
11305 };
11306 }
11307
11308 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
11309
11310 if auto_scroll {
11311 self.request_autoscroll(Autoscroll::fit(), cx);
11312 }
11313
11314 for buffer_id in buffers_affected {
11315 Self::sync_expanded_diff_hunks(&mut self.diff_map, buffer_id, cx);
11316 }
11317
11318 cx.notify();
11319
11320 if let Some(active_diagnostics) = self.active_diagnostics.take() {
11321 // Clear diagnostics block when folding a range that contains it.
11322 let snapshot = self.snapshot(cx);
11323 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
11324 drop(snapshot);
11325 self.active_diagnostics = Some(active_diagnostics);
11326 self.dismiss_diagnostics(cx);
11327 } else {
11328 self.active_diagnostics = Some(active_diagnostics);
11329 }
11330 }
11331
11332 self.scrollbar_marker_state.dirty = true;
11333 }
11334
11335 /// Removes any folds whose ranges intersect any of the given ranges.
11336 pub fn unfold_ranges<T: ToOffset + Clone>(
11337 &mut self,
11338 ranges: &[Range<T>],
11339 inclusive: bool,
11340 auto_scroll: bool,
11341 cx: &mut ViewContext<Self>,
11342 ) {
11343 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
11344 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
11345 });
11346 }
11347
11348 /// Removes any folds with the given ranges.
11349 pub fn remove_folds_with_type<T: ToOffset + Clone>(
11350 &mut self,
11351 ranges: &[Range<T>],
11352 type_id: TypeId,
11353 auto_scroll: bool,
11354 cx: &mut ViewContext<Self>,
11355 ) {
11356 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
11357 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
11358 });
11359 }
11360
11361 fn remove_folds_with<T: ToOffset + Clone>(
11362 &mut self,
11363 ranges: &[Range<T>],
11364 auto_scroll: bool,
11365 cx: &mut ViewContext<Self>,
11366 update: impl FnOnce(&mut DisplayMap, &mut ModelContext<DisplayMap>),
11367 ) {
11368 if ranges.is_empty() {
11369 return;
11370 }
11371
11372 let mut buffers_affected = HashSet::default();
11373 let multi_buffer = self.buffer().read(cx);
11374 for range in ranges {
11375 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
11376 buffers_affected.insert(buffer.read(cx).remote_id());
11377 };
11378 }
11379
11380 self.display_map.update(cx, update);
11381
11382 if auto_scroll {
11383 self.request_autoscroll(Autoscroll::fit(), cx);
11384 }
11385
11386 for buffer_id in buffers_affected {
11387 Self::sync_expanded_diff_hunks(&mut self.diff_map, buffer_id, cx);
11388 }
11389
11390 cx.notify();
11391 self.scrollbar_marker_state.dirty = true;
11392 self.active_indent_guides_state.dirty = true;
11393 }
11394
11395 pub fn default_fold_placeholder(&self, cx: &AppContext) -> FoldPlaceholder {
11396 self.display_map.read(cx).fold_placeholder.clone()
11397 }
11398
11399 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
11400 if hovered != self.gutter_hovered {
11401 self.gutter_hovered = hovered;
11402 cx.notify();
11403 }
11404 }
11405
11406 pub fn insert_blocks(
11407 &mut self,
11408 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
11409 autoscroll: Option<Autoscroll>,
11410 cx: &mut ViewContext<Self>,
11411 ) -> Vec<CustomBlockId> {
11412 let blocks = self
11413 .display_map
11414 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
11415 if let Some(autoscroll) = autoscroll {
11416 self.request_autoscroll(autoscroll, cx);
11417 }
11418 cx.notify();
11419 blocks
11420 }
11421
11422 pub fn resize_blocks(
11423 &mut self,
11424 heights: HashMap<CustomBlockId, u32>,
11425 autoscroll: Option<Autoscroll>,
11426 cx: &mut ViewContext<Self>,
11427 ) {
11428 self.display_map
11429 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
11430 if let Some(autoscroll) = autoscroll {
11431 self.request_autoscroll(autoscroll, cx);
11432 }
11433 cx.notify();
11434 }
11435
11436 pub fn replace_blocks(
11437 &mut self,
11438 renderers: HashMap<CustomBlockId, RenderBlock>,
11439 autoscroll: Option<Autoscroll>,
11440 cx: &mut ViewContext<Self>,
11441 ) {
11442 self.display_map
11443 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
11444 if let Some(autoscroll) = autoscroll {
11445 self.request_autoscroll(autoscroll, cx);
11446 }
11447 cx.notify();
11448 }
11449
11450 pub fn remove_blocks(
11451 &mut self,
11452 block_ids: HashSet<CustomBlockId>,
11453 autoscroll: Option<Autoscroll>,
11454 cx: &mut ViewContext<Self>,
11455 ) {
11456 self.display_map.update(cx, |display_map, cx| {
11457 display_map.remove_blocks(block_ids, cx)
11458 });
11459 if let Some(autoscroll) = autoscroll {
11460 self.request_autoscroll(autoscroll, cx);
11461 }
11462 cx.notify();
11463 }
11464
11465 pub fn row_for_block(
11466 &self,
11467 block_id: CustomBlockId,
11468 cx: &mut ViewContext<Self>,
11469 ) -> Option<DisplayRow> {
11470 self.display_map
11471 .update(cx, |map, cx| map.row_for_block(block_id, cx))
11472 }
11473
11474 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
11475 self.focused_block = Some(focused_block);
11476 }
11477
11478 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
11479 self.focused_block.take()
11480 }
11481
11482 pub fn insert_creases(
11483 &mut self,
11484 creases: impl IntoIterator<Item = Crease<Anchor>>,
11485 cx: &mut ViewContext<Self>,
11486 ) -> Vec<CreaseId> {
11487 self.display_map
11488 .update(cx, |map, cx| map.insert_creases(creases, cx))
11489 }
11490
11491 pub fn remove_creases(
11492 &mut self,
11493 ids: impl IntoIterator<Item = CreaseId>,
11494 cx: &mut ViewContext<Self>,
11495 ) {
11496 self.display_map
11497 .update(cx, |map, cx| map.remove_creases(ids, cx));
11498 }
11499
11500 pub fn longest_row(&self, cx: &mut AppContext) -> DisplayRow {
11501 self.display_map
11502 .update(cx, |map, cx| map.snapshot(cx))
11503 .longest_row()
11504 }
11505
11506 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
11507 self.display_map
11508 .update(cx, |map, cx| map.snapshot(cx))
11509 .max_point()
11510 }
11511
11512 pub fn text(&self, cx: &AppContext) -> String {
11513 self.buffer.read(cx).read(cx).text()
11514 }
11515
11516 pub fn text_option(&self, cx: &AppContext) -> Option<String> {
11517 let text = self.text(cx);
11518 let text = text.trim();
11519
11520 if text.is_empty() {
11521 return None;
11522 }
11523
11524 Some(text.to_string())
11525 }
11526
11527 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
11528 self.transact(cx, |this, cx| {
11529 this.buffer
11530 .read(cx)
11531 .as_singleton()
11532 .expect("you can only call set_text on editors for singleton buffers")
11533 .update(cx, |buffer, cx| buffer.set_text(text, cx));
11534 });
11535 }
11536
11537 pub fn display_text(&self, cx: &mut AppContext) -> String {
11538 self.display_map
11539 .update(cx, |map, cx| map.snapshot(cx))
11540 .text()
11541 }
11542
11543 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
11544 let mut wrap_guides = smallvec::smallvec![];
11545
11546 if self.show_wrap_guides == Some(false) {
11547 return wrap_guides;
11548 }
11549
11550 let settings = self.buffer.read(cx).settings_at(0, cx);
11551 if settings.show_wrap_guides {
11552 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
11553 wrap_guides.push((soft_wrap as usize, true));
11554 } else if let SoftWrap::Bounded(soft_wrap) = self.soft_wrap_mode(cx) {
11555 wrap_guides.push((soft_wrap as usize, true));
11556 }
11557 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
11558 }
11559
11560 wrap_guides
11561 }
11562
11563 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
11564 let settings = self.buffer.read(cx).settings_at(0, cx);
11565 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
11566 match mode {
11567 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
11568 SoftWrap::None
11569 }
11570 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
11571 language_settings::SoftWrap::PreferredLineLength => {
11572 SoftWrap::Column(settings.preferred_line_length)
11573 }
11574 language_settings::SoftWrap::Bounded => {
11575 SoftWrap::Bounded(settings.preferred_line_length)
11576 }
11577 }
11578 }
11579
11580 pub fn set_soft_wrap_mode(
11581 &mut self,
11582 mode: language_settings::SoftWrap,
11583 cx: &mut ViewContext<Self>,
11584 ) {
11585 self.soft_wrap_mode_override = Some(mode);
11586 cx.notify();
11587 }
11588
11589 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
11590 self.text_style_refinement = Some(style);
11591 }
11592
11593 /// called by the Element so we know what style we were most recently rendered with.
11594 pub(crate) fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
11595 let rem_size = cx.rem_size();
11596 self.display_map.update(cx, |map, cx| {
11597 map.set_font(
11598 style.text.font(),
11599 style.text.font_size.to_pixels(rem_size),
11600 cx,
11601 )
11602 });
11603 self.style = Some(style);
11604 }
11605
11606 pub fn style(&self) -> Option<&EditorStyle> {
11607 self.style.as_ref()
11608 }
11609
11610 // Called by the element. This method is not designed to be called outside of the editor
11611 // element's layout code because it does not notify when rewrapping is computed synchronously.
11612 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
11613 self.display_map
11614 .update(cx, |map, cx| map.set_wrap_width(width, cx))
11615 }
11616
11617 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
11618 if self.soft_wrap_mode_override.is_some() {
11619 self.soft_wrap_mode_override.take();
11620 } else {
11621 let soft_wrap = match self.soft_wrap_mode(cx) {
11622 SoftWrap::GitDiff => return,
11623 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
11624 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
11625 language_settings::SoftWrap::None
11626 }
11627 };
11628 self.soft_wrap_mode_override = Some(soft_wrap);
11629 }
11630 cx.notify();
11631 }
11632
11633 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, cx: &mut ViewContext<Self>) {
11634 let Some(workspace) = self.workspace() else {
11635 return;
11636 };
11637 let fs = workspace.read(cx).app_state().fs.clone();
11638 let current_show = TabBarSettings::get_global(cx).show;
11639 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
11640 setting.show = Some(!current_show);
11641 });
11642 }
11643
11644 pub fn toggle_indent_guides(&mut self, _: &ToggleIndentGuides, cx: &mut ViewContext<Self>) {
11645 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
11646 self.buffer
11647 .read(cx)
11648 .settings_at(0, cx)
11649 .indent_guides
11650 .enabled
11651 });
11652 self.show_indent_guides = Some(!currently_enabled);
11653 cx.notify();
11654 }
11655
11656 fn should_show_indent_guides(&self) -> Option<bool> {
11657 self.show_indent_guides
11658 }
11659
11660 pub fn toggle_line_numbers(&mut self, _: &ToggleLineNumbers, cx: &mut ViewContext<Self>) {
11661 let mut editor_settings = EditorSettings::get_global(cx).clone();
11662 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
11663 EditorSettings::override_global(editor_settings, cx);
11664 }
11665
11666 pub fn should_use_relative_line_numbers(&self, cx: &WindowContext) -> bool {
11667 self.use_relative_line_numbers
11668 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
11669 }
11670
11671 pub fn toggle_relative_line_numbers(
11672 &mut self,
11673 _: &ToggleRelativeLineNumbers,
11674 cx: &mut ViewContext<Self>,
11675 ) {
11676 let is_relative = self.should_use_relative_line_numbers(cx);
11677 self.set_relative_line_number(Some(!is_relative), cx)
11678 }
11679
11680 pub fn set_relative_line_number(
11681 &mut self,
11682 is_relative: Option<bool>,
11683 cx: &mut ViewContext<Self>,
11684 ) {
11685 self.use_relative_line_numbers = is_relative;
11686 cx.notify();
11687 }
11688
11689 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
11690 self.show_gutter = show_gutter;
11691 cx.notify();
11692 }
11693
11694 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut ViewContext<Self>) {
11695 self.show_line_numbers = Some(show_line_numbers);
11696 cx.notify();
11697 }
11698
11699 pub fn set_show_git_diff_gutter(
11700 &mut self,
11701 show_git_diff_gutter: bool,
11702 cx: &mut ViewContext<Self>,
11703 ) {
11704 self.show_git_diff_gutter = Some(show_git_diff_gutter);
11705 cx.notify();
11706 }
11707
11708 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut ViewContext<Self>) {
11709 self.show_code_actions = Some(show_code_actions);
11710 cx.notify();
11711 }
11712
11713 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut ViewContext<Self>) {
11714 self.show_runnables = Some(show_runnables);
11715 cx.notify();
11716 }
11717
11718 pub fn set_masked(&mut self, masked: bool, cx: &mut ViewContext<Self>) {
11719 if self.display_map.read(cx).masked != masked {
11720 self.display_map.update(cx, |map, _| map.masked = masked);
11721 }
11722 cx.notify()
11723 }
11724
11725 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut ViewContext<Self>) {
11726 self.show_wrap_guides = Some(show_wrap_guides);
11727 cx.notify();
11728 }
11729
11730 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut ViewContext<Self>) {
11731 self.show_indent_guides = Some(show_indent_guides);
11732 cx.notify();
11733 }
11734
11735 pub fn working_directory(&self, cx: &WindowContext) -> Option<PathBuf> {
11736 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
11737 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
11738 if let Some(dir) = file.abs_path(cx).parent() {
11739 return Some(dir.to_owned());
11740 }
11741 }
11742
11743 if let Some(project_path) = buffer.read(cx).project_path(cx) {
11744 return Some(project_path.path.to_path_buf());
11745 }
11746 }
11747
11748 None
11749 }
11750
11751 fn target_file<'a>(&self, cx: &'a AppContext) -> Option<&'a dyn language::LocalFile> {
11752 self.active_excerpt(cx)?
11753 .1
11754 .read(cx)
11755 .file()
11756 .and_then(|f| f.as_local())
11757 }
11758
11759 pub fn reveal_in_finder(&mut self, _: &RevealInFileManager, cx: &mut ViewContext<Self>) {
11760 if let Some(target) = self.target_file(cx) {
11761 cx.reveal_path(&target.abs_path(cx));
11762 }
11763 }
11764
11765 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
11766 if let Some(file) = self.target_file(cx) {
11767 if let Some(path) = file.abs_path(cx).to_str() {
11768 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
11769 }
11770 }
11771 }
11772
11773 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
11774 if let Some(file) = self.target_file(cx) {
11775 if let Some(path) = file.path().to_str() {
11776 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
11777 }
11778 }
11779 }
11780
11781 pub fn toggle_git_blame(&mut self, _: &ToggleGitBlame, cx: &mut ViewContext<Self>) {
11782 self.show_git_blame_gutter = !self.show_git_blame_gutter;
11783
11784 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
11785 self.start_git_blame(true, cx);
11786 }
11787
11788 cx.notify();
11789 }
11790
11791 pub fn toggle_git_blame_inline(
11792 &mut self,
11793 _: &ToggleGitBlameInline,
11794 cx: &mut ViewContext<Self>,
11795 ) {
11796 self.toggle_git_blame_inline_internal(true, cx);
11797 cx.notify();
11798 }
11799
11800 pub fn git_blame_inline_enabled(&self) -> bool {
11801 self.git_blame_inline_enabled
11802 }
11803
11804 pub fn toggle_selection_menu(&mut self, _: &ToggleSelectionMenu, cx: &mut ViewContext<Self>) {
11805 self.show_selection_menu = self
11806 .show_selection_menu
11807 .map(|show_selections_menu| !show_selections_menu)
11808 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
11809
11810 cx.notify();
11811 }
11812
11813 pub fn selection_menu_enabled(&self, cx: &AppContext) -> bool {
11814 self.show_selection_menu
11815 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
11816 }
11817
11818 fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
11819 if let Some(project) = self.project.as_ref() {
11820 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
11821 return;
11822 };
11823
11824 if buffer.read(cx).file().is_none() {
11825 return;
11826 }
11827
11828 let focused = self.focus_handle(cx).contains_focused(cx);
11829
11830 let project = project.clone();
11831 let blame =
11832 cx.new_model(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
11833 self.blame_subscription = Some(cx.observe(&blame, |_, _, cx| cx.notify()));
11834 self.blame = Some(blame);
11835 }
11836 }
11837
11838 fn toggle_git_blame_inline_internal(
11839 &mut self,
11840 user_triggered: bool,
11841 cx: &mut ViewContext<Self>,
11842 ) {
11843 if self.git_blame_inline_enabled {
11844 self.git_blame_inline_enabled = false;
11845 self.show_git_blame_inline = false;
11846 self.show_git_blame_inline_delay_task.take();
11847 } else {
11848 self.git_blame_inline_enabled = true;
11849 self.start_git_blame_inline(user_triggered, cx);
11850 }
11851
11852 cx.notify();
11853 }
11854
11855 fn start_git_blame_inline(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
11856 self.start_git_blame(user_triggered, cx);
11857
11858 if ProjectSettings::get_global(cx)
11859 .git
11860 .inline_blame_delay()
11861 .is_some()
11862 {
11863 self.start_inline_blame_timer(cx);
11864 } else {
11865 self.show_git_blame_inline = true
11866 }
11867 }
11868
11869 pub fn blame(&self) -> Option<&Model<GitBlame>> {
11870 self.blame.as_ref()
11871 }
11872
11873 pub fn show_git_blame_gutter(&self) -> bool {
11874 self.show_git_blame_gutter
11875 }
11876
11877 pub fn render_git_blame_gutter(&mut self, cx: &mut WindowContext) -> bool {
11878 self.show_git_blame_gutter && self.has_blame_entries(cx)
11879 }
11880
11881 pub fn render_git_blame_inline(&mut self, cx: &mut WindowContext) -> bool {
11882 self.show_git_blame_inline
11883 && self.focus_handle.is_focused(cx)
11884 && !self.newest_selection_head_on_empty_line(cx)
11885 && self.has_blame_entries(cx)
11886 }
11887
11888 fn has_blame_entries(&self, cx: &mut WindowContext) -> bool {
11889 self.blame()
11890 .map_or(false, |blame| blame.read(cx).has_generated_entries())
11891 }
11892
11893 fn newest_selection_head_on_empty_line(&mut self, cx: &mut WindowContext) -> bool {
11894 let cursor_anchor = self.selections.newest_anchor().head();
11895
11896 let snapshot = self.buffer.read(cx).snapshot(cx);
11897 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
11898
11899 snapshot.line_len(buffer_row) == 0
11900 }
11901
11902 fn get_permalink_to_line(&mut self, cx: &mut ViewContext<Self>) -> Task<Result<url::Url>> {
11903 let buffer_and_selection = maybe!({
11904 let selection = self.selections.newest::<Point>(cx);
11905 let selection_range = selection.range();
11906
11907 let (buffer, selection) = if let Some(buffer) = self.buffer().read(cx).as_singleton() {
11908 (buffer, selection_range.start.row..selection_range.end.row)
11909 } else {
11910 let buffer_ranges = self
11911 .buffer()
11912 .read(cx)
11913 .range_to_buffer_ranges(selection_range, cx);
11914
11915 let (buffer, range, _) = if selection.reversed {
11916 buffer_ranges.first()
11917 } else {
11918 buffer_ranges.last()
11919 }?;
11920
11921 let snapshot = buffer.read(cx).snapshot();
11922 let selection = text::ToPoint::to_point(&range.start, &snapshot).row
11923 ..text::ToPoint::to_point(&range.end, &snapshot).row;
11924 (buffer.clone(), selection)
11925 };
11926
11927 Some((buffer, selection))
11928 });
11929
11930 let Some((buffer, selection)) = buffer_and_selection else {
11931 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
11932 };
11933
11934 let Some(project) = self.project.as_ref() else {
11935 return Task::ready(Err(anyhow!("editor does not have project")));
11936 };
11937
11938 project.update(cx, |project, cx| {
11939 project.get_permalink_to_line(&buffer, selection, cx)
11940 })
11941 }
11942
11943 pub fn copy_permalink_to_line(&mut self, _: &CopyPermalinkToLine, cx: &mut ViewContext<Self>) {
11944 let permalink_task = self.get_permalink_to_line(cx);
11945 let workspace = self.workspace();
11946
11947 cx.spawn(|_, mut cx| async move {
11948 match permalink_task.await {
11949 Ok(permalink) => {
11950 cx.update(|cx| {
11951 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
11952 })
11953 .ok();
11954 }
11955 Err(err) => {
11956 let message = format!("Failed to copy permalink: {err}");
11957
11958 Err::<(), anyhow::Error>(err).log_err();
11959
11960 if let Some(workspace) = workspace {
11961 workspace
11962 .update(&mut cx, |workspace, cx| {
11963 struct CopyPermalinkToLine;
11964
11965 workspace.show_toast(
11966 Toast::new(
11967 NotificationId::unique::<CopyPermalinkToLine>(),
11968 message,
11969 ),
11970 cx,
11971 )
11972 })
11973 .ok();
11974 }
11975 }
11976 }
11977 })
11978 .detach();
11979 }
11980
11981 pub fn copy_file_location(&mut self, _: &CopyFileLocation, cx: &mut ViewContext<Self>) {
11982 let selection = self.selections.newest::<Point>(cx).start.row + 1;
11983 if let Some(file) = self.target_file(cx) {
11984 if let Some(path) = file.path().to_str() {
11985 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
11986 }
11987 }
11988 }
11989
11990 pub fn open_permalink_to_line(&mut self, _: &OpenPermalinkToLine, cx: &mut ViewContext<Self>) {
11991 let permalink_task = self.get_permalink_to_line(cx);
11992 let workspace = self.workspace();
11993
11994 cx.spawn(|_, mut cx| async move {
11995 match permalink_task.await {
11996 Ok(permalink) => {
11997 cx.update(|cx| {
11998 cx.open_url(permalink.as_ref());
11999 })
12000 .ok();
12001 }
12002 Err(err) => {
12003 let message = format!("Failed to open permalink: {err}");
12004
12005 Err::<(), anyhow::Error>(err).log_err();
12006
12007 if let Some(workspace) = workspace {
12008 workspace
12009 .update(&mut cx, |workspace, cx| {
12010 struct OpenPermalinkToLine;
12011
12012 workspace.show_toast(
12013 Toast::new(
12014 NotificationId::unique::<OpenPermalinkToLine>(),
12015 message,
12016 ),
12017 cx,
12018 )
12019 })
12020 .ok();
12021 }
12022 }
12023 }
12024 })
12025 .detach();
12026 }
12027
12028 /// Adds a row highlight for the given range. If a row has multiple highlights, the
12029 /// last highlight added will be used.
12030 ///
12031 /// If the range ends at the beginning of a line, then that line will not be highlighted.
12032 pub fn highlight_rows<T: 'static>(
12033 &mut self,
12034 range: Range<Anchor>,
12035 color: Hsla,
12036 should_autoscroll: bool,
12037 cx: &mut ViewContext<Self>,
12038 ) {
12039 let snapshot = self.buffer().read(cx).snapshot(cx);
12040 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
12041 let ix = row_highlights.binary_search_by(|highlight| {
12042 Ordering::Equal
12043 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
12044 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
12045 });
12046
12047 if let Err(mut ix) = ix {
12048 let index = post_inc(&mut self.highlight_order);
12049
12050 // If this range intersects with the preceding highlight, then merge it with
12051 // the preceding highlight. Otherwise insert a new highlight.
12052 let mut merged = false;
12053 if ix > 0 {
12054 let prev_highlight = &mut row_highlights[ix - 1];
12055 if prev_highlight
12056 .range
12057 .end
12058 .cmp(&range.start, &snapshot)
12059 .is_ge()
12060 {
12061 ix -= 1;
12062 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
12063 prev_highlight.range.end = range.end;
12064 }
12065 merged = true;
12066 prev_highlight.index = index;
12067 prev_highlight.color = color;
12068 prev_highlight.should_autoscroll = should_autoscroll;
12069 }
12070 }
12071
12072 if !merged {
12073 row_highlights.insert(
12074 ix,
12075 RowHighlight {
12076 range: range.clone(),
12077 index,
12078 color,
12079 should_autoscroll,
12080 },
12081 );
12082 }
12083
12084 // If any of the following highlights intersect with this one, merge them.
12085 while let Some(next_highlight) = row_highlights.get(ix + 1) {
12086 let highlight = &row_highlights[ix];
12087 if next_highlight
12088 .range
12089 .start
12090 .cmp(&highlight.range.end, &snapshot)
12091 .is_le()
12092 {
12093 if next_highlight
12094 .range
12095 .end
12096 .cmp(&highlight.range.end, &snapshot)
12097 .is_gt()
12098 {
12099 row_highlights[ix].range.end = next_highlight.range.end;
12100 }
12101 row_highlights.remove(ix + 1);
12102 } else {
12103 break;
12104 }
12105 }
12106 }
12107 }
12108
12109 /// Remove any highlighted row ranges of the given type that intersect the
12110 /// given ranges.
12111 pub fn remove_highlighted_rows<T: 'static>(
12112 &mut self,
12113 ranges_to_remove: Vec<Range<Anchor>>,
12114 cx: &mut ViewContext<Self>,
12115 ) {
12116 let snapshot = self.buffer().read(cx).snapshot(cx);
12117 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
12118 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
12119 row_highlights.retain(|highlight| {
12120 while let Some(range_to_remove) = ranges_to_remove.peek() {
12121 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
12122 Ordering::Less | Ordering::Equal => {
12123 ranges_to_remove.next();
12124 }
12125 Ordering::Greater => {
12126 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
12127 Ordering::Less | Ordering::Equal => {
12128 return false;
12129 }
12130 Ordering::Greater => break,
12131 }
12132 }
12133 }
12134 }
12135
12136 true
12137 })
12138 }
12139
12140 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
12141 pub fn clear_row_highlights<T: 'static>(&mut self) {
12142 self.highlighted_rows.remove(&TypeId::of::<T>());
12143 }
12144
12145 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
12146 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
12147 self.highlighted_rows
12148 .get(&TypeId::of::<T>())
12149 .map_or(&[] as &[_], |vec| vec.as_slice())
12150 .iter()
12151 .map(|highlight| (highlight.range.clone(), highlight.color))
12152 }
12153
12154 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
12155 /// Rerturns a map of display rows that are highlighted and their corresponding highlight color.
12156 /// Allows to ignore certain kinds of highlights.
12157 pub fn highlighted_display_rows(
12158 &mut self,
12159 cx: &mut WindowContext,
12160 ) -> BTreeMap<DisplayRow, Hsla> {
12161 let snapshot = self.snapshot(cx);
12162 let mut used_highlight_orders = HashMap::default();
12163 self.highlighted_rows
12164 .iter()
12165 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
12166 .fold(
12167 BTreeMap::<DisplayRow, Hsla>::new(),
12168 |mut unique_rows, highlight| {
12169 let start = highlight.range.start.to_display_point(&snapshot);
12170 let end = highlight.range.end.to_display_point(&snapshot);
12171 let start_row = start.row().0;
12172 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
12173 && end.column() == 0
12174 {
12175 end.row().0.saturating_sub(1)
12176 } else {
12177 end.row().0
12178 };
12179 for row in start_row..=end_row {
12180 let used_index =
12181 used_highlight_orders.entry(row).or_insert(highlight.index);
12182 if highlight.index >= *used_index {
12183 *used_index = highlight.index;
12184 unique_rows.insert(DisplayRow(row), highlight.color);
12185 }
12186 }
12187 unique_rows
12188 },
12189 )
12190 }
12191
12192 pub fn highlighted_display_row_for_autoscroll(
12193 &self,
12194 snapshot: &DisplaySnapshot,
12195 ) -> Option<DisplayRow> {
12196 self.highlighted_rows
12197 .values()
12198 .flat_map(|highlighted_rows| highlighted_rows.iter())
12199 .filter_map(|highlight| {
12200 if highlight.should_autoscroll {
12201 Some(highlight.range.start.to_display_point(snapshot).row())
12202 } else {
12203 None
12204 }
12205 })
12206 .min()
12207 }
12208
12209 pub fn set_search_within_ranges(
12210 &mut self,
12211 ranges: &[Range<Anchor>],
12212 cx: &mut ViewContext<Self>,
12213 ) {
12214 self.highlight_background::<SearchWithinRange>(
12215 ranges,
12216 |colors| colors.editor_document_highlight_read_background,
12217 cx,
12218 )
12219 }
12220
12221 pub fn set_breadcrumb_header(&mut self, new_header: String) {
12222 self.breadcrumb_header = Some(new_header);
12223 }
12224
12225 pub fn clear_search_within_ranges(&mut self, cx: &mut ViewContext<Self>) {
12226 self.clear_background_highlights::<SearchWithinRange>(cx);
12227 }
12228
12229 pub fn highlight_background<T: 'static>(
12230 &mut self,
12231 ranges: &[Range<Anchor>],
12232 color_fetcher: fn(&ThemeColors) -> Hsla,
12233 cx: &mut ViewContext<Self>,
12234 ) {
12235 self.background_highlights
12236 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
12237 self.scrollbar_marker_state.dirty = true;
12238 cx.notify();
12239 }
12240
12241 pub fn clear_background_highlights<T: 'static>(
12242 &mut self,
12243 cx: &mut ViewContext<Self>,
12244 ) -> Option<BackgroundHighlight> {
12245 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
12246 if !text_highlights.1.is_empty() {
12247 self.scrollbar_marker_state.dirty = true;
12248 cx.notify();
12249 }
12250 Some(text_highlights)
12251 }
12252
12253 pub fn highlight_gutter<T: 'static>(
12254 &mut self,
12255 ranges: &[Range<Anchor>],
12256 color_fetcher: fn(&AppContext) -> Hsla,
12257 cx: &mut ViewContext<Self>,
12258 ) {
12259 self.gutter_highlights
12260 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
12261 cx.notify();
12262 }
12263
12264 pub fn clear_gutter_highlights<T: 'static>(
12265 &mut self,
12266 cx: &mut ViewContext<Self>,
12267 ) -> Option<GutterHighlight> {
12268 cx.notify();
12269 self.gutter_highlights.remove(&TypeId::of::<T>())
12270 }
12271
12272 #[cfg(feature = "test-support")]
12273 pub fn all_text_background_highlights(
12274 &mut self,
12275 cx: &mut ViewContext<Self>,
12276 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
12277 let snapshot = self.snapshot(cx);
12278 let buffer = &snapshot.buffer_snapshot;
12279 let start = buffer.anchor_before(0);
12280 let end = buffer.anchor_after(buffer.len());
12281 let theme = cx.theme().colors();
12282 self.background_highlights_in_range(start..end, &snapshot, theme)
12283 }
12284
12285 #[cfg(feature = "test-support")]
12286 pub fn search_background_highlights(
12287 &mut self,
12288 cx: &mut ViewContext<Self>,
12289 ) -> Vec<Range<Point>> {
12290 let snapshot = self.buffer().read(cx).snapshot(cx);
12291
12292 let highlights = self
12293 .background_highlights
12294 .get(&TypeId::of::<items::BufferSearchHighlights>());
12295
12296 if let Some((_color, ranges)) = highlights {
12297 ranges
12298 .iter()
12299 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
12300 .collect_vec()
12301 } else {
12302 vec![]
12303 }
12304 }
12305
12306 fn document_highlights_for_position<'a>(
12307 &'a self,
12308 position: Anchor,
12309 buffer: &'a MultiBufferSnapshot,
12310 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
12311 let read_highlights = self
12312 .background_highlights
12313 .get(&TypeId::of::<DocumentHighlightRead>())
12314 .map(|h| &h.1);
12315 let write_highlights = self
12316 .background_highlights
12317 .get(&TypeId::of::<DocumentHighlightWrite>())
12318 .map(|h| &h.1);
12319 let left_position = position.bias_left(buffer);
12320 let right_position = position.bias_right(buffer);
12321 read_highlights
12322 .into_iter()
12323 .chain(write_highlights)
12324 .flat_map(move |ranges| {
12325 let start_ix = match ranges.binary_search_by(|probe| {
12326 let cmp = probe.end.cmp(&left_position, buffer);
12327 if cmp.is_ge() {
12328 Ordering::Greater
12329 } else {
12330 Ordering::Less
12331 }
12332 }) {
12333 Ok(i) | Err(i) => i,
12334 };
12335
12336 ranges[start_ix..]
12337 .iter()
12338 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
12339 })
12340 }
12341
12342 pub fn has_background_highlights<T: 'static>(&self) -> bool {
12343 self.background_highlights
12344 .get(&TypeId::of::<T>())
12345 .map_or(false, |(_, highlights)| !highlights.is_empty())
12346 }
12347
12348 pub fn background_highlights_in_range(
12349 &self,
12350 search_range: Range<Anchor>,
12351 display_snapshot: &DisplaySnapshot,
12352 theme: &ThemeColors,
12353 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
12354 let mut results = Vec::new();
12355 for (color_fetcher, ranges) in self.background_highlights.values() {
12356 let color = color_fetcher(theme);
12357 let start_ix = match ranges.binary_search_by(|probe| {
12358 let cmp = probe
12359 .end
12360 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
12361 if cmp.is_gt() {
12362 Ordering::Greater
12363 } else {
12364 Ordering::Less
12365 }
12366 }) {
12367 Ok(i) | Err(i) => i,
12368 };
12369 for range in &ranges[start_ix..] {
12370 if range
12371 .start
12372 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
12373 .is_ge()
12374 {
12375 break;
12376 }
12377
12378 let start = range.start.to_display_point(display_snapshot);
12379 let end = range.end.to_display_point(display_snapshot);
12380 results.push((start..end, color))
12381 }
12382 }
12383 results
12384 }
12385
12386 pub fn background_highlight_row_ranges<T: 'static>(
12387 &self,
12388 search_range: Range<Anchor>,
12389 display_snapshot: &DisplaySnapshot,
12390 count: usize,
12391 ) -> Vec<RangeInclusive<DisplayPoint>> {
12392 let mut results = Vec::new();
12393 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
12394 return vec![];
12395 };
12396
12397 let start_ix = match ranges.binary_search_by(|probe| {
12398 let cmp = probe
12399 .end
12400 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
12401 if cmp.is_gt() {
12402 Ordering::Greater
12403 } else {
12404 Ordering::Less
12405 }
12406 }) {
12407 Ok(i) | Err(i) => i,
12408 };
12409 let mut push_region = |start: Option<Point>, end: Option<Point>| {
12410 if let (Some(start_display), Some(end_display)) = (start, end) {
12411 results.push(
12412 start_display.to_display_point(display_snapshot)
12413 ..=end_display.to_display_point(display_snapshot),
12414 );
12415 }
12416 };
12417 let mut start_row: Option<Point> = None;
12418 let mut end_row: Option<Point> = None;
12419 if ranges.len() > count {
12420 return Vec::new();
12421 }
12422 for range in &ranges[start_ix..] {
12423 if range
12424 .start
12425 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
12426 .is_ge()
12427 {
12428 break;
12429 }
12430 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
12431 if let Some(current_row) = &end_row {
12432 if end.row == current_row.row {
12433 continue;
12434 }
12435 }
12436 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
12437 if start_row.is_none() {
12438 assert_eq!(end_row, None);
12439 start_row = Some(start);
12440 end_row = Some(end);
12441 continue;
12442 }
12443 if let Some(current_end) = end_row.as_mut() {
12444 if start.row > current_end.row + 1 {
12445 push_region(start_row, end_row);
12446 start_row = Some(start);
12447 end_row = Some(end);
12448 } else {
12449 // Merge two hunks.
12450 *current_end = end;
12451 }
12452 } else {
12453 unreachable!();
12454 }
12455 }
12456 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
12457 push_region(start_row, end_row);
12458 results
12459 }
12460
12461 pub fn gutter_highlights_in_range(
12462 &self,
12463 search_range: Range<Anchor>,
12464 display_snapshot: &DisplaySnapshot,
12465 cx: &AppContext,
12466 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
12467 let mut results = Vec::new();
12468 for (color_fetcher, ranges) in self.gutter_highlights.values() {
12469 let color = color_fetcher(cx);
12470 let start_ix = match ranges.binary_search_by(|probe| {
12471 let cmp = probe
12472 .end
12473 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
12474 if cmp.is_gt() {
12475 Ordering::Greater
12476 } else {
12477 Ordering::Less
12478 }
12479 }) {
12480 Ok(i) | Err(i) => i,
12481 };
12482 for range in &ranges[start_ix..] {
12483 if range
12484 .start
12485 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
12486 .is_ge()
12487 {
12488 break;
12489 }
12490
12491 let start = range.start.to_display_point(display_snapshot);
12492 let end = range.end.to_display_point(display_snapshot);
12493 results.push((start..end, color))
12494 }
12495 }
12496 results
12497 }
12498
12499 /// Get the text ranges corresponding to the redaction query
12500 pub fn redacted_ranges(
12501 &self,
12502 search_range: Range<Anchor>,
12503 display_snapshot: &DisplaySnapshot,
12504 cx: &WindowContext,
12505 ) -> Vec<Range<DisplayPoint>> {
12506 display_snapshot
12507 .buffer_snapshot
12508 .redacted_ranges(search_range, |file| {
12509 if let Some(file) = file {
12510 file.is_private()
12511 && EditorSettings::get(
12512 Some(SettingsLocation {
12513 worktree_id: file.worktree_id(cx),
12514 path: file.path().as_ref(),
12515 }),
12516 cx,
12517 )
12518 .redact_private_values
12519 } else {
12520 false
12521 }
12522 })
12523 .map(|range| {
12524 range.start.to_display_point(display_snapshot)
12525 ..range.end.to_display_point(display_snapshot)
12526 })
12527 .collect()
12528 }
12529
12530 pub fn highlight_text<T: 'static>(
12531 &mut self,
12532 ranges: Vec<Range<Anchor>>,
12533 style: HighlightStyle,
12534 cx: &mut ViewContext<Self>,
12535 ) {
12536 self.display_map.update(cx, |map, _| {
12537 map.highlight_text(TypeId::of::<T>(), ranges, style)
12538 });
12539 cx.notify();
12540 }
12541
12542 pub(crate) fn highlight_inlays<T: 'static>(
12543 &mut self,
12544 highlights: Vec<InlayHighlight>,
12545 style: HighlightStyle,
12546 cx: &mut ViewContext<Self>,
12547 ) {
12548 self.display_map.update(cx, |map, _| {
12549 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
12550 });
12551 cx.notify();
12552 }
12553
12554 pub fn text_highlights<'a, T: 'static>(
12555 &'a self,
12556 cx: &'a AppContext,
12557 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
12558 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
12559 }
12560
12561 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
12562 let cleared = self
12563 .display_map
12564 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
12565 if cleared {
12566 cx.notify();
12567 }
12568 }
12569
12570 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
12571 (self.read_only(cx) || self.blink_manager.read(cx).visible())
12572 && self.focus_handle.is_focused(cx)
12573 }
12574
12575 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut ViewContext<Self>) {
12576 self.show_cursor_when_unfocused = is_enabled;
12577 cx.notify();
12578 }
12579
12580 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
12581 cx.notify();
12582 }
12583
12584 fn on_buffer_event(
12585 &mut self,
12586 multibuffer: Model<MultiBuffer>,
12587 event: &multi_buffer::Event,
12588 cx: &mut ViewContext<Self>,
12589 ) {
12590 match event {
12591 multi_buffer::Event::Edited {
12592 singleton_buffer_edited,
12593 } => {
12594 self.scrollbar_marker_state.dirty = true;
12595 self.active_indent_guides_state.dirty = true;
12596 self.refresh_active_diagnostics(cx);
12597 self.refresh_code_actions(cx);
12598 if self.has_active_inline_completion(cx) {
12599 self.update_visible_inline_completion(cx);
12600 }
12601 cx.emit(EditorEvent::BufferEdited);
12602 cx.emit(SearchEvent::MatchesInvalidated);
12603 if *singleton_buffer_edited {
12604 if let Some(project) = &self.project {
12605 let project = project.read(cx);
12606 #[allow(clippy::mutable_key_type)]
12607 let languages_affected = multibuffer
12608 .read(cx)
12609 .all_buffers()
12610 .into_iter()
12611 .filter_map(|buffer| {
12612 let buffer = buffer.read(cx);
12613 let language = buffer.language()?;
12614 if project.is_local()
12615 && project.language_servers_for_buffer(buffer, cx).count() == 0
12616 {
12617 None
12618 } else {
12619 Some(language)
12620 }
12621 })
12622 .cloned()
12623 .collect::<HashSet<_>>();
12624 if !languages_affected.is_empty() {
12625 self.refresh_inlay_hints(
12626 InlayHintRefreshReason::BufferEdited(languages_affected),
12627 cx,
12628 );
12629 }
12630 }
12631 }
12632
12633 let Some(project) = &self.project else { return };
12634 let (telemetry, is_via_ssh) = {
12635 let project = project.read(cx);
12636 let telemetry = project.client().telemetry().clone();
12637 let is_via_ssh = project.is_via_ssh();
12638 (telemetry, is_via_ssh)
12639 };
12640 refresh_linked_ranges(self, cx);
12641 telemetry.log_edit_event("editor", is_via_ssh);
12642 }
12643 multi_buffer::Event::ExcerptsAdded {
12644 buffer,
12645 predecessor,
12646 excerpts,
12647 } => {
12648 self.tasks_update_task = Some(self.refresh_runnables(cx));
12649 cx.emit(EditorEvent::ExcerptsAdded {
12650 buffer: buffer.clone(),
12651 predecessor: *predecessor,
12652 excerpts: excerpts.clone(),
12653 });
12654 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
12655 }
12656 multi_buffer::Event::ExcerptsRemoved { ids } => {
12657 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
12658 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
12659 }
12660 multi_buffer::Event::ExcerptsEdited { ids } => {
12661 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
12662 }
12663 multi_buffer::Event::ExcerptsExpanded { ids } => {
12664 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
12665 }
12666 multi_buffer::Event::Reparsed(buffer_id) => {
12667 self.tasks_update_task = Some(self.refresh_runnables(cx));
12668
12669 cx.emit(EditorEvent::Reparsed(*buffer_id));
12670 }
12671 multi_buffer::Event::LanguageChanged(buffer_id) => {
12672 linked_editing_ranges::refresh_linked_ranges(self, cx);
12673 cx.emit(EditorEvent::Reparsed(*buffer_id));
12674 cx.notify();
12675 }
12676 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
12677 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
12678 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
12679 cx.emit(EditorEvent::TitleChanged)
12680 }
12681 // multi_buffer::Event::DiffBaseChanged => {
12682 // self.scrollbar_marker_state.dirty = true;
12683 // cx.emit(EditorEvent::DiffBaseChanged);
12684 // cx.notify();
12685 // }
12686 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
12687 multi_buffer::Event::DiagnosticsUpdated => {
12688 self.refresh_active_diagnostics(cx);
12689 self.scrollbar_marker_state.dirty = true;
12690 cx.notify();
12691 }
12692 _ => {}
12693 };
12694 }
12695
12696 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
12697 cx.notify();
12698 }
12699
12700 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
12701 self.tasks_update_task = Some(self.refresh_runnables(cx));
12702 self.refresh_inline_completion(true, false, cx);
12703 self.refresh_inlay_hints(
12704 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
12705 self.selections.newest_anchor().head(),
12706 &self.buffer.read(cx).snapshot(cx),
12707 cx,
12708 )),
12709 cx,
12710 );
12711
12712 let old_cursor_shape = self.cursor_shape;
12713
12714 {
12715 let editor_settings = EditorSettings::get_global(cx);
12716 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
12717 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
12718 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
12719 }
12720
12721 if old_cursor_shape != self.cursor_shape {
12722 cx.emit(EditorEvent::CursorShapeChanged);
12723 }
12724
12725 let project_settings = ProjectSettings::get_global(cx);
12726 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
12727
12728 if self.mode == EditorMode::Full {
12729 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
12730 if self.git_blame_inline_enabled != inline_blame_enabled {
12731 self.toggle_git_blame_inline_internal(false, cx);
12732 }
12733 }
12734
12735 cx.notify();
12736 }
12737
12738 pub fn set_searchable(&mut self, searchable: bool) {
12739 self.searchable = searchable;
12740 }
12741
12742 pub fn searchable(&self) -> bool {
12743 self.searchable
12744 }
12745
12746 fn open_proposed_changes_editor(
12747 &mut self,
12748 _: &OpenProposedChangesEditor,
12749 cx: &mut ViewContext<Self>,
12750 ) {
12751 let Some(workspace) = self.workspace() else {
12752 cx.propagate();
12753 return;
12754 };
12755
12756 let selections = self.selections.all::<usize>(cx);
12757 let buffer = self.buffer.read(cx);
12758 let mut new_selections_by_buffer = HashMap::default();
12759 for selection in selections {
12760 for (buffer, range, _) in
12761 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
12762 {
12763 let mut range = range.to_point(buffer.read(cx));
12764 range.start.column = 0;
12765 range.end.column = buffer.read(cx).line_len(range.end.row);
12766 new_selections_by_buffer
12767 .entry(buffer)
12768 .or_insert(Vec::new())
12769 .push(range)
12770 }
12771 }
12772
12773 let proposed_changes_buffers = new_selections_by_buffer
12774 .into_iter()
12775 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
12776 .collect::<Vec<_>>();
12777 let proposed_changes_editor = cx.new_view(|cx| {
12778 ProposedChangesEditor::new(
12779 "Proposed changes",
12780 proposed_changes_buffers,
12781 self.project.clone(),
12782 cx,
12783 )
12784 });
12785
12786 cx.window_context().defer(move |cx| {
12787 workspace.update(cx, |workspace, cx| {
12788 workspace.active_pane().update(cx, |pane, cx| {
12789 pane.add_item(Box::new(proposed_changes_editor), true, true, None, cx);
12790 });
12791 });
12792 });
12793 }
12794
12795 pub fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext<Self>) {
12796 self.open_excerpts_common(None, true, cx)
12797 }
12798
12799 pub fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
12800 self.open_excerpts_common(None, false, cx)
12801 }
12802
12803 fn open_excerpts_common(
12804 &mut self,
12805 jump_data: Option<JumpData>,
12806 split: bool,
12807 cx: &mut ViewContext<Self>,
12808 ) {
12809 let Some(workspace) = self.workspace() else {
12810 cx.propagate();
12811 return;
12812 };
12813
12814 if self.buffer.read(cx).is_singleton() {
12815 cx.propagate();
12816 return;
12817 }
12818
12819 let mut new_selections_by_buffer = HashMap::default();
12820 match &jump_data {
12821 Some(jump_data) => {
12822 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12823 if let Some(buffer) = multi_buffer_snapshot
12824 .buffer_id_for_excerpt(jump_data.excerpt_id)
12825 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
12826 {
12827 let buffer_snapshot = buffer.read(cx).snapshot();
12828 let jump_to_point = if buffer_snapshot.can_resolve(&jump_data.anchor) {
12829 language::ToPoint::to_point(&jump_data.anchor, &buffer_snapshot)
12830 } else {
12831 buffer_snapshot.clip_point(jump_data.position, Bias::Left)
12832 };
12833 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
12834 new_selections_by_buffer.insert(
12835 buffer,
12836 (
12837 vec![jump_to_offset..jump_to_offset],
12838 Some(jump_data.line_offset_from_top),
12839 ),
12840 );
12841 }
12842 }
12843 None => {
12844 let selections = self.selections.all::<usize>(cx);
12845 let buffer = self.buffer.read(cx);
12846 for selection in selections {
12847 for (mut buffer_handle, mut range, _) in
12848 buffer.range_to_buffer_ranges(selection.range(), cx)
12849 {
12850 // When editing branch buffers, jump to the corresponding location
12851 // in their base buffer.
12852 let buffer = buffer_handle.read(cx);
12853 if let Some(base_buffer) = buffer.base_buffer() {
12854 range = buffer.range_to_version(range, &base_buffer.read(cx).version());
12855 buffer_handle = base_buffer;
12856 }
12857
12858 if selection.reversed {
12859 mem::swap(&mut range.start, &mut range.end);
12860 }
12861 new_selections_by_buffer
12862 .entry(buffer_handle)
12863 .or_insert((Vec::new(), None))
12864 .0
12865 .push(range)
12866 }
12867 }
12868 }
12869 }
12870
12871 if new_selections_by_buffer.is_empty() {
12872 return;
12873 }
12874
12875 // We defer the pane interaction because we ourselves are a workspace item
12876 // and activating a new item causes the pane to call a method on us reentrantly,
12877 // which panics if we're on the stack.
12878 cx.window_context().defer(move |cx| {
12879 workspace.update(cx, |workspace, cx| {
12880 let pane = if split {
12881 workspace.adjacent_pane(cx)
12882 } else {
12883 workspace.active_pane().clone()
12884 };
12885
12886 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
12887 let editor = buffer
12888 .read(cx)
12889 .file()
12890 .is_none()
12891 .then(|| {
12892 // Handle file-less buffers separately: those are not really the project items, so won't have a paroject path or entity id,
12893 // so `workspace.open_project_item` will never find them, always opening a new editor.
12894 // Instead, we try to activate the existing editor in the pane first.
12895 let (editor, pane_item_index) =
12896 pane.read(cx).items().enumerate().find_map(|(i, item)| {
12897 let editor = item.downcast::<Editor>()?;
12898 let singleton_buffer =
12899 editor.read(cx).buffer().read(cx).as_singleton()?;
12900 if singleton_buffer == buffer {
12901 Some((editor, i))
12902 } else {
12903 None
12904 }
12905 })?;
12906 pane.update(cx, |pane, cx| {
12907 pane.activate_item(pane_item_index, true, true, cx)
12908 });
12909 Some(editor)
12910 })
12911 .flatten()
12912 .unwrap_or_else(|| {
12913 workspace.open_project_item::<Self>(
12914 pane.clone(),
12915 buffer,
12916 true,
12917 true,
12918 cx,
12919 )
12920 });
12921
12922 editor.update(cx, |editor, cx| {
12923 let autoscroll = match scroll_offset {
12924 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
12925 None => Autoscroll::newest(),
12926 };
12927 let nav_history = editor.nav_history.take();
12928 editor.unfold_ranges(&ranges, false, true, cx);
12929 editor.change_selections(Some(autoscroll), cx, |s| {
12930 s.select_ranges(ranges);
12931 });
12932 editor.nav_history = nav_history;
12933 });
12934 }
12935 })
12936 });
12937 }
12938
12939 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
12940 let snapshot = self.buffer.read(cx).read(cx);
12941 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
12942 Some(
12943 ranges
12944 .iter()
12945 .map(move |range| {
12946 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
12947 })
12948 .collect(),
12949 )
12950 }
12951
12952 fn selection_replacement_ranges(
12953 &self,
12954 range: Range<OffsetUtf16>,
12955 cx: &mut AppContext,
12956 ) -> Vec<Range<OffsetUtf16>> {
12957 let selections = self.selections.all::<OffsetUtf16>(cx);
12958 let newest_selection = selections
12959 .iter()
12960 .max_by_key(|selection| selection.id)
12961 .unwrap();
12962 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
12963 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
12964 let snapshot = self.buffer.read(cx).read(cx);
12965 selections
12966 .into_iter()
12967 .map(|mut selection| {
12968 selection.start.0 =
12969 (selection.start.0 as isize).saturating_add(start_delta) as usize;
12970 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
12971 snapshot.clip_offset_utf16(selection.start, Bias::Left)
12972 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
12973 })
12974 .collect()
12975 }
12976
12977 fn report_editor_event(
12978 &self,
12979 operation: &'static str,
12980 file_extension: Option<String>,
12981 cx: &AppContext,
12982 ) {
12983 if cfg!(any(test, feature = "test-support")) {
12984 return;
12985 }
12986
12987 let Some(project) = &self.project else { return };
12988
12989 // If None, we are in a file without an extension
12990 let file = self
12991 .buffer
12992 .read(cx)
12993 .as_singleton()
12994 .and_then(|b| b.read(cx).file());
12995 let file_extension = file_extension.or(file
12996 .as_ref()
12997 .and_then(|file| Path::new(file.file_name(cx)).extension())
12998 .and_then(|e| e.to_str())
12999 .map(|a| a.to_string()));
13000
13001 let vim_mode = cx
13002 .global::<SettingsStore>()
13003 .raw_user_settings()
13004 .get("vim_mode")
13005 == Some(&serde_json::Value::Bool(true));
13006
13007 let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
13008 == language::language_settings::InlineCompletionProvider::Copilot;
13009 let copilot_enabled_for_language = self
13010 .buffer
13011 .read(cx)
13012 .settings_at(0, cx)
13013 .show_inline_completions;
13014
13015 let project = project.read(cx);
13016 let telemetry = project.client().telemetry().clone();
13017 telemetry.report_editor_event(
13018 file_extension,
13019 vim_mode,
13020 operation,
13021 copilot_enabled,
13022 copilot_enabled_for_language,
13023 project.is_via_ssh(),
13024 )
13025 }
13026
13027 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
13028 /// with each line being an array of {text, highlight} objects.
13029 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
13030 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
13031 return;
13032 };
13033
13034 #[derive(Serialize)]
13035 struct Chunk<'a> {
13036 text: String,
13037 highlight: Option<&'a str>,
13038 }
13039
13040 let snapshot = buffer.read(cx).snapshot();
13041 let range = self
13042 .selected_text_range(false, cx)
13043 .and_then(|selection| {
13044 if selection.range.is_empty() {
13045 None
13046 } else {
13047 Some(selection.range)
13048 }
13049 })
13050 .unwrap_or_else(|| 0..snapshot.len());
13051
13052 let chunks = snapshot.chunks(range, true);
13053 let mut lines = Vec::new();
13054 let mut line: VecDeque<Chunk> = VecDeque::new();
13055
13056 let Some(style) = self.style.as_ref() else {
13057 return;
13058 };
13059
13060 for chunk in chunks {
13061 let highlight = chunk
13062 .syntax_highlight_id
13063 .and_then(|id| id.name(&style.syntax));
13064 let mut chunk_lines = chunk.text.split('\n').peekable();
13065 while let Some(text) = chunk_lines.next() {
13066 let mut merged_with_last_token = false;
13067 if let Some(last_token) = line.back_mut() {
13068 if last_token.highlight == highlight {
13069 last_token.text.push_str(text);
13070 merged_with_last_token = true;
13071 }
13072 }
13073
13074 if !merged_with_last_token {
13075 line.push_back(Chunk {
13076 text: text.into(),
13077 highlight,
13078 });
13079 }
13080
13081 if chunk_lines.peek().is_some() {
13082 if line.len() > 1 && line.front().unwrap().text.is_empty() {
13083 line.pop_front();
13084 }
13085 if line.len() > 1 && line.back().unwrap().text.is_empty() {
13086 line.pop_back();
13087 }
13088
13089 lines.push(mem::take(&mut line));
13090 }
13091 }
13092 }
13093
13094 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
13095 return;
13096 };
13097 cx.write_to_clipboard(ClipboardItem::new_string(lines));
13098 }
13099
13100 pub fn open_context_menu(&mut self, _: &OpenContextMenu, cx: &mut ViewContext<Self>) {
13101 self.request_autoscroll(Autoscroll::newest(), cx);
13102 let position = self.selections.newest_display(cx).start;
13103 mouse_context_menu::deploy_context_menu(self, None, position, cx);
13104 }
13105
13106 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
13107 &self.inlay_hint_cache
13108 }
13109
13110 pub fn replay_insert_event(
13111 &mut self,
13112 text: &str,
13113 relative_utf16_range: Option<Range<isize>>,
13114 cx: &mut ViewContext<Self>,
13115 ) {
13116 if !self.input_enabled {
13117 cx.emit(EditorEvent::InputIgnored { text: text.into() });
13118 return;
13119 }
13120 if let Some(relative_utf16_range) = relative_utf16_range {
13121 let selections = self.selections.all::<OffsetUtf16>(cx);
13122 self.change_selections(None, cx, |s| {
13123 let new_ranges = selections.into_iter().map(|range| {
13124 let start = OffsetUtf16(
13125 range
13126 .head()
13127 .0
13128 .saturating_add_signed(relative_utf16_range.start),
13129 );
13130 let end = OffsetUtf16(
13131 range
13132 .head()
13133 .0
13134 .saturating_add_signed(relative_utf16_range.end),
13135 );
13136 start..end
13137 });
13138 s.select_ranges(new_ranges);
13139 });
13140 }
13141
13142 self.handle_input(text, cx);
13143 }
13144
13145 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
13146 let Some(provider) = self.semantics_provider.as_ref() else {
13147 return false;
13148 };
13149
13150 let mut supports = false;
13151 self.buffer().read(cx).for_each_buffer(|buffer| {
13152 supports |= provider.supports_inlay_hints(buffer, cx);
13153 });
13154 supports
13155 }
13156
13157 pub fn focus(&self, cx: &mut WindowContext) {
13158 cx.focus(&self.focus_handle)
13159 }
13160
13161 pub fn is_focused(&self, cx: &WindowContext) -> bool {
13162 self.focus_handle.is_focused(cx)
13163 }
13164
13165 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
13166 cx.emit(EditorEvent::Focused);
13167
13168 if let Some(descendant) = self
13169 .last_focused_descendant
13170 .take()
13171 .and_then(|descendant| descendant.upgrade())
13172 {
13173 cx.focus(&descendant);
13174 } else {
13175 if let Some(blame) = self.blame.as_ref() {
13176 blame.update(cx, GitBlame::focus)
13177 }
13178
13179 self.blink_manager.update(cx, BlinkManager::enable);
13180 self.show_cursor_names(cx);
13181 self.buffer.update(cx, |buffer, cx| {
13182 buffer.finalize_last_transaction(cx);
13183 if self.leader_peer_id.is_none() {
13184 buffer.set_active_selections(
13185 &self.selections.disjoint_anchors(),
13186 self.selections.line_mode,
13187 self.cursor_shape,
13188 cx,
13189 );
13190 }
13191 });
13192 }
13193 }
13194
13195 fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
13196 cx.emit(EditorEvent::FocusedIn)
13197 }
13198
13199 fn handle_focus_out(&mut self, event: FocusOutEvent, _cx: &mut ViewContext<Self>) {
13200 if event.blurred != self.focus_handle {
13201 self.last_focused_descendant = Some(event.blurred);
13202 }
13203 }
13204
13205 pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
13206 self.blink_manager.update(cx, BlinkManager::disable);
13207 self.buffer
13208 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
13209
13210 if let Some(blame) = self.blame.as_ref() {
13211 blame.update(cx, GitBlame::blur)
13212 }
13213 if !self.hover_state.focused(cx) {
13214 hide_hover(self, cx);
13215 }
13216
13217 self.hide_context_menu(cx);
13218 cx.emit(EditorEvent::Blurred);
13219 cx.notify();
13220 }
13221
13222 pub fn register_action<A: Action>(
13223 &mut self,
13224 listener: impl Fn(&A, &mut WindowContext) + 'static,
13225 ) -> Subscription {
13226 let id = self.next_editor_action_id.post_inc();
13227 let listener = Arc::new(listener);
13228 self.editor_actions.borrow_mut().insert(
13229 id,
13230 Box::new(move |cx| {
13231 let cx = cx.window_context();
13232 let listener = listener.clone();
13233 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
13234 let action = action.downcast_ref().unwrap();
13235 if phase == DispatchPhase::Bubble {
13236 listener(action, cx)
13237 }
13238 })
13239 }),
13240 );
13241
13242 let editor_actions = self.editor_actions.clone();
13243 Subscription::new(move || {
13244 editor_actions.borrow_mut().remove(&id);
13245 })
13246 }
13247
13248 pub fn file_header_size(&self) -> u32 {
13249 FILE_HEADER_HEIGHT
13250 }
13251
13252 pub fn revert(
13253 &mut self,
13254 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
13255 cx: &mut ViewContext<Self>,
13256 ) {
13257 self.buffer().update(cx, |multi_buffer, cx| {
13258 for (buffer_id, changes) in revert_changes {
13259 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
13260 buffer.update(cx, |buffer, cx| {
13261 buffer.edit(
13262 changes.into_iter().map(|(range, text)| {
13263 (range, text.to_string().map(Arc::<str>::from))
13264 }),
13265 None,
13266 cx,
13267 );
13268 });
13269 }
13270 }
13271 });
13272 self.change_selections(None, cx, |selections| selections.refresh());
13273 }
13274
13275 pub fn to_pixel_point(
13276 &mut self,
13277 source: multi_buffer::Anchor,
13278 editor_snapshot: &EditorSnapshot,
13279 cx: &mut ViewContext<Self>,
13280 ) -> Option<gpui::Point<Pixels>> {
13281 let source_point = source.to_display_point(editor_snapshot);
13282 self.display_to_pixel_point(source_point, editor_snapshot, cx)
13283 }
13284
13285 pub fn display_to_pixel_point(
13286 &mut self,
13287 source: DisplayPoint,
13288 editor_snapshot: &EditorSnapshot,
13289 cx: &mut ViewContext<Self>,
13290 ) -> Option<gpui::Point<Pixels>> {
13291 let line_height = self.style()?.text.line_height_in_pixels(cx.rem_size());
13292 let text_layout_details = self.text_layout_details(cx);
13293 let scroll_top = text_layout_details
13294 .scroll_anchor
13295 .scroll_position(editor_snapshot)
13296 .y;
13297
13298 if source.row().as_f32() < scroll_top.floor() {
13299 return None;
13300 }
13301 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
13302 let source_y = line_height * (source.row().as_f32() - scroll_top);
13303 Some(gpui::Point::new(source_x, source_y))
13304 }
13305
13306 pub fn has_active_completions_menu(&self) -> bool {
13307 self.context_menu.read().as_ref().map_or(false, |menu| {
13308 menu.visible() && matches!(menu, ContextMenu::Completions(_))
13309 })
13310 }
13311
13312 pub fn register_addon<T: Addon>(&mut self, instance: T) {
13313 self.addons
13314 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
13315 }
13316
13317 pub fn unregister_addon<T: Addon>(&mut self) {
13318 self.addons.remove(&std::any::TypeId::of::<T>());
13319 }
13320
13321 pub fn addon<T: Addon>(&self) -> Option<&T> {
13322 let type_id = std::any::TypeId::of::<T>();
13323 self.addons
13324 .get(&type_id)
13325 .and_then(|item| item.to_any().downcast_ref::<T>())
13326 }
13327
13328 fn character_size(&self, cx: &mut ViewContext<Self>) -> gpui::Point<Pixels> {
13329 let text_layout_details = self.text_layout_details(cx);
13330 let style = &text_layout_details.editor_style;
13331 let font_id = cx.text_system().resolve_font(&style.text.font());
13332 let font_size = style.text.font_size.to_pixels(cx.rem_size());
13333 let line_height = style.text.line_height_in_pixels(cx.rem_size());
13334
13335 let em_width = cx
13336 .text_system()
13337 .typographic_bounds(font_id, font_size, 'm')
13338 .unwrap()
13339 .size
13340 .width;
13341
13342 gpui::Point::new(em_width, line_height)
13343 }
13344}
13345
13346fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
13347 let tab_size = tab_size.get() as usize;
13348 let mut width = offset;
13349
13350 for ch in text.chars() {
13351 width += if ch == '\t' {
13352 tab_size - (width % tab_size)
13353 } else {
13354 1
13355 };
13356 }
13357
13358 width - offset
13359}
13360
13361#[cfg(test)]
13362mod tests {
13363 use super::*;
13364
13365 #[test]
13366 fn test_string_size_with_expanded_tabs() {
13367 let nz = |val| NonZeroU32::new(val).unwrap();
13368 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
13369 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
13370 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
13371 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
13372 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
13373 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
13374 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
13375 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
13376 }
13377}
13378
13379/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
13380struct WordBreakingTokenizer<'a> {
13381 input: &'a str,
13382}
13383
13384impl<'a> WordBreakingTokenizer<'a> {
13385 fn new(input: &'a str) -> Self {
13386 Self { input }
13387 }
13388}
13389
13390fn is_char_ideographic(ch: char) -> bool {
13391 use unicode_script::Script::*;
13392 use unicode_script::UnicodeScript;
13393 matches!(ch.script(), Han | Tangut | Yi)
13394}
13395
13396fn is_grapheme_ideographic(text: &str) -> bool {
13397 text.chars().any(is_char_ideographic)
13398}
13399
13400fn is_grapheme_whitespace(text: &str) -> bool {
13401 text.chars().any(|x| x.is_whitespace())
13402}
13403
13404fn should_stay_with_preceding_ideograph(text: &str) -> bool {
13405 text.chars().next().map_or(false, |ch| {
13406 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
13407 })
13408}
13409
13410#[derive(PartialEq, Eq, Debug, Clone, Copy)]
13411struct WordBreakToken<'a> {
13412 token: &'a str,
13413 grapheme_len: usize,
13414 is_whitespace: bool,
13415}
13416
13417impl<'a> Iterator for WordBreakingTokenizer<'a> {
13418 /// Yields a span, the count of graphemes in the token, and whether it was
13419 /// whitespace. Note that it also breaks at word boundaries.
13420 type Item = WordBreakToken<'a>;
13421
13422 fn next(&mut self) -> Option<Self::Item> {
13423 use unicode_segmentation::UnicodeSegmentation;
13424 if self.input.is_empty() {
13425 return None;
13426 }
13427
13428 let mut iter = self.input.graphemes(true).peekable();
13429 let mut offset = 0;
13430 let mut graphemes = 0;
13431 if let Some(first_grapheme) = iter.next() {
13432 let is_whitespace = is_grapheme_whitespace(first_grapheme);
13433 offset += first_grapheme.len();
13434 graphemes += 1;
13435 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
13436 if let Some(grapheme) = iter.peek().copied() {
13437 if should_stay_with_preceding_ideograph(grapheme) {
13438 offset += grapheme.len();
13439 graphemes += 1;
13440 }
13441 }
13442 } else {
13443 let mut words = self.input[offset..].split_word_bound_indices().peekable();
13444 let mut next_word_bound = words.peek().copied();
13445 if next_word_bound.map_or(false, |(i, _)| i == 0) {
13446 next_word_bound = words.next();
13447 }
13448 while let Some(grapheme) = iter.peek().copied() {
13449 if next_word_bound.map_or(false, |(i, _)| i == offset) {
13450 break;
13451 };
13452 if is_grapheme_whitespace(grapheme) != is_whitespace {
13453 break;
13454 };
13455 offset += grapheme.len();
13456 graphemes += 1;
13457 iter.next();
13458 }
13459 }
13460 let token = &self.input[..offset];
13461 self.input = &self.input[offset..];
13462 if is_whitespace {
13463 Some(WordBreakToken {
13464 token: " ",
13465 grapheme_len: 1,
13466 is_whitespace: true,
13467 })
13468 } else {
13469 Some(WordBreakToken {
13470 token,
13471 grapheme_len: graphemes,
13472 is_whitespace: false,
13473 })
13474 }
13475 } else {
13476 None
13477 }
13478 }
13479}
13480
13481#[test]
13482fn test_word_breaking_tokenizer() {
13483 let tests: &[(&str, &[(&str, usize, bool)])] = &[
13484 ("", &[]),
13485 (" ", &[(" ", 1, true)]),
13486 ("Ʒ", &[("Ʒ", 1, false)]),
13487 ("Ǽ", &[("Ǽ", 1, false)]),
13488 ("⋑", &[("⋑", 1, false)]),
13489 ("⋑⋑", &[("⋑⋑", 2, false)]),
13490 (
13491 "原理,进而",
13492 &[
13493 ("原", 1, false),
13494 ("理,", 2, false),
13495 ("进", 1, false),
13496 ("而", 1, false),
13497 ],
13498 ),
13499 (
13500 "hello world",
13501 &[("hello", 5, false), (" ", 1, true), ("world", 5, false)],
13502 ),
13503 (
13504 "hello, world",
13505 &[("hello,", 6, false), (" ", 1, true), ("world", 5, false)],
13506 ),
13507 (
13508 " hello world",
13509 &[
13510 (" ", 1, true),
13511 ("hello", 5, false),
13512 (" ", 1, true),
13513 ("world", 5, false),
13514 ],
13515 ),
13516 (
13517 "这是什么 \n 钢笔",
13518 &[
13519 ("这", 1, false),
13520 ("是", 1, false),
13521 ("什", 1, false),
13522 ("么", 1, false),
13523 (" ", 1, true),
13524 ("钢", 1, false),
13525 ("笔", 1, false),
13526 ],
13527 ),
13528 (" mutton", &[(" ", 1, true), ("mutton", 6, false)]),
13529 ];
13530
13531 for (input, result) in tests {
13532 assert_eq!(
13533 WordBreakingTokenizer::new(input).collect::<Vec<_>>(),
13534 result
13535 .iter()
13536 .copied()
13537 .map(|(token, grapheme_len, is_whitespace)| WordBreakToken {
13538 token,
13539 grapheme_len,
13540 is_whitespace,
13541 })
13542 .collect::<Vec<_>>()
13543 );
13544 }
13545}
13546
13547fn wrap_with_prefix(
13548 line_prefix: String,
13549 unwrapped_text: String,
13550 wrap_column: usize,
13551 tab_size: NonZeroU32,
13552) -> String {
13553 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
13554 let mut wrapped_text = String::new();
13555 let mut current_line = line_prefix.clone();
13556
13557 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
13558 let mut current_line_len = line_prefix_len;
13559 for WordBreakToken {
13560 token,
13561 grapheme_len,
13562 is_whitespace,
13563 } in tokenizer
13564 {
13565 if current_line_len + grapheme_len > wrap_column && current_line_len != line_prefix_len {
13566 wrapped_text.push_str(current_line.trim_end());
13567 wrapped_text.push('\n');
13568 current_line.truncate(line_prefix.len());
13569 current_line_len = line_prefix_len;
13570 if !is_whitespace {
13571 current_line.push_str(token);
13572 current_line_len += grapheme_len;
13573 }
13574 } else if !is_whitespace {
13575 current_line.push_str(token);
13576 current_line_len += grapheme_len;
13577 } else if current_line_len != line_prefix_len {
13578 current_line.push(' ');
13579 current_line_len += 1;
13580 }
13581 }
13582
13583 if !current_line.is_empty() {
13584 wrapped_text.push_str(¤t_line);
13585 }
13586 wrapped_text
13587}
13588
13589#[test]
13590fn test_wrap_with_prefix() {
13591 assert_eq!(
13592 wrap_with_prefix(
13593 "# ".to_string(),
13594 "abcdefg".to_string(),
13595 4,
13596 NonZeroU32::new(4).unwrap()
13597 ),
13598 "# abcdefg"
13599 );
13600 assert_eq!(
13601 wrap_with_prefix(
13602 "".to_string(),
13603 "\thello world".to_string(),
13604 8,
13605 NonZeroU32::new(4).unwrap()
13606 ),
13607 "hello\nworld"
13608 );
13609 assert_eq!(
13610 wrap_with_prefix(
13611 "// ".to_string(),
13612 "xx \nyy zz aa bb cc".to_string(),
13613 12,
13614 NonZeroU32::new(4).unwrap()
13615 ),
13616 "// xx yy zz\n// aa bb cc"
13617 );
13618 assert_eq!(
13619 wrap_with_prefix(
13620 String::new(),
13621 "这是什么 \n 钢笔".to_string(),
13622 3,
13623 NonZeroU32::new(4).unwrap()
13624 ),
13625 "这是什\n么 钢\n笔"
13626 );
13627}
13628
13629fn hunks_for_selections(
13630 snapshot: &EditorSnapshot,
13631 selections: &[Selection<Point>],
13632) -> Vec<MultiBufferDiffHunk> {
13633 hunks_for_ranges(
13634 selections.iter().map(|selection| selection.range()),
13635 snapshot,
13636 )
13637}
13638
13639pub fn hunks_for_ranges(
13640 ranges: impl Iterator<Item = Range<Point>>,
13641 snapshot: &EditorSnapshot,
13642) -> Vec<MultiBufferDiffHunk> {
13643 let mut hunks = Vec::new();
13644 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
13645 HashMap::default();
13646 for query_range in ranges {
13647 let query_rows =
13648 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
13649 for hunk in snapshot.diff_map.diff_hunks_in_range(
13650 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
13651 &snapshot.buffer_snapshot,
13652 ) {
13653 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
13654 // when the caret is just above or just below the deleted hunk.
13655 let allow_adjacent = hunk_status(&hunk) == DiffHunkStatus::Removed;
13656 let related_to_selection = if allow_adjacent {
13657 hunk.row_range.overlaps(&query_rows)
13658 || hunk.row_range.start == query_rows.end
13659 || hunk.row_range.end == query_rows.start
13660 } else {
13661 hunk.row_range.overlaps(&query_rows)
13662 };
13663 if related_to_selection {
13664 if !processed_buffer_rows
13665 .entry(hunk.buffer_id)
13666 .or_default()
13667 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
13668 {
13669 continue;
13670 }
13671 hunks.push(hunk);
13672 }
13673 }
13674 }
13675
13676 hunks
13677}
13678
13679pub trait CollaborationHub {
13680 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
13681 fn user_participant_indices<'a>(
13682 &self,
13683 cx: &'a AppContext,
13684 ) -> &'a HashMap<u64, ParticipantIndex>;
13685 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
13686}
13687
13688impl CollaborationHub for Model<Project> {
13689 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
13690 self.read(cx).collaborators()
13691 }
13692
13693 fn user_participant_indices<'a>(
13694 &self,
13695 cx: &'a AppContext,
13696 ) -> &'a HashMap<u64, ParticipantIndex> {
13697 self.read(cx).user_store().read(cx).participant_indices()
13698 }
13699
13700 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
13701 let this = self.read(cx);
13702 let user_ids = this.collaborators().values().map(|c| c.user_id);
13703 this.user_store().read_with(cx, |user_store, cx| {
13704 user_store.participant_names(user_ids, cx)
13705 })
13706 }
13707}
13708
13709pub trait SemanticsProvider {
13710 fn hover(
13711 &self,
13712 buffer: &Model<Buffer>,
13713 position: text::Anchor,
13714 cx: &mut AppContext,
13715 ) -> Option<Task<Vec<project::Hover>>>;
13716
13717 fn inlay_hints(
13718 &self,
13719 buffer_handle: Model<Buffer>,
13720 range: Range<text::Anchor>,
13721 cx: &mut AppContext,
13722 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
13723
13724 fn resolve_inlay_hint(
13725 &self,
13726 hint: InlayHint,
13727 buffer_handle: Model<Buffer>,
13728 server_id: LanguageServerId,
13729 cx: &mut AppContext,
13730 ) -> Option<Task<anyhow::Result<InlayHint>>>;
13731
13732 fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool;
13733
13734 fn document_highlights(
13735 &self,
13736 buffer: &Model<Buffer>,
13737 position: text::Anchor,
13738 cx: &mut AppContext,
13739 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
13740
13741 fn definitions(
13742 &self,
13743 buffer: &Model<Buffer>,
13744 position: text::Anchor,
13745 kind: GotoDefinitionKind,
13746 cx: &mut AppContext,
13747 ) -> Option<Task<Result<Vec<LocationLink>>>>;
13748
13749 fn range_for_rename(
13750 &self,
13751 buffer: &Model<Buffer>,
13752 position: text::Anchor,
13753 cx: &mut AppContext,
13754 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
13755
13756 fn perform_rename(
13757 &self,
13758 buffer: &Model<Buffer>,
13759 position: text::Anchor,
13760 new_name: String,
13761 cx: &mut AppContext,
13762 ) -> Option<Task<Result<ProjectTransaction>>>;
13763}
13764
13765pub trait CompletionProvider {
13766 fn completions(
13767 &self,
13768 buffer: &Model<Buffer>,
13769 buffer_position: text::Anchor,
13770 trigger: CompletionContext,
13771 cx: &mut ViewContext<Editor>,
13772 ) -> Task<Result<Vec<Completion>>>;
13773
13774 fn resolve_completions(
13775 &self,
13776 buffer: Model<Buffer>,
13777 completion_indices: Vec<usize>,
13778 completions: Arc<RwLock<Box<[Completion]>>>,
13779 cx: &mut ViewContext<Editor>,
13780 ) -> Task<Result<bool>>;
13781
13782 fn apply_additional_edits_for_completion(
13783 &self,
13784 buffer: Model<Buffer>,
13785 completion: Completion,
13786 push_to_history: bool,
13787 cx: &mut ViewContext<Editor>,
13788 ) -> Task<Result<Option<language::Transaction>>>;
13789
13790 fn is_completion_trigger(
13791 &self,
13792 buffer: &Model<Buffer>,
13793 position: language::Anchor,
13794 text: &str,
13795 trigger_in_words: bool,
13796 cx: &mut ViewContext<Editor>,
13797 ) -> bool;
13798
13799 fn sort_completions(&self) -> bool {
13800 true
13801 }
13802}
13803
13804pub trait CodeActionProvider {
13805 fn code_actions(
13806 &self,
13807 buffer: &Model<Buffer>,
13808 range: Range<text::Anchor>,
13809 cx: &mut WindowContext,
13810 ) -> Task<Result<Vec<CodeAction>>>;
13811
13812 fn apply_code_action(
13813 &self,
13814 buffer_handle: Model<Buffer>,
13815 action: CodeAction,
13816 excerpt_id: ExcerptId,
13817 push_to_history: bool,
13818 cx: &mut WindowContext,
13819 ) -> Task<Result<ProjectTransaction>>;
13820}
13821
13822impl CodeActionProvider for Model<Project> {
13823 fn code_actions(
13824 &self,
13825 buffer: &Model<Buffer>,
13826 range: Range<text::Anchor>,
13827 cx: &mut WindowContext,
13828 ) -> Task<Result<Vec<CodeAction>>> {
13829 self.update(cx, |project, cx| {
13830 project.code_actions(buffer, range, None, cx)
13831 })
13832 }
13833
13834 fn apply_code_action(
13835 &self,
13836 buffer_handle: Model<Buffer>,
13837 action: CodeAction,
13838 _excerpt_id: ExcerptId,
13839 push_to_history: bool,
13840 cx: &mut WindowContext,
13841 ) -> Task<Result<ProjectTransaction>> {
13842 self.update(cx, |project, cx| {
13843 project.apply_code_action(buffer_handle, action, push_to_history, cx)
13844 })
13845 }
13846}
13847
13848fn snippet_completions(
13849 project: &Project,
13850 buffer: &Model<Buffer>,
13851 buffer_position: text::Anchor,
13852 cx: &mut AppContext,
13853) -> Task<Result<Vec<Completion>>> {
13854 let language = buffer.read(cx).language_at(buffer_position);
13855 let language_name = language.as_ref().map(|language| language.lsp_id());
13856 let snippet_store = project.snippets().read(cx);
13857 let snippets = snippet_store.snippets_for(language_name, cx);
13858
13859 if snippets.is_empty() {
13860 return Task::ready(Ok(vec![]));
13861 }
13862 let snapshot = buffer.read(cx).text_snapshot();
13863 let chars: String = snapshot
13864 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
13865 .collect();
13866
13867 let scope = language.map(|language| language.default_scope());
13868 let executor = cx.background_executor().clone();
13869
13870 cx.background_executor().spawn(async move {
13871 let classifier = CharClassifier::new(scope).for_completion(true);
13872 let mut last_word = chars
13873 .chars()
13874 .take_while(|c| classifier.is_word(*c))
13875 .collect::<String>();
13876 last_word = last_word.chars().rev().collect();
13877 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
13878 let to_lsp = |point: &text::Anchor| {
13879 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
13880 point_to_lsp(end)
13881 };
13882 let lsp_end = to_lsp(&buffer_position);
13883
13884 let candidates = snippets
13885 .iter()
13886 .enumerate()
13887 .flat_map(|(ix, snippet)| {
13888 snippet
13889 .prefix
13890 .iter()
13891 .map(move |prefix| StringMatchCandidate::new(ix, prefix.clone()))
13892 })
13893 .collect::<Vec<StringMatchCandidate>>();
13894
13895 let mut matches = fuzzy::match_strings(
13896 &candidates,
13897 &last_word,
13898 last_word.chars().any(|c| c.is_uppercase()),
13899 100,
13900 &Default::default(),
13901 executor,
13902 )
13903 .await;
13904
13905 // Remove all candidates where the query's start does not match the start of any word in the candidate
13906 if let Some(query_start) = last_word.chars().next() {
13907 matches.retain(|string_match| {
13908 split_words(&string_match.string).any(|word| {
13909 // Check that the first codepoint of the word as lowercase matches the first
13910 // codepoint of the query as lowercase
13911 word.chars()
13912 .flat_map(|codepoint| codepoint.to_lowercase())
13913 .zip(query_start.to_lowercase())
13914 .all(|(word_cp, query_cp)| word_cp == query_cp)
13915 })
13916 });
13917 }
13918
13919 let matched_strings = matches
13920 .into_iter()
13921 .map(|m| m.string)
13922 .collect::<HashSet<_>>();
13923
13924 let result: Vec<Completion> = snippets
13925 .into_iter()
13926 .filter_map(|snippet| {
13927 let matching_prefix = snippet
13928 .prefix
13929 .iter()
13930 .find(|prefix| matched_strings.contains(*prefix))?;
13931 let start = as_offset - last_word.len();
13932 let start = snapshot.anchor_before(start);
13933 let range = start..buffer_position;
13934 let lsp_start = to_lsp(&start);
13935 let lsp_range = lsp::Range {
13936 start: lsp_start,
13937 end: lsp_end,
13938 };
13939 Some(Completion {
13940 old_range: range,
13941 new_text: snippet.body.clone(),
13942 label: CodeLabel {
13943 text: matching_prefix.clone(),
13944 runs: vec![],
13945 filter_range: 0..matching_prefix.len(),
13946 },
13947 server_id: LanguageServerId(usize::MAX),
13948 documentation: snippet.description.clone().map(Documentation::SingleLine),
13949 lsp_completion: lsp::CompletionItem {
13950 label: snippet.prefix.first().unwrap().clone(),
13951 kind: Some(CompletionItemKind::SNIPPET),
13952 label_details: snippet.description.as_ref().map(|description| {
13953 lsp::CompletionItemLabelDetails {
13954 detail: Some(description.clone()),
13955 description: None,
13956 }
13957 }),
13958 insert_text_format: Some(InsertTextFormat::SNIPPET),
13959 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13960 lsp::InsertReplaceEdit {
13961 new_text: snippet.body.clone(),
13962 insert: lsp_range,
13963 replace: lsp_range,
13964 },
13965 )),
13966 filter_text: Some(snippet.body.clone()),
13967 sort_text: Some(char::MAX.to_string()),
13968 ..Default::default()
13969 },
13970 confirm: None,
13971 })
13972 })
13973 .collect();
13974
13975 Ok(result)
13976 })
13977}
13978
13979impl CompletionProvider for Model<Project> {
13980 fn completions(
13981 &self,
13982 buffer: &Model<Buffer>,
13983 buffer_position: text::Anchor,
13984 options: CompletionContext,
13985 cx: &mut ViewContext<Editor>,
13986 ) -> Task<Result<Vec<Completion>>> {
13987 self.update(cx, |project, cx| {
13988 let snippets = snippet_completions(project, buffer, buffer_position, cx);
13989 let project_completions = project.completions(buffer, buffer_position, options, cx);
13990 cx.background_executor().spawn(async move {
13991 let mut completions = project_completions.await?;
13992 let snippets_completions = snippets.await?;
13993 completions.extend(snippets_completions);
13994 Ok(completions)
13995 })
13996 })
13997 }
13998
13999 fn resolve_completions(
14000 &self,
14001 buffer: Model<Buffer>,
14002 completion_indices: Vec<usize>,
14003 completions: Arc<RwLock<Box<[Completion]>>>,
14004 cx: &mut ViewContext<Editor>,
14005 ) -> Task<Result<bool>> {
14006 self.update(cx, |project, cx| {
14007 project.resolve_completions(buffer, completion_indices, completions, cx)
14008 })
14009 }
14010
14011 fn apply_additional_edits_for_completion(
14012 &self,
14013 buffer: Model<Buffer>,
14014 completion: Completion,
14015 push_to_history: bool,
14016 cx: &mut ViewContext<Editor>,
14017 ) -> Task<Result<Option<language::Transaction>>> {
14018 self.update(cx, |project, cx| {
14019 project.apply_additional_edits_for_completion(buffer, completion, push_to_history, cx)
14020 })
14021 }
14022
14023 fn is_completion_trigger(
14024 &self,
14025 buffer: &Model<Buffer>,
14026 position: language::Anchor,
14027 text: &str,
14028 trigger_in_words: bool,
14029 cx: &mut ViewContext<Editor>,
14030 ) -> bool {
14031 if !EditorSettings::get_global(cx).show_completions_on_input {
14032 return false;
14033 }
14034
14035 let mut chars = text.chars();
14036 let char = if let Some(char) = chars.next() {
14037 char
14038 } else {
14039 return false;
14040 };
14041 if chars.next().is_some() {
14042 return false;
14043 }
14044
14045 let buffer = buffer.read(cx);
14046 let classifier = buffer
14047 .snapshot()
14048 .char_classifier_at(position)
14049 .for_completion(true);
14050 if trigger_in_words && classifier.is_word(char) {
14051 return true;
14052 }
14053
14054 buffer.completion_triggers().contains(text)
14055 }
14056}
14057
14058impl SemanticsProvider for Model<Project> {
14059 fn hover(
14060 &self,
14061 buffer: &Model<Buffer>,
14062 position: text::Anchor,
14063 cx: &mut AppContext,
14064 ) -> Option<Task<Vec<project::Hover>>> {
14065 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
14066 }
14067
14068 fn document_highlights(
14069 &self,
14070 buffer: &Model<Buffer>,
14071 position: text::Anchor,
14072 cx: &mut AppContext,
14073 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
14074 Some(self.update(cx, |project, cx| {
14075 project.document_highlights(buffer, position, cx)
14076 }))
14077 }
14078
14079 fn definitions(
14080 &self,
14081 buffer: &Model<Buffer>,
14082 position: text::Anchor,
14083 kind: GotoDefinitionKind,
14084 cx: &mut AppContext,
14085 ) -> Option<Task<Result<Vec<LocationLink>>>> {
14086 Some(self.update(cx, |project, cx| match kind {
14087 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
14088 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
14089 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
14090 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
14091 }))
14092 }
14093
14094 fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool {
14095 // TODO: make this work for remote projects
14096 self.read(cx)
14097 .language_servers_for_buffer(buffer.read(cx), cx)
14098 .any(
14099 |(_, server)| match server.capabilities().inlay_hint_provider {
14100 Some(lsp::OneOf::Left(enabled)) => enabled,
14101 Some(lsp::OneOf::Right(_)) => true,
14102 None => false,
14103 },
14104 )
14105 }
14106
14107 fn inlay_hints(
14108 &self,
14109 buffer_handle: Model<Buffer>,
14110 range: Range<text::Anchor>,
14111 cx: &mut AppContext,
14112 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
14113 Some(self.update(cx, |project, cx| {
14114 project.inlay_hints(buffer_handle, range, cx)
14115 }))
14116 }
14117
14118 fn resolve_inlay_hint(
14119 &self,
14120 hint: InlayHint,
14121 buffer_handle: Model<Buffer>,
14122 server_id: LanguageServerId,
14123 cx: &mut AppContext,
14124 ) -> Option<Task<anyhow::Result<InlayHint>>> {
14125 Some(self.update(cx, |project, cx| {
14126 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
14127 }))
14128 }
14129
14130 fn range_for_rename(
14131 &self,
14132 buffer: &Model<Buffer>,
14133 position: text::Anchor,
14134 cx: &mut AppContext,
14135 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
14136 Some(self.update(cx, |project, cx| {
14137 project.prepare_rename(buffer.clone(), position, cx)
14138 }))
14139 }
14140
14141 fn perform_rename(
14142 &self,
14143 buffer: &Model<Buffer>,
14144 position: text::Anchor,
14145 new_name: String,
14146 cx: &mut AppContext,
14147 ) -> Option<Task<Result<ProjectTransaction>>> {
14148 Some(self.update(cx, |project, cx| {
14149 project.perform_rename(buffer.clone(), position, new_name, cx)
14150 }))
14151 }
14152}
14153
14154fn inlay_hint_settings(
14155 location: Anchor,
14156 snapshot: &MultiBufferSnapshot,
14157 cx: &mut ViewContext<'_, Editor>,
14158) -> InlayHintSettings {
14159 let file = snapshot.file_at(location);
14160 let language = snapshot.language_at(location).map(|l| l.name());
14161 language_settings(language, file, cx).inlay_hints
14162}
14163
14164fn consume_contiguous_rows(
14165 contiguous_row_selections: &mut Vec<Selection<Point>>,
14166 selection: &Selection<Point>,
14167 display_map: &DisplaySnapshot,
14168 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
14169) -> (MultiBufferRow, MultiBufferRow) {
14170 contiguous_row_selections.push(selection.clone());
14171 let start_row = MultiBufferRow(selection.start.row);
14172 let mut end_row = ending_row(selection, display_map);
14173
14174 while let Some(next_selection) = selections.peek() {
14175 if next_selection.start.row <= end_row.0 {
14176 end_row = ending_row(next_selection, display_map);
14177 contiguous_row_selections.push(selections.next().unwrap().clone());
14178 } else {
14179 break;
14180 }
14181 }
14182 (start_row, end_row)
14183}
14184
14185fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
14186 if next_selection.end.column > 0 || next_selection.is_empty() {
14187 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
14188 } else {
14189 MultiBufferRow(next_selection.end.row)
14190 }
14191}
14192
14193impl EditorSnapshot {
14194 pub fn remote_selections_in_range<'a>(
14195 &'a self,
14196 range: &'a Range<Anchor>,
14197 collaboration_hub: &dyn CollaborationHub,
14198 cx: &'a AppContext,
14199 ) -> impl 'a + Iterator<Item = RemoteSelection> {
14200 let participant_names = collaboration_hub.user_names(cx);
14201 let participant_indices = collaboration_hub.user_participant_indices(cx);
14202 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
14203 let collaborators_by_replica_id = collaborators_by_peer_id
14204 .iter()
14205 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
14206 .collect::<HashMap<_, _>>();
14207 self.buffer_snapshot
14208 .selections_in_range(range, false)
14209 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
14210 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
14211 let participant_index = participant_indices.get(&collaborator.user_id).copied();
14212 let user_name = participant_names.get(&collaborator.user_id).cloned();
14213 Some(RemoteSelection {
14214 replica_id,
14215 selection,
14216 cursor_shape,
14217 line_mode,
14218 participant_index,
14219 peer_id: collaborator.peer_id,
14220 user_name,
14221 })
14222 })
14223 }
14224
14225 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
14226 self.display_snapshot.buffer_snapshot.language_at(position)
14227 }
14228
14229 pub fn is_focused(&self) -> bool {
14230 self.is_focused
14231 }
14232
14233 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
14234 self.placeholder_text.as_ref()
14235 }
14236
14237 pub fn scroll_position(&self) -> gpui::Point<f32> {
14238 self.scroll_anchor.scroll_position(&self.display_snapshot)
14239 }
14240
14241 fn gutter_dimensions(
14242 &self,
14243 font_id: FontId,
14244 font_size: Pixels,
14245 em_width: Pixels,
14246 em_advance: Pixels,
14247 max_line_number_width: Pixels,
14248 cx: &AppContext,
14249 ) -> GutterDimensions {
14250 if !self.show_gutter {
14251 return GutterDimensions::default();
14252 }
14253 let descent = cx.text_system().descent(font_id, font_size);
14254
14255 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
14256 matches!(
14257 ProjectSettings::get_global(cx).git.git_gutter,
14258 Some(GitGutterSetting::TrackedFiles)
14259 )
14260 });
14261 let gutter_settings = EditorSettings::get_global(cx).gutter;
14262 let show_line_numbers = self
14263 .show_line_numbers
14264 .unwrap_or(gutter_settings.line_numbers);
14265 let line_gutter_width = if show_line_numbers {
14266 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
14267 let min_width_for_number_on_gutter = em_advance * 4.0;
14268 max_line_number_width.max(min_width_for_number_on_gutter)
14269 } else {
14270 0.0.into()
14271 };
14272
14273 let show_code_actions = self
14274 .show_code_actions
14275 .unwrap_or(gutter_settings.code_actions);
14276
14277 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
14278
14279 let git_blame_entries_width =
14280 self.git_blame_gutter_max_author_length
14281 .map(|max_author_length| {
14282 // Length of the author name, but also space for the commit hash,
14283 // the spacing and the timestamp.
14284 let max_char_count = max_author_length
14285 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
14286 + 7 // length of commit sha
14287 + 14 // length of max relative timestamp ("60 minutes ago")
14288 + 4; // gaps and margins
14289
14290 em_advance * max_char_count
14291 });
14292
14293 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
14294 left_padding += if show_code_actions || show_runnables {
14295 em_width * 3.0
14296 } else if show_git_gutter && show_line_numbers {
14297 em_width * 2.0
14298 } else if show_git_gutter || show_line_numbers {
14299 em_width
14300 } else {
14301 px(0.)
14302 };
14303
14304 let right_padding = if gutter_settings.folds && show_line_numbers {
14305 em_width * 4.0
14306 } else if gutter_settings.folds {
14307 em_width * 3.0
14308 } else if show_line_numbers {
14309 em_width
14310 } else {
14311 px(0.)
14312 };
14313
14314 GutterDimensions {
14315 left_padding,
14316 right_padding,
14317 width: line_gutter_width + left_padding + right_padding,
14318 margin: -descent,
14319 git_blame_entries_width,
14320 }
14321 }
14322
14323 pub fn render_crease_toggle(
14324 &self,
14325 buffer_row: MultiBufferRow,
14326 row_contains_cursor: bool,
14327 editor: View<Editor>,
14328 cx: &mut WindowContext,
14329 ) -> Option<AnyElement> {
14330 let folded = self.is_line_folded(buffer_row);
14331 let mut is_foldable = false;
14332
14333 if let Some(crease) = self
14334 .crease_snapshot
14335 .query_row(buffer_row, &self.buffer_snapshot)
14336 {
14337 is_foldable = true;
14338 match crease {
14339 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
14340 if let Some(render_toggle) = render_toggle {
14341 let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
14342 if folded {
14343 editor.update(cx, |editor, cx| {
14344 editor.fold_at(&crate::FoldAt { buffer_row }, cx)
14345 });
14346 } else {
14347 editor.update(cx, |editor, cx| {
14348 editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
14349 });
14350 }
14351 });
14352 return Some((render_toggle)(buffer_row, folded, toggle_callback, cx));
14353 }
14354 }
14355 }
14356 }
14357
14358 is_foldable |= self.starts_indent(buffer_row);
14359
14360 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
14361 Some(
14362 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
14363 .selected(folded)
14364 .on_click(cx.listener_for(&editor, move |this, _e, cx| {
14365 if folded {
14366 this.unfold_at(&UnfoldAt { buffer_row }, cx);
14367 } else {
14368 this.fold_at(&FoldAt { buffer_row }, cx);
14369 }
14370 }))
14371 .into_any_element(),
14372 )
14373 } else {
14374 None
14375 }
14376 }
14377
14378 pub fn render_crease_trailer(
14379 &self,
14380 buffer_row: MultiBufferRow,
14381 cx: &mut WindowContext,
14382 ) -> Option<AnyElement> {
14383 let folded = self.is_line_folded(buffer_row);
14384 if let Crease::Inline { render_trailer, .. } = self
14385 .crease_snapshot
14386 .query_row(buffer_row, &self.buffer_snapshot)?
14387 {
14388 let render_trailer = render_trailer.as_ref()?;
14389 Some(render_trailer(buffer_row, folded, cx))
14390 } else {
14391 None
14392 }
14393 }
14394}
14395
14396impl Deref for EditorSnapshot {
14397 type Target = DisplaySnapshot;
14398
14399 fn deref(&self) -> &Self::Target {
14400 &self.display_snapshot
14401 }
14402}
14403
14404#[derive(Clone, Debug, PartialEq, Eq)]
14405pub enum EditorEvent {
14406 InputIgnored {
14407 text: Arc<str>,
14408 },
14409 InputHandled {
14410 utf16_range_to_replace: Option<Range<isize>>,
14411 text: Arc<str>,
14412 },
14413 ExcerptsAdded {
14414 buffer: Model<Buffer>,
14415 predecessor: ExcerptId,
14416 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
14417 },
14418 ExcerptsRemoved {
14419 ids: Vec<ExcerptId>,
14420 },
14421 ExcerptsEdited {
14422 ids: Vec<ExcerptId>,
14423 },
14424 ExcerptsExpanded {
14425 ids: Vec<ExcerptId>,
14426 },
14427 BufferEdited,
14428 Edited {
14429 transaction_id: clock::Lamport,
14430 },
14431 Reparsed(BufferId),
14432 Focused,
14433 FocusedIn,
14434 Blurred,
14435 DirtyChanged,
14436 Saved,
14437 TitleChanged,
14438 DiffBaseChanged,
14439 SelectionsChanged {
14440 local: bool,
14441 },
14442 ScrollPositionChanged {
14443 local: bool,
14444 autoscroll: bool,
14445 },
14446 Closed,
14447 TransactionUndone {
14448 transaction_id: clock::Lamport,
14449 },
14450 TransactionBegun {
14451 transaction_id: clock::Lamport,
14452 },
14453 Reloaded,
14454 CursorShapeChanged,
14455}
14456
14457impl EventEmitter<EditorEvent> for Editor {}
14458
14459impl FocusableView for Editor {
14460 fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
14461 self.focus_handle.clone()
14462 }
14463}
14464
14465impl Render for Editor {
14466 fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
14467 let settings = ThemeSettings::get_global(cx);
14468
14469 let mut text_style = match self.mode {
14470 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
14471 color: cx.theme().colors().editor_foreground,
14472 font_family: settings.ui_font.family.clone(),
14473 font_features: settings.ui_font.features.clone(),
14474 font_fallbacks: settings.ui_font.fallbacks.clone(),
14475 font_size: rems(0.875).into(),
14476 font_weight: settings.ui_font.weight,
14477 line_height: relative(settings.buffer_line_height.value()),
14478 ..Default::default()
14479 },
14480 EditorMode::Full => TextStyle {
14481 color: cx.theme().colors().editor_foreground,
14482 font_family: settings.buffer_font.family.clone(),
14483 font_features: settings.buffer_font.features.clone(),
14484 font_fallbacks: settings.buffer_font.fallbacks.clone(),
14485 font_size: settings.buffer_font_size(cx).into(),
14486 font_weight: settings.buffer_font.weight,
14487 line_height: relative(settings.buffer_line_height.value()),
14488 ..Default::default()
14489 },
14490 };
14491 if let Some(text_style_refinement) = &self.text_style_refinement {
14492 text_style.refine(text_style_refinement)
14493 }
14494
14495 let background = match self.mode {
14496 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
14497 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
14498 EditorMode::Full => cx.theme().colors().editor_background,
14499 };
14500
14501 EditorElement::new(
14502 cx.view(),
14503 EditorStyle {
14504 background,
14505 local_player: cx.theme().players().local(),
14506 text: text_style,
14507 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
14508 syntax: cx.theme().syntax().clone(),
14509 status: cx.theme().status().clone(),
14510 inlay_hints_style: make_inlay_hints_style(cx),
14511 suggestions_style: HighlightStyle {
14512 color: Some(cx.theme().status().predictive),
14513 ..HighlightStyle::default()
14514 },
14515 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
14516 },
14517 )
14518 }
14519}
14520
14521impl ViewInputHandler for Editor {
14522 fn text_for_range(
14523 &mut self,
14524 range_utf16: Range<usize>,
14525 adjusted_range: &mut Option<Range<usize>>,
14526 cx: &mut ViewContext<Self>,
14527 ) -> Option<String> {
14528 let snapshot = self.buffer.read(cx).read(cx);
14529 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
14530 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
14531 if (start.0..end.0) != range_utf16 {
14532 adjusted_range.replace(start.0..end.0);
14533 }
14534 Some(snapshot.text_for_range(start..end).collect())
14535 }
14536
14537 fn selected_text_range(
14538 &mut self,
14539 ignore_disabled_input: bool,
14540 cx: &mut ViewContext<Self>,
14541 ) -> Option<UTF16Selection> {
14542 // Prevent the IME menu from appearing when holding down an alphabetic key
14543 // while input is disabled.
14544 if !ignore_disabled_input && !self.input_enabled {
14545 return None;
14546 }
14547
14548 let selection = self.selections.newest::<OffsetUtf16>(cx);
14549 let range = selection.range();
14550
14551 Some(UTF16Selection {
14552 range: range.start.0..range.end.0,
14553 reversed: selection.reversed,
14554 })
14555 }
14556
14557 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
14558 let snapshot = self.buffer.read(cx).read(cx);
14559 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
14560 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
14561 }
14562
14563 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
14564 self.clear_highlights::<InputComposition>(cx);
14565 self.ime_transaction.take();
14566 }
14567
14568 fn replace_text_in_range(
14569 &mut self,
14570 range_utf16: Option<Range<usize>>,
14571 text: &str,
14572 cx: &mut ViewContext<Self>,
14573 ) {
14574 if !self.input_enabled {
14575 cx.emit(EditorEvent::InputIgnored { text: text.into() });
14576 return;
14577 }
14578
14579 self.transact(cx, |this, cx| {
14580 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
14581 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
14582 Some(this.selection_replacement_ranges(range_utf16, cx))
14583 } else {
14584 this.marked_text_ranges(cx)
14585 };
14586
14587 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
14588 let newest_selection_id = this.selections.newest_anchor().id;
14589 this.selections
14590 .all::<OffsetUtf16>(cx)
14591 .iter()
14592 .zip(ranges_to_replace.iter())
14593 .find_map(|(selection, range)| {
14594 if selection.id == newest_selection_id {
14595 Some(
14596 (range.start.0 as isize - selection.head().0 as isize)
14597 ..(range.end.0 as isize - selection.head().0 as isize),
14598 )
14599 } else {
14600 None
14601 }
14602 })
14603 });
14604
14605 cx.emit(EditorEvent::InputHandled {
14606 utf16_range_to_replace: range_to_replace,
14607 text: text.into(),
14608 });
14609
14610 if let Some(new_selected_ranges) = new_selected_ranges {
14611 this.change_selections(None, cx, |selections| {
14612 selections.select_ranges(new_selected_ranges)
14613 });
14614 this.backspace(&Default::default(), cx);
14615 }
14616
14617 this.handle_input(text, cx);
14618 });
14619
14620 if let Some(transaction) = self.ime_transaction {
14621 self.buffer.update(cx, |buffer, cx| {
14622 buffer.group_until_transaction(transaction, cx);
14623 });
14624 }
14625
14626 self.unmark_text(cx);
14627 }
14628
14629 fn replace_and_mark_text_in_range(
14630 &mut self,
14631 range_utf16: Option<Range<usize>>,
14632 text: &str,
14633 new_selected_range_utf16: Option<Range<usize>>,
14634 cx: &mut ViewContext<Self>,
14635 ) {
14636 if !self.input_enabled {
14637 return;
14638 }
14639
14640 let transaction = self.transact(cx, |this, cx| {
14641 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
14642 let snapshot = this.buffer.read(cx).read(cx);
14643 if let Some(relative_range_utf16) = range_utf16.as_ref() {
14644 for marked_range in &mut marked_ranges {
14645 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
14646 marked_range.start.0 += relative_range_utf16.start;
14647 marked_range.start =
14648 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
14649 marked_range.end =
14650 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
14651 }
14652 }
14653 Some(marked_ranges)
14654 } else if let Some(range_utf16) = range_utf16 {
14655 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
14656 Some(this.selection_replacement_ranges(range_utf16, cx))
14657 } else {
14658 None
14659 };
14660
14661 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
14662 let newest_selection_id = this.selections.newest_anchor().id;
14663 this.selections
14664 .all::<OffsetUtf16>(cx)
14665 .iter()
14666 .zip(ranges_to_replace.iter())
14667 .find_map(|(selection, range)| {
14668 if selection.id == newest_selection_id {
14669 Some(
14670 (range.start.0 as isize - selection.head().0 as isize)
14671 ..(range.end.0 as isize - selection.head().0 as isize),
14672 )
14673 } else {
14674 None
14675 }
14676 })
14677 });
14678
14679 cx.emit(EditorEvent::InputHandled {
14680 utf16_range_to_replace: range_to_replace,
14681 text: text.into(),
14682 });
14683
14684 if let Some(ranges) = ranges_to_replace {
14685 this.change_selections(None, cx, |s| s.select_ranges(ranges));
14686 }
14687
14688 let marked_ranges = {
14689 let snapshot = this.buffer.read(cx).read(cx);
14690 this.selections
14691 .disjoint_anchors()
14692 .iter()
14693 .map(|selection| {
14694 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
14695 })
14696 .collect::<Vec<_>>()
14697 };
14698
14699 if text.is_empty() {
14700 this.unmark_text(cx);
14701 } else {
14702 this.highlight_text::<InputComposition>(
14703 marked_ranges.clone(),
14704 HighlightStyle {
14705 underline: Some(UnderlineStyle {
14706 thickness: px(1.),
14707 color: None,
14708 wavy: false,
14709 }),
14710 ..Default::default()
14711 },
14712 cx,
14713 );
14714 }
14715
14716 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
14717 let use_autoclose = this.use_autoclose;
14718 let use_auto_surround = this.use_auto_surround;
14719 this.set_use_autoclose(false);
14720 this.set_use_auto_surround(false);
14721 this.handle_input(text, cx);
14722 this.set_use_autoclose(use_autoclose);
14723 this.set_use_auto_surround(use_auto_surround);
14724
14725 if let Some(new_selected_range) = new_selected_range_utf16 {
14726 let snapshot = this.buffer.read(cx).read(cx);
14727 let new_selected_ranges = marked_ranges
14728 .into_iter()
14729 .map(|marked_range| {
14730 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
14731 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
14732 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
14733 snapshot.clip_offset_utf16(new_start, Bias::Left)
14734 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
14735 })
14736 .collect::<Vec<_>>();
14737
14738 drop(snapshot);
14739 this.change_selections(None, cx, |selections| {
14740 selections.select_ranges(new_selected_ranges)
14741 });
14742 }
14743 });
14744
14745 self.ime_transaction = self.ime_transaction.or(transaction);
14746 if let Some(transaction) = self.ime_transaction {
14747 self.buffer.update(cx, |buffer, cx| {
14748 buffer.group_until_transaction(transaction, cx);
14749 });
14750 }
14751
14752 if self.text_highlights::<InputComposition>(cx).is_none() {
14753 self.ime_transaction.take();
14754 }
14755 }
14756
14757 fn bounds_for_range(
14758 &mut self,
14759 range_utf16: Range<usize>,
14760 element_bounds: gpui::Bounds<Pixels>,
14761 cx: &mut ViewContext<Self>,
14762 ) -> Option<gpui::Bounds<Pixels>> {
14763 let text_layout_details = self.text_layout_details(cx);
14764 let gpui::Point {
14765 x: em_width,
14766 y: line_height,
14767 } = self.character_size(cx);
14768
14769 let snapshot = self.snapshot(cx);
14770 let scroll_position = snapshot.scroll_position();
14771 let scroll_left = scroll_position.x * em_width;
14772
14773 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
14774 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
14775 + self.gutter_dimensions.width
14776 + self.gutter_dimensions.margin;
14777 let y = line_height * (start.row().as_f32() - scroll_position.y);
14778
14779 Some(Bounds {
14780 origin: element_bounds.origin + point(x, y),
14781 size: size(em_width, line_height),
14782 })
14783 }
14784}
14785
14786trait SelectionExt {
14787 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
14788 fn spanned_rows(
14789 &self,
14790 include_end_if_at_line_start: bool,
14791 map: &DisplaySnapshot,
14792 ) -> Range<MultiBufferRow>;
14793}
14794
14795impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
14796 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
14797 let start = self
14798 .start
14799 .to_point(&map.buffer_snapshot)
14800 .to_display_point(map);
14801 let end = self
14802 .end
14803 .to_point(&map.buffer_snapshot)
14804 .to_display_point(map);
14805 if self.reversed {
14806 end..start
14807 } else {
14808 start..end
14809 }
14810 }
14811
14812 fn spanned_rows(
14813 &self,
14814 include_end_if_at_line_start: bool,
14815 map: &DisplaySnapshot,
14816 ) -> Range<MultiBufferRow> {
14817 let start = self.start.to_point(&map.buffer_snapshot);
14818 let mut end = self.end.to_point(&map.buffer_snapshot);
14819 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
14820 end.row -= 1;
14821 }
14822
14823 let buffer_start = map.prev_line_boundary(start).0;
14824 let buffer_end = map.next_line_boundary(end).0;
14825 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
14826 }
14827}
14828
14829impl<T: InvalidationRegion> InvalidationStack<T> {
14830 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
14831 where
14832 S: Clone + ToOffset,
14833 {
14834 while let Some(region) = self.last() {
14835 let all_selections_inside_invalidation_ranges =
14836 if selections.len() == region.ranges().len() {
14837 selections
14838 .iter()
14839 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
14840 .all(|(selection, invalidation_range)| {
14841 let head = selection.head().to_offset(buffer);
14842 invalidation_range.start <= head && invalidation_range.end >= head
14843 })
14844 } else {
14845 false
14846 };
14847
14848 if all_selections_inside_invalidation_ranges {
14849 break;
14850 } else {
14851 self.pop();
14852 }
14853 }
14854 }
14855}
14856
14857impl<T> Default for InvalidationStack<T> {
14858 fn default() -> Self {
14859 Self(Default::default())
14860 }
14861}
14862
14863impl<T> Deref for InvalidationStack<T> {
14864 type Target = Vec<T>;
14865
14866 fn deref(&self) -> &Self::Target {
14867 &self.0
14868 }
14869}
14870
14871impl<T> DerefMut for InvalidationStack<T> {
14872 fn deref_mut(&mut self) -> &mut Self::Target {
14873 &mut self.0
14874 }
14875}
14876
14877impl InvalidationRegion for SnippetState {
14878 fn ranges(&self) -> &[Range<Anchor>] {
14879 &self.ranges[self.active_index]
14880 }
14881}
14882
14883pub fn diagnostic_block_renderer(
14884 diagnostic: Diagnostic,
14885 max_message_rows: Option<u8>,
14886 allow_closing: bool,
14887 _is_valid: bool,
14888) -> RenderBlock {
14889 let (text_without_backticks, code_ranges) =
14890 highlight_diagnostic_message(&diagnostic, max_message_rows);
14891
14892 Arc::new(move |cx: &mut BlockContext| {
14893 let group_id: SharedString = cx.block_id.to_string().into();
14894
14895 let mut text_style = cx.text_style().clone();
14896 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
14897 let theme_settings = ThemeSettings::get_global(cx);
14898 text_style.font_family = theme_settings.buffer_font.family.clone();
14899 text_style.font_style = theme_settings.buffer_font.style;
14900 text_style.font_features = theme_settings.buffer_font.features.clone();
14901 text_style.font_weight = theme_settings.buffer_font.weight;
14902
14903 let multi_line_diagnostic = diagnostic.message.contains('\n');
14904
14905 let buttons = |diagnostic: &Diagnostic| {
14906 if multi_line_diagnostic {
14907 v_flex()
14908 } else {
14909 h_flex()
14910 }
14911 .when(allow_closing, |div| {
14912 div.children(diagnostic.is_primary.then(|| {
14913 IconButton::new("close-block", IconName::XCircle)
14914 .icon_color(Color::Muted)
14915 .size(ButtonSize::Compact)
14916 .style(ButtonStyle::Transparent)
14917 .visible_on_hover(group_id.clone())
14918 .on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
14919 .tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
14920 }))
14921 })
14922 .child(
14923 IconButton::new("copy-block", IconName::Copy)
14924 .icon_color(Color::Muted)
14925 .size(ButtonSize::Compact)
14926 .style(ButtonStyle::Transparent)
14927 .visible_on_hover(group_id.clone())
14928 .on_click({
14929 let message = diagnostic.message.clone();
14930 move |_click, cx| {
14931 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
14932 }
14933 })
14934 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
14935 )
14936 };
14937
14938 let icon_size = buttons(&diagnostic)
14939 .into_any_element()
14940 .layout_as_root(AvailableSpace::min_size(), cx);
14941
14942 h_flex()
14943 .id(cx.block_id)
14944 .group(group_id.clone())
14945 .relative()
14946 .size_full()
14947 .block_mouse_down()
14948 .pl(cx.gutter_dimensions.width)
14949 .w(cx.max_width - cx.gutter_dimensions.full_width())
14950 .child(
14951 div()
14952 .flex()
14953 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
14954 .flex_shrink(),
14955 )
14956 .child(buttons(&diagnostic))
14957 .child(div().flex().flex_shrink_0().child(
14958 StyledText::new(text_without_backticks.clone()).with_highlights(
14959 &text_style,
14960 code_ranges.iter().map(|range| {
14961 (
14962 range.clone(),
14963 HighlightStyle {
14964 font_weight: Some(FontWeight::BOLD),
14965 ..Default::default()
14966 },
14967 )
14968 }),
14969 ),
14970 ))
14971 .into_any_element()
14972 })
14973}
14974
14975pub fn highlight_diagnostic_message(
14976 diagnostic: &Diagnostic,
14977 mut max_message_rows: Option<u8>,
14978) -> (SharedString, Vec<Range<usize>>) {
14979 let mut text_without_backticks = String::new();
14980 let mut code_ranges = Vec::new();
14981
14982 if let Some(source) = &diagnostic.source {
14983 text_without_backticks.push_str(source);
14984 code_ranges.push(0..source.len());
14985 text_without_backticks.push_str(": ");
14986 }
14987
14988 let mut prev_offset = 0;
14989 let mut in_code_block = false;
14990 let has_row_limit = max_message_rows.is_some();
14991 let mut newline_indices = diagnostic
14992 .message
14993 .match_indices('\n')
14994 .filter(|_| has_row_limit)
14995 .map(|(ix, _)| ix)
14996 .fuse()
14997 .peekable();
14998
14999 for (quote_ix, _) in diagnostic
15000 .message
15001 .match_indices('`')
15002 .chain([(diagnostic.message.len(), "")])
15003 {
15004 let mut first_newline_ix = None;
15005 let mut last_newline_ix = None;
15006 while let Some(newline_ix) = newline_indices.peek() {
15007 if *newline_ix < quote_ix {
15008 if first_newline_ix.is_none() {
15009 first_newline_ix = Some(*newline_ix);
15010 }
15011 last_newline_ix = Some(*newline_ix);
15012
15013 if let Some(rows_left) = &mut max_message_rows {
15014 if *rows_left == 0 {
15015 break;
15016 } else {
15017 *rows_left -= 1;
15018 }
15019 }
15020 let _ = newline_indices.next();
15021 } else {
15022 break;
15023 }
15024 }
15025 let prev_len = text_without_backticks.len();
15026 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
15027 text_without_backticks.push_str(new_text);
15028 if in_code_block {
15029 code_ranges.push(prev_len..text_without_backticks.len());
15030 }
15031 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
15032 in_code_block = !in_code_block;
15033 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
15034 text_without_backticks.push_str("...");
15035 break;
15036 }
15037 }
15038
15039 (text_without_backticks.into(), code_ranges)
15040}
15041
15042fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
15043 match severity {
15044 DiagnosticSeverity::ERROR => colors.error,
15045 DiagnosticSeverity::WARNING => colors.warning,
15046 DiagnosticSeverity::INFORMATION => colors.info,
15047 DiagnosticSeverity::HINT => colors.info,
15048 _ => colors.ignored,
15049 }
15050}
15051
15052pub fn styled_runs_for_code_label<'a>(
15053 label: &'a CodeLabel,
15054 syntax_theme: &'a theme::SyntaxTheme,
15055) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
15056 let fade_out = HighlightStyle {
15057 fade_out: Some(0.35),
15058 ..Default::default()
15059 };
15060
15061 let mut prev_end = label.filter_range.end;
15062 label
15063 .runs
15064 .iter()
15065 .enumerate()
15066 .flat_map(move |(ix, (range, highlight_id))| {
15067 let style = if let Some(style) = highlight_id.style(syntax_theme) {
15068 style
15069 } else {
15070 return Default::default();
15071 };
15072 let mut muted_style = style;
15073 muted_style.highlight(fade_out);
15074
15075 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
15076 if range.start >= label.filter_range.end {
15077 if range.start > prev_end {
15078 runs.push((prev_end..range.start, fade_out));
15079 }
15080 runs.push((range.clone(), muted_style));
15081 } else if range.end <= label.filter_range.end {
15082 runs.push((range.clone(), style));
15083 } else {
15084 runs.push((range.start..label.filter_range.end, style));
15085 runs.push((label.filter_range.end..range.end, muted_style));
15086 }
15087 prev_end = cmp::max(prev_end, range.end);
15088
15089 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
15090 runs.push((prev_end..label.text.len(), fade_out));
15091 }
15092
15093 runs
15094 })
15095}
15096
15097pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
15098 let mut prev_index = 0;
15099 let mut prev_codepoint: Option<char> = None;
15100 text.char_indices()
15101 .chain([(text.len(), '\0')])
15102 .filter_map(move |(index, codepoint)| {
15103 let prev_codepoint = prev_codepoint.replace(codepoint)?;
15104 let is_boundary = index == text.len()
15105 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
15106 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
15107 if is_boundary {
15108 let chunk = &text[prev_index..index];
15109 prev_index = index;
15110 Some(chunk)
15111 } else {
15112 None
15113 }
15114 })
15115}
15116
15117pub trait RangeToAnchorExt: Sized {
15118 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
15119
15120 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
15121 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
15122 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
15123 }
15124}
15125
15126impl<T: ToOffset> RangeToAnchorExt for Range<T> {
15127 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
15128 let start_offset = self.start.to_offset(snapshot);
15129 let end_offset = self.end.to_offset(snapshot);
15130 if start_offset == end_offset {
15131 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
15132 } else {
15133 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
15134 }
15135 }
15136}
15137
15138pub trait RowExt {
15139 fn as_f32(&self) -> f32;
15140
15141 fn next_row(&self) -> Self;
15142
15143 fn previous_row(&self) -> Self;
15144
15145 fn minus(&self, other: Self) -> u32;
15146}
15147
15148impl RowExt for DisplayRow {
15149 fn as_f32(&self) -> f32 {
15150 self.0 as f32
15151 }
15152
15153 fn next_row(&self) -> Self {
15154 Self(self.0 + 1)
15155 }
15156
15157 fn previous_row(&self) -> Self {
15158 Self(self.0.saturating_sub(1))
15159 }
15160
15161 fn minus(&self, other: Self) -> u32 {
15162 self.0 - other.0
15163 }
15164}
15165
15166impl RowExt for MultiBufferRow {
15167 fn as_f32(&self) -> f32 {
15168 self.0 as f32
15169 }
15170
15171 fn next_row(&self) -> Self {
15172 Self(self.0 + 1)
15173 }
15174
15175 fn previous_row(&self) -> Self {
15176 Self(self.0.saturating_sub(1))
15177 }
15178
15179 fn minus(&self, other: Self) -> u32 {
15180 self.0 - other.0
15181 }
15182}
15183
15184trait RowRangeExt {
15185 type Row;
15186
15187 fn len(&self) -> usize;
15188
15189 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
15190}
15191
15192impl RowRangeExt for Range<MultiBufferRow> {
15193 type Row = MultiBufferRow;
15194
15195 fn len(&self) -> usize {
15196 (self.end.0 - self.start.0) as usize
15197 }
15198
15199 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
15200 (self.start.0..self.end.0).map(MultiBufferRow)
15201 }
15202}
15203
15204impl RowRangeExt for Range<DisplayRow> {
15205 type Row = DisplayRow;
15206
15207 fn len(&self) -> usize {
15208 (self.end.0 - self.start.0) as usize
15209 }
15210
15211 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
15212 (self.start.0..self.end.0).map(DisplayRow)
15213 }
15214}
15215
15216fn hunk_status(hunk: &MultiBufferDiffHunk) -> DiffHunkStatus {
15217 if hunk.diff_base_byte_range.is_empty() {
15218 DiffHunkStatus::Added
15219 } else if hunk.row_range.is_empty() {
15220 DiffHunkStatus::Removed
15221 } else {
15222 DiffHunkStatus::Modified
15223 }
15224}
15225
15226/// If select range has more than one line, we
15227/// just point the cursor to range.start.
15228fn check_multiline_range(buffer: &Buffer, range: Range<usize>) -> Range<usize> {
15229 if buffer.offset_to_point(range.start).row == buffer.offset_to_point(range.end).row {
15230 range
15231 } else {
15232 range.start..range.start
15233 }
15234}
15235
15236pub struct KillRing(ClipboardItem);
15237impl Global for KillRing {}
15238
15239const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);