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::{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 aside_was_displayed: Cell<bool>,
1012}
1013
1014impl CompletionsMenu {
1015 fn new(
1016 id: CompletionId,
1017 sort_completions: bool,
1018 initial_position: Anchor,
1019 buffer: Model<Buffer>,
1020 completions: Box<[Completion]>,
1021 ) -> Self {
1022 let match_candidates = completions
1023 .iter()
1024 .enumerate()
1025 .map(|(id, completion)| {
1026 StringMatchCandidate::new(
1027 id,
1028 completion.label.text[completion.label.filter_range.clone()].into(),
1029 )
1030 })
1031 .collect();
1032
1033 Self {
1034 id,
1035 sort_completions,
1036 initial_position,
1037 buffer,
1038 completions: Arc::new(RwLock::new(completions)),
1039 match_candidates,
1040 matches: Vec::new().into(),
1041 selected_item: 0,
1042 scroll_handle: UniformListScrollHandle::new(),
1043 selected_completion_resolve_debounce: Some(Arc::new(Mutex::new(DebouncedDelay::new()))),
1044 aside_was_displayed: Cell::new(false),
1045 }
1046 }
1047
1048 fn new_snippet_choices(
1049 id: CompletionId,
1050 sort_completions: bool,
1051 choices: &Vec<String>,
1052 selection: Range<Anchor>,
1053 buffer: Model<Buffer>,
1054 ) -> Self {
1055 let completions = choices
1056 .iter()
1057 .map(|choice| Completion {
1058 old_range: selection.start.text_anchor..selection.end.text_anchor,
1059 new_text: choice.to_string(),
1060 label: CodeLabel {
1061 text: choice.to_string(),
1062 runs: Default::default(),
1063 filter_range: Default::default(),
1064 },
1065 server_id: LanguageServerId(usize::MAX),
1066 documentation: None,
1067 lsp_completion: Default::default(),
1068 confirm: None,
1069 })
1070 .collect();
1071
1072 let match_candidates = choices
1073 .iter()
1074 .enumerate()
1075 .map(|(id, completion)| StringMatchCandidate::new(id, completion.to_string()))
1076 .collect();
1077 let matches = choices
1078 .iter()
1079 .enumerate()
1080 .map(|(id, completion)| StringMatch {
1081 candidate_id: id,
1082 score: 1.,
1083 positions: vec![],
1084 string: completion.clone(),
1085 })
1086 .collect();
1087 Self {
1088 id,
1089 sort_completions,
1090 initial_position: selection.start,
1091 buffer,
1092 completions: Arc::new(RwLock::new(completions)),
1093 match_candidates,
1094 matches,
1095 selected_item: 0,
1096 scroll_handle: UniformListScrollHandle::new(),
1097 selected_completion_resolve_debounce: Some(Arc::new(Mutex::new(DebouncedDelay::new()))),
1098 aside_was_displayed: Cell::new(false),
1099 }
1100 }
1101
1102 fn suppress_documentation_resolution(mut self) -> Self {
1103 self.selected_completion_resolve_debounce.take();
1104 self
1105 }
1106
1107 fn select_first(
1108 &mut self,
1109 provider: Option<&dyn CompletionProvider>,
1110 cx: &mut ViewContext<Editor>,
1111 ) {
1112 self.selected_item = 0;
1113 self.scroll_handle
1114 .scroll_to_item(self.selected_item, ScrollStrategy::Top);
1115 self.resolve_selected_completion(provider, cx);
1116 cx.notify();
1117 }
1118
1119 fn select_prev(
1120 &mut self,
1121 provider: Option<&dyn CompletionProvider>,
1122 cx: &mut ViewContext<Editor>,
1123 ) {
1124 if self.selected_item > 0 {
1125 self.selected_item -= 1;
1126 } else {
1127 self.selected_item = self.matches.len() - 1;
1128 }
1129 self.scroll_handle
1130 .scroll_to_item(self.selected_item, ScrollStrategy::Top);
1131 self.resolve_selected_completion(provider, cx);
1132 cx.notify();
1133 }
1134
1135 fn select_next(
1136 &mut self,
1137 provider: Option<&dyn CompletionProvider>,
1138 cx: &mut ViewContext<Editor>,
1139 ) {
1140 if self.selected_item + 1 < self.matches.len() {
1141 self.selected_item += 1;
1142 } else {
1143 self.selected_item = 0;
1144 }
1145 self.scroll_handle
1146 .scroll_to_item(self.selected_item, ScrollStrategy::Top);
1147 self.resolve_selected_completion(provider, cx);
1148 cx.notify();
1149 }
1150
1151 fn select_last(
1152 &mut self,
1153 provider: Option<&dyn CompletionProvider>,
1154 cx: &mut ViewContext<Editor>,
1155 ) {
1156 self.selected_item = self.matches.len() - 1;
1157 self.scroll_handle
1158 .scroll_to_item(self.selected_item, ScrollStrategy::Top);
1159 self.resolve_selected_completion(provider, cx);
1160 cx.notify();
1161 }
1162
1163 fn resolve_selected_completion(
1164 &mut self,
1165 provider: Option<&dyn CompletionProvider>,
1166 cx: &mut ViewContext<Editor>,
1167 ) {
1168 let completion_index = self.matches[self.selected_item].candidate_id;
1169 let Some(provider) = provider else {
1170 return;
1171 };
1172 let Some(completion_resolve) = self.selected_completion_resolve_debounce.as_ref() else {
1173 return;
1174 };
1175
1176 let resolve_task = provider.resolve_completions(
1177 self.buffer.clone(),
1178 vec![completion_index],
1179 self.completions.clone(),
1180 cx,
1181 );
1182
1183 let delay_ms =
1184 EditorSettings::get_global(cx).completion_documentation_secondary_query_debounce;
1185 let delay = Duration::from_millis(delay_ms);
1186
1187 completion_resolve.lock().fire_new(delay, cx, |_, cx| {
1188 cx.spawn(move |editor, mut cx| async move {
1189 if let Some(true) = resolve_task.await.log_err() {
1190 editor.update(&mut cx, |_, cx| cx.notify()).ok();
1191 }
1192 })
1193 });
1194 }
1195
1196 fn visible(&self) -> bool {
1197 !self.matches.is_empty()
1198 }
1199
1200 fn render(
1201 &self,
1202 style: &EditorStyle,
1203 max_height: Pixels,
1204 workspace: Option<WeakView<Workspace>>,
1205 cx: &mut ViewContext<Editor>,
1206 ) -> AnyElement {
1207 let settings = EditorSettings::get_global(cx);
1208 let show_completion_documentation = settings.show_completion_documentation;
1209
1210 let widest_completion_ix = self
1211 .matches
1212 .iter()
1213 .enumerate()
1214 .max_by_key(|(_, mat)| {
1215 let completions = self.completions.read();
1216 let completion = &completions[mat.candidate_id];
1217 let documentation = &completion.documentation;
1218
1219 let mut len = completion.label.text.chars().count();
1220 if let Some(Documentation::SingleLine(text)) = documentation {
1221 if show_completion_documentation {
1222 len += text.chars().count();
1223 }
1224 }
1225
1226 len
1227 })
1228 .map(|(ix, _)| ix);
1229
1230 let completions = self.completions.clone();
1231 let matches = self.matches.clone();
1232 let selected_item = self.selected_item;
1233 let style = style.clone();
1234
1235 let multiline_docs = if show_completion_documentation {
1236 let mat = &self.matches[selected_item];
1237 match &self.completions.read()[mat.candidate_id].documentation {
1238 Some(Documentation::MultiLinePlainText(text)) => {
1239 Some(div().child(SharedString::from(text.clone())))
1240 }
1241 Some(Documentation::MultiLineMarkdown(parsed)) if !parsed.text.is_empty() => {
1242 Some(div().child(render_parsed_markdown(
1243 "completions_markdown",
1244 parsed,
1245 &style,
1246 workspace,
1247 cx,
1248 )))
1249 }
1250 Some(Documentation::Undocumented) if self.aside_was_displayed.get() => {
1251 Some(div().child("No documentation"))
1252 }
1253 _ => None,
1254 }
1255 } else {
1256 None
1257 };
1258
1259 let aside_contents = if let Some(multiline_docs) = multiline_docs {
1260 Some(multiline_docs)
1261 } else if self.aside_was_displayed.get() {
1262 Some(div().child("Fetching documentation..."))
1263 } else {
1264 None
1265 };
1266 self.aside_was_displayed.set(aside_contents.is_some());
1267
1268 let aside_contents = aside_contents.map(|div| {
1269 div.id("multiline_docs")
1270 .max_h(max_height)
1271 .flex_1()
1272 .px_1p5()
1273 .py_1()
1274 .min_w(px(260.))
1275 .max_w(px(640.))
1276 .w(px(500.))
1277 .overflow_y_scroll()
1278 .occlude()
1279 });
1280
1281 let list = uniform_list(
1282 cx.view().clone(),
1283 "completions",
1284 matches.len(),
1285 move |_editor, range, cx| {
1286 let start_ix = range.start;
1287 let completions_guard = completions.read();
1288
1289 matches[range]
1290 .iter()
1291 .enumerate()
1292 .map(|(ix, mat)| {
1293 let item_ix = start_ix + ix;
1294 let candidate_id = mat.candidate_id;
1295 let completion = &completions_guard[candidate_id];
1296
1297 let documentation = if show_completion_documentation {
1298 &completion.documentation
1299 } else {
1300 &None
1301 };
1302
1303 let highlights = gpui::combine_highlights(
1304 mat.ranges().map(|range| (range, FontWeight::BOLD.into())),
1305 styled_runs_for_code_label(&completion.label, &style.syntax).map(
1306 |(range, mut highlight)| {
1307 // Ignore font weight for syntax highlighting, as we'll use it
1308 // for fuzzy matches.
1309 highlight.font_weight = None;
1310
1311 if completion.lsp_completion.deprecated.unwrap_or(false) {
1312 highlight.strikethrough = Some(StrikethroughStyle {
1313 thickness: 1.0.into(),
1314 ..Default::default()
1315 });
1316 highlight.color = Some(cx.theme().colors().text_muted);
1317 }
1318
1319 (range, highlight)
1320 },
1321 ),
1322 );
1323 let completion_label = StyledText::new(completion.label.text.clone())
1324 .with_highlights(&style.text, highlights);
1325 let documentation_label =
1326 if let Some(Documentation::SingleLine(text)) = documentation {
1327 if text.trim().is_empty() {
1328 None
1329 } else {
1330 Some(
1331 Label::new(text.clone())
1332 .ml_4()
1333 .size(LabelSize::Small)
1334 .color(Color::Muted),
1335 )
1336 }
1337 } else {
1338 None
1339 };
1340
1341 let color_swatch = completion
1342 .color()
1343 .map(|color| div().size_4().bg(color).rounded_sm());
1344
1345 div().min_w(px(220.)).max_w(px(540.)).child(
1346 ListItem::new(mat.candidate_id)
1347 .inset(true)
1348 .selected(item_ix == selected_item)
1349 .on_click(cx.listener(move |editor, _event, cx| {
1350 cx.stop_propagation();
1351 if let Some(task) = editor.confirm_completion(
1352 &ConfirmCompletion {
1353 item_ix: Some(item_ix),
1354 },
1355 cx,
1356 ) {
1357 task.detach_and_log_err(cx)
1358 }
1359 }))
1360 .start_slot::<Div>(color_swatch)
1361 .child(h_flex().overflow_hidden().child(completion_label))
1362 .end_slot::<Label>(documentation_label),
1363 )
1364 })
1365 .collect()
1366 },
1367 )
1368 .occlude()
1369 .max_h(max_height)
1370 .track_scroll(self.scroll_handle.clone())
1371 .with_width_from_item(widest_completion_ix)
1372 .with_sizing_behavior(ListSizingBehavior::Infer);
1373
1374 Popover::new()
1375 .child(list)
1376 .when_some(aside_contents, |popover, aside_contents| {
1377 popover.aside(aside_contents)
1378 })
1379 .into_any_element()
1380 }
1381
1382 pub async fn filter(&mut self, query: Option<&str>, executor: BackgroundExecutor) {
1383 let mut matches = if let Some(query) = query {
1384 fuzzy::match_strings(
1385 &self.match_candidates,
1386 query,
1387 query.chars().any(|c| c.is_uppercase()),
1388 100,
1389 &Default::default(),
1390 executor,
1391 )
1392 .await
1393 } else {
1394 self.match_candidates
1395 .iter()
1396 .enumerate()
1397 .map(|(candidate_id, candidate)| StringMatch {
1398 candidate_id,
1399 score: Default::default(),
1400 positions: Default::default(),
1401 string: candidate.string.clone(),
1402 })
1403 .collect()
1404 };
1405
1406 // Remove all candidates where the query's start does not match the start of any word in the candidate
1407 if let Some(query) = query {
1408 if let Some(query_start) = query.chars().next() {
1409 matches.retain(|string_match| {
1410 split_words(&string_match.string).any(|word| {
1411 // Check that the first codepoint of the word as lowercase matches the first
1412 // codepoint of the query as lowercase
1413 word.chars()
1414 .flat_map(|codepoint| codepoint.to_lowercase())
1415 .zip(query_start.to_lowercase())
1416 .all(|(word_cp, query_cp)| word_cp == query_cp)
1417 })
1418 });
1419 }
1420 }
1421
1422 let completions = self.completions.read();
1423 if self.sort_completions {
1424 matches.sort_unstable_by_key(|mat| {
1425 // We do want to strike a balance here between what the language server tells us
1426 // to sort by (the sort_text) and what are "obvious" good matches (i.e. when you type
1427 // `Creat` and there is a local variable called `CreateComponent`).
1428 // So what we do is: we bucket all matches into two buckets
1429 // - Strong matches
1430 // - Weak matches
1431 // Strong matches are the ones with a high fuzzy-matcher score (the "obvious" matches)
1432 // and the Weak matches are the rest.
1433 //
1434 // For the strong matches, we sort by our fuzzy-finder score first and for the weak
1435 // matches, we prefer language-server sort_text first.
1436 //
1437 // The thinking behind that: we want to show strong matches first in order of relevance(fuzzy score).
1438 // Rest of the matches(weak) can be sorted as language-server expects.
1439
1440 #[derive(PartialEq, Eq, PartialOrd, Ord)]
1441 enum MatchScore<'a> {
1442 Strong {
1443 score: Reverse<OrderedFloat<f64>>,
1444 sort_text: Option<&'a str>,
1445 sort_key: (usize, &'a str),
1446 },
1447 Weak {
1448 sort_text: Option<&'a str>,
1449 score: Reverse<OrderedFloat<f64>>,
1450 sort_key: (usize, &'a str),
1451 },
1452 }
1453
1454 let completion = &completions[mat.candidate_id];
1455 let sort_key = completion.sort_key();
1456 let sort_text = completion.lsp_completion.sort_text.as_deref();
1457 let score = Reverse(OrderedFloat(mat.score));
1458
1459 if mat.score >= 0.2 {
1460 MatchScore::Strong {
1461 score,
1462 sort_text,
1463 sort_key,
1464 }
1465 } else {
1466 MatchScore::Weak {
1467 sort_text,
1468 score,
1469 sort_key,
1470 }
1471 }
1472 });
1473 }
1474
1475 for mat in &mut matches {
1476 let completion = &completions[mat.candidate_id];
1477 mat.string.clone_from(&completion.label.text);
1478 for position in &mut mat.positions {
1479 *position += completion.label.filter_range.start;
1480 }
1481 }
1482 drop(completions);
1483
1484 self.matches = matches.into();
1485 self.selected_item = 0;
1486 }
1487}
1488
1489#[derive(Clone)]
1490struct AvailableCodeAction {
1491 excerpt_id: ExcerptId,
1492 action: CodeAction,
1493 provider: Arc<dyn CodeActionProvider>,
1494}
1495
1496#[derive(Clone)]
1497struct CodeActionContents {
1498 tasks: Option<Arc<ResolvedTasks>>,
1499 actions: Option<Arc<[AvailableCodeAction]>>,
1500}
1501
1502impl CodeActionContents {
1503 fn len(&self) -> usize {
1504 match (&self.tasks, &self.actions) {
1505 (Some(tasks), Some(actions)) => actions.len() + tasks.templates.len(),
1506 (Some(tasks), None) => tasks.templates.len(),
1507 (None, Some(actions)) => actions.len(),
1508 (None, None) => 0,
1509 }
1510 }
1511
1512 fn is_empty(&self) -> bool {
1513 match (&self.tasks, &self.actions) {
1514 (Some(tasks), Some(actions)) => actions.is_empty() && tasks.templates.is_empty(),
1515 (Some(tasks), None) => tasks.templates.is_empty(),
1516 (None, Some(actions)) => actions.is_empty(),
1517 (None, None) => true,
1518 }
1519 }
1520
1521 fn iter(&self) -> impl Iterator<Item = CodeActionsItem> + '_ {
1522 self.tasks
1523 .iter()
1524 .flat_map(|tasks| {
1525 tasks
1526 .templates
1527 .iter()
1528 .map(|(kind, task)| CodeActionsItem::Task(kind.clone(), task.clone()))
1529 })
1530 .chain(self.actions.iter().flat_map(|actions| {
1531 actions.iter().map(|available| CodeActionsItem::CodeAction {
1532 excerpt_id: available.excerpt_id,
1533 action: available.action.clone(),
1534 provider: available.provider.clone(),
1535 })
1536 }))
1537 }
1538 fn get(&self, index: usize) -> Option<CodeActionsItem> {
1539 match (&self.tasks, &self.actions) {
1540 (Some(tasks), Some(actions)) => {
1541 if index < tasks.templates.len() {
1542 tasks
1543 .templates
1544 .get(index)
1545 .cloned()
1546 .map(|(kind, task)| CodeActionsItem::Task(kind, task))
1547 } else {
1548 actions.get(index - tasks.templates.len()).map(|available| {
1549 CodeActionsItem::CodeAction {
1550 excerpt_id: available.excerpt_id,
1551 action: available.action.clone(),
1552 provider: available.provider.clone(),
1553 }
1554 })
1555 }
1556 }
1557 (Some(tasks), None) => tasks
1558 .templates
1559 .get(index)
1560 .cloned()
1561 .map(|(kind, task)| CodeActionsItem::Task(kind, task)),
1562 (None, Some(actions)) => {
1563 actions
1564 .get(index)
1565 .map(|available| CodeActionsItem::CodeAction {
1566 excerpt_id: available.excerpt_id,
1567 action: available.action.clone(),
1568 provider: available.provider.clone(),
1569 })
1570 }
1571 (None, None) => None,
1572 }
1573 }
1574}
1575
1576#[allow(clippy::large_enum_variant)]
1577#[derive(Clone)]
1578enum CodeActionsItem {
1579 Task(TaskSourceKind, ResolvedTask),
1580 CodeAction {
1581 excerpt_id: ExcerptId,
1582 action: CodeAction,
1583 provider: Arc<dyn CodeActionProvider>,
1584 },
1585}
1586
1587impl CodeActionsItem {
1588 fn as_task(&self) -> Option<&ResolvedTask> {
1589 let Self::Task(_, task) = self else {
1590 return None;
1591 };
1592 Some(task)
1593 }
1594 fn as_code_action(&self) -> Option<&CodeAction> {
1595 let Self::CodeAction { action, .. } = self else {
1596 return None;
1597 };
1598 Some(action)
1599 }
1600 fn label(&self) -> String {
1601 match self {
1602 Self::CodeAction { action, .. } => action.lsp_action.title.clone(),
1603 Self::Task(_, task) => task.resolved_label.clone(),
1604 }
1605 }
1606}
1607
1608struct CodeActionsMenu {
1609 actions: CodeActionContents,
1610 buffer: Model<Buffer>,
1611 selected_item: usize,
1612 scroll_handle: UniformListScrollHandle,
1613 deployed_from_indicator: Option<DisplayRow>,
1614}
1615
1616impl CodeActionsMenu {
1617 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
1618 self.selected_item = 0;
1619 self.scroll_handle
1620 .scroll_to_item(self.selected_item, ScrollStrategy::Top);
1621 cx.notify()
1622 }
1623
1624 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
1625 if self.selected_item > 0 {
1626 self.selected_item -= 1;
1627 } else {
1628 self.selected_item = self.actions.len() - 1;
1629 }
1630 self.scroll_handle
1631 .scroll_to_item(self.selected_item, ScrollStrategy::Top);
1632 cx.notify();
1633 }
1634
1635 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
1636 if self.selected_item + 1 < self.actions.len() {
1637 self.selected_item += 1;
1638 } else {
1639 self.selected_item = 0;
1640 }
1641 self.scroll_handle
1642 .scroll_to_item(self.selected_item, ScrollStrategy::Top);
1643 cx.notify();
1644 }
1645
1646 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
1647 self.selected_item = self.actions.len() - 1;
1648 self.scroll_handle
1649 .scroll_to_item(self.selected_item, ScrollStrategy::Top);
1650 cx.notify()
1651 }
1652
1653 fn visible(&self) -> bool {
1654 !self.actions.is_empty()
1655 }
1656
1657 fn render(
1658 &self,
1659 cursor_position: DisplayPoint,
1660 _style: &EditorStyle,
1661 max_height: Pixels,
1662 cx: &mut ViewContext<Editor>,
1663 ) -> (ContextMenuOrigin, AnyElement) {
1664 let actions = self.actions.clone();
1665 let selected_item = self.selected_item;
1666 let element = uniform_list(
1667 cx.view().clone(),
1668 "code_actions_menu",
1669 self.actions.len(),
1670 move |_this, range, cx| {
1671 actions
1672 .iter()
1673 .skip(range.start)
1674 .take(range.end - range.start)
1675 .enumerate()
1676 .map(|(ix, action)| {
1677 let item_ix = range.start + ix;
1678 let selected = selected_item == item_ix;
1679 let colors = cx.theme().colors();
1680 div()
1681 .px_1()
1682 .rounded_md()
1683 .text_color(colors.text)
1684 .when(selected, |style| {
1685 style
1686 .bg(colors.element_active)
1687 .text_color(colors.text_accent)
1688 })
1689 .hover(|style| {
1690 style
1691 .bg(colors.element_hover)
1692 .text_color(colors.text_accent)
1693 })
1694 .whitespace_nowrap()
1695 .when_some(action.as_code_action(), |this, action| {
1696 this.on_mouse_down(
1697 MouseButton::Left,
1698 cx.listener(move |editor, _, cx| {
1699 cx.stop_propagation();
1700 if let Some(task) = editor.confirm_code_action(
1701 &ConfirmCodeAction {
1702 item_ix: Some(item_ix),
1703 },
1704 cx,
1705 ) {
1706 task.detach_and_log_err(cx)
1707 }
1708 }),
1709 )
1710 // TASK: It would be good to make lsp_action.title a SharedString to avoid allocating here.
1711 .child(SharedString::from(
1712 action.lsp_action.title.replace("\n", ""),
1713 ))
1714 })
1715 .when_some(action.as_task(), |this, task| {
1716 this.on_mouse_down(
1717 MouseButton::Left,
1718 cx.listener(move |editor, _, cx| {
1719 cx.stop_propagation();
1720 if let Some(task) = editor.confirm_code_action(
1721 &ConfirmCodeAction {
1722 item_ix: Some(item_ix),
1723 },
1724 cx,
1725 ) {
1726 task.detach_and_log_err(cx)
1727 }
1728 }),
1729 )
1730 .child(SharedString::from(task.resolved_label.replace("\n", "")))
1731 })
1732 })
1733 .collect()
1734 },
1735 )
1736 .elevation_1(cx)
1737 .p_1()
1738 .max_h(max_height)
1739 .occlude()
1740 .track_scroll(self.scroll_handle.clone())
1741 .with_width_from_item(
1742 self.actions
1743 .iter()
1744 .enumerate()
1745 .max_by_key(|(_, action)| match action {
1746 CodeActionsItem::Task(_, task) => task.resolved_label.chars().count(),
1747 CodeActionsItem::CodeAction { action, .. } => {
1748 action.lsp_action.title.chars().count()
1749 }
1750 })
1751 .map(|(ix, _)| ix),
1752 )
1753 .with_sizing_behavior(ListSizingBehavior::Infer)
1754 .into_any_element();
1755
1756 let cursor_position = if let Some(row) = self.deployed_from_indicator {
1757 ContextMenuOrigin::GutterIndicator(row)
1758 } else {
1759 ContextMenuOrigin::EditorPoint(cursor_position)
1760 };
1761
1762 (cursor_position, element)
1763 }
1764}
1765
1766#[derive(Debug)]
1767struct ActiveDiagnosticGroup {
1768 primary_range: Range<Anchor>,
1769 primary_message: String,
1770 group_id: usize,
1771 blocks: HashMap<CustomBlockId, Diagnostic>,
1772 is_valid: bool,
1773}
1774
1775#[derive(Serialize, Deserialize, Clone, Debug)]
1776pub struct ClipboardSelection {
1777 pub len: usize,
1778 pub is_entire_line: bool,
1779 pub first_line_indent: u32,
1780}
1781
1782#[derive(Debug)]
1783pub(crate) struct NavigationData {
1784 cursor_anchor: Anchor,
1785 cursor_position: Point,
1786 scroll_anchor: ScrollAnchor,
1787 scroll_top_row: u32,
1788}
1789
1790#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1791pub enum GotoDefinitionKind {
1792 Symbol,
1793 Declaration,
1794 Type,
1795 Implementation,
1796}
1797
1798#[derive(Debug, Clone)]
1799enum InlayHintRefreshReason {
1800 Toggle(bool),
1801 SettingsChange(InlayHintSettings),
1802 NewLinesShown,
1803 BufferEdited(HashSet<Arc<Language>>),
1804 RefreshRequested,
1805 ExcerptsRemoved(Vec<ExcerptId>),
1806}
1807
1808impl InlayHintRefreshReason {
1809 fn description(&self) -> &'static str {
1810 match self {
1811 Self::Toggle(_) => "toggle",
1812 Self::SettingsChange(_) => "settings change",
1813 Self::NewLinesShown => "new lines shown",
1814 Self::BufferEdited(_) => "buffer edited",
1815 Self::RefreshRequested => "refresh requested",
1816 Self::ExcerptsRemoved(_) => "excerpts removed",
1817 }
1818 }
1819}
1820
1821pub(crate) struct FocusedBlock {
1822 id: BlockId,
1823 focus_handle: WeakFocusHandle,
1824}
1825
1826#[derive(Clone)]
1827struct JumpData {
1828 excerpt_id: ExcerptId,
1829 position: Point,
1830 anchor: text::Anchor,
1831 path: Option<project::ProjectPath>,
1832 line_offset_from_top: u32,
1833}
1834
1835impl Editor {
1836 pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
1837 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1838 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1839 Self::new(
1840 EditorMode::SingleLine { auto_width: false },
1841 buffer,
1842 None,
1843 false,
1844 cx,
1845 )
1846 }
1847
1848 pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
1849 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1850 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1851 Self::new(EditorMode::Full, buffer, None, false, cx)
1852 }
1853
1854 pub fn auto_width(cx: &mut ViewContext<Self>) -> Self {
1855 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1856 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1857 Self::new(
1858 EditorMode::SingleLine { auto_width: true },
1859 buffer,
1860 None,
1861 false,
1862 cx,
1863 )
1864 }
1865
1866 pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
1867 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1868 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1869 Self::new(
1870 EditorMode::AutoHeight { max_lines },
1871 buffer,
1872 None,
1873 false,
1874 cx,
1875 )
1876 }
1877
1878 pub fn for_buffer(
1879 buffer: Model<Buffer>,
1880 project: Option<Model<Project>>,
1881 cx: &mut ViewContext<Self>,
1882 ) -> Self {
1883 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1884 Self::new(EditorMode::Full, buffer, project, false, cx)
1885 }
1886
1887 pub fn for_multibuffer(
1888 buffer: Model<MultiBuffer>,
1889 project: Option<Model<Project>>,
1890 show_excerpt_controls: bool,
1891 cx: &mut ViewContext<Self>,
1892 ) -> Self {
1893 Self::new(EditorMode::Full, buffer, project, show_excerpt_controls, cx)
1894 }
1895
1896 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1897 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1898 let mut clone = Self::new(
1899 self.mode,
1900 self.buffer.clone(),
1901 self.project.clone(),
1902 show_excerpt_controls,
1903 cx,
1904 );
1905 self.display_map.update(cx, |display_map, cx| {
1906 let snapshot = display_map.snapshot(cx);
1907 clone.display_map.update(cx, |display_map, cx| {
1908 display_map.set_state(&snapshot, cx);
1909 });
1910 });
1911 clone.selections.clone_state(&self.selections);
1912 clone.scroll_manager.clone_state(&self.scroll_manager);
1913 clone.searchable = self.searchable;
1914 clone
1915 }
1916
1917 pub fn new(
1918 mode: EditorMode,
1919 buffer: Model<MultiBuffer>,
1920 project: Option<Model<Project>>,
1921 show_excerpt_controls: bool,
1922 cx: &mut ViewContext<Self>,
1923 ) -> Self {
1924 let style = cx.text_style();
1925 let font_size = style.font_size.to_pixels(cx.rem_size());
1926 let editor = cx.view().downgrade();
1927 let fold_placeholder = FoldPlaceholder {
1928 constrain_width: true,
1929 render: Arc::new(move |fold_id, fold_range, cx| {
1930 let editor = editor.clone();
1931 div()
1932 .id(fold_id)
1933 .bg(cx.theme().colors().ghost_element_background)
1934 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1935 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1936 .rounded_sm()
1937 .size_full()
1938 .cursor_pointer()
1939 .child("⋯")
1940 .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
1941 .on_click(move |_, cx| {
1942 editor
1943 .update(cx, |editor, cx| {
1944 editor.unfold_ranges(
1945 &[fold_range.start..fold_range.end],
1946 true,
1947 false,
1948 cx,
1949 );
1950 cx.stop_propagation();
1951 })
1952 .ok();
1953 })
1954 .into_any()
1955 }),
1956 merge_adjacent: true,
1957 ..Default::default()
1958 };
1959 let display_map = cx.new_model(|cx| {
1960 DisplayMap::new(
1961 buffer.clone(),
1962 style.font(),
1963 font_size,
1964 None,
1965 show_excerpt_controls,
1966 FILE_HEADER_HEIGHT,
1967 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1968 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1969 fold_placeholder,
1970 cx,
1971 )
1972 });
1973
1974 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1975
1976 let blink_manager = cx.new_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1977
1978 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1979 .then(|| language_settings::SoftWrap::None);
1980
1981 let mut project_subscriptions = Vec::new();
1982 if mode == EditorMode::Full {
1983 if let Some(project) = project.as_ref() {
1984 if buffer.read(cx).is_singleton() {
1985 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1986 cx.emit(EditorEvent::TitleChanged);
1987 }));
1988 }
1989 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1990 if let project::Event::RefreshInlayHints = event {
1991 editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1992 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1993 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1994 let focus_handle = editor.focus_handle(cx);
1995 if focus_handle.is_focused(cx) {
1996 let snapshot = buffer.read(cx).snapshot();
1997 for (range, snippet) in snippet_edits {
1998 let editor_range =
1999 language::range_from_lsp(*range).to_offset(&snapshot);
2000 editor
2001 .insert_snippet(&[editor_range], snippet.clone(), cx)
2002 .ok();
2003 }
2004 }
2005 }
2006 }
2007 }));
2008 if let Some(task_inventory) = project
2009 .read(cx)
2010 .task_store()
2011 .read(cx)
2012 .task_inventory()
2013 .cloned()
2014 {
2015 project_subscriptions.push(cx.observe(&task_inventory, |editor, _, cx| {
2016 editor.tasks_update_task = Some(editor.refresh_runnables(cx));
2017 }));
2018 }
2019 }
2020 }
2021
2022 let buffer_snapshot = buffer.read(cx).snapshot(cx);
2023
2024 let inlay_hint_settings =
2025 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
2026 let focus_handle = cx.focus_handle();
2027 cx.on_focus(&focus_handle, Self::handle_focus).detach();
2028 cx.on_focus_in(&focus_handle, Self::handle_focus_in)
2029 .detach();
2030 cx.on_focus_out(&focus_handle, Self::handle_focus_out)
2031 .detach();
2032 cx.on_blur(&focus_handle, Self::handle_blur).detach();
2033
2034 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
2035 Some(false)
2036 } else {
2037 None
2038 };
2039
2040 let mut code_action_providers = Vec::new();
2041 if let Some(project) = project.clone() {
2042 get_unstaged_changes_for_buffers(&project, buffer.read(cx).all_buffers(), cx);
2043 code_action_providers.push(Arc::new(project) as Arc<_>);
2044 }
2045
2046 let mut this = Self {
2047 focus_handle,
2048 show_cursor_when_unfocused: false,
2049 last_focused_descendant: None,
2050 buffer: buffer.clone(),
2051 display_map: display_map.clone(),
2052 selections,
2053 scroll_manager: ScrollManager::new(cx),
2054 columnar_selection_tail: None,
2055 add_selections_state: None,
2056 select_next_state: None,
2057 select_prev_state: None,
2058 selection_history: Default::default(),
2059 autoclose_regions: Default::default(),
2060 snippet_stack: Default::default(),
2061 select_larger_syntax_node_stack: Vec::new(),
2062 ime_transaction: Default::default(),
2063 active_diagnostics: None,
2064 soft_wrap_mode_override,
2065 completion_provider: project.clone().map(|project| Box::new(project) as _),
2066 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
2067 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
2068 project,
2069 blink_manager: blink_manager.clone(),
2070 show_local_selections: true,
2071 mode,
2072 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
2073 show_gutter: mode == EditorMode::Full,
2074 show_line_numbers: None,
2075 use_relative_line_numbers: None,
2076 show_git_diff_gutter: None,
2077 show_code_actions: None,
2078 show_runnables: None,
2079 show_wrap_guides: None,
2080 show_indent_guides,
2081 placeholder_text: None,
2082 highlight_order: 0,
2083 highlighted_rows: HashMap::default(),
2084 background_highlights: Default::default(),
2085 gutter_highlights: TreeMap::default(),
2086 scrollbar_marker_state: ScrollbarMarkerState::default(),
2087 active_indent_guides_state: ActiveIndentGuidesState::default(),
2088 nav_history: None,
2089 context_menu: RwLock::new(None),
2090 mouse_context_menu: None,
2091 hunk_controls_menu_handle: PopoverMenuHandle::default(),
2092 completion_tasks: Default::default(),
2093 signature_help_state: SignatureHelpState::default(),
2094 auto_signature_help: None,
2095 find_all_references_task_sources: Vec::new(),
2096 next_completion_id: 0,
2097 next_inlay_id: 0,
2098 code_action_providers,
2099 available_code_actions: Default::default(),
2100 code_actions_task: Default::default(),
2101 document_highlights_task: Default::default(),
2102 linked_editing_range_task: Default::default(),
2103 pending_rename: Default::default(),
2104 searchable: true,
2105 cursor_shape: EditorSettings::get_global(cx)
2106 .cursor_shape
2107 .unwrap_or_default(),
2108 current_line_highlight: None,
2109 autoindent_mode: Some(AutoindentMode::EachLine),
2110 collapse_matches: false,
2111 workspace: None,
2112 input_enabled: true,
2113 use_modal_editing: mode == EditorMode::Full,
2114 read_only: false,
2115 use_autoclose: true,
2116 use_auto_surround: true,
2117 auto_replace_emoji_shortcode: false,
2118 leader_peer_id: None,
2119 remote_id: None,
2120 hover_state: Default::default(),
2121 hovered_link_state: Default::default(),
2122 inline_completion_provider: None,
2123 active_inline_completion: None,
2124 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
2125 diff_map: DiffMap::default(),
2126 gutter_hovered: false,
2127 pixel_position_of_newest_cursor: None,
2128 last_bounds: None,
2129 expect_bounds_change: None,
2130 gutter_dimensions: GutterDimensions::default(),
2131 style: None,
2132 show_cursor_names: false,
2133 hovered_cursors: Default::default(),
2134 next_editor_action_id: EditorActionId::default(),
2135 editor_actions: Rc::default(),
2136 show_inline_completions_override: None,
2137 enable_inline_completions: true,
2138 custom_context_menu: None,
2139 show_git_blame_gutter: false,
2140 show_git_blame_inline: false,
2141 show_selection_menu: None,
2142 show_git_blame_inline_delay_task: None,
2143 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
2144 serialize_dirty_buffers: ProjectSettings::get_global(cx)
2145 .session
2146 .restore_unsaved_buffers,
2147 blame: None,
2148 blame_subscription: None,
2149 tasks: Default::default(),
2150 _subscriptions: vec![
2151 cx.observe(&buffer, Self::on_buffer_changed),
2152 cx.subscribe(&buffer, Self::on_buffer_event),
2153 cx.observe(&display_map, Self::on_display_map_changed),
2154 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
2155 cx.observe_global::<SettingsStore>(Self::settings_changed),
2156 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
2157 cx.observe_window_activation(|editor, cx| {
2158 let active = cx.is_window_active();
2159 editor.blink_manager.update(cx, |blink_manager, cx| {
2160 if active {
2161 blink_manager.enable(cx);
2162 } else {
2163 blink_manager.disable(cx);
2164 }
2165 });
2166 }),
2167 ],
2168 tasks_update_task: None,
2169 linked_edit_ranges: Default::default(),
2170 previous_search_ranges: None,
2171 breadcrumb_header: None,
2172 focused_block: None,
2173 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
2174 addons: HashMap::default(),
2175 _scroll_cursor_center_top_bottom_task: Task::ready(()),
2176 text_style_refinement: None,
2177 };
2178 this.tasks_update_task = Some(this.refresh_runnables(cx));
2179 this._subscriptions.extend(project_subscriptions);
2180
2181 this.end_selection(cx);
2182 this.scroll_manager.show_scrollbar(cx);
2183
2184 if mode == EditorMode::Full {
2185 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
2186 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
2187
2188 if this.git_blame_inline_enabled {
2189 this.git_blame_inline_enabled = true;
2190 this.start_git_blame_inline(false, cx);
2191 }
2192 }
2193
2194 this.report_editor_event("open", None, cx);
2195 this
2196 }
2197
2198 pub fn mouse_menu_is_focused(&self, cx: &WindowContext) -> bool {
2199 self.mouse_context_menu
2200 .as_ref()
2201 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(cx))
2202 }
2203
2204 fn key_context(&self, cx: &ViewContext<Self>) -> KeyContext {
2205 let mut key_context = KeyContext::new_with_defaults();
2206 key_context.add("Editor");
2207 let mode = match self.mode {
2208 EditorMode::SingleLine { .. } => "single_line",
2209 EditorMode::AutoHeight { .. } => "auto_height",
2210 EditorMode::Full => "full",
2211 };
2212
2213 if EditorSettings::jupyter_enabled(cx) {
2214 key_context.add("jupyter");
2215 }
2216
2217 key_context.set("mode", mode);
2218 if self.pending_rename.is_some() {
2219 key_context.add("renaming");
2220 }
2221 if self.context_menu_visible() {
2222 match self.context_menu.read().as_ref() {
2223 Some(ContextMenu::Completions(_)) => {
2224 key_context.add("menu");
2225 key_context.add("showing_completions")
2226 }
2227 Some(ContextMenu::CodeActions(_)) => {
2228 key_context.add("menu");
2229 key_context.add("showing_code_actions")
2230 }
2231 None => {}
2232 }
2233 }
2234
2235 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
2236 if !self.focus_handle(cx).contains_focused(cx)
2237 || (self.is_focused(cx) || self.mouse_menu_is_focused(cx))
2238 {
2239 for addon in self.addons.values() {
2240 addon.extend_key_context(&mut key_context, cx)
2241 }
2242 }
2243
2244 if let Some(extension) = self
2245 .buffer
2246 .read(cx)
2247 .as_singleton()
2248 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
2249 {
2250 key_context.set("extension", extension.to_string());
2251 }
2252
2253 if self.has_active_inline_completion(cx) {
2254 key_context.add("copilot_suggestion");
2255 key_context.add("inline_completion");
2256 }
2257
2258 key_context
2259 }
2260
2261 pub fn new_file(
2262 workspace: &mut Workspace,
2263 _: &workspace::NewFile,
2264 cx: &mut ViewContext<Workspace>,
2265 ) {
2266 Self::new_in_workspace(workspace, cx).detach_and_prompt_err(
2267 "Failed to create buffer",
2268 cx,
2269 |e, _| match e.error_code() {
2270 ErrorCode::RemoteUpgradeRequired => Some(format!(
2271 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2272 e.error_tag("required").unwrap_or("the latest version")
2273 )),
2274 _ => None,
2275 },
2276 );
2277 }
2278
2279 pub fn new_in_workspace(
2280 workspace: &mut Workspace,
2281 cx: &mut ViewContext<Workspace>,
2282 ) -> Task<Result<View<Editor>>> {
2283 let project = workspace.project().clone();
2284 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2285
2286 cx.spawn(|workspace, mut cx| async move {
2287 let buffer = create.await?;
2288 workspace.update(&mut cx, |workspace, cx| {
2289 let editor =
2290 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx));
2291 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
2292 editor
2293 })
2294 })
2295 }
2296
2297 fn new_file_vertical(
2298 workspace: &mut Workspace,
2299 _: &workspace::NewFileSplitVertical,
2300 cx: &mut ViewContext<Workspace>,
2301 ) {
2302 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), cx)
2303 }
2304
2305 fn new_file_horizontal(
2306 workspace: &mut Workspace,
2307 _: &workspace::NewFileSplitHorizontal,
2308 cx: &mut ViewContext<Workspace>,
2309 ) {
2310 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), cx)
2311 }
2312
2313 fn new_file_in_direction(
2314 workspace: &mut Workspace,
2315 direction: SplitDirection,
2316 cx: &mut ViewContext<Workspace>,
2317 ) {
2318 let project = workspace.project().clone();
2319 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2320
2321 cx.spawn(|workspace, mut cx| async move {
2322 let buffer = create.await?;
2323 workspace.update(&mut cx, move |workspace, cx| {
2324 workspace.split_item(
2325 direction,
2326 Box::new(
2327 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)),
2328 ),
2329 cx,
2330 )
2331 })?;
2332 anyhow::Ok(())
2333 })
2334 .detach_and_prompt_err("Failed to create buffer", cx, |e, _| match e.error_code() {
2335 ErrorCode::RemoteUpgradeRequired => Some(format!(
2336 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2337 e.error_tag("required").unwrap_or("the latest version")
2338 )),
2339 _ => None,
2340 });
2341 }
2342
2343 pub fn leader_peer_id(&self) -> Option<PeerId> {
2344 self.leader_peer_id
2345 }
2346
2347 pub fn buffer(&self) -> &Model<MultiBuffer> {
2348 &self.buffer
2349 }
2350
2351 pub fn workspace(&self) -> Option<View<Workspace>> {
2352 self.workspace.as_ref()?.0.upgrade()
2353 }
2354
2355 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
2356 self.buffer().read(cx).title(cx)
2357 }
2358
2359 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
2360 let git_blame_gutter_max_author_length = self
2361 .render_git_blame_gutter(cx)
2362 .then(|| {
2363 if let Some(blame) = self.blame.as_ref() {
2364 let max_author_length =
2365 blame.update(cx, |blame, cx| blame.max_author_length(cx));
2366 Some(max_author_length)
2367 } else {
2368 None
2369 }
2370 })
2371 .flatten();
2372
2373 EditorSnapshot {
2374 mode: self.mode,
2375 show_gutter: self.show_gutter,
2376 show_line_numbers: self.show_line_numbers,
2377 show_git_diff_gutter: self.show_git_diff_gutter,
2378 show_code_actions: self.show_code_actions,
2379 show_runnables: self.show_runnables,
2380 git_blame_gutter_max_author_length,
2381 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
2382 scroll_anchor: self.scroll_manager.anchor(),
2383 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
2384 placeholder_text: self.placeholder_text.clone(),
2385 diff_map: self.diff_map.snapshot(),
2386 is_focused: self.focus_handle.is_focused(cx),
2387 current_line_highlight: self
2388 .current_line_highlight
2389 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
2390 gutter_hovered: self.gutter_hovered,
2391 }
2392 }
2393
2394 pub fn language_at<T: ToOffset>(&self, point: T, cx: &AppContext) -> Option<Arc<Language>> {
2395 self.buffer.read(cx).language_at(point, cx)
2396 }
2397
2398 pub fn file_at<T: ToOffset>(
2399 &self,
2400 point: T,
2401 cx: &AppContext,
2402 ) -> Option<Arc<dyn language::File>> {
2403 self.buffer.read(cx).read(cx).file_at(point).cloned()
2404 }
2405
2406 pub fn active_excerpt(
2407 &self,
2408 cx: &AppContext,
2409 ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
2410 self.buffer
2411 .read(cx)
2412 .excerpt_containing(self.selections.newest_anchor().head(), cx)
2413 }
2414
2415 pub fn mode(&self) -> EditorMode {
2416 self.mode
2417 }
2418
2419 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
2420 self.collaboration_hub.as_deref()
2421 }
2422
2423 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
2424 self.collaboration_hub = Some(hub);
2425 }
2426
2427 pub fn set_custom_context_menu(
2428 &mut self,
2429 f: impl 'static
2430 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
2431 ) {
2432 self.custom_context_menu = Some(Box::new(f))
2433 }
2434
2435 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
2436 self.completion_provider = provider;
2437 }
2438
2439 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
2440 self.semantics_provider.clone()
2441 }
2442
2443 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
2444 self.semantics_provider = provider;
2445 }
2446
2447 pub fn set_inline_completion_provider<T>(
2448 &mut self,
2449 provider: Option<Model<T>>,
2450 cx: &mut ViewContext<Self>,
2451 ) where
2452 T: InlineCompletionProvider,
2453 {
2454 self.inline_completion_provider =
2455 provider.map(|provider| RegisteredInlineCompletionProvider {
2456 _subscription: cx.observe(&provider, |this, _, cx| {
2457 if this.focus_handle.is_focused(cx) {
2458 this.update_visible_inline_completion(cx);
2459 }
2460 }),
2461 provider: Arc::new(provider),
2462 });
2463 self.refresh_inline_completion(false, false, cx);
2464 }
2465
2466 pub fn placeholder_text(&self, _cx: &WindowContext) -> Option<&str> {
2467 self.placeholder_text.as_deref()
2468 }
2469
2470 pub fn set_placeholder_text(
2471 &mut self,
2472 placeholder_text: impl Into<Arc<str>>,
2473 cx: &mut ViewContext<Self>,
2474 ) {
2475 let placeholder_text = Some(placeholder_text.into());
2476 if self.placeholder_text != placeholder_text {
2477 self.placeholder_text = placeholder_text;
2478 cx.notify();
2479 }
2480 }
2481
2482 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
2483 self.cursor_shape = cursor_shape;
2484
2485 // Disrupt blink for immediate user feedback that the cursor shape has changed
2486 self.blink_manager.update(cx, BlinkManager::show_cursor);
2487
2488 cx.notify();
2489 }
2490
2491 pub fn set_current_line_highlight(
2492 &mut self,
2493 current_line_highlight: Option<CurrentLineHighlight>,
2494 ) {
2495 self.current_line_highlight = current_line_highlight;
2496 }
2497
2498 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
2499 self.collapse_matches = collapse_matches;
2500 }
2501
2502 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
2503 if self.collapse_matches {
2504 return range.start..range.start;
2505 }
2506 range.clone()
2507 }
2508
2509 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
2510 if self.display_map.read(cx).clip_at_line_ends != clip {
2511 self.display_map
2512 .update(cx, |map, _| map.clip_at_line_ends = clip);
2513 }
2514 }
2515
2516 pub fn set_input_enabled(&mut self, input_enabled: bool) {
2517 self.input_enabled = input_enabled;
2518 }
2519
2520 pub fn set_inline_completions_enabled(&mut self, enabled: bool) {
2521 self.enable_inline_completions = enabled;
2522 }
2523
2524 pub fn set_autoindent(&mut self, autoindent: bool) {
2525 if autoindent {
2526 self.autoindent_mode = Some(AutoindentMode::EachLine);
2527 } else {
2528 self.autoindent_mode = None;
2529 }
2530 }
2531
2532 pub fn read_only(&self, cx: &AppContext) -> bool {
2533 self.read_only || self.buffer.read(cx).read_only()
2534 }
2535
2536 pub fn set_read_only(&mut self, read_only: bool) {
2537 self.read_only = read_only;
2538 }
2539
2540 pub fn set_use_autoclose(&mut self, autoclose: bool) {
2541 self.use_autoclose = autoclose;
2542 }
2543
2544 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
2545 self.use_auto_surround = auto_surround;
2546 }
2547
2548 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
2549 self.auto_replace_emoji_shortcode = auto_replace;
2550 }
2551
2552 pub fn toggle_inline_completions(
2553 &mut self,
2554 _: &ToggleInlineCompletions,
2555 cx: &mut ViewContext<Self>,
2556 ) {
2557 if self.show_inline_completions_override.is_some() {
2558 self.set_show_inline_completions(None, cx);
2559 } else {
2560 let cursor = self.selections.newest_anchor().head();
2561 if let Some((buffer, cursor_buffer_position)) =
2562 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
2563 {
2564 let show_inline_completions =
2565 !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx);
2566 self.set_show_inline_completions(Some(show_inline_completions), cx);
2567 }
2568 }
2569 }
2570
2571 pub fn set_show_inline_completions(
2572 &mut self,
2573 show_inline_completions: Option<bool>,
2574 cx: &mut ViewContext<Self>,
2575 ) {
2576 self.show_inline_completions_override = show_inline_completions;
2577 self.refresh_inline_completion(false, true, cx);
2578 }
2579
2580 fn should_show_inline_completions(
2581 &self,
2582 buffer: &Model<Buffer>,
2583 buffer_position: language::Anchor,
2584 cx: &AppContext,
2585 ) -> bool {
2586 if !self.snippet_stack.is_empty() {
2587 return false;
2588 }
2589
2590 if self.inline_completions_disabled_in_scope(buffer, buffer_position, cx) {
2591 return false;
2592 }
2593
2594 if let Some(provider) = self.inline_completion_provider() {
2595 if let Some(show_inline_completions) = self.show_inline_completions_override {
2596 show_inline_completions
2597 } else {
2598 self.mode == EditorMode::Full && provider.is_enabled(buffer, buffer_position, cx)
2599 }
2600 } else {
2601 false
2602 }
2603 }
2604
2605 fn inline_completions_disabled_in_scope(
2606 &self,
2607 buffer: &Model<Buffer>,
2608 buffer_position: language::Anchor,
2609 cx: &AppContext,
2610 ) -> bool {
2611 let snapshot = buffer.read(cx).snapshot();
2612 let settings = snapshot.settings_at(buffer_position, cx);
2613
2614 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
2615 return false;
2616 };
2617
2618 scope.override_name().map_or(false, |scope_name| {
2619 settings
2620 .inline_completions_disabled_in
2621 .iter()
2622 .any(|s| s == scope_name)
2623 })
2624 }
2625
2626 pub fn set_use_modal_editing(&mut self, to: bool) {
2627 self.use_modal_editing = to;
2628 }
2629
2630 pub fn use_modal_editing(&self) -> bool {
2631 self.use_modal_editing
2632 }
2633
2634 fn selections_did_change(
2635 &mut self,
2636 local: bool,
2637 old_cursor_position: &Anchor,
2638 show_completions: bool,
2639 cx: &mut ViewContext<Self>,
2640 ) {
2641 cx.invalidate_character_coordinates();
2642
2643 // Copy selections to primary selection buffer
2644 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
2645 if local {
2646 let selections = self.selections.all::<usize>(cx);
2647 let buffer_handle = self.buffer.read(cx).read(cx);
2648
2649 let mut text = String::new();
2650 for (index, selection) in selections.iter().enumerate() {
2651 let text_for_selection = buffer_handle
2652 .text_for_range(selection.start..selection.end)
2653 .collect::<String>();
2654
2655 text.push_str(&text_for_selection);
2656 if index != selections.len() - 1 {
2657 text.push('\n');
2658 }
2659 }
2660
2661 if !text.is_empty() {
2662 cx.write_to_primary(ClipboardItem::new_string(text));
2663 }
2664 }
2665
2666 if self.focus_handle.is_focused(cx) && self.leader_peer_id.is_none() {
2667 self.buffer.update(cx, |buffer, cx| {
2668 buffer.set_active_selections(
2669 &self.selections.disjoint_anchors(),
2670 self.selections.line_mode,
2671 self.cursor_shape,
2672 cx,
2673 )
2674 });
2675 }
2676 let display_map = self
2677 .display_map
2678 .update(cx, |display_map, cx| display_map.snapshot(cx));
2679 let buffer = &display_map.buffer_snapshot;
2680 self.add_selections_state = None;
2681 self.select_next_state = None;
2682 self.select_prev_state = None;
2683 self.select_larger_syntax_node_stack.clear();
2684 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2685 self.snippet_stack
2686 .invalidate(&self.selections.disjoint_anchors(), buffer);
2687 self.take_rename(false, cx);
2688
2689 let new_cursor_position = self.selections.newest_anchor().head();
2690
2691 self.push_to_nav_history(
2692 *old_cursor_position,
2693 Some(new_cursor_position.to_point(buffer)),
2694 cx,
2695 );
2696
2697 if local {
2698 let new_cursor_position = self.selections.newest_anchor().head();
2699 let mut context_menu = self.context_menu.write();
2700 let completion_menu = match context_menu.as_ref() {
2701 Some(ContextMenu::Completions(menu)) => Some(menu),
2702
2703 _ => {
2704 *context_menu = None;
2705 None
2706 }
2707 };
2708
2709 if let Some(completion_menu) = completion_menu {
2710 let cursor_position = new_cursor_position.to_offset(buffer);
2711 let (word_range, kind) =
2712 buffer.surrounding_word(completion_menu.initial_position, true);
2713 if kind == Some(CharKind::Word)
2714 && word_range.to_inclusive().contains(&cursor_position)
2715 {
2716 let mut completion_menu = completion_menu.clone();
2717 drop(context_menu);
2718
2719 let query = Self::completion_query(buffer, cursor_position);
2720 cx.spawn(move |this, mut cx| async move {
2721 completion_menu
2722 .filter(query.as_deref(), cx.background_executor().clone())
2723 .await;
2724
2725 this.update(&mut cx, |this, cx| {
2726 let mut context_menu = this.context_menu.write();
2727 let Some(ContextMenu::Completions(menu)) = context_menu.as_ref() else {
2728 return;
2729 };
2730
2731 if menu.id > completion_menu.id {
2732 return;
2733 }
2734
2735 *context_menu = Some(ContextMenu::Completions(completion_menu));
2736 drop(context_menu);
2737 cx.notify();
2738 })
2739 })
2740 .detach();
2741
2742 if show_completions {
2743 self.show_completions(&ShowCompletions { trigger: None }, cx);
2744 }
2745 } else {
2746 drop(context_menu);
2747 self.hide_context_menu(cx);
2748 }
2749 } else {
2750 drop(context_menu);
2751 }
2752
2753 hide_hover(self, cx);
2754
2755 if old_cursor_position.to_display_point(&display_map).row()
2756 != new_cursor_position.to_display_point(&display_map).row()
2757 {
2758 self.available_code_actions.take();
2759 }
2760 self.refresh_code_actions(cx);
2761 self.refresh_document_highlights(cx);
2762 refresh_matching_bracket_highlights(self, cx);
2763 self.discard_inline_completion(false, cx);
2764 linked_editing_ranges::refresh_linked_ranges(self, cx);
2765 if self.git_blame_inline_enabled {
2766 self.start_inline_blame_timer(cx);
2767 }
2768 }
2769
2770 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2771 cx.emit(EditorEvent::SelectionsChanged { local });
2772
2773 if self.selections.disjoint_anchors().len() == 1 {
2774 cx.emit(SearchEvent::ActiveMatchChanged)
2775 }
2776 cx.notify();
2777 }
2778
2779 pub fn change_selections<R>(
2780 &mut self,
2781 autoscroll: Option<Autoscroll>,
2782 cx: &mut ViewContext<Self>,
2783 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2784 ) -> R {
2785 self.change_selections_inner(autoscroll, true, cx, change)
2786 }
2787
2788 pub fn change_selections_inner<R>(
2789 &mut self,
2790 autoscroll: Option<Autoscroll>,
2791 request_completions: bool,
2792 cx: &mut ViewContext<Self>,
2793 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2794 ) -> R {
2795 let old_cursor_position = self.selections.newest_anchor().head();
2796 self.push_to_selection_history();
2797
2798 let (changed, result) = self.selections.change_with(cx, change);
2799
2800 if changed {
2801 if let Some(autoscroll) = autoscroll {
2802 self.request_autoscroll(autoscroll, cx);
2803 }
2804 self.selections_did_change(true, &old_cursor_position, request_completions, cx);
2805
2806 if self.should_open_signature_help_automatically(
2807 &old_cursor_position,
2808 self.signature_help_state.backspace_pressed(),
2809 cx,
2810 ) {
2811 self.show_signature_help(&ShowSignatureHelp, cx);
2812 }
2813 self.signature_help_state.set_backspace_pressed(false);
2814 }
2815
2816 result
2817 }
2818
2819 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2820 where
2821 I: IntoIterator<Item = (Range<S>, T)>,
2822 S: ToOffset,
2823 T: Into<Arc<str>>,
2824 {
2825 if self.read_only(cx) {
2826 return;
2827 }
2828
2829 self.buffer
2830 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2831 }
2832
2833 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2834 where
2835 I: IntoIterator<Item = (Range<S>, T)>,
2836 S: ToOffset,
2837 T: Into<Arc<str>>,
2838 {
2839 if self.read_only(cx) {
2840 return;
2841 }
2842
2843 self.buffer.update(cx, |buffer, cx| {
2844 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2845 });
2846 }
2847
2848 pub fn edit_with_block_indent<I, S, T>(
2849 &mut self,
2850 edits: I,
2851 original_indent_columns: Vec<u32>,
2852 cx: &mut ViewContext<Self>,
2853 ) where
2854 I: IntoIterator<Item = (Range<S>, T)>,
2855 S: ToOffset,
2856 T: Into<Arc<str>>,
2857 {
2858 if self.read_only(cx) {
2859 return;
2860 }
2861
2862 self.buffer.update(cx, |buffer, cx| {
2863 buffer.edit(
2864 edits,
2865 Some(AutoindentMode::Block {
2866 original_indent_columns,
2867 }),
2868 cx,
2869 )
2870 });
2871 }
2872
2873 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
2874 self.hide_context_menu(cx);
2875
2876 match phase {
2877 SelectPhase::Begin {
2878 position,
2879 add,
2880 click_count,
2881 } => self.begin_selection(position, add, click_count, cx),
2882 SelectPhase::BeginColumnar {
2883 position,
2884 goal_column,
2885 reset,
2886 } => self.begin_columnar_selection(position, goal_column, reset, cx),
2887 SelectPhase::Extend {
2888 position,
2889 click_count,
2890 } => self.extend_selection(position, click_count, cx),
2891 SelectPhase::Update {
2892 position,
2893 goal_column,
2894 scroll_delta,
2895 } => self.update_selection(position, goal_column, scroll_delta, cx),
2896 SelectPhase::End => self.end_selection(cx),
2897 }
2898 }
2899
2900 fn extend_selection(
2901 &mut self,
2902 position: DisplayPoint,
2903 click_count: usize,
2904 cx: &mut ViewContext<Self>,
2905 ) {
2906 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2907 let tail = self.selections.newest::<usize>(cx).tail();
2908 self.begin_selection(position, false, click_count, cx);
2909
2910 let position = position.to_offset(&display_map, Bias::Left);
2911 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2912
2913 let mut pending_selection = self
2914 .selections
2915 .pending_anchor()
2916 .expect("extend_selection not called with pending selection");
2917 if position >= tail {
2918 pending_selection.start = tail_anchor;
2919 } else {
2920 pending_selection.end = tail_anchor;
2921 pending_selection.reversed = true;
2922 }
2923
2924 let mut pending_mode = self.selections.pending_mode().unwrap();
2925 match &mut pending_mode {
2926 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2927 _ => {}
2928 }
2929
2930 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
2931 s.set_pending(pending_selection, pending_mode)
2932 });
2933 }
2934
2935 fn begin_selection(
2936 &mut self,
2937 position: DisplayPoint,
2938 add: bool,
2939 click_count: usize,
2940 cx: &mut ViewContext<Self>,
2941 ) {
2942 if !self.focus_handle.is_focused(cx) {
2943 self.last_focused_descendant = None;
2944 cx.focus(&self.focus_handle);
2945 }
2946
2947 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2948 let buffer = &display_map.buffer_snapshot;
2949 let newest_selection = self.selections.newest_anchor().clone();
2950 let position = display_map.clip_point(position, Bias::Left);
2951
2952 let start;
2953 let end;
2954 let mode;
2955 let mut auto_scroll;
2956 match click_count {
2957 1 => {
2958 start = buffer.anchor_before(position.to_point(&display_map));
2959 end = start;
2960 mode = SelectMode::Character;
2961 auto_scroll = true;
2962 }
2963 2 => {
2964 let range = movement::surrounding_word(&display_map, position);
2965 start = buffer.anchor_before(range.start.to_point(&display_map));
2966 end = buffer.anchor_before(range.end.to_point(&display_map));
2967 mode = SelectMode::Word(start..end);
2968 auto_scroll = true;
2969 }
2970 3 => {
2971 let position = display_map
2972 .clip_point(position, Bias::Left)
2973 .to_point(&display_map);
2974 let line_start = display_map.prev_line_boundary(position).0;
2975 let next_line_start = buffer.clip_point(
2976 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2977 Bias::Left,
2978 );
2979 start = buffer.anchor_before(line_start);
2980 end = buffer.anchor_before(next_line_start);
2981 mode = SelectMode::Line(start..end);
2982 auto_scroll = true;
2983 }
2984 _ => {
2985 start = buffer.anchor_before(0);
2986 end = buffer.anchor_before(buffer.len());
2987 mode = SelectMode::All;
2988 auto_scroll = false;
2989 }
2990 }
2991 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2992
2993 let point_to_delete: Option<usize> = {
2994 let selected_points: Vec<Selection<Point>> =
2995 self.selections.disjoint_in_range(start..end, cx);
2996
2997 if !add || click_count > 1 {
2998 None
2999 } else if !selected_points.is_empty() {
3000 Some(selected_points[0].id)
3001 } else {
3002 let clicked_point_already_selected =
3003 self.selections.disjoint.iter().find(|selection| {
3004 selection.start.to_point(buffer) == start.to_point(buffer)
3005 || selection.end.to_point(buffer) == end.to_point(buffer)
3006 });
3007
3008 clicked_point_already_selected.map(|selection| selection.id)
3009 }
3010 };
3011
3012 let selections_count = self.selections.count();
3013
3014 self.change_selections(auto_scroll.then(Autoscroll::newest), cx, |s| {
3015 if let Some(point_to_delete) = point_to_delete {
3016 s.delete(point_to_delete);
3017
3018 if selections_count == 1 {
3019 s.set_pending_anchor_range(start..end, mode);
3020 }
3021 } else {
3022 if !add {
3023 s.clear_disjoint();
3024 } else if click_count > 1 {
3025 s.delete(newest_selection.id)
3026 }
3027
3028 s.set_pending_anchor_range(start..end, mode);
3029 }
3030 });
3031 }
3032
3033 fn begin_columnar_selection(
3034 &mut self,
3035 position: DisplayPoint,
3036 goal_column: u32,
3037 reset: bool,
3038 cx: &mut ViewContext<Self>,
3039 ) {
3040 if !self.focus_handle.is_focused(cx) {
3041 self.last_focused_descendant = None;
3042 cx.focus(&self.focus_handle);
3043 }
3044
3045 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3046
3047 if reset {
3048 let pointer_position = display_map
3049 .buffer_snapshot
3050 .anchor_before(position.to_point(&display_map));
3051
3052 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
3053 s.clear_disjoint();
3054 s.set_pending_anchor_range(
3055 pointer_position..pointer_position,
3056 SelectMode::Character,
3057 );
3058 });
3059 }
3060
3061 let tail = self.selections.newest::<Point>(cx).tail();
3062 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
3063
3064 if !reset {
3065 self.select_columns(
3066 tail.to_display_point(&display_map),
3067 position,
3068 goal_column,
3069 &display_map,
3070 cx,
3071 );
3072 }
3073 }
3074
3075 fn update_selection(
3076 &mut self,
3077 position: DisplayPoint,
3078 goal_column: u32,
3079 scroll_delta: gpui::Point<f32>,
3080 cx: &mut ViewContext<Self>,
3081 ) {
3082 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3083
3084 if let Some(tail) = self.columnar_selection_tail.as_ref() {
3085 let tail = tail.to_display_point(&display_map);
3086 self.select_columns(tail, position, goal_column, &display_map, cx);
3087 } else if let Some(mut pending) = self.selections.pending_anchor() {
3088 let buffer = self.buffer.read(cx).snapshot(cx);
3089 let head;
3090 let tail;
3091 let mode = self.selections.pending_mode().unwrap();
3092 match &mode {
3093 SelectMode::Character => {
3094 head = position.to_point(&display_map);
3095 tail = pending.tail().to_point(&buffer);
3096 }
3097 SelectMode::Word(original_range) => {
3098 let original_display_range = original_range.start.to_display_point(&display_map)
3099 ..original_range.end.to_display_point(&display_map);
3100 let original_buffer_range = original_display_range.start.to_point(&display_map)
3101 ..original_display_range.end.to_point(&display_map);
3102 if movement::is_inside_word(&display_map, position)
3103 || original_display_range.contains(&position)
3104 {
3105 let word_range = movement::surrounding_word(&display_map, position);
3106 if word_range.start < original_display_range.start {
3107 head = word_range.start.to_point(&display_map);
3108 } else {
3109 head = word_range.end.to_point(&display_map);
3110 }
3111 } else {
3112 head = position.to_point(&display_map);
3113 }
3114
3115 if head <= original_buffer_range.start {
3116 tail = original_buffer_range.end;
3117 } else {
3118 tail = original_buffer_range.start;
3119 }
3120 }
3121 SelectMode::Line(original_range) => {
3122 let original_range = original_range.to_point(&display_map.buffer_snapshot);
3123
3124 let position = display_map
3125 .clip_point(position, Bias::Left)
3126 .to_point(&display_map);
3127 let line_start = display_map.prev_line_boundary(position).0;
3128 let next_line_start = buffer.clip_point(
3129 display_map.next_line_boundary(position).0 + Point::new(1, 0),
3130 Bias::Left,
3131 );
3132
3133 if line_start < original_range.start {
3134 head = line_start
3135 } else {
3136 head = next_line_start
3137 }
3138
3139 if head <= original_range.start {
3140 tail = original_range.end;
3141 } else {
3142 tail = original_range.start;
3143 }
3144 }
3145 SelectMode::All => {
3146 return;
3147 }
3148 };
3149
3150 if head < tail {
3151 pending.start = buffer.anchor_before(head);
3152 pending.end = buffer.anchor_before(tail);
3153 pending.reversed = true;
3154 } else {
3155 pending.start = buffer.anchor_before(tail);
3156 pending.end = buffer.anchor_before(head);
3157 pending.reversed = false;
3158 }
3159
3160 self.change_selections(None, cx, |s| {
3161 s.set_pending(pending, mode);
3162 });
3163 } else {
3164 log::error!("update_selection dispatched with no pending selection");
3165 return;
3166 }
3167
3168 self.apply_scroll_delta(scroll_delta, cx);
3169 cx.notify();
3170 }
3171
3172 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
3173 self.columnar_selection_tail.take();
3174 if self.selections.pending_anchor().is_some() {
3175 let selections = self.selections.all::<usize>(cx);
3176 self.change_selections(None, cx, |s| {
3177 s.select(selections);
3178 s.clear_pending();
3179 });
3180 }
3181 }
3182
3183 fn select_columns(
3184 &mut self,
3185 tail: DisplayPoint,
3186 head: DisplayPoint,
3187 goal_column: u32,
3188 display_map: &DisplaySnapshot,
3189 cx: &mut ViewContext<Self>,
3190 ) {
3191 let start_row = cmp::min(tail.row(), head.row());
3192 let end_row = cmp::max(tail.row(), head.row());
3193 let start_column = cmp::min(tail.column(), goal_column);
3194 let end_column = cmp::max(tail.column(), goal_column);
3195 let reversed = start_column < tail.column();
3196
3197 let selection_ranges = (start_row.0..=end_row.0)
3198 .map(DisplayRow)
3199 .filter_map(|row| {
3200 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
3201 let start = display_map
3202 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
3203 .to_point(display_map);
3204 let end = display_map
3205 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
3206 .to_point(display_map);
3207 if reversed {
3208 Some(end..start)
3209 } else {
3210 Some(start..end)
3211 }
3212 } else {
3213 None
3214 }
3215 })
3216 .collect::<Vec<_>>();
3217
3218 self.change_selections(None, cx, |s| {
3219 s.select_ranges(selection_ranges);
3220 });
3221 cx.notify();
3222 }
3223
3224 pub fn has_pending_nonempty_selection(&self) -> bool {
3225 let pending_nonempty_selection = match self.selections.pending_anchor() {
3226 Some(Selection { start, end, .. }) => start != end,
3227 None => false,
3228 };
3229
3230 pending_nonempty_selection
3231 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
3232 }
3233
3234 pub fn has_pending_selection(&self) -> bool {
3235 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
3236 }
3237
3238 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
3239 if self.clear_expanded_diff_hunks(cx) {
3240 cx.notify();
3241 return;
3242 }
3243 if self.dismiss_menus_and_popups(true, cx) {
3244 return;
3245 }
3246
3247 if self.mode == EditorMode::Full
3248 && self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel())
3249 {
3250 return;
3251 }
3252
3253 cx.propagate();
3254 }
3255
3256 pub fn dismiss_menus_and_popups(
3257 &mut self,
3258 should_report_inline_completion_event: bool,
3259 cx: &mut ViewContext<Self>,
3260 ) -> bool {
3261 if self.take_rename(false, cx).is_some() {
3262 return true;
3263 }
3264
3265 if hide_hover(self, cx) {
3266 return true;
3267 }
3268
3269 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
3270 return true;
3271 }
3272
3273 if self.hide_context_menu(cx).is_some() {
3274 return true;
3275 }
3276
3277 if self.mouse_context_menu.take().is_some() {
3278 return true;
3279 }
3280
3281 if self.discard_inline_completion(should_report_inline_completion_event, cx) {
3282 return true;
3283 }
3284
3285 if self.snippet_stack.pop().is_some() {
3286 return true;
3287 }
3288
3289 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
3290 self.dismiss_diagnostics(cx);
3291 return true;
3292 }
3293
3294 false
3295 }
3296
3297 fn linked_editing_ranges_for(
3298 &self,
3299 selection: Range<text::Anchor>,
3300 cx: &AppContext,
3301 ) -> Option<HashMap<Model<Buffer>, Vec<Range<text::Anchor>>>> {
3302 if self.linked_edit_ranges.is_empty() {
3303 return None;
3304 }
3305 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
3306 selection.end.buffer_id.and_then(|end_buffer_id| {
3307 if selection.start.buffer_id != Some(end_buffer_id) {
3308 return None;
3309 }
3310 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
3311 let snapshot = buffer.read(cx).snapshot();
3312 self.linked_edit_ranges
3313 .get(end_buffer_id, selection.start..selection.end, &snapshot)
3314 .map(|ranges| (ranges, snapshot, buffer))
3315 })?;
3316 use text::ToOffset as TO;
3317 // find offset from the start of current range to current cursor position
3318 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
3319
3320 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
3321 let start_difference = start_offset - start_byte_offset;
3322 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
3323 let end_difference = end_offset - start_byte_offset;
3324 // Current range has associated linked ranges.
3325 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3326 for range in linked_ranges.iter() {
3327 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
3328 let end_offset = start_offset + end_difference;
3329 let start_offset = start_offset + start_difference;
3330 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
3331 continue;
3332 }
3333 if self.selections.disjoint_anchor_ranges().iter().any(|s| {
3334 if s.start.buffer_id != selection.start.buffer_id
3335 || s.end.buffer_id != selection.end.buffer_id
3336 {
3337 return false;
3338 }
3339 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
3340 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
3341 }) {
3342 continue;
3343 }
3344 let start = buffer_snapshot.anchor_after(start_offset);
3345 let end = buffer_snapshot.anchor_after(end_offset);
3346 linked_edits
3347 .entry(buffer.clone())
3348 .or_default()
3349 .push(start..end);
3350 }
3351 Some(linked_edits)
3352 }
3353
3354 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3355 let text: Arc<str> = text.into();
3356
3357 if self.read_only(cx) {
3358 return;
3359 }
3360
3361 let selections = self.selections.all_adjusted(cx);
3362 let mut bracket_inserted = false;
3363 let mut edits = Vec::new();
3364 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3365 let mut new_selections = Vec::with_capacity(selections.len());
3366 let mut new_autoclose_regions = Vec::new();
3367 let snapshot = self.buffer.read(cx).read(cx);
3368
3369 for (selection, autoclose_region) in
3370 self.selections_with_autoclose_regions(selections, &snapshot)
3371 {
3372 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
3373 // Determine if the inserted text matches the opening or closing
3374 // bracket of any of this language's bracket pairs.
3375 let mut bracket_pair = None;
3376 let mut is_bracket_pair_start = false;
3377 let mut is_bracket_pair_end = false;
3378 if !text.is_empty() {
3379 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
3380 // and they are removing the character that triggered IME popup.
3381 for (pair, enabled) in scope.brackets() {
3382 if !pair.close && !pair.surround {
3383 continue;
3384 }
3385
3386 if enabled && pair.start.ends_with(text.as_ref()) {
3387 let prefix_len = pair.start.len() - text.len();
3388 let preceding_text_matches_prefix = prefix_len == 0
3389 || (selection.start.column >= (prefix_len as u32)
3390 && snapshot.contains_str_at(
3391 Point::new(
3392 selection.start.row,
3393 selection.start.column - (prefix_len as u32),
3394 ),
3395 &pair.start[..prefix_len],
3396 ));
3397 if preceding_text_matches_prefix {
3398 bracket_pair = Some(pair.clone());
3399 is_bracket_pair_start = true;
3400 break;
3401 }
3402 }
3403 if pair.end.as_str() == text.as_ref() {
3404 bracket_pair = Some(pair.clone());
3405 is_bracket_pair_end = true;
3406 break;
3407 }
3408 }
3409 }
3410
3411 if let Some(bracket_pair) = bracket_pair {
3412 let snapshot_settings = snapshot.settings_at(selection.start, cx);
3413 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
3414 let auto_surround =
3415 self.use_auto_surround && snapshot_settings.use_auto_surround;
3416 if selection.is_empty() {
3417 if is_bracket_pair_start {
3418 // If the inserted text is a suffix of an opening bracket and the
3419 // selection is preceded by the rest of the opening bracket, then
3420 // insert the closing bracket.
3421 let following_text_allows_autoclose = snapshot
3422 .chars_at(selection.start)
3423 .next()
3424 .map_or(true, |c| scope.should_autoclose_before(c));
3425
3426 let is_closing_quote = if bracket_pair.end == bracket_pair.start
3427 && bracket_pair.start.len() == 1
3428 {
3429 let target = bracket_pair.start.chars().next().unwrap();
3430 let current_line_count = snapshot
3431 .reversed_chars_at(selection.start)
3432 .take_while(|&c| c != '\n')
3433 .filter(|&c| c == target)
3434 .count();
3435 current_line_count % 2 == 1
3436 } else {
3437 false
3438 };
3439
3440 if autoclose
3441 && bracket_pair.close
3442 && following_text_allows_autoclose
3443 && !is_closing_quote
3444 {
3445 let anchor = snapshot.anchor_before(selection.end);
3446 new_selections.push((selection.map(|_| anchor), text.len()));
3447 new_autoclose_regions.push((
3448 anchor,
3449 text.len(),
3450 selection.id,
3451 bracket_pair.clone(),
3452 ));
3453 edits.push((
3454 selection.range(),
3455 format!("{}{}", text, bracket_pair.end).into(),
3456 ));
3457 bracket_inserted = true;
3458 continue;
3459 }
3460 }
3461
3462 if let Some(region) = autoclose_region {
3463 // If the selection is followed by an auto-inserted closing bracket,
3464 // then don't insert that closing bracket again; just move the selection
3465 // past the closing bracket.
3466 let should_skip = selection.end == region.range.end.to_point(&snapshot)
3467 && text.as_ref() == region.pair.end.as_str();
3468 if should_skip {
3469 let anchor = snapshot.anchor_after(selection.end);
3470 new_selections
3471 .push((selection.map(|_| anchor), region.pair.end.len()));
3472 continue;
3473 }
3474 }
3475
3476 let always_treat_brackets_as_autoclosed = snapshot
3477 .settings_at(selection.start, cx)
3478 .always_treat_brackets_as_autoclosed;
3479 if always_treat_brackets_as_autoclosed
3480 && is_bracket_pair_end
3481 && snapshot.contains_str_at(selection.end, text.as_ref())
3482 {
3483 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
3484 // and the inserted text is a closing bracket and the selection is followed
3485 // by the closing bracket then move the selection past the closing bracket.
3486 let anchor = snapshot.anchor_after(selection.end);
3487 new_selections.push((selection.map(|_| anchor), text.len()));
3488 continue;
3489 }
3490 }
3491 // If an opening bracket is 1 character long and is typed while
3492 // text is selected, then surround that text with the bracket pair.
3493 else if auto_surround
3494 && bracket_pair.surround
3495 && is_bracket_pair_start
3496 && bracket_pair.start.chars().count() == 1
3497 {
3498 edits.push((selection.start..selection.start, text.clone()));
3499 edits.push((
3500 selection.end..selection.end,
3501 bracket_pair.end.as_str().into(),
3502 ));
3503 bracket_inserted = true;
3504 new_selections.push((
3505 Selection {
3506 id: selection.id,
3507 start: snapshot.anchor_after(selection.start),
3508 end: snapshot.anchor_before(selection.end),
3509 reversed: selection.reversed,
3510 goal: selection.goal,
3511 },
3512 0,
3513 ));
3514 continue;
3515 }
3516 }
3517 }
3518
3519 if self.auto_replace_emoji_shortcode
3520 && selection.is_empty()
3521 && text.as_ref().ends_with(':')
3522 {
3523 if let Some(possible_emoji_short_code) =
3524 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
3525 {
3526 if !possible_emoji_short_code.is_empty() {
3527 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
3528 let emoji_shortcode_start = Point::new(
3529 selection.start.row,
3530 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
3531 );
3532
3533 // Remove shortcode from buffer
3534 edits.push((
3535 emoji_shortcode_start..selection.start,
3536 "".to_string().into(),
3537 ));
3538 new_selections.push((
3539 Selection {
3540 id: selection.id,
3541 start: snapshot.anchor_after(emoji_shortcode_start),
3542 end: snapshot.anchor_before(selection.start),
3543 reversed: selection.reversed,
3544 goal: selection.goal,
3545 },
3546 0,
3547 ));
3548
3549 // Insert emoji
3550 let selection_start_anchor = snapshot.anchor_after(selection.start);
3551 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3552 edits.push((selection.start..selection.end, emoji.to_string().into()));
3553
3554 continue;
3555 }
3556 }
3557 }
3558 }
3559
3560 // If not handling any auto-close operation, then just replace the selected
3561 // text with the given input and move the selection to the end of the
3562 // newly inserted text.
3563 let anchor = snapshot.anchor_after(selection.end);
3564 if !self.linked_edit_ranges.is_empty() {
3565 let start_anchor = snapshot.anchor_before(selection.start);
3566
3567 let is_word_char = text.chars().next().map_or(true, |char| {
3568 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
3569 classifier.is_word(char)
3570 });
3571
3572 if is_word_char {
3573 if let Some(ranges) = self
3574 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3575 {
3576 for (buffer, edits) in ranges {
3577 linked_edits
3578 .entry(buffer.clone())
3579 .or_default()
3580 .extend(edits.into_iter().map(|range| (range, text.clone())));
3581 }
3582 }
3583 }
3584 }
3585
3586 new_selections.push((selection.map(|_| anchor), 0));
3587 edits.push((selection.start..selection.end, text.clone()));
3588 }
3589
3590 drop(snapshot);
3591
3592 self.transact(cx, |this, cx| {
3593 this.buffer.update(cx, |buffer, cx| {
3594 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3595 });
3596 for (buffer, edits) in linked_edits {
3597 buffer.update(cx, |buffer, cx| {
3598 let snapshot = buffer.snapshot();
3599 let edits = edits
3600 .into_iter()
3601 .map(|(range, text)| {
3602 use text::ToPoint as TP;
3603 let end_point = TP::to_point(&range.end, &snapshot);
3604 let start_point = TP::to_point(&range.start, &snapshot);
3605 (start_point..end_point, text)
3606 })
3607 .sorted_by_key(|(range, _)| range.start)
3608 .collect::<Vec<_>>();
3609 buffer.edit(edits, None, cx);
3610 })
3611 }
3612 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3613 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3614 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3615 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
3616 .zip(new_selection_deltas)
3617 .map(|(selection, delta)| Selection {
3618 id: selection.id,
3619 start: selection.start + delta,
3620 end: selection.end + delta,
3621 reversed: selection.reversed,
3622 goal: SelectionGoal::None,
3623 })
3624 .collect::<Vec<_>>();
3625
3626 let mut i = 0;
3627 for (position, delta, selection_id, pair) in new_autoclose_regions {
3628 let position = position.to_offset(&map.buffer_snapshot) + delta;
3629 let start = map.buffer_snapshot.anchor_before(position);
3630 let end = map.buffer_snapshot.anchor_after(position);
3631 while let Some(existing_state) = this.autoclose_regions.get(i) {
3632 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
3633 Ordering::Less => i += 1,
3634 Ordering::Greater => break,
3635 Ordering::Equal => {
3636 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
3637 Ordering::Less => i += 1,
3638 Ordering::Equal => break,
3639 Ordering::Greater => break,
3640 }
3641 }
3642 }
3643 }
3644 this.autoclose_regions.insert(
3645 i,
3646 AutocloseRegion {
3647 selection_id,
3648 range: start..end,
3649 pair,
3650 },
3651 );
3652 }
3653
3654 let had_active_inline_completion = this.has_active_inline_completion(cx);
3655 this.change_selections_inner(Some(Autoscroll::fit()), false, cx, |s| {
3656 s.select(new_selections)
3657 });
3658
3659 if !bracket_inserted {
3660 if let Some(on_type_format_task) =
3661 this.trigger_on_type_formatting(text.to_string(), cx)
3662 {
3663 on_type_format_task.detach_and_log_err(cx);
3664 }
3665 }
3666
3667 let editor_settings = EditorSettings::get_global(cx);
3668 if bracket_inserted
3669 && (editor_settings.auto_signature_help
3670 || editor_settings.show_signature_help_after_edits)
3671 {
3672 this.show_signature_help(&ShowSignatureHelp, cx);
3673 }
3674
3675 let trigger_in_words = !had_active_inline_completion;
3676 this.trigger_completion_on_input(&text, trigger_in_words, cx);
3677 linked_editing_ranges::refresh_linked_ranges(this, cx);
3678 this.refresh_inline_completion(true, false, cx);
3679 });
3680 }
3681
3682 fn find_possible_emoji_shortcode_at_position(
3683 snapshot: &MultiBufferSnapshot,
3684 position: Point,
3685 ) -> Option<String> {
3686 let mut chars = Vec::new();
3687 let mut found_colon = false;
3688 for char in snapshot.reversed_chars_at(position).take(100) {
3689 // Found a possible emoji shortcode in the middle of the buffer
3690 if found_colon {
3691 if char.is_whitespace() {
3692 chars.reverse();
3693 return Some(chars.iter().collect());
3694 }
3695 // If the previous character is not a whitespace, we are in the middle of a word
3696 // and we only want to complete the shortcode if the word is made up of other emojis
3697 let mut containing_word = String::new();
3698 for ch in snapshot
3699 .reversed_chars_at(position)
3700 .skip(chars.len() + 1)
3701 .take(100)
3702 {
3703 if ch.is_whitespace() {
3704 break;
3705 }
3706 containing_word.push(ch);
3707 }
3708 let containing_word = containing_word.chars().rev().collect::<String>();
3709 if util::word_consists_of_emojis(containing_word.as_str()) {
3710 chars.reverse();
3711 return Some(chars.iter().collect());
3712 }
3713 }
3714
3715 if char.is_whitespace() || !char.is_ascii() {
3716 return None;
3717 }
3718 if char == ':' {
3719 found_colon = true;
3720 } else {
3721 chars.push(char);
3722 }
3723 }
3724 // Found a possible emoji shortcode at the beginning of the buffer
3725 chars.reverse();
3726 Some(chars.iter().collect())
3727 }
3728
3729 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
3730 self.transact(cx, |this, cx| {
3731 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3732 let selections = this.selections.all::<usize>(cx);
3733 let multi_buffer = this.buffer.read(cx);
3734 let buffer = multi_buffer.snapshot(cx);
3735 selections
3736 .iter()
3737 .map(|selection| {
3738 let start_point = selection.start.to_point(&buffer);
3739 let mut indent =
3740 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3741 indent.len = cmp::min(indent.len, start_point.column);
3742 let start = selection.start;
3743 let end = selection.end;
3744 let selection_is_empty = start == end;
3745 let language_scope = buffer.language_scope_at(start);
3746 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3747 &language_scope
3748 {
3749 let leading_whitespace_len = buffer
3750 .reversed_chars_at(start)
3751 .take_while(|c| c.is_whitespace() && *c != '\n')
3752 .map(|c| c.len_utf8())
3753 .sum::<usize>();
3754
3755 let trailing_whitespace_len = buffer
3756 .chars_at(end)
3757 .take_while(|c| c.is_whitespace() && *c != '\n')
3758 .map(|c| c.len_utf8())
3759 .sum::<usize>();
3760
3761 let insert_extra_newline =
3762 language.brackets().any(|(pair, enabled)| {
3763 let pair_start = pair.start.trim_end();
3764 let pair_end = pair.end.trim_start();
3765
3766 enabled
3767 && pair.newline
3768 && buffer.contains_str_at(
3769 end + trailing_whitespace_len,
3770 pair_end,
3771 )
3772 && buffer.contains_str_at(
3773 (start - leading_whitespace_len)
3774 .saturating_sub(pair_start.len()),
3775 pair_start,
3776 )
3777 });
3778
3779 // Comment extension on newline is allowed only for cursor selections
3780 let comment_delimiter = maybe!({
3781 if !selection_is_empty {
3782 return None;
3783 }
3784
3785 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
3786 return None;
3787 }
3788
3789 let delimiters = language.line_comment_prefixes();
3790 let max_len_of_delimiter =
3791 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3792 let (snapshot, range) =
3793 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3794
3795 let mut index_of_first_non_whitespace = 0;
3796 let comment_candidate = snapshot
3797 .chars_for_range(range)
3798 .skip_while(|c| {
3799 let should_skip = c.is_whitespace();
3800 if should_skip {
3801 index_of_first_non_whitespace += 1;
3802 }
3803 should_skip
3804 })
3805 .take(max_len_of_delimiter)
3806 .collect::<String>();
3807 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3808 comment_candidate.starts_with(comment_prefix.as_ref())
3809 })?;
3810 let cursor_is_placed_after_comment_marker =
3811 index_of_first_non_whitespace + comment_prefix.len()
3812 <= start_point.column as usize;
3813 if cursor_is_placed_after_comment_marker {
3814 Some(comment_prefix.clone())
3815 } else {
3816 None
3817 }
3818 });
3819 (comment_delimiter, insert_extra_newline)
3820 } else {
3821 (None, false)
3822 };
3823
3824 let capacity_for_delimiter = comment_delimiter
3825 .as_deref()
3826 .map(str::len)
3827 .unwrap_or_default();
3828 let mut new_text =
3829 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3830 new_text.push('\n');
3831 new_text.extend(indent.chars());
3832 if let Some(delimiter) = &comment_delimiter {
3833 new_text.push_str(delimiter);
3834 }
3835 if insert_extra_newline {
3836 new_text = new_text.repeat(2);
3837 }
3838
3839 let anchor = buffer.anchor_after(end);
3840 let new_selection = selection.map(|_| anchor);
3841 (
3842 (start..end, new_text),
3843 (insert_extra_newline, new_selection),
3844 )
3845 })
3846 .unzip()
3847 };
3848
3849 this.edit_with_autoindent(edits, cx);
3850 let buffer = this.buffer.read(cx).snapshot(cx);
3851 let new_selections = selection_fixup_info
3852 .into_iter()
3853 .map(|(extra_newline_inserted, new_selection)| {
3854 let mut cursor = new_selection.end.to_point(&buffer);
3855 if extra_newline_inserted {
3856 cursor.row -= 1;
3857 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3858 }
3859 new_selection.map(|_| cursor)
3860 })
3861 .collect();
3862
3863 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
3864 this.refresh_inline_completion(true, false, cx);
3865 });
3866 }
3867
3868 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
3869 let buffer = self.buffer.read(cx);
3870 let snapshot = buffer.snapshot(cx);
3871
3872 let mut edits = Vec::new();
3873 let mut rows = Vec::new();
3874
3875 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3876 let cursor = selection.head();
3877 let row = cursor.row;
3878
3879 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3880
3881 let newline = "\n".to_string();
3882 edits.push((start_of_line..start_of_line, newline));
3883
3884 rows.push(row + rows_inserted as u32);
3885 }
3886
3887 self.transact(cx, |editor, cx| {
3888 editor.edit(edits, cx);
3889
3890 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3891 let mut index = 0;
3892 s.move_cursors_with(|map, _, _| {
3893 let row = rows[index];
3894 index += 1;
3895
3896 let point = Point::new(row, 0);
3897 let boundary = map.next_line_boundary(point).1;
3898 let clipped = map.clip_point(boundary, Bias::Left);
3899
3900 (clipped, SelectionGoal::None)
3901 });
3902 });
3903
3904 let mut indent_edits = Vec::new();
3905 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3906 for row in rows {
3907 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3908 for (row, indent) in indents {
3909 if indent.len == 0 {
3910 continue;
3911 }
3912
3913 let text = match indent.kind {
3914 IndentKind::Space => " ".repeat(indent.len as usize),
3915 IndentKind::Tab => "\t".repeat(indent.len as usize),
3916 };
3917 let point = Point::new(row.0, 0);
3918 indent_edits.push((point..point, text));
3919 }
3920 }
3921 editor.edit(indent_edits, cx);
3922 });
3923 }
3924
3925 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
3926 let buffer = self.buffer.read(cx);
3927 let snapshot = buffer.snapshot(cx);
3928
3929 let mut edits = Vec::new();
3930 let mut rows = Vec::new();
3931 let mut rows_inserted = 0;
3932
3933 for selection in self.selections.all_adjusted(cx) {
3934 let cursor = selection.head();
3935 let row = cursor.row;
3936
3937 let point = Point::new(row + 1, 0);
3938 let start_of_line = snapshot.clip_point(point, Bias::Left);
3939
3940 let newline = "\n".to_string();
3941 edits.push((start_of_line..start_of_line, newline));
3942
3943 rows_inserted += 1;
3944 rows.push(row + rows_inserted);
3945 }
3946
3947 self.transact(cx, |editor, cx| {
3948 editor.edit(edits, cx);
3949
3950 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3951 let mut index = 0;
3952 s.move_cursors_with(|map, _, _| {
3953 let row = rows[index];
3954 index += 1;
3955
3956 let point = Point::new(row, 0);
3957 let boundary = map.next_line_boundary(point).1;
3958 let clipped = map.clip_point(boundary, Bias::Left);
3959
3960 (clipped, SelectionGoal::None)
3961 });
3962 });
3963
3964 let mut indent_edits = Vec::new();
3965 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3966 for row in rows {
3967 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3968 for (row, indent) in indents {
3969 if indent.len == 0 {
3970 continue;
3971 }
3972
3973 let text = match indent.kind {
3974 IndentKind::Space => " ".repeat(indent.len as usize),
3975 IndentKind::Tab => "\t".repeat(indent.len as usize),
3976 };
3977 let point = Point::new(row.0, 0);
3978 indent_edits.push((point..point, text));
3979 }
3980 }
3981 editor.edit(indent_edits, cx);
3982 });
3983 }
3984
3985 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3986 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3987 original_indent_columns: Vec::new(),
3988 });
3989 self.insert_with_autoindent_mode(text, autoindent, cx);
3990 }
3991
3992 fn insert_with_autoindent_mode(
3993 &mut self,
3994 text: &str,
3995 autoindent_mode: Option<AutoindentMode>,
3996 cx: &mut ViewContext<Self>,
3997 ) {
3998 if self.read_only(cx) {
3999 return;
4000 }
4001
4002 let text: Arc<str> = text.into();
4003 self.transact(cx, |this, cx| {
4004 let old_selections = this.selections.all_adjusted(cx);
4005 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
4006 let anchors = {
4007 let snapshot = buffer.read(cx);
4008 old_selections
4009 .iter()
4010 .map(|s| {
4011 let anchor = snapshot.anchor_after(s.head());
4012 s.map(|_| anchor)
4013 })
4014 .collect::<Vec<_>>()
4015 };
4016 buffer.edit(
4017 old_selections
4018 .iter()
4019 .map(|s| (s.start..s.end, text.clone())),
4020 autoindent_mode,
4021 cx,
4022 );
4023 anchors
4024 });
4025
4026 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4027 s.select_anchors(selection_anchors);
4028 })
4029 });
4030 }
4031
4032 fn trigger_completion_on_input(
4033 &mut self,
4034 text: &str,
4035 trigger_in_words: bool,
4036 cx: &mut ViewContext<Self>,
4037 ) {
4038 if self.is_completion_trigger(text, trigger_in_words, cx) {
4039 self.show_completions(
4040 &ShowCompletions {
4041 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
4042 },
4043 cx,
4044 );
4045 } else {
4046 self.hide_context_menu(cx);
4047 }
4048 }
4049
4050 fn is_completion_trigger(
4051 &self,
4052 text: &str,
4053 trigger_in_words: bool,
4054 cx: &mut ViewContext<Self>,
4055 ) -> bool {
4056 let position = self.selections.newest_anchor().head();
4057 let multibuffer = self.buffer.read(cx);
4058 let Some(buffer) = position
4059 .buffer_id
4060 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
4061 else {
4062 return false;
4063 };
4064
4065 if let Some(completion_provider) = &self.completion_provider {
4066 completion_provider.is_completion_trigger(
4067 &buffer,
4068 position.text_anchor,
4069 text,
4070 trigger_in_words,
4071 cx,
4072 )
4073 } else {
4074 false
4075 }
4076 }
4077
4078 /// If any empty selections is touching the start of its innermost containing autoclose
4079 /// region, expand it to select the brackets.
4080 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
4081 let selections = self.selections.all::<usize>(cx);
4082 let buffer = self.buffer.read(cx).read(cx);
4083 let new_selections = self
4084 .selections_with_autoclose_regions(selections, &buffer)
4085 .map(|(mut selection, region)| {
4086 if !selection.is_empty() {
4087 return selection;
4088 }
4089
4090 if let Some(region) = region {
4091 let mut range = region.range.to_offset(&buffer);
4092 if selection.start == range.start && range.start >= region.pair.start.len() {
4093 range.start -= region.pair.start.len();
4094 if buffer.contains_str_at(range.start, ®ion.pair.start)
4095 && buffer.contains_str_at(range.end, ®ion.pair.end)
4096 {
4097 range.end += region.pair.end.len();
4098 selection.start = range.start;
4099 selection.end = range.end;
4100
4101 return selection;
4102 }
4103 }
4104 }
4105
4106 let always_treat_brackets_as_autoclosed = buffer
4107 .settings_at(selection.start, cx)
4108 .always_treat_brackets_as_autoclosed;
4109
4110 if !always_treat_brackets_as_autoclosed {
4111 return selection;
4112 }
4113
4114 if let Some(scope) = buffer.language_scope_at(selection.start) {
4115 for (pair, enabled) in scope.brackets() {
4116 if !enabled || !pair.close {
4117 continue;
4118 }
4119
4120 if buffer.contains_str_at(selection.start, &pair.end) {
4121 let pair_start_len = pair.start.len();
4122 if buffer.contains_str_at(
4123 selection.start.saturating_sub(pair_start_len),
4124 &pair.start,
4125 ) {
4126 selection.start -= pair_start_len;
4127 selection.end += pair.end.len();
4128
4129 return selection;
4130 }
4131 }
4132 }
4133 }
4134
4135 selection
4136 })
4137 .collect();
4138
4139 drop(buffer);
4140 self.change_selections(None, cx, |selections| selections.select(new_selections));
4141 }
4142
4143 /// Iterate the given selections, and for each one, find the smallest surrounding
4144 /// autoclose region. This uses the ordering of the selections and the autoclose
4145 /// regions to avoid repeated comparisons.
4146 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
4147 &'a self,
4148 selections: impl IntoIterator<Item = Selection<D>>,
4149 buffer: &'a MultiBufferSnapshot,
4150 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
4151 let mut i = 0;
4152 let mut regions = self.autoclose_regions.as_slice();
4153 selections.into_iter().map(move |selection| {
4154 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
4155
4156 let mut enclosing = None;
4157 while let Some(pair_state) = regions.get(i) {
4158 if pair_state.range.end.to_offset(buffer) < range.start {
4159 regions = ®ions[i + 1..];
4160 i = 0;
4161 } else if pair_state.range.start.to_offset(buffer) > range.end {
4162 break;
4163 } else {
4164 if pair_state.selection_id == selection.id {
4165 enclosing = Some(pair_state);
4166 }
4167 i += 1;
4168 }
4169 }
4170
4171 (selection, enclosing)
4172 })
4173 }
4174
4175 /// Remove any autoclose regions that no longer contain their selection.
4176 fn invalidate_autoclose_regions(
4177 &mut self,
4178 mut selections: &[Selection<Anchor>],
4179 buffer: &MultiBufferSnapshot,
4180 ) {
4181 self.autoclose_regions.retain(|state| {
4182 let mut i = 0;
4183 while let Some(selection) = selections.get(i) {
4184 if selection.end.cmp(&state.range.start, buffer).is_lt() {
4185 selections = &selections[1..];
4186 continue;
4187 }
4188 if selection.start.cmp(&state.range.end, buffer).is_gt() {
4189 break;
4190 }
4191 if selection.id == state.selection_id {
4192 return true;
4193 } else {
4194 i += 1;
4195 }
4196 }
4197 false
4198 });
4199 }
4200
4201 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
4202 let offset = position.to_offset(buffer);
4203 let (word_range, kind) = buffer.surrounding_word(offset, true);
4204 if offset > word_range.start && kind == Some(CharKind::Word) {
4205 Some(
4206 buffer
4207 .text_for_range(word_range.start..offset)
4208 .collect::<String>(),
4209 )
4210 } else {
4211 None
4212 }
4213 }
4214
4215 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
4216 self.refresh_inlay_hints(
4217 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
4218 cx,
4219 );
4220 }
4221
4222 pub fn inlay_hints_enabled(&self) -> bool {
4223 self.inlay_hint_cache.enabled
4224 }
4225
4226 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
4227 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
4228 return;
4229 }
4230
4231 let reason_description = reason.description();
4232 let ignore_debounce = matches!(
4233 reason,
4234 InlayHintRefreshReason::SettingsChange(_)
4235 | InlayHintRefreshReason::Toggle(_)
4236 | InlayHintRefreshReason::ExcerptsRemoved(_)
4237 );
4238 let (invalidate_cache, required_languages) = match reason {
4239 InlayHintRefreshReason::Toggle(enabled) => {
4240 self.inlay_hint_cache.enabled = enabled;
4241 if enabled {
4242 (InvalidationStrategy::RefreshRequested, None)
4243 } else {
4244 self.inlay_hint_cache.clear();
4245 self.splice_inlays(
4246 self.visible_inlay_hints(cx)
4247 .iter()
4248 .map(|inlay| inlay.id)
4249 .collect(),
4250 Vec::new(),
4251 cx,
4252 );
4253 return;
4254 }
4255 }
4256 InlayHintRefreshReason::SettingsChange(new_settings) => {
4257 match self.inlay_hint_cache.update_settings(
4258 &self.buffer,
4259 new_settings,
4260 self.visible_inlay_hints(cx),
4261 cx,
4262 ) {
4263 ControlFlow::Break(Some(InlaySplice {
4264 to_remove,
4265 to_insert,
4266 })) => {
4267 self.splice_inlays(to_remove, to_insert, cx);
4268 return;
4269 }
4270 ControlFlow::Break(None) => return,
4271 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
4272 }
4273 }
4274 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
4275 if let Some(InlaySplice {
4276 to_remove,
4277 to_insert,
4278 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
4279 {
4280 self.splice_inlays(to_remove, to_insert, cx);
4281 }
4282 return;
4283 }
4284 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
4285 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
4286 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
4287 }
4288 InlayHintRefreshReason::RefreshRequested => {
4289 (InvalidationStrategy::RefreshRequested, None)
4290 }
4291 };
4292
4293 if let Some(InlaySplice {
4294 to_remove,
4295 to_insert,
4296 }) = self.inlay_hint_cache.spawn_hint_refresh(
4297 reason_description,
4298 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
4299 invalidate_cache,
4300 ignore_debounce,
4301 cx,
4302 ) {
4303 self.splice_inlays(to_remove, to_insert, cx);
4304 }
4305 }
4306
4307 fn visible_inlay_hints(&self, cx: &ViewContext<'_, Editor>) -> Vec<Inlay> {
4308 self.display_map
4309 .read(cx)
4310 .current_inlays()
4311 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
4312 .cloned()
4313 .collect()
4314 }
4315
4316 pub fn excerpts_for_inlay_hints_query(
4317 &self,
4318 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
4319 cx: &mut ViewContext<Editor>,
4320 ) -> HashMap<ExcerptId, (Model<Buffer>, clock::Global, Range<usize>)> {
4321 let Some(project) = self.project.as_ref() else {
4322 return HashMap::default();
4323 };
4324 let project = project.read(cx);
4325 let multi_buffer = self.buffer().read(cx);
4326 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
4327 let multi_buffer_visible_start = self
4328 .scroll_manager
4329 .anchor()
4330 .anchor
4331 .to_point(&multi_buffer_snapshot);
4332 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
4333 multi_buffer_visible_start
4334 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
4335 Bias::Left,
4336 );
4337 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
4338 multi_buffer
4339 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
4340 .into_iter()
4341 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
4342 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
4343 let buffer = buffer_handle.read(cx);
4344 let buffer_file = project::File::from_dyn(buffer.file())?;
4345 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
4346 let worktree_entry = buffer_worktree
4347 .read(cx)
4348 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
4349 if worktree_entry.is_ignored {
4350 return None;
4351 }
4352
4353 let language = buffer.language()?;
4354 if let Some(restrict_to_languages) = restrict_to_languages {
4355 if !restrict_to_languages.contains(language) {
4356 return None;
4357 }
4358 }
4359 Some((
4360 excerpt_id,
4361 (
4362 buffer_handle,
4363 buffer.version().clone(),
4364 excerpt_visible_range,
4365 ),
4366 ))
4367 })
4368 .collect()
4369 }
4370
4371 pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
4372 TextLayoutDetails {
4373 text_system: cx.text_system().clone(),
4374 editor_style: self.style.clone().unwrap(),
4375 rem_size: cx.rem_size(),
4376 scroll_anchor: self.scroll_manager.anchor(),
4377 visible_rows: self.visible_line_count(),
4378 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
4379 }
4380 }
4381
4382 fn splice_inlays(
4383 &self,
4384 to_remove: Vec<InlayId>,
4385 to_insert: Vec<Inlay>,
4386 cx: &mut ViewContext<Self>,
4387 ) {
4388 self.display_map.update(cx, |display_map, cx| {
4389 display_map.splice_inlays(to_remove, to_insert, cx);
4390 });
4391 cx.notify();
4392 }
4393
4394 fn trigger_on_type_formatting(
4395 &self,
4396 input: String,
4397 cx: &mut ViewContext<Self>,
4398 ) -> Option<Task<Result<()>>> {
4399 if input.len() != 1 {
4400 return None;
4401 }
4402
4403 let project = self.project.as_ref()?;
4404 let position = self.selections.newest_anchor().head();
4405 let (buffer, buffer_position) = self
4406 .buffer
4407 .read(cx)
4408 .text_anchor_for_position(position, cx)?;
4409
4410 let settings = language_settings::language_settings(
4411 buffer
4412 .read(cx)
4413 .language_at(buffer_position)
4414 .map(|l| l.name()),
4415 buffer.read(cx).file(),
4416 cx,
4417 );
4418 if !settings.use_on_type_format {
4419 return None;
4420 }
4421
4422 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
4423 // hence we do LSP request & edit on host side only — add formats to host's history.
4424 let push_to_lsp_host_history = true;
4425 // If this is not the host, append its history with new edits.
4426 let push_to_client_history = project.read(cx).is_via_collab();
4427
4428 let on_type_formatting = project.update(cx, |project, cx| {
4429 project.on_type_format(
4430 buffer.clone(),
4431 buffer_position,
4432 input,
4433 push_to_lsp_host_history,
4434 cx,
4435 )
4436 });
4437 Some(cx.spawn(|editor, mut cx| async move {
4438 if let Some(transaction) = on_type_formatting.await? {
4439 if push_to_client_history {
4440 buffer
4441 .update(&mut cx, |buffer, _| {
4442 buffer.push_transaction(transaction, Instant::now());
4443 })
4444 .ok();
4445 }
4446 editor.update(&mut cx, |editor, cx| {
4447 editor.refresh_document_highlights(cx);
4448 })?;
4449 }
4450 Ok(())
4451 }))
4452 }
4453
4454 pub fn show_completions(&mut self, options: &ShowCompletions, cx: &mut ViewContext<Self>) {
4455 if self.pending_rename.is_some() {
4456 return;
4457 }
4458
4459 let Some(provider) = self.completion_provider.as_ref() else {
4460 return;
4461 };
4462
4463 if !self.snippet_stack.is_empty() && self.context_menu.read().as_ref().is_some() {
4464 return;
4465 }
4466
4467 let position = self.selections.newest_anchor().head();
4468 let (buffer, buffer_position) =
4469 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
4470 output
4471 } else {
4472 return;
4473 };
4474
4475 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
4476 let is_followup_invoke = {
4477 let context_menu_state = self.context_menu.read();
4478 matches!(
4479 context_menu_state.deref(),
4480 Some(ContextMenu::Completions(_))
4481 )
4482 };
4483 let trigger_kind = match (&options.trigger, is_followup_invoke) {
4484 (_, true) => CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS,
4485 (Some(trigger), _) if buffer.read(cx).completion_triggers().contains(trigger) => {
4486 CompletionTriggerKind::TRIGGER_CHARACTER
4487 }
4488
4489 _ => CompletionTriggerKind::INVOKED,
4490 };
4491 let completion_context = CompletionContext {
4492 trigger_character: options.trigger.as_ref().and_then(|trigger| {
4493 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
4494 Some(String::from(trigger))
4495 } else {
4496 None
4497 }
4498 }),
4499 trigger_kind,
4500 };
4501 let completions = provider.completions(&buffer, buffer_position, completion_context, cx);
4502 let sort_completions = provider.sort_completions();
4503
4504 let id = post_inc(&mut self.next_completion_id);
4505 let task = cx.spawn(|editor, mut cx| {
4506 async move {
4507 editor.update(&mut cx, |this, _| {
4508 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
4509 })?;
4510 let completions = completions.await.log_err();
4511 let menu = if let Some(completions) = completions {
4512 let mut menu = CompletionsMenu::new(
4513 id,
4514 sort_completions,
4515 position,
4516 buffer.clone(),
4517 completions.into(),
4518 );
4519 menu.filter(query.as_deref(), cx.background_executor().clone())
4520 .await;
4521
4522 if menu.matches.is_empty() {
4523 None
4524 } else {
4525 Some(menu)
4526 }
4527 } else {
4528 None
4529 };
4530
4531 editor.update(&mut cx, |editor, cx| {
4532 let mut context_menu = editor.context_menu.write();
4533 match context_menu.as_ref() {
4534 None => {}
4535
4536 Some(ContextMenu::Completions(prev_menu)) => {
4537 if prev_menu.id > id {
4538 return;
4539 }
4540 }
4541
4542 _ => return,
4543 }
4544
4545 if editor.focus_handle.is_focused(cx) && menu.is_some() {
4546 let mut menu = menu.unwrap();
4547 menu.resolve_selected_completion(editor.completion_provider.as_deref(), cx);
4548 *context_menu = Some(ContextMenu::Completions(menu));
4549 drop(context_menu);
4550 editor.discard_inline_completion(false, cx);
4551 cx.notify();
4552 } else if editor.completion_tasks.len() <= 1 {
4553 // If there are no more completion tasks and the last menu was
4554 // empty, we should hide it. If it was already hidden, we should
4555 // also show the copilot completion when available.
4556 drop(context_menu);
4557 if editor.hide_context_menu(cx).is_none() {
4558 editor.update_visible_inline_completion(cx);
4559 }
4560 }
4561 })?;
4562
4563 Ok::<_, anyhow::Error>(())
4564 }
4565 .log_err()
4566 });
4567
4568 self.completion_tasks.push((id, task));
4569 }
4570
4571 pub fn confirm_completion(
4572 &mut self,
4573 action: &ConfirmCompletion,
4574 cx: &mut ViewContext<Self>,
4575 ) -> Option<Task<Result<()>>> {
4576 self.do_completion(action.item_ix, CompletionIntent::Complete, cx)
4577 }
4578
4579 pub fn compose_completion(
4580 &mut self,
4581 action: &ComposeCompletion,
4582 cx: &mut ViewContext<Self>,
4583 ) -> Option<Task<Result<()>>> {
4584 self.do_completion(action.item_ix, CompletionIntent::Compose, cx)
4585 }
4586
4587 fn do_completion(
4588 &mut self,
4589 item_ix: Option<usize>,
4590 intent: CompletionIntent,
4591 cx: &mut ViewContext<Editor>,
4592 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
4593 use language::ToOffset as _;
4594
4595 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
4596 menu
4597 } else {
4598 return None;
4599 };
4600
4601 let mat = completions_menu
4602 .matches
4603 .get(item_ix.unwrap_or(completions_menu.selected_item))?;
4604 let buffer_handle = completions_menu.buffer;
4605 let completions = completions_menu.completions.read();
4606 let completion = completions.get(mat.candidate_id)?;
4607 cx.stop_propagation();
4608
4609 let snippet;
4610 let text;
4611
4612 if completion.is_snippet() {
4613 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4614 text = snippet.as_ref().unwrap().text.clone();
4615 } else {
4616 snippet = None;
4617 text = completion.new_text.clone();
4618 };
4619 let selections = self.selections.all::<usize>(cx);
4620 let buffer = buffer_handle.read(cx);
4621 let old_range = completion.old_range.to_offset(buffer);
4622 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4623
4624 let newest_selection = self.selections.newest_anchor();
4625 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4626 return None;
4627 }
4628
4629 let lookbehind = newest_selection
4630 .start
4631 .text_anchor
4632 .to_offset(buffer)
4633 .saturating_sub(old_range.start);
4634 let lookahead = old_range
4635 .end
4636 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4637 let mut common_prefix_len = old_text
4638 .bytes()
4639 .zip(text.bytes())
4640 .take_while(|(a, b)| a == b)
4641 .count();
4642
4643 let snapshot = self.buffer.read(cx).snapshot(cx);
4644 let mut range_to_replace: Option<Range<isize>> = None;
4645 let mut ranges = Vec::new();
4646 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4647 for selection in &selections {
4648 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4649 let start = selection.start.saturating_sub(lookbehind);
4650 let end = selection.end + lookahead;
4651 if selection.id == newest_selection.id {
4652 range_to_replace = Some(
4653 ((start + common_prefix_len) as isize - selection.start as isize)
4654 ..(end as isize - selection.start as isize),
4655 );
4656 }
4657 ranges.push(start + common_prefix_len..end);
4658 } else {
4659 common_prefix_len = 0;
4660 ranges.clear();
4661 ranges.extend(selections.iter().map(|s| {
4662 if s.id == newest_selection.id {
4663 range_to_replace = Some(
4664 old_range.start.to_offset_utf16(&snapshot).0 as isize
4665 - selection.start as isize
4666 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4667 - selection.start as isize,
4668 );
4669 old_range.clone()
4670 } else {
4671 s.start..s.end
4672 }
4673 }));
4674 break;
4675 }
4676 if !self.linked_edit_ranges.is_empty() {
4677 let start_anchor = snapshot.anchor_before(selection.head());
4678 let end_anchor = snapshot.anchor_after(selection.tail());
4679 if let Some(ranges) = self
4680 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4681 {
4682 for (buffer, edits) in ranges {
4683 linked_edits.entry(buffer.clone()).or_default().extend(
4684 edits
4685 .into_iter()
4686 .map(|range| (range, text[common_prefix_len..].to_owned())),
4687 );
4688 }
4689 }
4690 }
4691 }
4692 let text = &text[common_prefix_len..];
4693
4694 cx.emit(EditorEvent::InputHandled {
4695 utf16_range_to_replace: range_to_replace,
4696 text: text.into(),
4697 });
4698
4699 self.transact(cx, |this, cx| {
4700 if let Some(mut snippet) = snippet {
4701 snippet.text = text.to_string();
4702 for tabstop in snippet
4703 .tabstops
4704 .iter_mut()
4705 .flat_map(|tabstop| tabstop.ranges.iter_mut())
4706 {
4707 tabstop.start -= common_prefix_len as isize;
4708 tabstop.end -= common_prefix_len as isize;
4709 }
4710
4711 this.insert_snippet(&ranges, snippet, cx).log_err();
4712 } else {
4713 this.buffer.update(cx, |buffer, cx| {
4714 buffer.edit(
4715 ranges.iter().map(|range| (range.clone(), text)),
4716 this.autoindent_mode.clone(),
4717 cx,
4718 );
4719 });
4720 }
4721 for (buffer, edits) in linked_edits {
4722 buffer.update(cx, |buffer, cx| {
4723 let snapshot = buffer.snapshot();
4724 let edits = edits
4725 .into_iter()
4726 .map(|(range, text)| {
4727 use text::ToPoint as TP;
4728 let end_point = TP::to_point(&range.end, &snapshot);
4729 let start_point = TP::to_point(&range.start, &snapshot);
4730 (start_point..end_point, text)
4731 })
4732 .sorted_by_key(|(range, _)| range.start)
4733 .collect::<Vec<_>>();
4734 buffer.edit(edits, None, cx);
4735 })
4736 }
4737
4738 this.refresh_inline_completion(true, false, cx);
4739 });
4740
4741 let show_new_completions_on_confirm = completion
4742 .confirm
4743 .as_ref()
4744 .map_or(false, |confirm| confirm(intent, cx));
4745 if show_new_completions_on_confirm {
4746 self.show_completions(&ShowCompletions { trigger: None }, cx);
4747 }
4748
4749 let provider = self.completion_provider.as_ref()?;
4750 let apply_edits = provider.apply_additional_edits_for_completion(
4751 buffer_handle,
4752 completion.clone(),
4753 true,
4754 cx,
4755 );
4756
4757 let editor_settings = EditorSettings::get_global(cx);
4758 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4759 // After the code completion is finished, users often want to know what signatures are needed.
4760 // so we should automatically call signature_help
4761 self.show_signature_help(&ShowSignatureHelp, cx);
4762 }
4763
4764 Some(cx.foreground_executor().spawn(async move {
4765 apply_edits.await?;
4766 Ok(())
4767 }))
4768 }
4769
4770 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
4771 let mut context_menu = self.context_menu.write();
4772 if let Some(ContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4773 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4774 // Toggle if we're selecting the same one
4775 *context_menu = None;
4776 cx.notify();
4777 return;
4778 } else {
4779 // Otherwise, clear it and start a new one
4780 *context_menu = None;
4781 cx.notify();
4782 }
4783 }
4784 drop(context_menu);
4785 let snapshot = self.snapshot(cx);
4786 let deployed_from_indicator = action.deployed_from_indicator;
4787 let mut task = self.code_actions_task.take();
4788 let action = action.clone();
4789 cx.spawn(|editor, mut cx| async move {
4790 while let Some(prev_task) = task {
4791 prev_task.await.log_err();
4792 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4793 }
4794
4795 let spawned_test_task = editor.update(&mut cx, |editor, cx| {
4796 if editor.focus_handle.is_focused(cx) {
4797 let multibuffer_point = action
4798 .deployed_from_indicator
4799 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4800 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4801 let (buffer, buffer_row) = snapshot
4802 .buffer_snapshot
4803 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4804 .and_then(|(buffer_snapshot, range)| {
4805 editor
4806 .buffer
4807 .read(cx)
4808 .buffer(buffer_snapshot.remote_id())
4809 .map(|buffer| (buffer, range.start.row))
4810 })?;
4811 let (_, code_actions) = editor
4812 .available_code_actions
4813 .clone()
4814 .and_then(|(location, code_actions)| {
4815 let snapshot = location.buffer.read(cx).snapshot();
4816 let point_range = location.range.to_point(&snapshot);
4817 let point_range = point_range.start.row..=point_range.end.row;
4818 if point_range.contains(&buffer_row) {
4819 Some((location, code_actions))
4820 } else {
4821 None
4822 }
4823 })
4824 .unzip();
4825 let buffer_id = buffer.read(cx).remote_id();
4826 let tasks = editor
4827 .tasks
4828 .get(&(buffer_id, buffer_row))
4829 .map(|t| Arc::new(t.to_owned()));
4830 if tasks.is_none() && code_actions.is_none() {
4831 return None;
4832 }
4833
4834 editor.completion_tasks.clear();
4835 editor.discard_inline_completion(false, cx);
4836 let task_context =
4837 tasks
4838 .as_ref()
4839 .zip(editor.project.clone())
4840 .map(|(tasks, project)| {
4841 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4842 });
4843
4844 Some(cx.spawn(|editor, mut cx| async move {
4845 let task_context = match task_context {
4846 Some(task_context) => task_context.await,
4847 None => None,
4848 };
4849 let resolved_tasks =
4850 tasks.zip(task_context).map(|(tasks, task_context)| {
4851 Arc::new(ResolvedTasks {
4852 templates: tasks.resolve(&task_context).collect(),
4853 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4854 multibuffer_point.row,
4855 tasks.column,
4856 )),
4857 })
4858 });
4859 let spawn_straight_away = resolved_tasks
4860 .as_ref()
4861 .map_or(false, |tasks| tasks.templates.len() == 1)
4862 && code_actions
4863 .as_ref()
4864 .map_or(true, |actions| actions.is_empty());
4865 if let Ok(task) = editor.update(&mut cx, |editor, cx| {
4866 *editor.context_menu.write() =
4867 Some(ContextMenu::CodeActions(CodeActionsMenu {
4868 buffer,
4869 actions: CodeActionContents {
4870 tasks: resolved_tasks,
4871 actions: code_actions,
4872 },
4873 selected_item: Default::default(),
4874 scroll_handle: UniformListScrollHandle::default(),
4875 deployed_from_indicator,
4876 }));
4877 if spawn_straight_away {
4878 if let Some(task) = editor.confirm_code_action(
4879 &ConfirmCodeAction { item_ix: Some(0) },
4880 cx,
4881 ) {
4882 cx.notify();
4883 return task;
4884 }
4885 }
4886 cx.notify();
4887 Task::ready(Ok(()))
4888 }) {
4889 task.await
4890 } else {
4891 Ok(())
4892 }
4893 }))
4894 } else {
4895 Some(Task::ready(Ok(())))
4896 }
4897 })?;
4898 if let Some(task) = spawned_test_task {
4899 task.await?;
4900 }
4901
4902 Ok::<_, anyhow::Error>(())
4903 })
4904 .detach_and_log_err(cx);
4905 }
4906
4907 pub fn confirm_code_action(
4908 &mut self,
4909 action: &ConfirmCodeAction,
4910 cx: &mut ViewContext<Self>,
4911 ) -> Option<Task<Result<()>>> {
4912 let actions_menu = if let ContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
4913 menu
4914 } else {
4915 return None;
4916 };
4917 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4918 let action = actions_menu.actions.get(action_ix)?;
4919 let title = action.label();
4920 let buffer = actions_menu.buffer;
4921 let workspace = self.workspace()?;
4922
4923 match action {
4924 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4925 workspace.update(cx, |workspace, cx| {
4926 workspace::tasks::schedule_resolved_task(
4927 workspace,
4928 task_source_kind,
4929 resolved_task,
4930 false,
4931 cx,
4932 );
4933
4934 Some(Task::ready(Ok(())))
4935 })
4936 }
4937 CodeActionsItem::CodeAction {
4938 excerpt_id,
4939 action,
4940 provider,
4941 } => {
4942 let apply_code_action =
4943 provider.apply_code_action(buffer, action, excerpt_id, true, cx);
4944 let workspace = workspace.downgrade();
4945 Some(cx.spawn(|editor, cx| async move {
4946 let project_transaction = apply_code_action.await?;
4947 Self::open_project_transaction(
4948 &editor,
4949 workspace,
4950 project_transaction,
4951 title,
4952 cx,
4953 )
4954 .await
4955 }))
4956 }
4957 }
4958 }
4959
4960 pub async fn open_project_transaction(
4961 this: &WeakView<Editor>,
4962 workspace: WeakView<Workspace>,
4963 transaction: ProjectTransaction,
4964 title: String,
4965 mut cx: AsyncWindowContext,
4966 ) -> Result<()> {
4967 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4968 cx.update(|cx| {
4969 entries.sort_unstable_by_key(|(buffer, _)| {
4970 buffer.read(cx).file().map(|f| f.path().clone())
4971 });
4972 })?;
4973
4974 // If the project transaction's edits are all contained within this editor, then
4975 // avoid opening a new editor to display them.
4976
4977 if let Some((buffer, transaction)) = entries.first() {
4978 if entries.len() == 1 {
4979 let excerpt = this.update(&mut cx, |editor, cx| {
4980 editor
4981 .buffer()
4982 .read(cx)
4983 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4984 })?;
4985 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4986 if excerpted_buffer == *buffer {
4987 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4988 let excerpt_range = excerpt_range.to_offset(buffer);
4989 buffer
4990 .edited_ranges_for_transaction::<usize>(transaction)
4991 .all(|range| {
4992 excerpt_range.start <= range.start
4993 && excerpt_range.end >= range.end
4994 })
4995 })?;
4996
4997 if all_edits_within_excerpt {
4998 return Ok(());
4999 }
5000 }
5001 }
5002 }
5003 } else {
5004 return Ok(());
5005 }
5006
5007 let mut ranges_to_highlight = Vec::new();
5008 let excerpt_buffer = cx.new_model(|cx| {
5009 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
5010 for (buffer_handle, transaction) in &entries {
5011 let buffer = buffer_handle.read(cx);
5012 ranges_to_highlight.extend(
5013 multibuffer.push_excerpts_with_context_lines(
5014 buffer_handle.clone(),
5015 buffer
5016 .edited_ranges_for_transaction::<usize>(transaction)
5017 .collect(),
5018 DEFAULT_MULTIBUFFER_CONTEXT,
5019 cx,
5020 ),
5021 );
5022 }
5023 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
5024 multibuffer
5025 })?;
5026
5027 workspace.update(&mut cx, |workspace, cx| {
5028 let project = workspace.project().clone();
5029 let editor =
5030 cx.new_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, cx));
5031 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
5032 editor.update(cx, |editor, cx| {
5033 editor.highlight_background::<Self>(
5034 &ranges_to_highlight,
5035 |theme| theme.editor_highlighted_line_background,
5036 cx,
5037 );
5038 });
5039 })?;
5040
5041 Ok(())
5042 }
5043
5044 pub fn clear_code_action_providers(&mut self) {
5045 self.code_action_providers.clear();
5046 self.available_code_actions.take();
5047 }
5048
5049 pub fn push_code_action_provider(
5050 &mut self,
5051 provider: Arc<dyn CodeActionProvider>,
5052 cx: &mut ViewContext<Self>,
5053 ) {
5054 self.code_action_providers.push(provider);
5055 self.refresh_code_actions(cx);
5056 }
5057
5058 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
5059 let buffer = self.buffer.read(cx);
5060 let newest_selection = self.selections.newest_anchor().clone();
5061 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
5062 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
5063 if start_buffer != end_buffer {
5064 return None;
5065 }
5066
5067 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
5068 cx.background_executor()
5069 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
5070 .await;
5071
5072 let (providers, tasks) = this.update(&mut cx, |this, cx| {
5073 let providers = this.code_action_providers.clone();
5074 let tasks = this
5075 .code_action_providers
5076 .iter()
5077 .map(|provider| provider.code_actions(&start_buffer, start..end, cx))
5078 .collect::<Vec<_>>();
5079 (providers, tasks)
5080 })?;
5081
5082 let mut actions = Vec::new();
5083 for (provider, provider_actions) in
5084 providers.into_iter().zip(future::join_all(tasks).await)
5085 {
5086 if let Some(provider_actions) = provider_actions.log_err() {
5087 actions.extend(provider_actions.into_iter().map(|action| {
5088 AvailableCodeAction {
5089 excerpt_id: newest_selection.start.excerpt_id,
5090 action,
5091 provider: provider.clone(),
5092 }
5093 }));
5094 }
5095 }
5096
5097 this.update(&mut cx, |this, cx| {
5098 this.available_code_actions = if actions.is_empty() {
5099 None
5100 } else {
5101 Some((
5102 Location {
5103 buffer: start_buffer,
5104 range: start..end,
5105 },
5106 actions.into(),
5107 ))
5108 };
5109 cx.notify();
5110 })
5111 }));
5112 None
5113 }
5114
5115 fn start_inline_blame_timer(&mut self, cx: &mut ViewContext<Self>) {
5116 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
5117 self.show_git_blame_inline = false;
5118
5119 self.show_git_blame_inline_delay_task = Some(cx.spawn(|this, mut cx| async move {
5120 cx.background_executor().timer(delay).await;
5121
5122 this.update(&mut cx, |this, cx| {
5123 this.show_git_blame_inline = true;
5124 cx.notify();
5125 })
5126 .log_err();
5127 }));
5128 }
5129 }
5130
5131 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
5132 if self.pending_rename.is_some() {
5133 return None;
5134 }
5135
5136 let provider = self.semantics_provider.clone()?;
5137 let buffer = self.buffer.read(cx);
5138 let newest_selection = self.selections.newest_anchor().clone();
5139 let cursor_position = newest_selection.head();
5140 let (cursor_buffer, cursor_buffer_position) =
5141 buffer.text_anchor_for_position(cursor_position, cx)?;
5142 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
5143 if cursor_buffer != tail_buffer {
5144 return None;
5145 }
5146
5147 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
5148 cx.background_executor()
5149 .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT)
5150 .await;
5151
5152 let highlights = if let Some(highlights) = cx
5153 .update(|cx| {
5154 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
5155 })
5156 .ok()
5157 .flatten()
5158 {
5159 highlights.await.log_err()
5160 } else {
5161 None
5162 };
5163
5164 if let Some(highlights) = highlights {
5165 this.update(&mut cx, |this, cx| {
5166 if this.pending_rename.is_some() {
5167 return;
5168 }
5169
5170 let buffer_id = cursor_position.buffer_id;
5171 let buffer = this.buffer.read(cx);
5172 if !buffer
5173 .text_anchor_for_position(cursor_position, cx)
5174 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
5175 {
5176 return;
5177 }
5178
5179 let cursor_buffer_snapshot = cursor_buffer.read(cx);
5180 let mut write_ranges = Vec::new();
5181 let mut read_ranges = Vec::new();
5182 for highlight in highlights {
5183 for (excerpt_id, excerpt_range) in
5184 buffer.excerpts_for_buffer(&cursor_buffer, cx)
5185 {
5186 let start = highlight
5187 .range
5188 .start
5189 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
5190 let end = highlight
5191 .range
5192 .end
5193 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
5194 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
5195 continue;
5196 }
5197
5198 let range = Anchor {
5199 buffer_id,
5200 excerpt_id,
5201 text_anchor: start,
5202 }..Anchor {
5203 buffer_id,
5204 excerpt_id,
5205 text_anchor: end,
5206 };
5207 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
5208 write_ranges.push(range);
5209 } else {
5210 read_ranges.push(range);
5211 }
5212 }
5213 }
5214
5215 this.highlight_background::<DocumentHighlightRead>(
5216 &read_ranges,
5217 |theme| theme.editor_document_highlight_read_background,
5218 cx,
5219 );
5220 this.highlight_background::<DocumentHighlightWrite>(
5221 &write_ranges,
5222 |theme| theme.editor_document_highlight_write_background,
5223 cx,
5224 );
5225 cx.notify();
5226 })
5227 .log_err();
5228 }
5229 }));
5230 None
5231 }
5232
5233 pub fn refresh_inline_completion(
5234 &mut self,
5235 debounce: bool,
5236 user_requested: bool,
5237 cx: &mut ViewContext<Self>,
5238 ) -> Option<()> {
5239 let provider = self.inline_completion_provider()?;
5240 let cursor = self.selections.newest_anchor().head();
5241 let (buffer, cursor_buffer_position) =
5242 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5243
5244 if !user_requested
5245 && (!self.enable_inline_completions
5246 || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx))
5247 {
5248 self.discard_inline_completion(false, cx);
5249 return None;
5250 }
5251
5252 self.update_visible_inline_completion(cx);
5253 provider.refresh(buffer, cursor_buffer_position, debounce, cx);
5254 Some(())
5255 }
5256
5257 fn cycle_inline_completion(
5258 &mut self,
5259 direction: Direction,
5260 cx: &mut ViewContext<Self>,
5261 ) -> Option<()> {
5262 let provider = self.inline_completion_provider()?;
5263 let cursor = self.selections.newest_anchor().head();
5264 let (buffer, cursor_buffer_position) =
5265 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5266 if !self.enable_inline_completions
5267 || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
5268 {
5269 return None;
5270 }
5271
5272 provider.cycle(buffer, cursor_buffer_position, direction, cx);
5273 self.update_visible_inline_completion(cx);
5274
5275 Some(())
5276 }
5277
5278 pub fn show_inline_completion(&mut self, _: &ShowInlineCompletion, cx: &mut ViewContext<Self>) {
5279 if !self.has_active_inline_completion(cx) {
5280 self.refresh_inline_completion(false, true, cx);
5281 return;
5282 }
5283
5284 self.update_visible_inline_completion(cx);
5285 }
5286
5287 pub fn display_cursor_names(&mut self, _: &DisplayCursorNames, cx: &mut ViewContext<Self>) {
5288 self.show_cursor_names(cx);
5289 }
5290
5291 fn show_cursor_names(&mut self, cx: &mut ViewContext<Self>) {
5292 self.show_cursor_names = true;
5293 cx.notify();
5294 cx.spawn(|this, mut cx| async move {
5295 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5296 this.update(&mut cx, |this, cx| {
5297 this.show_cursor_names = false;
5298 cx.notify()
5299 })
5300 .ok()
5301 })
5302 .detach();
5303 }
5304
5305 pub fn next_inline_completion(&mut self, _: &NextInlineCompletion, cx: &mut ViewContext<Self>) {
5306 if self.has_active_inline_completion(cx) {
5307 self.cycle_inline_completion(Direction::Next, cx);
5308 } else {
5309 let is_copilot_disabled = self.refresh_inline_completion(false, true, cx).is_none();
5310 if is_copilot_disabled {
5311 cx.propagate();
5312 }
5313 }
5314 }
5315
5316 pub fn previous_inline_completion(
5317 &mut self,
5318 _: &PreviousInlineCompletion,
5319 cx: &mut ViewContext<Self>,
5320 ) {
5321 if self.has_active_inline_completion(cx) {
5322 self.cycle_inline_completion(Direction::Prev, cx);
5323 } else {
5324 let is_copilot_disabled = self.refresh_inline_completion(false, true, cx).is_none();
5325 if is_copilot_disabled {
5326 cx.propagate();
5327 }
5328 }
5329 }
5330
5331 pub fn accept_inline_completion(
5332 &mut self,
5333 _: &AcceptInlineCompletion,
5334 cx: &mut ViewContext<Self>,
5335 ) {
5336 let Some(completion) = self.take_active_inline_completion(cx) else {
5337 return;
5338 };
5339 if let Some(provider) = self.inline_completion_provider() {
5340 provider.accept(cx);
5341 }
5342
5343 cx.emit(EditorEvent::InputHandled {
5344 utf16_range_to_replace: None,
5345 text: completion.text.to_string().into(),
5346 });
5347
5348 if let Some(range) = completion.delete_range {
5349 self.change_selections(None, cx, |s| s.select_ranges([range]))
5350 }
5351 self.insert_with_autoindent_mode(&completion.text.to_string(), None, cx);
5352 self.refresh_inline_completion(true, true, cx);
5353 cx.notify();
5354 }
5355
5356 pub fn accept_partial_inline_completion(
5357 &mut self,
5358 _: &AcceptPartialInlineCompletion,
5359 cx: &mut ViewContext<Self>,
5360 ) {
5361 if self.selections.count() == 1 && self.has_active_inline_completion(cx) {
5362 if let Some(completion) = self.take_active_inline_completion(cx) {
5363 let mut partial_completion = completion
5364 .text
5365 .chars()
5366 .by_ref()
5367 .take_while(|c| c.is_alphabetic())
5368 .collect::<String>();
5369 if partial_completion.is_empty() {
5370 partial_completion = completion
5371 .text
5372 .chars()
5373 .by_ref()
5374 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5375 .collect::<String>();
5376 }
5377
5378 cx.emit(EditorEvent::InputHandled {
5379 utf16_range_to_replace: None,
5380 text: partial_completion.clone().into(),
5381 });
5382
5383 if let Some(range) = completion.delete_range {
5384 self.change_selections(None, cx, |s| s.select_ranges([range]))
5385 }
5386 self.insert_with_autoindent_mode(&partial_completion, None, cx);
5387
5388 self.refresh_inline_completion(true, true, cx);
5389 cx.notify();
5390 }
5391 }
5392 }
5393
5394 fn discard_inline_completion(
5395 &mut self,
5396 should_report_inline_completion_event: bool,
5397 cx: &mut ViewContext<Self>,
5398 ) -> bool {
5399 if let Some(provider) = self.inline_completion_provider() {
5400 provider.discard(should_report_inline_completion_event, cx);
5401 }
5402
5403 self.take_active_inline_completion(cx).is_some()
5404 }
5405
5406 pub fn has_active_inline_completion(&self, cx: &AppContext) -> bool {
5407 if let Some(completion) = self.active_inline_completion.as_ref() {
5408 let buffer = self.buffer.read(cx).read(cx);
5409 completion.position.is_valid(&buffer)
5410 } else {
5411 false
5412 }
5413 }
5414
5415 fn take_active_inline_completion(
5416 &mut self,
5417 cx: &mut ViewContext<Self>,
5418 ) -> Option<CompletionState> {
5419 let completion = self.active_inline_completion.take()?;
5420 let render_inlay_ids = completion.render_inlay_ids.clone();
5421 self.display_map.update(cx, |map, cx| {
5422 map.splice_inlays(render_inlay_ids, Default::default(), cx);
5423 });
5424 let buffer = self.buffer.read(cx).read(cx);
5425
5426 if completion.position.is_valid(&buffer) {
5427 Some(completion)
5428 } else {
5429 None
5430 }
5431 }
5432
5433 fn update_visible_inline_completion(&mut self, cx: &mut ViewContext<Self>) {
5434 let selection = self.selections.newest_anchor();
5435 let cursor = selection.head();
5436
5437 let excerpt_id = cursor.excerpt_id;
5438
5439 if self.context_menu.read().is_none()
5440 && self.completion_tasks.is_empty()
5441 && selection.start == selection.end
5442 {
5443 if let Some(provider) = self.inline_completion_provider() {
5444 if let Some((buffer, cursor_buffer_position)) =
5445 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5446 {
5447 if let Some(proposal) =
5448 provider.active_completion_text(&buffer, cursor_buffer_position, cx)
5449 {
5450 let mut to_remove = Vec::new();
5451 if let Some(completion) = self.active_inline_completion.take() {
5452 to_remove.extend(completion.render_inlay_ids.iter());
5453 }
5454
5455 let to_add = proposal
5456 .inlays
5457 .iter()
5458 .filter_map(|inlay| {
5459 let snapshot = self.buffer.read(cx).snapshot(cx);
5460 let id = post_inc(&mut self.next_inlay_id);
5461 match inlay {
5462 InlayProposal::Hint(position, hint) => {
5463 let position =
5464 snapshot.anchor_in_excerpt(excerpt_id, *position)?;
5465 Some(Inlay::hint(id, position, hint))
5466 }
5467 InlayProposal::Suggestion(position, text) => {
5468 let position =
5469 snapshot.anchor_in_excerpt(excerpt_id, *position)?;
5470 Some(Inlay::suggestion(id, position, text.clone()))
5471 }
5472 }
5473 })
5474 .collect_vec();
5475
5476 self.active_inline_completion = Some(CompletionState {
5477 position: cursor,
5478 text: proposal.text,
5479 delete_range: proposal.delete_range.and_then(|range| {
5480 let snapshot = self.buffer.read(cx).snapshot(cx);
5481 let start = snapshot.anchor_in_excerpt(excerpt_id, range.start);
5482 let end = snapshot.anchor_in_excerpt(excerpt_id, range.end);
5483 Some(start?..end?)
5484 }),
5485 render_inlay_ids: to_add.iter().map(|i| i.id).collect(),
5486 });
5487
5488 self.display_map
5489 .update(cx, move |map, cx| map.splice_inlays(to_remove, to_add, cx));
5490
5491 cx.notify();
5492 return;
5493 }
5494 }
5495 }
5496 }
5497
5498 self.discard_inline_completion(false, cx);
5499 }
5500
5501 fn inline_completion_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5502 Some(self.inline_completion_provider.as_ref()?.provider.clone())
5503 }
5504
5505 fn render_code_actions_indicator(
5506 &self,
5507 _style: &EditorStyle,
5508 row: DisplayRow,
5509 is_active: bool,
5510 cx: &mut ViewContext<Self>,
5511 ) -> Option<IconButton> {
5512 if self.available_code_actions.is_some() {
5513 Some(
5514 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5515 .shape(ui::IconButtonShape::Square)
5516 .icon_size(IconSize::XSmall)
5517 .icon_color(Color::Muted)
5518 .selected(is_active)
5519 .tooltip({
5520 let focus_handle = self.focus_handle.clone();
5521 move |cx| {
5522 Tooltip::for_action_in(
5523 "Toggle Code Actions",
5524 &ToggleCodeActions {
5525 deployed_from_indicator: None,
5526 },
5527 &focus_handle,
5528 cx,
5529 )
5530 }
5531 })
5532 .on_click(cx.listener(move |editor, _e, cx| {
5533 editor.focus(cx);
5534 editor.toggle_code_actions(
5535 &ToggleCodeActions {
5536 deployed_from_indicator: Some(row),
5537 },
5538 cx,
5539 );
5540 })),
5541 )
5542 } else {
5543 None
5544 }
5545 }
5546
5547 fn clear_tasks(&mut self) {
5548 self.tasks.clear()
5549 }
5550
5551 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5552 if self.tasks.insert(key, value).is_some() {
5553 // This case should hopefully be rare, but just in case...
5554 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5555 }
5556 }
5557
5558 fn build_tasks_context(
5559 project: &Model<Project>,
5560 buffer: &Model<Buffer>,
5561 buffer_row: u32,
5562 tasks: &Arc<RunnableTasks>,
5563 cx: &mut ViewContext<Self>,
5564 ) -> Task<Option<task::TaskContext>> {
5565 let position = Point::new(buffer_row, tasks.column);
5566 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
5567 let location = Location {
5568 buffer: buffer.clone(),
5569 range: range_start..range_start,
5570 };
5571 // Fill in the environmental variables from the tree-sitter captures
5572 let mut captured_task_variables = TaskVariables::default();
5573 for (capture_name, value) in tasks.extra_variables.clone() {
5574 captured_task_variables.insert(
5575 task::VariableName::Custom(capture_name.into()),
5576 value.clone(),
5577 );
5578 }
5579 project.update(cx, |project, cx| {
5580 project.task_store().update(cx, |task_store, cx| {
5581 task_store.task_context_for_location(captured_task_variables, location, cx)
5582 })
5583 })
5584 }
5585
5586 pub fn spawn_nearest_task(&mut self, action: &SpawnNearestTask, cx: &mut ViewContext<Self>) {
5587 let Some((workspace, _)) = self.workspace.clone() else {
5588 return;
5589 };
5590 let Some(project) = self.project.clone() else {
5591 return;
5592 };
5593
5594 // Try to find a closest, enclosing node using tree-sitter that has a
5595 // task
5596 let Some((buffer, buffer_row, tasks)) = self
5597 .find_enclosing_node_task(cx)
5598 // Or find the task that's closest in row-distance.
5599 .or_else(|| self.find_closest_task(cx))
5600 else {
5601 return;
5602 };
5603
5604 let reveal_strategy = action.reveal;
5605 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
5606 cx.spawn(|_, mut cx| async move {
5607 let context = task_context.await?;
5608 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
5609
5610 let resolved = resolved_task.resolved.as_mut()?;
5611 resolved.reveal = reveal_strategy;
5612
5613 workspace
5614 .update(&mut cx, |workspace, cx| {
5615 workspace::tasks::schedule_resolved_task(
5616 workspace,
5617 task_source_kind,
5618 resolved_task,
5619 false,
5620 cx,
5621 );
5622 })
5623 .ok()
5624 })
5625 .detach();
5626 }
5627
5628 fn find_closest_task(
5629 &mut self,
5630 cx: &mut ViewContext<Self>,
5631 ) -> Option<(Model<Buffer>, u32, Arc<RunnableTasks>)> {
5632 let cursor_row = self.selections.newest_adjusted(cx).head().row;
5633
5634 let ((buffer_id, row), tasks) = self
5635 .tasks
5636 .iter()
5637 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
5638
5639 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
5640 let tasks = Arc::new(tasks.to_owned());
5641 Some((buffer, *row, tasks))
5642 }
5643
5644 fn find_enclosing_node_task(
5645 &mut self,
5646 cx: &mut ViewContext<Self>,
5647 ) -> Option<(Model<Buffer>, u32, Arc<RunnableTasks>)> {
5648 let snapshot = self.buffer.read(cx).snapshot(cx);
5649 let offset = self.selections.newest::<usize>(cx).head();
5650 let excerpt = snapshot.excerpt_containing(offset..offset)?;
5651 let buffer_id = excerpt.buffer().remote_id();
5652
5653 let layer = excerpt.buffer().syntax_layer_at(offset)?;
5654 let mut cursor = layer.node().walk();
5655
5656 while cursor.goto_first_child_for_byte(offset).is_some() {
5657 if cursor.node().end_byte() == offset {
5658 cursor.goto_next_sibling();
5659 }
5660 }
5661
5662 // Ascend to the smallest ancestor that contains the range and has a task.
5663 loop {
5664 let node = cursor.node();
5665 let node_range = node.byte_range();
5666 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
5667
5668 // Check if this node contains our offset
5669 if node_range.start <= offset && node_range.end >= offset {
5670 // If it contains offset, check for task
5671 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
5672 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
5673 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
5674 }
5675 }
5676
5677 if !cursor.goto_parent() {
5678 break;
5679 }
5680 }
5681 None
5682 }
5683
5684 fn render_run_indicator(
5685 &self,
5686 _style: &EditorStyle,
5687 is_active: bool,
5688 row: DisplayRow,
5689 cx: &mut ViewContext<Self>,
5690 ) -> IconButton {
5691 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5692 .shape(ui::IconButtonShape::Square)
5693 .icon_size(IconSize::XSmall)
5694 .icon_color(Color::Muted)
5695 .selected(is_active)
5696 .on_click(cx.listener(move |editor, _e, cx| {
5697 editor.focus(cx);
5698 editor.toggle_code_actions(
5699 &ToggleCodeActions {
5700 deployed_from_indicator: Some(row),
5701 },
5702 cx,
5703 );
5704 }))
5705 }
5706
5707 pub fn context_menu_visible(&self) -> bool {
5708 self.context_menu
5709 .read()
5710 .as_ref()
5711 .map_or(false, |menu| menu.visible())
5712 }
5713
5714 fn render_context_menu(
5715 &self,
5716 cursor_position: DisplayPoint,
5717 style: &EditorStyle,
5718 max_height: Pixels,
5719 cx: &mut ViewContext<Editor>,
5720 ) -> Option<(ContextMenuOrigin, AnyElement)> {
5721 self.context_menu.read().as_ref().map(|menu| {
5722 menu.render(
5723 cursor_position,
5724 style,
5725 max_height,
5726 self.workspace.as_ref().map(|(w, _)| w.clone()),
5727 cx,
5728 )
5729 })
5730 }
5731
5732 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
5733 cx.notify();
5734 self.completion_tasks.clear();
5735 let context_menu = self.context_menu.write().take();
5736 if context_menu.is_some() {
5737 self.update_visible_inline_completion(cx);
5738 }
5739 context_menu
5740 }
5741
5742 fn show_snippet_choices(
5743 &mut self,
5744 choices: &Vec<String>,
5745 selection: Range<Anchor>,
5746 cx: &mut ViewContext<Self>,
5747 ) {
5748 if selection.start.buffer_id.is_none() {
5749 return;
5750 }
5751 let buffer_id = selection.start.buffer_id.unwrap();
5752 let buffer = self.buffer().read(cx).buffer(buffer_id);
5753 let id = post_inc(&mut self.next_completion_id);
5754
5755 if let Some(buffer) = buffer {
5756 *self.context_menu.write() = Some(ContextMenu::Completions(
5757 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer)
5758 .suppress_documentation_resolution(),
5759 ));
5760 }
5761 }
5762
5763 pub fn insert_snippet(
5764 &mut self,
5765 insertion_ranges: &[Range<usize>],
5766 snippet: Snippet,
5767 cx: &mut ViewContext<Self>,
5768 ) -> Result<()> {
5769 struct Tabstop<T> {
5770 is_end_tabstop: bool,
5771 ranges: Vec<Range<T>>,
5772 choices: Option<Vec<String>>,
5773 }
5774
5775 let tabstops = self.buffer.update(cx, |buffer, cx| {
5776 let snippet_text: Arc<str> = snippet.text.clone().into();
5777 buffer.edit(
5778 insertion_ranges
5779 .iter()
5780 .cloned()
5781 .map(|range| (range, snippet_text.clone())),
5782 Some(AutoindentMode::EachLine),
5783 cx,
5784 );
5785
5786 let snapshot = &*buffer.read(cx);
5787 let snippet = &snippet;
5788 snippet
5789 .tabstops
5790 .iter()
5791 .map(|tabstop| {
5792 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
5793 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
5794 });
5795 let mut tabstop_ranges = tabstop
5796 .ranges
5797 .iter()
5798 .flat_map(|tabstop_range| {
5799 let mut delta = 0_isize;
5800 insertion_ranges.iter().map(move |insertion_range| {
5801 let insertion_start = insertion_range.start as isize + delta;
5802 delta +=
5803 snippet.text.len() as isize - insertion_range.len() as isize;
5804
5805 let start = ((insertion_start + tabstop_range.start) as usize)
5806 .min(snapshot.len());
5807 let end = ((insertion_start + tabstop_range.end) as usize)
5808 .min(snapshot.len());
5809 snapshot.anchor_before(start)..snapshot.anchor_after(end)
5810 })
5811 })
5812 .collect::<Vec<_>>();
5813 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
5814
5815 Tabstop {
5816 is_end_tabstop,
5817 ranges: tabstop_ranges,
5818 choices: tabstop.choices.clone(),
5819 }
5820 })
5821 .collect::<Vec<_>>()
5822 });
5823 if let Some(tabstop) = tabstops.first() {
5824 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5825 s.select_ranges(tabstop.ranges.iter().cloned());
5826 });
5827
5828 if let Some(choices) = &tabstop.choices {
5829 if let Some(selection) = tabstop.ranges.first() {
5830 self.show_snippet_choices(choices, selection.clone(), cx)
5831 }
5832 }
5833
5834 // If we're already at the last tabstop and it's at the end of the snippet,
5835 // we're done, we don't need to keep the state around.
5836 if !tabstop.is_end_tabstop {
5837 let choices = tabstops
5838 .iter()
5839 .map(|tabstop| tabstop.choices.clone())
5840 .collect();
5841
5842 let ranges = tabstops
5843 .into_iter()
5844 .map(|tabstop| tabstop.ranges)
5845 .collect::<Vec<_>>();
5846
5847 self.snippet_stack.push(SnippetState {
5848 active_index: 0,
5849 ranges,
5850 choices,
5851 });
5852 }
5853
5854 // Check whether the just-entered snippet ends with an auto-closable bracket.
5855 if self.autoclose_regions.is_empty() {
5856 let snapshot = self.buffer.read(cx).snapshot(cx);
5857 for selection in &mut self.selections.all::<Point>(cx) {
5858 let selection_head = selection.head();
5859 let Some(scope) = snapshot.language_scope_at(selection_head) else {
5860 continue;
5861 };
5862
5863 let mut bracket_pair = None;
5864 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
5865 let prev_chars = snapshot
5866 .reversed_chars_at(selection_head)
5867 .collect::<String>();
5868 for (pair, enabled) in scope.brackets() {
5869 if enabled
5870 && pair.close
5871 && prev_chars.starts_with(pair.start.as_str())
5872 && next_chars.starts_with(pair.end.as_str())
5873 {
5874 bracket_pair = Some(pair.clone());
5875 break;
5876 }
5877 }
5878 if let Some(pair) = bracket_pair {
5879 let start = snapshot.anchor_after(selection_head);
5880 let end = snapshot.anchor_after(selection_head);
5881 self.autoclose_regions.push(AutocloseRegion {
5882 selection_id: selection.id,
5883 range: start..end,
5884 pair,
5885 });
5886 }
5887 }
5888 }
5889 }
5890 Ok(())
5891 }
5892
5893 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5894 self.move_to_snippet_tabstop(Bias::Right, cx)
5895 }
5896
5897 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5898 self.move_to_snippet_tabstop(Bias::Left, cx)
5899 }
5900
5901 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
5902 if let Some(mut snippet) = self.snippet_stack.pop() {
5903 match bias {
5904 Bias::Left => {
5905 if snippet.active_index > 0 {
5906 snippet.active_index -= 1;
5907 } else {
5908 self.snippet_stack.push(snippet);
5909 return false;
5910 }
5911 }
5912 Bias::Right => {
5913 if snippet.active_index + 1 < snippet.ranges.len() {
5914 snippet.active_index += 1;
5915 } else {
5916 self.snippet_stack.push(snippet);
5917 return false;
5918 }
5919 }
5920 }
5921 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
5922 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5923 s.select_anchor_ranges(current_ranges.iter().cloned())
5924 });
5925
5926 if let Some(choices) = &snippet.choices[snippet.active_index] {
5927 if let Some(selection) = current_ranges.first() {
5928 self.show_snippet_choices(&choices, selection.clone(), cx);
5929 }
5930 }
5931
5932 // If snippet state is not at the last tabstop, push it back on the stack
5933 if snippet.active_index + 1 < snippet.ranges.len() {
5934 self.snippet_stack.push(snippet);
5935 }
5936 return true;
5937 }
5938 }
5939
5940 false
5941 }
5942
5943 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
5944 self.transact(cx, |this, cx| {
5945 this.select_all(&SelectAll, cx);
5946 this.insert("", cx);
5947 });
5948 }
5949
5950 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
5951 self.transact(cx, |this, cx| {
5952 this.select_autoclose_pair(cx);
5953 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
5954 if !this.linked_edit_ranges.is_empty() {
5955 let selections = this.selections.all::<MultiBufferPoint>(cx);
5956 let snapshot = this.buffer.read(cx).snapshot(cx);
5957
5958 for selection in selections.iter() {
5959 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
5960 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
5961 if selection_start.buffer_id != selection_end.buffer_id {
5962 continue;
5963 }
5964 if let Some(ranges) =
5965 this.linked_editing_ranges_for(selection_start..selection_end, cx)
5966 {
5967 for (buffer, entries) in ranges {
5968 linked_ranges.entry(buffer).or_default().extend(entries);
5969 }
5970 }
5971 }
5972 }
5973
5974 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
5975 if !this.selections.line_mode {
5976 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
5977 for selection in &mut selections {
5978 if selection.is_empty() {
5979 let old_head = selection.head();
5980 let mut new_head =
5981 movement::left(&display_map, old_head.to_display_point(&display_map))
5982 .to_point(&display_map);
5983 if let Some((buffer, line_buffer_range)) = display_map
5984 .buffer_snapshot
5985 .buffer_line_for_row(MultiBufferRow(old_head.row))
5986 {
5987 let indent_size =
5988 buffer.indent_size_for_line(line_buffer_range.start.row);
5989 let indent_len = match indent_size.kind {
5990 IndentKind::Space => {
5991 buffer.settings_at(line_buffer_range.start, cx).tab_size
5992 }
5993 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
5994 };
5995 if old_head.column <= indent_size.len && old_head.column > 0 {
5996 let indent_len = indent_len.get();
5997 new_head = cmp::min(
5998 new_head,
5999 MultiBufferPoint::new(
6000 old_head.row,
6001 ((old_head.column - 1) / indent_len) * indent_len,
6002 ),
6003 );
6004 }
6005 }
6006
6007 selection.set_head(new_head, SelectionGoal::None);
6008 }
6009 }
6010 }
6011
6012 this.signature_help_state.set_backspace_pressed(true);
6013 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6014 this.insert("", cx);
6015 let empty_str: Arc<str> = Arc::from("");
6016 for (buffer, edits) in linked_ranges {
6017 let snapshot = buffer.read(cx).snapshot();
6018 use text::ToPoint as TP;
6019
6020 let edits = edits
6021 .into_iter()
6022 .map(|range| {
6023 let end_point = TP::to_point(&range.end, &snapshot);
6024 let mut start_point = TP::to_point(&range.start, &snapshot);
6025
6026 if end_point == start_point {
6027 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
6028 .saturating_sub(1);
6029 start_point = TP::to_point(&offset, &snapshot);
6030 };
6031
6032 (start_point..end_point, empty_str.clone())
6033 })
6034 .sorted_by_key(|(range, _)| range.start)
6035 .collect::<Vec<_>>();
6036 buffer.update(cx, |this, cx| {
6037 this.edit(edits, None, cx);
6038 })
6039 }
6040 this.refresh_inline_completion(true, false, cx);
6041 linked_editing_ranges::refresh_linked_ranges(this, cx);
6042 });
6043 }
6044
6045 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
6046 self.transact(cx, |this, cx| {
6047 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6048 let line_mode = s.line_mode;
6049 s.move_with(|map, selection| {
6050 if selection.is_empty() && !line_mode {
6051 let cursor = movement::right(map, selection.head());
6052 selection.end = cursor;
6053 selection.reversed = true;
6054 selection.goal = SelectionGoal::None;
6055 }
6056 })
6057 });
6058 this.insert("", cx);
6059 this.refresh_inline_completion(true, false, cx);
6060 });
6061 }
6062
6063 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
6064 if self.move_to_prev_snippet_tabstop(cx) {
6065 return;
6066 }
6067
6068 self.outdent(&Outdent, cx);
6069 }
6070
6071 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
6072 if self.move_to_next_snippet_tabstop(cx) || self.read_only(cx) {
6073 return;
6074 }
6075
6076 let mut selections = self.selections.all_adjusted(cx);
6077 let buffer = self.buffer.read(cx);
6078 let snapshot = buffer.snapshot(cx);
6079 let rows_iter = selections.iter().map(|s| s.head().row);
6080 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
6081
6082 let mut edits = Vec::new();
6083 let mut prev_edited_row = 0;
6084 let mut row_delta = 0;
6085 for selection in &mut selections {
6086 if selection.start.row != prev_edited_row {
6087 row_delta = 0;
6088 }
6089 prev_edited_row = selection.end.row;
6090
6091 // If the selection is non-empty, then increase the indentation of the selected lines.
6092 if !selection.is_empty() {
6093 row_delta =
6094 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
6095 continue;
6096 }
6097
6098 // If the selection is empty and the cursor is in the leading whitespace before the
6099 // suggested indentation, then auto-indent the line.
6100 let cursor = selection.head();
6101 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
6102 if let Some(suggested_indent) =
6103 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
6104 {
6105 if cursor.column < suggested_indent.len
6106 && cursor.column <= current_indent.len
6107 && current_indent.len <= suggested_indent.len
6108 {
6109 selection.start = Point::new(cursor.row, suggested_indent.len);
6110 selection.end = selection.start;
6111 if row_delta == 0 {
6112 edits.extend(Buffer::edit_for_indent_size_adjustment(
6113 cursor.row,
6114 current_indent,
6115 suggested_indent,
6116 ));
6117 row_delta = suggested_indent.len - current_indent.len;
6118 }
6119 continue;
6120 }
6121 }
6122
6123 // Otherwise, insert a hard or soft tab.
6124 let settings = buffer.settings_at(cursor, cx);
6125 let tab_size = if settings.hard_tabs {
6126 IndentSize::tab()
6127 } else {
6128 let tab_size = settings.tab_size.get();
6129 let char_column = snapshot
6130 .text_for_range(Point::new(cursor.row, 0)..cursor)
6131 .flat_map(str::chars)
6132 .count()
6133 + row_delta as usize;
6134 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
6135 IndentSize::spaces(chars_to_next_tab_stop)
6136 };
6137 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
6138 selection.end = selection.start;
6139 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
6140 row_delta += tab_size.len;
6141 }
6142
6143 self.transact(cx, |this, cx| {
6144 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
6145 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6146 this.refresh_inline_completion(true, false, cx);
6147 });
6148 }
6149
6150 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
6151 if self.read_only(cx) {
6152 return;
6153 }
6154 let mut selections = self.selections.all::<Point>(cx);
6155 let mut prev_edited_row = 0;
6156 let mut row_delta = 0;
6157 let mut edits = Vec::new();
6158 let buffer = self.buffer.read(cx);
6159 let snapshot = buffer.snapshot(cx);
6160 for selection in &mut selections {
6161 if selection.start.row != prev_edited_row {
6162 row_delta = 0;
6163 }
6164 prev_edited_row = selection.end.row;
6165
6166 row_delta =
6167 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
6168 }
6169
6170 self.transact(cx, |this, cx| {
6171 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
6172 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6173 });
6174 }
6175
6176 fn indent_selection(
6177 buffer: &MultiBuffer,
6178 snapshot: &MultiBufferSnapshot,
6179 selection: &mut Selection<Point>,
6180 edits: &mut Vec<(Range<Point>, String)>,
6181 delta_for_start_row: u32,
6182 cx: &AppContext,
6183 ) -> u32 {
6184 let settings = buffer.settings_at(selection.start, cx);
6185 let tab_size = settings.tab_size.get();
6186 let indent_kind = if settings.hard_tabs {
6187 IndentKind::Tab
6188 } else {
6189 IndentKind::Space
6190 };
6191 let mut start_row = selection.start.row;
6192 let mut end_row = selection.end.row + 1;
6193
6194 // If a selection ends at the beginning of a line, don't indent
6195 // that last line.
6196 if selection.end.column == 0 && selection.end.row > selection.start.row {
6197 end_row -= 1;
6198 }
6199
6200 // Avoid re-indenting a row that has already been indented by a
6201 // previous selection, but still update this selection's column
6202 // to reflect that indentation.
6203 if delta_for_start_row > 0 {
6204 start_row += 1;
6205 selection.start.column += delta_for_start_row;
6206 if selection.end.row == selection.start.row {
6207 selection.end.column += delta_for_start_row;
6208 }
6209 }
6210
6211 let mut delta_for_end_row = 0;
6212 let has_multiple_rows = start_row + 1 != end_row;
6213 for row in start_row..end_row {
6214 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
6215 let indent_delta = match (current_indent.kind, indent_kind) {
6216 (IndentKind::Space, IndentKind::Space) => {
6217 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
6218 IndentSize::spaces(columns_to_next_tab_stop)
6219 }
6220 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
6221 (_, IndentKind::Tab) => IndentSize::tab(),
6222 };
6223
6224 let start = if has_multiple_rows || current_indent.len < selection.start.column {
6225 0
6226 } else {
6227 selection.start.column
6228 };
6229 let row_start = Point::new(row, start);
6230 edits.push((
6231 row_start..row_start,
6232 indent_delta.chars().collect::<String>(),
6233 ));
6234
6235 // Update this selection's endpoints to reflect the indentation.
6236 if row == selection.start.row {
6237 selection.start.column += indent_delta.len;
6238 }
6239 if row == selection.end.row {
6240 selection.end.column += indent_delta.len;
6241 delta_for_end_row = indent_delta.len;
6242 }
6243 }
6244
6245 if selection.start.row == selection.end.row {
6246 delta_for_start_row + delta_for_end_row
6247 } else {
6248 delta_for_end_row
6249 }
6250 }
6251
6252 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
6253 if self.read_only(cx) {
6254 return;
6255 }
6256 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6257 let selections = self.selections.all::<Point>(cx);
6258 let mut deletion_ranges = Vec::new();
6259 let mut last_outdent = None;
6260 {
6261 let buffer = self.buffer.read(cx);
6262 let snapshot = buffer.snapshot(cx);
6263 for selection in &selections {
6264 let settings = buffer.settings_at(selection.start, cx);
6265 let tab_size = settings.tab_size.get();
6266 let mut rows = selection.spanned_rows(false, &display_map);
6267
6268 // Avoid re-outdenting a row that has already been outdented by a
6269 // previous selection.
6270 if let Some(last_row) = last_outdent {
6271 if last_row == rows.start {
6272 rows.start = rows.start.next_row();
6273 }
6274 }
6275 let has_multiple_rows = rows.len() > 1;
6276 for row in rows.iter_rows() {
6277 let indent_size = snapshot.indent_size_for_line(row);
6278 if indent_size.len > 0 {
6279 let deletion_len = match indent_size.kind {
6280 IndentKind::Space => {
6281 let columns_to_prev_tab_stop = indent_size.len % tab_size;
6282 if columns_to_prev_tab_stop == 0 {
6283 tab_size
6284 } else {
6285 columns_to_prev_tab_stop
6286 }
6287 }
6288 IndentKind::Tab => 1,
6289 };
6290 let start = if has_multiple_rows
6291 || deletion_len > selection.start.column
6292 || indent_size.len < selection.start.column
6293 {
6294 0
6295 } else {
6296 selection.start.column - deletion_len
6297 };
6298 deletion_ranges.push(
6299 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
6300 );
6301 last_outdent = Some(row);
6302 }
6303 }
6304 }
6305 }
6306
6307 self.transact(cx, |this, cx| {
6308 this.buffer.update(cx, |buffer, cx| {
6309 let empty_str: Arc<str> = Arc::default();
6310 buffer.edit(
6311 deletion_ranges
6312 .into_iter()
6313 .map(|range| (range, empty_str.clone())),
6314 None,
6315 cx,
6316 );
6317 });
6318 let selections = this.selections.all::<usize>(cx);
6319 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6320 });
6321 }
6322
6323 pub fn autoindent(&mut self, _: &AutoIndent, cx: &mut ViewContext<Self>) {
6324 if self.read_only(cx) {
6325 return;
6326 }
6327 let selections = self
6328 .selections
6329 .all::<usize>(cx)
6330 .into_iter()
6331 .map(|s| s.range());
6332
6333 self.transact(cx, |this, cx| {
6334 this.buffer.update(cx, |buffer, cx| {
6335 buffer.autoindent_ranges(selections, cx);
6336 });
6337 let selections = this.selections.all::<usize>(cx);
6338 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6339 });
6340 }
6341
6342 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
6343 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6344 let selections = self.selections.all::<Point>(cx);
6345
6346 let mut new_cursors = Vec::new();
6347 let mut edit_ranges = Vec::new();
6348 let mut selections = selections.iter().peekable();
6349 while let Some(selection) = selections.next() {
6350 let mut rows = selection.spanned_rows(false, &display_map);
6351 let goal_display_column = selection.head().to_display_point(&display_map).column();
6352
6353 // Accumulate contiguous regions of rows that we want to delete.
6354 while let Some(next_selection) = selections.peek() {
6355 let next_rows = next_selection.spanned_rows(false, &display_map);
6356 if next_rows.start <= rows.end {
6357 rows.end = next_rows.end;
6358 selections.next().unwrap();
6359 } else {
6360 break;
6361 }
6362 }
6363
6364 let buffer = &display_map.buffer_snapshot;
6365 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
6366 let edit_end;
6367 let cursor_buffer_row;
6368 if buffer.max_point().row >= rows.end.0 {
6369 // If there's a line after the range, delete the \n from the end of the row range
6370 // and position the cursor on the next line.
6371 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
6372 cursor_buffer_row = rows.end;
6373 } else {
6374 // If there isn't a line after the range, delete the \n from the line before the
6375 // start of the row range and position the cursor there.
6376 edit_start = edit_start.saturating_sub(1);
6377 edit_end = buffer.len();
6378 cursor_buffer_row = rows.start.previous_row();
6379 }
6380
6381 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
6382 *cursor.column_mut() =
6383 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
6384
6385 new_cursors.push((
6386 selection.id,
6387 buffer.anchor_after(cursor.to_point(&display_map)),
6388 ));
6389 edit_ranges.push(edit_start..edit_end);
6390 }
6391
6392 self.transact(cx, |this, cx| {
6393 let buffer = this.buffer.update(cx, |buffer, cx| {
6394 let empty_str: Arc<str> = Arc::default();
6395 buffer.edit(
6396 edit_ranges
6397 .into_iter()
6398 .map(|range| (range, empty_str.clone())),
6399 None,
6400 cx,
6401 );
6402 buffer.snapshot(cx)
6403 });
6404 let new_selections = new_cursors
6405 .into_iter()
6406 .map(|(id, cursor)| {
6407 let cursor = cursor.to_point(&buffer);
6408 Selection {
6409 id,
6410 start: cursor,
6411 end: cursor,
6412 reversed: false,
6413 goal: SelectionGoal::None,
6414 }
6415 })
6416 .collect();
6417
6418 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6419 s.select(new_selections);
6420 });
6421 });
6422 }
6423
6424 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
6425 if self.read_only(cx) {
6426 return;
6427 }
6428 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
6429 for selection in self.selections.all::<Point>(cx) {
6430 let start = MultiBufferRow(selection.start.row);
6431 // Treat single line selections as if they include the next line. Otherwise this action
6432 // would do nothing for single line selections individual cursors.
6433 let end = if selection.start.row == selection.end.row {
6434 MultiBufferRow(selection.start.row + 1)
6435 } else {
6436 MultiBufferRow(selection.end.row)
6437 };
6438
6439 if let Some(last_row_range) = row_ranges.last_mut() {
6440 if start <= last_row_range.end {
6441 last_row_range.end = end;
6442 continue;
6443 }
6444 }
6445 row_ranges.push(start..end);
6446 }
6447
6448 let snapshot = self.buffer.read(cx).snapshot(cx);
6449 let mut cursor_positions = Vec::new();
6450 for row_range in &row_ranges {
6451 let anchor = snapshot.anchor_before(Point::new(
6452 row_range.end.previous_row().0,
6453 snapshot.line_len(row_range.end.previous_row()),
6454 ));
6455 cursor_positions.push(anchor..anchor);
6456 }
6457
6458 self.transact(cx, |this, cx| {
6459 for row_range in row_ranges.into_iter().rev() {
6460 for row in row_range.iter_rows().rev() {
6461 let end_of_line = Point::new(row.0, snapshot.line_len(row));
6462 let next_line_row = row.next_row();
6463 let indent = snapshot.indent_size_for_line(next_line_row);
6464 let start_of_next_line = Point::new(next_line_row.0, indent.len);
6465
6466 let replace = if snapshot.line_len(next_line_row) > indent.len {
6467 " "
6468 } else {
6469 ""
6470 };
6471
6472 this.buffer.update(cx, |buffer, cx| {
6473 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
6474 });
6475 }
6476 }
6477
6478 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6479 s.select_anchor_ranges(cursor_positions)
6480 });
6481 });
6482 }
6483
6484 pub fn sort_lines_case_sensitive(
6485 &mut self,
6486 _: &SortLinesCaseSensitive,
6487 cx: &mut ViewContext<Self>,
6488 ) {
6489 self.manipulate_lines(cx, |lines| lines.sort())
6490 }
6491
6492 pub fn sort_lines_case_insensitive(
6493 &mut self,
6494 _: &SortLinesCaseInsensitive,
6495 cx: &mut ViewContext<Self>,
6496 ) {
6497 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
6498 }
6499
6500 pub fn unique_lines_case_insensitive(
6501 &mut self,
6502 _: &UniqueLinesCaseInsensitive,
6503 cx: &mut ViewContext<Self>,
6504 ) {
6505 self.manipulate_lines(cx, |lines| {
6506 let mut seen = HashSet::default();
6507 lines.retain(|line| seen.insert(line.to_lowercase()));
6508 })
6509 }
6510
6511 pub fn unique_lines_case_sensitive(
6512 &mut self,
6513 _: &UniqueLinesCaseSensitive,
6514 cx: &mut ViewContext<Self>,
6515 ) {
6516 self.manipulate_lines(cx, |lines| {
6517 let mut seen = HashSet::default();
6518 lines.retain(|line| seen.insert(*line));
6519 })
6520 }
6521
6522 pub fn revert_file(&mut self, _: &RevertFile, cx: &mut ViewContext<Self>) {
6523 let mut revert_changes = HashMap::default();
6524 let snapshot = self.snapshot(cx);
6525 for hunk in hunks_for_ranges(
6526 Some(Point::zero()..snapshot.buffer_snapshot.max_point()).into_iter(),
6527 &snapshot,
6528 ) {
6529 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
6530 }
6531 if !revert_changes.is_empty() {
6532 self.transact(cx, |editor, cx| {
6533 editor.revert(revert_changes, cx);
6534 });
6535 }
6536 }
6537
6538 pub fn reload_file(&mut self, _: &ReloadFile, cx: &mut ViewContext<Self>) {
6539 let Some(project) = self.project.clone() else {
6540 return;
6541 };
6542 self.reload(project, cx).detach_and_notify_err(cx);
6543 }
6544
6545 pub fn revert_selected_hunks(&mut self, _: &RevertSelectedHunks, cx: &mut ViewContext<Self>) {
6546 let revert_changes = self.gather_revert_changes(&self.selections.all(cx), cx);
6547 if !revert_changes.is_empty() {
6548 self.transact(cx, |editor, cx| {
6549 editor.revert(revert_changes, cx);
6550 });
6551 }
6552 }
6553
6554 fn revert_hunk(&mut self, hunk: HoveredHunk, cx: &mut ViewContext<Editor>) {
6555 let snapshot = self.buffer.read(cx).read(cx);
6556 if let Some(hunk) = crate::hunk_diff::to_diff_hunk(&hunk, &snapshot) {
6557 drop(snapshot);
6558 let mut revert_changes = HashMap::default();
6559 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
6560 if !revert_changes.is_empty() {
6561 self.revert(revert_changes, cx)
6562 }
6563 }
6564 }
6565
6566 pub fn open_active_item_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
6567 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
6568 let project_path = buffer.read(cx).project_path(cx)?;
6569 let project = self.project.as_ref()?.read(cx);
6570 let entry = project.entry_for_path(&project_path, cx)?;
6571 let parent = match &entry.canonical_path {
6572 Some(canonical_path) => canonical_path.to_path_buf(),
6573 None => project.absolute_path(&project_path, cx)?,
6574 }
6575 .parent()?
6576 .to_path_buf();
6577 Some(parent)
6578 }) {
6579 cx.dispatch_action(OpenTerminal { working_directory }.boxed_clone());
6580 }
6581 }
6582
6583 fn gather_revert_changes(
6584 &mut self,
6585 selections: &[Selection<Point>],
6586 cx: &mut ViewContext<'_, Editor>,
6587 ) -> HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>> {
6588 let mut revert_changes = HashMap::default();
6589 let snapshot = self.snapshot(cx);
6590 for hunk in hunks_for_selections(&snapshot, selections) {
6591 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
6592 }
6593 revert_changes
6594 }
6595
6596 pub fn prepare_revert_change(
6597 &mut self,
6598 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
6599 hunk: &MultiBufferDiffHunk,
6600 cx: &AppContext,
6601 ) -> Option<()> {
6602 let buffer = self.buffer.read(cx).buffer(hunk.buffer_id)?;
6603 let buffer = buffer.read(cx);
6604 let change_set = &self.diff_map.diff_bases.get(&hunk.buffer_id)?.change_set;
6605 let original_text = change_set
6606 .read(cx)
6607 .base_text
6608 .as_ref()?
6609 .read(cx)
6610 .as_rope()
6611 .slice(hunk.diff_base_byte_range.clone());
6612 let buffer_snapshot = buffer.snapshot();
6613 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
6614 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
6615 probe
6616 .0
6617 .start
6618 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
6619 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
6620 }) {
6621 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
6622 Some(())
6623 } else {
6624 None
6625 }
6626 }
6627
6628 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
6629 self.manipulate_lines(cx, |lines| lines.reverse())
6630 }
6631
6632 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
6633 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
6634 }
6635
6636 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6637 where
6638 Fn: FnMut(&mut Vec<&str>),
6639 {
6640 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6641 let buffer = self.buffer.read(cx).snapshot(cx);
6642
6643 let mut edits = Vec::new();
6644
6645 let selections = self.selections.all::<Point>(cx);
6646 let mut selections = selections.iter().peekable();
6647 let mut contiguous_row_selections = Vec::new();
6648 let mut new_selections = Vec::new();
6649 let mut added_lines = 0;
6650 let mut removed_lines = 0;
6651
6652 while let Some(selection) = selections.next() {
6653 let (start_row, end_row) = consume_contiguous_rows(
6654 &mut contiguous_row_selections,
6655 selection,
6656 &display_map,
6657 &mut selections,
6658 );
6659
6660 let start_point = Point::new(start_row.0, 0);
6661 let end_point = Point::new(
6662 end_row.previous_row().0,
6663 buffer.line_len(end_row.previous_row()),
6664 );
6665 let text = buffer
6666 .text_for_range(start_point..end_point)
6667 .collect::<String>();
6668
6669 let mut lines = text.split('\n').collect_vec();
6670
6671 let lines_before = lines.len();
6672 callback(&mut lines);
6673 let lines_after = lines.len();
6674
6675 edits.push((start_point..end_point, lines.join("\n")));
6676
6677 // Selections must change based on added and removed line count
6678 let start_row =
6679 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
6680 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
6681 new_selections.push(Selection {
6682 id: selection.id,
6683 start: start_row,
6684 end: end_row,
6685 goal: SelectionGoal::None,
6686 reversed: selection.reversed,
6687 });
6688
6689 if lines_after > lines_before {
6690 added_lines += lines_after - lines_before;
6691 } else if lines_before > lines_after {
6692 removed_lines += lines_before - lines_after;
6693 }
6694 }
6695
6696 self.transact(cx, |this, cx| {
6697 let buffer = this.buffer.update(cx, |buffer, cx| {
6698 buffer.edit(edits, None, cx);
6699 buffer.snapshot(cx)
6700 });
6701
6702 // Recalculate offsets on newly edited buffer
6703 let new_selections = new_selections
6704 .iter()
6705 .map(|s| {
6706 let start_point = Point::new(s.start.0, 0);
6707 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
6708 Selection {
6709 id: s.id,
6710 start: buffer.point_to_offset(start_point),
6711 end: buffer.point_to_offset(end_point),
6712 goal: s.goal,
6713 reversed: s.reversed,
6714 }
6715 })
6716 .collect();
6717
6718 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6719 s.select(new_selections);
6720 });
6721
6722 this.request_autoscroll(Autoscroll::fit(), cx);
6723 });
6724 }
6725
6726 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
6727 self.manipulate_text(cx, |text| text.to_uppercase())
6728 }
6729
6730 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
6731 self.manipulate_text(cx, |text| text.to_lowercase())
6732 }
6733
6734 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
6735 self.manipulate_text(cx, |text| {
6736 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6737 // https://github.com/rutrum/convert-case/issues/16
6738 text.split('\n')
6739 .map(|line| line.to_case(Case::Title))
6740 .join("\n")
6741 })
6742 }
6743
6744 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
6745 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
6746 }
6747
6748 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
6749 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
6750 }
6751
6752 pub fn convert_to_upper_camel_case(
6753 &mut self,
6754 _: &ConvertToUpperCamelCase,
6755 cx: &mut ViewContext<Self>,
6756 ) {
6757 self.manipulate_text(cx, |text| {
6758 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6759 // https://github.com/rutrum/convert-case/issues/16
6760 text.split('\n')
6761 .map(|line| line.to_case(Case::UpperCamel))
6762 .join("\n")
6763 })
6764 }
6765
6766 pub fn convert_to_lower_camel_case(
6767 &mut self,
6768 _: &ConvertToLowerCamelCase,
6769 cx: &mut ViewContext<Self>,
6770 ) {
6771 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
6772 }
6773
6774 pub fn convert_to_opposite_case(
6775 &mut self,
6776 _: &ConvertToOppositeCase,
6777 cx: &mut ViewContext<Self>,
6778 ) {
6779 self.manipulate_text(cx, |text| {
6780 text.chars()
6781 .fold(String::with_capacity(text.len()), |mut t, c| {
6782 if c.is_uppercase() {
6783 t.extend(c.to_lowercase());
6784 } else {
6785 t.extend(c.to_uppercase());
6786 }
6787 t
6788 })
6789 })
6790 }
6791
6792 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6793 where
6794 Fn: FnMut(&str) -> String,
6795 {
6796 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6797 let buffer = self.buffer.read(cx).snapshot(cx);
6798
6799 let mut new_selections = Vec::new();
6800 let mut edits = Vec::new();
6801 let mut selection_adjustment = 0i32;
6802
6803 for selection in self.selections.all::<usize>(cx) {
6804 let selection_is_empty = selection.is_empty();
6805
6806 let (start, end) = if selection_is_empty {
6807 let word_range = movement::surrounding_word(
6808 &display_map,
6809 selection.start.to_display_point(&display_map),
6810 );
6811 let start = word_range.start.to_offset(&display_map, Bias::Left);
6812 let end = word_range.end.to_offset(&display_map, Bias::Left);
6813 (start, end)
6814 } else {
6815 (selection.start, selection.end)
6816 };
6817
6818 let text = buffer.text_for_range(start..end).collect::<String>();
6819 let old_length = text.len() as i32;
6820 let text = callback(&text);
6821
6822 new_selections.push(Selection {
6823 start: (start as i32 - selection_adjustment) as usize,
6824 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
6825 goal: SelectionGoal::None,
6826 ..selection
6827 });
6828
6829 selection_adjustment += old_length - text.len() as i32;
6830
6831 edits.push((start..end, text));
6832 }
6833
6834 self.transact(cx, |this, cx| {
6835 this.buffer.update(cx, |buffer, cx| {
6836 buffer.edit(edits, None, cx);
6837 });
6838
6839 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6840 s.select(new_selections);
6841 });
6842
6843 this.request_autoscroll(Autoscroll::fit(), cx);
6844 });
6845 }
6846
6847 pub fn duplicate_line(&mut self, upwards: bool, cx: &mut ViewContext<Self>) {
6848 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6849 let buffer = &display_map.buffer_snapshot;
6850 let selections = self.selections.all::<Point>(cx);
6851
6852 let mut edits = Vec::new();
6853 let mut selections_iter = selections.iter().peekable();
6854 while let Some(selection) = selections_iter.next() {
6855 // Avoid duplicating the same lines twice.
6856 let mut rows = selection.spanned_rows(false, &display_map);
6857
6858 while let Some(next_selection) = selections_iter.peek() {
6859 let next_rows = next_selection.spanned_rows(false, &display_map);
6860 if next_rows.start < rows.end {
6861 rows.end = next_rows.end;
6862 selections_iter.next().unwrap();
6863 } else {
6864 break;
6865 }
6866 }
6867
6868 // Copy the text from the selected row region and splice it either at the start
6869 // or end of the region.
6870 let start = Point::new(rows.start.0, 0);
6871 let end = Point::new(
6872 rows.end.previous_row().0,
6873 buffer.line_len(rows.end.previous_row()),
6874 );
6875 let text = buffer
6876 .text_for_range(start..end)
6877 .chain(Some("\n"))
6878 .collect::<String>();
6879 let insert_location = if upwards {
6880 Point::new(rows.end.0, 0)
6881 } else {
6882 start
6883 };
6884 edits.push((insert_location..insert_location, text));
6885 }
6886
6887 self.transact(cx, |this, cx| {
6888 this.buffer.update(cx, |buffer, cx| {
6889 buffer.edit(edits, None, cx);
6890 });
6891
6892 this.request_autoscroll(Autoscroll::fit(), cx);
6893 });
6894 }
6895
6896 pub fn duplicate_line_up(&mut self, _: &DuplicateLineUp, cx: &mut ViewContext<Self>) {
6897 self.duplicate_line(true, cx);
6898 }
6899
6900 pub fn duplicate_line_down(&mut self, _: &DuplicateLineDown, cx: &mut ViewContext<Self>) {
6901 self.duplicate_line(false, cx);
6902 }
6903
6904 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
6905 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6906 let buffer = self.buffer.read(cx).snapshot(cx);
6907
6908 let mut edits = Vec::new();
6909 let mut unfold_ranges = Vec::new();
6910 let mut refold_creases = Vec::new();
6911
6912 let selections = self.selections.all::<Point>(cx);
6913 let mut selections = selections.iter().peekable();
6914 let mut contiguous_row_selections = Vec::new();
6915 let mut new_selections = Vec::new();
6916
6917 while let Some(selection) = selections.next() {
6918 // Find all the selections that span a contiguous row range
6919 let (start_row, end_row) = consume_contiguous_rows(
6920 &mut contiguous_row_selections,
6921 selection,
6922 &display_map,
6923 &mut selections,
6924 );
6925
6926 // Move the text spanned by the row range to be before the line preceding the row range
6927 if start_row.0 > 0 {
6928 let range_to_move = Point::new(
6929 start_row.previous_row().0,
6930 buffer.line_len(start_row.previous_row()),
6931 )
6932 ..Point::new(
6933 end_row.previous_row().0,
6934 buffer.line_len(end_row.previous_row()),
6935 );
6936 let insertion_point = display_map
6937 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
6938 .0;
6939
6940 // Don't move lines across excerpts
6941 if buffer
6942 .excerpt_boundaries_in_range((
6943 Bound::Excluded(insertion_point),
6944 Bound::Included(range_to_move.end),
6945 ))
6946 .next()
6947 .is_none()
6948 {
6949 let text = buffer
6950 .text_for_range(range_to_move.clone())
6951 .flat_map(|s| s.chars())
6952 .skip(1)
6953 .chain(['\n'])
6954 .collect::<String>();
6955
6956 edits.push((
6957 buffer.anchor_after(range_to_move.start)
6958 ..buffer.anchor_before(range_to_move.end),
6959 String::new(),
6960 ));
6961 let insertion_anchor = buffer.anchor_after(insertion_point);
6962 edits.push((insertion_anchor..insertion_anchor, text));
6963
6964 let row_delta = range_to_move.start.row - insertion_point.row + 1;
6965
6966 // Move selections up
6967 new_selections.extend(contiguous_row_selections.drain(..).map(
6968 |mut selection| {
6969 selection.start.row -= row_delta;
6970 selection.end.row -= row_delta;
6971 selection
6972 },
6973 ));
6974
6975 // Move folds up
6976 unfold_ranges.push(range_to_move.clone());
6977 for fold in display_map.folds_in_range(
6978 buffer.anchor_before(range_to_move.start)
6979 ..buffer.anchor_after(range_to_move.end),
6980 ) {
6981 let mut start = fold.range.start.to_point(&buffer);
6982 let mut end = fold.range.end.to_point(&buffer);
6983 start.row -= row_delta;
6984 end.row -= row_delta;
6985 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
6986 }
6987 }
6988 }
6989
6990 // If we didn't move line(s), preserve the existing selections
6991 new_selections.append(&mut contiguous_row_selections);
6992 }
6993
6994 self.transact(cx, |this, cx| {
6995 this.unfold_ranges(&unfold_ranges, true, true, cx);
6996 this.buffer.update(cx, |buffer, cx| {
6997 for (range, text) in edits {
6998 buffer.edit([(range, text)], None, cx);
6999 }
7000 });
7001 this.fold_creases(refold_creases, true, cx);
7002 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7003 s.select(new_selections);
7004 })
7005 });
7006 }
7007
7008 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
7009 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7010 let buffer = self.buffer.read(cx).snapshot(cx);
7011
7012 let mut edits = Vec::new();
7013 let mut unfold_ranges = Vec::new();
7014 let mut refold_creases = Vec::new();
7015
7016 let selections = self.selections.all::<Point>(cx);
7017 let mut selections = selections.iter().peekable();
7018 let mut contiguous_row_selections = Vec::new();
7019 let mut new_selections = Vec::new();
7020
7021 while let Some(selection) = selections.next() {
7022 // Find all the selections that span a contiguous row range
7023 let (start_row, end_row) = consume_contiguous_rows(
7024 &mut contiguous_row_selections,
7025 selection,
7026 &display_map,
7027 &mut selections,
7028 );
7029
7030 // Move the text spanned by the row range to be after the last line of the row range
7031 if end_row.0 <= buffer.max_point().row {
7032 let range_to_move =
7033 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
7034 let insertion_point = display_map
7035 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
7036 .0;
7037
7038 // Don't move lines across excerpt boundaries
7039 if buffer
7040 .excerpt_boundaries_in_range((
7041 Bound::Excluded(range_to_move.start),
7042 Bound::Included(insertion_point),
7043 ))
7044 .next()
7045 .is_none()
7046 {
7047 let mut text = String::from("\n");
7048 text.extend(buffer.text_for_range(range_to_move.clone()));
7049 text.pop(); // Drop trailing newline
7050 edits.push((
7051 buffer.anchor_after(range_to_move.start)
7052 ..buffer.anchor_before(range_to_move.end),
7053 String::new(),
7054 ));
7055 let insertion_anchor = buffer.anchor_after(insertion_point);
7056 edits.push((insertion_anchor..insertion_anchor, text));
7057
7058 let row_delta = insertion_point.row - range_to_move.end.row + 1;
7059
7060 // Move selections down
7061 new_selections.extend(contiguous_row_selections.drain(..).map(
7062 |mut selection| {
7063 selection.start.row += row_delta;
7064 selection.end.row += row_delta;
7065 selection
7066 },
7067 ));
7068
7069 // Move folds down
7070 unfold_ranges.push(range_to_move.clone());
7071 for fold in display_map.folds_in_range(
7072 buffer.anchor_before(range_to_move.start)
7073 ..buffer.anchor_after(range_to_move.end),
7074 ) {
7075 let mut start = fold.range.start.to_point(&buffer);
7076 let mut end = fold.range.end.to_point(&buffer);
7077 start.row += row_delta;
7078 end.row += row_delta;
7079 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
7080 }
7081 }
7082 }
7083
7084 // If we didn't move line(s), preserve the existing selections
7085 new_selections.append(&mut contiguous_row_selections);
7086 }
7087
7088 self.transact(cx, |this, cx| {
7089 this.unfold_ranges(&unfold_ranges, true, true, cx);
7090 this.buffer.update(cx, |buffer, cx| {
7091 for (range, text) in edits {
7092 buffer.edit([(range, text)], None, cx);
7093 }
7094 });
7095 this.fold_creases(refold_creases, true, cx);
7096 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
7097 });
7098 }
7099
7100 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
7101 let text_layout_details = &self.text_layout_details(cx);
7102 self.transact(cx, |this, cx| {
7103 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7104 let mut edits: Vec<(Range<usize>, String)> = Default::default();
7105 let line_mode = s.line_mode;
7106 s.move_with(|display_map, selection| {
7107 if !selection.is_empty() || line_mode {
7108 return;
7109 }
7110
7111 let mut head = selection.head();
7112 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
7113 if head.column() == display_map.line_len(head.row()) {
7114 transpose_offset = display_map
7115 .buffer_snapshot
7116 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
7117 }
7118
7119 if transpose_offset == 0 {
7120 return;
7121 }
7122
7123 *head.column_mut() += 1;
7124 head = display_map.clip_point(head, Bias::Right);
7125 let goal = SelectionGoal::HorizontalPosition(
7126 display_map
7127 .x_for_display_point(head, text_layout_details)
7128 .into(),
7129 );
7130 selection.collapse_to(head, goal);
7131
7132 let transpose_start = display_map
7133 .buffer_snapshot
7134 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
7135 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
7136 let transpose_end = display_map
7137 .buffer_snapshot
7138 .clip_offset(transpose_offset + 1, Bias::Right);
7139 if let Some(ch) =
7140 display_map.buffer_snapshot.chars_at(transpose_start).next()
7141 {
7142 edits.push((transpose_start..transpose_offset, String::new()));
7143 edits.push((transpose_end..transpose_end, ch.to_string()));
7144 }
7145 }
7146 });
7147 edits
7148 });
7149 this.buffer
7150 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
7151 let selections = this.selections.all::<usize>(cx);
7152 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7153 s.select(selections);
7154 });
7155 });
7156 }
7157
7158 pub fn rewrap(&mut self, _: &Rewrap, cx: &mut ViewContext<Self>) {
7159 self.rewrap_impl(IsVimMode::No, cx)
7160 }
7161
7162 pub fn rewrap_impl(&mut self, is_vim_mode: IsVimMode, cx: &mut ViewContext<Self>) {
7163 let buffer = self.buffer.read(cx).snapshot(cx);
7164 let selections = self.selections.all::<Point>(cx);
7165 let mut selections = selections.iter().peekable();
7166
7167 let mut edits = Vec::new();
7168 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
7169
7170 while let Some(selection) = selections.next() {
7171 let mut start_row = selection.start.row;
7172 let mut end_row = selection.end.row;
7173
7174 // Skip selections that overlap with a range that has already been rewrapped.
7175 let selection_range = start_row..end_row;
7176 if rewrapped_row_ranges
7177 .iter()
7178 .any(|range| range.overlaps(&selection_range))
7179 {
7180 continue;
7181 }
7182
7183 let mut should_rewrap = is_vim_mode == IsVimMode::Yes;
7184
7185 if let Some(language_scope) = buffer.language_scope_at(selection.head()) {
7186 match language_scope.language_name().0.as_ref() {
7187 "Markdown" | "Plain Text" => {
7188 should_rewrap = true;
7189 }
7190 _ => {}
7191 }
7192 }
7193
7194 let tab_size = buffer.settings_at(selection.head(), cx).tab_size;
7195
7196 // Since not all lines in the selection may be at the same indent
7197 // level, choose the indent size that is the most common between all
7198 // of the lines.
7199 //
7200 // If there is a tie, we use the deepest indent.
7201 let (indent_size, indent_end) = {
7202 let mut indent_size_occurrences = HashMap::default();
7203 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
7204
7205 for row in start_row..=end_row {
7206 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
7207 rows_by_indent_size.entry(indent).or_default().push(row);
7208 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
7209 }
7210
7211 let indent_size = indent_size_occurrences
7212 .into_iter()
7213 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
7214 .map(|(indent, _)| indent)
7215 .unwrap_or_default();
7216 let row = rows_by_indent_size[&indent_size][0];
7217 let indent_end = Point::new(row, indent_size.len);
7218
7219 (indent_size, indent_end)
7220 };
7221
7222 let mut line_prefix = indent_size.chars().collect::<String>();
7223
7224 if let Some(comment_prefix) =
7225 buffer
7226 .language_scope_at(selection.head())
7227 .and_then(|language| {
7228 language
7229 .line_comment_prefixes()
7230 .iter()
7231 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
7232 .cloned()
7233 })
7234 {
7235 line_prefix.push_str(&comment_prefix);
7236 should_rewrap = true;
7237 }
7238
7239 if !should_rewrap {
7240 continue;
7241 }
7242
7243 if selection.is_empty() {
7244 'expand_upwards: while start_row > 0 {
7245 let prev_row = start_row - 1;
7246 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
7247 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
7248 {
7249 start_row = prev_row;
7250 } else {
7251 break 'expand_upwards;
7252 }
7253 }
7254
7255 'expand_downwards: while end_row < buffer.max_point().row {
7256 let next_row = end_row + 1;
7257 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
7258 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
7259 {
7260 end_row = next_row;
7261 } else {
7262 break 'expand_downwards;
7263 }
7264 }
7265 }
7266
7267 let start = Point::new(start_row, 0);
7268 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
7269 let selection_text = buffer.text_for_range(start..end).collect::<String>();
7270 let Some(lines_without_prefixes) = selection_text
7271 .lines()
7272 .map(|line| {
7273 line.strip_prefix(&line_prefix)
7274 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
7275 .ok_or_else(|| {
7276 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
7277 })
7278 })
7279 .collect::<Result<Vec<_>, _>>()
7280 .log_err()
7281 else {
7282 continue;
7283 };
7284
7285 let wrap_column = buffer
7286 .settings_at(Point::new(start_row, 0), cx)
7287 .preferred_line_length as usize;
7288 let wrapped_text = wrap_with_prefix(
7289 line_prefix,
7290 lines_without_prefixes.join(" "),
7291 wrap_column,
7292 tab_size,
7293 );
7294
7295 // TODO: should always use char-based diff while still supporting cursor behavior that
7296 // matches vim.
7297 let diff = match is_vim_mode {
7298 IsVimMode::Yes => TextDiff::from_lines(&selection_text, &wrapped_text),
7299 IsVimMode::No => TextDiff::from_chars(&selection_text, &wrapped_text),
7300 };
7301 let mut offset = start.to_offset(&buffer);
7302 let mut moved_since_edit = true;
7303
7304 for change in diff.iter_all_changes() {
7305 let value = change.value();
7306 match change.tag() {
7307 ChangeTag::Equal => {
7308 offset += value.len();
7309 moved_since_edit = true;
7310 }
7311 ChangeTag::Delete => {
7312 let start = buffer.anchor_after(offset);
7313 let end = buffer.anchor_before(offset + value.len());
7314
7315 if moved_since_edit {
7316 edits.push((start..end, String::new()));
7317 } else {
7318 edits.last_mut().unwrap().0.end = end;
7319 }
7320
7321 offset += value.len();
7322 moved_since_edit = false;
7323 }
7324 ChangeTag::Insert => {
7325 if moved_since_edit {
7326 let anchor = buffer.anchor_after(offset);
7327 edits.push((anchor..anchor, value.to_string()));
7328 } else {
7329 edits.last_mut().unwrap().1.push_str(value);
7330 }
7331
7332 moved_since_edit = false;
7333 }
7334 }
7335 }
7336
7337 rewrapped_row_ranges.push(start_row..=end_row);
7338 }
7339
7340 self.buffer
7341 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
7342 }
7343
7344 pub fn cut_common(&mut self, cx: &mut ViewContext<Self>) -> ClipboardItem {
7345 let mut text = String::new();
7346 let buffer = self.buffer.read(cx).snapshot(cx);
7347 let mut selections = self.selections.all::<Point>(cx);
7348 let mut clipboard_selections = Vec::with_capacity(selections.len());
7349 {
7350 let max_point = buffer.max_point();
7351 let mut is_first = true;
7352 for selection in &mut selections {
7353 let is_entire_line = selection.is_empty() || self.selections.line_mode;
7354 if is_entire_line {
7355 selection.start = Point::new(selection.start.row, 0);
7356 if !selection.is_empty() && selection.end.column == 0 {
7357 selection.end = cmp::min(max_point, selection.end);
7358 } else {
7359 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
7360 }
7361 selection.goal = SelectionGoal::None;
7362 }
7363 if is_first {
7364 is_first = false;
7365 } else {
7366 text += "\n";
7367 }
7368 let mut len = 0;
7369 for chunk in buffer.text_for_range(selection.start..selection.end) {
7370 text.push_str(chunk);
7371 len += chunk.len();
7372 }
7373 clipboard_selections.push(ClipboardSelection {
7374 len,
7375 is_entire_line,
7376 first_line_indent: buffer
7377 .indent_size_for_line(MultiBufferRow(selection.start.row))
7378 .len,
7379 });
7380 }
7381 }
7382
7383 self.transact(cx, |this, cx| {
7384 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7385 s.select(selections);
7386 });
7387 this.insert("", cx);
7388 });
7389 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
7390 }
7391
7392 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
7393 let item = self.cut_common(cx);
7394 cx.write_to_clipboard(item);
7395 }
7396
7397 pub fn kill_ring_cut(&mut self, _: &KillRingCut, cx: &mut ViewContext<Self>) {
7398 self.change_selections(None, cx, |s| {
7399 s.move_with(|snapshot, sel| {
7400 if sel.is_empty() {
7401 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
7402 }
7403 });
7404 });
7405 let item = self.cut_common(cx);
7406 cx.set_global(KillRing(item))
7407 }
7408
7409 pub fn kill_ring_yank(&mut self, _: &KillRingYank, cx: &mut ViewContext<Self>) {
7410 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
7411 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
7412 (kill_ring.text().to_string(), kill_ring.metadata_json())
7413 } else {
7414 return;
7415 }
7416 } else {
7417 return;
7418 };
7419 self.do_paste(&text, metadata, false, cx);
7420 }
7421
7422 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
7423 let selections = self.selections.all::<Point>(cx);
7424 let buffer = self.buffer.read(cx).read(cx);
7425 let mut text = String::new();
7426
7427 let mut clipboard_selections = Vec::with_capacity(selections.len());
7428 {
7429 let max_point = buffer.max_point();
7430 let mut is_first = true;
7431 for selection in selections.iter() {
7432 let mut start = selection.start;
7433 let mut end = selection.end;
7434 let is_entire_line = selection.is_empty() || self.selections.line_mode;
7435 if is_entire_line {
7436 start = Point::new(start.row, 0);
7437 end = cmp::min(max_point, Point::new(end.row + 1, 0));
7438 }
7439 if is_first {
7440 is_first = false;
7441 } else {
7442 text += "\n";
7443 }
7444 let mut len = 0;
7445 for chunk in buffer.text_for_range(start..end) {
7446 text.push_str(chunk);
7447 len += chunk.len();
7448 }
7449 clipboard_selections.push(ClipboardSelection {
7450 len,
7451 is_entire_line,
7452 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
7453 });
7454 }
7455 }
7456
7457 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
7458 text,
7459 clipboard_selections,
7460 ));
7461 }
7462
7463 pub fn do_paste(
7464 &mut self,
7465 text: &String,
7466 clipboard_selections: Option<Vec<ClipboardSelection>>,
7467 handle_entire_lines: bool,
7468 cx: &mut ViewContext<Self>,
7469 ) {
7470 if self.read_only(cx) {
7471 return;
7472 }
7473
7474 let clipboard_text = Cow::Borrowed(text);
7475
7476 self.transact(cx, |this, cx| {
7477 if let Some(mut clipboard_selections) = clipboard_selections {
7478 let old_selections = this.selections.all::<usize>(cx);
7479 let all_selections_were_entire_line =
7480 clipboard_selections.iter().all(|s| s.is_entire_line);
7481 let first_selection_indent_column =
7482 clipboard_selections.first().map(|s| s.first_line_indent);
7483 if clipboard_selections.len() != old_selections.len() {
7484 clipboard_selections.drain(..);
7485 }
7486 let cursor_offset = this.selections.last::<usize>(cx).head();
7487 let mut auto_indent_on_paste = true;
7488
7489 this.buffer.update(cx, |buffer, cx| {
7490 let snapshot = buffer.read(cx);
7491 auto_indent_on_paste =
7492 snapshot.settings_at(cursor_offset, cx).auto_indent_on_paste;
7493
7494 let mut start_offset = 0;
7495 let mut edits = Vec::new();
7496 let mut original_indent_columns = Vec::new();
7497 for (ix, selection) in old_selections.iter().enumerate() {
7498 let to_insert;
7499 let entire_line;
7500 let original_indent_column;
7501 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
7502 let end_offset = start_offset + clipboard_selection.len;
7503 to_insert = &clipboard_text[start_offset..end_offset];
7504 entire_line = clipboard_selection.is_entire_line;
7505 start_offset = end_offset + 1;
7506 original_indent_column = Some(clipboard_selection.first_line_indent);
7507 } else {
7508 to_insert = clipboard_text.as_str();
7509 entire_line = all_selections_were_entire_line;
7510 original_indent_column = first_selection_indent_column
7511 }
7512
7513 // If the corresponding selection was empty when this slice of the
7514 // clipboard text was written, then the entire line containing the
7515 // selection was copied. If this selection is also currently empty,
7516 // then paste the line before the current line of the buffer.
7517 let range = if selection.is_empty() && handle_entire_lines && entire_line {
7518 let column = selection.start.to_point(&snapshot).column as usize;
7519 let line_start = selection.start - column;
7520 line_start..line_start
7521 } else {
7522 selection.range()
7523 };
7524
7525 edits.push((range, to_insert));
7526 original_indent_columns.extend(original_indent_column);
7527 }
7528 drop(snapshot);
7529
7530 buffer.edit(
7531 edits,
7532 if auto_indent_on_paste {
7533 Some(AutoindentMode::Block {
7534 original_indent_columns,
7535 })
7536 } else {
7537 None
7538 },
7539 cx,
7540 );
7541 });
7542
7543 let selections = this.selections.all::<usize>(cx);
7544 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
7545 } else {
7546 this.insert(&clipboard_text, cx);
7547 }
7548 });
7549 }
7550
7551 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
7552 if let Some(item) = cx.read_from_clipboard() {
7553 let entries = item.entries();
7554
7555 match entries.first() {
7556 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
7557 // of all the pasted entries.
7558 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
7559 .do_paste(
7560 clipboard_string.text(),
7561 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
7562 true,
7563 cx,
7564 ),
7565 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, cx),
7566 }
7567 }
7568 }
7569
7570 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
7571 if self.read_only(cx) {
7572 return;
7573 }
7574
7575 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
7576 if let Some((selections, _)) =
7577 self.selection_history.transaction(transaction_id).cloned()
7578 {
7579 self.change_selections(None, cx, |s| {
7580 s.select_anchors(selections.to_vec());
7581 });
7582 }
7583 self.request_autoscroll(Autoscroll::fit(), cx);
7584 self.unmark_text(cx);
7585 self.refresh_inline_completion(true, false, cx);
7586 cx.emit(EditorEvent::Edited { transaction_id });
7587 cx.emit(EditorEvent::TransactionUndone { transaction_id });
7588 }
7589 }
7590
7591 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
7592 if self.read_only(cx) {
7593 return;
7594 }
7595
7596 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
7597 if let Some((_, Some(selections))) =
7598 self.selection_history.transaction(transaction_id).cloned()
7599 {
7600 self.change_selections(None, cx, |s| {
7601 s.select_anchors(selections.to_vec());
7602 });
7603 }
7604 self.request_autoscroll(Autoscroll::fit(), cx);
7605 self.unmark_text(cx);
7606 self.refresh_inline_completion(true, false, cx);
7607 cx.emit(EditorEvent::Edited { transaction_id });
7608 }
7609 }
7610
7611 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
7612 self.buffer
7613 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
7614 }
7615
7616 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut ViewContext<Self>) {
7617 self.buffer
7618 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
7619 }
7620
7621 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
7622 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7623 let line_mode = s.line_mode;
7624 s.move_with(|map, selection| {
7625 let cursor = if selection.is_empty() && !line_mode {
7626 movement::left(map, selection.start)
7627 } else {
7628 selection.start
7629 };
7630 selection.collapse_to(cursor, SelectionGoal::None);
7631 });
7632 })
7633 }
7634
7635 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
7636 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7637 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
7638 })
7639 }
7640
7641 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
7642 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7643 let line_mode = s.line_mode;
7644 s.move_with(|map, selection| {
7645 let cursor = if selection.is_empty() && !line_mode {
7646 movement::right(map, selection.end)
7647 } else {
7648 selection.end
7649 };
7650 selection.collapse_to(cursor, SelectionGoal::None)
7651 });
7652 })
7653 }
7654
7655 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
7656 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7657 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
7658 })
7659 }
7660
7661 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
7662 if self.take_rename(true, cx).is_some() {
7663 return;
7664 }
7665
7666 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7667 cx.propagate();
7668 return;
7669 }
7670
7671 let text_layout_details = &self.text_layout_details(cx);
7672 let selection_count = self.selections.count();
7673 let first_selection = self.selections.first_anchor();
7674
7675 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7676 let line_mode = s.line_mode;
7677 s.move_with(|map, selection| {
7678 if !selection.is_empty() && !line_mode {
7679 selection.goal = SelectionGoal::None;
7680 }
7681 let (cursor, goal) = movement::up(
7682 map,
7683 selection.start,
7684 selection.goal,
7685 false,
7686 text_layout_details,
7687 );
7688 selection.collapse_to(cursor, goal);
7689 });
7690 });
7691
7692 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7693 {
7694 cx.propagate();
7695 }
7696 }
7697
7698 pub fn move_up_by_lines(&mut self, action: &MoveUpByLines, cx: &mut ViewContext<Self>) {
7699 if self.take_rename(true, cx).is_some() {
7700 return;
7701 }
7702
7703 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7704 cx.propagate();
7705 return;
7706 }
7707
7708 let text_layout_details = &self.text_layout_details(cx);
7709
7710 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7711 let line_mode = s.line_mode;
7712 s.move_with(|map, selection| {
7713 if !selection.is_empty() && !line_mode {
7714 selection.goal = SelectionGoal::None;
7715 }
7716 let (cursor, goal) = movement::up_by_rows(
7717 map,
7718 selection.start,
7719 action.lines,
7720 selection.goal,
7721 false,
7722 text_layout_details,
7723 );
7724 selection.collapse_to(cursor, goal);
7725 });
7726 })
7727 }
7728
7729 pub fn move_down_by_lines(&mut self, action: &MoveDownByLines, cx: &mut ViewContext<Self>) {
7730 if self.take_rename(true, cx).is_some() {
7731 return;
7732 }
7733
7734 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7735 cx.propagate();
7736 return;
7737 }
7738
7739 let text_layout_details = &self.text_layout_details(cx);
7740
7741 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7742 let line_mode = s.line_mode;
7743 s.move_with(|map, selection| {
7744 if !selection.is_empty() && !line_mode {
7745 selection.goal = SelectionGoal::None;
7746 }
7747 let (cursor, goal) = movement::down_by_rows(
7748 map,
7749 selection.start,
7750 action.lines,
7751 selection.goal,
7752 false,
7753 text_layout_details,
7754 );
7755 selection.collapse_to(cursor, goal);
7756 });
7757 })
7758 }
7759
7760 pub fn select_down_by_lines(&mut self, action: &SelectDownByLines, cx: &mut ViewContext<Self>) {
7761 let text_layout_details = &self.text_layout_details(cx);
7762 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7763 s.move_heads_with(|map, head, goal| {
7764 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
7765 })
7766 })
7767 }
7768
7769 pub fn select_up_by_lines(&mut self, action: &SelectUpByLines, cx: &mut ViewContext<Self>) {
7770 let text_layout_details = &self.text_layout_details(cx);
7771 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7772 s.move_heads_with(|map, head, goal| {
7773 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
7774 })
7775 })
7776 }
7777
7778 pub fn select_page_up(&mut self, _: &SelectPageUp, cx: &mut ViewContext<Self>) {
7779 let Some(row_count) = self.visible_row_count() else {
7780 return;
7781 };
7782
7783 let text_layout_details = &self.text_layout_details(cx);
7784
7785 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7786 s.move_heads_with(|map, head, goal| {
7787 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
7788 })
7789 })
7790 }
7791
7792 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
7793 if self.take_rename(true, cx).is_some() {
7794 return;
7795 }
7796
7797 if self
7798 .context_menu
7799 .write()
7800 .as_mut()
7801 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
7802 .unwrap_or(false)
7803 {
7804 return;
7805 }
7806
7807 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7808 cx.propagate();
7809 return;
7810 }
7811
7812 let Some(row_count) = self.visible_row_count() else {
7813 return;
7814 };
7815
7816 let autoscroll = if action.center_cursor {
7817 Autoscroll::center()
7818 } else {
7819 Autoscroll::fit()
7820 };
7821
7822 let text_layout_details = &self.text_layout_details(cx);
7823
7824 self.change_selections(Some(autoscroll), cx, |s| {
7825 let line_mode = s.line_mode;
7826 s.move_with(|map, selection| {
7827 if !selection.is_empty() && !line_mode {
7828 selection.goal = SelectionGoal::None;
7829 }
7830 let (cursor, goal) = movement::up_by_rows(
7831 map,
7832 selection.end,
7833 row_count,
7834 selection.goal,
7835 false,
7836 text_layout_details,
7837 );
7838 selection.collapse_to(cursor, goal);
7839 });
7840 });
7841 }
7842
7843 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
7844 let text_layout_details = &self.text_layout_details(cx);
7845 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7846 s.move_heads_with(|map, head, goal| {
7847 movement::up(map, head, goal, false, text_layout_details)
7848 })
7849 })
7850 }
7851
7852 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
7853 self.take_rename(true, cx);
7854
7855 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7856 cx.propagate();
7857 return;
7858 }
7859
7860 let text_layout_details = &self.text_layout_details(cx);
7861 let selection_count = self.selections.count();
7862 let first_selection = self.selections.first_anchor();
7863
7864 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7865 let line_mode = s.line_mode;
7866 s.move_with(|map, selection| {
7867 if !selection.is_empty() && !line_mode {
7868 selection.goal = SelectionGoal::None;
7869 }
7870 let (cursor, goal) = movement::down(
7871 map,
7872 selection.end,
7873 selection.goal,
7874 false,
7875 text_layout_details,
7876 );
7877 selection.collapse_to(cursor, goal);
7878 });
7879 });
7880
7881 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7882 {
7883 cx.propagate();
7884 }
7885 }
7886
7887 pub fn select_page_down(&mut self, _: &SelectPageDown, cx: &mut ViewContext<Self>) {
7888 let Some(row_count) = self.visible_row_count() else {
7889 return;
7890 };
7891
7892 let text_layout_details = &self.text_layout_details(cx);
7893
7894 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7895 s.move_heads_with(|map, head, goal| {
7896 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
7897 })
7898 })
7899 }
7900
7901 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
7902 if self.take_rename(true, cx).is_some() {
7903 return;
7904 }
7905
7906 if self
7907 .context_menu
7908 .write()
7909 .as_mut()
7910 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
7911 .unwrap_or(false)
7912 {
7913 return;
7914 }
7915
7916 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7917 cx.propagate();
7918 return;
7919 }
7920
7921 let Some(row_count) = self.visible_row_count() else {
7922 return;
7923 };
7924
7925 let autoscroll = if action.center_cursor {
7926 Autoscroll::center()
7927 } else {
7928 Autoscroll::fit()
7929 };
7930
7931 let text_layout_details = &self.text_layout_details(cx);
7932 self.change_selections(Some(autoscroll), cx, |s| {
7933 let line_mode = s.line_mode;
7934 s.move_with(|map, selection| {
7935 if !selection.is_empty() && !line_mode {
7936 selection.goal = SelectionGoal::None;
7937 }
7938 let (cursor, goal) = movement::down_by_rows(
7939 map,
7940 selection.end,
7941 row_count,
7942 selection.goal,
7943 false,
7944 text_layout_details,
7945 );
7946 selection.collapse_to(cursor, goal);
7947 });
7948 });
7949 }
7950
7951 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
7952 let text_layout_details = &self.text_layout_details(cx);
7953 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7954 s.move_heads_with(|map, head, goal| {
7955 movement::down(map, head, goal, false, text_layout_details)
7956 })
7957 });
7958 }
7959
7960 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
7961 if let Some(context_menu) = self.context_menu.write().as_mut() {
7962 context_menu.select_first(self.completion_provider.as_deref(), cx);
7963 }
7964 }
7965
7966 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
7967 if let Some(context_menu) = self.context_menu.write().as_mut() {
7968 context_menu.select_prev(self.completion_provider.as_deref(), cx);
7969 }
7970 }
7971
7972 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
7973 if let Some(context_menu) = self.context_menu.write().as_mut() {
7974 context_menu.select_next(self.completion_provider.as_deref(), cx);
7975 }
7976 }
7977
7978 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
7979 if let Some(context_menu) = self.context_menu.write().as_mut() {
7980 context_menu.select_last(self.completion_provider.as_deref(), cx);
7981 }
7982 }
7983
7984 pub fn move_to_previous_word_start(
7985 &mut self,
7986 _: &MoveToPreviousWordStart,
7987 cx: &mut ViewContext<Self>,
7988 ) {
7989 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7990 s.move_cursors_with(|map, head, _| {
7991 (
7992 movement::previous_word_start(map, head),
7993 SelectionGoal::None,
7994 )
7995 });
7996 })
7997 }
7998
7999 pub fn move_to_previous_subword_start(
8000 &mut self,
8001 _: &MoveToPreviousSubwordStart,
8002 cx: &mut ViewContext<Self>,
8003 ) {
8004 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8005 s.move_cursors_with(|map, head, _| {
8006 (
8007 movement::previous_subword_start(map, head),
8008 SelectionGoal::None,
8009 )
8010 });
8011 })
8012 }
8013
8014 pub fn select_to_previous_word_start(
8015 &mut self,
8016 _: &SelectToPreviousWordStart,
8017 cx: &mut ViewContext<Self>,
8018 ) {
8019 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8020 s.move_heads_with(|map, head, _| {
8021 (
8022 movement::previous_word_start(map, head),
8023 SelectionGoal::None,
8024 )
8025 });
8026 })
8027 }
8028
8029 pub fn select_to_previous_subword_start(
8030 &mut self,
8031 _: &SelectToPreviousSubwordStart,
8032 cx: &mut ViewContext<Self>,
8033 ) {
8034 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8035 s.move_heads_with(|map, head, _| {
8036 (
8037 movement::previous_subword_start(map, head),
8038 SelectionGoal::None,
8039 )
8040 });
8041 })
8042 }
8043
8044 pub fn delete_to_previous_word_start(
8045 &mut self,
8046 action: &DeleteToPreviousWordStart,
8047 cx: &mut ViewContext<Self>,
8048 ) {
8049 self.transact(cx, |this, cx| {
8050 this.select_autoclose_pair(cx);
8051 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8052 let line_mode = s.line_mode;
8053 s.move_with(|map, selection| {
8054 if selection.is_empty() && !line_mode {
8055 let cursor = if action.ignore_newlines {
8056 movement::previous_word_start(map, selection.head())
8057 } else {
8058 movement::previous_word_start_or_newline(map, selection.head())
8059 };
8060 selection.set_head(cursor, SelectionGoal::None);
8061 }
8062 });
8063 });
8064 this.insert("", cx);
8065 });
8066 }
8067
8068 pub fn delete_to_previous_subword_start(
8069 &mut self,
8070 _: &DeleteToPreviousSubwordStart,
8071 cx: &mut ViewContext<Self>,
8072 ) {
8073 self.transact(cx, |this, cx| {
8074 this.select_autoclose_pair(cx);
8075 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8076 let line_mode = s.line_mode;
8077 s.move_with(|map, selection| {
8078 if selection.is_empty() && !line_mode {
8079 let cursor = movement::previous_subword_start(map, selection.head());
8080 selection.set_head(cursor, SelectionGoal::None);
8081 }
8082 });
8083 });
8084 this.insert("", cx);
8085 });
8086 }
8087
8088 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
8089 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8090 s.move_cursors_with(|map, head, _| {
8091 (movement::next_word_end(map, head), SelectionGoal::None)
8092 });
8093 })
8094 }
8095
8096 pub fn move_to_next_subword_end(
8097 &mut self,
8098 _: &MoveToNextSubwordEnd,
8099 cx: &mut ViewContext<Self>,
8100 ) {
8101 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8102 s.move_cursors_with(|map, head, _| {
8103 (movement::next_subword_end(map, head), SelectionGoal::None)
8104 });
8105 })
8106 }
8107
8108 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
8109 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8110 s.move_heads_with(|map, head, _| {
8111 (movement::next_word_end(map, head), SelectionGoal::None)
8112 });
8113 })
8114 }
8115
8116 pub fn select_to_next_subword_end(
8117 &mut self,
8118 _: &SelectToNextSubwordEnd,
8119 cx: &mut ViewContext<Self>,
8120 ) {
8121 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8122 s.move_heads_with(|map, head, _| {
8123 (movement::next_subword_end(map, head), SelectionGoal::None)
8124 });
8125 })
8126 }
8127
8128 pub fn delete_to_next_word_end(
8129 &mut self,
8130 action: &DeleteToNextWordEnd,
8131 cx: &mut ViewContext<Self>,
8132 ) {
8133 self.transact(cx, |this, cx| {
8134 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8135 let line_mode = s.line_mode;
8136 s.move_with(|map, selection| {
8137 if selection.is_empty() && !line_mode {
8138 let cursor = if action.ignore_newlines {
8139 movement::next_word_end(map, selection.head())
8140 } else {
8141 movement::next_word_end_or_newline(map, selection.head())
8142 };
8143 selection.set_head(cursor, SelectionGoal::None);
8144 }
8145 });
8146 });
8147 this.insert("", cx);
8148 });
8149 }
8150
8151 pub fn delete_to_next_subword_end(
8152 &mut self,
8153 _: &DeleteToNextSubwordEnd,
8154 cx: &mut ViewContext<Self>,
8155 ) {
8156 self.transact(cx, |this, cx| {
8157 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8158 s.move_with(|map, selection| {
8159 if selection.is_empty() {
8160 let cursor = movement::next_subword_end(map, selection.head());
8161 selection.set_head(cursor, SelectionGoal::None);
8162 }
8163 });
8164 });
8165 this.insert("", cx);
8166 });
8167 }
8168
8169 pub fn move_to_beginning_of_line(
8170 &mut self,
8171 action: &MoveToBeginningOfLine,
8172 cx: &mut ViewContext<Self>,
8173 ) {
8174 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8175 s.move_cursors_with(|map, head, _| {
8176 (
8177 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
8178 SelectionGoal::None,
8179 )
8180 });
8181 })
8182 }
8183
8184 pub fn select_to_beginning_of_line(
8185 &mut self,
8186 action: &SelectToBeginningOfLine,
8187 cx: &mut ViewContext<Self>,
8188 ) {
8189 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8190 s.move_heads_with(|map, head, _| {
8191 (
8192 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
8193 SelectionGoal::None,
8194 )
8195 });
8196 });
8197 }
8198
8199 pub fn delete_to_beginning_of_line(
8200 &mut self,
8201 _: &DeleteToBeginningOfLine,
8202 cx: &mut ViewContext<Self>,
8203 ) {
8204 self.transact(cx, |this, cx| {
8205 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8206 s.move_with(|_, selection| {
8207 selection.reversed = true;
8208 });
8209 });
8210
8211 this.select_to_beginning_of_line(
8212 &SelectToBeginningOfLine {
8213 stop_at_soft_wraps: false,
8214 },
8215 cx,
8216 );
8217 this.backspace(&Backspace, cx);
8218 });
8219 }
8220
8221 pub fn move_to_end_of_line(&mut self, action: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
8222 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8223 s.move_cursors_with(|map, head, _| {
8224 (
8225 movement::line_end(map, head, action.stop_at_soft_wraps),
8226 SelectionGoal::None,
8227 )
8228 });
8229 })
8230 }
8231
8232 pub fn select_to_end_of_line(
8233 &mut self,
8234 action: &SelectToEndOfLine,
8235 cx: &mut ViewContext<Self>,
8236 ) {
8237 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8238 s.move_heads_with(|map, head, _| {
8239 (
8240 movement::line_end(map, head, action.stop_at_soft_wraps),
8241 SelectionGoal::None,
8242 )
8243 });
8244 })
8245 }
8246
8247 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
8248 self.transact(cx, |this, cx| {
8249 this.select_to_end_of_line(
8250 &SelectToEndOfLine {
8251 stop_at_soft_wraps: false,
8252 },
8253 cx,
8254 );
8255 this.delete(&Delete, cx);
8256 });
8257 }
8258
8259 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
8260 self.transact(cx, |this, cx| {
8261 this.select_to_end_of_line(
8262 &SelectToEndOfLine {
8263 stop_at_soft_wraps: false,
8264 },
8265 cx,
8266 );
8267 this.cut(&Cut, cx);
8268 });
8269 }
8270
8271 pub fn move_to_start_of_paragraph(
8272 &mut self,
8273 _: &MoveToStartOfParagraph,
8274 cx: &mut ViewContext<Self>,
8275 ) {
8276 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8277 cx.propagate();
8278 return;
8279 }
8280
8281 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8282 s.move_with(|map, selection| {
8283 selection.collapse_to(
8284 movement::start_of_paragraph(map, selection.head(), 1),
8285 SelectionGoal::None,
8286 )
8287 });
8288 })
8289 }
8290
8291 pub fn move_to_end_of_paragraph(
8292 &mut self,
8293 _: &MoveToEndOfParagraph,
8294 cx: &mut ViewContext<Self>,
8295 ) {
8296 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8297 cx.propagate();
8298 return;
8299 }
8300
8301 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8302 s.move_with(|map, selection| {
8303 selection.collapse_to(
8304 movement::end_of_paragraph(map, selection.head(), 1),
8305 SelectionGoal::None,
8306 )
8307 });
8308 })
8309 }
8310
8311 pub fn select_to_start_of_paragraph(
8312 &mut self,
8313 _: &SelectToStartOfParagraph,
8314 cx: &mut ViewContext<Self>,
8315 ) {
8316 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8317 cx.propagate();
8318 return;
8319 }
8320
8321 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8322 s.move_heads_with(|map, head, _| {
8323 (
8324 movement::start_of_paragraph(map, head, 1),
8325 SelectionGoal::None,
8326 )
8327 });
8328 })
8329 }
8330
8331 pub fn select_to_end_of_paragraph(
8332 &mut self,
8333 _: &SelectToEndOfParagraph,
8334 cx: &mut ViewContext<Self>,
8335 ) {
8336 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8337 cx.propagate();
8338 return;
8339 }
8340
8341 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8342 s.move_heads_with(|map, head, _| {
8343 (
8344 movement::end_of_paragraph(map, head, 1),
8345 SelectionGoal::None,
8346 )
8347 });
8348 })
8349 }
8350
8351 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
8352 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8353 cx.propagate();
8354 return;
8355 }
8356
8357 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8358 s.select_ranges(vec![0..0]);
8359 });
8360 }
8361
8362 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
8363 let mut selection = self.selections.last::<Point>(cx);
8364 selection.set_head(Point::zero(), SelectionGoal::None);
8365
8366 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8367 s.select(vec![selection]);
8368 });
8369 }
8370
8371 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
8372 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8373 cx.propagate();
8374 return;
8375 }
8376
8377 let cursor = self.buffer.read(cx).read(cx).len();
8378 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8379 s.select_ranges(vec![cursor..cursor])
8380 });
8381 }
8382
8383 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
8384 self.nav_history = nav_history;
8385 }
8386
8387 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
8388 self.nav_history.as_ref()
8389 }
8390
8391 fn push_to_nav_history(
8392 &mut self,
8393 cursor_anchor: Anchor,
8394 new_position: Option<Point>,
8395 cx: &mut ViewContext<Self>,
8396 ) {
8397 if let Some(nav_history) = self.nav_history.as_mut() {
8398 let buffer = self.buffer.read(cx).read(cx);
8399 let cursor_position = cursor_anchor.to_point(&buffer);
8400 let scroll_state = self.scroll_manager.anchor();
8401 let scroll_top_row = scroll_state.top_row(&buffer);
8402 drop(buffer);
8403
8404 if let Some(new_position) = new_position {
8405 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
8406 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
8407 return;
8408 }
8409 }
8410
8411 nav_history.push(
8412 Some(NavigationData {
8413 cursor_anchor,
8414 cursor_position,
8415 scroll_anchor: scroll_state,
8416 scroll_top_row,
8417 }),
8418 cx,
8419 );
8420 }
8421 }
8422
8423 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
8424 let buffer = self.buffer.read(cx).snapshot(cx);
8425 let mut selection = self.selections.first::<usize>(cx);
8426 selection.set_head(buffer.len(), SelectionGoal::None);
8427 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8428 s.select(vec![selection]);
8429 });
8430 }
8431
8432 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
8433 let end = self.buffer.read(cx).read(cx).len();
8434 self.change_selections(None, cx, |s| {
8435 s.select_ranges(vec![0..end]);
8436 });
8437 }
8438
8439 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
8440 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8441 let mut selections = self.selections.all::<Point>(cx);
8442 let max_point = display_map.buffer_snapshot.max_point();
8443 for selection in &mut selections {
8444 let rows = selection.spanned_rows(true, &display_map);
8445 selection.start = Point::new(rows.start.0, 0);
8446 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
8447 selection.reversed = false;
8448 }
8449 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8450 s.select(selections);
8451 });
8452 }
8453
8454 pub fn split_selection_into_lines(
8455 &mut self,
8456 _: &SplitSelectionIntoLines,
8457 cx: &mut ViewContext<Self>,
8458 ) {
8459 let mut to_unfold = Vec::new();
8460 let mut new_selection_ranges = Vec::new();
8461 {
8462 let selections = self.selections.all::<Point>(cx);
8463 let buffer = self.buffer.read(cx).read(cx);
8464 for selection in selections {
8465 for row in selection.start.row..selection.end.row {
8466 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
8467 new_selection_ranges.push(cursor..cursor);
8468 }
8469 new_selection_ranges.push(selection.end..selection.end);
8470 to_unfold.push(selection.start..selection.end);
8471 }
8472 }
8473 self.unfold_ranges(&to_unfold, true, true, cx);
8474 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8475 s.select_ranges(new_selection_ranges);
8476 });
8477 }
8478
8479 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
8480 self.add_selection(true, cx);
8481 }
8482
8483 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
8484 self.add_selection(false, cx);
8485 }
8486
8487 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
8488 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8489 let mut selections = self.selections.all::<Point>(cx);
8490 let text_layout_details = self.text_layout_details(cx);
8491 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
8492 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
8493 let range = oldest_selection.display_range(&display_map).sorted();
8494
8495 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
8496 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
8497 let positions = start_x.min(end_x)..start_x.max(end_x);
8498
8499 selections.clear();
8500 let mut stack = Vec::new();
8501 for row in range.start.row().0..=range.end.row().0 {
8502 if let Some(selection) = self.selections.build_columnar_selection(
8503 &display_map,
8504 DisplayRow(row),
8505 &positions,
8506 oldest_selection.reversed,
8507 &text_layout_details,
8508 ) {
8509 stack.push(selection.id);
8510 selections.push(selection);
8511 }
8512 }
8513
8514 if above {
8515 stack.reverse();
8516 }
8517
8518 AddSelectionsState { above, stack }
8519 });
8520
8521 let last_added_selection = *state.stack.last().unwrap();
8522 let mut new_selections = Vec::new();
8523 if above == state.above {
8524 let end_row = if above {
8525 DisplayRow(0)
8526 } else {
8527 display_map.max_point().row()
8528 };
8529
8530 'outer: for selection in selections {
8531 if selection.id == last_added_selection {
8532 let range = selection.display_range(&display_map).sorted();
8533 debug_assert_eq!(range.start.row(), range.end.row());
8534 let mut row = range.start.row();
8535 let positions =
8536 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
8537 px(start)..px(end)
8538 } else {
8539 let start_x =
8540 display_map.x_for_display_point(range.start, &text_layout_details);
8541 let end_x =
8542 display_map.x_for_display_point(range.end, &text_layout_details);
8543 start_x.min(end_x)..start_x.max(end_x)
8544 };
8545
8546 while row != end_row {
8547 if above {
8548 row.0 -= 1;
8549 } else {
8550 row.0 += 1;
8551 }
8552
8553 if let Some(new_selection) = self.selections.build_columnar_selection(
8554 &display_map,
8555 row,
8556 &positions,
8557 selection.reversed,
8558 &text_layout_details,
8559 ) {
8560 state.stack.push(new_selection.id);
8561 if above {
8562 new_selections.push(new_selection);
8563 new_selections.push(selection);
8564 } else {
8565 new_selections.push(selection);
8566 new_selections.push(new_selection);
8567 }
8568
8569 continue 'outer;
8570 }
8571 }
8572 }
8573
8574 new_selections.push(selection);
8575 }
8576 } else {
8577 new_selections = selections;
8578 new_selections.retain(|s| s.id != last_added_selection);
8579 state.stack.pop();
8580 }
8581
8582 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8583 s.select(new_selections);
8584 });
8585 if state.stack.len() > 1 {
8586 self.add_selections_state = Some(state);
8587 }
8588 }
8589
8590 pub fn select_next_match_internal(
8591 &mut self,
8592 display_map: &DisplaySnapshot,
8593 replace_newest: bool,
8594 autoscroll: Option<Autoscroll>,
8595 cx: &mut ViewContext<Self>,
8596 ) -> Result<()> {
8597 fn select_next_match_ranges(
8598 this: &mut Editor,
8599 range: Range<usize>,
8600 replace_newest: bool,
8601 auto_scroll: Option<Autoscroll>,
8602 cx: &mut ViewContext<Editor>,
8603 ) {
8604 this.unfold_ranges(&[range.clone()], false, true, cx);
8605 this.change_selections(auto_scroll, cx, |s| {
8606 if replace_newest {
8607 s.delete(s.newest_anchor().id);
8608 }
8609 s.insert_range(range.clone());
8610 });
8611 }
8612
8613 let buffer = &display_map.buffer_snapshot;
8614 let mut selections = self.selections.all::<usize>(cx);
8615 if let Some(mut select_next_state) = self.select_next_state.take() {
8616 let query = &select_next_state.query;
8617 if !select_next_state.done {
8618 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
8619 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
8620 let mut next_selected_range = None;
8621
8622 let bytes_after_last_selection =
8623 buffer.bytes_in_range(last_selection.end..buffer.len());
8624 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
8625 let query_matches = query
8626 .stream_find_iter(bytes_after_last_selection)
8627 .map(|result| (last_selection.end, result))
8628 .chain(
8629 query
8630 .stream_find_iter(bytes_before_first_selection)
8631 .map(|result| (0, result)),
8632 );
8633
8634 for (start_offset, query_match) in query_matches {
8635 let query_match = query_match.unwrap(); // can only fail due to I/O
8636 let offset_range =
8637 start_offset + query_match.start()..start_offset + query_match.end();
8638 let display_range = offset_range.start.to_display_point(display_map)
8639 ..offset_range.end.to_display_point(display_map);
8640
8641 if !select_next_state.wordwise
8642 || (!movement::is_inside_word(display_map, display_range.start)
8643 && !movement::is_inside_word(display_map, display_range.end))
8644 {
8645 // TODO: This is n^2, because we might check all the selections
8646 if !selections
8647 .iter()
8648 .any(|selection| selection.range().overlaps(&offset_range))
8649 {
8650 next_selected_range = Some(offset_range);
8651 break;
8652 }
8653 }
8654 }
8655
8656 if let Some(next_selected_range) = next_selected_range {
8657 select_next_match_ranges(
8658 self,
8659 next_selected_range,
8660 replace_newest,
8661 autoscroll,
8662 cx,
8663 );
8664 } else {
8665 select_next_state.done = true;
8666 }
8667 }
8668
8669 self.select_next_state = Some(select_next_state);
8670 } else {
8671 let mut only_carets = true;
8672 let mut same_text_selected = true;
8673 let mut selected_text = None;
8674
8675 let mut selections_iter = selections.iter().peekable();
8676 while let Some(selection) = selections_iter.next() {
8677 if selection.start != selection.end {
8678 only_carets = false;
8679 }
8680
8681 if same_text_selected {
8682 if selected_text.is_none() {
8683 selected_text =
8684 Some(buffer.text_for_range(selection.range()).collect::<String>());
8685 }
8686
8687 if let Some(next_selection) = selections_iter.peek() {
8688 if next_selection.range().len() == selection.range().len() {
8689 let next_selected_text = buffer
8690 .text_for_range(next_selection.range())
8691 .collect::<String>();
8692 if Some(next_selected_text) != selected_text {
8693 same_text_selected = false;
8694 selected_text = None;
8695 }
8696 } else {
8697 same_text_selected = false;
8698 selected_text = None;
8699 }
8700 }
8701 }
8702 }
8703
8704 if only_carets {
8705 for selection in &mut selections {
8706 let word_range = movement::surrounding_word(
8707 display_map,
8708 selection.start.to_display_point(display_map),
8709 );
8710 selection.start = word_range.start.to_offset(display_map, Bias::Left);
8711 selection.end = word_range.end.to_offset(display_map, Bias::Left);
8712 selection.goal = SelectionGoal::None;
8713 selection.reversed = false;
8714 select_next_match_ranges(
8715 self,
8716 selection.start..selection.end,
8717 replace_newest,
8718 autoscroll,
8719 cx,
8720 );
8721 }
8722
8723 if selections.len() == 1 {
8724 let selection = selections
8725 .last()
8726 .expect("ensured that there's only one selection");
8727 let query = buffer
8728 .text_for_range(selection.start..selection.end)
8729 .collect::<String>();
8730 let is_empty = query.is_empty();
8731 let select_state = SelectNextState {
8732 query: AhoCorasick::new(&[query])?,
8733 wordwise: true,
8734 done: is_empty,
8735 };
8736 self.select_next_state = Some(select_state);
8737 } else {
8738 self.select_next_state = None;
8739 }
8740 } else if let Some(selected_text) = selected_text {
8741 self.select_next_state = Some(SelectNextState {
8742 query: AhoCorasick::new(&[selected_text])?,
8743 wordwise: false,
8744 done: false,
8745 });
8746 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
8747 }
8748 }
8749 Ok(())
8750 }
8751
8752 pub fn select_all_matches(
8753 &mut self,
8754 _action: &SelectAllMatches,
8755 cx: &mut ViewContext<Self>,
8756 ) -> Result<()> {
8757 self.push_to_selection_history();
8758 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8759
8760 self.select_next_match_internal(&display_map, false, None, cx)?;
8761 let Some(select_next_state) = self.select_next_state.as_mut() else {
8762 return Ok(());
8763 };
8764 if select_next_state.done {
8765 return Ok(());
8766 }
8767
8768 let mut new_selections = self.selections.all::<usize>(cx);
8769
8770 let buffer = &display_map.buffer_snapshot;
8771 let query_matches = select_next_state
8772 .query
8773 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
8774
8775 for query_match in query_matches {
8776 let query_match = query_match.unwrap(); // can only fail due to I/O
8777 let offset_range = query_match.start()..query_match.end();
8778 let display_range = offset_range.start.to_display_point(&display_map)
8779 ..offset_range.end.to_display_point(&display_map);
8780
8781 if !select_next_state.wordwise
8782 || (!movement::is_inside_word(&display_map, display_range.start)
8783 && !movement::is_inside_word(&display_map, display_range.end))
8784 {
8785 self.selections.change_with(cx, |selections| {
8786 new_selections.push(Selection {
8787 id: selections.new_selection_id(),
8788 start: offset_range.start,
8789 end: offset_range.end,
8790 reversed: false,
8791 goal: SelectionGoal::None,
8792 });
8793 });
8794 }
8795 }
8796
8797 new_selections.sort_by_key(|selection| selection.start);
8798 let mut ix = 0;
8799 while ix + 1 < new_selections.len() {
8800 let current_selection = &new_selections[ix];
8801 let next_selection = &new_selections[ix + 1];
8802 if current_selection.range().overlaps(&next_selection.range()) {
8803 if current_selection.id < next_selection.id {
8804 new_selections.remove(ix + 1);
8805 } else {
8806 new_selections.remove(ix);
8807 }
8808 } else {
8809 ix += 1;
8810 }
8811 }
8812
8813 select_next_state.done = true;
8814 self.unfold_ranges(
8815 &new_selections
8816 .iter()
8817 .map(|selection| selection.range())
8818 .collect::<Vec<_>>(),
8819 false,
8820 false,
8821 cx,
8822 );
8823 self.change_selections(Some(Autoscroll::fit()), cx, |selections| {
8824 selections.select(new_selections)
8825 });
8826
8827 Ok(())
8828 }
8829
8830 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
8831 self.push_to_selection_history();
8832 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8833 self.select_next_match_internal(
8834 &display_map,
8835 action.replace_newest,
8836 Some(Autoscroll::newest()),
8837 cx,
8838 )?;
8839 Ok(())
8840 }
8841
8842 pub fn select_previous(
8843 &mut self,
8844 action: &SelectPrevious,
8845 cx: &mut ViewContext<Self>,
8846 ) -> Result<()> {
8847 self.push_to_selection_history();
8848 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8849 let buffer = &display_map.buffer_snapshot;
8850 let mut selections = self.selections.all::<usize>(cx);
8851 if let Some(mut select_prev_state) = self.select_prev_state.take() {
8852 let query = &select_prev_state.query;
8853 if !select_prev_state.done {
8854 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
8855 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
8856 let mut next_selected_range = None;
8857 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
8858 let bytes_before_last_selection =
8859 buffer.reversed_bytes_in_range(0..last_selection.start);
8860 let bytes_after_first_selection =
8861 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
8862 let query_matches = query
8863 .stream_find_iter(bytes_before_last_selection)
8864 .map(|result| (last_selection.start, result))
8865 .chain(
8866 query
8867 .stream_find_iter(bytes_after_first_selection)
8868 .map(|result| (buffer.len(), result)),
8869 );
8870 for (end_offset, query_match) in query_matches {
8871 let query_match = query_match.unwrap(); // can only fail due to I/O
8872 let offset_range =
8873 end_offset - query_match.end()..end_offset - query_match.start();
8874 let display_range = offset_range.start.to_display_point(&display_map)
8875 ..offset_range.end.to_display_point(&display_map);
8876
8877 if !select_prev_state.wordwise
8878 || (!movement::is_inside_word(&display_map, display_range.start)
8879 && !movement::is_inside_word(&display_map, display_range.end))
8880 {
8881 next_selected_range = Some(offset_range);
8882 break;
8883 }
8884 }
8885
8886 if let Some(next_selected_range) = next_selected_range {
8887 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
8888 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8889 if action.replace_newest {
8890 s.delete(s.newest_anchor().id);
8891 }
8892 s.insert_range(next_selected_range);
8893 });
8894 } else {
8895 select_prev_state.done = true;
8896 }
8897 }
8898
8899 self.select_prev_state = Some(select_prev_state);
8900 } else {
8901 let mut only_carets = true;
8902 let mut same_text_selected = true;
8903 let mut selected_text = None;
8904
8905 let mut selections_iter = selections.iter().peekable();
8906 while let Some(selection) = selections_iter.next() {
8907 if selection.start != selection.end {
8908 only_carets = false;
8909 }
8910
8911 if same_text_selected {
8912 if selected_text.is_none() {
8913 selected_text =
8914 Some(buffer.text_for_range(selection.range()).collect::<String>());
8915 }
8916
8917 if let Some(next_selection) = selections_iter.peek() {
8918 if next_selection.range().len() == selection.range().len() {
8919 let next_selected_text = buffer
8920 .text_for_range(next_selection.range())
8921 .collect::<String>();
8922 if Some(next_selected_text) != selected_text {
8923 same_text_selected = false;
8924 selected_text = None;
8925 }
8926 } else {
8927 same_text_selected = false;
8928 selected_text = None;
8929 }
8930 }
8931 }
8932 }
8933
8934 if only_carets {
8935 for selection in &mut selections {
8936 let word_range = movement::surrounding_word(
8937 &display_map,
8938 selection.start.to_display_point(&display_map),
8939 );
8940 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
8941 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
8942 selection.goal = SelectionGoal::None;
8943 selection.reversed = false;
8944 }
8945 if selections.len() == 1 {
8946 let selection = selections
8947 .last()
8948 .expect("ensured that there's only one selection");
8949 let query = buffer
8950 .text_for_range(selection.start..selection.end)
8951 .collect::<String>();
8952 let is_empty = query.is_empty();
8953 let select_state = SelectNextState {
8954 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
8955 wordwise: true,
8956 done: is_empty,
8957 };
8958 self.select_prev_state = Some(select_state);
8959 } else {
8960 self.select_prev_state = None;
8961 }
8962
8963 self.unfold_ranges(
8964 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
8965 false,
8966 true,
8967 cx,
8968 );
8969 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8970 s.select(selections);
8971 });
8972 } else if let Some(selected_text) = selected_text {
8973 self.select_prev_state = Some(SelectNextState {
8974 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
8975 wordwise: false,
8976 done: false,
8977 });
8978 self.select_previous(action, cx)?;
8979 }
8980 }
8981 Ok(())
8982 }
8983
8984 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
8985 if self.read_only(cx) {
8986 return;
8987 }
8988 let text_layout_details = &self.text_layout_details(cx);
8989 self.transact(cx, |this, cx| {
8990 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
8991 let mut edits = Vec::new();
8992 let mut selection_edit_ranges = Vec::new();
8993 let mut last_toggled_row = None;
8994 let snapshot = this.buffer.read(cx).read(cx);
8995 let empty_str: Arc<str> = Arc::default();
8996 let mut suffixes_inserted = Vec::new();
8997 let ignore_indent = action.ignore_indent;
8998
8999 fn comment_prefix_range(
9000 snapshot: &MultiBufferSnapshot,
9001 row: MultiBufferRow,
9002 comment_prefix: &str,
9003 comment_prefix_whitespace: &str,
9004 ignore_indent: bool,
9005 ) -> Range<Point> {
9006 let indent_size = if ignore_indent {
9007 0
9008 } else {
9009 snapshot.indent_size_for_line(row).len
9010 };
9011
9012 let start = Point::new(row.0, indent_size);
9013
9014 let mut line_bytes = snapshot
9015 .bytes_in_range(start..snapshot.max_point())
9016 .flatten()
9017 .copied();
9018
9019 // If this line currently begins with the line comment prefix, then record
9020 // the range containing the prefix.
9021 if line_bytes
9022 .by_ref()
9023 .take(comment_prefix.len())
9024 .eq(comment_prefix.bytes())
9025 {
9026 // Include any whitespace that matches the comment prefix.
9027 let matching_whitespace_len = line_bytes
9028 .zip(comment_prefix_whitespace.bytes())
9029 .take_while(|(a, b)| a == b)
9030 .count() as u32;
9031 let end = Point::new(
9032 start.row,
9033 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
9034 );
9035 start..end
9036 } else {
9037 start..start
9038 }
9039 }
9040
9041 fn comment_suffix_range(
9042 snapshot: &MultiBufferSnapshot,
9043 row: MultiBufferRow,
9044 comment_suffix: &str,
9045 comment_suffix_has_leading_space: bool,
9046 ) -> Range<Point> {
9047 let end = Point::new(row.0, snapshot.line_len(row));
9048 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
9049
9050 let mut line_end_bytes = snapshot
9051 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
9052 .flatten()
9053 .copied();
9054
9055 let leading_space_len = if suffix_start_column > 0
9056 && line_end_bytes.next() == Some(b' ')
9057 && comment_suffix_has_leading_space
9058 {
9059 1
9060 } else {
9061 0
9062 };
9063
9064 // If this line currently begins with the line comment prefix, then record
9065 // the range containing the prefix.
9066 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
9067 let start = Point::new(end.row, suffix_start_column - leading_space_len);
9068 start..end
9069 } else {
9070 end..end
9071 }
9072 }
9073
9074 // TODO: Handle selections that cross excerpts
9075 for selection in &mut selections {
9076 let start_column = snapshot
9077 .indent_size_for_line(MultiBufferRow(selection.start.row))
9078 .len;
9079 let language = if let Some(language) =
9080 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
9081 {
9082 language
9083 } else {
9084 continue;
9085 };
9086
9087 selection_edit_ranges.clear();
9088
9089 // If multiple selections contain a given row, avoid processing that
9090 // row more than once.
9091 let mut start_row = MultiBufferRow(selection.start.row);
9092 if last_toggled_row == Some(start_row) {
9093 start_row = start_row.next_row();
9094 }
9095 let end_row =
9096 if selection.end.row > selection.start.row && selection.end.column == 0 {
9097 MultiBufferRow(selection.end.row - 1)
9098 } else {
9099 MultiBufferRow(selection.end.row)
9100 };
9101 last_toggled_row = Some(end_row);
9102
9103 if start_row > end_row {
9104 continue;
9105 }
9106
9107 // If the language has line comments, toggle those.
9108 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
9109
9110 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
9111 if ignore_indent {
9112 full_comment_prefixes = full_comment_prefixes
9113 .into_iter()
9114 .map(|s| Arc::from(s.trim_end()))
9115 .collect();
9116 }
9117
9118 if !full_comment_prefixes.is_empty() {
9119 let first_prefix = full_comment_prefixes
9120 .first()
9121 .expect("prefixes is non-empty");
9122 let prefix_trimmed_lengths = full_comment_prefixes
9123 .iter()
9124 .map(|p| p.trim_end_matches(' ').len())
9125 .collect::<SmallVec<[usize; 4]>>();
9126
9127 let mut all_selection_lines_are_comments = true;
9128
9129 for row in start_row.0..=end_row.0 {
9130 let row = MultiBufferRow(row);
9131 if start_row < end_row && snapshot.is_line_blank(row) {
9132 continue;
9133 }
9134
9135 let prefix_range = full_comment_prefixes
9136 .iter()
9137 .zip(prefix_trimmed_lengths.iter().copied())
9138 .map(|(prefix, trimmed_prefix_len)| {
9139 comment_prefix_range(
9140 snapshot.deref(),
9141 row,
9142 &prefix[..trimmed_prefix_len],
9143 &prefix[trimmed_prefix_len..],
9144 ignore_indent,
9145 )
9146 })
9147 .max_by_key(|range| range.end.column - range.start.column)
9148 .expect("prefixes is non-empty");
9149
9150 if prefix_range.is_empty() {
9151 all_selection_lines_are_comments = false;
9152 }
9153
9154 selection_edit_ranges.push(prefix_range);
9155 }
9156
9157 if all_selection_lines_are_comments {
9158 edits.extend(
9159 selection_edit_ranges
9160 .iter()
9161 .cloned()
9162 .map(|range| (range, empty_str.clone())),
9163 );
9164 } else {
9165 let min_column = selection_edit_ranges
9166 .iter()
9167 .map(|range| range.start.column)
9168 .min()
9169 .unwrap_or(0);
9170 edits.extend(selection_edit_ranges.iter().map(|range| {
9171 let position = Point::new(range.start.row, min_column);
9172 (position..position, first_prefix.clone())
9173 }));
9174 }
9175 } else if let Some((full_comment_prefix, comment_suffix)) =
9176 language.block_comment_delimiters()
9177 {
9178 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
9179 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
9180 let prefix_range = comment_prefix_range(
9181 snapshot.deref(),
9182 start_row,
9183 comment_prefix,
9184 comment_prefix_whitespace,
9185 ignore_indent,
9186 );
9187 let suffix_range = comment_suffix_range(
9188 snapshot.deref(),
9189 end_row,
9190 comment_suffix.trim_start_matches(' '),
9191 comment_suffix.starts_with(' '),
9192 );
9193
9194 if prefix_range.is_empty() || suffix_range.is_empty() {
9195 edits.push((
9196 prefix_range.start..prefix_range.start,
9197 full_comment_prefix.clone(),
9198 ));
9199 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
9200 suffixes_inserted.push((end_row, comment_suffix.len()));
9201 } else {
9202 edits.push((prefix_range, empty_str.clone()));
9203 edits.push((suffix_range, empty_str.clone()));
9204 }
9205 } else {
9206 continue;
9207 }
9208 }
9209
9210 drop(snapshot);
9211 this.buffer.update(cx, |buffer, cx| {
9212 buffer.edit(edits, None, cx);
9213 });
9214
9215 // Adjust selections so that they end before any comment suffixes that
9216 // were inserted.
9217 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
9218 let mut selections = this.selections.all::<Point>(cx);
9219 let snapshot = this.buffer.read(cx).read(cx);
9220 for selection in &mut selections {
9221 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
9222 match row.cmp(&MultiBufferRow(selection.end.row)) {
9223 Ordering::Less => {
9224 suffixes_inserted.next();
9225 continue;
9226 }
9227 Ordering::Greater => break,
9228 Ordering::Equal => {
9229 if selection.end.column == snapshot.line_len(row) {
9230 if selection.is_empty() {
9231 selection.start.column -= suffix_len as u32;
9232 }
9233 selection.end.column -= suffix_len as u32;
9234 }
9235 break;
9236 }
9237 }
9238 }
9239 }
9240
9241 drop(snapshot);
9242 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
9243
9244 let selections = this.selections.all::<Point>(cx);
9245 let selections_on_single_row = selections.windows(2).all(|selections| {
9246 selections[0].start.row == selections[1].start.row
9247 && selections[0].end.row == selections[1].end.row
9248 && selections[0].start.row == selections[0].end.row
9249 });
9250 let selections_selecting = selections
9251 .iter()
9252 .any(|selection| selection.start != selection.end);
9253 let advance_downwards = action.advance_downwards
9254 && selections_on_single_row
9255 && !selections_selecting
9256 && !matches!(this.mode, EditorMode::SingleLine { .. });
9257
9258 if advance_downwards {
9259 let snapshot = this.buffer.read(cx).snapshot(cx);
9260
9261 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
9262 s.move_cursors_with(|display_snapshot, display_point, _| {
9263 let mut point = display_point.to_point(display_snapshot);
9264 point.row += 1;
9265 point = snapshot.clip_point(point, Bias::Left);
9266 let display_point = point.to_display_point(display_snapshot);
9267 let goal = SelectionGoal::HorizontalPosition(
9268 display_snapshot
9269 .x_for_display_point(display_point, text_layout_details)
9270 .into(),
9271 );
9272 (display_point, goal)
9273 })
9274 });
9275 }
9276 });
9277 }
9278
9279 pub fn select_enclosing_symbol(
9280 &mut self,
9281 _: &SelectEnclosingSymbol,
9282 cx: &mut ViewContext<Self>,
9283 ) {
9284 let buffer = self.buffer.read(cx).snapshot(cx);
9285 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
9286
9287 fn update_selection(
9288 selection: &Selection<usize>,
9289 buffer_snap: &MultiBufferSnapshot,
9290 ) -> Option<Selection<usize>> {
9291 let cursor = selection.head();
9292 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
9293 for symbol in symbols.iter().rev() {
9294 let start = symbol.range.start.to_offset(buffer_snap);
9295 let end = symbol.range.end.to_offset(buffer_snap);
9296 let new_range = start..end;
9297 if start < selection.start || end > selection.end {
9298 return Some(Selection {
9299 id: selection.id,
9300 start: new_range.start,
9301 end: new_range.end,
9302 goal: SelectionGoal::None,
9303 reversed: selection.reversed,
9304 });
9305 }
9306 }
9307 None
9308 }
9309
9310 let mut selected_larger_symbol = false;
9311 let new_selections = old_selections
9312 .iter()
9313 .map(|selection| match update_selection(selection, &buffer) {
9314 Some(new_selection) => {
9315 if new_selection.range() != selection.range() {
9316 selected_larger_symbol = true;
9317 }
9318 new_selection
9319 }
9320 None => selection.clone(),
9321 })
9322 .collect::<Vec<_>>();
9323
9324 if selected_larger_symbol {
9325 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9326 s.select(new_selections);
9327 });
9328 }
9329 }
9330
9331 pub fn select_larger_syntax_node(
9332 &mut self,
9333 _: &SelectLargerSyntaxNode,
9334 cx: &mut ViewContext<Self>,
9335 ) {
9336 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9337 let buffer = self.buffer.read(cx).snapshot(cx);
9338 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
9339
9340 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
9341 let mut selected_larger_node = false;
9342 let new_selections = old_selections
9343 .iter()
9344 .map(|selection| {
9345 let old_range = selection.start..selection.end;
9346 let mut new_range = old_range.clone();
9347 while let Some(containing_range) =
9348 buffer.range_for_syntax_ancestor(new_range.clone())
9349 {
9350 new_range = containing_range;
9351 if !display_map.intersects_fold(new_range.start)
9352 && !display_map.intersects_fold(new_range.end)
9353 {
9354 break;
9355 }
9356 }
9357
9358 selected_larger_node |= new_range != old_range;
9359 Selection {
9360 id: selection.id,
9361 start: new_range.start,
9362 end: new_range.end,
9363 goal: SelectionGoal::None,
9364 reversed: selection.reversed,
9365 }
9366 })
9367 .collect::<Vec<_>>();
9368
9369 if selected_larger_node {
9370 stack.push(old_selections);
9371 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9372 s.select(new_selections);
9373 });
9374 }
9375 self.select_larger_syntax_node_stack = stack;
9376 }
9377
9378 pub fn select_smaller_syntax_node(
9379 &mut self,
9380 _: &SelectSmallerSyntaxNode,
9381 cx: &mut ViewContext<Self>,
9382 ) {
9383 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
9384 if let Some(selections) = stack.pop() {
9385 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9386 s.select(selections.to_vec());
9387 });
9388 }
9389 self.select_larger_syntax_node_stack = stack;
9390 }
9391
9392 fn refresh_runnables(&mut self, cx: &mut ViewContext<Self>) -> Task<()> {
9393 if !EditorSettings::get_global(cx).gutter.runnables {
9394 self.clear_tasks();
9395 return Task::ready(());
9396 }
9397 let project = self.project.as_ref().map(Model::downgrade);
9398 cx.spawn(|this, mut cx| async move {
9399 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
9400 let Some(project) = project.and_then(|p| p.upgrade()) else {
9401 return;
9402 };
9403 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
9404 this.display_map.update(cx, |map, cx| map.snapshot(cx))
9405 }) else {
9406 return;
9407 };
9408
9409 let hide_runnables = project
9410 .update(&mut cx, |project, cx| {
9411 // Do not display any test indicators in non-dev server remote projects.
9412 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
9413 })
9414 .unwrap_or(true);
9415 if hide_runnables {
9416 return;
9417 }
9418 let new_rows =
9419 cx.background_executor()
9420 .spawn({
9421 let snapshot = display_snapshot.clone();
9422 async move {
9423 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
9424 }
9425 })
9426 .await;
9427 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
9428
9429 this.update(&mut cx, |this, _| {
9430 this.clear_tasks();
9431 for (key, value) in rows {
9432 this.insert_tasks(key, value);
9433 }
9434 })
9435 .ok();
9436 })
9437 }
9438 fn fetch_runnable_ranges(
9439 snapshot: &DisplaySnapshot,
9440 range: Range<Anchor>,
9441 ) -> Vec<language::RunnableRange> {
9442 snapshot.buffer_snapshot.runnable_ranges(range).collect()
9443 }
9444
9445 fn runnable_rows(
9446 project: Model<Project>,
9447 snapshot: DisplaySnapshot,
9448 runnable_ranges: Vec<RunnableRange>,
9449 mut cx: AsyncWindowContext,
9450 ) -> Vec<((BufferId, u32), RunnableTasks)> {
9451 runnable_ranges
9452 .into_iter()
9453 .filter_map(|mut runnable| {
9454 let tasks = cx
9455 .update(|cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
9456 .ok()?;
9457 if tasks.is_empty() {
9458 return None;
9459 }
9460
9461 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
9462
9463 let row = snapshot
9464 .buffer_snapshot
9465 .buffer_line_for_row(MultiBufferRow(point.row))?
9466 .1
9467 .start
9468 .row;
9469
9470 let context_range =
9471 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
9472 Some((
9473 (runnable.buffer_id, row),
9474 RunnableTasks {
9475 templates: tasks,
9476 offset: MultiBufferOffset(runnable.run_range.start),
9477 context_range,
9478 column: point.column,
9479 extra_variables: runnable.extra_captures,
9480 },
9481 ))
9482 })
9483 .collect()
9484 }
9485
9486 fn templates_with_tags(
9487 project: &Model<Project>,
9488 runnable: &mut Runnable,
9489 cx: &WindowContext<'_>,
9490 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
9491 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
9492 let (worktree_id, file) = project
9493 .buffer_for_id(runnable.buffer, cx)
9494 .and_then(|buffer| buffer.read(cx).file())
9495 .map(|file| (file.worktree_id(cx), file.clone()))
9496 .unzip();
9497
9498 (
9499 project.task_store().read(cx).task_inventory().cloned(),
9500 worktree_id,
9501 file,
9502 )
9503 });
9504
9505 let tags = mem::take(&mut runnable.tags);
9506 let mut tags: Vec<_> = tags
9507 .into_iter()
9508 .flat_map(|tag| {
9509 let tag = tag.0.clone();
9510 inventory
9511 .as_ref()
9512 .into_iter()
9513 .flat_map(|inventory| {
9514 inventory.read(cx).list_tasks(
9515 file.clone(),
9516 Some(runnable.language.clone()),
9517 worktree_id,
9518 cx,
9519 )
9520 })
9521 .filter(move |(_, template)| {
9522 template.tags.iter().any(|source_tag| source_tag == &tag)
9523 })
9524 })
9525 .sorted_by_key(|(kind, _)| kind.to_owned())
9526 .collect();
9527 if let Some((leading_tag_source, _)) = tags.first() {
9528 // Strongest source wins; if we have worktree tag binding, prefer that to
9529 // global and language bindings;
9530 // if we have a global binding, prefer that to language binding.
9531 let first_mismatch = tags
9532 .iter()
9533 .position(|(tag_source, _)| tag_source != leading_tag_source);
9534 if let Some(index) = first_mismatch {
9535 tags.truncate(index);
9536 }
9537 }
9538
9539 tags
9540 }
9541
9542 pub fn move_to_enclosing_bracket(
9543 &mut self,
9544 _: &MoveToEnclosingBracket,
9545 cx: &mut ViewContext<Self>,
9546 ) {
9547 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9548 s.move_offsets_with(|snapshot, selection| {
9549 let Some(enclosing_bracket_ranges) =
9550 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
9551 else {
9552 return;
9553 };
9554
9555 let mut best_length = usize::MAX;
9556 let mut best_inside = false;
9557 let mut best_in_bracket_range = false;
9558 let mut best_destination = None;
9559 for (open, close) in enclosing_bracket_ranges {
9560 let close = close.to_inclusive();
9561 let length = close.end() - open.start;
9562 let inside = selection.start >= open.end && selection.end <= *close.start();
9563 let in_bracket_range = open.to_inclusive().contains(&selection.head())
9564 || close.contains(&selection.head());
9565
9566 // If best is next to a bracket and current isn't, skip
9567 if !in_bracket_range && best_in_bracket_range {
9568 continue;
9569 }
9570
9571 // Prefer smaller lengths unless best is inside and current isn't
9572 if length > best_length && (best_inside || !inside) {
9573 continue;
9574 }
9575
9576 best_length = length;
9577 best_inside = inside;
9578 best_in_bracket_range = in_bracket_range;
9579 best_destination = Some(
9580 if close.contains(&selection.start) && close.contains(&selection.end) {
9581 if inside {
9582 open.end
9583 } else {
9584 open.start
9585 }
9586 } else if inside {
9587 *close.start()
9588 } else {
9589 *close.end()
9590 },
9591 );
9592 }
9593
9594 if let Some(destination) = best_destination {
9595 selection.collapse_to(destination, SelectionGoal::None);
9596 }
9597 })
9598 });
9599 }
9600
9601 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
9602 self.end_selection(cx);
9603 self.selection_history.mode = SelectionHistoryMode::Undoing;
9604 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
9605 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
9606 self.select_next_state = entry.select_next_state;
9607 self.select_prev_state = entry.select_prev_state;
9608 self.add_selections_state = entry.add_selections_state;
9609 self.request_autoscroll(Autoscroll::newest(), cx);
9610 }
9611 self.selection_history.mode = SelectionHistoryMode::Normal;
9612 }
9613
9614 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
9615 self.end_selection(cx);
9616 self.selection_history.mode = SelectionHistoryMode::Redoing;
9617 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
9618 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
9619 self.select_next_state = entry.select_next_state;
9620 self.select_prev_state = entry.select_prev_state;
9621 self.add_selections_state = entry.add_selections_state;
9622 self.request_autoscroll(Autoscroll::newest(), cx);
9623 }
9624 self.selection_history.mode = SelectionHistoryMode::Normal;
9625 }
9626
9627 pub fn expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
9628 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
9629 }
9630
9631 pub fn expand_excerpts_down(
9632 &mut self,
9633 action: &ExpandExcerptsDown,
9634 cx: &mut ViewContext<Self>,
9635 ) {
9636 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
9637 }
9638
9639 pub fn expand_excerpts_up(&mut self, action: &ExpandExcerptsUp, cx: &mut ViewContext<Self>) {
9640 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
9641 }
9642
9643 pub fn expand_excerpts_for_direction(
9644 &mut self,
9645 lines: u32,
9646 direction: ExpandExcerptDirection,
9647 cx: &mut ViewContext<Self>,
9648 ) {
9649 let selections = self.selections.disjoint_anchors();
9650
9651 let lines = if lines == 0 {
9652 EditorSettings::get_global(cx).expand_excerpt_lines
9653 } else {
9654 lines
9655 };
9656
9657 self.buffer.update(cx, |buffer, cx| {
9658 buffer.expand_excerpts(
9659 selections
9660 .iter()
9661 .map(|selection| selection.head().excerpt_id)
9662 .dedup(),
9663 lines,
9664 direction,
9665 cx,
9666 )
9667 })
9668 }
9669
9670 pub fn expand_excerpt(
9671 &mut self,
9672 excerpt: ExcerptId,
9673 direction: ExpandExcerptDirection,
9674 cx: &mut ViewContext<Self>,
9675 ) {
9676 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
9677 self.buffer.update(cx, |buffer, cx| {
9678 buffer.expand_excerpts([excerpt], lines, direction, cx)
9679 })
9680 }
9681
9682 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
9683 self.go_to_diagnostic_impl(Direction::Next, cx)
9684 }
9685
9686 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
9687 self.go_to_diagnostic_impl(Direction::Prev, cx)
9688 }
9689
9690 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
9691 let buffer = self.buffer.read(cx).snapshot(cx);
9692 let selection = self.selections.newest::<usize>(cx);
9693
9694 // If there is an active Diagnostic Popover jump to its diagnostic instead.
9695 if direction == Direction::Next {
9696 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
9697 let (group_id, jump_to) = popover.activation_info();
9698 if self.activate_diagnostics(group_id, cx) {
9699 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9700 let mut new_selection = s.newest_anchor().clone();
9701 new_selection.collapse_to(jump_to, SelectionGoal::None);
9702 s.select_anchors(vec![new_selection.clone()]);
9703 });
9704 }
9705 return;
9706 }
9707 }
9708
9709 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
9710 active_diagnostics
9711 .primary_range
9712 .to_offset(&buffer)
9713 .to_inclusive()
9714 });
9715 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
9716 if active_primary_range.contains(&selection.head()) {
9717 *active_primary_range.start()
9718 } else {
9719 selection.head()
9720 }
9721 } else {
9722 selection.head()
9723 };
9724 let snapshot = self.snapshot(cx);
9725 loop {
9726 let diagnostics = if direction == Direction::Prev {
9727 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
9728 } else {
9729 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
9730 }
9731 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start));
9732 let group = diagnostics
9733 // relies on diagnostics_in_range to return diagnostics with the same starting range to
9734 // be sorted in a stable way
9735 // skip until we are at current active diagnostic, if it exists
9736 .skip_while(|entry| {
9737 (match direction {
9738 Direction::Prev => entry.range.start >= search_start,
9739 Direction::Next => entry.range.start <= search_start,
9740 }) && self
9741 .active_diagnostics
9742 .as_ref()
9743 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
9744 })
9745 .find_map(|entry| {
9746 if entry.diagnostic.is_primary
9747 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
9748 && !entry.range.is_empty()
9749 // if we match with the active diagnostic, skip it
9750 && Some(entry.diagnostic.group_id)
9751 != self.active_diagnostics.as_ref().map(|d| d.group_id)
9752 {
9753 Some((entry.range, entry.diagnostic.group_id))
9754 } else {
9755 None
9756 }
9757 });
9758
9759 if let Some((primary_range, group_id)) = group {
9760 if self.activate_diagnostics(group_id, cx) {
9761 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9762 s.select(vec![Selection {
9763 id: selection.id,
9764 start: primary_range.start,
9765 end: primary_range.start,
9766 reversed: false,
9767 goal: SelectionGoal::None,
9768 }]);
9769 });
9770 }
9771 break;
9772 } else {
9773 // Cycle around to the start of the buffer, potentially moving back to the start of
9774 // the currently active diagnostic.
9775 active_primary_range.take();
9776 if direction == Direction::Prev {
9777 if search_start == buffer.len() {
9778 break;
9779 } else {
9780 search_start = buffer.len();
9781 }
9782 } else if search_start == 0 {
9783 break;
9784 } else {
9785 search_start = 0;
9786 }
9787 }
9788 }
9789 }
9790
9791 fn go_to_next_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
9792 let snapshot = self.snapshot(cx);
9793 let selection = self.selections.newest::<Point>(cx);
9794 self.go_to_hunk_after_position(&snapshot, selection.head(), cx);
9795 }
9796
9797 fn go_to_hunk_after_position(
9798 &mut self,
9799 snapshot: &EditorSnapshot,
9800 position: Point,
9801 cx: &mut ViewContext<'_, Editor>,
9802 ) -> Option<MultiBufferDiffHunk> {
9803 for (ix, position) in [position, Point::zero()].into_iter().enumerate() {
9804 if let Some(hunk) = self.go_to_next_hunk_in_direction(
9805 snapshot,
9806 position,
9807 ix > 0,
9808 snapshot.diff_map.diff_hunks_in_range(
9809 position + Point::new(1, 0)..snapshot.buffer_snapshot.max_point(),
9810 &snapshot.buffer_snapshot,
9811 ),
9812 cx,
9813 ) {
9814 return Some(hunk);
9815 }
9816 }
9817 None
9818 }
9819
9820 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
9821 let snapshot = self.snapshot(cx);
9822 let selection = self.selections.newest::<Point>(cx);
9823 self.go_to_hunk_before_position(&snapshot, selection.head(), cx);
9824 }
9825
9826 fn go_to_hunk_before_position(
9827 &mut self,
9828 snapshot: &EditorSnapshot,
9829 position: Point,
9830 cx: &mut ViewContext<'_, Editor>,
9831 ) -> Option<MultiBufferDiffHunk> {
9832 for (ix, position) in [position, snapshot.buffer_snapshot.max_point()]
9833 .into_iter()
9834 .enumerate()
9835 {
9836 if let Some(hunk) = self.go_to_next_hunk_in_direction(
9837 snapshot,
9838 position,
9839 ix > 0,
9840 snapshot
9841 .diff_map
9842 .diff_hunks_in_range_rev(Point::zero()..position, &snapshot.buffer_snapshot),
9843 cx,
9844 ) {
9845 return Some(hunk);
9846 }
9847 }
9848 None
9849 }
9850
9851 fn go_to_next_hunk_in_direction(
9852 &mut self,
9853 snapshot: &DisplaySnapshot,
9854 initial_point: Point,
9855 is_wrapped: bool,
9856 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
9857 cx: &mut ViewContext<Editor>,
9858 ) -> Option<MultiBufferDiffHunk> {
9859 let display_point = initial_point.to_display_point(snapshot);
9860 let mut hunks = hunks
9861 .map(|hunk| (diff_hunk_to_display(&hunk, snapshot), hunk))
9862 .filter(|(display_hunk, _)| {
9863 is_wrapped || !display_hunk.contains_display_row(display_point.row())
9864 })
9865 .dedup();
9866
9867 if let Some((display_hunk, hunk)) = hunks.next() {
9868 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9869 let row = display_hunk.start_display_row();
9870 let point = DisplayPoint::new(row, 0);
9871 s.select_display_ranges([point..point]);
9872 });
9873
9874 Some(hunk)
9875 } else {
9876 None
9877 }
9878 }
9879
9880 pub fn go_to_definition(
9881 &mut self,
9882 _: &GoToDefinition,
9883 cx: &mut ViewContext<Self>,
9884 ) -> Task<Result<Navigated>> {
9885 let definition = self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
9886 cx.spawn(|editor, mut cx| async move {
9887 if definition.await? == Navigated::Yes {
9888 return Ok(Navigated::Yes);
9889 }
9890 match editor.update(&mut cx, |editor, cx| {
9891 editor.find_all_references(&FindAllReferences, cx)
9892 })? {
9893 Some(references) => references.await,
9894 None => Ok(Navigated::No),
9895 }
9896 })
9897 }
9898
9899 pub fn go_to_declaration(
9900 &mut self,
9901 _: &GoToDeclaration,
9902 cx: &mut ViewContext<Self>,
9903 ) -> Task<Result<Navigated>> {
9904 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, cx)
9905 }
9906
9907 pub fn go_to_declaration_split(
9908 &mut self,
9909 _: &GoToDeclaration,
9910 cx: &mut ViewContext<Self>,
9911 ) -> Task<Result<Navigated>> {
9912 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, cx)
9913 }
9914
9915 pub fn go_to_implementation(
9916 &mut self,
9917 _: &GoToImplementation,
9918 cx: &mut ViewContext<Self>,
9919 ) -> Task<Result<Navigated>> {
9920 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx)
9921 }
9922
9923 pub fn go_to_implementation_split(
9924 &mut self,
9925 _: &GoToImplementationSplit,
9926 cx: &mut ViewContext<Self>,
9927 ) -> Task<Result<Navigated>> {
9928 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx)
9929 }
9930
9931 pub fn go_to_type_definition(
9932 &mut self,
9933 _: &GoToTypeDefinition,
9934 cx: &mut ViewContext<Self>,
9935 ) -> Task<Result<Navigated>> {
9936 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx)
9937 }
9938
9939 pub fn go_to_definition_split(
9940 &mut self,
9941 _: &GoToDefinitionSplit,
9942 cx: &mut ViewContext<Self>,
9943 ) -> Task<Result<Navigated>> {
9944 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx)
9945 }
9946
9947 pub fn go_to_type_definition_split(
9948 &mut self,
9949 _: &GoToTypeDefinitionSplit,
9950 cx: &mut ViewContext<Self>,
9951 ) -> Task<Result<Navigated>> {
9952 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx)
9953 }
9954
9955 fn go_to_definition_of_kind(
9956 &mut self,
9957 kind: GotoDefinitionKind,
9958 split: bool,
9959 cx: &mut ViewContext<Self>,
9960 ) -> Task<Result<Navigated>> {
9961 let Some(provider) = self.semantics_provider.clone() else {
9962 return Task::ready(Ok(Navigated::No));
9963 };
9964 let head = self.selections.newest::<usize>(cx).head();
9965 let buffer = self.buffer.read(cx);
9966 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
9967 text_anchor
9968 } else {
9969 return Task::ready(Ok(Navigated::No));
9970 };
9971
9972 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
9973 return Task::ready(Ok(Navigated::No));
9974 };
9975
9976 cx.spawn(|editor, mut cx| async move {
9977 let definitions = definitions.await?;
9978 let navigated = editor
9979 .update(&mut cx, |editor, cx| {
9980 editor.navigate_to_hover_links(
9981 Some(kind),
9982 definitions
9983 .into_iter()
9984 .filter(|location| {
9985 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
9986 })
9987 .map(HoverLink::Text)
9988 .collect::<Vec<_>>(),
9989 split,
9990 cx,
9991 )
9992 })?
9993 .await?;
9994 anyhow::Ok(navigated)
9995 })
9996 }
9997
9998 pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext<Self>) {
9999 let position = self.selections.newest_anchor().head();
10000 let Some((buffer, buffer_position)) =
10001 self.buffer.read(cx).text_anchor_for_position(position, cx)
10002 else {
10003 return;
10004 };
10005
10006 cx.spawn(|editor, mut cx| async move {
10007 if let Some((_, url)) = find_url(&buffer, buffer_position, cx.clone()) {
10008 editor.update(&mut cx, |_, cx| {
10009 cx.open_url(&url);
10010 })
10011 } else {
10012 Ok(())
10013 }
10014 })
10015 .detach();
10016 }
10017
10018 pub fn open_file(&mut self, _: &OpenFile, cx: &mut ViewContext<Self>) {
10019 let Some(workspace) = self.workspace() else {
10020 return;
10021 };
10022
10023 let position = self.selections.newest_anchor().head();
10024
10025 let Some((buffer, buffer_position)) =
10026 self.buffer.read(cx).text_anchor_for_position(position, cx)
10027 else {
10028 return;
10029 };
10030
10031 let project = self.project.clone();
10032
10033 cx.spawn(|_, mut cx| async move {
10034 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
10035
10036 if let Some((_, path)) = result {
10037 workspace
10038 .update(&mut cx, |workspace, cx| {
10039 workspace.open_resolved_path(path, cx)
10040 })?
10041 .await?;
10042 }
10043 anyhow::Ok(())
10044 })
10045 .detach();
10046 }
10047
10048 pub(crate) fn navigate_to_hover_links(
10049 &mut self,
10050 kind: Option<GotoDefinitionKind>,
10051 mut definitions: Vec<HoverLink>,
10052 split: bool,
10053 cx: &mut ViewContext<Editor>,
10054 ) -> Task<Result<Navigated>> {
10055 // If there is one definition, just open it directly
10056 if definitions.len() == 1 {
10057 let definition = definitions.pop().unwrap();
10058
10059 enum TargetTaskResult {
10060 Location(Option<Location>),
10061 AlreadyNavigated,
10062 }
10063
10064 let target_task = match definition {
10065 HoverLink::Text(link) => {
10066 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
10067 }
10068 HoverLink::InlayHint(lsp_location, server_id) => {
10069 let computation = self.compute_target_location(lsp_location, server_id, cx);
10070 cx.background_executor().spawn(async move {
10071 let location = computation.await?;
10072 Ok(TargetTaskResult::Location(location))
10073 })
10074 }
10075 HoverLink::Url(url) => {
10076 cx.open_url(&url);
10077 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
10078 }
10079 HoverLink::File(path) => {
10080 if let Some(workspace) = self.workspace() {
10081 cx.spawn(|_, mut cx| async move {
10082 workspace
10083 .update(&mut cx, |workspace, cx| {
10084 workspace.open_resolved_path(path, cx)
10085 })?
10086 .await
10087 .map(|_| TargetTaskResult::AlreadyNavigated)
10088 })
10089 } else {
10090 Task::ready(Ok(TargetTaskResult::Location(None)))
10091 }
10092 }
10093 };
10094 cx.spawn(|editor, mut cx| async move {
10095 let target = match target_task.await.context("target resolution task")? {
10096 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
10097 TargetTaskResult::Location(None) => return Ok(Navigated::No),
10098 TargetTaskResult::Location(Some(target)) => target,
10099 };
10100
10101 editor.update(&mut cx, |editor, cx| {
10102 let Some(workspace) = editor.workspace() else {
10103 return Navigated::No;
10104 };
10105 let pane = workspace.read(cx).active_pane().clone();
10106
10107 let range = target.range.to_offset(target.buffer.read(cx));
10108 let range = editor.range_for_match(&range);
10109
10110 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
10111 let buffer = target.buffer.read(cx);
10112 let range = check_multiline_range(buffer, range);
10113 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
10114 s.select_ranges([range]);
10115 });
10116 } else {
10117 cx.window_context().defer(move |cx| {
10118 let target_editor: View<Self> =
10119 workspace.update(cx, |workspace, cx| {
10120 let pane = if split {
10121 workspace.adjacent_pane(cx)
10122 } else {
10123 workspace.active_pane().clone()
10124 };
10125
10126 workspace.open_project_item(
10127 pane,
10128 target.buffer.clone(),
10129 true,
10130 true,
10131 cx,
10132 )
10133 });
10134 target_editor.update(cx, |target_editor, cx| {
10135 // When selecting a definition in a different buffer, disable the nav history
10136 // to avoid creating a history entry at the previous cursor location.
10137 pane.update(cx, |pane, _| pane.disable_history());
10138 let buffer = target.buffer.read(cx);
10139 let range = check_multiline_range(buffer, range);
10140 target_editor.change_selections(
10141 Some(Autoscroll::focused()),
10142 cx,
10143 |s| {
10144 s.select_ranges([range]);
10145 },
10146 );
10147 pane.update(cx, |pane, _| pane.enable_history());
10148 });
10149 });
10150 }
10151 Navigated::Yes
10152 })
10153 })
10154 } else if !definitions.is_empty() {
10155 cx.spawn(|editor, mut cx| async move {
10156 let (title, location_tasks, workspace) = editor
10157 .update(&mut cx, |editor, cx| {
10158 let tab_kind = match kind {
10159 Some(GotoDefinitionKind::Implementation) => "Implementations",
10160 _ => "Definitions",
10161 };
10162 let title = definitions
10163 .iter()
10164 .find_map(|definition| match definition {
10165 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
10166 let buffer = origin.buffer.read(cx);
10167 format!(
10168 "{} for {}",
10169 tab_kind,
10170 buffer
10171 .text_for_range(origin.range.clone())
10172 .collect::<String>()
10173 )
10174 }),
10175 HoverLink::InlayHint(_, _) => None,
10176 HoverLink::Url(_) => None,
10177 HoverLink::File(_) => None,
10178 })
10179 .unwrap_or(tab_kind.to_string());
10180 let location_tasks = definitions
10181 .into_iter()
10182 .map(|definition| match definition {
10183 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
10184 HoverLink::InlayHint(lsp_location, server_id) => {
10185 editor.compute_target_location(lsp_location, server_id, cx)
10186 }
10187 HoverLink::Url(_) => Task::ready(Ok(None)),
10188 HoverLink::File(_) => Task::ready(Ok(None)),
10189 })
10190 .collect::<Vec<_>>();
10191 (title, location_tasks, editor.workspace().clone())
10192 })
10193 .context("location tasks preparation")?;
10194
10195 let locations = future::join_all(location_tasks)
10196 .await
10197 .into_iter()
10198 .filter_map(|location| location.transpose())
10199 .collect::<Result<_>>()
10200 .context("location tasks")?;
10201
10202 let Some(workspace) = workspace else {
10203 return Ok(Navigated::No);
10204 };
10205 let opened = workspace
10206 .update(&mut cx, |workspace, cx| {
10207 Self::open_locations_in_multibuffer(workspace, locations, title, split, cx)
10208 })
10209 .ok();
10210
10211 anyhow::Ok(Navigated::from_bool(opened.is_some()))
10212 })
10213 } else {
10214 Task::ready(Ok(Navigated::No))
10215 }
10216 }
10217
10218 fn compute_target_location(
10219 &self,
10220 lsp_location: lsp::Location,
10221 server_id: LanguageServerId,
10222 cx: &mut ViewContext<Self>,
10223 ) -> Task<anyhow::Result<Option<Location>>> {
10224 let Some(project) = self.project.clone() else {
10225 return Task::Ready(Some(Ok(None)));
10226 };
10227
10228 cx.spawn(move |editor, mut cx| async move {
10229 let location_task = editor.update(&mut cx, |_, cx| {
10230 project.update(cx, |project, cx| {
10231 let language_server_name = project
10232 .language_server_statuses(cx)
10233 .find(|(id, _)| server_id == *id)
10234 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
10235 language_server_name.map(|language_server_name| {
10236 project.open_local_buffer_via_lsp(
10237 lsp_location.uri.clone(),
10238 server_id,
10239 language_server_name,
10240 cx,
10241 )
10242 })
10243 })
10244 })?;
10245 let location = match location_task {
10246 Some(task) => Some({
10247 let target_buffer_handle = task.await.context("open local buffer")?;
10248 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
10249 let target_start = target_buffer
10250 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
10251 let target_end = target_buffer
10252 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
10253 target_buffer.anchor_after(target_start)
10254 ..target_buffer.anchor_before(target_end)
10255 })?;
10256 Location {
10257 buffer: target_buffer_handle,
10258 range,
10259 }
10260 }),
10261 None => None,
10262 };
10263 Ok(location)
10264 })
10265 }
10266
10267 pub fn find_all_references(
10268 &mut self,
10269 _: &FindAllReferences,
10270 cx: &mut ViewContext<Self>,
10271 ) -> Option<Task<Result<Navigated>>> {
10272 let selection = self.selections.newest::<usize>(cx);
10273 let multi_buffer = self.buffer.read(cx);
10274 let head = selection.head();
10275
10276 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
10277 let head_anchor = multi_buffer_snapshot.anchor_at(
10278 head,
10279 if head < selection.tail() {
10280 Bias::Right
10281 } else {
10282 Bias::Left
10283 },
10284 );
10285
10286 match self
10287 .find_all_references_task_sources
10288 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
10289 {
10290 Ok(_) => {
10291 log::info!(
10292 "Ignoring repeated FindAllReferences invocation with the position of already running task"
10293 );
10294 return None;
10295 }
10296 Err(i) => {
10297 self.find_all_references_task_sources.insert(i, head_anchor);
10298 }
10299 }
10300
10301 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
10302 let workspace = self.workspace()?;
10303 let project = workspace.read(cx).project().clone();
10304 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
10305 Some(cx.spawn(|editor, mut cx| async move {
10306 let _cleanup = defer({
10307 let mut cx = cx.clone();
10308 move || {
10309 let _ = editor.update(&mut cx, |editor, _| {
10310 if let Ok(i) =
10311 editor
10312 .find_all_references_task_sources
10313 .binary_search_by(|anchor| {
10314 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
10315 })
10316 {
10317 editor.find_all_references_task_sources.remove(i);
10318 }
10319 });
10320 }
10321 });
10322
10323 let locations = references.await?;
10324 if locations.is_empty() {
10325 return anyhow::Ok(Navigated::No);
10326 }
10327
10328 workspace.update(&mut cx, |workspace, cx| {
10329 let title = locations
10330 .first()
10331 .as_ref()
10332 .map(|location| {
10333 let buffer = location.buffer.read(cx);
10334 format!(
10335 "References to `{}`",
10336 buffer
10337 .text_for_range(location.range.clone())
10338 .collect::<String>()
10339 )
10340 })
10341 .unwrap();
10342 Self::open_locations_in_multibuffer(workspace, locations, title, false, cx);
10343 Navigated::Yes
10344 })
10345 }))
10346 }
10347
10348 /// Opens a multibuffer with the given project locations in it
10349 pub fn open_locations_in_multibuffer(
10350 workspace: &mut Workspace,
10351 mut locations: Vec<Location>,
10352 title: String,
10353 split: bool,
10354 cx: &mut ViewContext<Workspace>,
10355 ) {
10356 // If there are multiple definitions, open them in a multibuffer
10357 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
10358 let mut locations = locations.into_iter().peekable();
10359 let mut ranges_to_highlight = Vec::new();
10360 let capability = workspace.project().read(cx).capability();
10361
10362 let excerpt_buffer = cx.new_model(|cx| {
10363 let mut multibuffer = MultiBuffer::new(capability);
10364 while let Some(location) = locations.next() {
10365 let buffer = location.buffer.read(cx);
10366 let mut ranges_for_buffer = Vec::new();
10367 let range = location.range.to_offset(buffer);
10368 ranges_for_buffer.push(range.clone());
10369
10370 while let Some(next_location) = locations.peek() {
10371 if next_location.buffer == location.buffer {
10372 ranges_for_buffer.push(next_location.range.to_offset(buffer));
10373 locations.next();
10374 } else {
10375 break;
10376 }
10377 }
10378
10379 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
10380 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
10381 location.buffer.clone(),
10382 ranges_for_buffer,
10383 DEFAULT_MULTIBUFFER_CONTEXT,
10384 cx,
10385 ))
10386 }
10387
10388 multibuffer.with_title(title)
10389 });
10390
10391 let editor = cx.new_view(|cx| {
10392 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), true, cx)
10393 });
10394 editor.update(cx, |editor, cx| {
10395 if let Some(first_range) = ranges_to_highlight.first() {
10396 editor.change_selections(None, cx, |selections| {
10397 selections.clear_disjoint();
10398 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
10399 });
10400 }
10401 editor.highlight_background::<Self>(
10402 &ranges_to_highlight,
10403 |theme| theme.editor_highlighted_line_background,
10404 cx,
10405 );
10406 });
10407
10408 let item = Box::new(editor);
10409 let item_id = item.item_id();
10410
10411 if split {
10412 workspace.split_item(SplitDirection::Right, item.clone(), cx);
10413 } else {
10414 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
10415 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
10416 pane.close_current_preview_item(cx)
10417 } else {
10418 None
10419 }
10420 });
10421 workspace.add_item_to_active_pane(item.clone(), destination_index, true, cx);
10422 }
10423 workspace.active_pane().update(cx, |pane, cx| {
10424 pane.set_preview_item_id(Some(item_id), cx);
10425 });
10426 }
10427
10428 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
10429 use language::ToOffset as _;
10430
10431 let provider = self.semantics_provider.clone()?;
10432 let selection = self.selections.newest_anchor().clone();
10433 let (cursor_buffer, cursor_buffer_position) = self
10434 .buffer
10435 .read(cx)
10436 .text_anchor_for_position(selection.head(), cx)?;
10437 let (tail_buffer, cursor_buffer_position_end) = self
10438 .buffer
10439 .read(cx)
10440 .text_anchor_for_position(selection.tail(), cx)?;
10441 if tail_buffer != cursor_buffer {
10442 return None;
10443 }
10444
10445 let snapshot = cursor_buffer.read(cx).snapshot();
10446 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
10447 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
10448 let prepare_rename = provider
10449 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
10450 .unwrap_or_else(|| Task::ready(Ok(None)));
10451 drop(snapshot);
10452
10453 Some(cx.spawn(|this, mut cx| async move {
10454 let rename_range = if let Some(range) = prepare_rename.await? {
10455 Some(range)
10456 } else {
10457 this.update(&mut cx, |this, cx| {
10458 let buffer = this.buffer.read(cx).snapshot(cx);
10459 let mut buffer_highlights = this
10460 .document_highlights_for_position(selection.head(), &buffer)
10461 .filter(|highlight| {
10462 highlight.start.excerpt_id == selection.head().excerpt_id
10463 && highlight.end.excerpt_id == selection.head().excerpt_id
10464 });
10465 buffer_highlights
10466 .next()
10467 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
10468 })?
10469 };
10470 if let Some(rename_range) = rename_range {
10471 this.update(&mut cx, |this, cx| {
10472 let snapshot = cursor_buffer.read(cx).snapshot();
10473 let rename_buffer_range = rename_range.to_offset(&snapshot);
10474 let cursor_offset_in_rename_range =
10475 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
10476 let cursor_offset_in_rename_range_end =
10477 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
10478
10479 this.take_rename(false, cx);
10480 let buffer = this.buffer.read(cx).read(cx);
10481 let cursor_offset = selection.head().to_offset(&buffer);
10482 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
10483 let rename_end = rename_start + rename_buffer_range.len();
10484 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
10485 let mut old_highlight_id = None;
10486 let old_name: Arc<str> = buffer
10487 .chunks(rename_start..rename_end, true)
10488 .map(|chunk| {
10489 if old_highlight_id.is_none() {
10490 old_highlight_id = chunk.syntax_highlight_id;
10491 }
10492 chunk.text
10493 })
10494 .collect::<String>()
10495 .into();
10496
10497 drop(buffer);
10498
10499 // Position the selection in the rename editor so that it matches the current selection.
10500 this.show_local_selections = false;
10501 let rename_editor = cx.new_view(|cx| {
10502 let mut editor = Editor::single_line(cx);
10503 editor.buffer.update(cx, |buffer, cx| {
10504 buffer.edit([(0..0, old_name.clone())], None, cx)
10505 });
10506 let rename_selection_range = match cursor_offset_in_rename_range
10507 .cmp(&cursor_offset_in_rename_range_end)
10508 {
10509 Ordering::Equal => {
10510 editor.select_all(&SelectAll, cx);
10511 return editor;
10512 }
10513 Ordering::Less => {
10514 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
10515 }
10516 Ordering::Greater => {
10517 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
10518 }
10519 };
10520 if rename_selection_range.end > old_name.len() {
10521 editor.select_all(&SelectAll, cx);
10522 } else {
10523 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
10524 s.select_ranges([rename_selection_range]);
10525 });
10526 }
10527 editor
10528 });
10529 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
10530 if e == &EditorEvent::Focused {
10531 cx.emit(EditorEvent::FocusedIn)
10532 }
10533 })
10534 .detach();
10535
10536 let write_highlights =
10537 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
10538 let read_highlights =
10539 this.clear_background_highlights::<DocumentHighlightRead>(cx);
10540 let ranges = write_highlights
10541 .iter()
10542 .flat_map(|(_, ranges)| ranges.iter())
10543 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
10544 .cloned()
10545 .collect();
10546
10547 this.highlight_text::<Rename>(
10548 ranges,
10549 HighlightStyle {
10550 fade_out: Some(0.6),
10551 ..Default::default()
10552 },
10553 cx,
10554 );
10555 let rename_focus_handle = rename_editor.focus_handle(cx);
10556 cx.focus(&rename_focus_handle);
10557 let block_id = this.insert_blocks(
10558 [BlockProperties {
10559 style: BlockStyle::Flex,
10560 placement: BlockPlacement::Below(range.start),
10561 height: 1,
10562 render: Arc::new({
10563 let rename_editor = rename_editor.clone();
10564 move |cx: &mut BlockContext| {
10565 let mut text_style = cx.editor_style.text.clone();
10566 if let Some(highlight_style) = old_highlight_id
10567 .and_then(|h| h.style(&cx.editor_style.syntax))
10568 {
10569 text_style = text_style.highlight(highlight_style);
10570 }
10571 div()
10572 .block_mouse_down()
10573 .pl(cx.anchor_x)
10574 .child(EditorElement::new(
10575 &rename_editor,
10576 EditorStyle {
10577 background: cx.theme().system().transparent,
10578 local_player: cx.editor_style.local_player,
10579 text: text_style,
10580 scrollbar_width: cx.editor_style.scrollbar_width,
10581 syntax: cx.editor_style.syntax.clone(),
10582 status: cx.editor_style.status.clone(),
10583 inlay_hints_style: HighlightStyle {
10584 font_weight: Some(FontWeight::BOLD),
10585 ..make_inlay_hints_style(cx)
10586 },
10587 suggestions_style: HighlightStyle {
10588 color: Some(cx.theme().status().predictive),
10589 ..HighlightStyle::default()
10590 },
10591 ..EditorStyle::default()
10592 },
10593 ))
10594 .into_any_element()
10595 }
10596 }),
10597 priority: 0,
10598 }],
10599 Some(Autoscroll::fit()),
10600 cx,
10601 )[0];
10602 this.pending_rename = Some(RenameState {
10603 range,
10604 old_name,
10605 editor: rename_editor,
10606 block_id,
10607 });
10608 })?;
10609 }
10610
10611 Ok(())
10612 }))
10613 }
10614
10615 pub fn confirm_rename(
10616 &mut self,
10617 _: &ConfirmRename,
10618 cx: &mut ViewContext<Self>,
10619 ) -> Option<Task<Result<()>>> {
10620 let rename = self.take_rename(false, cx)?;
10621 let workspace = self.workspace()?.downgrade();
10622 let (buffer, start) = self
10623 .buffer
10624 .read(cx)
10625 .text_anchor_for_position(rename.range.start, cx)?;
10626 let (end_buffer, _) = self
10627 .buffer
10628 .read(cx)
10629 .text_anchor_for_position(rename.range.end, cx)?;
10630 if buffer != end_buffer {
10631 return None;
10632 }
10633
10634 let old_name = rename.old_name;
10635 let new_name = rename.editor.read(cx).text(cx);
10636
10637 let rename = self.semantics_provider.as_ref()?.perform_rename(
10638 &buffer,
10639 start,
10640 new_name.clone(),
10641 cx,
10642 )?;
10643
10644 Some(cx.spawn(|editor, mut cx| async move {
10645 let project_transaction = rename.await?;
10646 Self::open_project_transaction(
10647 &editor,
10648 workspace,
10649 project_transaction,
10650 format!("Rename: {} → {}", old_name, new_name),
10651 cx.clone(),
10652 )
10653 .await?;
10654
10655 editor.update(&mut cx, |editor, cx| {
10656 editor.refresh_document_highlights(cx);
10657 })?;
10658 Ok(())
10659 }))
10660 }
10661
10662 fn take_rename(
10663 &mut self,
10664 moving_cursor: bool,
10665 cx: &mut ViewContext<Self>,
10666 ) -> Option<RenameState> {
10667 let rename = self.pending_rename.take()?;
10668 if rename.editor.focus_handle(cx).is_focused(cx) {
10669 cx.focus(&self.focus_handle);
10670 }
10671
10672 self.remove_blocks(
10673 [rename.block_id].into_iter().collect(),
10674 Some(Autoscroll::fit()),
10675 cx,
10676 );
10677 self.clear_highlights::<Rename>(cx);
10678 self.show_local_selections = true;
10679
10680 if moving_cursor {
10681 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
10682 editor.selections.newest::<usize>(cx).head()
10683 });
10684
10685 // Update the selection to match the position of the selection inside
10686 // the rename editor.
10687 let snapshot = self.buffer.read(cx).read(cx);
10688 let rename_range = rename.range.to_offset(&snapshot);
10689 let cursor_in_editor = snapshot
10690 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
10691 .min(rename_range.end);
10692 drop(snapshot);
10693
10694 self.change_selections(None, cx, |s| {
10695 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
10696 });
10697 } else {
10698 self.refresh_document_highlights(cx);
10699 }
10700
10701 Some(rename)
10702 }
10703
10704 pub fn pending_rename(&self) -> Option<&RenameState> {
10705 self.pending_rename.as_ref()
10706 }
10707
10708 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
10709 let project = match &self.project {
10710 Some(project) => project.clone(),
10711 None => return None,
10712 };
10713
10714 Some(self.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffer, cx))
10715 }
10716
10717 fn format_selections(
10718 &mut self,
10719 _: &FormatSelections,
10720 cx: &mut ViewContext<Self>,
10721 ) -> Option<Task<Result<()>>> {
10722 let project = match &self.project {
10723 Some(project) => project.clone(),
10724 None => return None,
10725 };
10726
10727 let selections = self
10728 .selections
10729 .all_adjusted(cx)
10730 .into_iter()
10731 .filter(|s| !s.is_empty())
10732 .collect_vec();
10733
10734 Some(self.perform_format(
10735 project,
10736 FormatTrigger::Manual,
10737 FormatTarget::Ranges(selections),
10738 cx,
10739 ))
10740 }
10741
10742 fn perform_format(
10743 &mut self,
10744 project: Model<Project>,
10745 trigger: FormatTrigger,
10746 target: FormatTarget,
10747 cx: &mut ViewContext<Self>,
10748 ) -> Task<Result<()>> {
10749 let buffer = self.buffer().clone();
10750 let mut buffers = buffer.read(cx).all_buffers();
10751 if trigger == FormatTrigger::Save {
10752 buffers.retain(|buffer| buffer.read(cx).is_dirty());
10753 }
10754
10755 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
10756 let format = project.update(cx, |project, cx| {
10757 project.format(buffers, true, trigger, target, cx)
10758 });
10759
10760 cx.spawn(|_, mut cx| async move {
10761 let transaction = futures::select_biased! {
10762 () = timeout => {
10763 log::warn!("timed out waiting for formatting");
10764 None
10765 }
10766 transaction = format.log_err().fuse() => transaction,
10767 };
10768
10769 buffer
10770 .update(&mut cx, |buffer, cx| {
10771 if let Some(transaction) = transaction {
10772 if !buffer.is_singleton() {
10773 buffer.push_transaction(&transaction.0, cx);
10774 }
10775 }
10776
10777 cx.notify();
10778 })
10779 .ok();
10780
10781 Ok(())
10782 })
10783 }
10784
10785 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
10786 if let Some(project) = self.project.clone() {
10787 self.buffer.update(cx, |multi_buffer, cx| {
10788 project.update(cx, |project, cx| {
10789 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
10790 });
10791 })
10792 }
10793 }
10794
10795 fn cancel_language_server_work(
10796 &mut self,
10797 _: &actions::CancelLanguageServerWork,
10798 cx: &mut ViewContext<Self>,
10799 ) {
10800 if let Some(project) = self.project.clone() {
10801 self.buffer.update(cx, |multi_buffer, cx| {
10802 project.update(cx, |project, cx| {
10803 project.cancel_language_server_work_for_buffers(multi_buffer.all_buffers(), cx);
10804 });
10805 })
10806 }
10807 }
10808
10809 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
10810 cx.show_character_palette();
10811 }
10812
10813 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
10814 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
10815 let buffer = self.buffer.read(cx).snapshot(cx);
10816 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
10817 let is_valid = buffer
10818 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
10819 .any(|entry| {
10820 entry.diagnostic.is_primary
10821 && !entry.range.is_empty()
10822 && entry.range.start == primary_range_start
10823 && entry.diagnostic.message == active_diagnostics.primary_message
10824 });
10825
10826 if is_valid != active_diagnostics.is_valid {
10827 active_diagnostics.is_valid = is_valid;
10828 let mut new_styles = HashMap::default();
10829 for (block_id, diagnostic) in &active_diagnostics.blocks {
10830 new_styles.insert(
10831 *block_id,
10832 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
10833 );
10834 }
10835 self.display_map.update(cx, |display_map, _cx| {
10836 display_map.replace_blocks(new_styles)
10837 });
10838 }
10839 }
10840 }
10841
10842 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
10843 self.dismiss_diagnostics(cx);
10844 let snapshot = self.snapshot(cx);
10845 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
10846 let buffer = self.buffer.read(cx).snapshot(cx);
10847
10848 let mut primary_range = None;
10849 let mut primary_message = None;
10850 let mut group_end = Point::zero();
10851 let diagnostic_group = buffer
10852 .diagnostic_group::<MultiBufferPoint>(group_id)
10853 .filter_map(|entry| {
10854 if snapshot.is_line_folded(MultiBufferRow(entry.range.start.row))
10855 && (entry.range.start.row == entry.range.end.row
10856 || snapshot.is_line_folded(MultiBufferRow(entry.range.end.row)))
10857 {
10858 return None;
10859 }
10860 if entry.range.end > group_end {
10861 group_end = entry.range.end;
10862 }
10863 if entry.diagnostic.is_primary {
10864 primary_range = Some(entry.range.clone());
10865 primary_message = Some(entry.diagnostic.message.clone());
10866 }
10867 Some(entry)
10868 })
10869 .collect::<Vec<_>>();
10870 let primary_range = primary_range?;
10871 let primary_message = primary_message?;
10872 let primary_range =
10873 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
10874
10875 let blocks = display_map
10876 .insert_blocks(
10877 diagnostic_group.iter().map(|entry| {
10878 let diagnostic = entry.diagnostic.clone();
10879 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
10880 BlockProperties {
10881 style: BlockStyle::Fixed,
10882 placement: BlockPlacement::Below(
10883 buffer.anchor_after(entry.range.start),
10884 ),
10885 height: message_height,
10886 render: diagnostic_block_renderer(diagnostic, None, true, true),
10887 priority: 0,
10888 }
10889 }),
10890 cx,
10891 )
10892 .into_iter()
10893 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
10894 .collect();
10895
10896 Some(ActiveDiagnosticGroup {
10897 primary_range,
10898 primary_message,
10899 group_id,
10900 blocks,
10901 is_valid: true,
10902 })
10903 });
10904 self.active_diagnostics.is_some()
10905 }
10906
10907 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
10908 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
10909 self.display_map.update(cx, |display_map, cx| {
10910 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
10911 });
10912 cx.notify();
10913 }
10914 }
10915
10916 pub fn set_selections_from_remote(
10917 &mut self,
10918 selections: Vec<Selection<Anchor>>,
10919 pending_selection: Option<Selection<Anchor>>,
10920 cx: &mut ViewContext<Self>,
10921 ) {
10922 let old_cursor_position = self.selections.newest_anchor().head();
10923 self.selections.change_with(cx, |s| {
10924 s.select_anchors(selections);
10925 if let Some(pending_selection) = pending_selection {
10926 s.set_pending(pending_selection, SelectMode::Character);
10927 } else {
10928 s.clear_pending();
10929 }
10930 });
10931 self.selections_did_change(false, &old_cursor_position, true, cx);
10932 }
10933
10934 fn push_to_selection_history(&mut self) {
10935 self.selection_history.push(SelectionHistoryEntry {
10936 selections: self.selections.disjoint_anchors(),
10937 select_next_state: self.select_next_state.clone(),
10938 select_prev_state: self.select_prev_state.clone(),
10939 add_selections_state: self.add_selections_state.clone(),
10940 });
10941 }
10942
10943 pub fn transact(
10944 &mut self,
10945 cx: &mut ViewContext<Self>,
10946 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
10947 ) -> Option<TransactionId> {
10948 self.start_transaction_at(Instant::now(), cx);
10949 update(self, cx);
10950 self.end_transaction_at(Instant::now(), cx)
10951 }
10952
10953 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
10954 self.end_selection(cx);
10955 if let Some(tx_id) = self
10956 .buffer
10957 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
10958 {
10959 self.selection_history
10960 .insert_transaction(tx_id, self.selections.disjoint_anchors());
10961 cx.emit(EditorEvent::TransactionBegun {
10962 transaction_id: tx_id,
10963 })
10964 }
10965 }
10966
10967 fn end_transaction_at(
10968 &mut self,
10969 now: Instant,
10970 cx: &mut ViewContext<Self>,
10971 ) -> Option<TransactionId> {
10972 if let Some(transaction_id) = self
10973 .buffer
10974 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
10975 {
10976 if let Some((_, end_selections)) =
10977 self.selection_history.transaction_mut(transaction_id)
10978 {
10979 *end_selections = Some(self.selections.disjoint_anchors());
10980 } else {
10981 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
10982 }
10983
10984 cx.emit(EditorEvent::Edited { transaction_id });
10985 Some(transaction_id)
10986 } else {
10987 None
10988 }
10989 }
10990
10991 pub fn toggle_fold(&mut self, _: &actions::ToggleFold, cx: &mut ViewContext<Self>) {
10992 let selection = self.selections.newest::<Point>(cx);
10993
10994 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10995 let range = if selection.is_empty() {
10996 let point = selection.head().to_display_point(&display_map);
10997 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
10998 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
10999 .to_point(&display_map);
11000 start..end
11001 } else {
11002 selection.range()
11003 };
11004 if display_map.folds_in_range(range).next().is_some() {
11005 self.unfold_lines(&Default::default(), cx)
11006 } else {
11007 self.fold(&Default::default(), cx)
11008 }
11009 }
11010
11011 pub fn toggle_fold_recursive(
11012 &mut self,
11013 _: &actions::ToggleFoldRecursive,
11014 cx: &mut ViewContext<Self>,
11015 ) {
11016 let selection = self.selections.newest::<Point>(cx);
11017
11018 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11019 let range = if selection.is_empty() {
11020 let point = selection.head().to_display_point(&display_map);
11021 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
11022 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
11023 .to_point(&display_map);
11024 start..end
11025 } else {
11026 selection.range()
11027 };
11028 if display_map.folds_in_range(range).next().is_some() {
11029 self.unfold_recursive(&Default::default(), cx)
11030 } else {
11031 self.fold_recursive(&Default::default(), cx)
11032 }
11033 }
11034
11035 pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
11036 let mut to_fold = Vec::new();
11037 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11038 let selections = self.selections.all_adjusted(cx);
11039
11040 for selection in selections {
11041 let range = selection.range().sorted();
11042 let buffer_start_row = range.start.row;
11043
11044 if range.start.row != range.end.row {
11045 let mut found = false;
11046 let mut row = range.start.row;
11047 while row <= range.end.row {
11048 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
11049 found = true;
11050 row = crease.range().end.row + 1;
11051 to_fold.push(crease);
11052 } else {
11053 row += 1
11054 }
11055 }
11056 if found {
11057 continue;
11058 }
11059 }
11060
11061 for row in (0..=range.start.row).rev() {
11062 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
11063 if crease.range().end.row >= buffer_start_row {
11064 to_fold.push(crease);
11065 if row <= range.start.row {
11066 break;
11067 }
11068 }
11069 }
11070 }
11071 }
11072
11073 self.fold_creases(to_fold, true, cx);
11074 }
11075
11076 fn fold_at_level(&mut self, fold_at: &FoldAtLevel, cx: &mut ViewContext<Self>) {
11077 if !self.buffer.read(cx).is_singleton() {
11078 return;
11079 }
11080
11081 let fold_at_level = fold_at.level;
11082 let snapshot = self.buffer.read(cx).snapshot(cx);
11083 let mut to_fold = Vec::new();
11084 let mut stack = vec![(0, snapshot.max_row().0, 1)];
11085
11086 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
11087 while start_row < end_row {
11088 match self
11089 .snapshot(cx)
11090 .crease_for_buffer_row(MultiBufferRow(start_row))
11091 {
11092 Some(crease) => {
11093 let nested_start_row = crease.range().start.row + 1;
11094 let nested_end_row = crease.range().end.row;
11095
11096 if current_level < fold_at_level {
11097 stack.push((nested_start_row, nested_end_row, current_level + 1));
11098 } else if current_level == fold_at_level {
11099 to_fold.push(crease);
11100 }
11101
11102 start_row = nested_end_row + 1;
11103 }
11104 None => start_row += 1,
11105 }
11106 }
11107 }
11108
11109 self.fold_creases(to_fold, true, cx);
11110 }
11111
11112 pub fn fold_all(&mut self, _: &actions::FoldAll, cx: &mut ViewContext<Self>) {
11113 if !self.buffer.read(cx).is_singleton() {
11114 return;
11115 }
11116
11117 let mut fold_ranges = Vec::new();
11118 let snapshot = self.buffer.read(cx).snapshot(cx);
11119
11120 for row in 0..snapshot.max_row().0 {
11121 if let Some(foldable_range) =
11122 self.snapshot(cx).crease_for_buffer_row(MultiBufferRow(row))
11123 {
11124 fold_ranges.push(foldable_range);
11125 }
11126 }
11127
11128 self.fold_creases(fold_ranges, true, cx);
11129 }
11130
11131 pub fn fold_function_bodies(
11132 &mut self,
11133 _: &actions::FoldFunctionBodies,
11134 cx: &mut ViewContext<Self>,
11135 ) {
11136 let snapshot = self.buffer.read(cx).snapshot(cx);
11137 let Some((_, _, buffer)) = snapshot.as_singleton() else {
11138 return;
11139 };
11140 let creases = buffer
11141 .function_body_fold_ranges(0..buffer.len())
11142 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
11143 .collect();
11144
11145 self.fold_creases(creases, true, cx);
11146 }
11147
11148 pub fn fold_recursive(&mut self, _: &actions::FoldRecursive, cx: &mut ViewContext<Self>) {
11149 let mut to_fold = Vec::new();
11150 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11151 let selections = self.selections.all_adjusted(cx);
11152
11153 for selection in selections {
11154 let range = selection.range().sorted();
11155 let buffer_start_row = range.start.row;
11156
11157 if range.start.row != range.end.row {
11158 let mut found = false;
11159 for row in range.start.row..=range.end.row {
11160 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
11161 found = true;
11162 to_fold.push(crease);
11163 }
11164 }
11165 if found {
11166 continue;
11167 }
11168 }
11169
11170 for row in (0..=range.start.row).rev() {
11171 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
11172 if crease.range().end.row >= buffer_start_row {
11173 to_fold.push(crease);
11174 } else {
11175 break;
11176 }
11177 }
11178 }
11179 }
11180
11181 self.fold_creases(to_fold, true, cx);
11182 }
11183
11184 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
11185 let buffer_row = fold_at.buffer_row;
11186 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11187
11188 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
11189 let autoscroll = self
11190 .selections
11191 .all::<Point>(cx)
11192 .iter()
11193 .any(|selection| crease.range().overlaps(&selection.range()));
11194
11195 self.fold_creases(vec![crease], autoscroll, cx);
11196 }
11197 }
11198
11199 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
11200 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11201 let buffer = &display_map.buffer_snapshot;
11202 let selections = self.selections.all::<Point>(cx);
11203 let ranges = selections
11204 .iter()
11205 .map(|s| {
11206 let range = s.display_range(&display_map).sorted();
11207 let mut start = range.start.to_point(&display_map);
11208 let mut end = range.end.to_point(&display_map);
11209 start.column = 0;
11210 end.column = buffer.line_len(MultiBufferRow(end.row));
11211 start..end
11212 })
11213 .collect::<Vec<_>>();
11214
11215 self.unfold_ranges(&ranges, true, true, cx);
11216 }
11217
11218 pub fn unfold_recursive(&mut self, _: &UnfoldRecursive, cx: &mut ViewContext<Self>) {
11219 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11220 let selections = self.selections.all::<Point>(cx);
11221 let ranges = selections
11222 .iter()
11223 .map(|s| {
11224 let mut range = s.display_range(&display_map).sorted();
11225 *range.start.column_mut() = 0;
11226 *range.end.column_mut() = display_map.line_len(range.end.row());
11227 let start = range.start.to_point(&display_map);
11228 let end = range.end.to_point(&display_map);
11229 start..end
11230 })
11231 .collect::<Vec<_>>();
11232
11233 self.unfold_ranges(&ranges, true, true, cx);
11234 }
11235
11236 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
11237 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11238
11239 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
11240 ..Point::new(
11241 unfold_at.buffer_row.0,
11242 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
11243 );
11244
11245 let autoscroll = self
11246 .selections
11247 .all::<Point>(cx)
11248 .iter()
11249 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
11250
11251 self.unfold_ranges(&[intersection_range], true, autoscroll, cx)
11252 }
11253
11254 pub fn unfold_all(&mut self, _: &actions::UnfoldAll, cx: &mut ViewContext<Self>) {
11255 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11256 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
11257 }
11258
11259 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
11260 let selections = self.selections.all::<Point>(cx);
11261 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11262 let line_mode = self.selections.line_mode;
11263 let ranges = selections
11264 .into_iter()
11265 .map(|s| {
11266 if line_mode {
11267 let start = Point::new(s.start.row, 0);
11268 let end = Point::new(
11269 s.end.row,
11270 display_map
11271 .buffer_snapshot
11272 .line_len(MultiBufferRow(s.end.row)),
11273 );
11274 Crease::simple(start..end, display_map.fold_placeholder.clone())
11275 } else {
11276 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
11277 }
11278 })
11279 .collect::<Vec<_>>();
11280 self.fold_creases(ranges, true, cx);
11281 }
11282
11283 pub fn fold_creases<T: ToOffset + Clone>(
11284 &mut self,
11285 creases: Vec<Crease<T>>,
11286 auto_scroll: bool,
11287 cx: &mut ViewContext<Self>,
11288 ) {
11289 if creases.is_empty() {
11290 return;
11291 }
11292
11293 let mut buffers_affected = HashSet::default();
11294 let multi_buffer = self.buffer().read(cx);
11295 for crease in &creases {
11296 if let Some((_, buffer, _)) =
11297 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
11298 {
11299 buffers_affected.insert(buffer.read(cx).remote_id());
11300 };
11301 }
11302
11303 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
11304
11305 if auto_scroll {
11306 self.request_autoscroll(Autoscroll::fit(), cx);
11307 }
11308
11309 for buffer_id in buffers_affected {
11310 Self::sync_expanded_diff_hunks(&mut self.diff_map, buffer_id, cx);
11311 }
11312
11313 cx.notify();
11314
11315 if let Some(active_diagnostics) = self.active_diagnostics.take() {
11316 // Clear diagnostics block when folding a range that contains it.
11317 let snapshot = self.snapshot(cx);
11318 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
11319 drop(snapshot);
11320 self.active_diagnostics = Some(active_diagnostics);
11321 self.dismiss_diagnostics(cx);
11322 } else {
11323 self.active_diagnostics = Some(active_diagnostics);
11324 }
11325 }
11326
11327 self.scrollbar_marker_state.dirty = true;
11328 }
11329
11330 /// Removes any folds whose ranges intersect any of the given ranges.
11331 pub fn unfold_ranges<T: ToOffset + Clone>(
11332 &mut self,
11333 ranges: &[Range<T>],
11334 inclusive: bool,
11335 auto_scroll: bool,
11336 cx: &mut ViewContext<Self>,
11337 ) {
11338 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
11339 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
11340 });
11341 }
11342
11343 /// Removes any folds with the given ranges.
11344 pub fn remove_folds_with_type<T: ToOffset + Clone>(
11345 &mut self,
11346 ranges: &[Range<T>],
11347 type_id: TypeId,
11348 auto_scroll: bool,
11349 cx: &mut ViewContext<Self>,
11350 ) {
11351 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
11352 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
11353 });
11354 }
11355
11356 fn remove_folds_with<T: ToOffset + Clone>(
11357 &mut self,
11358 ranges: &[Range<T>],
11359 auto_scroll: bool,
11360 cx: &mut ViewContext<Self>,
11361 update: impl FnOnce(&mut DisplayMap, &mut ModelContext<DisplayMap>),
11362 ) {
11363 if ranges.is_empty() {
11364 return;
11365 }
11366
11367 let mut buffers_affected = HashSet::default();
11368 let multi_buffer = self.buffer().read(cx);
11369 for range in ranges {
11370 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
11371 buffers_affected.insert(buffer.read(cx).remote_id());
11372 };
11373 }
11374
11375 self.display_map.update(cx, update);
11376
11377 if auto_scroll {
11378 self.request_autoscroll(Autoscroll::fit(), cx);
11379 }
11380
11381 for buffer_id in buffers_affected {
11382 Self::sync_expanded_diff_hunks(&mut self.diff_map, buffer_id, cx);
11383 }
11384
11385 cx.notify();
11386 self.scrollbar_marker_state.dirty = true;
11387 self.active_indent_guides_state.dirty = true;
11388 }
11389
11390 pub fn default_fold_placeholder(&self, cx: &AppContext) -> FoldPlaceholder {
11391 self.display_map.read(cx).fold_placeholder.clone()
11392 }
11393
11394 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
11395 if hovered != self.gutter_hovered {
11396 self.gutter_hovered = hovered;
11397 cx.notify();
11398 }
11399 }
11400
11401 pub fn insert_blocks(
11402 &mut self,
11403 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
11404 autoscroll: Option<Autoscroll>,
11405 cx: &mut ViewContext<Self>,
11406 ) -> Vec<CustomBlockId> {
11407 let blocks = self
11408 .display_map
11409 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
11410 if let Some(autoscroll) = autoscroll {
11411 self.request_autoscroll(autoscroll, cx);
11412 }
11413 cx.notify();
11414 blocks
11415 }
11416
11417 pub fn resize_blocks(
11418 &mut self,
11419 heights: HashMap<CustomBlockId, u32>,
11420 autoscroll: Option<Autoscroll>,
11421 cx: &mut ViewContext<Self>,
11422 ) {
11423 self.display_map
11424 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
11425 if let Some(autoscroll) = autoscroll {
11426 self.request_autoscroll(autoscroll, cx);
11427 }
11428 cx.notify();
11429 }
11430
11431 pub fn replace_blocks(
11432 &mut self,
11433 renderers: HashMap<CustomBlockId, RenderBlock>,
11434 autoscroll: Option<Autoscroll>,
11435 cx: &mut ViewContext<Self>,
11436 ) {
11437 self.display_map
11438 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
11439 if let Some(autoscroll) = autoscroll {
11440 self.request_autoscroll(autoscroll, cx);
11441 }
11442 cx.notify();
11443 }
11444
11445 pub fn remove_blocks(
11446 &mut self,
11447 block_ids: HashSet<CustomBlockId>,
11448 autoscroll: Option<Autoscroll>,
11449 cx: &mut ViewContext<Self>,
11450 ) {
11451 self.display_map.update(cx, |display_map, cx| {
11452 display_map.remove_blocks(block_ids, cx)
11453 });
11454 if let Some(autoscroll) = autoscroll {
11455 self.request_autoscroll(autoscroll, cx);
11456 }
11457 cx.notify();
11458 }
11459
11460 pub fn row_for_block(
11461 &self,
11462 block_id: CustomBlockId,
11463 cx: &mut ViewContext<Self>,
11464 ) -> Option<DisplayRow> {
11465 self.display_map
11466 .update(cx, |map, cx| map.row_for_block(block_id, cx))
11467 }
11468
11469 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
11470 self.focused_block = Some(focused_block);
11471 }
11472
11473 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
11474 self.focused_block.take()
11475 }
11476
11477 pub fn insert_creases(
11478 &mut self,
11479 creases: impl IntoIterator<Item = Crease<Anchor>>,
11480 cx: &mut ViewContext<Self>,
11481 ) -> Vec<CreaseId> {
11482 self.display_map
11483 .update(cx, |map, cx| map.insert_creases(creases, cx))
11484 }
11485
11486 pub fn remove_creases(
11487 &mut self,
11488 ids: impl IntoIterator<Item = CreaseId>,
11489 cx: &mut ViewContext<Self>,
11490 ) {
11491 self.display_map
11492 .update(cx, |map, cx| map.remove_creases(ids, cx));
11493 }
11494
11495 pub fn longest_row(&self, cx: &mut AppContext) -> DisplayRow {
11496 self.display_map
11497 .update(cx, |map, cx| map.snapshot(cx))
11498 .longest_row()
11499 }
11500
11501 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
11502 self.display_map
11503 .update(cx, |map, cx| map.snapshot(cx))
11504 .max_point()
11505 }
11506
11507 pub fn text(&self, cx: &AppContext) -> String {
11508 self.buffer.read(cx).read(cx).text()
11509 }
11510
11511 pub fn text_option(&self, cx: &AppContext) -> Option<String> {
11512 let text = self.text(cx);
11513 let text = text.trim();
11514
11515 if text.is_empty() {
11516 return None;
11517 }
11518
11519 Some(text.to_string())
11520 }
11521
11522 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
11523 self.transact(cx, |this, cx| {
11524 this.buffer
11525 .read(cx)
11526 .as_singleton()
11527 .expect("you can only call set_text on editors for singleton buffers")
11528 .update(cx, |buffer, cx| buffer.set_text(text, cx));
11529 });
11530 }
11531
11532 pub fn display_text(&self, cx: &mut AppContext) -> String {
11533 self.display_map
11534 .update(cx, |map, cx| map.snapshot(cx))
11535 .text()
11536 }
11537
11538 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
11539 let mut wrap_guides = smallvec::smallvec![];
11540
11541 if self.show_wrap_guides == Some(false) {
11542 return wrap_guides;
11543 }
11544
11545 let settings = self.buffer.read(cx).settings_at(0, cx);
11546 if settings.show_wrap_guides {
11547 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
11548 wrap_guides.push((soft_wrap as usize, true));
11549 } else if let SoftWrap::Bounded(soft_wrap) = self.soft_wrap_mode(cx) {
11550 wrap_guides.push((soft_wrap as usize, true));
11551 }
11552 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
11553 }
11554
11555 wrap_guides
11556 }
11557
11558 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
11559 let settings = self.buffer.read(cx).settings_at(0, cx);
11560 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
11561 match mode {
11562 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
11563 SoftWrap::None
11564 }
11565 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
11566 language_settings::SoftWrap::PreferredLineLength => {
11567 SoftWrap::Column(settings.preferred_line_length)
11568 }
11569 language_settings::SoftWrap::Bounded => {
11570 SoftWrap::Bounded(settings.preferred_line_length)
11571 }
11572 }
11573 }
11574
11575 pub fn set_soft_wrap_mode(
11576 &mut self,
11577 mode: language_settings::SoftWrap,
11578 cx: &mut ViewContext<Self>,
11579 ) {
11580 self.soft_wrap_mode_override = Some(mode);
11581 cx.notify();
11582 }
11583
11584 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
11585 self.text_style_refinement = Some(style);
11586 }
11587
11588 /// called by the Element so we know what style we were most recently rendered with.
11589 pub(crate) fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
11590 let rem_size = cx.rem_size();
11591 self.display_map.update(cx, |map, cx| {
11592 map.set_font(
11593 style.text.font(),
11594 style.text.font_size.to_pixels(rem_size),
11595 cx,
11596 )
11597 });
11598 self.style = Some(style);
11599 }
11600
11601 pub fn style(&self) -> Option<&EditorStyle> {
11602 self.style.as_ref()
11603 }
11604
11605 // Called by the element. This method is not designed to be called outside of the editor
11606 // element's layout code because it does not notify when rewrapping is computed synchronously.
11607 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
11608 self.display_map
11609 .update(cx, |map, cx| map.set_wrap_width(width, cx))
11610 }
11611
11612 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
11613 if self.soft_wrap_mode_override.is_some() {
11614 self.soft_wrap_mode_override.take();
11615 } else {
11616 let soft_wrap = match self.soft_wrap_mode(cx) {
11617 SoftWrap::GitDiff => return,
11618 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
11619 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
11620 language_settings::SoftWrap::None
11621 }
11622 };
11623 self.soft_wrap_mode_override = Some(soft_wrap);
11624 }
11625 cx.notify();
11626 }
11627
11628 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, cx: &mut ViewContext<Self>) {
11629 let Some(workspace) = self.workspace() else {
11630 return;
11631 };
11632 let fs = workspace.read(cx).app_state().fs.clone();
11633 let current_show = TabBarSettings::get_global(cx).show;
11634 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
11635 setting.show = Some(!current_show);
11636 });
11637 }
11638
11639 pub fn toggle_indent_guides(&mut self, _: &ToggleIndentGuides, cx: &mut ViewContext<Self>) {
11640 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
11641 self.buffer
11642 .read(cx)
11643 .settings_at(0, cx)
11644 .indent_guides
11645 .enabled
11646 });
11647 self.show_indent_guides = Some(!currently_enabled);
11648 cx.notify();
11649 }
11650
11651 fn should_show_indent_guides(&self) -> Option<bool> {
11652 self.show_indent_guides
11653 }
11654
11655 pub fn toggle_line_numbers(&mut self, _: &ToggleLineNumbers, cx: &mut ViewContext<Self>) {
11656 let mut editor_settings = EditorSettings::get_global(cx).clone();
11657 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
11658 EditorSettings::override_global(editor_settings, cx);
11659 }
11660
11661 pub fn should_use_relative_line_numbers(&self, cx: &WindowContext) -> bool {
11662 self.use_relative_line_numbers
11663 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
11664 }
11665
11666 pub fn toggle_relative_line_numbers(
11667 &mut self,
11668 _: &ToggleRelativeLineNumbers,
11669 cx: &mut ViewContext<Self>,
11670 ) {
11671 let is_relative = self.should_use_relative_line_numbers(cx);
11672 self.set_relative_line_number(Some(!is_relative), cx)
11673 }
11674
11675 pub fn set_relative_line_number(
11676 &mut self,
11677 is_relative: Option<bool>,
11678 cx: &mut ViewContext<Self>,
11679 ) {
11680 self.use_relative_line_numbers = is_relative;
11681 cx.notify();
11682 }
11683
11684 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
11685 self.show_gutter = show_gutter;
11686 cx.notify();
11687 }
11688
11689 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut ViewContext<Self>) {
11690 self.show_line_numbers = Some(show_line_numbers);
11691 cx.notify();
11692 }
11693
11694 pub fn set_show_git_diff_gutter(
11695 &mut self,
11696 show_git_diff_gutter: bool,
11697 cx: &mut ViewContext<Self>,
11698 ) {
11699 self.show_git_diff_gutter = Some(show_git_diff_gutter);
11700 cx.notify();
11701 }
11702
11703 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut ViewContext<Self>) {
11704 self.show_code_actions = Some(show_code_actions);
11705 cx.notify();
11706 }
11707
11708 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut ViewContext<Self>) {
11709 self.show_runnables = Some(show_runnables);
11710 cx.notify();
11711 }
11712
11713 pub fn set_masked(&mut self, masked: bool, cx: &mut ViewContext<Self>) {
11714 if self.display_map.read(cx).masked != masked {
11715 self.display_map.update(cx, |map, _| map.masked = masked);
11716 }
11717 cx.notify()
11718 }
11719
11720 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut ViewContext<Self>) {
11721 self.show_wrap_guides = Some(show_wrap_guides);
11722 cx.notify();
11723 }
11724
11725 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut ViewContext<Self>) {
11726 self.show_indent_guides = Some(show_indent_guides);
11727 cx.notify();
11728 }
11729
11730 pub fn working_directory(&self, cx: &WindowContext) -> Option<PathBuf> {
11731 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
11732 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
11733 if let Some(dir) = file.abs_path(cx).parent() {
11734 return Some(dir.to_owned());
11735 }
11736 }
11737
11738 if let Some(project_path) = buffer.read(cx).project_path(cx) {
11739 return Some(project_path.path.to_path_buf());
11740 }
11741 }
11742
11743 None
11744 }
11745
11746 fn target_file<'a>(&self, cx: &'a AppContext) -> Option<&'a dyn language::LocalFile> {
11747 self.active_excerpt(cx)?
11748 .1
11749 .read(cx)
11750 .file()
11751 .and_then(|f| f.as_local())
11752 }
11753
11754 pub fn reveal_in_finder(&mut self, _: &RevealInFileManager, cx: &mut ViewContext<Self>) {
11755 if let Some(target) = self.target_file(cx) {
11756 cx.reveal_path(&target.abs_path(cx));
11757 }
11758 }
11759
11760 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
11761 if let Some(file) = self.target_file(cx) {
11762 if let Some(path) = file.abs_path(cx).to_str() {
11763 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
11764 }
11765 }
11766 }
11767
11768 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
11769 if let Some(file) = self.target_file(cx) {
11770 if let Some(path) = file.path().to_str() {
11771 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
11772 }
11773 }
11774 }
11775
11776 pub fn toggle_git_blame(&mut self, _: &ToggleGitBlame, cx: &mut ViewContext<Self>) {
11777 self.show_git_blame_gutter = !self.show_git_blame_gutter;
11778
11779 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
11780 self.start_git_blame(true, cx);
11781 }
11782
11783 cx.notify();
11784 }
11785
11786 pub fn toggle_git_blame_inline(
11787 &mut self,
11788 _: &ToggleGitBlameInline,
11789 cx: &mut ViewContext<Self>,
11790 ) {
11791 self.toggle_git_blame_inline_internal(true, cx);
11792 cx.notify();
11793 }
11794
11795 pub fn git_blame_inline_enabled(&self) -> bool {
11796 self.git_blame_inline_enabled
11797 }
11798
11799 pub fn toggle_selection_menu(&mut self, _: &ToggleSelectionMenu, cx: &mut ViewContext<Self>) {
11800 self.show_selection_menu = self
11801 .show_selection_menu
11802 .map(|show_selections_menu| !show_selections_menu)
11803 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
11804
11805 cx.notify();
11806 }
11807
11808 pub fn selection_menu_enabled(&self, cx: &AppContext) -> bool {
11809 self.show_selection_menu
11810 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
11811 }
11812
11813 fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
11814 if let Some(project) = self.project.as_ref() {
11815 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
11816 return;
11817 };
11818
11819 if buffer.read(cx).file().is_none() {
11820 return;
11821 }
11822
11823 let focused = self.focus_handle(cx).contains_focused(cx);
11824
11825 let project = project.clone();
11826 let blame =
11827 cx.new_model(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
11828 self.blame_subscription = Some(cx.observe(&blame, |_, _, cx| cx.notify()));
11829 self.blame = Some(blame);
11830 }
11831 }
11832
11833 fn toggle_git_blame_inline_internal(
11834 &mut self,
11835 user_triggered: bool,
11836 cx: &mut ViewContext<Self>,
11837 ) {
11838 if self.git_blame_inline_enabled {
11839 self.git_blame_inline_enabled = false;
11840 self.show_git_blame_inline = false;
11841 self.show_git_blame_inline_delay_task.take();
11842 } else {
11843 self.git_blame_inline_enabled = true;
11844 self.start_git_blame_inline(user_triggered, cx);
11845 }
11846
11847 cx.notify();
11848 }
11849
11850 fn start_git_blame_inline(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
11851 self.start_git_blame(user_triggered, cx);
11852
11853 if ProjectSettings::get_global(cx)
11854 .git
11855 .inline_blame_delay()
11856 .is_some()
11857 {
11858 self.start_inline_blame_timer(cx);
11859 } else {
11860 self.show_git_blame_inline = true
11861 }
11862 }
11863
11864 pub fn blame(&self) -> Option<&Model<GitBlame>> {
11865 self.blame.as_ref()
11866 }
11867
11868 pub fn show_git_blame_gutter(&self) -> bool {
11869 self.show_git_blame_gutter
11870 }
11871
11872 pub fn render_git_blame_gutter(&mut self, cx: &mut WindowContext) -> bool {
11873 self.show_git_blame_gutter && self.has_blame_entries(cx)
11874 }
11875
11876 pub fn render_git_blame_inline(&mut self, cx: &mut WindowContext) -> bool {
11877 self.show_git_blame_inline
11878 && self.focus_handle.is_focused(cx)
11879 && !self.newest_selection_head_on_empty_line(cx)
11880 && self.has_blame_entries(cx)
11881 }
11882
11883 fn has_blame_entries(&self, cx: &mut WindowContext) -> bool {
11884 self.blame()
11885 .map_or(false, |blame| blame.read(cx).has_generated_entries())
11886 }
11887
11888 fn newest_selection_head_on_empty_line(&mut self, cx: &mut WindowContext) -> bool {
11889 let cursor_anchor = self.selections.newest_anchor().head();
11890
11891 let snapshot = self.buffer.read(cx).snapshot(cx);
11892 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
11893
11894 snapshot.line_len(buffer_row) == 0
11895 }
11896
11897 fn get_permalink_to_line(&mut self, cx: &mut ViewContext<Self>) -> Task<Result<url::Url>> {
11898 let buffer_and_selection = maybe!({
11899 let selection = self.selections.newest::<Point>(cx);
11900 let selection_range = selection.range();
11901
11902 let (buffer, selection) = if let Some(buffer) = self.buffer().read(cx).as_singleton() {
11903 (buffer, selection_range.start.row..selection_range.end.row)
11904 } else {
11905 let buffer_ranges = self
11906 .buffer()
11907 .read(cx)
11908 .range_to_buffer_ranges(selection_range, cx);
11909
11910 let (buffer, range, _) = if selection.reversed {
11911 buffer_ranges.first()
11912 } else {
11913 buffer_ranges.last()
11914 }?;
11915
11916 let snapshot = buffer.read(cx).snapshot();
11917 let selection = text::ToPoint::to_point(&range.start, &snapshot).row
11918 ..text::ToPoint::to_point(&range.end, &snapshot).row;
11919 (buffer.clone(), selection)
11920 };
11921
11922 Some((buffer, selection))
11923 });
11924
11925 let Some((buffer, selection)) = buffer_and_selection else {
11926 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
11927 };
11928
11929 let Some(project) = self.project.as_ref() else {
11930 return Task::ready(Err(anyhow!("editor does not have project")));
11931 };
11932
11933 project.update(cx, |project, cx| {
11934 project.get_permalink_to_line(&buffer, selection, cx)
11935 })
11936 }
11937
11938 pub fn copy_permalink_to_line(&mut self, _: &CopyPermalinkToLine, cx: &mut ViewContext<Self>) {
11939 let permalink_task = self.get_permalink_to_line(cx);
11940 let workspace = self.workspace();
11941
11942 cx.spawn(|_, mut cx| async move {
11943 match permalink_task.await {
11944 Ok(permalink) => {
11945 cx.update(|cx| {
11946 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
11947 })
11948 .ok();
11949 }
11950 Err(err) => {
11951 let message = format!("Failed to copy permalink: {err}");
11952
11953 Err::<(), anyhow::Error>(err).log_err();
11954
11955 if let Some(workspace) = workspace {
11956 workspace
11957 .update(&mut cx, |workspace, cx| {
11958 struct CopyPermalinkToLine;
11959
11960 workspace.show_toast(
11961 Toast::new(
11962 NotificationId::unique::<CopyPermalinkToLine>(),
11963 message,
11964 ),
11965 cx,
11966 )
11967 })
11968 .ok();
11969 }
11970 }
11971 }
11972 })
11973 .detach();
11974 }
11975
11976 pub fn copy_file_location(&mut self, _: &CopyFileLocation, cx: &mut ViewContext<Self>) {
11977 let selection = self.selections.newest::<Point>(cx).start.row + 1;
11978 if let Some(file) = self.target_file(cx) {
11979 if let Some(path) = file.path().to_str() {
11980 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
11981 }
11982 }
11983 }
11984
11985 pub fn open_permalink_to_line(&mut self, _: &OpenPermalinkToLine, cx: &mut ViewContext<Self>) {
11986 let permalink_task = self.get_permalink_to_line(cx);
11987 let workspace = self.workspace();
11988
11989 cx.spawn(|_, mut cx| async move {
11990 match permalink_task.await {
11991 Ok(permalink) => {
11992 cx.update(|cx| {
11993 cx.open_url(permalink.as_ref());
11994 })
11995 .ok();
11996 }
11997 Err(err) => {
11998 let message = format!("Failed to open permalink: {err}");
11999
12000 Err::<(), anyhow::Error>(err).log_err();
12001
12002 if let Some(workspace) = workspace {
12003 workspace
12004 .update(&mut cx, |workspace, cx| {
12005 struct OpenPermalinkToLine;
12006
12007 workspace.show_toast(
12008 Toast::new(
12009 NotificationId::unique::<OpenPermalinkToLine>(),
12010 message,
12011 ),
12012 cx,
12013 )
12014 })
12015 .ok();
12016 }
12017 }
12018 }
12019 })
12020 .detach();
12021 }
12022
12023 pub fn insert_uuid_v4(&mut self, _: &InsertUuidV4, cx: &mut ViewContext<Self>) {
12024 self.insert_uuid(UuidVersion::V4, cx);
12025 }
12026
12027 pub fn insert_uuid_v7(&mut self, _: &InsertUuidV7, cx: &mut ViewContext<Self>) {
12028 self.insert_uuid(UuidVersion::V7, cx);
12029 }
12030
12031 fn insert_uuid(&mut self, version: UuidVersion, cx: &mut ViewContext<Self>) {
12032 self.transact(cx, |this, cx| {
12033 let edits = this
12034 .selections
12035 .all::<Point>(cx)
12036 .into_iter()
12037 .map(|selection| {
12038 let uuid = match version {
12039 UuidVersion::V4 => uuid::Uuid::new_v4(),
12040 UuidVersion::V7 => uuid::Uuid::now_v7(),
12041 };
12042
12043 (selection.range(), uuid.to_string())
12044 });
12045 this.edit(edits, cx);
12046 this.refresh_inline_completion(true, false, cx);
12047 });
12048 }
12049
12050 /// Adds a row highlight for the given range. If a row has multiple highlights, the
12051 /// last highlight added will be used.
12052 ///
12053 /// If the range ends at the beginning of a line, then that line will not be highlighted.
12054 pub fn highlight_rows<T: 'static>(
12055 &mut self,
12056 range: Range<Anchor>,
12057 color: Hsla,
12058 should_autoscroll: bool,
12059 cx: &mut ViewContext<Self>,
12060 ) {
12061 let snapshot = self.buffer().read(cx).snapshot(cx);
12062 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
12063 let ix = row_highlights.binary_search_by(|highlight| {
12064 Ordering::Equal
12065 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
12066 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
12067 });
12068
12069 if let Err(mut ix) = ix {
12070 let index = post_inc(&mut self.highlight_order);
12071
12072 // If this range intersects with the preceding highlight, then merge it with
12073 // the preceding highlight. Otherwise insert a new highlight.
12074 let mut merged = false;
12075 if ix > 0 {
12076 let prev_highlight = &mut row_highlights[ix - 1];
12077 if prev_highlight
12078 .range
12079 .end
12080 .cmp(&range.start, &snapshot)
12081 .is_ge()
12082 {
12083 ix -= 1;
12084 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
12085 prev_highlight.range.end = range.end;
12086 }
12087 merged = true;
12088 prev_highlight.index = index;
12089 prev_highlight.color = color;
12090 prev_highlight.should_autoscroll = should_autoscroll;
12091 }
12092 }
12093
12094 if !merged {
12095 row_highlights.insert(
12096 ix,
12097 RowHighlight {
12098 range: range.clone(),
12099 index,
12100 color,
12101 should_autoscroll,
12102 },
12103 );
12104 }
12105
12106 // If any of the following highlights intersect with this one, merge them.
12107 while let Some(next_highlight) = row_highlights.get(ix + 1) {
12108 let highlight = &row_highlights[ix];
12109 if next_highlight
12110 .range
12111 .start
12112 .cmp(&highlight.range.end, &snapshot)
12113 .is_le()
12114 {
12115 if next_highlight
12116 .range
12117 .end
12118 .cmp(&highlight.range.end, &snapshot)
12119 .is_gt()
12120 {
12121 row_highlights[ix].range.end = next_highlight.range.end;
12122 }
12123 row_highlights.remove(ix + 1);
12124 } else {
12125 break;
12126 }
12127 }
12128 }
12129 }
12130
12131 /// Remove any highlighted row ranges of the given type that intersect the
12132 /// given ranges.
12133 pub fn remove_highlighted_rows<T: 'static>(
12134 &mut self,
12135 ranges_to_remove: Vec<Range<Anchor>>,
12136 cx: &mut ViewContext<Self>,
12137 ) {
12138 let snapshot = self.buffer().read(cx).snapshot(cx);
12139 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
12140 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
12141 row_highlights.retain(|highlight| {
12142 while let Some(range_to_remove) = ranges_to_remove.peek() {
12143 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
12144 Ordering::Less | Ordering::Equal => {
12145 ranges_to_remove.next();
12146 }
12147 Ordering::Greater => {
12148 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
12149 Ordering::Less | Ordering::Equal => {
12150 return false;
12151 }
12152 Ordering::Greater => break,
12153 }
12154 }
12155 }
12156 }
12157
12158 true
12159 })
12160 }
12161
12162 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
12163 pub fn clear_row_highlights<T: 'static>(&mut self) {
12164 self.highlighted_rows.remove(&TypeId::of::<T>());
12165 }
12166
12167 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
12168 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
12169 self.highlighted_rows
12170 .get(&TypeId::of::<T>())
12171 .map_or(&[] as &[_], |vec| vec.as_slice())
12172 .iter()
12173 .map(|highlight| (highlight.range.clone(), highlight.color))
12174 }
12175
12176 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
12177 /// Rerturns a map of display rows that are highlighted and their corresponding highlight color.
12178 /// Allows to ignore certain kinds of highlights.
12179 pub fn highlighted_display_rows(
12180 &mut self,
12181 cx: &mut WindowContext,
12182 ) -> BTreeMap<DisplayRow, Hsla> {
12183 let snapshot = self.snapshot(cx);
12184 let mut used_highlight_orders = HashMap::default();
12185 self.highlighted_rows
12186 .iter()
12187 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
12188 .fold(
12189 BTreeMap::<DisplayRow, Hsla>::new(),
12190 |mut unique_rows, highlight| {
12191 let start = highlight.range.start.to_display_point(&snapshot);
12192 let end = highlight.range.end.to_display_point(&snapshot);
12193 let start_row = start.row().0;
12194 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
12195 && end.column() == 0
12196 {
12197 end.row().0.saturating_sub(1)
12198 } else {
12199 end.row().0
12200 };
12201 for row in start_row..=end_row {
12202 let used_index =
12203 used_highlight_orders.entry(row).or_insert(highlight.index);
12204 if highlight.index >= *used_index {
12205 *used_index = highlight.index;
12206 unique_rows.insert(DisplayRow(row), highlight.color);
12207 }
12208 }
12209 unique_rows
12210 },
12211 )
12212 }
12213
12214 pub fn highlighted_display_row_for_autoscroll(
12215 &self,
12216 snapshot: &DisplaySnapshot,
12217 ) -> Option<DisplayRow> {
12218 self.highlighted_rows
12219 .values()
12220 .flat_map(|highlighted_rows| highlighted_rows.iter())
12221 .filter_map(|highlight| {
12222 if highlight.should_autoscroll {
12223 Some(highlight.range.start.to_display_point(snapshot).row())
12224 } else {
12225 None
12226 }
12227 })
12228 .min()
12229 }
12230
12231 pub fn set_search_within_ranges(
12232 &mut self,
12233 ranges: &[Range<Anchor>],
12234 cx: &mut ViewContext<Self>,
12235 ) {
12236 self.highlight_background::<SearchWithinRange>(
12237 ranges,
12238 |colors| colors.editor_document_highlight_read_background,
12239 cx,
12240 )
12241 }
12242
12243 pub fn set_breadcrumb_header(&mut self, new_header: String) {
12244 self.breadcrumb_header = Some(new_header);
12245 }
12246
12247 pub fn clear_search_within_ranges(&mut self, cx: &mut ViewContext<Self>) {
12248 self.clear_background_highlights::<SearchWithinRange>(cx);
12249 }
12250
12251 pub fn highlight_background<T: 'static>(
12252 &mut self,
12253 ranges: &[Range<Anchor>],
12254 color_fetcher: fn(&ThemeColors) -> Hsla,
12255 cx: &mut ViewContext<Self>,
12256 ) {
12257 self.background_highlights
12258 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
12259 self.scrollbar_marker_state.dirty = true;
12260 cx.notify();
12261 }
12262
12263 pub fn clear_background_highlights<T: 'static>(
12264 &mut self,
12265 cx: &mut ViewContext<Self>,
12266 ) -> Option<BackgroundHighlight> {
12267 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
12268 if !text_highlights.1.is_empty() {
12269 self.scrollbar_marker_state.dirty = true;
12270 cx.notify();
12271 }
12272 Some(text_highlights)
12273 }
12274
12275 pub fn highlight_gutter<T: 'static>(
12276 &mut self,
12277 ranges: &[Range<Anchor>],
12278 color_fetcher: fn(&AppContext) -> Hsla,
12279 cx: &mut ViewContext<Self>,
12280 ) {
12281 self.gutter_highlights
12282 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
12283 cx.notify();
12284 }
12285
12286 pub fn clear_gutter_highlights<T: 'static>(
12287 &mut self,
12288 cx: &mut ViewContext<Self>,
12289 ) -> Option<GutterHighlight> {
12290 cx.notify();
12291 self.gutter_highlights.remove(&TypeId::of::<T>())
12292 }
12293
12294 #[cfg(feature = "test-support")]
12295 pub fn all_text_background_highlights(
12296 &mut self,
12297 cx: &mut ViewContext<Self>,
12298 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
12299 let snapshot = self.snapshot(cx);
12300 let buffer = &snapshot.buffer_snapshot;
12301 let start = buffer.anchor_before(0);
12302 let end = buffer.anchor_after(buffer.len());
12303 let theme = cx.theme().colors();
12304 self.background_highlights_in_range(start..end, &snapshot, theme)
12305 }
12306
12307 #[cfg(feature = "test-support")]
12308 pub fn search_background_highlights(
12309 &mut self,
12310 cx: &mut ViewContext<Self>,
12311 ) -> Vec<Range<Point>> {
12312 let snapshot = self.buffer().read(cx).snapshot(cx);
12313
12314 let highlights = self
12315 .background_highlights
12316 .get(&TypeId::of::<items::BufferSearchHighlights>());
12317
12318 if let Some((_color, ranges)) = highlights {
12319 ranges
12320 .iter()
12321 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
12322 .collect_vec()
12323 } else {
12324 vec![]
12325 }
12326 }
12327
12328 fn document_highlights_for_position<'a>(
12329 &'a self,
12330 position: Anchor,
12331 buffer: &'a MultiBufferSnapshot,
12332 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
12333 let read_highlights = self
12334 .background_highlights
12335 .get(&TypeId::of::<DocumentHighlightRead>())
12336 .map(|h| &h.1);
12337 let write_highlights = self
12338 .background_highlights
12339 .get(&TypeId::of::<DocumentHighlightWrite>())
12340 .map(|h| &h.1);
12341 let left_position = position.bias_left(buffer);
12342 let right_position = position.bias_right(buffer);
12343 read_highlights
12344 .into_iter()
12345 .chain(write_highlights)
12346 .flat_map(move |ranges| {
12347 let start_ix = match ranges.binary_search_by(|probe| {
12348 let cmp = probe.end.cmp(&left_position, buffer);
12349 if cmp.is_ge() {
12350 Ordering::Greater
12351 } else {
12352 Ordering::Less
12353 }
12354 }) {
12355 Ok(i) | Err(i) => i,
12356 };
12357
12358 ranges[start_ix..]
12359 .iter()
12360 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
12361 })
12362 }
12363
12364 pub fn has_background_highlights<T: 'static>(&self) -> bool {
12365 self.background_highlights
12366 .get(&TypeId::of::<T>())
12367 .map_or(false, |(_, highlights)| !highlights.is_empty())
12368 }
12369
12370 pub fn background_highlights_in_range(
12371 &self,
12372 search_range: Range<Anchor>,
12373 display_snapshot: &DisplaySnapshot,
12374 theme: &ThemeColors,
12375 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
12376 let mut results = Vec::new();
12377 for (color_fetcher, ranges) in self.background_highlights.values() {
12378 let color = color_fetcher(theme);
12379 let start_ix = match ranges.binary_search_by(|probe| {
12380 let cmp = probe
12381 .end
12382 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
12383 if cmp.is_gt() {
12384 Ordering::Greater
12385 } else {
12386 Ordering::Less
12387 }
12388 }) {
12389 Ok(i) | Err(i) => i,
12390 };
12391 for range in &ranges[start_ix..] {
12392 if range
12393 .start
12394 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
12395 .is_ge()
12396 {
12397 break;
12398 }
12399
12400 let start = range.start.to_display_point(display_snapshot);
12401 let end = range.end.to_display_point(display_snapshot);
12402 results.push((start..end, color))
12403 }
12404 }
12405 results
12406 }
12407
12408 pub fn background_highlight_row_ranges<T: 'static>(
12409 &self,
12410 search_range: Range<Anchor>,
12411 display_snapshot: &DisplaySnapshot,
12412 count: usize,
12413 ) -> Vec<RangeInclusive<DisplayPoint>> {
12414 let mut results = Vec::new();
12415 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
12416 return vec![];
12417 };
12418
12419 let start_ix = match ranges.binary_search_by(|probe| {
12420 let cmp = probe
12421 .end
12422 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
12423 if cmp.is_gt() {
12424 Ordering::Greater
12425 } else {
12426 Ordering::Less
12427 }
12428 }) {
12429 Ok(i) | Err(i) => i,
12430 };
12431 let mut push_region = |start: Option<Point>, end: Option<Point>| {
12432 if let (Some(start_display), Some(end_display)) = (start, end) {
12433 results.push(
12434 start_display.to_display_point(display_snapshot)
12435 ..=end_display.to_display_point(display_snapshot),
12436 );
12437 }
12438 };
12439 let mut start_row: Option<Point> = None;
12440 let mut end_row: Option<Point> = None;
12441 if ranges.len() > count {
12442 return Vec::new();
12443 }
12444 for range in &ranges[start_ix..] {
12445 if range
12446 .start
12447 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
12448 .is_ge()
12449 {
12450 break;
12451 }
12452 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
12453 if let Some(current_row) = &end_row {
12454 if end.row == current_row.row {
12455 continue;
12456 }
12457 }
12458 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
12459 if start_row.is_none() {
12460 assert_eq!(end_row, None);
12461 start_row = Some(start);
12462 end_row = Some(end);
12463 continue;
12464 }
12465 if let Some(current_end) = end_row.as_mut() {
12466 if start.row > current_end.row + 1 {
12467 push_region(start_row, end_row);
12468 start_row = Some(start);
12469 end_row = Some(end);
12470 } else {
12471 // Merge two hunks.
12472 *current_end = end;
12473 }
12474 } else {
12475 unreachable!();
12476 }
12477 }
12478 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
12479 push_region(start_row, end_row);
12480 results
12481 }
12482
12483 pub fn gutter_highlights_in_range(
12484 &self,
12485 search_range: Range<Anchor>,
12486 display_snapshot: &DisplaySnapshot,
12487 cx: &AppContext,
12488 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
12489 let mut results = Vec::new();
12490 for (color_fetcher, ranges) in self.gutter_highlights.values() {
12491 let color = color_fetcher(cx);
12492 let start_ix = match ranges.binary_search_by(|probe| {
12493 let cmp = probe
12494 .end
12495 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
12496 if cmp.is_gt() {
12497 Ordering::Greater
12498 } else {
12499 Ordering::Less
12500 }
12501 }) {
12502 Ok(i) | Err(i) => i,
12503 };
12504 for range in &ranges[start_ix..] {
12505 if range
12506 .start
12507 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
12508 .is_ge()
12509 {
12510 break;
12511 }
12512
12513 let start = range.start.to_display_point(display_snapshot);
12514 let end = range.end.to_display_point(display_snapshot);
12515 results.push((start..end, color))
12516 }
12517 }
12518 results
12519 }
12520
12521 /// Get the text ranges corresponding to the redaction query
12522 pub fn redacted_ranges(
12523 &self,
12524 search_range: Range<Anchor>,
12525 display_snapshot: &DisplaySnapshot,
12526 cx: &WindowContext,
12527 ) -> Vec<Range<DisplayPoint>> {
12528 display_snapshot
12529 .buffer_snapshot
12530 .redacted_ranges(search_range, |file| {
12531 if let Some(file) = file {
12532 file.is_private()
12533 && EditorSettings::get(
12534 Some(SettingsLocation {
12535 worktree_id: file.worktree_id(cx),
12536 path: file.path().as_ref(),
12537 }),
12538 cx,
12539 )
12540 .redact_private_values
12541 } else {
12542 false
12543 }
12544 })
12545 .map(|range| {
12546 range.start.to_display_point(display_snapshot)
12547 ..range.end.to_display_point(display_snapshot)
12548 })
12549 .collect()
12550 }
12551
12552 pub fn highlight_text<T: 'static>(
12553 &mut self,
12554 ranges: Vec<Range<Anchor>>,
12555 style: HighlightStyle,
12556 cx: &mut ViewContext<Self>,
12557 ) {
12558 self.display_map.update(cx, |map, _| {
12559 map.highlight_text(TypeId::of::<T>(), ranges, style)
12560 });
12561 cx.notify();
12562 }
12563
12564 pub(crate) fn highlight_inlays<T: 'static>(
12565 &mut self,
12566 highlights: Vec<InlayHighlight>,
12567 style: HighlightStyle,
12568 cx: &mut ViewContext<Self>,
12569 ) {
12570 self.display_map.update(cx, |map, _| {
12571 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
12572 });
12573 cx.notify();
12574 }
12575
12576 pub fn text_highlights<'a, T: 'static>(
12577 &'a self,
12578 cx: &'a AppContext,
12579 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
12580 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
12581 }
12582
12583 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
12584 let cleared = self
12585 .display_map
12586 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
12587 if cleared {
12588 cx.notify();
12589 }
12590 }
12591
12592 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
12593 (self.read_only(cx) || self.blink_manager.read(cx).visible())
12594 && self.focus_handle.is_focused(cx)
12595 }
12596
12597 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut ViewContext<Self>) {
12598 self.show_cursor_when_unfocused = is_enabled;
12599 cx.notify();
12600 }
12601
12602 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
12603 cx.notify();
12604 }
12605
12606 fn on_buffer_event(
12607 &mut self,
12608 multibuffer: Model<MultiBuffer>,
12609 event: &multi_buffer::Event,
12610 cx: &mut ViewContext<Self>,
12611 ) {
12612 match event {
12613 multi_buffer::Event::Edited {
12614 singleton_buffer_edited,
12615 } => {
12616 self.scrollbar_marker_state.dirty = true;
12617 self.active_indent_guides_state.dirty = true;
12618 self.refresh_active_diagnostics(cx);
12619 self.refresh_code_actions(cx);
12620 if self.has_active_inline_completion(cx) {
12621 self.update_visible_inline_completion(cx);
12622 }
12623 cx.emit(EditorEvent::BufferEdited);
12624 cx.emit(SearchEvent::MatchesInvalidated);
12625 if *singleton_buffer_edited {
12626 if let Some(project) = &self.project {
12627 let project = project.read(cx);
12628 #[allow(clippy::mutable_key_type)]
12629 let languages_affected = multibuffer
12630 .read(cx)
12631 .all_buffers()
12632 .into_iter()
12633 .filter_map(|buffer| {
12634 let buffer = buffer.read(cx);
12635 let language = buffer.language()?;
12636 if project.is_local()
12637 && project.language_servers_for_buffer(buffer, cx).count() == 0
12638 {
12639 None
12640 } else {
12641 Some(language)
12642 }
12643 })
12644 .cloned()
12645 .collect::<HashSet<_>>();
12646 if !languages_affected.is_empty() {
12647 self.refresh_inlay_hints(
12648 InlayHintRefreshReason::BufferEdited(languages_affected),
12649 cx,
12650 );
12651 }
12652 }
12653 }
12654
12655 let Some(project) = &self.project else { return };
12656 let (telemetry, is_via_ssh) = {
12657 let project = project.read(cx);
12658 let telemetry = project.client().telemetry().clone();
12659 let is_via_ssh = project.is_via_ssh();
12660 (telemetry, is_via_ssh)
12661 };
12662 refresh_linked_ranges(self, cx);
12663 telemetry.log_edit_event("editor", is_via_ssh);
12664 }
12665 multi_buffer::Event::ExcerptsAdded {
12666 buffer,
12667 predecessor,
12668 excerpts,
12669 } => {
12670 self.tasks_update_task = Some(self.refresh_runnables(cx));
12671 let buffer_id = buffer.read(cx).remote_id();
12672 if !self.diff_map.diff_bases.contains_key(&buffer_id) {
12673 if let Some(project) = &self.project {
12674 get_unstaged_changes_for_buffers(project, [buffer.clone()], cx);
12675 }
12676 }
12677 cx.emit(EditorEvent::ExcerptsAdded {
12678 buffer: buffer.clone(),
12679 predecessor: *predecessor,
12680 excerpts: excerpts.clone(),
12681 });
12682 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
12683 }
12684 multi_buffer::Event::ExcerptsRemoved { ids } => {
12685 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
12686 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
12687 }
12688 multi_buffer::Event::ExcerptsEdited { ids } => {
12689 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
12690 }
12691 multi_buffer::Event::ExcerptsExpanded { ids } => {
12692 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
12693 }
12694 multi_buffer::Event::Reparsed(buffer_id) => {
12695 self.tasks_update_task = Some(self.refresh_runnables(cx));
12696
12697 cx.emit(EditorEvent::Reparsed(*buffer_id));
12698 }
12699 multi_buffer::Event::LanguageChanged(buffer_id) => {
12700 linked_editing_ranges::refresh_linked_ranges(self, cx);
12701 cx.emit(EditorEvent::Reparsed(*buffer_id));
12702 cx.notify();
12703 }
12704 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
12705 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
12706 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
12707 cx.emit(EditorEvent::TitleChanged)
12708 }
12709 // multi_buffer::Event::DiffBaseChanged => {
12710 // self.scrollbar_marker_state.dirty = true;
12711 // cx.emit(EditorEvent::DiffBaseChanged);
12712 // cx.notify();
12713 // }
12714 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
12715 multi_buffer::Event::DiagnosticsUpdated => {
12716 self.refresh_active_diagnostics(cx);
12717 self.scrollbar_marker_state.dirty = true;
12718 cx.notify();
12719 }
12720 _ => {}
12721 };
12722 }
12723
12724 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
12725 cx.notify();
12726 }
12727
12728 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
12729 self.tasks_update_task = Some(self.refresh_runnables(cx));
12730 self.refresh_inline_completion(true, false, cx);
12731 self.refresh_inlay_hints(
12732 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
12733 self.selections.newest_anchor().head(),
12734 &self.buffer.read(cx).snapshot(cx),
12735 cx,
12736 )),
12737 cx,
12738 );
12739
12740 let old_cursor_shape = self.cursor_shape;
12741
12742 {
12743 let editor_settings = EditorSettings::get_global(cx);
12744 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
12745 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
12746 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
12747 }
12748
12749 if old_cursor_shape != self.cursor_shape {
12750 cx.emit(EditorEvent::CursorShapeChanged);
12751 }
12752
12753 let project_settings = ProjectSettings::get_global(cx);
12754 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
12755
12756 if self.mode == EditorMode::Full {
12757 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
12758 if self.git_blame_inline_enabled != inline_blame_enabled {
12759 self.toggle_git_blame_inline_internal(false, cx);
12760 }
12761 }
12762
12763 cx.notify();
12764 }
12765
12766 pub fn set_searchable(&mut self, searchable: bool) {
12767 self.searchable = searchable;
12768 }
12769
12770 pub fn searchable(&self) -> bool {
12771 self.searchable
12772 }
12773
12774 fn open_proposed_changes_editor(
12775 &mut self,
12776 _: &OpenProposedChangesEditor,
12777 cx: &mut ViewContext<Self>,
12778 ) {
12779 let Some(workspace) = self.workspace() else {
12780 cx.propagate();
12781 return;
12782 };
12783
12784 let selections = self.selections.all::<usize>(cx);
12785 let buffer = self.buffer.read(cx);
12786 let mut new_selections_by_buffer = HashMap::default();
12787 for selection in selections {
12788 for (buffer, range, _) in
12789 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
12790 {
12791 let mut range = range.to_point(buffer.read(cx));
12792 range.start.column = 0;
12793 range.end.column = buffer.read(cx).line_len(range.end.row);
12794 new_selections_by_buffer
12795 .entry(buffer)
12796 .or_insert(Vec::new())
12797 .push(range)
12798 }
12799 }
12800
12801 let proposed_changes_buffers = new_selections_by_buffer
12802 .into_iter()
12803 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
12804 .collect::<Vec<_>>();
12805 let proposed_changes_editor = cx.new_view(|cx| {
12806 ProposedChangesEditor::new(
12807 "Proposed changes",
12808 proposed_changes_buffers,
12809 self.project.clone(),
12810 cx,
12811 )
12812 });
12813
12814 cx.window_context().defer(move |cx| {
12815 workspace.update(cx, |workspace, cx| {
12816 workspace.active_pane().update(cx, |pane, cx| {
12817 pane.add_item(Box::new(proposed_changes_editor), true, true, None, cx);
12818 });
12819 });
12820 });
12821 }
12822
12823 pub fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext<Self>) {
12824 self.open_excerpts_common(None, true, cx)
12825 }
12826
12827 pub fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
12828 self.open_excerpts_common(None, false, cx)
12829 }
12830
12831 fn open_excerpts_common(
12832 &mut self,
12833 jump_data: Option<JumpData>,
12834 split: bool,
12835 cx: &mut ViewContext<Self>,
12836 ) {
12837 let Some(workspace) = self.workspace() else {
12838 cx.propagate();
12839 return;
12840 };
12841
12842 if self.buffer.read(cx).is_singleton() {
12843 cx.propagate();
12844 return;
12845 }
12846
12847 let mut new_selections_by_buffer = HashMap::default();
12848 match &jump_data {
12849 Some(jump_data) => {
12850 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12851 if let Some(buffer) = multi_buffer_snapshot
12852 .buffer_id_for_excerpt(jump_data.excerpt_id)
12853 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
12854 {
12855 let buffer_snapshot = buffer.read(cx).snapshot();
12856 let jump_to_point = if buffer_snapshot.can_resolve(&jump_data.anchor) {
12857 language::ToPoint::to_point(&jump_data.anchor, &buffer_snapshot)
12858 } else {
12859 buffer_snapshot.clip_point(jump_data.position, Bias::Left)
12860 };
12861 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
12862 new_selections_by_buffer.insert(
12863 buffer,
12864 (
12865 vec![jump_to_offset..jump_to_offset],
12866 Some(jump_data.line_offset_from_top),
12867 ),
12868 );
12869 }
12870 }
12871 None => {
12872 let selections = self.selections.all::<usize>(cx);
12873 let buffer = self.buffer.read(cx);
12874 for selection in selections {
12875 for (mut buffer_handle, mut range, _) in
12876 buffer.range_to_buffer_ranges(selection.range(), cx)
12877 {
12878 // When editing branch buffers, jump to the corresponding location
12879 // in their base buffer.
12880 let buffer = buffer_handle.read(cx);
12881 if let Some(base_buffer) = buffer.base_buffer() {
12882 range = buffer.range_to_version(range, &base_buffer.read(cx).version());
12883 buffer_handle = base_buffer;
12884 }
12885
12886 if selection.reversed {
12887 mem::swap(&mut range.start, &mut range.end);
12888 }
12889 new_selections_by_buffer
12890 .entry(buffer_handle)
12891 .or_insert((Vec::new(), None))
12892 .0
12893 .push(range)
12894 }
12895 }
12896 }
12897 }
12898
12899 if new_selections_by_buffer.is_empty() {
12900 return;
12901 }
12902
12903 // We defer the pane interaction because we ourselves are a workspace item
12904 // and activating a new item causes the pane to call a method on us reentrantly,
12905 // which panics if we're on the stack.
12906 cx.window_context().defer(move |cx| {
12907 workspace.update(cx, |workspace, cx| {
12908 let pane = if split {
12909 workspace.adjacent_pane(cx)
12910 } else {
12911 workspace.active_pane().clone()
12912 };
12913
12914 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
12915 let editor = buffer
12916 .read(cx)
12917 .file()
12918 .is_none()
12919 .then(|| {
12920 // Handle file-less buffers separately: those are not really the project items, so won't have a paroject path or entity id,
12921 // so `workspace.open_project_item` will never find them, always opening a new editor.
12922 // Instead, we try to activate the existing editor in the pane first.
12923 let (editor, pane_item_index) =
12924 pane.read(cx).items().enumerate().find_map(|(i, item)| {
12925 let editor = item.downcast::<Editor>()?;
12926 let singleton_buffer =
12927 editor.read(cx).buffer().read(cx).as_singleton()?;
12928 if singleton_buffer == buffer {
12929 Some((editor, i))
12930 } else {
12931 None
12932 }
12933 })?;
12934 pane.update(cx, |pane, cx| {
12935 pane.activate_item(pane_item_index, true, true, cx)
12936 });
12937 Some(editor)
12938 })
12939 .flatten()
12940 .unwrap_or_else(|| {
12941 workspace.open_project_item::<Self>(
12942 pane.clone(),
12943 buffer,
12944 true,
12945 true,
12946 cx,
12947 )
12948 });
12949
12950 editor.update(cx, |editor, cx| {
12951 let autoscroll = match scroll_offset {
12952 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
12953 None => Autoscroll::newest(),
12954 };
12955 let nav_history = editor.nav_history.take();
12956 editor.change_selections(Some(autoscroll), cx, |s| {
12957 s.select_ranges(ranges);
12958 });
12959 editor.nav_history = nav_history;
12960 });
12961 }
12962 })
12963 });
12964 }
12965
12966 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
12967 let snapshot = self.buffer.read(cx).read(cx);
12968 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
12969 Some(
12970 ranges
12971 .iter()
12972 .map(move |range| {
12973 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
12974 })
12975 .collect(),
12976 )
12977 }
12978
12979 fn selection_replacement_ranges(
12980 &self,
12981 range: Range<OffsetUtf16>,
12982 cx: &mut AppContext,
12983 ) -> Vec<Range<OffsetUtf16>> {
12984 let selections = self.selections.all::<OffsetUtf16>(cx);
12985 let newest_selection = selections
12986 .iter()
12987 .max_by_key(|selection| selection.id)
12988 .unwrap();
12989 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
12990 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
12991 let snapshot = self.buffer.read(cx).read(cx);
12992 selections
12993 .into_iter()
12994 .map(|mut selection| {
12995 selection.start.0 =
12996 (selection.start.0 as isize).saturating_add(start_delta) as usize;
12997 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
12998 snapshot.clip_offset_utf16(selection.start, Bias::Left)
12999 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
13000 })
13001 .collect()
13002 }
13003
13004 fn report_editor_event(
13005 &self,
13006 operation: &'static str,
13007 file_extension: Option<String>,
13008 cx: &AppContext,
13009 ) {
13010 if cfg!(any(test, feature = "test-support")) {
13011 return;
13012 }
13013
13014 let Some(project) = &self.project else { return };
13015
13016 // If None, we are in a file without an extension
13017 let file = self
13018 .buffer
13019 .read(cx)
13020 .as_singleton()
13021 .and_then(|b| b.read(cx).file());
13022 let file_extension = file_extension.or(file
13023 .as_ref()
13024 .and_then(|file| Path::new(file.file_name(cx)).extension())
13025 .and_then(|e| e.to_str())
13026 .map(|a| a.to_string()));
13027
13028 let vim_mode = cx
13029 .global::<SettingsStore>()
13030 .raw_user_settings()
13031 .get("vim_mode")
13032 == Some(&serde_json::Value::Bool(true));
13033
13034 let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
13035 == language::language_settings::InlineCompletionProvider::Copilot;
13036 let copilot_enabled_for_language = self
13037 .buffer
13038 .read(cx)
13039 .settings_at(0, cx)
13040 .show_inline_completions;
13041
13042 let project = project.read(cx);
13043 let telemetry = project.client().telemetry().clone();
13044 telemetry.report_editor_event(
13045 file_extension,
13046 vim_mode,
13047 operation,
13048 copilot_enabled,
13049 copilot_enabled_for_language,
13050 project.is_via_ssh(),
13051 )
13052 }
13053
13054 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
13055 /// with each line being an array of {text, highlight} objects.
13056 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
13057 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
13058 return;
13059 };
13060
13061 #[derive(Serialize)]
13062 struct Chunk<'a> {
13063 text: String,
13064 highlight: Option<&'a str>,
13065 }
13066
13067 let snapshot = buffer.read(cx).snapshot();
13068 let range = self
13069 .selected_text_range(false, cx)
13070 .and_then(|selection| {
13071 if selection.range.is_empty() {
13072 None
13073 } else {
13074 Some(selection.range)
13075 }
13076 })
13077 .unwrap_or_else(|| 0..snapshot.len());
13078
13079 let chunks = snapshot.chunks(range, true);
13080 let mut lines = Vec::new();
13081 let mut line: VecDeque<Chunk> = VecDeque::new();
13082
13083 let Some(style) = self.style.as_ref() else {
13084 return;
13085 };
13086
13087 for chunk in chunks {
13088 let highlight = chunk
13089 .syntax_highlight_id
13090 .and_then(|id| id.name(&style.syntax));
13091 let mut chunk_lines = chunk.text.split('\n').peekable();
13092 while let Some(text) = chunk_lines.next() {
13093 let mut merged_with_last_token = false;
13094 if let Some(last_token) = line.back_mut() {
13095 if last_token.highlight == highlight {
13096 last_token.text.push_str(text);
13097 merged_with_last_token = true;
13098 }
13099 }
13100
13101 if !merged_with_last_token {
13102 line.push_back(Chunk {
13103 text: text.into(),
13104 highlight,
13105 });
13106 }
13107
13108 if chunk_lines.peek().is_some() {
13109 if line.len() > 1 && line.front().unwrap().text.is_empty() {
13110 line.pop_front();
13111 }
13112 if line.len() > 1 && line.back().unwrap().text.is_empty() {
13113 line.pop_back();
13114 }
13115
13116 lines.push(mem::take(&mut line));
13117 }
13118 }
13119 }
13120
13121 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
13122 return;
13123 };
13124 cx.write_to_clipboard(ClipboardItem::new_string(lines));
13125 }
13126
13127 pub fn open_context_menu(&mut self, _: &OpenContextMenu, cx: &mut ViewContext<Self>) {
13128 self.request_autoscroll(Autoscroll::newest(), cx);
13129 let position = self.selections.newest_display(cx).start;
13130 mouse_context_menu::deploy_context_menu(self, None, position, cx);
13131 }
13132
13133 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
13134 &self.inlay_hint_cache
13135 }
13136
13137 pub fn replay_insert_event(
13138 &mut self,
13139 text: &str,
13140 relative_utf16_range: Option<Range<isize>>,
13141 cx: &mut ViewContext<Self>,
13142 ) {
13143 if !self.input_enabled {
13144 cx.emit(EditorEvent::InputIgnored { text: text.into() });
13145 return;
13146 }
13147 if let Some(relative_utf16_range) = relative_utf16_range {
13148 let selections = self.selections.all::<OffsetUtf16>(cx);
13149 self.change_selections(None, cx, |s| {
13150 let new_ranges = selections.into_iter().map(|range| {
13151 let start = OffsetUtf16(
13152 range
13153 .head()
13154 .0
13155 .saturating_add_signed(relative_utf16_range.start),
13156 );
13157 let end = OffsetUtf16(
13158 range
13159 .head()
13160 .0
13161 .saturating_add_signed(relative_utf16_range.end),
13162 );
13163 start..end
13164 });
13165 s.select_ranges(new_ranges);
13166 });
13167 }
13168
13169 self.handle_input(text, cx);
13170 }
13171
13172 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
13173 let Some(provider) = self.semantics_provider.as_ref() else {
13174 return false;
13175 };
13176
13177 let mut supports = false;
13178 self.buffer().read(cx).for_each_buffer(|buffer| {
13179 supports |= provider.supports_inlay_hints(buffer, cx);
13180 });
13181 supports
13182 }
13183
13184 pub fn focus(&self, cx: &mut WindowContext) {
13185 cx.focus(&self.focus_handle)
13186 }
13187
13188 pub fn is_focused(&self, cx: &WindowContext) -> bool {
13189 self.focus_handle.is_focused(cx)
13190 }
13191
13192 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
13193 cx.emit(EditorEvent::Focused);
13194
13195 if let Some(descendant) = self
13196 .last_focused_descendant
13197 .take()
13198 .and_then(|descendant| descendant.upgrade())
13199 {
13200 cx.focus(&descendant);
13201 } else {
13202 if let Some(blame) = self.blame.as_ref() {
13203 blame.update(cx, GitBlame::focus)
13204 }
13205
13206 self.blink_manager.update(cx, BlinkManager::enable);
13207 self.show_cursor_names(cx);
13208 self.buffer.update(cx, |buffer, cx| {
13209 buffer.finalize_last_transaction(cx);
13210 if self.leader_peer_id.is_none() {
13211 buffer.set_active_selections(
13212 &self.selections.disjoint_anchors(),
13213 self.selections.line_mode,
13214 self.cursor_shape,
13215 cx,
13216 );
13217 }
13218 });
13219 }
13220 }
13221
13222 fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
13223 cx.emit(EditorEvent::FocusedIn)
13224 }
13225
13226 fn handle_focus_out(&mut self, event: FocusOutEvent, _cx: &mut ViewContext<Self>) {
13227 if event.blurred != self.focus_handle {
13228 self.last_focused_descendant = Some(event.blurred);
13229 }
13230 }
13231
13232 pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
13233 self.blink_manager.update(cx, BlinkManager::disable);
13234 self.buffer
13235 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
13236
13237 if let Some(blame) = self.blame.as_ref() {
13238 blame.update(cx, GitBlame::blur)
13239 }
13240 if !self.hover_state.focused(cx) {
13241 hide_hover(self, cx);
13242 }
13243
13244 self.hide_context_menu(cx);
13245 cx.emit(EditorEvent::Blurred);
13246 cx.notify();
13247 }
13248
13249 pub fn register_action<A: Action>(
13250 &mut self,
13251 listener: impl Fn(&A, &mut WindowContext) + 'static,
13252 ) -> Subscription {
13253 let id = self.next_editor_action_id.post_inc();
13254 let listener = Arc::new(listener);
13255 self.editor_actions.borrow_mut().insert(
13256 id,
13257 Box::new(move |cx| {
13258 let cx = cx.window_context();
13259 let listener = listener.clone();
13260 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
13261 let action = action.downcast_ref().unwrap();
13262 if phase == DispatchPhase::Bubble {
13263 listener(action, cx)
13264 }
13265 })
13266 }),
13267 );
13268
13269 let editor_actions = self.editor_actions.clone();
13270 Subscription::new(move || {
13271 editor_actions.borrow_mut().remove(&id);
13272 })
13273 }
13274
13275 pub fn file_header_size(&self) -> u32 {
13276 FILE_HEADER_HEIGHT
13277 }
13278
13279 pub fn revert(
13280 &mut self,
13281 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
13282 cx: &mut ViewContext<Self>,
13283 ) {
13284 self.buffer().update(cx, |multi_buffer, cx| {
13285 for (buffer_id, changes) in revert_changes {
13286 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
13287 buffer.update(cx, |buffer, cx| {
13288 buffer.edit(
13289 changes.into_iter().map(|(range, text)| {
13290 (range, text.to_string().map(Arc::<str>::from))
13291 }),
13292 None,
13293 cx,
13294 );
13295 });
13296 }
13297 }
13298 });
13299 self.change_selections(None, cx, |selections| selections.refresh());
13300 }
13301
13302 pub fn to_pixel_point(
13303 &mut self,
13304 source: multi_buffer::Anchor,
13305 editor_snapshot: &EditorSnapshot,
13306 cx: &mut ViewContext<Self>,
13307 ) -> Option<gpui::Point<Pixels>> {
13308 let source_point = source.to_display_point(editor_snapshot);
13309 self.display_to_pixel_point(source_point, editor_snapshot, cx)
13310 }
13311
13312 pub fn display_to_pixel_point(
13313 &mut self,
13314 source: DisplayPoint,
13315 editor_snapshot: &EditorSnapshot,
13316 cx: &mut ViewContext<Self>,
13317 ) -> Option<gpui::Point<Pixels>> {
13318 let line_height = self.style()?.text.line_height_in_pixels(cx.rem_size());
13319 let text_layout_details = self.text_layout_details(cx);
13320 let scroll_top = text_layout_details
13321 .scroll_anchor
13322 .scroll_position(editor_snapshot)
13323 .y;
13324
13325 if source.row().as_f32() < scroll_top.floor() {
13326 return None;
13327 }
13328 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
13329 let source_y = line_height * (source.row().as_f32() - scroll_top);
13330 Some(gpui::Point::new(source_x, source_y))
13331 }
13332
13333 pub fn has_active_completions_menu(&self) -> bool {
13334 self.context_menu.read().as_ref().map_or(false, |menu| {
13335 menu.visible() && matches!(menu, ContextMenu::Completions(_))
13336 })
13337 }
13338
13339 pub fn register_addon<T: Addon>(&mut self, instance: T) {
13340 self.addons
13341 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
13342 }
13343
13344 pub fn unregister_addon<T: Addon>(&mut self) {
13345 self.addons.remove(&std::any::TypeId::of::<T>());
13346 }
13347
13348 pub fn addon<T: Addon>(&self) -> Option<&T> {
13349 let type_id = std::any::TypeId::of::<T>();
13350 self.addons
13351 .get(&type_id)
13352 .and_then(|item| item.to_any().downcast_ref::<T>())
13353 }
13354
13355 fn character_size(&self, cx: &mut ViewContext<Self>) -> gpui::Point<Pixels> {
13356 let text_layout_details = self.text_layout_details(cx);
13357 let style = &text_layout_details.editor_style;
13358 let font_id = cx.text_system().resolve_font(&style.text.font());
13359 let font_size = style.text.font_size.to_pixels(cx.rem_size());
13360 let line_height = style.text.line_height_in_pixels(cx.rem_size());
13361
13362 let em_width = cx
13363 .text_system()
13364 .typographic_bounds(font_id, font_size, 'm')
13365 .unwrap()
13366 .size
13367 .width;
13368
13369 gpui::Point::new(em_width, line_height)
13370 }
13371}
13372
13373fn get_unstaged_changes_for_buffers(
13374 project: &Model<Project>,
13375 buffers: impl IntoIterator<Item = Model<Buffer>>,
13376 cx: &mut ViewContext<Editor>,
13377) {
13378 let mut tasks = Vec::new();
13379 project.update(cx, |project, cx| {
13380 for buffer in buffers {
13381 tasks.push(project.open_unstaged_changes(buffer.clone(), cx))
13382 }
13383 });
13384 cx.spawn(|this, mut cx| async move {
13385 let change_sets = futures::future::join_all(tasks).await;
13386 this.update(&mut cx, |this, cx| {
13387 for change_set in change_sets {
13388 if let Some(change_set) = change_set.log_err() {
13389 this.diff_map.add_change_set(change_set, cx);
13390 }
13391 }
13392 })
13393 .ok();
13394 })
13395 .detach();
13396}
13397
13398fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
13399 let tab_size = tab_size.get() as usize;
13400 let mut width = offset;
13401
13402 for ch in text.chars() {
13403 width += if ch == '\t' {
13404 tab_size - (width % tab_size)
13405 } else {
13406 1
13407 };
13408 }
13409
13410 width - offset
13411}
13412
13413#[cfg(test)]
13414mod tests {
13415 use super::*;
13416
13417 #[test]
13418 fn test_string_size_with_expanded_tabs() {
13419 let nz = |val| NonZeroU32::new(val).unwrap();
13420 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
13421 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
13422 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
13423 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
13424 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
13425 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
13426 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
13427 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
13428 }
13429}
13430
13431/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
13432struct WordBreakingTokenizer<'a> {
13433 input: &'a str,
13434}
13435
13436impl<'a> WordBreakingTokenizer<'a> {
13437 fn new(input: &'a str) -> Self {
13438 Self { input }
13439 }
13440}
13441
13442fn is_char_ideographic(ch: char) -> bool {
13443 use unicode_script::Script::*;
13444 use unicode_script::UnicodeScript;
13445 matches!(ch.script(), Han | Tangut | Yi)
13446}
13447
13448fn is_grapheme_ideographic(text: &str) -> bool {
13449 text.chars().any(is_char_ideographic)
13450}
13451
13452fn is_grapheme_whitespace(text: &str) -> bool {
13453 text.chars().any(|x| x.is_whitespace())
13454}
13455
13456fn should_stay_with_preceding_ideograph(text: &str) -> bool {
13457 text.chars().next().map_or(false, |ch| {
13458 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
13459 })
13460}
13461
13462#[derive(PartialEq, Eq, Debug, Clone, Copy)]
13463struct WordBreakToken<'a> {
13464 token: &'a str,
13465 grapheme_len: usize,
13466 is_whitespace: bool,
13467}
13468
13469impl<'a> Iterator for WordBreakingTokenizer<'a> {
13470 /// Yields a span, the count of graphemes in the token, and whether it was
13471 /// whitespace. Note that it also breaks at word boundaries.
13472 type Item = WordBreakToken<'a>;
13473
13474 fn next(&mut self) -> Option<Self::Item> {
13475 use unicode_segmentation::UnicodeSegmentation;
13476 if self.input.is_empty() {
13477 return None;
13478 }
13479
13480 let mut iter = self.input.graphemes(true).peekable();
13481 let mut offset = 0;
13482 let mut graphemes = 0;
13483 if let Some(first_grapheme) = iter.next() {
13484 let is_whitespace = is_grapheme_whitespace(first_grapheme);
13485 offset += first_grapheme.len();
13486 graphemes += 1;
13487 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
13488 if let Some(grapheme) = iter.peek().copied() {
13489 if should_stay_with_preceding_ideograph(grapheme) {
13490 offset += grapheme.len();
13491 graphemes += 1;
13492 }
13493 }
13494 } else {
13495 let mut words = self.input[offset..].split_word_bound_indices().peekable();
13496 let mut next_word_bound = words.peek().copied();
13497 if next_word_bound.map_or(false, |(i, _)| i == 0) {
13498 next_word_bound = words.next();
13499 }
13500 while let Some(grapheme) = iter.peek().copied() {
13501 if next_word_bound.map_or(false, |(i, _)| i == offset) {
13502 break;
13503 };
13504 if is_grapheme_whitespace(grapheme) != is_whitespace {
13505 break;
13506 };
13507 offset += grapheme.len();
13508 graphemes += 1;
13509 iter.next();
13510 }
13511 }
13512 let token = &self.input[..offset];
13513 self.input = &self.input[offset..];
13514 if is_whitespace {
13515 Some(WordBreakToken {
13516 token: " ",
13517 grapheme_len: 1,
13518 is_whitespace: true,
13519 })
13520 } else {
13521 Some(WordBreakToken {
13522 token,
13523 grapheme_len: graphemes,
13524 is_whitespace: false,
13525 })
13526 }
13527 } else {
13528 None
13529 }
13530 }
13531}
13532
13533#[test]
13534fn test_word_breaking_tokenizer() {
13535 let tests: &[(&str, &[(&str, usize, bool)])] = &[
13536 ("", &[]),
13537 (" ", &[(" ", 1, true)]),
13538 ("Ʒ", &[("Ʒ", 1, false)]),
13539 ("Ǽ", &[("Ǽ", 1, false)]),
13540 ("⋑", &[("⋑", 1, false)]),
13541 ("⋑⋑", &[("⋑⋑", 2, false)]),
13542 (
13543 "原理,进而",
13544 &[
13545 ("原", 1, false),
13546 ("理,", 2, false),
13547 ("进", 1, false),
13548 ("而", 1, false),
13549 ],
13550 ),
13551 (
13552 "hello world",
13553 &[("hello", 5, false), (" ", 1, true), ("world", 5, false)],
13554 ),
13555 (
13556 "hello, world",
13557 &[("hello,", 6, false), (" ", 1, true), ("world", 5, false)],
13558 ),
13559 (
13560 " hello world",
13561 &[
13562 (" ", 1, true),
13563 ("hello", 5, false),
13564 (" ", 1, true),
13565 ("world", 5, false),
13566 ],
13567 ),
13568 (
13569 "这是什么 \n 钢笔",
13570 &[
13571 ("这", 1, false),
13572 ("是", 1, false),
13573 ("什", 1, false),
13574 ("么", 1, false),
13575 (" ", 1, true),
13576 ("钢", 1, false),
13577 ("笔", 1, false),
13578 ],
13579 ),
13580 (" mutton", &[(" ", 1, true), ("mutton", 6, false)]),
13581 ];
13582
13583 for (input, result) in tests {
13584 assert_eq!(
13585 WordBreakingTokenizer::new(input).collect::<Vec<_>>(),
13586 result
13587 .iter()
13588 .copied()
13589 .map(|(token, grapheme_len, is_whitespace)| WordBreakToken {
13590 token,
13591 grapheme_len,
13592 is_whitespace,
13593 })
13594 .collect::<Vec<_>>()
13595 );
13596 }
13597}
13598
13599fn wrap_with_prefix(
13600 line_prefix: String,
13601 unwrapped_text: String,
13602 wrap_column: usize,
13603 tab_size: NonZeroU32,
13604) -> String {
13605 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
13606 let mut wrapped_text = String::new();
13607 let mut current_line = line_prefix.clone();
13608
13609 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
13610 let mut current_line_len = line_prefix_len;
13611 for WordBreakToken {
13612 token,
13613 grapheme_len,
13614 is_whitespace,
13615 } in tokenizer
13616 {
13617 if current_line_len + grapheme_len > wrap_column && current_line_len != line_prefix_len {
13618 wrapped_text.push_str(current_line.trim_end());
13619 wrapped_text.push('\n');
13620 current_line.truncate(line_prefix.len());
13621 current_line_len = line_prefix_len;
13622 if !is_whitespace {
13623 current_line.push_str(token);
13624 current_line_len += grapheme_len;
13625 }
13626 } else if !is_whitespace {
13627 current_line.push_str(token);
13628 current_line_len += grapheme_len;
13629 } else if current_line_len != line_prefix_len {
13630 current_line.push(' ');
13631 current_line_len += 1;
13632 }
13633 }
13634
13635 if !current_line.is_empty() {
13636 wrapped_text.push_str(¤t_line);
13637 }
13638 wrapped_text
13639}
13640
13641#[test]
13642fn test_wrap_with_prefix() {
13643 assert_eq!(
13644 wrap_with_prefix(
13645 "# ".to_string(),
13646 "abcdefg".to_string(),
13647 4,
13648 NonZeroU32::new(4).unwrap()
13649 ),
13650 "# abcdefg"
13651 );
13652 assert_eq!(
13653 wrap_with_prefix(
13654 "".to_string(),
13655 "\thello world".to_string(),
13656 8,
13657 NonZeroU32::new(4).unwrap()
13658 ),
13659 "hello\nworld"
13660 );
13661 assert_eq!(
13662 wrap_with_prefix(
13663 "// ".to_string(),
13664 "xx \nyy zz aa bb cc".to_string(),
13665 12,
13666 NonZeroU32::new(4).unwrap()
13667 ),
13668 "// xx yy zz\n// aa bb cc"
13669 );
13670 assert_eq!(
13671 wrap_with_prefix(
13672 String::new(),
13673 "这是什么 \n 钢笔".to_string(),
13674 3,
13675 NonZeroU32::new(4).unwrap()
13676 ),
13677 "这是什\n么 钢\n笔"
13678 );
13679}
13680
13681fn hunks_for_selections(
13682 snapshot: &EditorSnapshot,
13683 selections: &[Selection<Point>],
13684) -> Vec<MultiBufferDiffHunk> {
13685 hunks_for_ranges(
13686 selections.iter().map(|selection| selection.range()),
13687 snapshot,
13688 )
13689}
13690
13691pub fn hunks_for_ranges(
13692 ranges: impl Iterator<Item = Range<Point>>,
13693 snapshot: &EditorSnapshot,
13694) -> Vec<MultiBufferDiffHunk> {
13695 let mut hunks = Vec::new();
13696 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
13697 HashMap::default();
13698 for query_range in ranges {
13699 let query_rows =
13700 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
13701 for hunk in snapshot.diff_map.diff_hunks_in_range(
13702 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
13703 &snapshot.buffer_snapshot,
13704 ) {
13705 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
13706 // when the caret is just above or just below the deleted hunk.
13707 let allow_adjacent = hunk_status(&hunk) == DiffHunkStatus::Removed;
13708 let related_to_selection = if allow_adjacent {
13709 hunk.row_range.overlaps(&query_rows)
13710 || hunk.row_range.start == query_rows.end
13711 || hunk.row_range.end == query_rows.start
13712 } else {
13713 hunk.row_range.overlaps(&query_rows)
13714 };
13715 if related_to_selection {
13716 if !processed_buffer_rows
13717 .entry(hunk.buffer_id)
13718 .or_default()
13719 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
13720 {
13721 continue;
13722 }
13723 hunks.push(hunk);
13724 }
13725 }
13726 }
13727
13728 hunks
13729}
13730
13731pub trait CollaborationHub {
13732 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
13733 fn user_participant_indices<'a>(
13734 &self,
13735 cx: &'a AppContext,
13736 ) -> &'a HashMap<u64, ParticipantIndex>;
13737 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
13738}
13739
13740impl CollaborationHub for Model<Project> {
13741 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
13742 self.read(cx).collaborators()
13743 }
13744
13745 fn user_participant_indices<'a>(
13746 &self,
13747 cx: &'a AppContext,
13748 ) -> &'a HashMap<u64, ParticipantIndex> {
13749 self.read(cx).user_store().read(cx).participant_indices()
13750 }
13751
13752 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
13753 let this = self.read(cx);
13754 let user_ids = this.collaborators().values().map(|c| c.user_id);
13755 this.user_store().read_with(cx, |user_store, cx| {
13756 user_store.participant_names(user_ids, cx)
13757 })
13758 }
13759}
13760
13761pub trait SemanticsProvider {
13762 fn hover(
13763 &self,
13764 buffer: &Model<Buffer>,
13765 position: text::Anchor,
13766 cx: &mut AppContext,
13767 ) -> Option<Task<Vec<project::Hover>>>;
13768
13769 fn inlay_hints(
13770 &self,
13771 buffer_handle: Model<Buffer>,
13772 range: Range<text::Anchor>,
13773 cx: &mut AppContext,
13774 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
13775
13776 fn resolve_inlay_hint(
13777 &self,
13778 hint: InlayHint,
13779 buffer_handle: Model<Buffer>,
13780 server_id: LanguageServerId,
13781 cx: &mut AppContext,
13782 ) -> Option<Task<anyhow::Result<InlayHint>>>;
13783
13784 fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool;
13785
13786 fn document_highlights(
13787 &self,
13788 buffer: &Model<Buffer>,
13789 position: text::Anchor,
13790 cx: &mut AppContext,
13791 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
13792
13793 fn definitions(
13794 &self,
13795 buffer: &Model<Buffer>,
13796 position: text::Anchor,
13797 kind: GotoDefinitionKind,
13798 cx: &mut AppContext,
13799 ) -> Option<Task<Result<Vec<LocationLink>>>>;
13800
13801 fn range_for_rename(
13802 &self,
13803 buffer: &Model<Buffer>,
13804 position: text::Anchor,
13805 cx: &mut AppContext,
13806 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
13807
13808 fn perform_rename(
13809 &self,
13810 buffer: &Model<Buffer>,
13811 position: text::Anchor,
13812 new_name: String,
13813 cx: &mut AppContext,
13814 ) -> Option<Task<Result<ProjectTransaction>>>;
13815}
13816
13817pub trait CompletionProvider {
13818 fn completions(
13819 &self,
13820 buffer: &Model<Buffer>,
13821 buffer_position: text::Anchor,
13822 trigger: CompletionContext,
13823 cx: &mut ViewContext<Editor>,
13824 ) -> Task<Result<Vec<Completion>>>;
13825
13826 fn resolve_completions(
13827 &self,
13828 buffer: Model<Buffer>,
13829 completion_indices: Vec<usize>,
13830 completions: Arc<RwLock<Box<[Completion]>>>,
13831 cx: &mut ViewContext<Editor>,
13832 ) -> Task<Result<bool>>;
13833
13834 fn apply_additional_edits_for_completion(
13835 &self,
13836 buffer: Model<Buffer>,
13837 completion: Completion,
13838 push_to_history: bool,
13839 cx: &mut ViewContext<Editor>,
13840 ) -> Task<Result<Option<language::Transaction>>>;
13841
13842 fn is_completion_trigger(
13843 &self,
13844 buffer: &Model<Buffer>,
13845 position: language::Anchor,
13846 text: &str,
13847 trigger_in_words: bool,
13848 cx: &mut ViewContext<Editor>,
13849 ) -> bool;
13850
13851 fn sort_completions(&self) -> bool {
13852 true
13853 }
13854}
13855
13856pub trait CodeActionProvider {
13857 fn code_actions(
13858 &self,
13859 buffer: &Model<Buffer>,
13860 range: Range<text::Anchor>,
13861 cx: &mut WindowContext,
13862 ) -> Task<Result<Vec<CodeAction>>>;
13863
13864 fn apply_code_action(
13865 &self,
13866 buffer_handle: Model<Buffer>,
13867 action: CodeAction,
13868 excerpt_id: ExcerptId,
13869 push_to_history: bool,
13870 cx: &mut WindowContext,
13871 ) -> Task<Result<ProjectTransaction>>;
13872}
13873
13874impl CodeActionProvider for Model<Project> {
13875 fn code_actions(
13876 &self,
13877 buffer: &Model<Buffer>,
13878 range: Range<text::Anchor>,
13879 cx: &mut WindowContext,
13880 ) -> Task<Result<Vec<CodeAction>>> {
13881 self.update(cx, |project, cx| {
13882 project.code_actions(buffer, range, None, cx)
13883 })
13884 }
13885
13886 fn apply_code_action(
13887 &self,
13888 buffer_handle: Model<Buffer>,
13889 action: CodeAction,
13890 _excerpt_id: ExcerptId,
13891 push_to_history: bool,
13892 cx: &mut WindowContext,
13893 ) -> Task<Result<ProjectTransaction>> {
13894 self.update(cx, |project, cx| {
13895 project.apply_code_action(buffer_handle, action, push_to_history, cx)
13896 })
13897 }
13898}
13899
13900fn snippet_completions(
13901 project: &Project,
13902 buffer: &Model<Buffer>,
13903 buffer_position: text::Anchor,
13904 cx: &mut AppContext,
13905) -> Task<Result<Vec<Completion>>> {
13906 let language = buffer.read(cx).language_at(buffer_position);
13907 let language_name = language.as_ref().map(|language| language.lsp_id());
13908 let snippet_store = project.snippets().read(cx);
13909 let snippets = snippet_store.snippets_for(language_name, cx);
13910
13911 if snippets.is_empty() {
13912 return Task::ready(Ok(vec![]));
13913 }
13914 let snapshot = buffer.read(cx).text_snapshot();
13915 let chars: String = snapshot
13916 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
13917 .collect();
13918
13919 let scope = language.map(|language| language.default_scope());
13920 let executor = cx.background_executor().clone();
13921
13922 cx.background_executor().spawn(async move {
13923 let classifier = CharClassifier::new(scope).for_completion(true);
13924 let mut last_word = chars
13925 .chars()
13926 .take_while(|c| classifier.is_word(*c))
13927 .collect::<String>();
13928 last_word = last_word.chars().rev().collect();
13929
13930 if last_word.is_empty() {
13931 return Ok(vec![]);
13932 }
13933
13934 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
13935 let to_lsp = |point: &text::Anchor| {
13936 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
13937 point_to_lsp(end)
13938 };
13939 let lsp_end = to_lsp(&buffer_position);
13940
13941 let candidates = snippets
13942 .iter()
13943 .enumerate()
13944 .flat_map(|(ix, snippet)| {
13945 snippet
13946 .prefix
13947 .iter()
13948 .map(move |prefix| StringMatchCandidate::new(ix, prefix.clone()))
13949 })
13950 .collect::<Vec<StringMatchCandidate>>();
13951
13952 let mut matches = fuzzy::match_strings(
13953 &candidates,
13954 &last_word,
13955 last_word.chars().any(|c| c.is_uppercase()),
13956 100,
13957 &Default::default(),
13958 executor,
13959 )
13960 .await;
13961
13962 // Remove all candidates where the query's start does not match the start of any word in the candidate
13963 if let Some(query_start) = last_word.chars().next() {
13964 matches.retain(|string_match| {
13965 split_words(&string_match.string).any(|word| {
13966 // Check that the first codepoint of the word as lowercase matches the first
13967 // codepoint of the query as lowercase
13968 word.chars()
13969 .flat_map(|codepoint| codepoint.to_lowercase())
13970 .zip(query_start.to_lowercase())
13971 .all(|(word_cp, query_cp)| word_cp == query_cp)
13972 })
13973 });
13974 }
13975
13976 let matched_strings = matches
13977 .into_iter()
13978 .map(|m| m.string)
13979 .collect::<HashSet<_>>();
13980
13981 let result: Vec<Completion> = snippets
13982 .into_iter()
13983 .filter_map(|snippet| {
13984 let matching_prefix = snippet
13985 .prefix
13986 .iter()
13987 .find(|prefix| matched_strings.contains(*prefix))?;
13988 let start = as_offset - last_word.len();
13989 let start = snapshot.anchor_before(start);
13990 let range = start..buffer_position;
13991 let lsp_start = to_lsp(&start);
13992 let lsp_range = lsp::Range {
13993 start: lsp_start,
13994 end: lsp_end,
13995 };
13996 Some(Completion {
13997 old_range: range,
13998 new_text: snippet.body.clone(),
13999 label: CodeLabel {
14000 text: matching_prefix.clone(),
14001 runs: vec![],
14002 filter_range: 0..matching_prefix.len(),
14003 },
14004 server_id: LanguageServerId(usize::MAX),
14005 documentation: snippet.description.clone().map(Documentation::SingleLine),
14006 lsp_completion: lsp::CompletionItem {
14007 label: snippet.prefix.first().unwrap().clone(),
14008 kind: Some(CompletionItemKind::SNIPPET),
14009 label_details: snippet.description.as_ref().map(|description| {
14010 lsp::CompletionItemLabelDetails {
14011 detail: Some(description.clone()),
14012 description: None,
14013 }
14014 }),
14015 insert_text_format: Some(InsertTextFormat::SNIPPET),
14016 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
14017 lsp::InsertReplaceEdit {
14018 new_text: snippet.body.clone(),
14019 insert: lsp_range,
14020 replace: lsp_range,
14021 },
14022 )),
14023 filter_text: Some(snippet.body.clone()),
14024 sort_text: Some(char::MAX.to_string()),
14025 ..Default::default()
14026 },
14027 confirm: None,
14028 })
14029 })
14030 .collect();
14031
14032 Ok(result)
14033 })
14034}
14035
14036impl CompletionProvider for Model<Project> {
14037 fn completions(
14038 &self,
14039 buffer: &Model<Buffer>,
14040 buffer_position: text::Anchor,
14041 options: CompletionContext,
14042 cx: &mut ViewContext<Editor>,
14043 ) -> Task<Result<Vec<Completion>>> {
14044 self.update(cx, |project, cx| {
14045 let snippets = snippet_completions(project, buffer, buffer_position, cx);
14046 let project_completions = project.completions(buffer, buffer_position, options, cx);
14047 cx.background_executor().spawn(async move {
14048 let mut completions = project_completions.await?;
14049 let snippets_completions = snippets.await?;
14050 completions.extend(snippets_completions);
14051 Ok(completions)
14052 })
14053 })
14054 }
14055
14056 fn resolve_completions(
14057 &self,
14058 buffer: Model<Buffer>,
14059 completion_indices: Vec<usize>,
14060 completions: Arc<RwLock<Box<[Completion]>>>,
14061 cx: &mut ViewContext<Editor>,
14062 ) -> Task<Result<bool>> {
14063 self.update(cx, |project, cx| {
14064 project.resolve_completions(buffer, completion_indices, completions, cx)
14065 })
14066 }
14067
14068 fn apply_additional_edits_for_completion(
14069 &self,
14070 buffer: Model<Buffer>,
14071 completion: Completion,
14072 push_to_history: bool,
14073 cx: &mut ViewContext<Editor>,
14074 ) -> Task<Result<Option<language::Transaction>>> {
14075 self.update(cx, |project, cx| {
14076 project.apply_additional_edits_for_completion(buffer, completion, push_to_history, cx)
14077 })
14078 }
14079
14080 fn is_completion_trigger(
14081 &self,
14082 buffer: &Model<Buffer>,
14083 position: language::Anchor,
14084 text: &str,
14085 trigger_in_words: bool,
14086 cx: &mut ViewContext<Editor>,
14087 ) -> bool {
14088 if !EditorSettings::get_global(cx).show_completions_on_input {
14089 return false;
14090 }
14091
14092 let mut chars = text.chars();
14093 let char = if let Some(char) = chars.next() {
14094 char
14095 } else {
14096 return false;
14097 };
14098 if chars.next().is_some() {
14099 return false;
14100 }
14101
14102 let buffer = buffer.read(cx);
14103 let classifier = buffer
14104 .snapshot()
14105 .char_classifier_at(position)
14106 .for_completion(true);
14107 if trigger_in_words && classifier.is_word(char) {
14108 return true;
14109 }
14110
14111 buffer.completion_triggers().contains(text)
14112 }
14113}
14114
14115impl SemanticsProvider for Model<Project> {
14116 fn hover(
14117 &self,
14118 buffer: &Model<Buffer>,
14119 position: text::Anchor,
14120 cx: &mut AppContext,
14121 ) -> Option<Task<Vec<project::Hover>>> {
14122 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
14123 }
14124
14125 fn document_highlights(
14126 &self,
14127 buffer: &Model<Buffer>,
14128 position: text::Anchor,
14129 cx: &mut AppContext,
14130 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
14131 Some(self.update(cx, |project, cx| {
14132 project.document_highlights(buffer, position, cx)
14133 }))
14134 }
14135
14136 fn definitions(
14137 &self,
14138 buffer: &Model<Buffer>,
14139 position: text::Anchor,
14140 kind: GotoDefinitionKind,
14141 cx: &mut AppContext,
14142 ) -> Option<Task<Result<Vec<LocationLink>>>> {
14143 Some(self.update(cx, |project, cx| match kind {
14144 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
14145 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
14146 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
14147 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
14148 }))
14149 }
14150
14151 fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool {
14152 // TODO: make this work for remote projects
14153 self.read(cx)
14154 .language_servers_for_buffer(buffer.read(cx), cx)
14155 .any(
14156 |(_, server)| match server.capabilities().inlay_hint_provider {
14157 Some(lsp::OneOf::Left(enabled)) => enabled,
14158 Some(lsp::OneOf::Right(_)) => true,
14159 None => false,
14160 },
14161 )
14162 }
14163
14164 fn inlay_hints(
14165 &self,
14166 buffer_handle: Model<Buffer>,
14167 range: Range<text::Anchor>,
14168 cx: &mut AppContext,
14169 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
14170 Some(self.update(cx, |project, cx| {
14171 project.inlay_hints(buffer_handle, range, cx)
14172 }))
14173 }
14174
14175 fn resolve_inlay_hint(
14176 &self,
14177 hint: InlayHint,
14178 buffer_handle: Model<Buffer>,
14179 server_id: LanguageServerId,
14180 cx: &mut AppContext,
14181 ) -> Option<Task<anyhow::Result<InlayHint>>> {
14182 Some(self.update(cx, |project, cx| {
14183 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
14184 }))
14185 }
14186
14187 fn range_for_rename(
14188 &self,
14189 buffer: &Model<Buffer>,
14190 position: text::Anchor,
14191 cx: &mut AppContext,
14192 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
14193 Some(self.update(cx, |project, cx| {
14194 project.prepare_rename(buffer.clone(), position, cx)
14195 }))
14196 }
14197
14198 fn perform_rename(
14199 &self,
14200 buffer: &Model<Buffer>,
14201 position: text::Anchor,
14202 new_name: String,
14203 cx: &mut AppContext,
14204 ) -> Option<Task<Result<ProjectTransaction>>> {
14205 Some(self.update(cx, |project, cx| {
14206 project.perform_rename(buffer.clone(), position, new_name, cx)
14207 }))
14208 }
14209}
14210
14211fn inlay_hint_settings(
14212 location: Anchor,
14213 snapshot: &MultiBufferSnapshot,
14214 cx: &mut ViewContext<'_, Editor>,
14215) -> InlayHintSettings {
14216 let file = snapshot.file_at(location);
14217 let language = snapshot.language_at(location).map(|l| l.name());
14218 language_settings(language, file, cx).inlay_hints
14219}
14220
14221fn consume_contiguous_rows(
14222 contiguous_row_selections: &mut Vec<Selection<Point>>,
14223 selection: &Selection<Point>,
14224 display_map: &DisplaySnapshot,
14225 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
14226) -> (MultiBufferRow, MultiBufferRow) {
14227 contiguous_row_selections.push(selection.clone());
14228 let start_row = MultiBufferRow(selection.start.row);
14229 let mut end_row = ending_row(selection, display_map);
14230
14231 while let Some(next_selection) = selections.peek() {
14232 if next_selection.start.row <= end_row.0 {
14233 end_row = ending_row(next_selection, display_map);
14234 contiguous_row_selections.push(selections.next().unwrap().clone());
14235 } else {
14236 break;
14237 }
14238 }
14239 (start_row, end_row)
14240}
14241
14242fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
14243 if next_selection.end.column > 0 || next_selection.is_empty() {
14244 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
14245 } else {
14246 MultiBufferRow(next_selection.end.row)
14247 }
14248}
14249
14250impl EditorSnapshot {
14251 pub fn remote_selections_in_range<'a>(
14252 &'a self,
14253 range: &'a Range<Anchor>,
14254 collaboration_hub: &dyn CollaborationHub,
14255 cx: &'a AppContext,
14256 ) -> impl 'a + Iterator<Item = RemoteSelection> {
14257 let participant_names = collaboration_hub.user_names(cx);
14258 let participant_indices = collaboration_hub.user_participant_indices(cx);
14259 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
14260 let collaborators_by_replica_id = collaborators_by_peer_id
14261 .iter()
14262 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
14263 .collect::<HashMap<_, _>>();
14264 self.buffer_snapshot
14265 .selections_in_range(range, false)
14266 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
14267 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
14268 let participant_index = participant_indices.get(&collaborator.user_id).copied();
14269 let user_name = participant_names.get(&collaborator.user_id).cloned();
14270 Some(RemoteSelection {
14271 replica_id,
14272 selection,
14273 cursor_shape,
14274 line_mode,
14275 participant_index,
14276 peer_id: collaborator.peer_id,
14277 user_name,
14278 })
14279 })
14280 }
14281
14282 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
14283 self.display_snapshot.buffer_snapshot.language_at(position)
14284 }
14285
14286 pub fn is_focused(&self) -> bool {
14287 self.is_focused
14288 }
14289
14290 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
14291 self.placeholder_text.as_ref()
14292 }
14293
14294 pub fn scroll_position(&self) -> gpui::Point<f32> {
14295 self.scroll_anchor.scroll_position(&self.display_snapshot)
14296 }
14297
14298 fn gutter_dimensions(
14299 &self,
14300 font_id: FontId,
14301 font_size: Pixels,
14302 em_width: Pixels,
14303 em_advance: Pixels,
14304 max_line_number_width: Pixels,
14305 cx: &AppContext,
14306 ) -> GutterDimensions {
14307 if !self.show_gutter {
14308 return GutterDimensions::default();
14309 }
14310 let descent = cx.text_system().descent(font_id, font_size);
14311
14312 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
14313 matches!(
14314 ProjectSettings::get_global(cx).git.git_gutter,
14315 Some(GitGutterSetting::TrackedFiles)
14316 )
14317 });
14318 let gutter_settings = EditorSettings::get_global(cx).gutter;
14319 let show_line_numbers = self
14320 .show_line_numbers
14321 .unwrap_or(gutter_settings.line_numbers);
14322 let line_gutter_width = if show_line_numbers {
14323 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
14324 let min_width_for_number_on_gutter = em_advance * 4.0;
14325 max_line_number_width.max(min_width_for_number_on_gutter)
14326 } else {
14327 0.0.into()
14328 };
14329
14330 let show_code_actions = self
14331 .show_code_actions
14332 .unwrap_or(gutter_settings.code_actions);
14333
14334 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
14335
14336 let git_blame_entries_width =
14337 self.git_blame_gutter_max_author_length
14338 .map(|max_author_length| {
14339 // Length of the author name, but also space for the commit hash,
14340 // the spacing and the timestamp.
14341 let max_char_count = max_author_length
14342 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
14343 + 7 // length of commit sha
14344 + 14 // length of max relative timestamp ("60 minutes ago")
14345 + 4; // gaps and margins
14346
14347 em_advance * max_char_count
14348 });
14349
14350 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
14351 left_padding += if show_code_actions || show_runnables {
14352 em_width * 3.0
14353 } else if show_git_gutter && show_line_numbers {
14354 em_width * 2.0
14355 } else if show_git_gutter || show_line_numbers {
14356 em_width
14357 } else {
14358 px(0.)
14359 };
14360
14361 let right_padding = if gutter_settings.folds && show_line_numbers {
14362 em_width * 4.0
14363 } else if gutter_settings.folds {
14364 em_width * 3.0
14365 } else if show_line_numbers {
14366 em_width
14367 } else {
14368 px(0.)
14369 };
14370
14371 GutterDimensions {
14372 left_padding,
14373 right_padding,
14374 width: line_gutter_width + left_padding + right_padding,
14375 margin: -descent,
14376 git_blame_entries_width,
14377 }
14378 }
14379
14380 pub fn render_crease_toggle(
14381 &self,
14382 buffer_row: MultiBufferRow,
14383 row_contains_cursor: bool,
14384 editor: View<Editor>,
14385 cx: &mut WindowContext,
14386 ) -> Option<AnyElement> {
14387 let folded = self.is_line_folded(buffer_row);
14388 let mut is_foldable = false;
14389
14390 if let Some(crease) = self
14391 .crease_snapshot
14392 .query_row(buffer_row, &self.buffer_snapshot)
14393 {
14394 is_foldable = true;
14395 match crease {
14396 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
14397 if let Some(render_toggle) = render_toggle {
14398 let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
14399 if folded {
14400 editor.update(cx, |editor, cx| {
14401 editor.fold_at(&crate::FoldAt { buffer_row }, cx)
14402 });
14403 } else {
14404 editor.update(cx, |editor, cx| {
14405 editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
14406 });
14407 }
14408 });
14409 return Some((render_toggle)(buffer_row, folded, toggle_callback, cx));
14410 }
14411 }
14412 }
14413 }
14414
14415 is_foldable |= self.starts_indent(buffer_row);
14416
14417 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
14418 Some(
14419 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
14420 .selected(folded)
14421 .on_click(cx.listener_for(&editor, move |this, _e, cx| {
14422 if folded {
14423 this.unfold_at(&UnfoldAt { buffer_row }, cx);
14424 } else {
14425 this.fold_at(&FoldAt { buffer_row }, cx);
14426 }
14427 }))
14428 .into_any_element(),
14429 )
14430 } else {
14431 None
14432 }
14433 }
14434
14435 pub fn render_crease_trailer(
14436 &self,
14437 buffer_row: MultiBufferRow,
14438 cx: &mut WindowContext,
14439 ) -> Option<AnyElement> {
14440 let folded = self.is_line_folded(buffer_row);
14441 if let Crease::Inline { render_trailer, .. } = self
14442 .crease_snapshot
14443 .query_row(buffer_row, &self.buffer_snapshot)?
14444 {
14445 let render_trailer = render_trailer.as_ref()?;
14446 Some(render_trailer(buffer_row, folded, cx))
14447 } else {
14448 None
14449 }
14450 }
14451}
14452
14453impl Deref for EditorSnapshot {
14454 type Target = DisplaySnapshot;
14455
14456 fn deref(&self) -> &Self::Target {
14457 &self.display_snapshot
14458 }
14459}
14460
14461#[derive(Clone, Debug, PartialEq, Eq)]
14462pub enum EditorEvent {
14463 InputIgnored {
14464 text: Arc<str>,
14465 },
14466 InputHandled {
14467 utf16_range_to_replace: Option<Range<isize>>,
14468 text: Arc<str>,
14469 },
14470 ExcerptsAdded {
14471 buffer: Model<Buffer>,
14472 predecessor: ExcerptId,
14473 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
14474 },
14475 ExcerptsRemoved {
14476 ids: Vec<ExcerptId>,
14477 },
14478 ExcerptsEdited {
14479 ids: Vec<ExcerptId>,
14480 },
14481 ExcerptsExpanded {
14482 ids: Vec<ExcerptId>,
14483 },
14484 BufferEdited,
14485 Edited {
14486 transaction_id: clock::Lamport,
14487 },
14488 Reparsed(BufferId),
14489 Focused,
14490 FocusedIn,
14491 Blurred,
14492 DirtyChanged,
14493 Saved,
14494 TitleChanged,
14495 DiffBaseChanged,
14496 SelectionsChanged {
14497 local: bool,
14498 },
14499 ScrollPositionChanged {
14500 local: bool,
14501 autoscroll: bool,
14502 },
14503 Closed,
14504 TransactionUndone {
14505 transaction_id: clock::Lamport,
14506 },
14507 TransactionBegun {
14508 transaction_id: clock::Lamport,
14509 },
14510 Reloaded,
14511 CursorShapeChanged,
14512}
14513
14514impl EventEmitter<EditorEvent> for Editor {}
14515
14516impl FocusableView for Editor {
14517 fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
14518 self.focus_handle.clone()
14519 }
14520}
14521
14522impl Render for Editor {
14523 fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
14524 let settings = ThemeSettings::get_global(cx);
14525
14526 let mut text_style = match self.mode {
14527 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
14528 color: cx.theme().colors().editor_foreground,
14529 font_family: settings.ui_font.family.clone(),
14530 font_features: settings.ui_font.features.clone(),
14531 font_fallbacks: settings.ui_font.fallbacks.clone(),
14532 font_size: rems(0.875).into(),
14533 font_weight: settings.ui_font.weight,
14534 line_height: relative(settings.buffer_line_height.value()),
14535 ..Default::default()
14536 },
14537 EditorMode::Full => TextStyle {
14538 color: cx.theme().colors().editor_foreground,
14539 font_family: settings.buffer_font.family.clone(),
14540 font_features: settings.buffer_font.features.clone(),
14541 font_fallbacks: settings.buffer_font.fallbacks.clone(),
14542 font_size: settings.buffer_font_size(cx).into(),
14543 font_weight: settings.buffer_font.weight,
14544 line_height: relative(settings.buffer_line_height.value()),
14545 ..Default::default()
14546 },
14547 };
14548 if let Some(text_style_refinement) = &self.text_style_refinement {
14549 text_style.refine(text_style_refinement)
14550 }
14551
14552 let background = match self.mode {
14553 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
14554 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
14555 EditorMode::Full => cx.theme().colors().editor_background,
14556 };
14557
14558 EditorElement::new(
14559 cx.view(),
14560 EditorStyle {
14561 background,
14562 local_player: cx.theme().players().local(),
14563 text: text_style,
14564 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
14565 syntax: cx.theme().syntax().clone(),
14566 status: cx.theme().status().clone(),
14567 inlay_hints_style: make_inlay_hints_style(cx),
14568 suggestions_style: HighlightStyle {
14569 color: Some(cx.theme().status().predictive),
14570 ..HighlightStyle::default()
14571 },
14572 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
14573 },
14574 )
14575 }
14576}
14577
14578impl ViewInputHandler for Editor {
14579 fn text_for_range(
14580 &mut self,
14581 range_utf16: Range<usize>,
14582 adjusted_range: &mut Option<Range<usize>>,
14583 cx: &mut ViewContext<Self>,
14584 ) -> Option<String> {
14585 let snapshot = self.buffer.read(cx).read(cx);
14586 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
14587 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
14588 if (start.0..end.0) != range_utf16 {
14589 adjusted_range.replace(start.0..end.0);
14590 }
14591 Some(snapshot.text_for_range(start..end).collect())
14592 }
14593
14594 fn selected_text_range(
14595 &mut self,
14596 ignore_disabled_input: bool,
14597 cx: &mut ViewContext<Self>,
14598 ) -> Option<UTF16Selection> {
14599 // Prevent the IME menu from appearing when holding down an alphabetic key
14600 // while input is disabled.
14601 if !ignore_disabled_input && !self.input_enabled {
14602 return None;
14603 }
14604
14605 let selection = self.selections.newest::<OffsetUtf16>(cx);
14606 let range = selection.range();
14607
14608 Some(UTF16Selection {
14609 range: range.start.0..range.end.0,
14610 reversed: selection.reversed,
14611 })
14612 }
14613
14614 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
14615 let snapshot = self.buffer.read(cx).read(cx);
14616 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
14617 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
14618 }
14619
14620 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
14621 self.clear_highlights::<InputComposition>(cx);
14622 self.ime_transaction.take();
14623 }
14624
14625 fn replace_text_in_range(
14626 &mut self,
14627 range_utf16: Option<Range<usize>>,
14628 text: &str,
14629 cx: &mut ViewContext<Self>,
14630 ) {
14631 if !self.input_enabled {
14632 cx.emit(EditorEvent::InputIgnored { text: text.into() });
14633 return;
14634 }
14635
14636 self.transact(cx, |this, cx| {
14637 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
14638 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
14639 Some(this.selection_replacement_ranges(range_utf16, cx))
14640 } else {
14641 this.marked_text_ranges(cx)
14642 };
14643
14644 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
14645 let newest_selection_id = this.selections.newest_anchor().id;
14646 this.selections
14647 .all::<OffsetUtf16>(cx)
14648 .iter()
14649 .zip(ranges_to_replace.iter())
14650 .find_map(|(selection, range)| {
14651 if selection.id == newest_selection_id {
14652 Some(
14653 (range.start.0 as isize - selection.head().0 as isize)
14654 ..(range.end.0 as isize - selection.head().0 as isize),
14655 )
14656 } else {
14657 None
14658 }
14659 })
14660 });
14661
14662 cx.emit(EditorEvent::InputHandled {
14663 utf16_range_to_replace: range_to_replace,
14664 text: text.into(),
14665 });
14666
14667 if let Some(new_selected_ranges) = new_selected_ranges {
14668 this.change_selections(None, cx, |selections| {
14669 selections.select_ranges(new_selected_ranges)
14670 });
14671 this.backspace(&Default::default(), cx);
14672 }
14673
14674 this.handle_input(text, cx);
14675 });
14676
14677 if let Some(transaction) = self.ime_transaction {
14678 self.buffer.update(cx, |buffer, cx| {
14679 buffer.group_until_transaction(transaction, cx);
14680 });
14681 }
14682
14683 self.unmark_text(cx);
14684 }
14685
14686 fn replace_and_mark_text_in_range(
14687 &mut self,
14688 range_utf16: Option<Range<usize>>,
14689 text: &str,
14690 new_selected_range_utf16: Option<Range<usize>>,
14691 cx: &mut ViewContext<Self>,
14692 ) {
14693 if !self.input_enabled {
14694 return;
14695 }
14696
14697 let transaction = self.transact(cx, |this, cx| {
14698 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
14699 let snapshot = this.buffer.read(cx).read(cx);
14700 if let Some(relative_range_utf16) = range_utf16.as_ref() {
14701 for marked_range in &mut marked_ranges {
14702 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
14703 marked_range.start.0 += relative_range_utf16.start;
14704 marked_range.start =
14705 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
14706 marked_range.end =
14707 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
14708 }
14709 }
14710 Some(marked_ranges)
14711 } else if let Some(range_utf16) = range_utf16 {
14712 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
14713 Some(this.selection_replacement_ranges(range_utf16, cx))
14714 } else {
14715 None
14716 };
14717
14718 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
14719 let newest_selection_id = this.selections.newest_anchor().id;
14720 this.selections
14721 .all::<OffsetUtf16>(cx)
14722 .iter()
14723 .zip(ranges_to_replace.iter())
14724 .find_map(|(selection, range)| {
14725 if selection.id == newest_selection_id {
14726 Some(
14727 (range.start.0 as isize - selection.head().0 as isize)
14728 ..(range.end.0 as isize - selection.head().0 as isize),
14729 )
14730 } else {
14731 None
14732 }
14733 })
14734 });
14735
14736 cx.emit(EditorEvent::InputHandled {
14737 utf16_range_to_replace: range_to_replace,
14738 text: text.into(),
14739 });
14740
14741 if let Some(ranges) = ranges_to_replace {
14742 this.change_selections(None, cx, |s| s.select_ranges(ranges));
14743 }
14744
14745 let marked_ranges = {
14746 let snapshot = this.buffer.read(cx).read(cx);
14747 this.selections
14748 .disjoint_anchors()
14749 .iter()
14750 .map(|selection| {
14751 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
14752 })
14753 .collect::<Vec<_>>()
14754 };
14755
14756 if text.is_empty() {
14757 this.unmark_text(cx);
14758 } else {
14759 this.highlight_text::<InputComposition>(
14760 marked_ranges.clone(),
14761 HighlightStyle {
14762 underline: Some(UnderlineStyle {
14763 thickness: px(1.),
14764 color: None,
14765 wavy: false,
14766 }),
14767 ..Default::default()
14768 },
14769 cx,
14770 );
14771 }
14772
14773 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
14774 let use_autoclose = this.use_autoclose;
14775 let use_auto_surround = this.use_auto_surround;
14776 this.set_use_autoclose(false);
14777 this.set_use_auto_surround(false);
14778 this.handle_input(text, cx);
14779 this.set_use_autoclose(use_autoclose);
14780 this.set_use_auto_surround(use_auto_surround);
14781
14782 if let Some(new_selected_range) = new_selected_range_utf16 {
14783 let snapshot = this.buffer.read(cx).read(cx);
14784 let new_selected_ranges = marked_ranges
14785 .into_iter()
14786 .map(|marked_range| {
14787 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
14788 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
14789 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
14790 snapshot.clip_offset_utf16(new_start, Bias::Left)
14791 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
14792 })
14793 .collect::<Vec<_>>();
14794
14795 drop(snapshot);
14796 this.change_selections(None, cx, |selections| {
14797 selections.select_ranges(new_selected_ranges)
14798 });
14799 }
14800 });
14801
14802 self.ime_transaction = self.ime_transaction.or(transaction);
14803 if let Some(transaction) = self.ime_transaction {
14804 self.buffer.update(cx, |buffer, cx| {
14805 buffer.group_until_transaction(transaction, cx);
14806 });
14807 }
14808
14809 if self.text_highlights::<InputComposition>(cx).is_none() {
14810 self.ime_transaction.take();
14811 }
14812 }
14813
14814 fn bounds_for_range(
14815 &mut self,
14816 range_utf16: Range<usize>,
14817 element_bounds: gpui::Bounds<Pixels>,
14818 cx: &mut ViewContext<Self>,
14819 ) -> Option<gpui::Bounds<Pixels>> {
14820 let text_layout_details = self.text_layout_details(cx);
14821 let gpui::Point {
14822 x: em_width,
14823 y: line_height,
14824 } = self.character_size(cx);
14825
14826 let snapshot = self.snapshot(cx);
14827 let scroll_position = snapshot.scroll_position();
14828 let scroll_left = scroll_position.x * em_width;
14829
14830 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
14831 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
14832 + self.gutter_dimensions.width
14833 + self.gutter_dimensions.margin;
14834 let y = line_height * (start.row().as_f32() - scroll_position.y);
14835
14836 Some(Bounds {
14837 origin: element_bounds.origin + point(x, y),
14838 size: size(em_width, line_height),
14839 })
14840 }
14841}
14842
14843trait SelectionExt {
14844 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
14845 fn spanned_rows(
14846 &self,
14847 include_end_if_at_line_start: bool,
14848 map: &DisplaySnapshot,
14849 ) -> Range<MultiBufferRow>;
14850}
14851
14852impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
14853 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
14854 let start = self
14855 .start
14856 .to_point(&map.buffer_snapshot)
14857 .to_display_point(map);
14858 let end = self
14859 .end
14860 .to_point(&map.buffer_snapshot)
14861 .to_display_point(map);
14862 if self.reversed {
14863 end..start
14864 } else {
14865 start..end
14866 }
14867 }
14868
14869 fn spanned_rows(
14870 &self,
14871 include_end_if_at_line_start: bool,
14872 map: &DisplaySnapshot,
14873 ) -> Range<MultiBufferRow> {
14874 let start = self.start.to_point(&map.buffer_snapshot);
14875 let mut end = self.end.to_point(&map.buffer_snapshot);
14876 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
14877 end.row -= 1;
14878 }
14879
14880 let buffer_start = map.prev_line_boundary(start).0;
14881 let buffer_end = map.next_line_boundary(end).0;
14882 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
14883 }
14884}
14885
14886impl<T: InvalidationRegion> InvalidationStack<T> {
14887 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
14888 where
14889 S: Clone + ToOffset,
14890 {
14891 while let Some(region) = self.last() {
14892 let all_selections_inside_invalidation_ranges =
14893 if selections.len() == region.ranges().len() {
14894 selections
14895 .iter()
14896 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
14897 .all(|(selection, invalidation_range)| {
14898 let head = selection.head().to_offset(buffer);
14899 invalidation_range.start <= head && invalidation_range.end >= head
14900 })
14901 } else {
14902 false
14903 };
14904
14905 if all_selections_inside_invalidation_ranges {
14906 break;
14907 } else {
14908 self.pop();
14909 }
14910 }
14911 }
14912}
14913
14914impl<T> Default for InvalidationStack<T> {
14915 fn default() -> Self {
14916 Self(Default::default())
14917 }
14918}
14919
14920impl<T> Deref for InvalidationStack<T> {
14921 type Target = Vec<T>;
14922
14923 fn deref(&self) -> &Self::Target {
14924 &self.0
14925 }
14926}
14927
14928impl<T> DerefMut for InvalidationStack<T> {
14929 fn deref_mut(&mut self) -> &mut Self::Target {
14930 &mut self.0
14931 }
14932}
14933
14934impl InvalidationRegion for SnippetState {
14935 fn ranges(&self) -> &[Range<Anchor>] {
14936 &self.ranges[self.active_index]
14937 }
14938}
14939
14940pub fn diagnostic_block_renderer(
14941 diagnostic: Diagnostic,
14942 max_message_rows: Option<u8>,
14943 allow_closing: bool,
14944 _is_valid: bool,
14945) -> RenderBlock {
14946 let (text_without_backticks, code_ranges) =
14947 highlight_diagnostic_message(&diagnostic, max_message_rows);
14948
14949 Arc::new(move |cx: &mut BlockContext| {
14950 let group_id: SharedString = cx.block_id.to_string().into();
14951
14952 let mut text_style = cx.text_style().clone();
14953 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
14954 let theme_settings = ThemeSettings::get_global(cx);
14955 text_style.font_family = theme_settings.buffer_font.family.clone();
14956 text_style.font_style = theme_settings.buffer_font.style;
14957 text_style.font_features = theme_settings.buffer_font.features.clone();
14958 text_style.font_weight = theme_settings.buffer_font.weight;
14959
14960 let multi_line_diagnostic = diagnostic.message.contains('\n');
14961
14962 let buttons = |diagnostic: &Diagnostic| {
14963 if multi_line_diagnostic {
14964 v_flex()
14965 } else {
14966 h_flex()
14967 }
14968 .when(allow_closing, |div| {
14969 div.children(diagnostic.is_primary.then(|| {
14970 IconButton::new("close-block", IconName::XCircle)
14971 .icon_color(Color::Muted)
14972 .size(ButtonSize::Compact)
14973 .style(ButtonStyle::Transparent)
14974 .visible_on_hover(group_id.clone())
14975 .on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
14976 .tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
14977 }))
14978 })
14979 .child(
14980 IconButton::new("copy-block", IconName::Copy)
14981 .icon_color(Color::Muted)
14982 .size(ButtonSize::Compact)
14983 .style(ButtonStyle::Transparent)
14984 .visible_on_hover(group_id.clone())
14985 .on_click({
14986 let message = diagnostic.message.clone();
14987 move |_click, cx| {
14988 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
14989 }
14990 })
14991 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
14992 )
14993 };
14994
14995 let icon_size = buttons(&diagnostic)
14996 .into_any_element()
14997 .layout_as_root(AvailableSpace::min_size(), cx);
14998
14999 h_flex()
15000 .id(cx.block_id)
15001 .group(group_id.clone())
15002 .relative()
15003 .size_full()
15004 .block_mouse_down()
15005 .pl(cx.gutter_dimensions.width)
15006 .w(cx.max_width - cx.gutter_dimensions.full_width())
15007 .child(
15008 div()
15009 .flex()
15010 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
15011 .flex_shrink(),
15012 )
15013 .child(buttons(&diagnostic))
15014 .child(div().flex().flex_shrink_0().child(
15015 StyledText::new(text_without_backticks.clone()).with_highlights(
15016 &text_style,
15017 code_ranges.iter().map(|range| {
15018 (
15019 range.clone(),
15020 HighlightStyle {
15021 font_weight: Some(FontWeight::BOLD),
15022 ..Default::default()
15023 },
15024 )
15025 }),
15026 ),
15027 ))
15028 .into_any_element()
15029 })
15030}
15031
15032pub fn highlight_diagnostic_message(
15033 diagnostic: &Diagnostic,
15034 mut max_message_rows: Option<u8>,
15035) -> (SharedString, Vec<Range<usize>>) {
15036 let mut text_without_backticks = String::new();
15037 let mut code_ranges = Vec::new();
15038
15039 if let Some(source) = &diagnostic.source {
15040 text_without_backticks.push_str(source);
15041 code_ranges.push(0..source.len());
15042 text_without_backticks.push_str(": ");
15043 }
15044
15045 let mut prev_offset = 0;
15046 let mut in_code_block = false;
15047 let has_row_limit = max_message_rows.is_some();
15048 let mut newline_indices = diagnostic
15049 .message
15050 .match_indices('\n')
15051 .filter(|_| has_row_limit)
15052 .map(|(ix, _)| ix)
15053 .fuse()
15054 .peekable();
15055
15056 for (quote_ix, _) in diagnostic
15057 .message
15058 .match_indices('`')
15059 .chain([(diagnostic.message.len(), "")])
15060 {
15061 let mut first_newline_ix = None;
15062 let mut last_newline_ix = None;
15063 while let Some(newline_ix) = newline_indices.peek() {
15064 if *newline_ix < quote_ix {
15065 if first_newline_ix.is_none() {
15066 first_newline_ix = Some(*newline_ix);
15067 }
15068 last_newline_ix = Some(*newline_ix);
15069
15070 if let Some(rows_left) = &mut max_message_rows {
15071 if *rows_left == 0 {
15072 break;
15073 } else {
15074 *rows_left -= 1;
15075 }
15076 }
15077 let _ = newline_indices.next();
15078 } else {
15079 break;
15080 }
15081 }
15082 let prev_len = text_without_backticks.len();
15083 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
15084 text_without_backticks.push_str(new_text);
15085 if in_code_block {
15086 code_ranges.push(prev_len..text_without_backticks.len());
15087 }
15088 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
15089 in_code_block = !in_code_block;
15090 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
15091 text_without_backticks.push_str("...");
15092 break;
15093 }
15094 }
15095
15096 (text_without_backticks.into(), code_ranges)
15097}
15098
15099fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
15100 match severity {
15101 DiagnosticSeverity::ERROR => colors.error,
15102 DiagnosticSeverity::WARNING => colors.warning,
15103 DiagnosticSeverity::INFORMATION => colors.info,
15104 DiagnosticSeverity::HINT => colors.info,
15105 _ => colors.ignored,
15106 }
15107}
15108
15109pub fn styled_runs_for_code_label<'a>(
15110 label: &'a CodeLabel,
15111 syntax_theme: &'a theme::SyntaxTheme,
15112) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
15113 let fade_out = HighlightStyle {
15114 fade_out: Some(0.35),
15115 ..Default::default()
15116 };
15117
15118 let mut prev_end = label.filter_range.end;
15119 label
15120 .runs
15121 .iter()
15122 .enumerate()
15123 .flat_map(move |(ix, (range, highlight_id))| {
15124 let style = if let Some(style) = highlight_id.style(syntax_theme) {
15125 style
15126 } else {
15127 return Default::default();
15128 };
15129 let mut muted_style = style;
15130 muted_style.highlight(fade_out);
15131
15132 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
15133 if range.start >= label.filter_range.end {
15134 if range.start > prev_end {
15135 runs.push((prev_end..range.start, fade_out));
15136 }
15137 runs.push((range.clone(), muted_style));
15138 } else if range.end <= label.filter_range.end {
15139 runs.push((range.clone(), style));
15140 } else {
15141 runs.push((range.start..label.filter_range.end, style));
15142 runs.push((label.filter_range.end..range.end, muted_style));
15143 }
15144 prev_end = cmp::max(prev_end, range.end);
15145
15146 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
15147 runs.push((prev_end..label.text.len(), fade_out));
15148 }
15149
15150 runs
15151 })
15152}
15153
15154pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
15155 let mut prev_index = 0;
15156 let mut prev_codepoint: Option<char> = None;
15157 text.char_indices()
15158 .chain([(text.len(), '\0')])
15159 .filter_map(move |(index, codepoint)| {
15160 let prev_codepoint = prev_codepoint.replace(codepoint)?;
15161 let is_boundary = index == text.len()
15162 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
15163 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
15164 if is_boundary {
15165 let chunk = &text[prev_index..index];
15166 prev_index = index;
15167 Some(chunk)
15168 } else {
15169 None
15170 }
15171 })
15172}
15173
15174pub trait RangeToAnchorExt: Sized {
15175 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
15176
15177 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
15178 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
15179 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
15180 }
15181}
15182
15183impl<T: ToOffset> RangeToAnchorExt for Range<T> {
15184 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
15185 let start_offset = self.start.to_offset(snapshot);
15186 let end_offset = self.end.to_offset(snapshot);
15187 if start_offset == end_offset {
15188 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
15189 } else {
15190 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
15191 }
15192 }
15193}
15194
15195pub trait RowExt {
15196 fn as_f32(&self) -> f32;
15197
15198 fn next_row(&self) -> Self;
15199
15200 fn previous_row(&self) -> Self;
15201
15202 fn minus(&self, other: Self) -> u32;
15203}
15204
15205impl RowExt for DisplayRow {
15206 fn as_f32(&self) -> f32 {
15207 self.0 as f32
15208 }
15209
15210 fn next_row(&self) -> Self {
15211 Self(self.0 + 1)
15212 }
15213
15214 fn previous_row(&self) -> Self {
15215 Self(self.0.saturating_sub(1))
15216 }
15217
15218 fn minus(&self, other: Self) -> u32 {
15219 self.0 - other.0
15220 }
15221}
15222
15223impl RowExt for MultiBufferRow {
15224 fn as_f32(&self) -> f32 {
15225 self.0 as f32
15226 }
15227
15228 fn next_row(&self) -> Self {
15229 Self(self.0 + 1)
15230 }
15231
15232 fn previous_row(&self) -> Self {
15233 Self(self.0.saturating_sub(1))
15234 }
15235
15236 fn minus(&self, other: Self) -> u32 {
15237 self.0 - other.0
15238 }
15239}
15240
15241trait RowRangeExt {
15242 type Row;
15243
15244 fn len(&self) -> usize;
15245
15246 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
15247}
15248
15249impl RowRangeExt for Range<MultiBufferRow> {
15250 type Row = MultiBufferRow;
15251
15252 fn len(&self) -> usize {
15253 (self.end.0 - self.start.0) as usize
15254 }
15255
15256 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
15257 (self.start.0..self.end.0).map(MultiBufferRow)
15258 }
15259}
15260
15261impl RowRangeExt for Range<DisplayRow> {
15262 type Row = DisplayRow;
15263
15264 fn len(&self) -> usize {
15265 (self.end.0 - self.start.0) as usize
15266 }
15267
15268 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
15269 (self.start.0..self.end.0).map(DisplayRow)
15270 }
15271}
15272
15273fn hunk_status(hunk: &MultiBufferDiffHunk) -> DiffHunkStatus {
15274 if hunk.diff_base_byte_range.is_empty() {
15275 DiffHunkStatus::Added
15276 } else if hunk.row_range.is_empty() {
15277 DiffHunkStatus::Removed
15278 } else {
15279 DiffHunkStatus::Modified
15280 }
15281}
15282
15283/// If select range has more than one line, we
15284/// just point the cursor to range.start.
15285fn check_multiline_range(buffer: &Buffer, range: Range<usize>) -> Range<usize> {
15286 if buffer.offset_to_point(range.start).row == buffer.offset_to_point(range.end).row {
15287 range
15288 } else {
15289 range.start..range.start
15290 }
15291}
15292
15293pub struct KillRing(ClipboardItem);
15294impl Global for KillRing {}
15295
15296const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);