1#![allow(rustdoc::private_intra_doc_links)]
2//! This is the place where everything editor-related is stored (data-wise) and displayed (ui-wise).
3//! The main point of interest in this crate is [`Editor`] type, which is used in every other Zed part as a user input element.
4//! It comes in different flavors: single line, multiline and a fixed height one.
5//!
6//! Editor contains of multiple large submodules:
7//! * [`element`] — the place where all rendering happens
8//! * [`display_map`] - chunks up text in the editor into the logical blocks, establishes coordinates and mapping between each of them.
9//! Contains all metadata related to text transformations (folds, fake inlay text insertions, soft wraps, tab markup, etc.).
10//! * [`inlay_hint_cache`] - is a storage of inlay hints out of LSP requests, responsible for querying LSP and updating `display_map`'s state accordingly.
11//!
12//! All other submodules and structs are mostly concerned with holding editor data about the way it displays current buffer region(s).
13//!
14//! If you're looking to improve Vim mode, you should check out Vim crate that wraps Editor and overrides its behavior.
15pub mod actions;
16mod blame_entry_tooltip;
17mod blink_manager;
18mod clangd_ext;
19mod code_context_menus;
20pub mod display_map;
21mod editor_settings;
22mod editor_settings_controls;
23mod element;
24mod git;
25mod highlight_matching_bracket;
26mod hover_links;
27mod hover_popover;
28mod hunk_diff;
29mod indent_guides;
30mod inlay_hint_cache;
31pub mod items;
32mod linked_editing_ranges;
33mod lsp_ext;
34mod mouse_context_menu;
35pub mod movement;
36mod persistence;
37mod proposed_changes_editor;
38mod rust_analyzer_ext;
39pub mod scroll;
40mod selections_collection;
41pub mod tasks;
42
43#[cfg(test)]
44mod editor_tests;
45#[cfg(test)]
46mod inline_completion_tests;
47mod signature_help;
48#[cfg(any(test, feature = "test-support"))]
49pub mod test;
50
51use ::git::diff::DiffHunkStatus;
52pub(crate) use actions::*;
53pub use actions::{OpenExcerpts, OpenExcerptsSplit};
54use aho_corasick::AhoCorasick;
55use anyhow::{anyhow, Context as _, Result};
56use blink_manager::BlinkManager;
57use client::{Collaborator, ParticipantIndex};
58use clock::ReplicaId;
59use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
60use convert_case::{Case, Casing};
61use display_map::*;
62pub use display_map::{DisplayPoint, FoldPlaceholder};
63pub use editor_settings::{
64 CurrentLineHighlight, EditorSettings, ScrollBeyondLastLine, SearchSettings, ShowScrollbar,
65};
66pub use editor_settings_controls::*;
67use element::LineWithInvisibles;
68pub use element::{
69 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
70};
71use futures::{future, FutureExt};
72use fuzzy::StringMatchCandidate;
73
74use code_context_menus::{
75 AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
76 CompletionsMenu, ContextMenuOrigin,
77};
78use git::blame::GitBlame;
79use gpui::{
80 div, impl_actions, point, prelude::*, px, relative, size, Action, AnyElement, AppContext,
81 AsyncWindowContext, AvailableSpace, Bounds, ClipboardEntry, ClipboardItem, Context,
82 DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusOutEvent, FocusableView, FontId,
83 FontWeight, Global, HighlightStyle, Hsla, InteractiveText, KeyContext, Model, ModelContext,
84 MouseButton, PaintQuad, ParentElement, Pixels, Render, SharedString, Size, Styled, StyledText,
85 Subscription, Task, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle,
86 UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext, WeakFocusHandle,
87 WeakView, WindowContext,
88};
89use highlight_matching_bracket::refresh_matching_bracket_highlights;
90use hover_popover::{hide_hover, HoverState};
91pub(crate) use hunk_diff::HoveredHunk;
92use hunk_diff::{diff_hunk_to_display, DiffMap, DiffMapSnapshot};
93use indent_guides::ActiveIndentGuidesState;
94use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
95pub use inline_completion::Direction;
96use inline_completion::{InlineCompletionProvider, InlineCompletionProviderHandle};
97pub use items::MAX_TAB_TITLE_LEN;
98use itertools::Itertools;
99use language::{
100 language_settings::{self, all_language_settings, language_settings, InlayHintSettings},
101 markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
102 CursorShape, Diagnostic, Documentation, IndentKind, IndentSize, Language, OffsetRangeExt,
103 Point, Selection, SelectionGoal, TransactionId,
104};
105use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
106use linked_editing_ranges::refresh_linked_ranges;
107use mouse_context_menu::MouseContextMenu;
108pub use proposed_changes_editor::{
109 ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar,
110};
111use similar::{ChangeTag, TextDiff};
112use std::iter::Peekable;
113use task::{ResolvedTask, TaskTemplate, TaskVariables};
114
115use hover_links::{find_file, HoverLink, HoveredLinkState, InlayHighlight};
116pub use lsp::CompletionContext;
117use lsp::{
118 CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity, InsertTextFormat,
119 LanguageServerId, LanguageServerName,
120};
121
122use movement::TextLayoutDetails;
123pub use multi_buffer::{
124 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
125 ToPoint,
126};
127use multi_buffer::{
128 ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow, ToOffsetUtf16,
129};
130use parking_lot::RwLock;
131use project::{
132 lsp_store::{FormatTarget, FormatTrigger, OpenLspBufferHandle},
133 project_settings::{GitGutterSetting, ProjectSettings},
134 CodeAction, Completion, CompletionIntent, DocumentHighlight, InlayHint, Location, LocationLink,
135 LspStore, Project, ProjectItem, ProjectTransaction, TaskSourceKind,
136};
137use rand::prelude::*;
138use rpc::{proto::*, ErrorExt};
139use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
140use selections_collection::{
141 resolve_selections, MutableSelectionsCollection, SelectionsCollection,
142};
143use serde::{Deserialize, Serialize};
144use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
145use smallvec::SmallVec;
146use snippet::Snippet;
147use std::{
148 any::TypeId,
149 borrow::Cow,
150 cell::RefCell,
151 cmp::{self, Ordering, Reverse},
152 mem,
153 num::NonZeroU32,
154 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
155 path::{Path, PathBuf},
156 rc::Rc,
157 sync::Arc,
158 time::{Duration, Instant},
159};
160pub use sum_tree::Bias;
161use sum_tree::TreeMap;
162use text::{BufferId, OffsetUtf16, Rope};
163use theme::{
164 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
165 ThemeColors, ThemeSettings,
166};
167use ui::{
168 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize,
169 PopoverMenuHandle, Tooltip,
170};
171use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
172use workspace::item::{ItemHandle, PreviewTabsSettings};
173use workspace::notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt};
174use workspace::{
175 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
176};
177use workspace::{Item as WorkspaceItem, OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
178
179use crate::hover_links::{find_url, find_url_from_range};
180use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
181
182pub const FILE_HEADER_HEIGHT: u32 = 2;
183pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
184pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u32 = 1;
185pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
186const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
187const MAX_LINE_LEN: usize = 1024;
188const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
189const MAX_SELECTION_HISTORY_LEN: usize = 1024;
190pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
191#[doc(hidden)]
192pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
193
194pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
195pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
196
197pub fn render_parsed_markdown(
198 element_id: impl Into<ElementId>,
199 parsed: &language::ParsedMarkdown,
200 editor_style: &EditorStyle,
201 workspace: Option<WeakView<Workspace>>,
202 cx: &mut WindowContext,
203) -> InteractiveText {
204 let code_span_background_color = cx
205 .theme()
206 .colors()
207 .editor_document_highlight_read_background;
208
209 let highlights = gpui::combine_highlights(
210 parsed.highlights.iter().filter_map(|(range, highlight)| {
211 let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
212 Some((range.clone(), highlight))
213 }),
214 parsed
215 .regions
216 .iter()
217 .zip(&parsed.region_ranges)
218 .filter_map(|(region, range)| {
219 if region.code {
220 Some((
221 range.clone(),
222 HighlightStyle {
223 background_color: Some(code_span_background_color),
224 ..Default::default()
225 },
226 ))
227 } else {
228 None
229 }
230 }),
231 );
232
233 let mut links = Vec::new();
234 let mut link_ranges = Vec::new();
235 for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
236 if let Some(link) = region.link.clone() {
237 links.push(link);
238 link_ranges.push(range.clone());
239 }
240 }
241
242 InteractiveText::new(
243 element_id,
244 StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
245 )
246 .on_click(link_ranges, move |clicked_range_ix, cx| {
247 match &links[clicked_range_ix] {
248 markdown::Link::Web { url } => cx.open_url(url),
249 markdown::Link::Path { path } => {
250 if let Some(workspace) = &workspace {
251 _ = workspace.update(cx, |workspace, cx| {
252 workspace.open_abs_path(path.clone(), false, cx).detach();
253 });
254 }
255 }
256 }
257 })
258}
259
260#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
261pub(crate) enum InlayId {
262 InlineCompletion(usize),
263 Hint(usize),
264}
265
266impl InlayId {
267 fn id(&self) -> usize {
268 match self {
269 Self::InlineCompletion(id) => *id,
270 Self::Hint(id) => *id,
271 }
272 }
273}
274
275enum DiffRowHighlight {}
276enum DocumentHighlightRead {}
277enum DocumentHighlightWrite {}
278enum InputComposition {}
279
280#[derive(Debug, Copy, Clone, PartialEq, Eq)]
281pub enum Navigated {
282 Yes,
283 No,
284}
285
286impl Navigated {
287 pub fn from_bool(yes: bool) -> Navigated {
288 if yes {
289 Navigated::Yes
290 } else {
291 Navigated::No
292 }
293 }
294}
295
296pub fn init_settings(cx: &mut AppContext) {
297 EditorSettings::register(cx);
298}
299
300pub fn init(cx: &mut AppContext) {
301 init_settings(cx);
302
303 workspace::register_project_item::<Editor>(cx);
304 workspace::FollowableViewRegistry::register::<Editor>(cx);
305 workspace::register_serializable_item::<Editor>(cx);
306
307 cx.observe_new_views(
308 |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
309 workspace.register_action(Editor::new_file);
310 workspace.register_action(Editor::new_file_vertical);
311 workspace.register_action(Editor::new_file_horizontal);
312 },
313 )
314 .detach();
315
316 cx.on_action(move |_: &workspace::NewFile, cx| {
317 let app_state = workspace::AppState::global(cx);
318 if let Some(app_state) = app_state.upgrade() {
319 workspace::open_new(Default::default(), app_state, cx, |workspace, cx| {
320 Editor::new_file(workspace, &Default::default(), cx)
321 })
322 .detach();
323 }
324 });
325 cx.on_action(move |_: &workspace::NewWindow, cx| {
326 let app_state = workspace::AppState::global(cx);
327 if let Some(app_state) = app_state.upgrade() {
328 workspace::open_new(Default::default(), app_state, cx, |workspace, cx| {
329 Editor::new_file(workspace, &Default::default(), cx)
330 })
331 .detach();
332 }
333 });
334 git::project_diff::init(cx);
335}
336
337pub struct SearchWithinRange;
338
339trait InvalidationRegion {
340 fn ranges(&self) -> &[Range<Anchor>];
341}
342
343#[derive(Clone, Debug, PartialEq)]
344pub enum SelectPhase {
345 Begin {
346 position: DisplayPoint,
347 add: bool,
348 click_count: usize,
349 },
350 BeginColumnar {
351 position: DisplayPoint,
352 reset: bool,
353 goal_column: u32,
354 },
355 Extend {
356 position: DisplayPoint,
357 click_count: usize,
358 },
359 Update {
360 position: DisplayPoint,
361 goal_column: u32,
362 scroll_delta: gpui::Point<f32>,
363 },
364 End,
365}
366
367#[derive(Clone, Debug)]
368pub enum SelectMode {
369 Character,
370 Word(Range<Anchor>),
371 Line(Range<Anchor>),
372 All,
373}
374
375#[derive(Copy, Clone, PartialEq, Eq, Debug)]
376pub enum EditorMode {
377 SingleLine { auto_width: bool },
378 AutoHeight { max_lines: usize },
379 Full,
380}
381
382#[derive(Copy, Clone, Debug)]
383pub enum SoftWrap {
384 /// Prefer not to wrap at all.
385 ///
386 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
387 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
388 GitDiff,
389 /// Prefer a single line generally, unless an overly long line is encountered.
390 None,
391 /// Soft wrap lines that exceed the editor width.
392 EditorWidth,
393 /// Soft wrap lines at the preferred line length.
394 Column(u32),
395 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
396 Bounded(u32),
397}
398
399#[derive(Clone)]
400pub struct EditorStyle {
401 pub background: Hsla,
402 pub local_player: PlayerColor,
403 pub text: TextStyle,
404 pub scrollbar_width: Pixels,
405 pub syntax: Arc<SyntaxTheme>,
406 pub status: StatusColors,
407 pub inlay_hints_style: HighlightStyle,
408 pub inline_completion_styles: InlineCompletionStyles,
409 pub unnecessary_code_fade: f32,
410}
411
412impl Default for EditorStyle {
413 fn default() -> Self {
414 Self {
415 background: Hsla::default(),
416 local_player: PlayerColor::default(),
417 text: TextStyle::default(),
418 scrollbar_width: Pixels::default(),
419 syntax: Default::default(),
420 // HACK: Status colors don't have a real default.
421 // We should look into removing the status colors from the editor
422 // style and retrieve them directly from the theme.
423 status: StatusColors::dark(),
424 inlay_hints_style: HighlightStyle::default(),
425 inline_completion_styles: InlineCompletionStyles {
426 insertion: HighlightStyle::default(),
427 whitespace: HighlightStyle::default(),
428 },
429 unnecessary_code_fade: Default::default(),
430 }
431 }
432}
433
434pub fn make_inlay_hints_style(cx: &WindowContext) -> HighlightStyle {
435 let show_background = language_settings::language_settings(None, None, cx)
436 .inlay_hints
437 .show_background;
438
439 HighlightStyle {
440 color: Some(cx.theme().status().hint),
441 background_color: show_background.then(|| cx.theme().status().hint_background),
442 ..HighlightStyle::default()
443 }
444}
445
446pub fn make_suggestion_styles(cx: &WindowContext) -> InlineCompletionStyles {
447 InlineCompletionStyles {
448 insertion: HighlightStyle {
449 color: Some(cx.theme().status().predictive),
450 ..HighlightStyle::default()
451 },
452 whitespace: HighlightStyle {
453 background_color: Some(cx.theme().status().created_background),
454 ..HighlightStyle::default()
455 },
456 }
457}
458
459type CompletionId = usize;
460
461enum InlineCompletion {
462 Edit(Vec<(Range<Anchor>, String)>),
463 Move(Anchor),
464}
465
466struct InlineCompletionState {
467 inlay_ids: Vec<InlayId>,
468 completion: InlineCompletion,
469 invalidation_range: Range<Anchor>,
470}
471
472enum InlineCompletionHighlight {}
473
474#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
475struct EditorActionId(usize);
476
477impl EditorActionId {
478 pub fn post_inc(&mut self) -> Self {
479 let answer = self.0;
480
481 *self = Self(answer + 1);
482
483 Self(answer)
484 }
485}
486
487// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
488// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
489
490type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
491type GutterHighlight = (fn(&AppContext) -> Hsla, Arc<[Range<Anchor>]>);
492
493#[derive(Default)]
494struct ScrollbarMarkerState {
495 scrollbar_size: Size<Pixels>,
496 dirty: bool,
497 markers: Arc<[PaintQuad]>,
498 pending_refresh: Option<Task<Result<()>>>,
499}
500
501impl ScrollbarMarkerState {
502 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
503 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
504 }
505}
506
507#[derive(Clone, Debug)]
508struct RunnableTasks {
509 templates: Vec<(TaskSourceKind, TaskTemplate)>,
510 offset: MultiBufferOffset,
511 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
512 column: u32,
513 // Values of all named captures, including those starting with '_'
514 extra_variables: HashMap<String, String>,
515 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
516 context_range: Range<BufferOffset>,
517}
518
519impl RunnableTasks {
520 fn resolve<'a>(
521 &'a self,
522 cx: &'a task::TaskContext,
523 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
524 self.templates.iter().filter_map(|(kind, template)| {
525 template
526 .resolve_task(&kind.to_id_base(), Default::default(), cx)
527 .map(|task| (kind.clone(), task))
528 })
529 }
530}
531
532#[derive(Clone)]
533struct ResolvedTasks {
534 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
535 position: Anchor,
536}
537#[derive(Copy, Clone, Debug)]
538struct MultiBufferOffset(usize);
539#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
540struct BufferOffset(usize);
541
542// Addons allow storing per-editor state in other crates (e.g. Vim)
543pub trait Addon: 'static {
544 fn extend_key_context(&self, _: &mut KeyContext, _: &AppContext) {}
545
546 fn to_any(&self) -> &dyn std::any::Any;
547}
548
549#[derive(Debug, Copy, Clone, PartialEq, Eq)]
550pub enum IsVimMode {
551 Yes,
552 No,
553}
554
555/// Zed's primary text input `View`, allowing users to edit a [`MultiBuffer`]
556///
557/// See the [module level documentation](self) for more information.
558pub struct Editor {
559 focus_handle: FocusHandle,
560 last_focused_descendant: Option<WeakFocusHandle>,
561 /// The text buffer being edited
562 buffer: Model<MultiBuffer>,
563 /// Map of how text in the buffer should be displayed.
564 /// Handles soft wraps, folds, fake inlay text insertions, etc.
565 pub display_map: Model<DisplayMap>,
566 pub selections: SelectionsCollection,
567 pub scroll_manager: ScrollManager,
568 /// When inline assist editors are linked, they all render cursors because
569 /// typing enters text into each of them, even the ones that aren't focused.
570 pub(crate) show_cursor_when_unfocused: bool,
571 columnar_selection_tail: Option<Anchor>,
572 add_selections_state: Option<AddSelectionsState>,
573 select_next_state: Option<SelectNextState>,
574 select_prev_state: Option<SelectNextState>,
575 selection_history: SelectionHistory,
576 autoclose_regions: Vec<AutocloseRegion>,
577 snippet_stack: InvalidationStack<SnippetState>,
578 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
579 ime_transaction: Option<TransactionId>,
580 active_diagnostics: Option<ActiveDiagnosticGroup>,
581 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
582
583 project: Option<Model<Project>>,
584 semantics_provider: Option<Rc<dyn SemanticsProvider>>,
585 completion_provider: Option<Box<dyn CompletionProvider>>,
586 collaboration_hub: Option<Box<dyn CollaborationHub>>,
587 blink_manager: Model<BlinkManager>,
588 show_cursor_names: bool,
589 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
590 pub show_local_selections: bool,
591 mode: EditorMode,
592 show_breadcrumbs: bool,
593 show_gutter: bool,
594 show_line_numbers: Option<bool>,
595 use_relative_line_numbers: Option<bool>,
596 show_git_diff_gutter: Option<bool>,
597 show_code_actions: Option<bool>,
598 show_runnables: Option<bool>,
599 show_wrap_guides: Option<bool>,
600 show_indent_guides: Option<bool>,
601 placeholder_text: Option<Arc<str>>,
602 highlight_order: usize,
603 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
604 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
605 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
606 scrollbar_marker_state: ScrollbarMarkerState,
607 active_indent_guides_state: ActiveIndentGuidesState,
608 nav_history: Option<ItemNavHistory>,
609 context_menu: RwLock<Option<CodeContextMenu>>,
610 mouse_context_menu: Option<MouseContextMenu>,
611 hunk_controls_menu_handle: PopoverMenuHandle<ui::ContextMenu>,
612 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
613 signature_help_state: SignatureHelpState,
614 auto_signature_help: Option<bool>,
615 find_all_references_task_sources: Vec<Anchor>,
616 next_completion_id: CompletionId,
617 available_code_actions: Option<(Location, Arc<[AvailableCodeAction]>)>,
618 code_actions_task: Option<Task<Result<()>>>,
619 document_highlights_task: Option<Task<()>>,
620 linked_editing_range_task: Option<Task<Option<()>>>,
621 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
622 pending_rename: Option<RenameState>,
623 searchable: bool,
624 cursor_shape: CursorShape,
625 current_line_highlight: Option<CurrentLineHighlight>,
626 collapse_matches: bool,
627 autoindent_mode: Option<AutoindentMode>,
628 workspace: Option<(WeakView<Workspace>, Option<WorkspaceId>)>,
629 input_enabled: bool,
630 use_modal_editing: bool,
631 read_only: bool,
632 leader_peer_id: Option<PeerId>,
633 remote_id: Option<ViewId>,
634 hover_state: HoverState,
635 gutter_hovered: bool,
636 hovered_link_state: Option<HoveredLinkState>,
637 inline_completion_provider: Option<RegisteredInlineCompletionProvider>,
638 code_action_providers: Vec<Arc<dyn CodeActionProvider>>,
639 active_inline_completion: Option<InlineCompletionState>,
640 // enable_inline_completions is a switch that Vim can use to disable
641 // inline completions based on its mode.
642 enable_inline_completions: bool,
643 show_inline_completions_override: Option<bool>,
644 inlay_hint_cache: InlayHintCache,
645 diff_map: DiffMap,
646 next_inlay_id: usize,
647 _subscriptions: Vec<Subscription>,
648 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
649 gutter_dimensions: GutterDimensions,
650 style: Option<EditorStyle>,
651 text_style_refinement: Option<TextStyleRefinement>,
652 next_editor_action_id: EditorActionId,
653 editor_actions: Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut ViewContext<Self>)>>>>,
654 use_autoclose: bool,
655 use_auto_surround: bool,
656 auto_replace_emoji_shortcode: bool,
657 show_git_blame_gutter: bool,
658 show_git_blame_inline: bool,
659 show_git_blame_inline_delay_task: Option<Task<()>>,
660 git_blame_inline_enabled: bool,
661 serialize_dirty_buffers: bool,
662 show_selection_menu: Option<bool>,
663 blame: Option<Model<GitBlame>>,
664 blame_subscription: Option<Subscription>,
665 custom_context_menu: Option<
666 Box<
667 dyn 'static
668 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
669 >,
670 >,
671 last_bounds: Option<Bounds<Pixels>>,
672 expect_bounds_change: Option<Bounds<Pixels>>,
673 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
674 tasks_update_task: Option<Task<()>>,
675 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
676 breadcrumb_header: Option<String>,
677 focused_block: Option<FocusedBlock>,
678 next_scroll_position: NextScrollCursorCenterTopBottom,
679 addons: HashMap<TypeId, Box<dyn Addon>>,
680 registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
681 _scroll_cursor_center_top_bottom_task: Task<()>,
682}
683
684#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
685enum NextScrollCursorCenterTopBottom {
686 #[default]
687 Center,
688 Top,
689 Bottom,
690}
691
692impl NextScrollCursorCenterTopBottom {
693 fn next(&self) -> Self {
694 match self {
695 Self::Center => Self::Top,
696 Self::Top => Self::Bottom,
697 Self::Bottom => Self::Center,
698 }
699 }
700}
701
702#[derive(Clone)]
703pub struct EditorSnapshot {
704 pub mode: EditorMode,
705 show_gutter: bool,
706 show_line_numbers: Option<bool>,
707 show_git_diff_gutter: Option<bool>,
708 show_code_actions: Option<bool>,
709 show_runnables: Option<bool>,
710 git_blame_gutter_max_author_length: Option<usize>,
711 pub display_snapshot: DisplaySnapshot,
712 pub placeholder_text: Option<Arc<str>>,
713 diff_map: DiffMapSnapshot,
714 is_focused: bool,
715 scroll_anchor: ScrollAnchor,
716 ongoing_scroll: OngoingScroll,
717 current_line_highlight: CurrentLineHighlight,
718 gutter_hovered: bool,
719}
720
721const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
722
723#[derive(Default, Debug, Clone, Copy)]
724pub struct GutterDimensions {
725 pub left_padding: Pixels,
726 pub right_padding: Pixels,
727 pub width: Pixels,
728 pub margin: Pixels,
729 pub git_blame_entries_width: Option<Pixels>,
730}
731
732impl GutterDimensions {
733 /// The full width of the space taken up by the gutter.
734 pub fn full_width(&self) -> Pixels {
735 self.margin + self.width
736 }
737
738 /// The width of the space reserved for the fold indicators,
739 /// use alongside 'justify_end' and `gutter_width` to
740 /// right align content with the line numbers
741 pub fn fold_area_width(&self) -> Pixels {
742 self.margin + self.right_padding
743 }
744}
745
746#[derive(Debug)]
747pub struct RemoteSelection {
748 pub replica_id: ReplicaId,
749 pub selection: Selection<Anchor>,
750 pub cursor_shape: CursorShape,
751 pub peer_id: PeerId,
752 pub line_mode: bool,
753 pub participant_index: Option<ParticipantIndex>,
754 pub user_name: Option<SharedString>,
755}
756
757#[derive(Clone, Debug)]
758struct SelectionHistoryEntry {
759 selections: Arc<[Selection<Anchor>]>,
760 select_next_state: Option<SelectNextState>,
761 select_prev_state: Option<SelectNextState>,
762 add_selections_state: Option<AddSelectionsState>,
763}
764
765enum SelectionHistoryMode {
766 Normal,
767 Undoing,
768 Redoing,
769}
770
771#[derive(Clone, PartialEq, Eq, Hash)]
772struct HoveredCursor {
773 replica_id: u16,
774 selection_id: usize,
775}
776
777impl Default for SelectionHistoryMode {
778 fn default() -> Self {
779 Self::Normal
780 }
781}
782
783#[derive(Default)]
784struct SelectionHistory {
785 #[allow(clippy::type_complexity)]
786 selections_by_transaction:
787 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
788 mode: SelectionHistoryMode,
789 undo_stack: VecDeque<SelectionHistoryEntry>,
790 redo_stack: VecDeque<SelectionHistoryEntry>,
791}
792
793impl SelectionHistory {
794 fn insert_transaction(
795 &mut self,
796 transaction_id: TransactionId,
797 selections: Arc<[Selection<Anchor>]>,
798 ) {
799 self.selections_by_transaction
800 .insert(transaction_id, (selections, None));
801 }
802
803 #[allow(clippy::type_complexity)]
804 fn transaction(
805 &self,
806 transaction_id: TransactionId,
807 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
808 self.selections_by_transaction.get(&transaction_id)
809 }
810
811 #[allow(clippy::type_complexity)]
812 fn transaction_mut(
813 &mut self,
814 transaction_id: TransactionId,
815 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
816 self.selections_by_transaction.get_mut(&transaction_id)
817 }
818
819 fn push(&mut self, entry: SelectionHistoryEntry) {
820 if !entry.selections.is_empty() {
821 match self.mode {
822 SelectionHistoryMode::Normal => {
823 self.push_undo(entry);
824 self.redo_stack.clear();
825 }
826 SelectionHistoryMode::Undoing => self.push_redo(entry),
827 SelectionHistoryMode::Redoing => self.push_undo(entry),
828 }
829 }
830 }
831
832 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
833 if self
834 .undo_stack
835 .back()
836 .map_or(true, |e| e.selections != entry.selections)
837 {
838 self.undo_stack.push_back(entry);
839 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
840 self.undo_stack.pop_front();
841 }
842 }
843 }
844
845 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
846 if self
847 .redo_stack
848 .back()
849 .map_or(true, |e| e.selections != entry.selections)
850 {
851 self.redo_stack.push_back(entry);
852 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
853 self.redo_stack.pop_front();
854 }
855 }
856 }
857}
858
859struct RowHighlight {
860 index: usize,
861 range: Range<Anchor>,
862 color: Hsla,
863 should_autoscroll: bool,
864}
865
866#[derive(Clone, Debug)]
867struct AddSelectionsState {
868 above: bool,
869 stack: Vec<usize>,
870}
871
872#[derive(Clone)]
873struct SelectNextState {
874 query: AhoCorasick,
875 wordwise: bool,
876 done: bool,
877}
878
879impl std::fmt::Debug for SelectNextState {
880 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
881 f.debug_struct(std::any::type_name::<Self>())
882 .field("wordwise", &self.wordwise)
883 .field("done", &self.done)
884 .finish()
885 }
886}
887
888#[derive(Debug)]
889struct AutocloseRegion {
890 selection_id: usize,
891 range: Range<Anchor>,
892 pair: BracketPair,
893}
894
895#[derive(Debug)]
896struct SnippetState {
897 ranges: Vec<Vec<Range<Anchor>>>,
898 active_index: usize,
899 choices: Vec<Option<Vec<String>>>,
900}
901
902#[doc(hidden)]
903pub struct RenameState {
904 pub range: Range<Anchor>,
905 pub old_name: Arc<str>,
906 pub editor: View<Editor>,
907 block_id: CustomBlockId,
908}
909
910struct InvalidationStack<T>(Vec<T>);
911
912struct RegisteredInlineCompletionProvider {
913 provider: Arc<dyn InlineCompletionProviderHandle>,
914 _subscription: Subscription,
915}
916
917#[derive(Debug)]
918struct ActiveDiagnosticGroup {
919 primary_range: Range<Anchor>,
920 primary_message: String,
921 group_id: usize,
922 blocks: HashMap<CustomBlockId, Diagnostic>,
923 is_valid: bool,
924}
925
926#[derive(Serialize, Deserialize, Clone, Debug)]
927pub struct ClipboardSelection {
928 pub len: usize,
929 pub is_entire_line: bool,
930 pub first_line_indent: u32,
931}
932
933#[derive(Debug)]
934pub(crate) struct NavigationData {
935 cursor_anchor: Anchor,
936 cursor_position: Point,
937 scroll_anchor: ScrollAnchor,
938 scroll_top_row: u32,
939}
940
941#[derive(Debug, Clone, Copy, PartialEq, Eq)]
942pub enum GotoDefinitionKind {
943 Symbol,
944 Declaration,
945 Type,
946 Implementation,
947}
948
949#[derive(Debug, Clone)]
950enum InlayHintRefreshReason {
951 Toggle(bool),
952 SettingsChange(InlayHintSettings),
953 NewLinesShown,
954 BufferEdited(HashSet<Arc<Language>>),
955 RefreshRequested,
956 ExcerptsRemoved(Vec<ExcerptId>),
957}
958
959impl InlayHintRefreshReason {
960 fn description(&self) -> &'static str {
961 match self {
962 Self::Toggle(_) => "toggle",
963 Self::SettingsChange(_) => "settings change",
964 Self::NewLinesShown => "new lines shown",
965 Self::BufferEdited(_) => "buffer edited",
966 Self::RefreshRequested => "refresh requested",
967 Self::ExcerptsRemoved(_) => "excerpts removed",
968 }
969 }
970}
971
972pub(crate) struct FocusedBlock {
973 id: BlockId,
974 focus_handle: WeakFocusHandle,
975}
976
977#[derive(Clone)]
978struct JumpData {
979 excerpt_id: ExcerptId,
980 position: Point,
981 anchor: text::Anchor,
982 path: Option<project::ProjectPath>,
983 line_offset_from_top: u32,
984}
985
986impl Editor {
987 pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
988 let buffer = cx.new_model(|cx| Buffer::local("", cx));
989 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
990 Self::new(
991 EditorMode::SingleLine { auto_width: false },
992 buffer,
993 None,
994 false,
995 cx,
996 )
997 }
998
999 pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
1000 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1001 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1002 Self::new(EditorMode::Full, buffer, None, false, cx)
1003 }
1004
1005 pub fn auto_width(cx: &mut ViewContext<Self>) -> Self {
1006 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1007 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1008 Self::new(
1009 EditorMode::SingleLine { auto_width: true },
1010 buffer,
1011 None,
1012 false,
1013 cx,
1014 )
1015 }
1016
1017 pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
1018 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1019 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1020 Self::new(
1021 EditorMode::AutoHeight { max_lines },
1022 buffer,
1023 None,
1024 false,
1025 cx,
1026 )
1027 }
1028
1029 pub fn for_buffer(
1030 buffer: Model<Buffer>,
1031 project: Option<Model<Project>>,
1032 cx: &mut ViewContext<Self>,
1033 ) -> Self {
1034 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1035 Self::new(EditorMode::Full, buffer, project, false, cx)
1036 }
1037
1038 pub fn for_multibuffer(
1039 buffer: Model<MultiBuffer>,
1040 project: Option<Model<Project>>,
1041 show_excerpt_controls: bool,
1042 cx: &mut ViewContext<Self>,
1043 ) -> Self {
1044 Self::new(EditorMode::Full, buffer, project, show_excerpt_controls, cx)
1045 }
1046
1047 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1048 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1049 let mut clone = Self::new(
1050 self.mode,
1051 self.buffer.clone(),
1052 self.project.clone(),
1053 show_excerpt_controls,
1054 cx,
1055 );
1056 self.display_map.update(cx, |display_map, cx| {
1057 let snapshot = display_map.snapshot(cx);
1058 clone.display_map.update(cx, |display_map, cx| {
1059 display_map.set_state(&snapshot, cx);
1060 });
1061 });
1062 clone.selections.clone_state(&self.selections);
1063 clone.scroll_manager.clone_state(&self.scroll_manager);
1064 clone.searchable = self.searchable;
1065 clone
1066 }
1067
1068 pub fn new(
1069 mode: EditorMode,
1070 buffer: Model<MultiBuffer>,
1071 project: Option<Model<Project>>,
1072 show_excerpt_controls: bool,
1073 cx: &mut ViewContext<Self>,
1074 ) -> Self {
1075 let style = cx.text_style();
1076 let font_size = style.font_size.to_pixels(cx.rem_size());
1077 let editor = cx.view().downgrade();
1078 let fold_placeholder = FoldPlaceholder {
1079 constrain_width: true,
1080 render: Arc::new(move |fold_id, fold_range, cx| {
1081 let editor = editor.clone();
1082 div()
1083 .id(fold_id)
1084 .bg(cx.theme().colors().ghost_element_background)
1085 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1086 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1087 .rounded_sm()
1088 .size_full()
1089 .cursor_pointer()
1090 .child("⋯")
1091 .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
1092 .on_click(move |_, cx| {
1093 editor
1094 .update(cx, |editor, cx| {
1095 editor.unfold_ranges(
1096 &[fold_range.start..fold_range.end],
1097 true,
1098 false,
1099 cx,
1100 );
1101 cx.stop_propagation();
1102 })
1103 .ok();
1104 })
1105 .into_any()
1106 }),
1107 merge_adjacent: true,
1108 ..Default::default()
1109 };
1110 let display_map = cx.new_model(|cx| {
1111 DisplayMap::new(
1112 buffer.clone(),
1113 style.font(),
1114 font_size,
1115 None,
1116 show_excerpt_controls,
1117 FILE_HEADER_HEIGHT,
1118 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1119 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1120 fold_placeholder,
1121 cx,
1122 )
1123 });
1124
1125 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1126
1127 let blink_manager = cx.new_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1128
1129 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1130 .then(|| language_settings::SoftWrap::None);
1131
1132 let mut project_subscriptions = Vec::new();
1133 if mode == EditorMode::Full {
1134 if let Some(project) = project.as_ref() {
1135 if buffer.read(cx).is_singleton() {
1136 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1137 cx.emit(EditorEvent::TitleChanged);
1138 }));
1139 }
1140 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1141 if let project::Event::RefreshInlayHints = event {
1142 editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1143 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1144 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1145 let focus_handle = editor.focus_handle(cx);
1146 if focus_handle.is_focused(cx) {
1147 let snapshot = buffer.read(cx).snapshot();
1148 for (range, snippet) in snippet_edits {
1149 let editor_range =
1150 language::range_from_lsp(*range).to_offset(&snapshot);
1151 editor
1152 .insert_snippet(&[editor_range], snippet.clone(), cx)
1153 .ok();
1154 }
1155 }
1156 }
1157 }
1158 }));
1159 if let Some(task_inventory) = project
1160 .read(cx)
1161 .task_store()
1162 .read(cx)
1163 .task_inventory()
1164 .cloned()
1165 {
1166 project_subscriptions.push(cx.observe(&task_inventory, |editor, _, cx| {
1167 editor.tasks_update_task = Some(editor.refresh_runnables(cx));
1168 }));
1169 }
1170 }
1171 }
1172
1173 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1174
1175 let inlay_hint_settings =
1176 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1177 let focus_handle = cx.focus_handle();
1178 cx.on_focus(&focus_handle, Self::handle_focus).detach();
1179 cx.on_focus_in(&focus_handle, Self::handle_focus_in)
1180 .detach();
1181 cx.on_focus_out(&focus_handle, Self::handle_focus_out)
1182 .detach();
1183 cx.on_blur(&focus_handle, Self::handle_blur).detach();
1184
1185 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1186 Some(false)
1187 } else {
1188 None
1189 };
1190
1191 let mut code_action_providers = Vec::new();
1192 if let Some(project) = project.clone() {
1193 get_unstaged_changes_for_buffers(&project, buffer.read(cx).all_buffers(), cx);
1194 code_action_providers.push(Arc::new(project) as Arc<_>);
1195 }
1196
1197 let mut this = Self {
1198 focus_handle,
1199 show_cursor_when_unfocused: false,
1200 last_focused_descendant: None,
1201 buffer: buffer.clone(),
1202 display_map: display_map.clone(),
1203 selections,
1204 scroll_manager: ScrollManager::new(cx),
1205 columnar_selection_tail: None,
1206 add_selections_state: None,
1207 select_next_state: None,
1208 select_prev_state: None,
1209 selection_history: Default::default(),
1210 autoclose_regions: Default::default(),
1211 snippet_stack: Default::default(),
1212 select_larger_syntax_node_stack: Vec::new(),
1213 ime_transaction: Default::default(),
1214 active_diagnostics: None,
1215 soft_wrap_mode_override,
1216 completion_provider: project.clone().map(|project| Box::new(project) as _),
1217 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
1218 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1219 project,
1220 blink_manager: blink_manager.clone(),
1221 show_local_selections: true,
1222 mode,
1223 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1224 show_gutter: mode == EditorMode::Full,
1225 show_line_numbers: None,
1226 use_relative_line_numbers: None,
1227 show_git_diff_gutter: None,
1228 show_code_actions: None,
1229 show_runnables: None,
1230 show_wrap_guides: None,
1231 show_indent_guides,
1232 placeholder_text: None,
1233 highlight_order: 0,
1234 highlighted_rows: HashMap::default(),
1235 background_highlights: Default::default(),
1236 gutter_highlights: TreeMap::default(),
1237 scrollbar_marker_state: ScrollbarMarkerState::default(),
1238 active_indent_guides_state: ActiveIndentGuidesState::default(),
1239 nav_history: None,
1240 context_menu: RwLock::new(None),
1241 mouse_context_menu: None,
1242 hunk_controls_menu_handle: PopoverMenuHandle::default(),
1243 completion_tasks: Default::default(),
1244 signature_help_state: SignatureHelpState::default(),
1245 auto_signature_help: None,
1246 find_all_references_task_sources: Vec::new(),
1247 next_completion_id: 0,
1248 next_inlay_id: 0,
1249 code_action_providers,
1250 available_code_actions: Default::default(),
1251 code_actions_task: Default::default(),
1252 document_highlights_task: Default::default(),
1253 linked_editing_range_task: Default::default(),
1254 pending_rename: Default::default(),
1255 searchable: true,
1256 cursor_shape: EditorSettings::get_global(cx)
1257 .cursor_shape
1258 .unwrap_or_default(),
1259 current_line_highlight: None,
1260 autoindent_mode: Some(AutoindentMode::EachLine),
1261 collapse_matches: false,
1262 workspace: None,
1263 input_enabled: true,
1264 use_modal_editing: mode == EditorMode::Full,
1265 read_only: false,
1266 use_autoclose: true,
1267 use_auto_surround: true,
1268 auto_replace_emoji_shortcode: false,
1269 leader_peer_id: None,
1270 remote_id: None,
1271 hover_state: Default::default(),
1272 hovered_link_state: Default::default(),
1273 inline_completion_provider: None,
1274 active_inline_completion: None,
1275 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1276 diff_map: DiffMap::default(),
1277 gutter_hovered: false,
1278 pixel_position_of_newest_cursor: None,
1279 last_bounds: None,
1280 expect_bounds_change: None,
1281 gutter_dimensions: GutterDimensions::default(),
1282 style: None,
1283 show_cursor_names: false,
1284 hovered_cursors: Default::default(),
1285 next_editor_action_id: EditorActionId::default(),
1286 editor_actions: Rc::default(),
1287 show_inline_completions_override: None,
1288 enable_inline_completions: true,
1289 custom_context_menu: None,
1290 show_git_blame_gutter: false,
1291 show_git_blame_inline: false,
1292 show_selection_menu: None,
1293 show_git_blame_inline_delay_task: None,
1294 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1295 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1296 .session
1297 .restore_unsaved_buffers,
1298 blame: None,
1299 blame_subscription: None,
1300 tasks: Default::default(),
1301 _subscriptions: vec![
1302 cx.observe(&buffer, Self::on_buffer_changed),
1303 cx.subscribe(&buffer, Self::on_buffer_event),
1304 cx.observe(&display_map, Self::on_display_map_changed),
1305 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1306 cx.observe_global::<SettingsStore>(Self::settings_changed),
1307 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1308 cx.observe_window_activation(|editor, cx| {
1309 let active = cx.is_window_active();
1310 editor.blink_manager.update(cx, |blink_manager, cx| {
1311 if active {
1312 blink_manager.enable(cx);
1313 } else {
1314 blink_manager.disable(cx);
1315 }
1316 });
1317 }),
1318 ],
1319 tasks_update_task: None,
1320 linked_edit_ranges: Default::default(),
1321 previous_search_ranges: None,
1322 breadcrumb_header: None,
1323 focused_block: None,
1324 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1325 addons: HashMap::default(),
1326 registered_buffers: HashMap::default(),
1327 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1328 text_style_refinement: None,
1329 };
1330 this.tasks_update_task = Some(this.refresh_runnables(cx));
1331 this._subscriptions.extend(project_subscriptions);
1332
1333 this.end_selection(cx);
1334 this.scroll_manager.show_scrollbar(cx);
1335
1336 if mode == EditorMode::Full {
1337 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1338 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1339
1340 if this.git_blame_inline_enabled {
1341 this.git_blame_inline_enabled = true;
1342 this.start_git_blame_inline(false, cx);
1343 }
1344
1345 if let Some(buffer) = buffer.read(cx).as_singleton() {
1346 if let Some(project) = this.project.as_ref() {
1347 let lsp_store = project.read(cx).lsp_store();
1348 let handle = lsp_store.update(cx, |lsp_store, cx| {
1349 lsp_store.register_buffer_with_language_servers(&buffer, cx)
1350 });
1351 this.registered_buffers
1352 .insert(buffer.read(cx).remote_id(), handle);
1353 }
1354 }
1355 }
1356
1357 this.report_editor_event("open", None, cx);
1358 this
1359 }
1360
1361 pub fn mouse_menu_is_focused(&self, cx: &WindowContext) -> bool {
1362 self.mouse_context_menu
1363 .as_ref()
1364 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(cx))
1365 }
1366
1367 fn key_context(&self, cx: &ViewContext<Self>) -> KeyContext {
1368 let mut key_context = KeyContext::new_with_defaults();
1369 key_context.add("Editor");
1370 let mode = match self.mode {
1371 EditorMode::SingleLine { .. } => "single_line",
1372 EditorMode::AutoHeight { .. } => "auto_height",
1373 EditorMode::Full => "full",
1374 };
1375
1376 if EditorSettings::jupyter_enabled(cx) {
1377 key_context.add("jupyter");
1378 }
1379
1380 key_context.set("mode", mode);
1381 if self.pending_rename.is_some() {
1382 key_context.add("renaming");
1383 }
1384 if self.context_menu_visible() {
1385 match self.context_menu.read().as_ref() {
1386 Some(CodeContextMenu::Completions(_)) => {
1387 key_context.add("menu");
1388 key_context.add("showing_completions")
1389 }
1390 Some(CodeContextMenu::CodeActions(_)) => {
1391 key_context.add("menu");
1392 key_context.add("showing_code_actions")
1393 }
1394 None => {}
1395 }
1396 }
1397
1398 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
1399 if !self.focus_handle(cx).contains_focused(cx)
1400 || (self.is_focused(cx) || self.mouse_menu_is_focused(cx))
1401 {
1402 for addon in self.addons.values() {
1403 addon.extend_key_context(&mut key_context, cx)
1404 }
1405 }
1406
1407 if let Some(extension) = self
1408 .buffer
1409 .read(cx)
1410 .as_singleton()
1411 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
1412 {
1413 key_context.set("extension", extension.to_string());
1414 }
1415
1416 if self.has_active_inline_completion() {
1417 key_context.add("copilot_suggestion");
1418 key_context.add("inline_completion");
1419 }
1420
1421 if !self
1422 .selections
1423 .disjoint
1424 .iter()
1425 .all(|selection| selection.start == selection.end)
1426 {
1427 key_context.add("selection");
1428 }
1429
1430 key_context
1431 }
1432
1433 pub fn new_file(
1434 workspace: &mut Workspace,
1435 _: &workspace::NewFile,
1436 cx: &mut ViewContext<Workspace>,
1437 ) {
1438 Self::new_in_workspace(workspace, cx).detach_and_prompt_err(
1439 "Failed to create buffer",
1440 cx,
1441 |e, _| match e.error_code() {
1442 ErrorCode::RemoteUpgradeRequired => Some(format!(
1443 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1444 e.error_tag("required").unwrap_or("the latest version")
1445 )),
1446 _ => None,
1447 },
1448 );
1449 }
1450
1451 pub fn new_in_workspace(
1452 workspace: &mut Workspace,
1453 cx: &mut ViewContext<Workspace>,
1454 ) -> Task<Result<View<Editor>>> {
1455 let project = workspace.project().clone();
1456 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1457
1458 cx.spawn(|workspace, mut cx| async move {
1459 let buffer = create.await?;
1460 workspace.update(&mut cx, |workspace, cx| {
1461 let editor =
1462 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx));
1463 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
1464 editor
1465 })
1466 })
1467 }
1468
1469 fn new_file_vertical(
1470 workspace: &mut Workspace,
1471 _: &workspace::NewFileSplitVertical,
1472 cx: &mut ViewContext<Workspace>,
1473 ) {
1474 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), cx)
1475 }
1476
1477 fn new_file_horizontal(
1478 workspace: &mut Workspace,
1479 _: &workspace::NewFileSplitHorizontal,
1480 cx: &mut ViewContext<Workspace>,
1481 ) {
1482 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), cx)
1483 }
1484
1485 fn new_file_in_direction(
1486 workspace: &mut Workspace,
1487 direction: SplitDirection,
1488 cx: &mut ViewContext<Workspace>,
1489 ) {
1490 let project = workspace.project().clone();
1491 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1492
1493 cx.spawn(|workspace, mut cx| async move {
1494 let buffer = create.await?;
1495 workspace.update(&mut cx, move |workspace, cx| {
1496 workspace.split_item(
1497 direction,
1498 Box::new(
1499 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)),
1500 ),
1501 cx,
1502 )
1503 })?;
1504 anyhow::Ok(())
1505 })
1506 .detach_and_prompt_err("Failed to create buffer", cx, |e, _| match e.error_code() {
1507 ErrorCode::RemoteUpgradeRequired => Some(format!(
1508 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1509 e.error_tag("required").unwrap_or("the latest version")
1510 )),
1511 _ => None,
1512 });
1513 }
1514
1515 pub fn leader_peer_id(&self) -> Option<PeerId> {
1516 self.leader_peer_id
1517 }
1518
1519 pub fn buffer(&self) -> &Model<MultiBuffer> {
1520 &self.buffer
1521 }
1522
1523 pub fn workspace(&self) -> Option<View<Workspace>> {
1524 self.workspace.as_ref()?.0.upgrade()
1525 }
1526
1527 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
1528 self.buffer().read(cx).title(cx)
1529 }
1530
1531 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
1532 let git_blame_gutter_max_author_length = self
1533 .render_git_blame_gutter(cx)
1534 .then(|| {
1535 if let Some(blame) = self.blame.as_ref() {
1536 let max_author_length =
1537 blame.update(cx, |blame, cx| blame.max_author_length(cx));
1538 Some(max_author_length)
1539 } else {
1540 None
1541 }
1542 })
1543 .flatten();
1544
1545 EditorSnapshot {
1546 mode: self.mode,
1547 show_gutter: self.show_gutter,
1548 show_line_numbers: self.show_line_numbers,
1549 show_git_diff_gutter: self.show_git_diff_gutter,
1550 show_code_actions: self.show_code_actions,
1551 show_runnables: self.show_runnables,
1552 git_blame_gutter_max_author_length,
1553 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1554 scroll_anchor: self.scroll_manager.anchor(),
1555 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1556 placeholder_text: self.placeholder_text.clone(),
1557 diff_map: self.diff_map.snapshot(),
1558 is_focused: self.focus_handle.is_focused(cx),
1559 current_line_highlight: self
1560 .current_line_highlight
1561 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
1562 gutter_hovered: self.gutter_hovered,
1563 }
1564 }
1565
1566 pub fn language_at<T: ToOffset>(&self, point: T, cx: &AppContext) -> Option<Arc<Language>> {
1567 self.buffer.read(cx).language_at(point, cx)
1568 }
1569
1570 pub fn file_at<T: ToOffset>(
1571 &self,
1572 point: T,
1573 cx: &AppContext,
1574 ) -> Option<Arc<dyn language::File>> {
1575 self.buffer.read(cx).read(cx).file_at(point).cloned()
1576 }
1577
1578 pub fn active_excerpt(
1579 &self,
1580 cx: &AppContext,
1581 ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
1582 self.buffer
1583 .read(cx)
1584 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1585 }
1586
1587 pub fn mode(&self) -> EditorMode {
1588 self.mode
1589 }
1590
1591 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1592 self.collaboration_hub.as_deref()
1593 }
1594
1595 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1596 self.collaboration_hub = Some(hub);
1597 }
1598
1599 pub fn set_custom_context_menu(
1600 &mut self,
1601 f: impl 'static
1602 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
1603 ) {
1604 self.custom_context_menu = Some(Box::new(f))
1605 }
1606
1607 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
1608 self.completion_provider = provider;
1609 }
1610
1611 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
1612 self.semantics_provider.clone()
1613 }
1614
1615 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
1616 self.semantics_provider = provider;
1617 }
1618
1619 pub fn set_inline_completion_provider<T>(
1620 &mut self,
1621 provider: Option<Model<T>>,
1622 cx: &mut ViewContext<Self>,
1623 ) where
1624 T: InlineCompletionProvider,
1625 {
1626 self.inline_completion_provider =
1627 provider.map(|provider| RegisteredInlineCompletionProvider {
1628 _subscription: cx.observe(&provider, |this, _, cx| {
1629 if this.focus_handle.is_focused(cx) {
1630 this.update_visible_inline_completion(cx);
1631 }
1632 }),
1633 provider: Arc::new(provider),
1634 });
1635 self.refresh_inline_completion(false, false, cx);
1636 }
1637
1638 pub fn placeholder_text(&self, _cx: &WindowContext) -> Option<&str> {
1639 self.placeholder_text.as_deref()
1640 }
1641
1642 pub fn set_placeholder_text(
1643 &mut self,
1644 placeholder_text: impl Into<Arc<str>>,
1645 cx: &mut ViewContext<Self>,
1646 ) {
1647 let placeholder_text = Some(placeholder_text.into());
1648 if self.placeholder_text != placeholder_text {
1649 self.placeholder_text = placeholder_text;
1650 cx.notify();
1651 }
1652 }
1653
1654 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
1655 self.cursor_shape = cursor_shape;
1656
1657 // Disrupt blink for immediate user feedback that the cursor shape has changed
1658 self.blink_manager.update(cx, BlinkManager::show_cursor);
1659
1660 cx.notify();
1661 }
1662
1663 pub fn set_current_line_highlight(
1664 &mut self,
1665 current_line_highlight: Option<CurrentLineHighlight>,
1666 ) {
1667 self.current_line_highlight = current_line_highlight;
1668 }
1669
1670 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1671 self.collapse_matches = collapse_matches;
1672 }
1673
1674 pub fn register_buffers_with_language_servers(&mut self, cx: &mut ViewContext<Self>) {
1675 let buffers = self.buffer.read(cx).all_buffers();
1676 let Some(lsp_store) = self.lsp_store(cx) else {
1677 return;
1678 };
1679 lsp_store.update(cx, |lsp_store, cx| {
1680 for buffer in buffers {
1681 self.registered_buffers
1682 .entry(buffer.read(cx).remote_id())
1683 .or_insert_with(|| {
1684 lsp_store.register_buffer_with_language_servers(&buffer, cx)
1685 });
1686 }
1687 })
1688 }
1689
1690 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1691 if self.collapse_matches {
1692 return range.start..range.start;
1693 }
1694 range.clone()
1695 }
1696
1697 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
1698 if self.display_map.read(cx).clip_at_line_ends != clip {
1699 self.display_map
1700 .update(cx, |map, _| map.clip_at_line_ends = clip);
1701 }
1702 }
1703
1704 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1705 self.input_enabled = input_enabled;
1706 }
1707
1708 pub fn set_inline_completions_enabled(&mut self, enabled: bool) {
1709 self.enable_inline_completions = enabled;
1710 }
1711
1712 pub fn set_autoindent(&mut self, autoindent: bool) {
1713 if autoindent {
1714 self.autoindent_mode = Some(AutoindentMode::EachLine);
1715 } else {
1716 self.autoindent_mode = None;
1717 }
1718 }
1719
1720 pub fn read_only(&self, cx: &AppContext) -> bool {
1721 self.read_only || self.buffer.read(cx).read_only()
1722 }
1723
1724 pub fn set_read_only(&mut self, read_only: bool) {
1725 self.read_only = read_only;
1726 }
1727
1728 pub fn set_use_autoclose(&mut self, autoclose: bool) {
1729 self.use_autoclose = autoclose;
1730 }
1731
1732 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
1733 self.use_auto_surround = auto_surround;
1734 }
1735
1736 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
1737 self.auto_replace_emoji_shortcode = auto_replace;
1738 }
1739
1740 pub fn toggle_inline_completions(
1741 &mut self,
1742 _: &ToggleInlineCompletions,
1743 cx: &mut ViewContext<Self>,
1744 ) {
1745 if self.show_inline_completions_override.is_some() {
1746 self.set_show_inline_completions(None, cx);
1747 } else {
1748 let cursor = self.selections.newest_anchor().head();
1749 if let Some((buffer, cursor_buffer_position)) =
1750 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
1751 {
1752 let show_inline_completions =
1753 !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx);
1754 self.set_show_inline_completions(Some(show_inline_completions), cx);
1755 }
1756 }
1757 }
1758
1759 pub fn set_show_inline_completions(
1760 &mut self,
1761 show_inline_completions: Option<bool>,
1762 cx: &mut ViewContext<Self>,
1763 ) {
1764 self.show_inline_completions_override = show_inline_completions;
1765 self.refresh_inline_completion(false, true, cx);
1766 }
1767
1768 fn should_show_inline_completions(
1769 &self,
1770 buffer: &Model<Buffer>,
1771 buffer_position: language::Anchor,
1772 cx: &AppContext,
1773 ) -> bool {
1774 if !self.snippet_stack.is_empty() {
1775 return false;
1776 }
1777
1778 if self.inline_completions_disabled_in_scope(buffer, buffer_position, cx) {
1779 return false;
1780 }
1781
1782 if let Some(provider) = self.inline_completion_provider() {
1783 if let Some(show_inline_completions) = self.show_inline_completions_override {
1784 show_inline_completions
1785 } else {
1786 self.mode == EditorMode::Full && provider.is_enabled(buffer, buffer_position, cx)
1787 }
1788 } else {
1789 false
1790 }
1791 }
1792
1793 fn inline_completions_disabled_in_scope(
1794 &self,
1795 buffer: &Model<Buffer>,
1796 buffer_position: language::Anchor,
1797 cx: &AppContext,
1798 ) -> bool {
1799 let snapshot = buffer.read(cx).snapshot();
1800 let settings = snapshot.settings_at(buffer_position, cx);
1801
1802 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
1803 return false;
1804 };
1805
1806 scope.override_name().map_or(false, |scope_name| {
1807 settings
1808 .inline_completions_disabled_in
1809 .iter()
1810 .any(|s| s == scope_name)
1811 })
1812 }
1813
1814 pub fn set_use_modal_editing(&mut self, to: bool) {
1815 self.use_modal_editing = to;
1816 }
1817
1818 pub fn use_modal_editing(&self) -> bool {
1819 self.use_modal_editing
1820 }
1821
1822 fn selections_did_change(
1823 &mut self,
1824 local: bool,
1825 old_cursor_position: &Anchor,
1826 show_completions: bool,
1827 cx: &mut ViewContext<Self>,
1828 ) {
1829 cx.invalidate_character_coordinates();
1830
1831 // Copy selections to primary selection buffer
1832 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
1833 if local {
1834 let selections = self.selections.all::<usize>(cx);
1835 let buffer_handle = self.buffer.read(cx).read(cx);
1836
1837 let mut text = String::new();
1838 for (index, selection) in selections.iter().enumerate() {
1839 let text_for_selection = buffer_handle
1840 .text_for_range(selection.start..selection.end)
1841 .collect::<String>();
1842
1843 text.push_str(&text_for_selection);
1844 if index != selections.len() - 1 {
1845 text.push('\n');
1846 }
1847 }
1848
1849 if !text.is_empty() {
1850 cx.write_to_primary(ClipboardItem::new_string(text));
1851 }
1852 }
1853
1854 if self.focus_handle.is_focused(cx) && self.leader_peer_id.is_none() {
1855 self.buffer.update(cx, |buffer, cx| {
1856 buffer.set_active_selections(
1857 &self.selections.disjoint_anchors(),
1858 self.selections.line_mode,
1859 self.cursor_shape,
1860 cx,
1861 )
1862 });
1863 }
1864 let display_map = self
1865 .display_map
1866 .update(cx, |display_map, cx| display_map.snapshot(cx));
1867 let buffer = &display_map.buffer_snapshot;
1868 self.add_selections_state = None;
1869 self.select_next_state = None;
1870 self.select_prev_state = None;
1871 self.select_larger_syntax_node_stack.clear();
1872 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
1873 self.snippet_stack
1874 .invalidate(&self.selections.disjoint_anchors(), buffer);
1875 self.take_rename(false, cx);
1876
1877 let new_cursor_position = self.selections.newest_anchor().head();
1878
1879 self.push_to_nav_history(
1880 *old_cursor_position,
1881 Some(new_cursor_position.to_point(buffer)),
1882 cx,
1883 );
1884
1885 if local {
1886 let new_cursor_position = self.selections.newest_anchor().head();
1887 let mut context_menu = self.context_menu.write();
1888 let completion_menu = match context_menu.as_ref() {
1889 Some(CodeContextMenu::Completions(menu)) => Some(menu),
1890
1891 _ => {
1892 *context_menu = None;
1893 None
1894 }
1895 };
1896
1897 if let Some(completion_menu) = completion_menu {
1898 let cursor_position = new_cursor_position.to_offset(buffer);
1899 let (word_range, kind) =
1900 buffer.surrounding_word(completion_menu.initial_position, true);
1901 if kind == Some(CharKind::Word)
1902 && word_range.to_inclusive().contains(&cursor_position)
1903 {
1904 let mut completion_menu = completion_menu.clone();
1905 drop(context_menu);
1906
1907 let query = Self::completion_query(buffer, cursor_position);
1908 cx.spawn(move |this, mut cx| async move {
1909 completion_menu
1910 .filter(query.as_deref(), cx.background_executor().clone())
1911 .await;
1912
1913 this.update(&mut cx, |this, cx| {
1914 let mut context_menu = this.context_menu.write();
1915 let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
1916 else {
1917 return;
1918 };
1919
1920 if menu.id > completion_menu.id {
1921 return;
1922 }
1923
1924 *context_menu = Some(CodeContextMenu::Completions(completion_menu));
1925 drop(context_menu);
1926 cx.notify();
1927 })
1928 })
1929 .detach();
1930
1931 if show_completions {
1932 self.show_completions(&ShowCompletions { trigger: None }, cx);
1933 }
1934 } else {
1935 drop(context_menu);
1936 self.hide_context_menu(cx);
1937 }
1938 } else {
1939 drop(context_menu);
1940 }
1941
1942 hide_hover(self, cx);
1943
1944 if old_cursor_position.to_display_point(&display_map).row()
1945 != new_cursor_position.to_display_point(&display_map).row()
1946 {
1947 self.available_code_actions.take();
1948 }
1949 self.refresh_code_actions(cx);
1950 self.refresh_document_highlights(cx);
1951 refresh_matching_bracket_highlights(self, cx);
1952 self.update_visible_inline_completion(cx);
1953 linked_editing_ranges::refresh_linked_ranges(self, cx);
1954 if self.git_blame_inline_enabled {
1955 self.start_inline_blame_timer(cx);
1956 }
1957 }
1958
1959 self.blink_manager.update(cx, BlinkManager::pause_blinking);
1960 cx.emit(EditorEvent::SelectionsChanged { local });
1961
1962 if self.selections.disjoint_anchors().len() == 1 {
1963 cx.emit(SearchEvent::ActiveMatchChanged)
1964 }
1965 cx.notify();
1966 }
1967
1968 pub fn change_selections<R>(
1969 &mut self,
1970 autoscroll: Option<Autoscroll>,
1971 cx: &mut ViewContext<Self>,
1972 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
1973 ) -> R {
1974 self.change_selections_inner(autoscroll, true, cx, change)
1975 }
1976
1977 pub fn change_selections_inner<R>(
1978 &mut self,
1979 autoscroll: Option<Autoscroll>,
1980 request_completions: bool,
1981 cx: &mut ViewContext<Self>,
1982 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
1983 ) -> R {
1984 let old_cursor_position = self.selections.newest_anchor().head();
1985 self.push_to_selection_history();
1986
1987 let (changed, result) = self.selections.change_with(cx, change);
1988
1989 if changed {
1990 if let Some(autoscroll) = autoscroll {
1991 self.request_autoscroll(autoscroll, cx);
1992 }
1993 self.selections_did_change(true, &old_cursor_position, request_completions, cx);
1994
1995 if self.should_open_signature_help_automatically(
1996 &old_cursor_position,
1997 self.signature_help_state.backspace_pressed(),
1998 cx,
1999 ) {
2000 self.show_signature_help(&ShowSignatureHelp, cx);
2001 }
2002 self.signature_help_state.set_backspace_pressed(false);
2003 }
2004
2005 result
2006 }
2007
2008 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2009 where
2010 I: IntoIterator<Item = (Range<S>, T)>,
2011 S: ToOffset,
2012 T: Into<Arc<str>>,
2013 {
2014 if self.read_only(cx) {
2015 return;
2016 }
2017
2018 self.buffer
2019 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2020 }
2021
2022 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2023 where
2024 I: IntoIterator<Item = (Range<S>, T)>,
2025 S: ToOffset,
2026 T: Into<Arc<str>>,
2027 {
2028 if self.read_only(cx) {
2029 return;
2030 }
2031
2032 self.buffer.update(cx, |buffer, cx| {
2033 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2034 });
2035 }
2036
2037 pub fn edit_with_block_indent<I, S, T>(
2038 &mut self,
2039 edits: I,
2040 original_indent_columns: Vec<u32>,
2041 cx: &mut ViewContext<Self>,
2042 ) where
2043 I: IntoIterator<Item = (Range<S>, T)>,
2044 S: ToOffset,
2045 T: Into<Arc<str>>,
2046 {
2047 if self.read_only(cx) {
2048 return;
2049 }
2050
2051 self.buffer.update(cx, |buffer, cx| {
2052 buffer.edit(
2053 edits,
2054 Some(AutoindentMode::Block {
2055 original_indent_columns,
2056 }),
2057 cx,
2058 )
2059 });
2060 }
2061
2062 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
2063 self.hide_context_menu(cx);
2064
2065 match phase {
2066 SelectPhase::Begin {
2067 position,
2068 add,
2069 click_count,
2070 } => self.begin_selection(position, add, click_count, cx),
2071 SelectPhase::BeginColumnar {
2072 position,
2073 goal_column,
2074 reset,
2075 } => self.begin_columnar_selection(position, goal_column, reset, cx),
2076 SelectPhase::Extend {
2077 position,
2078 click_count,
2079 } => self.extend_selection(position, click_count, cx),
2080 SelectPhase::Update {
2081 position,
2082 goal_column,
2083 scroll_delta,
2084 } => self.update_selection(position, goal_column, scroll_delta, cx),
2085 SelectPhase::End => self.end_selection(cx),
2086 }
2087 }
2088
2089 fn extend_selection(
2090 &mut self,
2091 position: DisplayPoint,
2092 click_count: usize,
2093 cx: &mut ViewContext<Self>,
2094 ) {
2095 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2096 let tail = self.selections.newest::<usize>(cx).tail();
2097 self.begin_selection(position, false, click_count, cx);
2098
2099 let position = position.to_offset(&display_map, Bias::Left);
2100 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2101
2102 let mut pending_selection = self
2103 .selections
2104 .pending_anchor()
2105 .expect("extend_selection not called with pending selection");
2106 if position >= tail {
2107 pending_selection.start = tail_anchor;
2108 } else {
2109 pending_selection.end = tail_anchor;
2110 pending_selection.reversed = true;
2111 }
2112
2113 let mut pending_mode = self.selections.pending_mode().unwrap();
2114 match &mut pending_mode {
2115 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2116 _ => {}
2117 }
2118
2119 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
2120 s.set_pending(pending_selection, pending_mode)
2121 });
2122 }
2123
2124 fn begin_selection(
2125 &mut self,
2126 position: DisplayPoint,
2127 add: bool,
2128 click_count: usize,
2129 cx: &mut ViewContext<Self>,
2130 ) {
2131 if !self.focus_handle.is_focused(cx) {
2132 self.last_focused_descendant = None;
2133 cx.focus(&self.focus_handle);
2134 }
2135
2136 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2137 let buffer = &display_map.buffer_snapshot;
2138 let newest_selection = self.selections.newest_anchor().clone();
2139 let position = display_map.clip_point(position, Bias::Left);
2140
2141 let start;
2142 let end;
2143 let mode;
2144 let mut auto_scroll;
2145 match click_count {
2146 1 => {
2147 start = buffer.anchor_before(position.to_point(&display_map));
2148 end = start;
2149 mode = SelectMode::Character;
2150 auto_scroll = true;
2151 }
2152 2 => {
2153 let range = movement::surrounding_word(&display_map, position);
2154 start = buffer.anchor_before(range.start.to_point(&display_map));
2155 end = buffer.anchor_before(range.end.to_point(&display_map));
2156 mode = SelectMode::Word(start..end);
2157 auto_scroll = true;
2158 }
2159 3 => {
2160 let position = display_map
2161 .clip_point(position, Bias::Left)
2162 .to_point(&display_map);
2163 let line_start = display_map.prev_line_boundary(position).0;
2164 let next_line_start = buffer.clip_point(
2165 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2166 Bias::Left,
2167 );
2168 start = buffer.anchor_before(line_start);
2169 end = buffer.anchor_before(next_line_start);
2170 mode = SelectMode::Line(start..end);
2171 auto_scroll = true;
2172 }
2173 _ => {
2174 start = buffer.anchor_before(0);
2175 end = buffer.anchor_before(buffer.len());
2176 mode = SelectMode::All;
2177 auto_scroll = false;
2178 }
2179 }
2180 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2181
2182 let point_to_delete: Option<usize> = {
2183 let selected_points: Vec<Selection<Point>> =
2184 self.selections.disjoint_in_range(start..end, cx);
2185
2186 if !add || click_count > 1 {
2187 None
2188 } else if !selected_points.is_empty() {
2189 Some(selected_points[0].id)
2190 } else {
2191 let clicked_point_already_selected =
2192 self.selections.disjoint.iter().find(|selection| {
2193 selection.start.to_point(buffer) == start.to_point(buffer)
2194 || selection.end.to_point(buffer) == end.to_point(buffer)
2195 });
2196
2197 clicked_point_already_selected.map(|selection| selection.id)
2198 }
2199 };
2200
2201 let selections_count = self.selections.count();
2202
2203 self.change_selections(auto_scroll.then(Autoscroll::newest), cx, |s| {
2204 if let Some(point_to_delete) = point_to_delete {
2205 s.delete(point_to_delete);
2206
2207 if selections_count == 1 {
2208 s.set_pending_anchor_range(start..end, mode);
2209 }
2210 } else {
2211 if !add {
2212 s.clear_disjoint();
2213 } else if click_count > 1 {
2214 s.delete(newest_selection.id)
2215 }
2216
2217 s.set_pending_anchor_range(start..end, mode);
2218 }
2219 });
2220 }
2221
2222 fn begin_columnar_selection(
2223 &mut self,
2224 position: DisplayPoint,
2225 goal_column: u32,
2226 reset: bool,
2227 cx: &mut ViewContext<Self>,
2228 ) {
2229 if !self.focus_handle.is_focused(cx) {
2230 self.last_focused_descendant = None;
2231 cx.focus(&self.focus_handle);
2232 }
2233
2234 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2235
2236 if reset {
2237 let pointer_position = display_map
2238 .buffer_snapshot
2239 .anchor_before(position.to_point(&display_map));
2240
2241 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
2242 s.clear_disjoint();
2243 s.set_pending_anchor_range(
2244 pointer_position..pointer_position,
2245 SelectMode::Character,
2246 );
2247 });
2248 }
2249
2250 let tail = self.selections.newest::<Point>(cx).tail();
2251 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2252
2253 if !reset {
2254 self.select_columns(
2255 tail.to_display_point(&display_map),
2256 position,
2257 goal_column,
2258 &display_map,
2259 cx,
2260 );
2261 }
2262 }
2263
2264 fn update_selection(
2265 &mut self,
2266 position: DisplayPoint,
2267 goal_column: u32,
2268 scroll_delta: gpui::Point<f32>,
2269 cx: &mut ViewContext<Self>,
2270 ) {
2271 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2272
2273 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2274 let tail = tail.to_display_point(&display_map);
2275 self.select_columns(tail, position, goal_column, &display_map, cx);
2276 } else if let Some(mut pending) = self.selections.pending_anchor() {
2277 let buffer = self.buffer.read(cx).snapshot(cx);
2278 let head;
2279 let tail;
2280 let mode = self.selections.pending_mode().unwrap();
2281 match &mode {
2282 SelectMode::Character => {
2283 head = position.to_point(&display_map);
2284 tail = pending.tail().to_point(&buffer);
2285 }
2286 SelectMode::Word(original_range) => {
2287 let original_display_range = original_range.start.to_display_point(&display_map)
2288 ..original_range.end.to_display_point(&display_map);
2289 let original_buffer_range = original_display_range.start.to_point(&display_map)
2290 ..original_display_range.end.to_point(&display_map);
2291 if movement::is_inside_word(&display_map, position)
2292 || original_display_range.contains(&position)
2293 {
2294 let word_range = movement::surrounding_word(&display_map, position);
2295 if word_range.start < original_display_range.start {
2296 head = word_range.start.to_point(&display_map);
2297 } else {
2298 head = word_range.end.to_point(&display_map);
2299 }
2300 } else {
2301 head = position.to_point(&display_map);
2302 }
2303
2304 if head <= original_buffer_range.start {
2305 tail = original_buffer_range.end;
2306 } else {
2307 tail = original_buffer_range.start;
2308 }
2309 }
2310 SelectMode::Line(original_range) => {
2311 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2312
2313 let position = display_map
2314 .clip_point(position, Bias::Left)
2315 .to_point(&display_map);
2316 let line_start = display_map.prev_line_boundary(position).0;
2317 let next_line_start = buffer.clip_point(
2318 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2319 Bias::Left,
2320 );
2321
2322 if line_start < original_range.start {
2323 head = line_start
2324 } else {
2325 head = next_line_start
2326 }
2327
2328 if head <= original_range.start {
2329 tail = original_range.end;
2330 } else {
2331 tail = original_range.start;
2332 }
2333 }
2334 SelectMode::All => {
2335 return;
2336 }
2337 };
2338
2339 if head < tail {
2340 pending.start = buffer.anchor_before(head);
2341 pending.end = buffer.anchor_before(tail);
2342 pending.reversed = true;
2343 } else {
2344 pending.start = buffer.anchor_before(tail);
2345 pending.end = buffer.anchor_before(head);
2346 pending.reversed = false;
2347 }
2348
2349 self.change_selections(None, cx, |s| {
2350 s.set_pending(pending, mode);
2351 });
2352 } else {
2353 log::error!("update_selection dispatched with no pending selection");
2354 return;
2355 }
2356
2357 self.apply_scroll_delta(scroll_delta, cx);
2358 cx.notify();
2359 }
2360
2361 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
2362 self.columnar_selection_tail.take();
2363 if self.selections.pending_anchor().is_some() {
2364 let selections = self.selections.all::<usize>(cx);
2365 self.change_selections(None, cx, |s| {
2366 s.select(selections);
2367 s.clear_pending();
2368 });
2369 }
2370 }
2371
2372 fn select_columns(
2373 &mut self,
2374 tail: DisplayPoint,
2375 head: DisplayPoint,
2376 goal_column: u32,
2377 display_map: &DisplaySnapshot,
2378 cx: &mut ViewContext<Self>,
2379 ) {
2380 let start_row = cmp::min(tail.row(), head.row());
2381 let end_row = cmp::max(tail.row(), head.row());
2382 let start_column = cmp::min(tail.column(), goal_column);
2383 let end_column = cmp::max(tail.column(), goal_column);
2384 let reversed = start_column < tail.column();
2385
2386 let selection_ranges = (start_row.0..=end_row.0)
2387 .map(DisplayRow)
2388 .filter_map(|row| {
2389 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2390 let start = display_map
2391 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2392 .to_point(display_map);
2393 let end = display_map
2394 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2395 .to_point(display_map);
2396 if reversed {
2397 Some(end..start)
2398 } else {
2399 Some(start..end)
2400 }
2401 } else {
2402 None
2403 }
2404 })
2405 .collect::<Vec<_>>();
2406
2407 self.change_selections(None, cx, |s| {
2408 s.select_ranges(selection_ranges);
2409 });
2410 cx.notify();
2411 }
2412
2413 pub fn has_pending_nonempty_selection(&self) -> bool {
2414 let pending_nonempty_selection = match self.selections.pending_anchor() {
2415 Some(Selection { start, end, .. }) => start != end,
2416 None => false,
2417 };
2418
2419 pending_nonempty_selection
2420 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2421 }
2422
2423 pub fn has_pending_selection(&self) -> bool {
2424 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2425 }
2426
2427 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
2428 if self.clear_expanded_diff_hunks(cx) {
2429 cx.notify();
2430 return;
2431 }
2432 if self.dismiss_menus_and_popups(true, cx) {
2433 return;
2434 }
2435
2436 if self.mode == EditorMode::Full
2437 && self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel())
2438 {
2439 return;
2440 }
2441
2442 cx.propagate();
2443 }
2444
2445 pub fn dismiss_menus_and_popups(
2446 &mut self,
2447 should_report_inline_completion_event: bool,
2448 cx: &mut ViewContext<Self>,
2449 ) -> bool {
2450 if self.take_rename(false, cx).is_some() {
2451 return true;
2452 }
2453
2454 if hide_hover(self, cx) {
2455 return true;
2456 }
2457
2458 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2459 return true;
2460 }
2461
2462 if self.hide_context_menu(cx).is_some() {
2463 return true;
2464 }
2465
2466 if self.mouse_context_menu.take().is_some() {
2467 return true;
2468 }
2469
2470 if self.discard_inline_completion(should_report_inline_completion_event, cx) {
2471 return true;
2472 }
2473
2474 if self.snippet_stack.pop().is_some() {
2475 return true;
2476 }
2477
2478 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
2479 self.dismiss_diagnostics(cx);
2480 return true;
2481 }
2482
2483 false
2484 }
2485
2486 fn linked_editing_ranges_for(
2487 &self,
2488 selection: Range<text::Anchor>,
2489 cx: &AppContext,
2490 ) -> Option<HashMap<Model<Buffer>, Vec<Range<text::Anchor>>>> {
2491 if self.linked_edit_ranges.is_empty() {
2492 return None;
2493 }
2494 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2495 selection.end.buffer_id.and_then(|end_buffer_id| {
2496 if selection.start.buffer_id != Some(end_buffer_id) {
2497 return None;
2498 }
2499 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2500 let snapshot = buffer.read(cx).snapshot();
2501 self.linked_edit_ranges
2502 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2503 .map(|ranges| (ranges, snapshot, buffer))
2504 })?;
2505 use text::ToOffset as TO;
2506 // find offset from the start of current range to current cursor position
2507 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2508
2509 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2510 let start_difference = start_offset - start_byte_offset;
2511 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2512 let end_difference = end_offset - start_byte_offset;
2513 // Current range has associated linked ranges.
2514 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2515 for range in linked_ranges.iter() {
2516 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2517 let end_offset = start_offset + end_difference;
2518 let start_offset = start_offset + start_difference;
2519 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2520 continue;
2521 }
2522 if self.selections.disjoint_anchor_ranges().iter().any(|s| {
2523 if s.start.buffer_id != selection.start.buffer_id
2524 || s.end.buffer_id != selection.end.buffer_id
2525 {
2526 return false;
2527 }
2528 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
2529 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
2530 }) {
2531 continue;
2532 }
2533 let start = buffer_snapshot.anchor_after(start_offset);
2534 let end = buffer_snapshot.anchor_after(end_offset);
2535 linked_edits
2536 .entry(buffer.clone())
2537 .or_default()
2538 .push(start..end);
2539 }
2540 Some(linked_edits)
2541 }
2542
2543 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2544 let text: Arc<str> = text.into();
2545
2546 if self.read_only(cx) {
2547 return;
2548 }
2549
2550 let selections = self.selections.all_adjusted(cx);
2551 let mut bracket_inserted = false;
2552 let mut edits = Vec::new();
2553 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2554 let mut new_selections = Vec::with_capacity(selections.len());
2555 let mut new_autoclose_regions = Vec::new();
2556 let snapshot = self.buffer.read(cx).read(cx);
2557
2558 for (selection, autoclose_region) in
2559 self.selections_with_autoclose_regions(selections, &snapshot)
2560 {
2561 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2562 // Determine if the inserted text matches the opening or closing
2563 // bracket of any of this language's bracket pairs.
2564 let mut bracket_pair = None;
2565 let mut is_bracket_pair_start = false;
2566 let mut is_bracket_pair_end = false;
2567 if !text.is_empty() {
2568 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
2569 // and they are removing the character that triggered IME popup.
2570 for (pair, enabled) in scope.brackets() {
2571 if !pair.close && !pair.surround {
2572 continue;
2573 }
2574
2575 if enabled && pair.start.ends_with(text.as_ref()) {
2576 let prefix_len = pair.start.len() - text.len();
2577 let preceding_text_matches_prefix = prefix_len == 0
2578 || (selection.start.column >= (prefix_len as u32)
2579 && snapshot.contains_str_at(
2580 Point::new(
2581 selection.start.row,
2582 selection.start.column - (prefix_len as u32),
2583 ),
2584 &pair.start[..prefix_len],
2585 ));
2586 if preceding_text_matches_prefix {
2587 bracket_pair = Some(pair.clone());
2588 is_bracket_pair_start = true;
2589 break;
2590 }
2591 }
2592 if pair.end.as_str() == text.as_ref() {
2593 bracket_pair = Some(pair.clone());
2594 is_bracket_pair_end = true;
2595 break;
2596 }
2597 }
2598 }
2599
2600 if let Some(bracket_pair) = bracket_pair {
2601 let snapshot_settings = snapshot.settings_at(selection.start, cx);
2602 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
2603 let auto_surround =
2604 self.use_auto_surround && snapshot_settings.use_auto_surround;
2605 if selection.is_empty() {
2606 if is_bracket_pair_start {
2607 // If the inserted text is a suffix of an opening bracket and the
2608 // selection is preceded by the rest of the opening bracket, then
2609 // insert the closing bracket.
2610 let following_text_allows_autoclose = snapshot
2611 .chars_at(selection.start)
2612 .next()
2613 .map_or(true, |c| scope.should_autoclose_before(c));
2614
2615 let is_closing_quote = if bracket_pair.end == bracket_pair.start
2616 && bracket_pair.start.len() == 1
2617 {
2618 let target = bracket_pair.start.chars().next().unwrap();
2619 let current_line_count = snapshot
2620 .reversed_chars_at(selection.start)
2621 .take_while(|&c| c != '\n')
2622 .filter(|&c| c == target)
2623 .count();
2624 current_line_count % 2 == 1
2625 } else {
2626 false
2627 };
2628
2629 if autoclose
2630 && bracket_pair.close
2631 && following_text_allows_autoclose
2632 && !is_closing_quote
2633 {
2634 let anchor = snapshot.anchor_before(selection.end);
2635 new_selections.push((selection.map(|_| anchor), text.len()));
2636 new_autoclose_regions.push((
2637 anchor,
2638 text.len(),
2639 selection.id,
2640 bracket_pair.clone(),
2641 ));
2642 edits.push((
2643 selection.range(),
2644 format!("{}{}", text, bracket_pair.end).into(),
2645 ));
2646 bracket_inserted = true;
2647 continue;
2648 }
2649 }
2650
2651 if let Some(region) = autoclose_region {
2652 // If the selection is followed by an auto-inserted closing bracket,
2653 // then don't insert that closing bracket again; just move the selection
2654 // past the closing bracket.
2655 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2656 && text.as_ref() == region.pair.end.as_str();
2657 if should_skip {
2658 let anchor = snapshot.anchor_after(selection.end);
2659 new_selections
2660 .push((selection.map(|_| anchor), region.pair.end.len()));
2661 continue;
2662 }
2663 }
2664
2665 let always_treat_brackets_as_autoclosed = snapshot
2666 .settings_at(selection.start, cx)
2667 .always_treat_brackets_as_autoclosed;
2668 if always_treat_brackets_as_autoclosed
2669 && is_bracket_pair_end
2670 && snapshot.contains_str_at(selection.end, text.as_ref())
2671 {
2672 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
2673 // and the inserted text is a closing bracket and the selection is followed
2674 // by the closing bracket then move the selection past the closing bracket.
2675 let anchor = snapshot.anchor_after(selection.end);
2676 new_selections.push((selection.map(|_| anchor), text.len()));
2677 continue;
2678 }
2679 }
2680 // If an opening bracket is 1 character long and is typed while
2681 // text is selected, then surround that text with the bracket pair.
2682 else if auto_surround
2683 && bracket_pair.surround
2684 && is_bracket_pair_start
2685 && bracket_pair.start.chars().count() == 1
2686 {
2687 edits.push((selection.start..selection.start, text.clone()));
2688 edits.push((
2689 selection.end..selection.end,
2690 bracket_pair.end.as_str().into(),
2691 ));
2692 bracket_inserted = true;
2693 new_selections.push((
2694 Selection {
2695 id: selection.id,
2696 start: snapshot.anchor_after(selection.start),
2697 end: snapshot.anchor_before(selection.end),
2698 reversed: selection.reversed,
2699 goal: selection.goal,
2700 },
2701 0,
2702 ));
2703 continue;
2704 }
2705 }
2706 }
2707
2708 if self.auto_replace_emoji_shortcode
2709 && selection.is_empty()
2710 && text.as_ref().ends_with(':')
2711 {
2712 if let Some(possible_emoji_short_code) =
2713 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
2714 {
2715 if !possible_emoji_short_code.is_empty() {
2716 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
2717 let emoji_shortcode_start = Point::new(
2718 selection.start.row,
2719 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
2720 );
2721
2722 // Remove shortcode from buffer
2723 edits.push((
2724 emoji_shortcode_start..selection.start,
2725 "".to_string().into(),
2726 ));
2727 new_selections.push((
2728 Selection {
2729 id: selection.id,
2730 start: snapshot.anchor_after(emoji_shortcode_start),
2731 end: snapshot.anchor_before(selection.start),
2732 reversed: selection.reversed,
2733 goal: selection.goal,
2734 },
2735 0,
2736 ));
2737
2738 // Insert emoji
2739 let selection_start_anchor = snapshot.anchor_after(selection.start);
2740 new_selections.push((selection.map(|_| selection_start_anchor), 0));
2741 edits.push((selection.start..selection.end, emoji.to_string().into()));
2742
2743 continue;
2744 }
2745 }
2746 }
2747 }
2748
2749 // If not handling any auto-close operation, then just replace the selected
2750 // text with the given input and move the selection to the end of the
2751 // newly inserted text.
2752 let anchor = snapshot.anchor_after(selection.end);
2753 if !self.linked_edit_ranges.is_empty() {
2754 let start_anchor = snapshot.anchor_before(selection.start);
2755
2756 let is_word_char = text.chars().next().map_or(true, |char| {
2757 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
2758 classifier.is_word(char)
2759 });
2760
2761 if is_word_char {
2762 if let Some(ranges) = self
2763 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
2764 {
2765 for (buffer, edits) in ranges {
2766 linked_edits
2767 .entry(buffer.clone())
2768 .or_default()
2769 .extend(edits.into_iter().map(|range| (range, text.clone())));
2770 }
2771 }
2772 }
2773 }
2774
2775 new_selections.push((selection.map(|_| anchor), 0));
2776 edits.push((selection.start..selection.end, text.clone()));
2777 }
2778
2779 drop(snapshot);
2780
2781 self.transact(cx, |this, cx| {
2782 this.buffer.update(cx, |buffer, cx| {
2783 buffer.edit(edits, this.autoindent_mode.clone(), cx);
2784 });
2785 for (buffer, edits) in linked_edits {
2786 buffer.update(cx, |buffer, cx| {
2787 let snapshot = buffer.snapshot();
2788 let edits = edits
2789 .into_iter()
2790 .map(|(range, text)| {
2791 use text::ToPoint as TP;
2792 let end_point = TP::to_point(&range.end, &snapshot);
2793 let start_point = TP::to_point(&range.start, &snapshot);
2794 (start_point..end_point, text)
2795 })
2796 .sorted_by_key(|(range, _)| range.start)
2797 .collect::<Vec<_>>();
2798 buffer.edit(edits, None, cx);
2799 })
2800 }
2801 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
2802 let new_selection_deltas = new_selections.iter().map(|e| e.1);
2803 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
2804 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
2805 .zip(new_selection_deltas)
2806 .map(|(selection, delta)| Selection {
2807 id: selection.id,
2808 start: selection.start + delta,
2809 end: selection.end + delta,
2810 reversed: selection.reversed,
2811 goal: SelectionGoal::None,
2812 })
2813 .collect::<Vec<_>>();
2814
2815 let mut i = 0;
2816 for (position, delta, selection_id, pair) in new_autoclose_regions {
2817 let position = position.to_offset(&map.buffer_snapshot) + delta;
2818 let start = map.buffer_snapshot.anchor_before(position);
2819 let end = map.buffer_snapshot.anchor_after(position);
2820 while let Some(existing_state) = this.autoclose_regions.get(i) {
2821 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
2822 Ordering::Less => i += 1,
2823 Ordering::Greater => break,
2824 Ordering::Equal => {
2825 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
2826 Ordering::Less => i += 1,
2827 Ordering::Equal => break,
2828 Ordering::Greater => break,
2829 }
2830 }
2831 }
2832 }
2833 this.autoclose_regions.insert(
2834 i,
2835 AutocloseRegion {
2836 selection_id,
2837 range: start..end,
2838 pair,
2839 },
2840 );
2841 }
2842
2843 let had_active_inline_completion = this.has_active_inline_completion();
2844 this.change_selections_inner(Some(Autoscroll::fit()), false, cx, |s| {
2845 s.select(new_selections)
2846 });
2847
2848 if !bracket_inserted {
2849 if let Some(on_type_format_task) =
2850 this.trigger_on_type_formatting(text.to_string(), cx)
2851 {
2852 on_type_format_task.detach_and_log_err(cx);
2853 }
2854 }
2855
2856 let editor_settings = EditorSettings::get_global(cx);
2857 if bracket_inserted
2858 && (editor_settings.auto_signature_help
2859 || editor_settings.show_signature_help_after_edits)
2860 {
2861 this.show_signature_help(&ShowSignatureHelp, cx);
2862 }
2863
2864 let trigger_in_words = !had_active_inline_completion;
2865 this.trigger_completion_on_input(&text, trigger_in_words, cx);
2866 linked_editing_ranges::refresh_linked_ranges(this, cx);
2867 this.refresh_inline_completion(true, false, cx);
2868 });
2869 }
2870
2871 fn find_possible_emoji_shortcode_at_position(
2872 snapshot: &MultiBufferSnapshot,
2873 position: Point,
2874 ) -> Option<String> {
2875 let mut chars = Vec::new();
2876 let mut found_colon = false;
2877 for char in snapshot.reversed_chars_at(position).take(100) {
2878 // Found a possible emoji shortcode in the middle of the buffer
2879 if found_colon {
2880 if char.is_whitespace() {
2881 chars.reverse();
2882 return Some(chars.iter().collect());
2883 }
2884 // If the previous character is not a whitespace, we are in the middle of a word
2885 // and we only want to complete the shortcode if the word is made up of other emojis
2886 let mut containing_word = String::new();
2887 for ch in snapshot
2888 .reversed_chars_at(position)
2889 .skip(chars.len() + 1)
2890 .take(100)
2891 {
2892 if ch.is_whitespace() {
2893 break;
2894 }
2895 containing_word.push(ch);
2896 }
2897 let containing_word = containing_word.chars().rev().collect::<String>();
2898 if util::word_consists_of_emojis(containing_word.as_str()) {
2899 chars.reverse();
2900 return Some(chars.iter().collect());
2901 }
2902 }
2903
2904 if char.is_whitespace() || !char.is_ascii() {
2905 return None;
2906 }
2907 if char == ':' {
2908 found_colon = true;
2909 } else {
2910 chars.push(char);
2911 }
2912 }
2913 // Found a possible emoji shortcode at the beginning of the buffer
2914 chars.reverse();
2915 Some(chars.iter().collect())
2916 }
2917
2918 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
2919 self.transact(cx, |this, cx| {
2920 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
2921 let selections = this.selections.all::<usize>(cx);
2922 let multi_buffer = this.buffer.read(cx);
2923 let buffer = multi_buffer.snapshot(cx);
2924 selections
2925 .iter()
2926 .map(|selection| {
2927 let start_point = selection.start.to_point(&buffer);
2928 let mut indent =
2929 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
2930 indent.len = cmp::min(indent.len, start_point.column);
2931 let start = selection.start;
2932 let end = selection.end;
2933 let selection_is_empty = start == end;
2934 let language_scope = buffer.language_scope_at(start);
2935 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
2936 &language_scope
2937 {
2938 let leading_whitespace_len = buffer
2939 .reversed_chars_at(start)
2940 .take_while(|c| c.is_whitespace() && *c != '\n')
2941 .map(|c| c.len_utf8())
2942 .sum::<usize>();
2943
2944 let trailing_whitespace_len = buffer
2945 .chars_at(end)
2946 .take_while(|c| c.is_whitespace() && *c != '\n')
2947 .map(|c| c.len_utf8())
2948 .sum::<usize>();
2949
2950 let insert_extra_newline =
2951 language.brackets().any(|(pair, enabled)| {
2952 let pair_start = pair.start.trim_end();
2953 let pair_end = pair.end.trim_start();
2954
2955 enabled
2956 && pair.newline
2957 && buffer.contains_str_at(
2958 end + trailing_whitespace_len,
2959 pair_end,
2960 )
2961 && buffer.contains_str_at(
2962 (start - leading_whitespace_len)
2963 .saturating_sub(pair_start.len()),
2964 pair_start,
2965 )
2966 });
2967
2968 // Comment extension on newline is allowed only for cursor selections
2969 let comment_delimiter = maybe!({
2970 if !selection_is_empty {
2971 return None;
2972 }
2973
2974 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
2975 return None;
2976 }
2977
2978 let delimiters = language.line_comment_prefixes();
2979 let max_len_of_delimiter =
2980 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
2981 let (snapshot, range) =
2982 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
2983
2984 let mut index_of_first_non_whitespace = 0;
2985 let comment_candidate = snapshot
2986 .chars_for_range(range)
2987 .skip_while(|c| {
2988 let should_skip = c.is_whitespace();
2989 if should_skip {
2990 index_of_first_non_whitespace += 1;
2991 }
2992 should_skip
2993 })
2994 .take(max_len_of_delimiter)
2995 .collect::<String>();
2996 let comment_prefix = delimiters.iter().find(|comment_prefix| {
2997 comment_candidate.starts_with(comment_prefix.as_ref())
2998 })?;
2999 let cursor_is_placed_after_comment_marker =
3000 index_of_first_non_whitespace + comment_prefix.len()
3001 <= start_point.column as usize;
3002 if cursor_is_placed_after_comment_marker {
3003 Some(comment_prefix.clone())
3004 } else {
3005 None
3006 }
3007 });
3008 (comment_delimiter, insert_extra_newline)
3009 } else {
3010 (None, false)
3011 };
3012
3013 let capacity_for_delimiter = comment_delimiter
3014 .as_deref()
3015 .map(str::len)
3016 .unwrap_or_default();
3017 let mut new_text =
3018 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3019 new_text.push('\n');
3020 new_text.extend(indent.chars());
3021 if let Some(delimiter) = &comment_delimiter {
3022 new_text.push_str(delimiter);
3023 }
3024 if insert_extra_newline {
3025 new_text = new_text.repeat(2);
3026 }
3027
3028 let anchor = buffer.anchor_after(end);
3029 let new_selection = selection.map(|_| anchor);
3030 (
3031 (start..end, new_text),
3032 (insert_extra_newline, new_selection),
3033 )
3034 })
3035 .unzip()
3036 };
3037
3038 this.edit_with_autoindent(edits, cx);
3039 let buffer = this.buffer.read(cx).snapshot(cx);
3040 let new_selections = selection_fixup_info
3041 .into_iter()
3042 .map(|(extra_newline_inserted, new_selection)| {
3043 let mut cursor = new_selection.end.to_point(&buffer);
3044 if extra_newline_inserted {
3045 cursor.row -= 1;
3046 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3047 }
3048 new_selection.map(|_| cursor)
3049 })
3050 .collect();
3051
3052 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
3053 this.refresh_inline_completion(true, false, cx);
3054 });
3055 }
3056
3057 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
3058 let buffer = self.buffer.read(cx);
3059 let snapshot = buffer.snapshot(cx);
3060
3061 let mut edits = Vec::new();
3062 let mut rows = Vec::new();
3063
3064 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3065 let cursor = selection.head();
3066 let row = cursor.row;
3067
3068 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3069
3070 let newline = "\n".to_string();
3071 edits.push((start_of_line..start_of_line, newline));
3072
3073 rows.push(row + rows_inserted as u32);
3074 }
3075
3076 self.transact(cx, |editor, cx| {
3077 editor.edit(edits, cx);
3078
3079 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3080 let mut index = 0;
3081 s.move_cursors_with(|map, _, _| {
3082 let row = rows[index];
3083 index += 1;
3084
3085 let point = Point::new(row, 0);
3086 let boundary = map.next_line_boundary(point).1;
3087 let clipped = map.clip_point(boundary, Bias::Left);
3088
3089 (clipped, SelectionGoal::None)
3090 });
3091 });
3092
3093 let mut indent_edits = Vec::new();
3094 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3095 for row in rows {
3096 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3097 for (row, indent) in indents {
3098 if indent.len == 0 {
3099 continue;
3100 }
3101
3102 let text = match indent.kind {
3103 IndentKind::Space => " ".repeat(indent.len as usize),
3104 IndentKind::Tab => "\t".repeat(indent.len as usize),
3105 };
3106 let point = Point::new(row.0, 0);
3107 indent_edits.push((point..point, text));
3108 }
3109 }
3110 editor.edit(indent_edits, cx);
3111 });
3112 }
3113
3114 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
3115 let buffer = self.buffer.read(cx);
3116 let snapshot = buffer.snapshot(cx);
3117
3118 let mut edits = Vec::new();
3119 let mut rows = Vec::new();
3120 let mut rows_inserted = 0;
3121
3122 for selection in self.selections.all_adjusted(cx) {
3123 let cursor = selection.head();
3124 let row = cursor.row;
3125
3126 let point = Point::new(row + 1, 0);
3127 let start_of_line = snapshot.clip_point(point, Bias::Left);
3128
3129 let newline = "\n".to_string();
3130 edits.push((start_of_line..start_of_line, newline));
3131
3132 rows_inserted += 1;
3133 rows.push(row + rows_inserted);
3134 }
3135
3136 self.transact(cx, |editor, cx| {
3137 editor.edit(edits, cx);
3138
3139 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3140 let mut index = 0;
3141 s.move_cursors_with(|map, _, _| {
3142 let row = rows[index];
3143 index += 1;
3144
3145 let point = Point::new(row, 0);
3146 let boundary = map.next_line_boundary(point).1;
3147 let clipped = map.clip_point(boundary, Bias::Left);
3148
3149 (clipped, SelectionGoal::None)
3150 });
3151 });
3152
3153 let mut indent_edits = Vec::new();
3154 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3155 for row in rows {
3156 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3157 for (row, indent) in indents {
3158 if indent.len == 0 {
3159 continue;
3160 }
3161
3162 let text = match indent.kind {
3163 IndentKind::Space => " ".repeat(indent.len as usize),
3164 IndentKind::Tab => "\t".repeat(indent.len as usize),
3165 };
3166 let point = Point::new(row.0, 0);
3167 indent_edits.push((point..point, text));
3168 }
3169 }
3170 editor.edit(indent_edits, cx);
3171 });
3172 }
3173
3174 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3175 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3176 original_indent_columns: Vec::new(),
3177 });
3178 self.insert_with_autoindent_mode(text, autoindent, cx);
3179 }
3180
3181 fn insert_with_autoindent_mode(
3182 &mut self,
3183 text: &str,
3184 autoindent_mode: Option<AutoindentMode>,
3185 cx: &mut ViewContext<Self>,
3186 ) {
3187 if self.read_only(cx) {
3188 return;
3189 }
3190
3191 let text: Arc<str> = text.into();
3192 self.transact(cx, |this, cx| {
3193 let old_selections = this.selections.all_adjusted(cx);
3194 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3195 let anchors = {
3196 let snapshot = buffer.read(cx);
3197 old_selections
3198 .iter()
3199 .map(|s| {
3200 let anchor = snapshot.anchor_after(s.head());
3201 s.map(|_| anchor)
3202 })
3203 .collect::<Vec<_>>()
3204 };
3205 buffer.edit(
3206 old_selections
3207 .iter()
3208 .map(|s| (s.start..s.end, text.clone())),
3209 autoindent_mode,
3210 cx,
3211 );
3212 anchors
3213 });
3214
3215 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3216 s.select_anchors(selection_anchors);
3217 })
3218 });
3219 }
3220
3221 fn trigger_completion_on_input(
3222 &mut self,
3223 text: &str,
3224 trigger_in_words: bool,
3225 cx: &mut ViewContext<Self>,
3226 ) {
3227 if self.is_completion_trigger(text, trigger_in_words, cx) {
3228 self.show_completions(
3229 &ShowCompletions {
3230 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3231 },
3232 cx,
3233 );
3234 } else {
3235 self.hide_context_menu(cx);
3236 }
3237 }
3238
3239 fn is_completion_trigger(
3240 &self,
3241 text: &str,
3242 trigger_in_words: bool,
3243 cx: &mut ViewContext<Self>,
3244 ) -> bool {
3245 let position = self.selections.newest_anchor().head();
3246 let multibuffer = self.buffer.read(cx);
3247 let Some(buffer) = position
3248 .buffer_id
3249 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3250 else {
3251 return false;
3252 };
3253
3254 if let Some(completion_provider) = &self.completion_provider {
3255 completion_provider.is_completion_trigger(
3256 &buffer,
3257 position.text_anchor,
3258 text,
3259 trigger_in_words,
3260 cx,
3261 )
3262 } else {
3263 false
3264 }
3265 }
3266
3267 /// If any empty selections is touching the start of its innermost containing autoclose
3268 /// region, expand it to select the brackets.
3269 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
3270 let selections = self.selections.all::<usize>(cx);
3271 let buffer = self.buffer.read(cx).read(cx);
3272 let new_selections = self
3273 .selections_with_autoclose_regions(selections, &buffer)
3274 .map(|(mut selection, region)| {
3275 if !selection.is_empty() {
3276 return selection;
3277 }
3278
3279 if let Some(region) = region {
3280 let mut range = region.range.to_offset(&buffer);
3281 if selection.start == range.start && range.start >= region.pair.start.len() {
3282 range.start -= region.pair.start.len();
3283 if buffer.contains_str_at(range.start, ®ion.pair.start)
3284 && buffer.contains_str_at(range.end, ®ion.pair.end)
3285 {
3286 range.end += region.pair.end.len();
3287 selection.start = range.start;
3288 selection.end = range.end;
3289
3290 return selection;
3291 }
3292 }
3293 }
3294
3295 let always_treat_brackets_as_autoclosed = buffer
3296 .settings_at(selection.start, cx)
3297 .always_treat_brackets_as_autoclosed;
3298
3299 if !always_treat_brackets_as_autoclosed {
3300 return selection;
3301 }
3302
3303 if let Some(scope) = buffer.language_scope_at(selection.start) {
3304 for (pair, enabled) in scope.brackets() {
3305 if !enabled || !pair.close {
3306 continue;
3307 }
3308
3309 if buffer.contains_str_at(selection.start, &pair.end) {
3310 let pair_start_len = pair.start.len();
3311 if buffer.contains_str_at(
3312 selection.start.saturating_sub(pair_start_len),
3313 &pair.start,
3314 ) {
3315 selection.start -= pair_start_len;
3316 selection.end += pair.end.len();
3317
3318 return selection;
3319 }
3320 }
3321 }
3322 }
3323
3324 selection
3325 })
3326 .collect();
3327
3328 drop(buffer);
3329 self.change_selections(None, cx, |selections| selections.select(new_selections));
3330 }
3331
3332 /// Iterate the given selections, and for each one, find the smallest surrounding
3333 /// autoclose region. This uses the ordering of the selections and the autoclose
3334 /// regions to avoid repeated comparisons.
3335 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3336 &'a self,
3337 selections: impl IntoIterator<Item = Selection<D>>,
3338 buffer: &'a MultiBufferSnapshot,
3339 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3340 let mut i = 0;
3341 let mut regions = self.autoclose_regions.as_slice();
3342 selections.into_iter().map(move |selection| {
3343 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3344
3345 let mut enclosing = None;
3346 while let Some(pair_state) = regions.get(i) {
3347 if pair_state.range.end.to_offset(buffer) < range.start {
3348 regions = ®ions[i + 1..];
3349 i = 0;
3350 } else if pair_state.range.start.to_offset(buffer) > range.end {
3351 break;
3352 } else {
3353 if pair_state.selection_id == selection.id {
3354 enclosing = Some(pair_state);
3355 }
3356 i += 1;
3357 }
3358 }
3359
3360 (selection, enclosing)
3361 })
3362 }
3363
3364 /// Remove any autoclose regions that no longer contain their selection.
3365 fn invalidate_autoclose_regions(
3366 &mut self,
3367 mut selections: &[Selection<Anchor>],
3368 buffer: &MultiBufferSnapshot,
3369 ) {
3370 self.autoclose_regions.retain(|state| {
3371 let mut i = 0;
3372 while let Some(selection) = selections.get(i) {
3373 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3374 selections = &selections[1..];
3375 continue;
3376 }
3377 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3378 break;
3379 }
3380 if selection.id == state.selection_id {
3381 return true;
3382 } else {
3383 i += 1;
3384 }
3385 }
3386 false
3387 });
3388 }
3389
3390 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3391 let offset = position.to_offset(buffer);
3392 let (word_range, kind) = buffer.surrounding_word(offset, true);
3393 if offset > word_range.start && kind == Some(CharKind::Word) {
3394 Some(
3395 buffer
3396 .text_for_range(word_range.start..offset)
3397 .collect::<String>(),
3398 )
3399 } else {
3400 None
3401 }
3402 }
3403
3404 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
3405 self.refresh_inlay_hints(
3406 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
3407 cx,
3408 );
3409 }
3410
3411 pub fn inlay_hints_enabled(&self) -> bool {
3412 self.inlay_hint_cache.enabled
3413 }
3414
3415 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
3416 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
3417 return;
3418 }
3419
3420 let reason_description = reason.description();
3421 let ignore_debounce = matches!(
3422 reason,
3423 InlayHintRefreshReason::SettingsChange(_)
3424 | InlayHintRefreshReason::Toggle(_)
3425 | InlayHintRefreshReason::ExcerptsRemoved(_)
3426 );
3427 let (invalidate_cache, required_languages) = match reason {
3428 InlayHintRefreshReason::Toggle(enabled) => {
3429 self.inlay_hint_cache.enabled = enabled;
3430 if enabled {
3431 (InvalidationStrategy::RefreshRequested, None)
3432 } else {
3433 self.inlay_hint_cache.clear();
3434 self.splice_inlays(
3435 self.visible_inlay_hints(cx)
3436 .iter()
3437 .map(|inlay| inlay.id)
3438 .collect(),
3439 Vec::new(),
3440 cx,
3441 );
3442 return;
3443 }
3444 }
3445 InlayHintRefreshReason::SettingsChange(new_settings) => {
3446 match self.inlay_hint_cache.update_settings(
3447 &self.buffer,
3448 new_settings,
3449 self.visible_inlay_hints(cx),
3450 cx,
3451 ) {
3452 ControlFlow::Break(Some(InlaySplice {
3453 to_remove,
3454 to_insert,
3455 })) => {
3456 self.splice_inlays(to_remove, to_insert, cx);
3457 return;
3458 }
3459 ControlFlow::Break(None) => return,
3460 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3461 }
3462 }
3463 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3464 if let Some(InlaySplice {
3465 to_remove,
3466 to_insert,
3467 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3468 {
3469 self.splice_inlays(to_remove, to_insert, cx);
3470 }
3471 return;
3472 }
3473 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3474 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3475 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3476 }
3477 InlayHintRefreshReason::RefreshRequested => {
3478 (InvalidationStrategy::RefreshRequested, None)
3479 }
3480 };
3481
3482 if let Some(InlaySplice {
3483 to_remove,
3484 to_insert,
3485 }) = self.inlay_hint_cache.spawn_hint_refresh(
3486 reason_description,
3487 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3488 invalidate_cache,
3489 ignore_debounce,
3490 cx,
3491 ) {
3492 self.splice_inlays(to_remove, to_insert, cx);
3493 }
3494 }
3495
3496 fn visible_inlay_hints(&self, cx: &ViewContext<'_, Editor>) -> Vec<Inlay> {
3497 self.display_map
3498 .read(cx)
3499 .current_inlays()
3500 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3501 .cloned()
3502 .collect()
3503 }
3504
3505 pub fn excerpts_for_inlay_hints_query(
3506 &self,
3507 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3508 cx: &mut ViewContext<Editor>,
3509 ) -> HashMap<ExcerptId, (Model<Buffer>, clock::Global, Range<usize>)> {
3510 let Some(project) = self.project.as_ref() else {
3511 return HashMap::default();
3512 };
3513 let project = project.read(cx);
3514 let multi_buffer = self.buffer().read(cx);
3515 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3516 let multi_buffer_visible_start = self
3517 .scroll_manager
3518 .anchor()
3519 .anchor
3520 .to_point(&multi_buffer_snapshot);
3521 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3522 multi_buffer_visible_start
3523 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3524 Bias::Left,
3525 );
3526 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3527 multi_buffer
3528 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
3529 .into_iter()
3530 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3531 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
3532 let buffer = buffer_handle.read(cx);
3533 let buffer_file = project::File::from_dyn(buffer.file())?;
3534 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3535 let worktree_entry = buffer_worktree
3536 .read(cx)
3537 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3538 if worktree_entry.is_ignored {
3539 return None;
3540 }
3541
3542 let language = buffer.language()?;
3543 if let Some(restrict_to_languages) = restrict_to_languages {
3544 if !restrict_to_languages.contains(language) {
3545 return None;
3546 }
3547 }
3548 Some((
3549 excerpt_id,
3550 (
3551 buffer_handle,
3552 buffer.version().clone(),
3553 excerpt_visible_range,
3554 ),
3555 ))
3556 })
3557 .collect()
3558 }
3559
3560 pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
3561 TextLayoutDetails {
3562 text_system: cx.text_system().clone(),
3563 editor_style: self.style.clone().unwrap(),
3564 rem_size: cx.rem_size(),
3565 scroll_anchor: self.scroll_manager.anchor(),
3566 visible_rows: self.visible_line_count(),
3567 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
3568 }
3569 }
3570
3571 fn splice_inlays(
3572 &self,
3573 to_remove: Vec<InlayId>,
3574 to_insert: Vec<Inlay>,
3575 cx: &mut ViewContext<Self>,
3576 ) {
3577 self.display_map.update(cx, |display_map, cx| {
3578 display_map.splice_inlays(to_remove, to_insert, cx)
3579 });
3580 cx.notify();
3581 }
3582
3583 fn trigger_on_type_formatting(
3584 &self,
3585 input: String,
3586 cx: &mut ViewContext<Self>,
3587 ) -> Option<Task<Result<()>>> {
3588 if input.len() != 1 {
3589 return None;
3590 }
3591
3592 let project = self.project.as_ref()?;
3593 let position = self.selections.newest_anchor().head();
3594 let (buffer, buffer_position) = self
3595 .buffer
3596 .read(cx)
3597 .text_anchor_for_position(position, cx)?;
3598
3599 let settings = language_settings::language_settings(
3600 buffer
3601 .read(cx)
3602 .language_at(buffer_position)
3603 .map(|l| l.name()),
3604 buffer.read(cx).file(),
3605 cx,
3606 );
3607 if !settings.use_on_type_format {
3608 return None;
3609 }
3610
3611 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3612 // hence we do LSP request & edit on host side only — add formats to host's history.
3613 let push_to_lsp_host_history = true;
3614 // If this is not the host, append its history with new edits.
3615 let push_to_client_history = project.read(cx).is_via_collab();
3616
3617 let on_type_formatting = project.update(cx, |project, cx| {
3618 project.on_type_format(
3619 buffer.clone(),
3620 buffer_position,
3621 input,
3622 push_to_lsp_host_history,
3623 cx,
3624 )
3625 });
3626 Some(cx.spawn(|editor, mut cx| async move {
3627 if let Some(transaction) = on_type_formatting.await? {
3628 if push_to_client_history {
3629 buffer
3630 .update(&mut cx, |buffer, _| {
3631 buffer.push_transaction(transaction, Instant::now());
3632 })
3633 .ok();
3634 }
3635 editor.update(&mut cx, |editor, cx| {
3636 editor.refresh_document_highlights(cx);
3637 })?;
3638 }
3639 Ok(())
3640 }))
3641 }
3642
3643 pub fn show_completions(&mut self, options: &ShowCompletions, cx: &mut ViewContext<Self>) {
3644 if self.pending_rename.is_some() {
3645 return;
3646 }
3647
3648 let Some(provider) = self.completion_provider.as_ref() else {
3649 return;
3650 };
3651
3652 if !self.snippet_stack.is_empty() && self.context_menu.read().as_ref().is_some() {
3653 return;
3654 }
3655
3656 let position = self.selections.newest_anchor().head();
3657 let (buffer, buffer_position) =
3658 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
3659 output
3660 } else {
3661 return;
3662 };
3663 let show_completion_documentation = buffer
3664 .read(cx)
3665 .snapshot()
3666 .settings_at(buffer_position, cx)
3667 .show_completion_documentation;
3668
3669 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
3670
3671 let aside_was_displayed = match self.context_menu.read().deref() {
3672 Some(CodeContextMenu::Completions(menu)) => menu.aside_was_displayed.get(),
3673 _ => false,
3674 };
3675 let trigger_kind = match &options.trigger {
3676 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
3677 CompletionTriggerKind::TRIGGER_CHARACTER
3678 }
3679 _ => CompletionTriggerKind::INVOKED,
3680 };
3681 let completion_context = CompletionContext {
3682 trigger_character: options.trigger.as_ref().and_then(|trigger| {
3683 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
3684 Some(String::from(trigger))
3685 } else {
3686 None
3687 }
3688 }),
3689 trigger_kind,
3690 };
3691 let completions = provider.completions(&buffer, buffer_position, completion_context, cx);
3692 let sort_completions = provider.sort_completions();
3693
3694 let id = post_inc(&mut self.next_completion_id);
3695 let task = cx.spawn(|editor, mut cx| {
3696 async move {
3697 editor.update(&mut cx, |this, _| {
3698 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
3699 })?;
3700 let completions = completions.await.log_err();
3701 let menu = if let Some(completions) = completions {
3702 let mut menu = CompletionsMenu::new(
3703 id,
3704 sort_completions,
3705 show_completion_documentation,
3706 position,
3707 buffer.clone(),
3708 completions.into(),
3709 aside_was_displayed,
3710 );
3711 menu.filter(query.as_deref(), cx.background_executor().clone())
3712 .await;
3713
3714 if menu.matches.is_empty() {
3715 None
3716 } else {
3717 Some(menu)
3718 }
3719 } else {
3720 None
3721 };
3722
3723 editor.update(&mut cx, |editor, cx| {
3724 let mut context_menu = editor.context_menu.write();
3725 match context_menu.as_ref() {
3726 None => {}
3727
3728 Some(CodeContextMenu::Completions(prev_menu)) => {
3729 if prev_menu.id > id {
3730 return;
3731 }
3732 }
3733
3734 _ => return,
3735 }
3736
3737 if editor.focus_handle.is_focused(cx) && menu.is_some() {
3738 let mut menu = menu.unwrap();
3739 menu.resolve_selected_completion(editor.completion_provider.as_deref(), cx);
3740 *context_menu = Some(CodeContextMenu::Completions(menu));
3741 drop(context_menu);
3742 cx.notify();
3743 } else if editor.completion_tasks.len() <= 1 {
3744 // If there are no more completion tasks and the last menu was
3745 // empty, we should hide it. If it was already hidden, we should
3746 // also show the copilot completion when available.
3747 drop(context_menu);
3748 editor.hide_context_menu(cx);
3749 }
3750 })?;
3751
3752 Ok::<_, anyhow::Error>(())
3753 }
3754 .log_err()
3755 });
3756
3757 self.completion_tasks.push((id, task));
3758 }
3759
3760 pub fn confirm_completion(
3761 &mut self,
3762 action: &ConfirmCompletion,
3763 cx: &mut ViewContext<Self>,
3764 ) -> Option<Task<Result<()>>> {
3765 self.do_completion(action.item_ix, CompletionIntent::Complete, cx)
3766 }
3767
3768 pub fn compose_completion(
3769 &mut self,
3770 action: &ComposeCompletion,
3771 cx: &mut ViewContext<Self>,
3772 ) -> Option<Task<Result<()>>> {
3773 self.do_completion(action.item_ix, CompletionIntent::Compose, cx)
3774 }
3775
3776 fn do_completion(
3777 &mut self,
3778 item_ix: Option<usize>,
3779 intent: CompletionIntent,
3780 cx: &mut ViewContext<Editor>,
3781 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
3782 use language::ToOffset as _;
3783
3784 self.discard_inline_completion(true, cx);
3785 let completions_menu =
3786 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
3787 menu
3788 } else {
3789 return None;
3790 };
3791
3792 let mat = completions_menu
3793 .matches
3794 .get(item_ix.unwrap_or(completions_menu.selected_item))?;
3795 let buffer_handle = completions_menu.buffer;
3796 let completions = completions_menu.completions.read();
3797 let completion = completions.get(mat.candidate_id)?;
3798 cx.stop_propagation();
3799
3800 let snippet;
3801 let text;
3802
3803 if completion.is_snippet() {
3804 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
3805 text = snippet.as_ref().unwrap().text.clone();
3806 } else {
3807 snippet = None;
3808 text = completion.new_text.clone();
3809 };
3810 let selections = self.selections.all::<usize>(cx);
3811 let buffer = buffer_handle.read(cx);
3812 let old_range = completion.old_range.to_offset(buffer);
3813 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
3814
3815 let newest_selection = self.selections.newest_anchor();
3816 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
3817 return None;
3818 }
3819
3820 let lookbehind = newest_selection
3821 .start
3822 .text_anchor
3823 .to_offset(buffer)
3824 .saturating_sub(old_range.start);
3825 let lookahead = old_range
3826 .end
3827 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
3828 let mut common_prefix_len = old_text
3829 .bytes()
3830 .zip(text.bytes())
3831 .take_while(|(a, b)| a == b)
3832 .count();
3833
3834 let snapshot = self.buffer.read(cx).snapshot(cx);
3835 let mut range_to_replace: Option<Range<isize>> = None;
3836 let mut ranges = Vec::new();
3837 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3838 for selection in &selections {
3839 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
3840 let start = selection.start.saturating_sub(lookbehind);
3841 let end = selection.end + lookahead;
3842 if selection.id == newest_selection.id {
3843 range_to_replace = Some(
3844 ((start + common_prefix_len) as isize - selection.start as isize)
3845 ..(end as isize - selection.start as isize),
3846 );
3847 }
3848 ranges.push(start + common_prefix_len..end);
3849 } else {
3850 common_prefix_len = 0;
3851 ranges.clear();
3852 ranges.extend(selections.iter().map(|s| {
3853 if s.id == newest_selection.id {
3854 range_to_replace = Some(
3855 old_range.start.to_offset_utf16(&snapshot).0 as isize
3856 - selection.start as isize
3857 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
3858 - selection.start as isize,
3859 );
3860 old_range.clone()
3861 } else {
3862 s.start..s.end
3863 }
3864 }));
3865 break;
3866 }
3867 if !self.linked_edit_ranges.is_empty() {
3868 let start_anchor = snapshot.anchor_before(selection.head());
3869 let end_anchor = snapshot.anchor_after(selection.tail());
3870 if let Some(ranges) = self
3871 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
3872 {
3873 for (buffer, edits) in ranges {
3874 linked_edits.entry(buffer.clone()).or_default().extend(
3875 edits
3876 .into_iter()
3877 .map(|range| (range, text[common_prefix_len..].to_owned())),
3878 );
3879 }
3880 }
3881 }
3882 }
3883 let text = &text[common_prefix_len..];
3884
3885 cx.emit(EditorEvent::InputHandled {
3886 utf16_range_to_replace: range_to_replace,
3887 text: text.into(),
3888 });
3889
3890 self.transact(cx, |this, cx| {
3891 if let Some(mut snippet) = snippet {
3892 snippet.text = text.to_string();
3893 for tabstop in snippet
3894 .tabstops
3895 .iter_mut()
3896 .flat_map(|tabstop| tabstop.ranges.iter_mut())
3897 {
3898 tabstop.start -= common_prefix_len as isize;
3899 tabstop.end -= common_prefix_len as isize;
3900 }
3901
3902 this.insert_snippet(&ranges, snippet, cx).log_err();
3903 } else {
3904 this.buffer.update(cx, |buffer, cx| {
3905 buffer.edit(
3906 ranges.iter().map(|range| (range.clone(), text)),
3907 this.autoindent_mode.clone(),
3908 cx,
3909 );
3910 });
3911 }
3912 for (buffer, edits) in linked_edits {
3913 buffer.update(cx, |buffer, cx| {
3914 let snapshot = buffer.snapshot();
3915 let edits = edits
3916 .into_iter()
3917 .map(|(range, text)| {
3918 use text::ToPoint as TP;
3919 let end_point = TP::to_point(&range.end, &snapshot);
3920 let start_point = TP::to_point(&range.start, &snapshot);
3921 (start_point..end_point, text)
3922 })
3923 .sorted_by_key(|(range, _)| range.start)
3924 .collect::<Vec<_>>();
3925 buffer.edit(edits, None, cx);
3926 })
3927 }
3928
3929 this.refresh_inline_completion(true, false, cx);
3930 });
3931
3932 let show_new_completions_on_confirm = completion
3933 .confirm
3934 .as_ref()
3935 .map_or(false, |confirm| confirm(intent, cx));
3936 if show_new_completions_on_confirm {
3937 self.show_completions(&ShowCompletions { trigger: None }, cx);
3938 }
3939
3940 let provider = self.completion_provider.as_ref()?;
3941 let apply_edits = provider.apply_additional_edits_for_completion(
3942 buffer_handle,
3943 completion.clone(),
3944 true,
3945 cx,
3946 );
3947
3948 let editor_settings = EditorSettings::get_global(cx);
3949 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
3950 // After the code completion is finished, users often want to know what signatures are needed.
3951 // so we should automatically call signature_help
3952 self.show_signature_help(&ShowSignatureHelp, cx);
3953 }
3954
3955 Some(cx.foreground_executor().spawn(async move {
3956 apply_edits.await?;
3957 Ok(())
3958 }))
3959 }
3960
3961 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
3962 let mut context_menu = self.context_menu.write();
3963 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
3964 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
3965 // Toggle if we're selecting the same one
3966 *context_menu = None;
3967 cx.notify();
3968 return;
3969 } else {
3970 // Otherwise, clear it and start a new one
3971 *context_menu = None;
3972 cx.notify();
3973 }
3974 }
3975 drop(context_menu);
3976 let snapshot = self.snapshot(cx);
3977 let deployed_from_indicator = action.deployed_from_indicator;
3978 let mut task = self.code_actions_task.take();
3979 let action = action.clone();
3980 cx.spawn(|editor, mut cx| async move {
3981 while let Some(prev_task) = task {
3982 prev_task.await.log_err();
3983 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
3984 }
3985
3986 let spawned_test_task = editor.update(&mut cx, |editor, cx| {
3987 if editor.focus_handle.is_focused(cx) {
3988 let multibuffer_point = action
3989 .deployed_from_indicator
3990 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
3991 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
3992 let (buffer, buffer_row) = snapshot
3993 .buffer_snapshot
3994 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
3995 .and_then(|(buffer_snapshot, range)| {
3996 editor
3997 .buffer
3998 .read(cx)
3999 .buffer(buffer_snapshot.remote_id())
4000 .map(|buffer| (buffer, range.start.row))
4001 })?;
4002 let (_, code_actions) = editor
4003 .available_code_actions
4004 .clone()
4005 .and_then(|(location, code_actions)| {
4006 let snapshot = location.buffer.read(cx).snapshot();
4007 let point_range = location.range.to_point(&snapshot);
4008 let point_range = point_range.start.row..=point_range.end.row;
4009 if point_range.contains(&buffer_row) {
4010 Some((location, code_actions))
4011 } else {
4012 None
4013 }
4014 })
4015 .unzip();
4016 let buffer_id = buffer.read(cx).remote_id();
4017 let tasks = editor
4018 .tasks
4019 .get(&(buffer_id, buffer_row))
4020 .map(|t| Arc::new(t.to_owned()));
4021 if tasks.is_none() && code_actions.is_none() {
4022 return None;
4023 }
4024
4025 editor.completion_tasks.clear();
4026 editor.discard_inline_completion(false, cx);
4027 let task_context =
4028 tasks
4029 .as_ref()
4030 .zip(editor.project.clone())
4031 .map(|(tasks, project)| {
4032 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4033 });
4034
4035 Some(cx.spawn(|editor, mut cx| async move {
4036 let task_context = match task_context {
4037 Some(task_context) => task_context.await,
4038 None => None,
4039 };
4040 let resolved_tasks =
4041 tasks.zip(task_context).map(|(tasks, task_context)| {
4042 Arc::new(ResolvedTasks {
4043 templates: tasks.resolve(&task_context).collect(),
4044 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4045 multibuffer_point.row,
4046 tasks.column,
4047 )),
4048 })
4049 });
4050 let spawn_straight_away = resolved_tasks
4051 .as_ref()
4052 .map_or(false, |tasks| tasks.templates.len() == 1)
4053 && code_actions
4054 .as_ref()
4055 .map_or(true, |actions| actions.is_empty());
4056 if let Ok(task) = editor.update(&mut cx, |editor, cx| {
4057 *editor.context_menu.write() =
4058 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4059 buffer,
4060 actions: CodeActionContents {
4061 tasks: resolved_tasks,
4062 actions: code_actions,
4063 },
4064 selected_item: Default::default(),
4065 scroll_handle: UniformListScrollHandle::default(),
4066 deployed_from_indicator,
4067 }));
4068 if spawn_straight_away {
4069 if let Some(task) = editor.confirm_code_action(
4070 &ConfirmCodeAction { item_ix: Some(0) },
4071 cx,
4072 ) {
4073 cx.notify();
4074 return task;
4075 }
4076 }
4077 cx.notify();
4078 Task::ready(Ok(()))
4079 }) {
4080 task.await
4081 } else {
4082 Ok(())
4083 }
4084 }))
4085 } else {
4086 Some(Task::ready(Ok(())))
4087 }
4088 })?;
4089 if let Some(task) = spawned_test_task {
4090 task.await?;
4091 }
4092
4093 Ok::<_, anyhow::Error>(())
4094 })
4095 .detach_and_log_err(cx);
4096 }
4097
4098 pub fn confirm_code_action(
4099 &mut self,
4100 action: &ConfirmCodeAction,
4101 cx: &mut ViewContext<Self>,
4102 ) -> Option<Task<Result<()>>> {
4103 let actions_menu = if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
4104 menu
4105 } else {
4106 return None;
4107 };
4108 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4109 let action = actions_menu.actions.get(action_ix)?;
4110 let title = action.label();
4111 let buffer = actions_menu.buffer;
4112 let workspace = self.workspace()?;
4113
4114 match action {
4115 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4116 workspace.update(cx, |workspace, cx| {
4117 workspace::tasks::schedule_resolved_task(
4118 workspace,
4119 task_source_kind,
4120 resolved_task,
4121 false,
4122 cx,
4123 );
4124
4125 Some(Task::ready(Ok(())))
4126 })
4127 }
4128 CodeActionsItem::CodeAction {
4129 excerpt_id,
4130 action,
4131 provider,
4132 } => {
4133 let apply_code_action =
4134 provider.apply_code_action(buffer, action, excerpt_id, true, cx);
4135 let workspace = workspace.downgrade();
4136 Some(cx.spawn(|editor, cx| async move {
4137 let project_transaction = apply_code_action.await?;
4138 Self::open_project_transaction(
4139 &editor,
4140 workspace,
4141 project_transaction,
4142 title,
4143 cx,
4144 )
4145 .await
4146 }))
4147 }
4148 }
4149 }
4150
4151 pub async fn open_project_transaction(
4152 this: &WeakView<Editor>,
4153 workspace: WeakView<Workspace>,
4154 transaction: ProjectTransaction,
4155 title: String,
4156 mut cx: AsyncWindowContext,
4157 ) -> Result<()> {
4158 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4159 cx.update(|cx| {
4160 entries.sort_unstable_by_key(|(buffer, _)| {
4161 buffer.read(cx).file().map(|f| f.path().clone())
4162 });
4163 })?;
4164
4165 // If the project transaction's edits are all contained within this editor, then
4166 // avoid opening a new editor to display them.
4167
4168 if let Some((buffer, transaction)) = entries.first() {
4169 if entries.len() == 1 {
4170 let excerpt = this.update(&mut cx, |editor, cx| {
4171 editor
4172 .buffer()
4173 .read(cx)
4174 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4175 })?;
4176 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4177 if excerpted_buffer == *buffer {
4178 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4179 let excerpt_range = excerpt_range.to_offset(buffer);
4180 buffer
4181 .edited_ranges_for_transaction::<usize>(transaction)
4182 .all(|range| {
4183 excerpt_range.start <= range.start
4184 && excerpt_range.end >= range.end
4185 })
4186 })?;
4187
4188 if all_edits_within_excerpt {
4189 return Ok(());
4190 }
4191 }
4192 }
4193 }
4194 } else {
4195 return Ok(());
4196 }
4197
4198 let mut ranges_to_highlight = Vec::new();
4199 let excerpt_buffer = cx.new_model(|cx| {
4200 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4201 for (buffer_handle, transaction) in &entries {
4202 let buffer = buffer_handle.read(cx);
4203 ranges_to_highlight.extend(
4204 multibuffer.push_excerpts_with_context_lines(
4205 buffer_handle.clone(),
4206 buffer
4207 .edited_ranges_for_transaction::<usize>(transaction)
4208 .collect(),
4209 DEFAULT_MULTIBUFFER_CONTEXT,
4210 cx,
4211 ),
4212 );
4213 }
4214 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4215 multibuffer
4216 })?;
4217
4218 workspace.update(&mut cx, |workspace, cx| {
4219 let project = workspace.project().clone();
4220 let editor =
4221 cx.new_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, cx));
4222 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
4223 editor.update(cx, |editor, cx| {
4224 editor.highlight_background::<Self>(
4225 &ranges_to_highlight,
4226 |theme| theme.editor_highlighted_line_background,
4227 cx,
4228 );
4229 });
4230 })?;
4231
4232 Ok(())
4233 }
4234
4235 pub fn clear_code_action_providers(&mut self) {
4236 self.code_action_providers.clear();
4237 self.available_code_actions.take();
4238 }
4239
4240 pub fn push_code_action_provider(
4241 &mut self,
4242 provider: Arc<dyn CodeActionProvider>,
4243 cx: &mut ViewContext<Self>,
4244 ) {
4245 self.code_action_providers.push(provider);
4246 self.refresh_code_actions(cx);
4247 }
4248
4249 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4250 let buffer = self.buffer.read(cx);
4251 let newest_selection = self.selections.newest_anchor().clone();
4252 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4253 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4254 if start_buffer != end_buffer {
4255 return None;
4256 }
4257
4258 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
4259 cx.background_executor()
4260 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4261 .await;
4262
4263 let (providers, tasks) = this.update(&mut cx, |this, cx| {
4264 let providers = this.code_action_providers.clone();
4265 let tasks = this
4266 .code_action_providers
4267 .iter()
4268 .map(|provider| provider.code_actions(&start_buffer, start..end, cx))
4269 .collect::<Vec<_>>();
4270 (providers, tasks)
4271 })?;
4272
4273 let mut actions = Vec::new();
4274 for (provider, provider_actions) in
4275 providers.into_iter().zip(future::join_all(tasks).await)
4276 {
4277 if let Some(provider_actions) = provider_actions.log_err() {
4278 actions.extend(provider_actions.into_iter().map(|action| {
4279 AvailableCodeAction {
4280 excerpt_id: newest_selection.start.excerpt_id,
4281 action,
4282 provider: provider.clone(),
4283 }
4284 }));
4285 }
4286 }
4287
4288 this.update(&mut cx, |this, cx| {
4289 this.available_code_actions = if actions.is_empty() {
4290 None
4291 } else {
4292 Some((
4293 Location {
4294 buffer: start_buffer,
4295 range: start..end,
4296 },
4297 actions.into(),
4298 ))
4299 };
4300 cx.notify();
4301 })
4302 }));
4303 None
4304 }
4305
4306 fn start_inline_blame_timer(&mut self, cx: &mut ViewContext<Self>) {
4307 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4308 self.show_git_blame_inline = false;
4309
4310 self.show_git_blame_inline_delay_task = Some(cx.spawn(|this, mut cx| async move {
4311 cx.background_executor().timer(delay).await;
4312
4313 this.update(&mut cx, |this, cx| {
4314 this.show_git_blame_inline = true;
4315 cx.notify();
4316 })
4317 .log_err();
4318 }));
4319 }
4320 }
4321
4322 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4323 if self.pending_rename.is_some() {
4324 return None;
4325 }
4326
4327 let provider = self.semantics_provider.clone()?;
4328 let buffer = self.buffer.read(cx);
4329 let newest_selection = self.selections.newest_anchor().clone();
4330 let cursor_position = newest_selection.head();
4331 let (cursor_buffer, cursor_buffer_position) =
4332 buffer.text_anchor_for_position(cursor_position, cx)?;
4333 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4334 if cursor_buffer != tail_buffer {
4335 return None;
4336 }
4337 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
4338 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4339 cx.background_executor()
4340 .timer(Duration::from_millis(debounce))
4341 .await;
4342
4343 let highlights = if let Some(highlights) = cx
4344 .update(|cx| {
4345 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4346 })
4347 .ok()
4348 .flatten()
4349 {
4350 highlights.await.log_err()
4351 } else {
4352 None
4353 };
4354
4355 if let Some(highlights) = highlights {
4356 this.update(&mut cx, |this, cx| {
4357 if this.pending_rename.is_some() {
4358 return;
4359 }
4360
4361 let buffer_id = cursor_position.buffer_id;
4362 let buffer = this.buffer.read(cx);
4363 if !buffer
4364 .text_anchor_for_position(cursor_position, cx)
4365 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4366 {
4367 return;
4368 }
4369
4370 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4371 let mut write_ranges = Vec::new();
4372 let mut read_ranges = Vec::new();
4373 for highlight in highlights {
4374 for (excerpt_id, excerpt_range) in
4375 buffer.excerpts_for_buffer(&cursor_buffer, cx)
4376 {
4377 let start = highlight
4378 .range
4379 .start
4380 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4381 let end = highlight
4382 .range
4383 .end
4384 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4385 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4386 continue;
4387 }
4388
4389 let range = Anchor {
4390 buffer_id,
4391 excerpt_id,
4392 text_anchor: start,
4393 }..Anchor {
4394 buffer_id,
4395 excerpt_id,
4396 text_anchor: end,
4397 };
4398 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4399 write_ranges.push(range);
4400 } else {
4401 read_ranges.push(range);
4402 }
4403 }
4404 }
4405
4406 this.highlight_background::<DocumentHighlightRead>(
4407 &read_ranges,
4408 |theme| theme.editor_document_highlight_read_background,
4409 cx,
4410 );
4411 this.highlight_background::<DocumentHighlightWrite>(
4412 &write_ranges,
4413 |theme| theme.editor_document_highlight_write_background,
4414 cx,
4415 );
4416 cx.notify();
4417 })
4418 .log_err();
4419 }
4420 }));
4421 None
4422 }
4423
4424 pub fn refresh_inline_completion(
4425 &mut self,
4426 debounce: bool,
4427 user_requested: bool,
4428 cx: &mut ViewContext<Self>,
4429 ) -> Option<()> {
4430 let provider = self.inline_completion_provider()?;
4431 let cursor = self.selections.newest_anchor().head();
4432 let (buffer, cursor_buffer_position) =
4433 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4434
4435 if !user_requested
4436 && (!self.enable_inline_completions
4437 || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
4438 || !self.is_focused(cx))
4439 {
4440 self.discard_inline_completion(false, cx);
4441 return None;
4442 }
4443
4444 self.update_visible_inline_completion(cx);
4445 provider.refresh(buffer, cursor_buffer_position, debounce, cx);
4446 Some(())
4447 }
4448
4449 fn cycle_inline_completion(
4450 &mut self,
4451 direction: Direction,
4452 cx: &mut ViewContext<Self>,
4453 ) -> Option<()> {
4454 let provider = self.inline_completion_provider()?;
4455 let cursor = self.selections.newest_anchor().head();
4456 let (buffer, cursor_buffer_position) =
4457 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4458 if !self.enable_inline_completions
4459 || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
4460 {
4461 return None;
4462 }
4463
4464 provider.cycle(buffer, cursor_buffer_position, direction, cx);
4465 self.update_visible_inline_completion(cx);
4466
4467 Some(())
4468 }
4469
4470 pub fn show_inline_completion(&mut self, _: &ShowInlineCompletion, cx: &mut ViewContext<Self>) {
4471 if !self.has_active_inline_completion() {
4472 self.refresh_inline_completion(false, true, cx);
4473 return;
4474 }
4475
4476 self.update_visible_inline_completion(cx);
4477 }
4478
4479 pub fn display_cursor_names(&mut self, _: &DisplayCursorNames, cx: &mut ViewContext<Self>) {
4480 self.show_cursor_names(cx);
4481 }
4482
4483 fn show_cursor_names(&mut self, cx: &mut ViewContext<Self>) {
4484 self.show_cursor_names = true;
4485 cx.notify();
4486 cx.spawn(|this, mut cx| async move {
4487 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
4488 this.update(&mut cx, |this, cx| {
4489 this.show_cursor_names = false;
4490 cx.notify()
4491 })
4492 .ok()
4493 })
4494 .detach();
4495 }
4496
4497 pub fn next_inline_completion(&mut self, _: &NextInlineCompletion, cx: &mut ViewContext<Self>) {
4498 if self.has_active_inline_completion() {
4499 self.cycle_inline_completion(Direction::Next, cx);
4500 } else {
4501 let is_copilot_disabled = self.refresh_inline_completion(false, true, cx).is_none();
4502 if is_copilot_disabled {
4503 cx.propagate();
4504 }
4505 }
4506 }
4507
4508 pub fn previous_inline_completion(
4509 &mut self,
4510 _: &PreviousInlineCompletion,
4511 cx: &mut ViewContext<Self>,
4512 ) {
4513 if self.has_active_inline_completion() {
4514 self.cycle_inline_completion(Direction::Prev, cx);
4515 } else {
4516 let is_copilot_disabled = self.refresh_inline_completion(false, true, cx).is_none();
4517 if is_copilot_disabled {
4518 cx.propagate();
4519 }
4520 }
4521 }
4522
4523 pub fn accept_inline_completion(
4524 &mut self,
4525 _: &AcceptInlineCompletion,
4526 cx: &mut ViewContext<Self>,
4527 ) {
4528 self.hide_context_menu(cx);
4529
4530 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
4531 return;
4532 };
4533
4534 self.report_inline_completion_event(true, cx);
4535
4536 match &active_inline_completion.completion {
4537 InlineCompletion::Move(position) => {
4538 let position = *position;
4539 self.change_selections(Some(Autoscroll::newest()), cx, |selections| {
4540 selections.select_anchor_ranges([position..position]);
4541 });
4542 }
4543 InlineCompletion::Edit(edits) => {
4544 if let Some(provider) = self.inline_completion_provider() {
4545 provider.accept(cx);
4546 }
4547
4548 let snapshot = self.buffer.read(cx).snapshot(cx);
4549 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
4550
4551 self.buffer.update(cx, |buffer, cx| {
4552 buffer.edit(edits.iter().cloned(), None, cx)
4553 });
4554
4555 self.change_selections(None, cx, |s| {
4556 s.select_anchor_ranges([last_edit_end..last_edit_end])
4557 });
4558
4559 self.update_visible_inline_completion(cx);
4560 if self.active_inline_completion.is_none() {
4561 self.refresh_inline_completion(true, true, cx);
4562 }
4563
4564 cx.notify();
4565 }
4566 }
4567 }
4568
4569 pub fn accept_partial_inline_completion(
4570 &mut self,
4571 _: &AcceptPartialInlineCompletion,
4572 cx: &mut ViewContext<Self>,
4573 ) {
4574 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
4575 return;
4576 };
4577 if self.selections.count() != 1 {
4578 return;
4579 }
4580
4581 self.report_inline_completion_event(true, cx);
4582
4583 match &active_inline_completion.completion {
4584 InlineCompletion::Move(position) => {
4585 let position = *position;
4586 self.change_selections(Some(Autoscroll::newest()), cx, |selections| {
4587 selections.select_anchor_ranges([position..position]);
4588 });
4589 }
4590 InlineCompletion::Edit(edits) => {
4591 if edits.len() == 1 && edits[0].0.start == edits[0].0.end {
4592 let text = edits[0].1.as_str();
4593 let mut partial_completion = text
4594 .chars()
4595 .by_ref()
4596 .take_while(|c| c.is_alphabetic())
4597 .collect::<String>();
4598 if partial_completion.is_empty() {
4599 partial_completion = text
4600 .chars()
4601 .by_ref()
4602 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
4603 .collect::<String>();
4604 }
4605
4606 cx.emit(EditorEvent::InputHandled {
4607 utf16_range_to_replace: None,
4608 text: partial_completion.clone().into(),
4609 });
4610
4611 self.insert_with_autoindent_mode(&partial_completion, None, cx);
4612
4613 self.refresh_inline_completion(true, true, cx);
4614 cx.notify();
4615 }
4616 }
4617 }
4618 }
4619
4620 fn discard_inline_completion(
4621 &mut self,
4622 should_report_inline_completion_event: bool,
4623 cx: &mut ViewContext<Self>,
4624 ) -> bool {
4625 if should_report_inline_completion_event {
4626 self.report_inline_completion_event(false, cx);
4627 }
4628
4629 if let Some(provider) = self.inline_completion_provider() {
4630 provider.discard(cx);
4631 }
4632
4633 self.take_active_inline_completion(cx).is_some()
4634 }
4635
4636 fn report_inline_completion_event(&self, accepted: bool, cx: &AppContext) {
4637 let Some(provider) = self.inline_completion_provider() else {
4638 return;
4639 };
4640 let Some(project) = self.project.as_ref() else {
4641 return;
4642 };
4643 let Some((_, buffer, _)) = self
4644 .buffer
4645 .read(cx)
4646 .excerpt_containing(self.selections.newest_anchor().head(), cx)
4647 else {
4648 return;
4649 };
4650
4651 let project = project.read(cx);
4652 let extension = buffer
4653 .read(cx)
4654 .file()
4655 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
4656 project.client().telemetry().report_inline_completion_event(
4657 provider.name().into(),
4658 accepted,
4659 extension,
4660 );
4661 }
4662
4663 pub fn has_active_inline_completion(&self) -> bool {
4664 self.active_inline_completion.is_some()
4665 }
4666
4667 fn take_active_inline_completion(
4668 &mut self,
4669 cx: &mut ViewContext<Self>,
4670 ) -> Option<InlineCompletion> {
4671 let active_inline_completion = self.active_inline_completion.take()?;
4672 self.splice_inlays(active_inline_completion.inlay_ids, Default::default(), cx);
4673 self.clear_highlights::<InlineCompletionHighlight>(cx);
4674 Some(active_inline_completion.completion)
4675 }
4676
4677 fn update_visible_inline_completion(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4678 let selection = self.selections.newest_anchor();
4679 let cursor = selection.head();
4680 let multibuffer = self.buffer.read(cx).snapshot(cx);
4681 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
4682 let excerpt_id = cursor.excerpt_id;
4683
4684 if !offset_selection.is_empty()
4685 || self
4686 .active_inline_completion
4687 .as_ref()
4688 .map_or(false, |completion| {
4689 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
4690 let invalidation_range = invalidation_range.start..=invalidation_range.end;
4691 !invalidation_range.contains(&offset_selection.head())
4692 })
4693 {
4694 self.discard_inline_completion(false, cx);
4695 return None;
4696 }
4697
4698 self.take_active_inline_completion(cx);
4699 let provider = self.inline_completion_provider()?;
4700
4701 let (buffer, cursor_buffer_position) =
4702 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4703
4704 let completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
4705 let edits = completion
4706 .edits
4707 .into_iter()
4708 .map(|(range, new_text)| {
4709 (
4710 multibuffer
4711 .anchor_in_excerpt(excerpt_id, range.start)
4712 .unwrap()
4713 ..multibuffer
4714 .anchor_in_excerpt(excerpt_id, range.end)
4715 .unwrap(),
4716 new_text,
4717 )
4718 })
4719 .collect::<Vec<_>>();
4720 if edits.is_empty() {
4721 return None;
4722 }
4723
4724 let first_edit_start = edits.first().unwrap().0.start;
4725 let edit_start_row = first_edit_start
4726 .to_point(&multibuffer)
4727 .row
4728 .saturating_sub(2);
4729
4730 let last_edit_end = edits.last().unwrap().0.end;
4731 let edit_end_row = cmp::min(
4732 multibuffer.max_point().row,
4733 last_edit_end.to_point(&multibuffer).row + 2,
4734 );
4735
4736 let cursor_row = cursor.to_point(&multibuffer).row;
4737
4738 let mut inlay_ids = Vec::new();
4739 let invalidation_row_range;
4740 let completion;
4741 if cursor_row < edit_start_row {
4742 invalidation_row_range = cursor_row..edit_end_row;
4743 completion = InlineCompletion::Move(first_edit_start);
4744 } else if cursor_row > edit_end_row {
4745 invalidation_row_range = edit_start_row..cursor_row;
4746 completion = InlineCompletion::Move(first_edit_start);
4747 } else {
4748 if edits
4749 .iter()
4750 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
4751 {
4752 let mut inlays = Vec::new();
4753 for (range, new_text) in &edits {
4754 let inlay = Inlay::inline_completion(
4755 post_inc(&mut self.next_inlay_id),
4756 range.start,
4757 new_text.as_str(),
4758 );
4759 inlay_ids.push(inlay.id);
4760 inlays.push(inlay);
4761 }
4762
4763 self.splice_inlays(vec![], inlays, cx);
4764 } else {
4765 let background_color = cx.theme().status().deleted_background;
4766 self.highlight_text::<InlineCompletionHighlight>(
4767 edits.iter().map(|(range, _)| range.clone()).collect(),
4768 HighlightStyle {
4769 background_color: Some(background_color),
4770 ..Default::default()
4771 },
4772 cx,
4773 );
4774 }
4775
4776 invalidation_row_range = edit_start_row..edit_end_row;
4777 completion = InlineCompletion::Edit(edits);
4778 };
4779
4780 let invalidation_range = multibuffer
4781 .anchor_before(Point::new(invalidation_row_range.start, 0))
4782 ..multibuffer.anchor_after(Point::new(
4783 invalidation_row_range.end,
4784 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
4785 ));
4786
4787 self.active_inline_completion = Some(InlineCompletionState {
4788 inlay_ids,
4789 completion,
4790 invalidation_range,
4791 });
4792 cx.notify();
4793
4794 Some(())
4795 }
4796
4797 fn inline_completion_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
4798 Some(self.inline_completion_provider.as_ref()?.provider.clone())
4799 }
4800
4801 fn render_code_actions_indicator(
4802 &self,
4803 _style: &EditorStyle,
4804 row: DisplayRow,
4805 is_active: bool,
4806 cx: &mut ViewContext<Self>,
4807 ) -> Option<IconButton> {
4808 if self.available_code_actions.is_some() {
4809 Some(
4810 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
4811 .shape(ui::IconButtonShape::Square)
4812 .icon_size(IconSize::XSmall)
4813 .icon_color(Color::Muted)
4814 .toggle_state(is_active)
4815 .tooltip({
4816 let focus_handle = self.focus_handle.clone();
4817 move |cx| {
4818 Tooltip::for_action_in(
4819 "Toggle Code Actions",
4820 &ToggleCodeActions {
4821 deployed_from_indicator: None,
4822 },
4823 &focus_handle,
4824 cx,
4825 )
4826 }
4827 })
4828 .on_click(cx.listener(move |editor, _e, cx| {
4829 editor.focus(cx);
4830 editor.toggle_code_actions(
4831 &ToggleCodeActions {
4832 deployed_from_indicator: Some(row),
4833 },
4834 cx,
4835 );
4836 })),
4837 )
4838 } else {
4839 None
4840 }
4841 }
4842
4843 fn clear_tasks(&mut self) {
4844 self.tasks.clear()
4845 }
4846
4847 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
4848 if self.tasks.insert(key, value).is_some() {
4849 // This case should hopefully be rare, but just in case...
4850 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
4851 }
4852 }
4853
4854 fn build_tasks_context(
4855 project: &Model<Project>,
4856 buffer: &Model<Buffer>,
4857 buffer_row: u32,
4858 tasks: &Arc<RunnableTasks>,
4859 cx: &mut ViewContext<Self>,
4860 ) -> Task<Option<task::TaskContext>> {
4861 let position = Point::new(buffer_row, tasks.column);
4862 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
4863 let location = Location {
4864 buffer: buffer.clone(),
4865 range: range_start..range_start,
4866 };
4867 // Fill in the environmental variables from the tree-sitter captures
4868 let mut captured_task_variables = TaskVariables::default();
4869 for (capture_name, value) in tasks.extra_variables.clone() {
4870 captured_task_variables.insert(
4871 task::VariableName::Custom(capture_name.into()),
4872 value.clone(),
4873 );
4874 }
4875 project.update(cx, |project, cx| {
4876 project.task_store().update(cx, |task_store, cx| {
4877 task_store.task_context_for_location(captured_task_variables, location, cx)
4878 })
4879 })
4880 }
4881
4882 pub fn spawn_nearest_task(&mut self, action: &SpawnNearestTask, cx: &mut ViewContext<Self>) {
4883 let Some((workspace, _)) = self.workspace.clone() else {
4884 return;
4885 };
4886 let Some(project) = self.project.clone() else {
4887 return;
4888 };
4889
4890 // Try to find a closest, enclosing node using tree-sitter that has a
4891 // task
4892 let Some((buffer, buffer_row, tasks)) = self
4893 .find_enclosing_node_task(cx)
4894 // Or find the task that's closest in row-distance.
4895 .or_else(|| self.find_closest_task(cx))
4896 else {
4897 return;
4898 };
4899
4900 let reveal_strategy = action.reveal;
4901 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
4902 cx.spawn(|_, mut cx| async move {
4903 let context = task_context.await?;
4904 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
4905
4906 let resolved = resolved_task.resolved.as_mut()?;
4907 resolved.reveal = reveal_strategy;
4908
4909 workspace
4910 .update(&mut cx, |workspace, cx| {
4911 workspace::tasks::schedule_resolved_task(
4912 workspace,
4913 task_source_kind,
4914 resolved_task,
4915 false,
4916 cx,
4917 );
4918 })
4919 .ok()
4920 })
4921 .detach();
4922 }
4923
4924 fn find_closest_task(
4925 &mut self,
4926 cx: &mut ViewContext<Self>,
4927 ) -> Option<(Model<Buffer>, u32, Arc<RunnableTasks>)> {
4928 let cursor_row = self.selections.newest_adjusted(cx).head().row;
4929
4930 let ((buffer_id, row), tasks) = self
4931 .tasks
4932 .iter()
4933 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
4934
4935 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
4936 let tasks = Arc::new(tasks.to_owned());
4937 Some((buffer, *row, tasks))
4938 }
4939
4940 fn find_enclosing_node_task(
4941 &mut self,
4942 cx: &mut ViewContext<Self>,
4943 ) -> Option<(Model<Buffer>, u32, Arc<RunnableTasks>)> {
4944 let snapshot = self.buffer.read(cx).snapshot(cx);
4945 let offset = self.selections.newest::<usize>(cx).head();
4946 let excerpt = snapshot.excerpt_containing(offset..offset)?;
4947 let buffer_id = excerpt.buffer().remote_id();
4948
4949 let layer = excerpt.buffer().syntax_layer_at(offset)?;
4950 let mut cursor = layer.node().walk();
4951
4952 while cursor.goto_first_child_for_byte(offset).is_some() {
4953 if cursor.node().end_byte() == offset {
4954 cursor.goto_next_sibling();
4955 }
4956 }
4957
4958 // Ascend to the smallest ancestor that contains the range and has a task.
4959 loop {
4960 let node = cursor.node();
4961 let node_range = node.byte_range();
4962 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
4963
4964 // Check if this node contains our offset
4965 if node_range.start <= offset && node_range.end >= offset {
4966 // If it contains offset, check for task
4967 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
4968 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
4969 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
4970 }
4971 }
4972
4973 if !cursor.goto_parent() {
4974 break;
4975 }
4976 }
4977 None
4978 }
4979
4980 fn render_run_indicator(
4981 &self,
4982 _style: &EditorStyle,
4983 is_active: bool,
4984 row: DisplayRow,
4985 cx: &mut ViewContext<Self>,
4986 ) -> IconButton {
4987 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
4988 .shape(ui::IconButtonShape::Square)
4989 .icon_size(IconSize::XSmall)
4990 .icon_color(Color::Muted)
4991 .toggle_state(is_active)
4992 .on_click(cx.listener(move |editor, _e, cx| {
4993 editor.focus(cx);
4994 editor.toggle_code_actions(
4995 &ToggleCodeActions {
4996 deployed_from_indicator: Some(row),
4997 },
4998 cx,
4999 );
5000 }))
5001 }
5002
5003 pub fn context_menu_visible(&self) -> bool {
5004 self.context_menu
5005 .read()
5006 .as_ref()
5007 .map_or(false, |menu| menu.visible())
5008 }
5009
5010 fn render_context_menu(
5011 &self,
5012 cursor_position: DisplayPoint,
5013 style: &EditorStyle,
5014 max_height: Pixels,
5015 cx: &mut ViewContext<Editor>,
5016 ) -> Option<(ContextMenuOrigin, AnyElement)> {
5017 self.context_menu.read().as_ref().map(|menu| {
5018 menu.render(
5019 cursor_position,
5020 style,
5021 max_height,
5022 self.workspace.as_ref().map(|(w, _)| w.clone()),
5023 cx,
5024 )
5025 })
5026 }
5027
5028 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<CodeContextMenu> {
5029 cx.notify();
5030 self.completion_tasks.clear();
5031 self.context_menu.write().take()
5032 }
5033
5034 fn show_snippet_choices(
5035 &mut self,
5036 choices: &Vec<String>,
5037 selection: Range<Anchor>,
5038 cx: &mut ViewContext<Self>,
5039 ) {
5040 if selection.start.buffer_id.is_none() {
5041 return;
5042 }
5043 let buffer_id = selection.start.buffer_id.unwrap();
5044 let buffer = self.buffer().read(cx).buffer(buffer_id);
5045 let id = post_inc(&mut self.next_completion_id);
5046
5047 if let Some(buffer) = buffer {
5048 *self.context_menu.write() = Some(CodeContextMenu::Completions(
5049 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
5050 ));
5051 }
5052 }
5053
5054 pub fn insert_snippet(
5055 &mut self,
5056 insertion_ranges: &[Range<usize>],
5057 snippet: Snippet,
5058 cx: &mut ViewContext<Self>,
5059 ) -> Result<()> {
5060 struct Tabstop<T> {
5061 is_end_tabstop: bool,
5062 ranges: Vec<Range<T>>,
5063 choices: Option<Vec<String>>,
5064 }
5065
5066 let tabstops = self.buffer.update(cx, |buffer, cx| {
5067 let snippet_text: Arc<str> = snippet.text.clone().into();
5068 buffer.edit(
5069 insertion_ranges
5070 .iter()
5071 .cloned()
5072 .map(|range| (range, snippet_text.clone())),
5073 Some(AutoindentMode::EachLine),
5074 cx,
5075 );
5076
5077 let snapshot = &*buffer.read(cx);
5078 let snippet = &snippet;
5079 snippet
5080 .tabstops
5081 .iter()
5082 .map(|tabstop| {
5083 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
5084 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
5085 });
5086 let mut tabstop_ranges = tabstop
5087 .ranges
5088 .iter()
5089 .flat_map(|tabstop_range| {
5090 let mut delta = 0_isize;
5091 insertion_ranges.iter().map(move |insertion_range| {
5092 let insertion_start = insertion_range.start as isize + delta;
5093 delta +=
5094 snippet.text.len() as isize - insertion_range.len() as isize;
5095
5096 let start = ((insertion_start + tabstop_range.start) as usize)
5097 .min(snapshot.len());
5098 let end = ((insertion_start + tabstop_range.end) as usize)
5099 .min(snapshot.len());
5100 snapshot.anchor_before(start)..snapshot.anchor_after(end)
5101 })
5102 })
5103 .collect::<Vec<_>>();
5104 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
5105
5106 Tabstop {
5107 is_end_tabstop,
5108 ranges: tabstop_ranges,
5109 choices: tabstop.choices.clone(),
5110 }
5111 })
5112 .collect::<Vec<_>>()
5113 });
5114 if let Some(tabstop) = tabstops.first() {
5115 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5116 s.select_ranges(tabstop.ranges.iter().cloned());
5117 });
5118
5119 if let Some(choices) = &tabstop.choices {
5120 if let Some(selection) = tabstop.ranges.first() {
5121 self.show_snippet_choices(choices, selection.clone(), cx)
5122 }
5123 }
5124
5125 // If we're already at the last tabstop and it's at the end of the snippet,
5126 // we're done, we don't need to keep the state around.
5127 if !tabstop.is_end_tabstop {
5128 let choices = tabstops
5129 .iter()
5130 .map(|tabstop| tabstop.choices.clone())
5131 .collect();
5132
5133 let ranges = tabstops
5134 .into_iter()
5135 .map(|tabstop| tabstop.ranges)
5136 .collect::<Vec<_>>();
5137
5138 self.snippet_stack.push(SnippetState {
5139 active_index: 0,
5140 ranges,
5141 choices,
5142 });
5143 }
5144
5145 // Check whether the just-entered snippet ends with an auto-closable bracket.
5146 if self.autoclose_regions.is_empty() {
5147 let snapshot = self.buffer.read(cx).snapshot(cx);
5148 for selection in &mut self.selections.all::<Point>(cx) {
5149 let selection_head = selection.head();
5150 let Some(scope) = snapshot.language_scope_at(selection_head) else {
5151 continue;
5152 };
5153
5154 let mut bracket_pair = None;
5155 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
5156 let prev_chars = snapshot
5157 .reversed_chars_at(selection_head)
5158 .collect::<String>();
5159 for (pair, enabled) in scope.brackets() {
5160 if enabled
5161 && pair.close
5162 && prev_chars.starts_with(pair.start.as_str())
5163 && next_chars.starts_with(pair.end.as_str())
5164 {
5165 bracket_pair = Some(pair.clone());
5166 break;
5167 }
5168 }
5169 if let Some(pair) = bracket_pair {
5170 let start = snapshot.anchor_after(selection_head);
5171 let end = snapshot.anchor_after(selection_head);
5172 self.autoclose_regions.push(AutocloseRegion {
5173 selection_id: selection.id,
5174 range: start..end,
5175 pair,
5176 });
5177 }
5178 }
5179 }
5180 }
5181 Ok(())
5182 }
5183
5184 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5185 self.move_to_snippet_tabstop(Bias::Right, cx)
5186 }
5187
5188 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5189 self.move_to_snippet_tabstop(Bias::Left, cx)
5190 }
5191
5192 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
5193 if let Some(mut snippet) = self.snippet_stack.pop() {
5194 match bias {
5195 Bias::Left => {
5196 if snippet.active_index > 0 {
5197 snippet.active_index -= 1;
5198 } else {
5199 self.snippet_stack.push(snippet);
5200 return false;
5201 }
5202 }
5203 Bias::Right => {
5204 if snippet.active_index + 1 < snippet.ranges.len() {
5205 snippet.active_index += 1;
5206 } else {
5207 self.snippet_stack.push(snippet);
5208 return false;
5209 }
5210 }
5211 }
5212 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
5213 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5214 s.select_anchor_ranges(current_ranges.iter().cloned())
5215 });
5216
5217 if let Some(choices) = &snippet.choices[snippet.active_index] {
5218 if let Some(selection) = current_ranges.first() {
5219 self.show_snippet_choices(&choices, selection.clone(), cx);
5220 }
5221 }
5222
5223 // If snippet state is not at the last tabstop, push it back on the stack
5224 if snippet.active_index + 1 < snippet.ranges.len() {
5225 self.snippet_stack.push(snippet);
5226 }
5227 return true;
5228 }
5229 }
5230
5231 false
5232 }
5233
5234 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
5235 self.transact(cx, |this, cx| {
5236 this.select_all(&SelectAll, cx);
5237 this.insert("", cx);
5238 });
5239 }
5240
5241 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
5242 self.transact(cx, |this, cx| {
5243 this.select_autoclose_pair(cx);
5244 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
5245 if !this.linked_edit_ranges.is_empty() {
5246 let selections = this.selections.all::<MultiBufferPoint>(cx);
5247 let snapshot = this.buffer.read(cx).snapshot(cx);
5248
5249 for selection in selections.iter() {
5250 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
5251 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
5252 if selection_start.buffer_id != selection_end.buffer_id {
5253 continue;
5254 }
5255 if let Some(ranges) =
5256 this.linked_editing_ranges_for(selection_start..selection_end, cx)
5257 {
5258 for (buffer, entries) in ranges {
5259 linked_ranges.entry(buffer).or_default().extend(entries);
5260 }
5261 }
5262 }
5263 }
5264
5265 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
5266 if !this.selections.line_mode {
5267 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
5268 for selection in &mut selections {
5269 if selection.is_empty() {
5270 let old_head = selection.head();
5271 let mut new_head =
5272 movement::left(&display_map, old_head.to_display_point(&display_map))
5273 .to_point(&display_map);
5274 if let Some((buffer, line_buffer_range)) = display_map
5275 .buffer_snapshot
5276 .buffer_line_for_row(MultiBufferRow(old_head.row))
5277 {
5278 let indent_size =
5279 buffer.indent_size_for_line(line_buffer_range.start.row);
5280 let indent_len = match indent_size.kind {
5281 IndentKind::Space => {
5282 buffer.settings_at(line_buffer_range.start, cx).tab_size
5283 }
5284 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
5285 };
5286 if old_head.column <= indent_size.len && old_head.column > 0 {
5287 let indent_len = indent_len.get();
5288 new_head = cmp::min(
5289 new_head,
5290 MultiBufferPoint::new(
5291 old_head.row,
5292 ((old_head.column - 1) / indent_len) * indent_len,
5293 ),
5294 );
5295 }
5296 }
5297
5298 selection.set_head(new_head, SelectionGoal::None);
5299 }
5300 }
5301 }
5302
5303 this.signature_help_state.set_backspace_pressed(true);
5304 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5305 this.insert("", cx);
5306 let empty_str: Arc<str> = Arc::from("");
5307 for (buffer, edits) in linked_ranges {
5308 let snapshot = buffer.read(cx).snapshot();
5309 use text::ToPoint as TP;
5310
5311 let edits = edits
5312 .into_iter()
5313 .map(|range| {
5314 let end_point = TP::to_point(&range.end, &snapshot);
5315 let mut start_point = TP::to_point(&range.start, &snapshot);
5316
5317 if end_point == start_point {
5318 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
5319 .saturating_sub(1);
5320 start_point = TP::to_point(&offset, &snapshot);
5321 };
5322
5323 (start_point..end_point, empty_str.clone())
5324 })
5325 .sorted_by_key(|(range, _)| range.start)
5326 .collect::<Vec<_>>();
5327 buffer.update(cx, |this, cx| {
5328 this.edit(edits, None, cx);
5329 })
5330 }
5331 this.refresh_inline_completion(true, false, cx);
5332 linked_editing_ranges::refresh_linked_ranges(this, cx);
5333 });
5334 }
5335
5336 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
5337 self.transact(cx, |this, cx| {
5338 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5339 let line_mode = s.line_mode;
5340 s.move_with(|map, selection| {
5341 if selection.is_empty() && !line_mode {
5342 let cursor = movement::right(map, selection.head());
5343 selection.end = cursor;
5344 selection.reversed = true;
5345 selection.goal = SelectionGoal::None;
5346 }
5347 })
5348 });
5349 this.insert("", cx);
5350 this.refresh_inline_completion(true, false, cx);
5351 });
5352 }
5353
5354 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
5355 if self.move_to_prev_snippet_tabstop(cx) {
5356 return;
5357 }
5358
5359 self.outdent(&Outdent, cx);
5360 }
5361
5362 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
5363 if self.move_to_next_snippet_tabstop(cx) || self.read_only(cx) {
5364 return;
5365 }
5366
5367 let mut selections = self.selections.all_adjusted(cx);
5368 let buffer = self.buffer.read(cx);
5369 let snapshot = buffer.snapshot(cx);
5370 let rows_iter = selections.iter().map(|s| s.head().row);
5371 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
5372
5373 let mut edits = Vec::new();
5374 let mut prev_edited_row = 0;
5375 let mut row_delta = 0;
5376 for selection in &mut selections {
5377 if selection.start.row != prev_edited_row {
5378 row_delta = 0;
5379 }
5380 prev_edited_row = selection.end.row;
5381
5382 // If the selection is non-empty, then increase the indentation of the selected lines.
5383 if !selection.is_empty() {
5384 row_delta =
5385 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5386 continue;
5387 }
5388
5389 // If the selection is empty and the cursor is in the leading whitespace before the
5390 // suggested indentation, then auto-indent the line.
5391 let cursor = selection.head();
5392 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
5393 if let Some(suggested_indent) =
5394 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
5395 {
5396 if cursor.column < suggested_indent.len
5397 && cursor.column <= current_indent.len
5398 && current_indent.len <= suggested_indent.len
5399 {
5400 selection.start = Point::new(cursor.row, suggested_indent.len);
5401 selection.end = selection.start;
5402 if row_delta == 0 {
5403 edits.extend(Buffer::edit_for_indent_size_adjustment(
5404 cursor.row,
5405 current_indent,
5406 suggested_indent,
5407 ));
5408 row_delta = suggested_indent.len - current_indent.len;
5409 }
5410 continue;
5411 }
5412 }
5413
5414 // Otherwise, insert a hard or soft tab.
5415 let settings = buffer.settings_at(cursor, cx);
5416 let tab_size = if settings.hard_tabs {
5417 IndentSize::tab()
5418 } else {
5419 let tab_size = settings.tab_size.get();
5420 let char_column = snapshot
5421 .text_for_range(Point::new(cursor.row, 0)..cursor)
5422 .flat_map(str::chars)
5423 .count()
5424 + row_delta as usize;
5425 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
5426 IndentSize::spaces(chars_to_next_tab_stop)
5427 };
5428 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
5429 selection.end = selection.start;
5430 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
5431 row_delta += tab_size.len;
5432 }
5433
5434 self.transact(cx, |this, cx| {
5435 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5436 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5437 this.refresh_inline_completion(true, false, cx);
5438 });
5439 }
5440
5441 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
5442 if self.read_only(cx) {
5443 return;
5444 }
5445 let mut selections = self.selections.all::<Point>(cx);
5446 let mut prev_edited_row = 0;
5447 let mut row_delta = 0;
5448 let mut edits = Vec::new();
5449 let buffer = self.buffer.read(cx);
5450 let snapshot = buffer.snapshot(cx);
5451 for selection in &mut selections {
5452 if selection.start.row != prev_edited_row {
5453 row_delta = 0;
5454 }
5455 prev_edited_row = selection.end.row;
5456
5457 row_delta =
5458 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5459 }
5460
5461 self.transact(cx, |this, cx| {
5462 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5463 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5464 });
5465 }
5466
5467 fn indent_selection(
5468 buffer: &MultiBuffer,
5469 snapshot: &MultiBufferSnapshot,
5470 selection: &mut Selection<Point>,
5471 edits: &mut Vec<(Range<Point>, String)>,
5472 delta_for_start_row: u32,
5473 cx: &AppContext,
5474 ) -> u32 {
5475 let settings = buffer.settings_at(selection.start, cx);
5476 let tab_size = settings.tab_size.get();
5477 let indent_kind = if settings.hard_tabs {
5478 IndentKind::Tab
5479 } else {
5480 IndentKind::Space
5481 };
5482 let mut start_row = selection.start.row;
5483 let mut end_row = selection.end.row + 1;
5484
5485 // If a selection ends at the beginning of a line, don't indent
5486 // that last line.
5487 if selection.end.column == 0 && selection.end.row > selection.start.row {
5488 end_row -= 1;
5489 }
5490
5491 // Avoid re-indenting a row that has already been indented by a
5492 // previous selection, but still update this selection's column
5493 // to reflect that indentation.
5494 if delta_for_start_row > 0 {
5495 start_row += 1;
5496 selection.start.column += delta_for_start_row;
5497 if selection.end.row == selection.start.row {
5498 selection.end.column += delta_for_start_row;
5499 }
5500 }
5501
5502 let mut delta_for_end_row = 0;
5503 let has_multiple_rows = start_row + 1 != end_row;
5504 for row in start_row..end_row {
5505 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
5506 let indent_delta = match (current_indent.kind, indent_kind) {
5507 (IndentKind::Space, IndentKind::Space) => {
5508 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
5509 IndentSize::spaces(columns_to_next_tab_stop)
5510 }
5511 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
5512 (_, IndentKind::Tab) => IndentSize::tab(),
5513 };
5514
5515 let start = if has_multiple_rows || current_indent.len < selection.start.column {
5516 0
5517 } else {
5518 selection.start.column
5519 };
5520 let row_start = Point::new(row, start);
5521 edits.push((
5522 row_start..row_start,
5523 indent_delta.chars().collect::<String>(),
5524 ));
5525
5526 // Update this selection's endpoints to reflect the indentation.
5527 if row == selection.start.row {
5528 selection.start.column += indent_delta.len;
5529 }
5530 if row == selection.end.row {
5531 selection.end.column += indent_delta.len;
5532 delta_for_end_row = indent_delta.len;
5533 }
5534 }
5535
5536 if selection.start.row == selection.end.row {
5537 delta_for_start_row + delta_for_end_row
5538 } else {
5539 delta_for_end_row
5540 }
5541 }
5542
5543 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
5544 if self.read_only(cx) {
5545 return;
5546 }
5547 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5548 let selections = self.selections.all::<Point>(cx);
5549 let mut deletion_ranges = Vec::new();
5550 let mut last_outdent = None;
5551 {
5552 let buffer = self.buffer.read(cx);
5553 let snapshot = buffer.snapshot(cx);
5554 for selection in &selections {
5555 let settings = buffer.settings_at(selection.start, cx);
5556 let tab_size = settings.tab_size.get();
5557 let mut rows = selection.spanned_rows(false, &display_map);
5558
5559 // Avoid re-outdenting a row that has already been outdented by a
5560 // previous selection.
5561 if let Some(last_row) = last_outdent {
5562 if last_row == rows.start {
5563 rows.start = rows.start.next_row();
5564 }
5565 }
5566 let has_multiple_rows = rows.len() > 1;
5567 for row in rows.iter_rows() {
5568 let indent_size = snapshot.indent_size_for_line(row);
5569 if indent_size.len > 0 {
5570 let deletion_len = match indent_size.kind {
5571 IndentKind::Space => {
5572 let columns_to_prev_tab_stop = indent_size.len % tab_size;
5573 if columns_to_prev_tab_stop == 0 {
5574 tab_size
5575 } else {
5576 columns_to_prev_tab_stop
5577 }
5578 }
5579 IndentKind::Tab => 1,
5580 };
5581 let start = if has_multiple_rows
5582 || deletion_len > selection.start.column
5583 || indent_size.len < selection.start.column
5584 {
5585 0
5586 } else {
5587 selection.start.column - deletion_len
5588 };
5589 deletion_ranges.push(
5590 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
5591 );
5592 last_outdent = Some(row);
5593 }
5594 }
5595 }
5596 }
5597
5598 self.transact(cx, |this, cx| {
5599 this.buffer.update(cx, |buffer, cx| {
5600 let empty_str: Arc<str> = Arc::default();
5601 buffer.edit(
5602 deletion_ranges
5603 .into_iter()
5604 .map(|range| (range, empty_str.clone())),
5605 None,
5606 cx,
5607 );
5608 });
5609 let selections = this.selections.all::<usize>(cx);
5610 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5611 });
5612 }
5613
5614 pub fn autoindent(&mut self, _: &AutoIndent, cx: &mut ViewContext<Self>) {
5615 if self.read_only(cx) {
5616 return;
5617 }
5618 let selections = self
5619 .selections
5620 .all::<usize>(cx)
5621 .into_iter()
5622 .map(|s| s.range());
5623
5624 self.transact(cx, |this, cx| {
5625 this.buffer.update(cx, |buffer, cx| {
5626 buffer.autoindent_ranges(selections, cx);
5627 });
5628 let selections = this.selections.all::<usize>(cx);
5629 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5630 });
5631 }
5632
5633 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
5634 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5635 let selections = self.selections.all::<Point>(cx);
5636
5637 let mut new_cursors = Vec::new();
5638 let mut edit_ranges = Vec::new();
5639 let mut selections = selections.iter().peekable();
5640 while let Some(selection) = selections.next() {
5641 let mut rows = selection.spanned_rows(false, &display_map);
5642 let goal_display_column = selection.head().to_display_point(&display_map).column();
5643
5644 // Accumulate contiguous regions of rows that we want to delete.
5645 while let Some(next_selection) = selections.peek() {
5646 let next_rows = next_selection.spanned_rows(false, &display_map);
5647 if next_rows.start <= rows.end {
5648 rows.end = next_rows.end;
5649 selections.next().unwrap();
5650 } else {
5651 break;
5652 }
5653 }
5654
5655 let buffer = &display_map.buffer_snapshot;
5656 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
5657 let edit_end;
5658 let cursor_buffer_row;
5659 if buffer.max_point().row >= rows.end.0 {
5660 // If there's a line after the range, delete the \n from the end of the row range
5661 // and position the cursor on the next line.
5662 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
5663 cursor_buffer_row = rows.end;
5664 } else {
5665 // If there isn't a line after the range, delete the \n from the line before the
5666 // start of the row range and position the cursor there.
5667 edit_start = edit_start.saturating_sub(1);
5668 edit_end = buffer.len();
5669 cursor_buffer_row = rows.start.previous_row();
5670 }
5671
5672 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
5673 *cursor.column_mut() =
5674 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
5675
5676 new_cursors.push((
5677 selection.id,
5678 buffer.anchor_after(cursor.to_point(&display_map)),
5679 ));
5680 edit_ranges.push(edit_start..edit_end);
5681 }
5682
5683 self.transact(cx, |this, cx| {
5684 let buffer = this.buffer.update(cx, |buffer, cx| {
5685 let empty_str: Arc<str> = Arc::default();
5686 buffer.edit(
5687 edit_ranges
5688 .into_iter()
5689 .map(|range| (range, empty_str.clone())),
5690 None,
5691 cx,
5692 );
5693 buffer.snapshot(cx)
5694 });
5695 let new_selections = new_cursors
5696 .into_iter()
5697 .map(|(id, cursor)| {
5698 let cursor = cursor.to_point(&buffer);
5699 Selection {
5700 id,
5701 start: cursor,
5702 end: cursor,
5703 reversed: false,
5704 goal: SelectionGoal::None,
5705 }
5706 })
5707 .collect();
5708
5709 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5710 s.select(new_selections);
5711 });
5712 });
5713 }
5714
5715 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
5716 if self.read_only(cx) {
5717 return;
5718 }
5719 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
5720 for selection in self.selections.all::<Point>(cx) {
5721 let start = MultiBufferRow(selection.start.row);
5722 // Treat single line selections as if they include the next line. Otherwise this action
5723 // would do nothing for single line selections individual cursors.
5724 let end = if selection.start.row == selection.end.row {
5725 MultiBufferRow(selection.start.row + 1)
5726 } else {
5727 MultiBufferRow(selection.end.row)
5728 };
5729
5730 if let Some(last_row_range) = row_ranges.last_mut() {
5731 if start <= last_row_range.end {
5732 last_row_range.end = end;
5733 continue;
5734 }
5735 }
5736 row_ranges.push(start..end);
5737 }
5738
5739 let snapshot = self.buffer.read(cx).snapshot(cx);
5740 let mut cursor_positions = Vec::new();
5741 for row_range in &row_ranges {
5742 let anchor = snapshot.anchor_before(Point::new(
5743 row_range.end.previous_row().0,
5744 snapshot.line_len(row_range.end.previous_row()),
5745 ));
5746 cursor_positions.push(anchor..anchor);
5747 }
5748
5749 self.transact(cx, |this, cx| {
5750 for row_range in row_ranges.into_iter().rev() {
5751 for row in row_range.iter_rows().rev() {
5752 let end_of_line = Point::new(row.0, snapshot.line_len(row));
5753 let next_line_row = row.next_row();
5754 let indent = snapshot.indent_size_for_line(next_line_row);
5755 let start_of_next_line = Point::new(next_line_row.0, indent.len);
5756
5757 let replace = if snapshot.line_len(next_line_row) > indent.len {
5758 " "
5759 } else {
5760 ""
5761 };
5762
5763 this.buffer.update(cx, |buffer, cx| {
5764 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
5765 });
5766 }
5767 }
5768
5769 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5770 s.select_anchor_ranges(cursor_positions)
5771 });
5772 });
5773 }
5774
5775 pub fn sort_lines_case_sensitive(
5776 &mut self,
5777 _: &SortLinesCaseSensitive,
5778 cx: &mut ViewContext<Self>,
5779 ) {
5780 self.manipulate_lines(cx, |lines| lines.sort())
5781 }
5782
5783 pub fn sort_lines_case_insensitive(
5784 &mut self,
5785 _: &SortLinesCaseInsensitive,
5786 cx: &mut ViewContext<Self>,
5787 ) {
5788 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
5789 }
5790
5791 pub fn unique_lines_case_insensitive(
5792 &mut self,
5793 _: &UniqueLinesCaseInsensitive,
5794 cx: &mut ViewContext<Self>,
5795 ) {
5796 self.manipulate_lines(cx, |lines| {
5797 let mut seen = HashSet::default();
5798 lines.retain(|line| seen.insert(line.to_lowercase()));
5799 })
5800 }
5801
5802 pub fn unique_lines_case_sensitive(
5803 &mut self,
5804 _: &UniqueLinesCaseSensitive,
5805 cx: &mut ViewContext<Self>,
5806 ) {
5807 self.manipulate_lines(cx, |lines| {
5808 let mut seen = HashSet::default();
5809 lines.retain(|line| seen.insert(*line));
5810 })
5811 }
5812
5813 pub fn revert_file(&mut self, _: &RevertFile, cx: &mut ViewContext<Self>) {
5814 let mut revert_changes = HashMap::default();
5815 let snapshot = self.snapshot(cx);
5816 for hunk in hunks_for_ranges(
5817 Some(Point::zero()..snapshot.buffer_snapshot.max_point()).into_iter(),
5818 &snapshot,
5819 ) {
5820 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
5821 }
5822 if !revert_changes.is_empty() {
5823 self.transact(cx, |editor, cx| {
5824 editor.revert(revert_changes, cx);
5825 });
5826 }
5827 }
5828
5829 pub fn reload_file(&mut self, _: &ReloadFile, cx: &mut ViewContext<Self>) {
5830 let Some(project) = self.project.clone() else {
5831 return;
5832 };
5833 self.reload(project, cx).detach_and_notify_err(cx);
5834 }
5835
5836 pub fn revert_selected_hunks(&mut self, _: &RevertSelectedHunks, cx: &mut ViewContext<Self>) {
5837 let revert_changes = self.gather_revert_changes(&self.selections.all(cx), cx);
5838 if !revert_changes.is_empty() {
5839 self.transact(cx, |editor, cx| {
5840 editor.revert(revert_changes, cx);
5841 });
5842 }
5843 }
5844
5845 fn revert_hunk(&mut self, hunk: HoveredHunk, cx: &mut ViewContext<Editor>) {
5846 let snapshot = self.buffer.read(cx).read(cx);
5847 if let Some(hunk) = crate::hunk_diff::to_diff_hunk(&hunk, &snapshot) {
5848 drop(snapshot);
5849 let mut revert_changes = HashMap::default();
5850 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
5851 if !revert_changes.is_empty() {
5852 self.revert(revert_changes, cx)
5853 }
5854 }
5855 }
5856
5857 pub fn open_active_item_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
5858 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
5859 let project_path = buffer.read(cx).project_path(cx)?;
5860 let project = self.project.as_ref()?.read(cx);
5861 let entry = project.entry_for_path(&project_path, cx)?;
5862 let parent = match &entry.canonical_path {
5863 Some(canonical_path) => canonical_path.to_path_buf(),
5864 None => project.absolute_path(&project_path, cx)?,
5865 }
5866 .parent()?
5867 .to_path_buf();
5868 Some(parent)
5869 }) {
5870 cx.dispatch_action(OpenTerminal { working_directory }.boxed_clone());
5871 }
5872 }
5873
5874 fn gather_revert_changes(
5875 &mut self,
5876 selections: &[Selection<Point>],
5877 cx: &mut ViewContext<'_, Editor>,
5878 ) -> HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>> {
5879 let mut revert_changes = HashMap::default();
5880 let snapshot = self.snapshot(cx);
5881 for hunk in hunks_for_selections(&snapshot, selections) {
5882 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
5883 }
5884 revert_changes
5885 }
5886
5887 pub fn prepare_revert_change(
5888 &mut self,
5889 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
5890 hunk: &MultiBufferDiffHunk,
5891 cx: &AppContext,
5892 ) -> Option<()> {
5893 let buffer = self.buffer.read(cx).buffer(hunk.buffer_id)?;
5894 let buffer = buffer.read(cx);
5895 let change_set = &self.diff_map.diff_bases.get(&hunk.buffer_id)?.change_set;
5896 let original_text = change_set
5897 .read(cx)
5898 .base_text
5899 .as_ref()?
5900 .read(cx)
5901 .as_rope()
5902 .slice(hunk.diff_base_byte_range.clone());
5903 let buffer_snapshot = buffer.snapshot();
5904 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
5905 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
5906 probe
5907 .0
5908 .start
5909 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
5910 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
5911 }) {
5912 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
5913 Some(())
5914 } else {
5915 None
5916 }
5917 }
5918
5919 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
5920 self.manipulate_lines(cx, |lines| lines.reverse())
5921 }
5922
5923 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
5924 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
5925 }
5926
5927 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
5928 where
5929 Fn: FnMut(&mut Vec<&str>),
5930 {
5931 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5932 let buffer = self.buffer.read(cx).snapshot(cx);
5933
5934 let mut edits = Vec::new();
5935
5936 let selections = self.selections.all::<Point>(cx);
5937 let mut selections = selections.iter().peekable();
5938 let mut contiguous_row_selections = Vec::new();
5939 let mut new_selections = Vec::new();
5940 let mut added_lines = 0;
5941 let mut removed_lines = 0;
5942
5943 while let Some(selection) = selections.next() {
5944 let (start_row, end_row) = consume_contiguous_rows(
5945 &mut contiguous_row_selections,
5946 selection,
5947 &display_map,
5948 &mut selections,
5949 );
5950
5951 let start_point = Point::new(start_row.0, 0);
5952 let end_point = Point::new(
5953 end_row.previous_row().0,
5954 buffer.line_len(end_row.previous_row()),
5955 );
5956 let text = buffer
5957 .text_for_range(start_point..end_point)
5958 .collect::<String>();
5959
5960 let mut lines = text.split('\n').collect_vec();
5961
5962 let lines_before = lines.len();
5963 callback(&mut lines);
5964 let lines_after = lines.len();
5965
5966 edits.push((start_point..end_point, lines.join("\n")));
5967
5968 // Selections must change based on added and removed line count
5969 let start_row =
5970 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
5971 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
5972 new_selections.push(Selection {
5973 id: selection.id,
5974 start: start_row,
5975 end: end_row,
5976 goal: SelectionGoal::None,
5977 reversed: selection.reversed,
5978 });
5979
5980 if lines_after > lines_before {
5981 added_lines += lines_after - lines_before;
5982 } else if lines_before > lines_after {
5983 removed_lines += lines_before - lines_after;
5984 }
5985 }
5986
5987 self.transact(cx, |this, cx| {
5988 let buffer = this.buffer.update(cx, |buffer, cx| {
5989 buffer.edit(edits, None, cx);
5990 buffer.snapshot(cx)
5991 });
5992
5993 // Recalculate offsets on newly edited buffer
5994 let new_selections = new_selections
5995 .iter()
5996 .map(|s| {
5997 let start_point = Point::new(s.start.0, 0);
5998 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
5999 Selection {
6000 id: s.id,
6001 start: buffer.point_to_offset(start_point),
6002 end: buffer.point_to_offset(end_point),
6003 goal: s.goal,
6004 reversed: s.reversed,
6005 }
6006 })
6007 .collect();
6008
6009 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6010 s.select(new_selections);
6011 });
6012
6013 this.request_autoscroll(Autoscroll::fit(), cx);
6014 });
6015 }
6016
6017 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
6018 self.manipulate_text(cx, |text| text.to_uppercase())
6019 }
6020
6021 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
6022 self.manipulate_text(cx, |text| text.to_lowercase())
6023 }
6024
6025 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
6026 self.manipulate_text(cx, |text| {
6027 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6028 // https://github.com/rutrum/convert-case/issues/16
6029 text.split('\n')
6030 .map(|line| line.to_case(Case::Title))
6031 .join("\n")
6032 })
6033 }
6034
6035 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
6036 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
6037 }
6038
6039 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
6040 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
6041 }
6042
6043 pub fn convert_to_upper_camel_case(
6044 &mut self,
6045 _: &ConvertToUpperCamelCase,
6046 cx: &mut ViewContext<Self>,
6047 ) {
6048 self.manipulate_text(cx, |text| {
6049 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6050 // https://github.com/rutrum/convert-case/issues/16
6051 text.split('\n')
6052 .map(|line| line.to_case(Case::UpperCamel))
6053 .join("\n")
6054 })
6055 }
6056
6057 pub fn convert_to_lower_camel_case(
6058 &mut self,
6059 _: &ConvertToLowerCamelCase,
6060 cx: &mut ViewContext<Self>,
6061 ) {
6062 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
6063 }
6064
6065 pub fn convert_to_opposite_case(
6066 &mut self,
6067 _: &ConvertToOppositeCase,
6068 cx: &mut ViewContext<Self>,
6069 ) {
6070 self.manipulate_text(cx, |text| {
6071 text.chars()
6072 .fold(String::with_capacity(text.len()), |mut t, c| {
6073 if c.is_uppercase() {
6074 t.extend(c.to_lowercase());
6075 } else {
6076 t.extend(c.to_uppercase());
6077 }
6078 t
6079 })
6080 })
6081 }
6082
6083 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6084 where
6085 Fn: FnMut(&str) -> String,
6086 {
6087 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6088 let buffer = self.buffer.read(cx).snapshot(cx);
6089
6090 let mut new_selections = Vec::new();
6091 let mut edits = Vec::new();
6092 let mut selection_adjustment = 0i32;
6093
6094 for selection in self.selections.all::<usize>(cx) {
6095 let selection_is_empty = selection.is_empty();
6096
6097 let (start, end) = if selection_is_empty {
6098 let word_range = movement::surrounding_word(
6099 &display_map,
6100 selection.start.to_display_point(&display_map),
6101 );
6102 let start = word_range.start.to_offset(&display_map, Bias::Left);
6103 let end = word_range.end.to_offset(&display_map, Bias::Left);
6104 (start, end)
6105 } else {
6106 (selection.start, selection.end)
6107 };
6108
6109 let text = buffer.text_for_range(start..end).collect::<String>();
6110 let old_length = text.len() as i32;
6111 let text = callback(&text);
6112
6113 new_selections.push(Selection {
6114 start: (start as i32 - selection_adjustment) as usize,
6115 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
6116 goal: SelectionGoal::None,
6117 ..selection
6118 });
6119
6120 selection_adjustment += old_length - text.len() as i32;
6121
6122 edits.push((start..end, text));
6123 }
6124
6125 self.transact(cx, |this, cx| {
6126 this.buffer.update(cx, |buffer, cx| {
6127 buffer.edit(edits, None, cx);
6128 });
6129
6130 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6131 s.select(new_selections);
6132 });
6133
6134 this.request_autoscroll(Autoscroll::fit(), cx);
6135 });
6136 }
6137
6138 pub fn duplicate(&mut self, upwards: bool, whole_lines: bool, cx: &mut ViewContext<Self>) {
6139 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6140 let buffer = &display_map.buffer_snapshot;
6141 let selections = self.selections.all::<Point>(cx);
6142
6143 let mut edits = Vec::new();
6144 let mut selections_iter = selections.iter().peekable();
6145 while let Some(selection) = selections_iter.next() {
6146 let mut rows = selection.spanned_rows(false, &display_map);
6147 // duplicate line-wise
6148 if whole_lines || selection.start == selection.end {
6149 // Avoid duplicating the same lines twice.
6150 while let Some(next_selection) = selections_iter.peek() {
6151 let next_rows = next_selection.spanned_rows(false, &display_map);
6152 if next_rows.start < rows.end {
6153 rows.end = next_rows.end;
6154 selections_iter.next().unwrap();
6155 } else {
6156 break;
6157 }
6158 }
6159
6160 // Copy the text from the selected row region and splice it either at the start
6161 // or end of the region.
6162 let start = Point::new(rows.start.0, 0);
6163 let end = Point::new(
6164 rows.end.previous_row().0,
6165 buffer.line_len(rows.end.previous_row()),
6166 );
6167 let text = buffer
6168 .text_for_range(start..end)
6169 .chain(Some("\n"))
6170 .collect::<String>();
6171 let insert_location = if upwards {
6172 Point::new(rows.end.0, 0)
6173 } else {
6174 start
6175 };
6176 edits.push((insert_location..insert_location, text));
6177 } else {
6178 // duplicate character-wise
6179 let start = selection.start;
6180 let end = selection.end;
6181 let text = buffer.text_for_range(start..end).collect::<String>();
6182 edits.push((selection.end..selection.end, text));
6183 }
6184 }
6185
6186 self.transact(cx, |this, cx| {
6187 this.buffer.update(cx, |buffer, cx| {
6188 buffer.edit(edits, None, cx);
6189 });
6190
6191 this.request_autoscroll(Autoscroll::fit(), cx);
6192 });
6193 }
6194
6195 pub fn duplicate_line_up(&mut self, _: &DuplicateLineUp, cx: &mut ViewContext<Self>) {
6196 self.duplicate(true, true, cx);
6197 }
6198
6199 pub fn duplicate_line_down(&mut self, _: &DuplicateLineDown, cx: &mut ViewContext<Self>) {
6200 self.duplicate(false, true, cx);
6201 }
6202
6203 pub fn duplicate_selection(&mut self, _: &DuplicateSelection, cx: &mut ViewContext<Self>) {
6204 self.duplicate(false, false, cx);
6205 }
6206
6207 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
6208 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6209 let buffer = self.buffer.read(cx).snapshot(cx);
6210
6211 let mut edits = Vec::new();
6212 let mut unfold_ranges = Vec::new();
6213 let mut refold_creases = Vec::new();
6214
6215 let selections = self.selections.all::<Point>(cx);
6216 let mut selections = selections.iter().peekable();
6217 let mut contiguous_row_selections = Vec::new();
6218 let mut new_selections = Vec::new();
6219
6220 while let Some(selection) = selections.next() {
6221 // Find all the selections that span a contiguous row range
6222 let (start_row, end_row) = consume_contiguous_rows(
6223 &mut contiguous_row_selections,
6224 selection,
6225 &display_map,
6226 &mut selections,
6227 );
6228
6229 // Move the text spanned by the row range to be before the line preceding the row range
6230 if start_row.0 > 0 {
6231 let range_to_move = Point::new(
6232 start_row.previous_row().0,
6233 buffer.line_len(start_row.previous_row()),
6234 )
6235 ..Point::new(
6236 end_row.previous_row().0,
6237 buffer.line_len(end_row.previous_row()),
6238 );
6239 let insertion_point = display_map
6240 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
6241 .0;
6242
6243 // Don't move lines across excerpts
6244 if buffer
6245 .excerpt_boundaries_in_range((
6246 Bound::Excluded(insertion_point),
6247 Bound::Included(range_to_move.end),
6248 ))
6249 .next()
6250 .is_none()
6251 {
6252 let text = buffer
6253 .text_for_range(range_to_move.clone())
6254 .flat_map(|s| s.chars())
6255 .skip(1)
6256 .chain(['\n'])
6257 .collect::<String>();
6258
6259 edits.push((
6260 buffer.anchor_after(range_to_move.start)
6261 ..buffer.anchor_before(range_to_move.end),
6262 String::new(),
6263 ));
6264 let insertion_anchor = buffer.anchor_after(insertion_point);
6265 edits.push((insertion_anchor..insertion_anchor, text));
6266
6267 let row_delta = range_to_move.start.row - insertion_point.row + 1;
6268
6269 // Move selections up
6270 new_selections.extend(contiguous_row_selections.drain(..).map(
6271 |mut selection| {
6272 selection.start.row -= row_delta;
6273 selection.end.row -= row_delta;
6274 selection
6275 },
6276 ));
6277
6278 // Move folds up
6279 unfold_ranges.push(range_to_move.clone());
6280 for fold in display_map.folds_in_range(
6281 buffer.anchor_before(range_to_move.start)
6282 ..buffer.anchor_after(range_to_move.end),
6283 ) {
6284 let mut start = fold.range.start.to_point(&buffer);
6285 let mut end = fold.range.end.to_point(&buffer);
6286 start.row -= row_delta;
6287 end.row -= row_delta;
6288 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
6289 }
6290 }
6291 }
6292
6293 // If we didn't move line(s), preserve the existing selections
6294 new_selections.append(&mut contiguous_row_selections);
6295 }
6296
6297 self.transact(cx, |this, cx| {
6298 this.unfold_ranges(&unfold_ranges, true, true, cx);
6299 this.buffer.update(cx, |buffer, cx| {
6300 for (range, text) in edits {
6301 buffer.edit([(range, text)], None, cx);
6302 }
6303 });
6304 this.fold_creases(refold_creases, true, cx);
6305 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6306 s.select(new_selections);
6307 })
6308 });
6309 }
6310
6311 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
6312 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6313 let buffer = self.buffer.read(cx).snapshot(cx);
6314
6315 let mut edits = Vec::new();
6316 let mut unfold_ranges = Vec::new();
6317 let mut refold_creases = Vec::new();
6318
6319 let selections = self.selections.all::<Point>(cx);
6320 let mut selections = selections.iter().peekable();
6321 let mut contiguous_row_selections = Vec::new();
6322 let mut new_selections = Vec::new();
6323
6324 while let Some(selection) = selections.next() {
6325 // Find all the selections that span a contiguous row range
6326 let (start_row, end_row) = consume_contiguous_rows(
6327 &mut contiguous_row_selections,
6328 selection,
6329 &display_map,
6330 &mut selections,
6331 );
6332
6333 // Move the text spanned by the row range to be after the last line of the row range
6334 if end_row.0 <= buffer.max_point().row {
6335 let range_to_move =
6336 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
6337 let insertion_point = display_map
6338 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
6339 .0;
6340
6341 // Don't move lines across excerpt boundaries
6342 if buffer
6343 .excerpt_boundaries_in_range((
6344 Bound::Excluded(range_to_move.start),
6345 Bound::Included(insertion_point),
6346 ))
6347 .next()
6348 .is_none()
6349 {
6350 let mut text = String::from("\n");
6351 text.extend(buffer.text_for_range(range_to_move.clone()));
6352 text.pop(); // Drop trailing newline
6353 edits.push((
6354 buffer.anchor_after(range_to_move.start)
6355 ..buffer.anchor_before(range_to_move.end),
6356 String::new(),
6357 ));
6358 let insertion_anchor = buffer.anchor_after(insertion_point);
6359 edits.push((insertion_anchor..insertion_anchor, text));
6360
6361 let row_delta = insertion_point.row - range_to_move.end.row + 1;
6362
6363 // Move selections down
6364 new_selections.extend(contiguous_row_selections.drain(..).map(
6365 |mut selection| {
6366 selection.start.row += row_delta;
6367 selection.end.row += row_delta;
6368 selection
6369 },
6370 ));
6371
6372 // Move folds down
6373 unfold_ranges.push(range_to_move.clone());
6374 for fold in display_map.folds_in_range(
6375 buffer.anchor_before(range_to_move.start)
6376 ..buffer.anchor_after(range_to_move.end),
6377 ) {
6378 let mut start = fold.range.start.to_point(&buffer);
6379 let mut end = fold.range.end.to_point(&buffer);
6380 start.row += row_delta;
6381 end.row += row_delta;
6382 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
6383 }
6384 }
6385 }
6386
6387 // If we didn't move line(s), preserve the existing selections
6388 new_selections.append(&mut contiguous_row_selections);
6389 }
6390
6391 self.transact(cx, |this, cx| {
6392 this.unfold_ranges(&unfold_ranges, true, true, cx);
6393 this.buffer.update(cx, |buffer, cx| {
6394 for (range, text) in edits {
6395 buffer.edit([(range, text)], None, cx);
6396 }
6397 });
6398 this.fold_creases(refold_creases, true, cx);
6399 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
6400 });
6401 }
6402
6403 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
6404 let text_layout_details = &self.text_layout_details(cx);
6405 self.transact(cx, |this, cx| {
6406 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6407 let mut edits: Vec<(Range<usize>, String)> = Default::default();
6408 let line_mode = s.line_mode;
6409 s.move_with(|display_map, selection| {
6410 if !selection.is_empty() || line_mode {
6411 return;
6412 }
6413
6414 let mut head = selection.head();
6415 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
6416 if head.column() == display_map.line_len(head.row()) {
6417 transpose_offset = display_map
6418 .buffer_snapshot
6419 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6420 }
6421
6422 if transpose_offset == 0 {
6423 return;
6424 }
6425
6426 *head.column_mut() += 1;
6427 head = display_map.clip_point(head, Bias::Right);
6428 let goal = SelectionGoal::HorizontalPosition(
6429 display_map
6430 .x_for_display_point(head, text_layout_details)
6431 .into(),
6432 );
6433 selection.collapse_to(head, goal);
6434
6435 let transpose_start = display_map
6436 .buffer_snapshot
6437 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6438 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
6439 let transpose_end = display_map
6440 .buffer_snapshot
6441 .clip_offset(transpose_offset + 1, Bias::Right);
6442 if let Some(ch) =
6443 display_map.buffer_snapshot.chars_at(transpose_start).next()
6444 {
6445 edits.push((transpose_start..transpose_offset, String::new()));
6446 edits.push((transpose_end..transpose_end, ch.to_string()));
6447 }
6448 }
6449 });
6450 edits
6451 });
6452 this.buffer
6453 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
6454 let selections = this.selections.all::<usize>(cx);
6455 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6456 s.select(selections);
6457 });
6458 });
6459 }
6460
6461 pub fn rewrap(&mut self, _: &Rewrap, cx: &mut ViewContext<Self>) {
6462 self.rewrap_impl(IsVimMode::No, cx)
6463 }
6464
6465 pub fn rewrap_impl(&mut self, is_vim_mode: IsVimMode, cx: &mut ViewContext<Self>) {
6466 let buffer = self.buffer.read(cx).snapshot(cx);
6467 let selections = self.selections.all::<Point>(cx);
6468 let mut selections = selections.iter().peekable();
6469
6470 let mut edits = Vec::new();
6471 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
6472
6473 while let Some(selection) = selections.next() {
6474 let mut start_row = selection.start.row;
6475 let mut end_row = selection.end.row;
6476
6477 // Skip selections that overlap with a range that has already been rewrapped.
6478 let selection_range = start_row..end_row;
6479 if rewrapped_row_ranges
6480 .iter()
6481 .any(|range| range.overlaps(&selection_range))
6482 {
6483 continue;
6484 }
6485
6486 let mut should_rewrap = is_vim_mode == IsVimMode::Yes;
6487
6488 if let Some(language_scope) = buffer.language_scope_at(selection.head()) {
6489 match language_scope.language_name().0.as_ref() {
6490 "Markdown" | "Plain Text" => {
6491 should_rewrap = true;
6492 }
6493 _ => {}
6494 }
6495 }
6496
6497 let tab_size = buffer.settings_at(selection.head(), cx).tab_size;
6498
6499 // Since not all lines in the selection may be at the same indent
6500 // level, choose the indent size that is the most common between all
6501 // of the lines.
6502 //
6503 // If there is a tie, we use the deepest indent.
6504 let (indent_size, indent_end) = {
6505 let mut indent_size_occurrences = HashMap::default();
6506 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
6507
6508 for row in start_row..=end_row {
6509 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
6510 rows_by_indent_size.entry(indent).or_default().push(row);
6511 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
6512 }
6513
6514 let indent_size = indent_size_occurrences
6515 .into_iter()
6516 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
6517 .map(|(indent, _)| indent)
6518 .unwrap_or_default();
6519 let row = rows_by_indent_size[&indent_size][0];
6520 let indent_end = Point::new(row, indent_size.len);
6521
6522 (indent_size, indent_end)
6523 };
6524
6525 let mut line_prefix = indent_size.chars().collect::<String>();
6526
6527 if let Some(comment_prefix) =
6528 buffer
6529 .language_scope_at(selection.head())
6530 .and_then(|language| {
6531 language
6532 .line_comment_prefixes()
6533 .iter()
6534 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
6535 .cloned()
6536 })
6537 {
6538 line_prefix.push_str(&comment_prefix);
6539 should_rewrap = true;
6540 }
6541
6542 if !should_rewrap {
6543 continue;
6544 }
6545
6546 if selection.is_empty() {
6547 'expand_upwards: while start_row > 0 {
6548 let prev_row = start_row - 1;
6549 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
6550 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
6551 {
6552 start_row = prev_row;
6553 } else {
6554 break 'expand_upwards;
6555 }
6556 }
6557
6558 'expand_downwards: while end_row < buffer.max_point().row {
6559 let next_row = end_row + 1;
6560 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
6561 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
6562 {
6563 end_row = next_row;
6564 } else {
6565 break 'expand_downwards;
6566 }
6567 }
6568 }
6569
6570 let start = Point::new(start_row, 0);
6571 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
6572 let selection_text = buffer.text_for_range(start..end).collect::<String>();
6573 let Some(lines_without_prefixes) = selection_text
6574 .lines()
6575 .map(|line| {
6576 line.strip_prefix(&line_prefix)
6577 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
6578 .ok_or_else(|| {
6579 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
6580 })
6581 })
6582 .collect::<Result<Vec<_>, _>>()
6583 .log_err()
6584 else {
6585 continue;
6586 };
6587
6588 let wrap_column = buffer
6589 .settings_at(Point::new(start_row, 0), cx)
6590 .preferred_line_length as usize;
6591 let wrapped_text = wrap_with_prefix(
6592 line_prefix,
6593 lines_without_prefixes.join(" "),
6594 wrap_column,
6595 tab_size,
6596 );
6597
6598 // TODO: should always use char-based diff while still supporting cursor behavior that
6599 // matches vim.
6600 let diff = match is_vim_mode {
6601 IsVimMode::Yes => TextDiff::from_lines(&selection_text, &wrapped_text),
6602 IsVimMode::No => TextDiff::from_chars(&selection_text, &wrapped_text),
6603 };
6604 let mut offset = start.to_offset(&buffer);
6605 let mut moved_since_edit = true;
6606
6607 for change in diff.iter_all_changes() {
6608 let value = change.value();
6609 match change.tag() {
6610 ChangeTag::Equal => {
6611 offset += value.len();
6612 moved_since_edit = true;
6613 }
6614 ChangeTag::Delete => {
6615 let start = buffer.anchor_after(offset);
6616 let end = buffer.anchor_before(offset + value.len());
6617
6618 if moved_since_edit {
6619 edits.push((start..end, String::new()));
6620 } else {
6621 edits.last_mut().unwrap().0.end = end;
6622 }
6623
6624 offset += value.len();
6625 moved_since_edit = false;
6626 }
6627 ChangeTag::Insert => {
6628 if moved_since_edit {
6629 let anchor = buffer.anchor_after(offset);
6630 edits.push((anchor..anchor, value.to_string()));
6631 } else {
6632 edits.last_mut().unwrap().1.push_str(value);
6633 }
6634
6635 moved_since_edit = false;
6636 }
6637 }
6638 }
6639
6640 rewrapped_row_ranges.push(start_row..=end_row);
6641 }
6642
6643 self.buffer
6644 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
6645 }
6646
6647 pub fn cut_common(&mut self, cx: &mut ViewContext<Self>) -> ClipboardItem {
6648 let mut text = String::new();
6649 let buffer = self.buffer.read(cx).snapshot(cx);
6650 let mut selections = self.selections.all::<Point>(cx);
6651 let mut clipboard_selections = Vec::with_capacity(selections.len());
6652 {
6653 let max_point = buffer.max_point();
6654 let mut is_first = true;
6655 for selection in &mut selections {
6656 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6657 if is_entire_line {
6658 selection.start = Point::new(selection.start.row, 0);
6659 if !selection.is_empty() && selection.end.column == 0 {
6660 selection.end = cmp::min(max_point, selection.end);
6661 } else {
6662 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
6663 }
6664 selection.goal = SelectionGoal::None;
6665 }
6666 if is_first {
6667 is_first = false;
6668 } else {
6669 text += "\n";
6670 }
6671 let mut len = 0;
6672 for chunk in buffer.text_for_range(selection.start..selection.end) {
6673 text.push_str(chunk);
6674 len += chunk.len();
6675 }
6676 clipboard_selections.push(ClipboardSelection {
6677 len,
6678 is_entire_line,
6679 first_line_indent: buffer
6680 .indent_size_for_line(MultiBufferRow(selection.start.row))
6681 .len,
6682 });
6683 }
6684 }
6685
6686 self.transact(cx, |this, cx| {
6687 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6688 s.select(selections);
6689 });
6690 this.insert("", cx);
6691 });
6692 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
6693 }
6694
6695 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
6696 let item = self.cut_common(cx);
6697 cx.write_to_clipboard(item);
6698 }
6699
6700 pub fn kill_ring_cut(&mut self, _: &KillRingCut, cx: &mut ViewContext<Self>) {
6701 self.change_selections(None, cx, |s| {
6702 s.move_with(|snapshot, sel| {
6703 if sel.is_empty() {
6704 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
6705 }
6706 });
6707 });
6708 let item = self.cut_common(cx);
6709 cx.set_global(KillRing(item))
6710 }
6711
6712 pub fn kill_ring_yank(&mut self, _: &KillRingYank, cx: &mut ViewContext<Self>) {
6713 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
6714 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
6715 (kill_ring.text().to_string(), kill_ring.metadata_json())
6716 } else {
6717 return;
6718 }
6719 } else {
6720 return;
6721 };
6722 self.do_paste(&text, metadata, false, cx);
6723 }
6724
6725 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
6726 let selections = self.selections.all::<Point>(cx);
6727 let buffer = self.buffer.read(cx).read(cx);
6728 let mut text = String::new();
6729
6730 let mut clipboard_selections = Vec::with_capacity(selections.len());
6731 {
6732 let max_point = buffer.max_point();
6733 let mut is_first = true;
6734 for selection in selections.iter() {
6735 let mut start = selection.start;
6736 let mut end = selection.end;
6737 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6738 if is_entire_line {
6739 start = Point::new(start.row, 0);
6740 end = cmp::min(max_point, Point::new(end.row + 1, 0));
6741 }
6742 if is_first {
6743 is_first = false;
6744 } else {
6745 text += "\n";
6746 }
6747 let mut len = 0;
6748 for chunk in buffer.text_for_range(start..end) {
6749 text.push_str(chunk);
6750 len += chunk.len();
6751 }
6752 clipboard_selections.push(ClipboardSelection {
6753 len,
6754 is_entire_line,
6755 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
6756 });
6757 }
6758 }
6759
6760 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
6761 text,
6762 clipboard_selections,
6763 ));
6764 }
6765
6766 pub fn do_paste(
6767 &mut self,
6768 text: &String,
6769 clipboard_selections: Option<Vec<ClipboardSelection>>,
6770 handle_entire_lines: bool,
6771 cx: &mut ViewContext<Self>,
6772 ) {
6773 if self.read_only(cx) {
6774 return;
6775 }
6776
6777 let clipboard_text = Cow::Borrowed(text);
6778
6779 self.transact(cx, |this, cx| {
6780 if let Some(mut clipboard_selections) = clipboard_selections {
6781 let old_selections = this.selections.all::<usize>(cx);
6782 let all_selections_were_entire_line =
6783 clipboard_selections.iter().all(|s| s.is_entire_line);
6784 let first_selection_indent_column =
6785 clipboard_selections.first().map(|s| s.first_line_indent);
6786 if clipboard_selections.len() != old_selections.len() {
6787 clipboard_selections.drain(..);
6788 }
6789 let cursor_offset = this.selections.last::<usize>(cx).head();
6790 let mut auto_indent_on_paste = true;
6791
6792 this.buffer.update(cx, |buffer, cx| {
6793 let snapshot = buffer.read(cx);
6794 auto_indent_on_paste =
6795 snapshot.settings_at(cursor_offset, cx).auto_indent_on_paste;
6796
6797 let mut start_offset = 0;
6798 let mut edits = Vec::new();
6799 let mut original_indent_columns = Vec::new();
6800 for (ix, selection) in old_selections.iter().enumerate() {
6801 let to_insert;
6802 let entire_line;
6803 let original_indent_column;
6804 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
6805 let end_offset = start_offset + clipboard_selection.len;
6806 to_insert = &clipboard_text[start_offset..end_offset];
6807 entire_line = clipboard_selection.is_entire_line;
6808 start_offset = end_offset + 1;
6809 original_indent_column = Some(clipboard_selection.first_line_indent);
6810 } else {
6811 to_insert = clipboard_text.as_str();
6812 entire_line = all_selections_were_entire_line;
6813 original_indent_column = first_selection_indent_column
6814 }
6815
6816 // If the corresponding selection was empty when this slice of the
6817 // clipboard text was written, then the entire line containing the
6818 // selection was copied. If this selection is also currently empty,
6819 // then paste the line before the current line of the buffer.
6820 let range = if selection.is_empty() && handle_entire_lines && entire_line {
6821 let column = selection.start.to_point(&snapshot).column as usize;
6822 let line_start = selection.start - column;
6823 line_start..line_start
6824 } else {
6825 selection.range()
6826 };
6827
6828 edits.push((range, to_insert));
6829 original_indent_columns.extend(original_indent_column);
6830 }
6831 drop(snapshot);
6832
6833 buffer.edit(
6834 edits,
6835 if auto_indent_on_paste {
6836 Some(AutoindentMode::Block {
6837 original_indent_columns,
6838 })
6839 } else {
6840 None
6841 },
6842 cx,
6843 );
6844 });
6845
6846 let selections = this.selections.all::<usize>(cx);
6847 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6848 } else {
6849 this.insert(&clipboard_text, cx);
6850 }
6851 });
6852 }
6853
6854 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
6855 if let Some(item) = cx.read_from_clipboard() {
6856 let entries = item.entries();
6857
6858 match entries.first() {
6859 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
6860 // of all the pasted entries.
6861 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
6862 .do_paste(
6863 clipboard_string.text(),
6864 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
6865 true,
6866 cx,
6867 ),
6868 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, cx),
6869 }
6870 }
6871 }
6872
6873 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
6874 if self.read_only(cx) {
6875 return;
6876 }
6877
6878 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
6879 if let Some((selections, _)) =
6880 self.selection_history.transaction(transaction_id).cloned()
6881 {
6882 self.change_selections(None, cx, |s| {
6883 s.select_anchors(selections.to_vec());
6884 });
6885 }
6886 self.request_autoscroll(Autoscroll::fit(), cx);
6887 self.unmark_text(cx);
6888 self.refresh_inline_completion(true, false, cx);
6889 cx.emit(EditorEvent::Edited { transaction_id });
6890 cx.emit(EditorEvent::TransactionUndone { transaction_id });
6891 }
6892 }
6893
6894 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
6895 if self.read_only(cx) {
6896 return;
6897 }
6898
6899 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
6900 if let Some((_, Some(selections))) =
6901 self.selection_history.transaction(transaction_id).cloned()
6902 {
6903 self.change_selections(None, cx, |s| {
6904 s.select_anchors(selections.to_vec());
6905 });
6906 }
6907 self.request_autoscroll(Autoscroll::fit(), cx);
6908 self.unmark_text(cx);
6909 self.refresh_inline_completion(true, false, cx);
6910 cx.emit(EditorEvent::Edited { transaction_id });
6911 }
6912 }
6913
6914 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
6915 self.buffer
6916 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
6917 }
6918
6919 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut ViewContext<Self>) {
6920 self.buffer
6921 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
6922 }
6923
6924 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
6925 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6926 let line_mode = s.line_mode;
6927 s.move_with(|map, selection| {
6928 let cursor = if selection.is_empty() && !line_mode {
6929 movement::left(map, selection.start)
6930 } else {
6931 selection.start
6932 };
6933 selection.collapse_to(cursor, SelectionGoal::None);
6934 });
6935 })
6936 }
6937
6938 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
6939 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6940 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
6941 })
6942 }
6943
6944 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
6945 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6946 let line_mode = s.line_mode;
6947 s.move_with(|map, selection| {
6948 let cursor = if selection.is_empty() && !line_mode {
6949 movement::right(map, selection.end)
6950 } else {
6951 selection.end
6952 };
6953 selection.collapse_to(cursor, SelectionGoal::None)
6954 });
6955 })
6956 }
6957
6958 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
6959 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6960 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
6961 })
6962 }
6963
6964 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
6965 if self.take_rename(true, cx).is_some() {
6966 return;
6967 }
6968
6969 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6970 cx.propagate();
6971 return;
6972 }
6973
6974 let text_layout_details = &self.text_layout_details(cx);
6975 let selection_count = self.selections.count();
6976 let first_selection = self.selections.first_anchor();
6977
6978 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6979 let line_mode = s.line_mode;
6980 s.move_with(|map, selection| {
6981 if !selection.is_empty() && !line_mode {
6982 selection.goal = SelectionGoal::None;
6983 }
6984 let (cursor, goal) = movement::up(
6985 map,
6986 selection.start,
6987 selection.goal,
6988 false,
6989 text_layout_details,
6990 );
6991 selection.collapse_to(cursor, goal);
6992 });
6993 });
6994
6995 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
6996 {
6997 cx.propagate();
6998 }
6999 }
7000
7001 pub fn move_up_by_lines(&mut self, action: &MoveUpByLines, cx: &mut ViewContext<Self>) {
7002 if self.take_rename(true, cx).is_some() {
7003 return;
7004 }
7005
7006 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7007 cx.propagate();
7008 return;
7009 }
7010
7011 let text_layout_details = &self.text_layout_details(cx);
7012
7013 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7014 let line_mode = s.line_mode;
7015 s.move_with(|map, selection| {
7016 if !selection.is_empty() && !line_mode {
7017 selection.goal = SelectionGoal::None;
7018 }
7019 let (cursor, goal) = movement::up_by_rows(
7020 map,
7021 selection.start,
7022 action.lines,
7023 selection.goal,
7024 false,
7025 text_layout_details,
7026 );
7027 selection.collapse_to(cursor, goal);
7028 });
7029 })
7030 }
7031
7032 pub fn move_down_by_lines(&mut self, action: &MoveDownByLines, cx: &mut ViewContext<Self>) {
7033 if self.take_rename(true, cx).is_some() {
7034 return;
7035 }
7036
7037 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7038 cx.propagate();
7039 return;
7040 }
7041
7042 let text_layout_details = &self.text_layout_details(cx);
7043
7044 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7045 let line_mode = s.line_mode;
7046 s.move_with(|map, selection| {
7047 if !selection.is_empty() && !line_mode {
7048 selection.goal = SelectionGoal::None;
7049 }
7050 let (cursor, goal) = movement::down_by_rows(
7051 map,
7052 selection.start,
7053 action.lines,
7054 selection.goal,
7055 false,
7056 text_layout_details,
7057 );
7058 selection.collapse_to(cursor, goal);
7059 });
7060 })
7061 }
7062
7063 pub fn select_down_by_lines(&mut self, action: &SelectDownByLines, cx: &mut ViewContext<Self>) {
7064 let text_layout_details = &self.text_layout_details(cx);
7065 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7066 s.move_heads_with(|map, head, goal| {
7067 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
7068 })
7069 })
7070 }
7071
7072 pub fn select_up_by_lines(&mut self, action: &SelectUpByLines, cx: &mut ViewContext<Self>) {
7073 let text_layout_details = &self.text_layout_details(cx);
7074 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7075 s.move_heads_with(|map, head, goal| {
7076 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
7077 })
7078 })
7079 }
7080
7081 pub fn select_page_up(&mut self, _: &SelectPageUp, cx: &mut ViewContext<Self>) {
7082 let Some(row_count) = self.visible_row_count() else {
7083 return;
7084 };
7085
7086 let text_layout_details = &self.text_layout_details(cx);
7087
7088 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7089 s.move_heads_with(|map, head, goal| {
7090 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
7091 })
7092 })
7093 }
7094
7095 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
7096 if self.take_rename(true, cx).is_some() {
7097 return;
7098 }
7099
7100 if self
7101 .context_menu
7102 .write()
7103 .as_mut()
7104 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
7105 .unwrap_or(false)
7106 {
7107 return;
7108 }
7109
7110 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7111 cx.propagate();
7112 return;
7113 }
7114
7115 let Some(row_count) = self.visible_row_count() else {
7116 return;
7117 };
7118
7119 let autoscroll = if action.center_cursor {
7120 Autoscroll::center()
7121 } else {
7122 Autoscroll::fit()
7123 };
7124
7125 let text_layout_details = &self.text_layout_details(cx);
7126
7127 self.change_selections(Some(autoscroll), cx, |s| {
7128 let line_mode = s.line_mode;
7129 s.move_with(|map, selection| {
7130 if !selection.is_empty() && !line_mode {
7131 selection.goal = SelectionGoal::None;
7132 }
7133 let (cursor, goal) = movement::up_by_rows(
7134 map,
7135 selection.end,
7136 row_count,
7137 selection.goal,
7138 false,
7139 text_layout_details,
7140 );
7141 selection.collapse_to(cursor, goal);
7142 });
7143 });
7144 }
7145
7146 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
7147 let text_layout_details = &self.text_layout_details(cx);
7148 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7149 s.move_heads_with(|map, head, goal| {
7150 movement::up(map, head, goal, false, text_layout_details)
7151 })
7152 })
7153 }
7154
7155 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
7156 self.take_rename(true, cx);
7157
7158 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7159 cx.propagate();
7160 return;
7161 }
7162
7163 let text_layout_details = &self.text_layout_details(cx);
7164 let selection_count = self.selections.count();
7165 let first_selection = self.selections.first_anchor();
7166
7167 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7168 let line_mode = s.line_mode;
7169 s.move_with(|map, selection| {
7170 if !selection.is_empty() && !line_mode {
7171 selection.goal = SelectionGoal::None;
7172 }
7173 let (cursor, goal) = movement::down(
7174 map,
7175 selection.end,
7176 selection.goal,
7177 false,
7178 text_layout_details,
7179 );
7180 selection.collapse_to(cursor, goal);
7181 });
7182 });
7183
7184 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7185 {
7186 cx.propagate();
7187 }
7188 }
7189
7190 pub fn select_page_down(&mut self, _: &SelectPageDown, cx: &mut ViewContext<Self>) {
7191 let Some(row_count) = self.visible_row_count() else {
7192 return;
7193 };
7194
7195 let text_layout_details = &self.text_layout_details(cx);
7196
7197 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7198 s.move_heads_with(|map, head, goal| {
7199 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
7200 })
7201 })
7202 }
7203
7204 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
7205 if self.take_rename(true, cx).is_some() {
7206 return;
7207 }
7208
7209 if self
7210 .context_menu
7211 .write()
7212 .as_mut()
7213 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
7214 .unwrap_or(false)
7215 {
7216 return;
7217 }
7218
7219 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7220 cx.propagate();
7221 return;
7222 }
7223
7224 let Some(row_count) = self.visible_row_count() else {
7225 return;
7226 };
7227
7228 let autoscroll = if action.center_cursor {
7229 Autoscroll::center()
7230 } else {
7231 Autoscroll::fit()
7232 };
7233
7234 let text_layout_details = &self.text_layout_details(cx);
7235 self.change_selections(Some(autoscroll), cx, |s| {
7236 let line_mode = s.line_mode;
7237 s.move_with(|map, selection| {
7238 if !selection.is_empty() && !line_mode {
7239 selection.goal = SelectionGoal::None;
7240 }
7241 let (cursor, goal) = movement::down_by_rows(
7242 map,
7243 selection.end,
7244 row_count,
7245 selection.goal,
7246 false,
7247 text_layout_details,
7248 );
7249 selection.collapse_to(cursor, goal);
7250 });
7251 });
7252 }
7253
7254 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
7255 let text_layout_details = &self.text_layout_details(cx);
7256 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7257 s.move_heads_with(|map, head, goal| {
7258 movement::down(map, head, goal, false, text_layout_details)
7259 })
7260 });
7261 }
7262
7263 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
7264 if let Some(context_menu) = self.context_menu.write().as_mut() {
7265 context_menu.select_first(self.completion_provider.as_deref(), cx);
7266 }
7267 }
7268
7269 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
7270 if let Some(context_menu) = self.context_menu.write().as_mut() {
7271 context_menu.select_prev(self.completion_provider.as_deref(), cx);
7272 }
7273 }
7274
7275 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
7276 if let Some(context_menu) = self.context_menu.write().as_mut() {
7277 context_menu.select_next(self.completion_provider.as_deref(), cx);
7278 }
7279 }
7280
7281 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
7282 if let Some(context_menu) = self.context_menu.write().as_mut() {
7283 context_menu.select_last(self.completion_provider.as_deref(), cx);
7284 }
7285 }
7286
7287 pub fn move_to_previous_word_start(
7288 &mut self,
7289 _: &MoveToPreviousWordStart,
7290 cx: &mut ViewContext<Self>,
7291 ) {
7292 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7293 s.move_cursors_with(|map, head, _| {
7294 (
7295 movement::previous_word_start(map, head),
7296 SelectionGoal::None,
7297 )
7298 });
7299 })
7300 }
7301
7302 pub fn move_to_previous_subword_start(
7303 &mut self,
7304 _: &MoveToPreviousSubwordStart,
7305 cx: &mut ViewContext<Self>,
7306 ) {
7307 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7308 s.move_cursors_with(|map, head, _| {
7309 (
7310 movement::previous_subword_start(map, head),
7311 SelectionGoal::None,
7312 )
7313 });
7314 })
7315 }
7316
7317 pub fn select_to_previous_word_start(
7318 &mut self,
7319 _: &SelectToPreviousWordStart,
7320 cx: &mut ViewContext<Self>,
7321 ) {
7322 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7323 s.move_heads_with(|map, head, _| {
7324 (
7325 movement::previous_word_start(map, head),
7326 SelectionGoal::None,
7327 )
7328 });
7329 })
7330 }
7331
7332 pub fn select_to_previous_subword_start(
7333 &mut self,
7334 _: &SelectToPreviousSubwordStart,
7335 cx: &mut ViewContext<Self>,
7336 ) {
7337 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7338 s.move_heads_with(|map, head, _| {
7339 (
7340 movement::previous_subword_start(map, head),
7341 SelectionGoal::None,
7342 )
7343 });
7344 })
7345 }
7346
7347 pub fn delete_to_previous_word_start(
7348 &mut self,
7349 action: &DeleteToPreviousWordStart,
7350 cx: &mut ViewContext<Self>,
7351 ) {
7352 self.transact(cx, |this, cx| {
7353 this.select_autoclose_pair(cx);
7354 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7355 let line_mode = s.line_mode;
7356 s.move_with(|map, selection| {
7357 if selection.is_empty() && !line_mode {
7358 let cursor = if action.ignore_newlines {
7359 movement::previous_word_start(map, selection.head())
7360 } else {
7361 movement::previous_word_start_or_newline(map, selection.head())
7362 };
7363 selection.set_head(cursor, SelectionGoal::None);
7364 }
7365 });
7366 });
7367 this.insert("", cx);
7368 });
7369 }
7370
7371 pub fn delete_to_previous_subword_start(
7372 &mut self,
7373 _: &DeleteToPreviousSubwordStart,
7374 cx: &mut ViewContext<Self>,
7375 ) {
7376 self.transact(cx, |this, cx| {
7377 this.select_autoclose_pair(cx);
7378 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7379 let line_mode = s.line_mode;
7380 s.move_with(|map, selection| {
7381 if selection.is_empty() && !line_mode {
7382 let cursor = movement::previous_subword_start(map, selection.head());
7383 selection.set_head(cursor, SelectionGoal::None);
7384 }
7385 });
7386 });
7387 this.insert("", cx);
7388 });
7389 }
7390
7391 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
7392 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7393 s.move_cursors_with(|map, head, _| {
7394 (movement::next_word_end(map, head), SelectionGoal::None)
7395 });
7396 })
7397 }
7398
7399 pub fn move_to_next_subword_end(
7400 &mut self,
7401 _: &MoveToNextSubwordEnd,
7402 cx: &mut ViewContext<Self>,
7403 ) {
7404 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7405 s.move_cursors_with(|map, head, _| {
7406 (movement::next_subword_end(map, head), SelectionGoal::None)
7407 });
7408 })
7409 }
7410
7411 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
7412 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7413 s.move_heads_with(|map, head, _| {
7414 (movement::next_word_end(map, head), SelectionGoal::None)
7415 });
7416 })
7417 }
7418
7419 pub fn select_to_next_subword_end(
7420 &mut self,
7421 _: &SelectToNextSubwordEnd,
7422 cx: &mut ViewContext<Self>,
7423 ) {
7424 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7425 s.move_heads_with(|map, head, _| {
7426 (movement::next_subword_end(map, head), SelectionGoal::None)
7427 });
7428 })
7429 }
7430
7431 pub fn delete_to_next_word_end(
7432 &mut self,
7433 action: &DeleteToNextWordEnd,
7434 cx: &mut ViewContext<Self>,
7435 ) {
7436 self.transact(cx, |this, cx| {
7437 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7438 let line_mode = s.line_mode;
7439 s.move_with(|map, selection| {
7440 if selection.is_empty() && !line_mode {
7441 let cursor = if action.ignore_newlines {
7442 movement::next_word_end(map, selection.head())
7443 } else {
7444 movement::next_word_end_or_newline(map, selection.head())
7445 };
7446 selection.set_head(cursor, SelectionGoal::None);
7447 }
7448 });
7449 });
7450 this.insert("", cx);
7451 });
7452 }
7453
7454 pub fn delete_to_next_subword_end(
7455 &mut self,
7456 _: &DeleteToNextSubwordEnd,
7457 cx: &mut ViewContext<Self>,
7458 ) {
7459 self.transact(cx, |this, cx| {
7460 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7461 s.move_with(|map, selection| {
7462 if selection.is_empty() {
7463 let cursor = movement::next_subword_end(map, selection.head());
7464 selection.set_head(cursor, SelectionGoal::None);
7465 }
7466 });
7467 });
7468 this.insert("", cx);
7469 });
7470 }
7471
7472 pub fn move_to_beginning_of_line(
7473 &mut self,
7474 action: &MoveToBeginningOfLine,
7475 cx: &mut ViewContext<Self>,
7476 ) {
7477 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7478 s.move_cursors_with(|map, head, _| {
7479 (
7480 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7481 SelectionGoal::None,
7482 )
7483 });
7484 })
7485 }
7486
7487 pub fn select_to_beginning_of_line(
7488 &mut self,
7489 action: &SelectToBeginningOfLine,
7490 cx: &mut ViewContext<Self>,
7491 ) {
7492 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7493 s.move_heads_with(|map, head, _| {
7494 (
7495 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7496 SelectionGoal::None,
7497 )
7498 });
7499 });
7500 }
7501
7502 pub fn delete_to_beginning_of_line(
7503 &mut self,
7504 _: &DeleteToBeginningOfLine,
7505 cx: &mut ViewContext<Self>,
7506 ) {
7507 self.transact(cx, |this, cx| {
7508 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7509 s.move_with(|_, selection| {
7510 selection.reversed = true;
7511 });
7512 });
7513
7514 this.select_to_beginning_of_line(
7515 &SelectToBeginningOfLine {
7516 stop_at_soft_wraps: false,
7517 },
7518 cx,
7519 );
7520 this.backspace(&Backspace, cx);
7521 });
7522 }
7523
7524 pub fn move_to_end_of_line(&mut self, action: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
7525 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7526 s.move_cursors_with(|map, head, _| {
7527 (
7528 movement::line_end(map, head, action.stop_at_soft_wraps),
7529 SelectionGoal::None,
7530 )
7531 });
7532 })
7533 }
7534
7535 pub fn select_to_end_of_line(
7536 &mut self,
7537 action: &SelectToEndOfLine,
7538 cx: &mut ViewContext<Self>,
7539 ) {
7540 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7541 s.move_heads_with(|map, head, _| {
7542 (
7543 movement::line_end(map, head, action.stop_at_soft_wraps),
7544 SelectionGoal::None,
7545 )
7546 });
7547 })
7548 }
7549
7550 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
7551 self.transact(cx, |this, cx| {
7552 this.select_to_end_of_line(
7553 &SelectToEndOfLine {
7554 stop_at_soft_wraps: false,
7555 },
7556 cx,
7557 );
7558 this.delete(&Delete, cx);
7559 });
7560 }
7561
7562 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
7563 self.transact(cx, |this, cx| {
7564 this.select_to_end_of_line(
7565 &SelectToEndOfLine {
7566 stop_at_soft_wraps: false,
7567 },
7568 cx,
7569 );
7570 this.cut(&Cut, cx);
7571 });
7572 }
7573
7574 pub fn move_to_start_of_paragraph(
7575 &mut self,
7576 _: &MoveToStartOfParagraph,
7577 cx: &mut ViewContext<Self>,
7578 ) {
7579 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7580 cx.propagate();
7581 return;
7582 }
7583
7584 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7585 s.move_with(|map, selection| {
7586 selection.collapse_to(
7587 movement::start_of_paragraph(map, selection.head(), 1),
7588 SelectionGoal::None,
7589 )
7590 });
7591 })
7592 }
7593
7594 pub fn move_to_end_of_paragraph(
7595 &mut self,
7596 _: &MoveToEndOfParagraph,
7597 cx: &mut ViewContext<Self>,
7598 ) {
7599 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7600 cx.propagate();
7601 return;
7602 }
7603
7604 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7605 s.move_with(|map, selection| {
7606 selection.collapse_to(
7607 movement::end_of_paragraph(map, selection.head(), 1),
7608 SelectionGoal::None,
7609 )
7610 });
7611 })
7612 }
7613
7614 pub fn select_to_start_of_paragraph(
7615 &mut self,
7616 _: &SelectToStartOfParagraph,
7617 cx: &mut ViewContext<Self>,
7618 ) {
7619 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7620 cx.propagate();
7621 return;
7622 }
7623
7624 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7625 s.move_heads_with(|map, head, _| {
7626 (
7627 movement::start_of_paragraph(map, head, 1),
7628 SelectionGoal::None,
7629 )
7630 });
7631 })
7632 }
7633
7634 pub fn select_to_end_of_paragraph(
7635 &mut self,
7636 _: &SelectToEndOfParagraph,
7637 cx: &mut ViewContext<Self>,
7638 ) {
7639 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7640 cx.propagate();
7641 return;
7642 }
7643
7644 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7645 s.move_heads_with(|map, head, _| {
7646 (
7647 movement::end_of_paragraph(map, head, 1),
7648 SelectionGoal::None,
7649 )
7650 });
7651 })
7652 }
7653
7654 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
7655 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7656 cx.propagate();
7657 return;
7658 }
7659
7660 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7661 s.select_ranges(vec![0..0]);
7662 });
7663 }
7664
7665 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
7666 let mut selection = self.selections.last::<Point>(cx);
7667 selection.set_head(Point::zero(), SelectionGoal::None);
7668
7669 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7670 s.select(vec![selection]);
7671 });
7672 }
7673
7674 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
7675 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7676 cx.propagate();
7677 return;
7678 }
7679
7680 let cursor = self.buffer.read(cx).read(cx).len();
7681 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7682 s.select_ranges(vec![cursor..cursor])
7683 });
7684 }
7685
7686 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
7687 self.nav_history = nav_history;
7688 }
7689
7690 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
7691 self.nav_history.as_ref()
7692 }
7693
7694 fn push_to_nav_history(
7695 &mut self,
7696 cursor_anchor: Anchor,
7697 new_position: Option<Point>,
7698 cx: &mut ViewContext<Self>,
7699 ) {
7700 if let Some(nav_history) = self.nav_history.as_mut() {
7701 let buffer = self.buffer.read(cx).read(cx);
7702 let cursor_position = cursor_anchor.to_point(&buffer);
7703 let scroll_state = self.scroll_manager.anchor();
7704 let scroll_top_row = scroll_state.top_row(&buffer);
7705 drop(buffer);
7706
7707 if let Some(new_position) = new_position {
7708 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
7709 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
7710 return;
7711 }
7712 }
7713
7714 nav_history.push(
7715 Some(NavigationData {
7716 cursor_anchor,
7717 cursor_position,
7718 scroll_anchor: scroll_state,
7719 scroll_top_row,
7720 }),
7721 cx,
7722 );
7723 }
7724 }
7725
7726 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
7727 let buffer = self.buffer.read(cx).snapshot(cx);
7728 let mut selection = self.selections.first::<usize>(cx);
7729 selection.set_head(buffer.len(), SelectionGoal::None);
7730 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7731 s.select(vec![selection]);
7732 });
7733 }
7734
7735 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
7736 let end = self.buffer.read(cx).read(cx).len();
7737 self.change_selections(None, cx, |s| {
7738 s.select_ranges(vec![0..end]);
7739 });
7740 }
7741
7742 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
7743 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7744 let mut selections = self.selections.all::<Point>(cx);
7745 let max_point = display_map.buffer_snapshot.max_point();
7746 for selection in &mut selections {
7747 let rows = selection.spanned_rows(true, &display_map);
7748 selection.start = Point::new(rows.start.0, 0);
7749 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
7750 selection.reversed = false;
7751 }
7752 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7753 s.select(selections);
7754 });
7755 }
7756
7757 pub fn split_selection_into_lines(
7758 &mut self,
7759 _: &SplitSelectionIntoLines,
7760 cx: &mut ViewContext<Self>,
7761 ) {
7762 let mut to_unfold = Vec::new();
7763 let mut new_selection_ranges = Vec::new();
7764 {
7765 let selections = self.selections.all::<Point>(cx);
7766 let buffer = self.buffer.read(cx).read(cx);
7767 for selection in selections {
7768 for row in selection.start.row..selection.end.row {
7769 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
7770 new_selection_ranges.push(cursor..cursor);
7771 }
7772 new_selection_ranges.push(selection.end..selection.end);
7773 to_unfold.push(selection.start..selection.end);
7774 }
7775 }
7776 self.unfold_ranges(&to_unfold, true, true, cx);
7777 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7778 s.select_ranges(new_selection_ranges);
7779 });
7780 }
7781
7782 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
7783 self.add_selection(true, cx);
7784 }
7785
7786 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
7787 self.add_selection(false, cx);
7788 }
7789
7790 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
7791 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7792 let mut selections = self.selections.all::<Point>(cx);
7793 let text_layout_details = self.text_layout_details(cx);
7794 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
7795 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
7796 let range = oldest_selection.display_range(&display_map).sorted();
7797
7798 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
7799 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
7800 let positions = start_x.min(end_x)..start_x.max(end_x);
7801
7802 selections.clear();
7803 let mut stack = Vec::new();
7804 for row in range.start.row().0..=range.end.row().0 {
7805 if let Some(selection) = self.selections.build_columnar_selection(
7806 &display_map,
7807 DisplayRow(row),
7808 &positions,
7809 oldest_selection.reversed,
7810 &text_layout_details,
7811 ) {
7812 stack.push(selection.id);
7813 selections.push(selection);
7814 }
7815 }
7816
7817 if above {
7818 stack.reverse();
7819 }
7820
7821 AddSelectionsState { above, stack }
7822 });
7823
7824 let last_added_selection = *state.stack.last().unwrap();
7825 let mut new_selections = Vec::new();
7826 if above == state.above {
7827 let end_row = if above {
7828 DisplayRow(0)
7829 } else {
7830 display_map.max_point().row()
7831 };
7832
7833 'outer: for selection in selections {
7834 if selection.id == last_added_selection {
7835 let range = selection.display_range(&display_map).sorted();
7836 debug_assert_eq!(range.start.row(), range.end.row());
7837 let mut row = range.start.row();
7838 let positions =
7839 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
7840 px(start)..px(end)
7841 } else {
7842 let start_x =
7843 display_map.x_for_display_point(range.start, &text_layout_details);
7844 let end_x =
7845 display_map.x_for_display_point(range.end, &text_layout_details);
7846 start_x.min(end_x)..start_x.max(end_x)
7847 };
7848
7849 while row != end_row {
7850 if above {
7851 row.0 -= 1;
7852 } else {
7853 row.0 += 1;
7854 }
7855
7856 if let Some(new_selection) = self.selections.build_columnar_selection(
7857 &display_map,
7858 row,
7859 &positions,
7860 selection.reversed,
7861 &text_layout_details,
7862 ) {
7863 state.stack.push(new_selection.id);
7864 if above {
7865 new_selections.push(new_selection);
7866 new_selections.push(selection);
7867 } else {
7868 new_selections.push(selection);
7869 new_selections.push(new_selection);
7870 }
7871
7872 continue 'outer;
7873 }
7874 }
7875 }
7876
7877 new_selections.push(selection);
7878 }
7879 } else {
7880 new_selections = selections;
7881 new_selections.retain(|s| s.id != last_added_selection);
7882 state.stack.pop();
7883 }
7884
7885 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7886 s.select(new_selections);
7887 });
7888 if state.stack.len() > 1 {
7889 self.add_selections_state = Some(state);
7890 }
7891 }
7892
7893 pub fn select_next_match_internal(
7894 &mut self,
7895 display_map: &DisplaySnapshot,
7896 replace_newest: bool,
7897 autoscroll: Option<Autoscroll>,
7898 cx: &mut ViewContext<Self>,
7899 ) -> Result<()> {
7900 fn select_next_match_ranges(
7901 this: &mut Editor,
7902 range: Range<usize>,
7903 replace_newest: bool,
7904 auto_scroll: Option<Autoscroll>,
7905 cx: &mut ViewContext<Editor>,
7906 ) {
7907 this.unfold_ranges(&[range.clone()], false, true, cx);
7908 this.change_selections(auto_scroll, cx, |s| {
7909 if replace_newest {
7910 s.delete(s.newest_anchor().id);
7911 }
7912 s.insert_range(range.clone());
7913 });
7914 }
7915
7916 let buffer = &display_map.buffer_snapshot;
7917 let mut selections = self.selections.all::<usize>(cx);
7918 if let Some(mut select_next_state) = self.select_next_state.take() {
7919 let query = &select_next_state.query;
7920 if !select_next_state.done {
7921 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
7922 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
7923 let mut next_selected_range = None;
7924
7925 let bytes_after_last_selection =
7926 buffer.bytes_in_range(last_selection.end..buffer.len());
7927 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
7928 let query_matches = query
7929 .stream_find_iter(bytes_after_last_selection)
7930 .map(|result| (last_selection.end, result))
7931 .chain(
7932 query
7933 .stream_find_iter(bytes_before_first_selection)
7934 .map(|result| (0, result)),
7935 );
7936
7937 for (start_offset, query_match) in query_matches {
7938 let query_match = query_match.unwrap(); // can only fail due to I/O
7939 let offset_range =
7940 start_offset + query_match.start()..start_offset + query_match.end();
7941 let display_range = offset_range.start.to_display_point(display_map)
7942 ..offset_range.end.to_display_point(display_map);
7943
7944 if !select_next_state.wordwise
7945 || (!movement::is_inside_word(display_map, display_range.start)
7946 && !movement::is_inside_word(display_map, display_range.end))
7947 {
7948 // TODO: This is n^2, because we might check all the selections
7949 if !selections
7950 .iter()
7951 .any(|selection| selection.range().overlaps(&offset_range))
7952 {
7953 next_selected_range = Some(offset_range);
7954 break;
7955 }
7956 }
7957 }
7958
7959 if let Some(next_selected_range) = next_selected_range {
7960 select_next_match_ranges(
7961 self,
7962 next_selected_range,
7963 replace_newest,
7964 autoscroll,
7965 cx,
7966 );
7967 } else {
7968 select_next_state.done = true;
7969 }
7970 }
7971
7972 self.select_next_state = Some(select_next_state);
7973 } else {
7974 let mut only_carets = true;
7975 let mut same_text_selected = true;
7976 let mut selected_text = None;
7977
7978 let mut selections_iter = selections.iter().peekable();
7979 while let Some(selection) = selections_iter.next() {
7980 if selection.start != selection.end {
7981 only_carets = false;
7982 }
7983
7984 if same_text_selected {
7985 if selected_text.is_none() {
7986 selected_text =
7987 Some(buffer.text_for_range(selection.range()).collect::<String>());
7988 }
7989
7990 if let Some(next_selection) = selections_iter.peek() {
7991 if next_selection.range().len() == selection.range().len() {
7992 let next_selected_text = buffer
7993 .text_for_range(next_selection.range())
7994 .collect::<String>();
7995 if Some(next_selected_text) != selected_text {
7996 same_text_selected = false;
7997 selected_text = None;
7998 }
7999 } else {
8000 same_text_selected = false;
8001 selected_text = None;
8002 }
8003 }
8004 }
8005 }
8006
8007 if only_carets {
8008 for selection in &mut selections {
8009 let word_range = movement::surrounding_word(
8010 display_map,
8011 selection.start.to_display_point(display_map),
8012 );
8013 selection.start = word_range.start.to_offset(display_map, Bias::Left);
8014 selection.end = word_range.end.to_offset(display_map, Bias::Left);
8015 selection.goal = SelectionGoal::None;
8016 selection.reversed = false;
8017 select_next_match_ranges(
8018 self,
8019 selection.start..selection.end,
8020 replace_newest,
8021 autoscroll,
8022 cx,
8023 );
8024 }
8025
8026 if selections.len() == 1 {
8027 let selection = selections
8028 .last()
8029 .expect("ensured that there's only one selection");
8030 let query = buffer
8031 .text_for_range(selection.start..selection.end)
8032 .collect::<String>();
8033 let is_empty = query.is_empty();
8034 let select_state = SelectNextState {
8035 query: AhoCorasick::new(&[query])?,
8036 wordwise: true,
8037 done: is_empty,
8038 };
8039 self.select_next_state = Some(select_state);
8040 } else {
8041 self.select_next_state = None;
8042 }
8043 } else if let Some(selected_text) = selected_text {
8044 self.select_next_state = Some(SelectNextState {
8045 query: AhoCorasick::new(&[selected_text])?,
8046 wordwise: false,
8047 done: false,
8048 });
8049 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
8050 }
8051 }
8052 Ok(())
8053 }
8054
8055 pub fn select_all_matches(
8056 &mut self,
8057 _action: &SelectAllMatches,
8058 cx: &mut ViewContext<Self>,
8059 ) -> Result<()> {
8060 self.push_to_selection_history();
8061 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8062
8063 self.select_next_match_internal(&display_map, false, None, cx)?;
8064 let Some(select_next_state) = self.select_next_state.as_mut() else {
8065 return Ok(());
8066 };
8067 if select_next_state.done {
8068 return Ok(());
8069 }
8070
8071 let mut new_selections = self.selections.all::<usize>(cx);
8072
8073 let buffer = &display_map.buffer_snapshot;
8074 let query_matches = select_next_state
8075 .query
8076 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
8077
8078 for query_match in query_matches {
8079 let query_match = query_match.unwrap(); // can only fail due to I/O
8080 let offset_range = query_match.start()..query_match.end();
8081 let display_range = offset_range.start.to_display_point(&display_map)
8082 ..offset_range.end.to_display_point(&display_map);
8083
8084 if !select_next_state.wordwise
8085 || (!movement::is_inside_word(&display_map, display_range.start)
8086 && !movement::is_inside_word(&display_map, display_range.end))
8087 {
8088 self.selections.change_with(cx, |selections| {
8089 new_selections.push(Selection {
8090 id: selections.new_selection_id(),
8091 start: offset_range.start,
8092 end: offset_range.end,
8093 reversed: false,
8094 goal: SelectionGoal::None,
8095 });
8096 });
8097 }
8098 }
8099
8100 new_selections.sort_by_key(|selection| selection.start);
8101 let mut ix = 0;
8102 while ix + 1 < new_selections.len() {
8103 let current_selection = &new_selections[ix];
8104 let next_selection = &new_selections[ix + 1];
8105 if current_selection.range().overlaps(&next_selection.range()) {
8106 if current_selection.id < next_selection.id {
8107 new_selections.remove(ix + 1);
8108 } else {
8109 new_selections.remove(ix);
8110 }
8111 } else {
8112 ix += 1;
8113 }
8114 }
8115
8116 select_next_state.done = true;
8117 self.unfold_ranges(
8118 &new_selections
8119 .iter()
8120 .map(|selection| selection.range())
8121 .collect::<Vec<_>>(),
8122 false,
8123 false,
8124 cx,
8125 );
8126 self.change_selections(Some(Autoscroll::fit()), cx, |selections| {
8127 selections.select(new_selections)
8128 });
8129
8130 Ok(())
8131 }
8132
8133 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
8134 self.push_to_selection_history();
8135 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8136 self.select_next_match_internal(
8137 &display_map,
8138 action.replace_newest,
8139 Some(Autoscroll::newest()),
8140 cx,
8141 )?;
8142 Ok(())
8143 }
8144
8145 pub fn select_previous(
8146 &mut self,
8147 action: &SelectPrevious,
8148 cx: &mut ViewContext<Self>,
8149 ) -> Result<()> {
8150 self.push_to_selection_history();
8151 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8152 let buffer = &display_map.buffer_snapshot;
8153 let mut selections = self.selections.all::<usize>(cx);
8154 if let Some(mut select_prev_state) = self.select_prev_state.take() {
8155 let query = &select_prev_state.query;
8156 if !select_prev_state.done {
8157 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
8158 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
8159 let mut next_selected_range = None;
8160 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
8161 let bytes_before_last_selection =
8162 buffer.reversed_bytes_in_range(0..last_selection.start);
8163 let bytes_after_first_selection =
8164 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
8165 let query_matches = query
8166 .stream_find_iter(bytes_before_last_selection)
8167 .map(|result| (last_selection.start, result))
8168 .chain(
8169 query
8170 .stream_find_iter(bytes_after_first_selection)
8171 .map(|result| (buffer.len(), result)),
8172 );
8173 for (end_offset, query_match) in query_matches {
8174 let query_match = query_match.unwrap(); // can only fail due to I/O
8175 let offset_range =
8176 end_offset - query_match.end()..end_offset - query_match.start();
8177 let display_range = offset_range.start.to_display_point(&display_map)
8178 ..offset_range.end.to_display_point(&display_map);
8179
8180 if !select_prev_state.wordwise
8181 || (!movement::is_inside_word(&display_map, display_range.start)
8182 && !movement::is_inside_word(&display_map, display_range.end))
8183 {
8184 next_selected_range = Some(offset_range);
8185 break;
8186 }
8187 }
8188
8189 if let Some(next_selected_range) = next_selected_range {
8190 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
8191 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8192 if action.replace_newest {
8193 s.delete(s.newest_anchor().id);
8194 }
8195 s.insert_range(next_selected_range);
8196 });
8197 } else {
8198 select_prev_state.done = true;
8199 }
8200 }
8201
8202 self.select_prev_state = Some(select_prev_state);
8203 } else {
8204 let mut only_carets = true;
8205 let mut same_text_selected = true;
8206 let mut selected_text = None;
8207
8208 let mut selections_iter = selections.iter().peekable();
8209 while let Some(selection) = selections_iter.next() {
8210 if selection.start != selection.end {
8211 only_carets = false;
8212 }
8213
8214 if same_text_selected {
8215 if selected_text.is_none() {
8216 selected_text =
8217 Some(buffer.text_for_range(selection.range()).collect::<String>());
8218 }
8219
8220 if let Some(next_selection) = selections_iter.peek() {
8221 if next_selection.range().len() == selection.range().len() {
8222 let next_selected_text = buffer
8223 .text_for_range(next_selection.range())
8224 .collect::<String>();
8225 if Some(next_selected_text) != selected_text {
8226 same_text_selected = false;
8227 selected_text = None;
8228 }
8229 } else {
8230 same_text_selected = false;
8231 selected_text = None;
8232 }
8233 }
8234 }
8235 }
8236
8237 if only_carets {
8238 for selection in &mut selections {
8239 let word_range = movement::surrounding_word(
8240 &display_map,
8241 selection.start.to_display_point(&display_map),
8242 );
8243 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
8244 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
8245 selection.goal = SelectionGoal::None;
8246 selection.reversed = false;
8247 }
8248 if selections.len() == 1 {
8249 let selection = selections
8250 .last()
8251 .expect("ensured that there's only one selection");
8252 let query = buffer
8253 .text_for_range(selection.start..selection.end)
8254 .collect::<String>();
8255 let is_empty = query.is_empty();
8256 let select_state = SelectNextState {
8257 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
8258 wordwise: true,
8259 done: is_empty,
8260 };
8261 self.select_prev_state = Some(select_state);
8262 } else {
8263 self.select_prev_state = None;
8264 }
8265
8266 self.unfold_ranges(
8267 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
8268 false,
8269 true,
8270 cx,
8271 );
8272 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8273 s.select(selections);
8274 });
8275 } else if let Some(selected_text) = selected_text {
8276 self.select_prev_state = Some(SelectNextState {
8277 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
8278 wordwise: false,
8279 done: false,
8280 });
8281 self.select_previous(action, cx)?;
8282 }
8283 }
8284 Ok(())
8285 }
8286
8287 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
8288 if self.read_only(cx) {
8289 return;
8290 }
8291 let text_layout_details = &self.text_layout_details(cx);
8292 self.transact(cx, |this, cx| {
8293 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
8294 let mut edits = Vec::new();
8295 let mut selection_edit_ranges = Vec::new();
8296 let mut last_toggled_row = None;
8297 let snapshot = this.buffer.read(cx).read(cx);
8298 let empty_str: Arc<str> = Arc::default();
8299 let mut suffixes_inserted = Vec::new();
8300 let ignore_indent = action.ignore_indent;
8301
8302 fn comment_prefix_range(
8303 snapshot: &MultiBufferSnapshot,
8304 row: MultiBufferRow,
8305 comment_prefix: &str,
8306 comment_prefix_whitespace: &str,
8307 ignore_indent: bool,
8308 ) -> Range<Point> {
8309 let indent_size = if ignore_indent {
8310 0
8311 } else {
8312 snapshot.indent_size_for_line(row).len
8313 };
8314
8315 let start = Point::new(row.0, indent_size);
8316
8317 let mut line_bytes = snapshot
8318 .bytes_in_range(start..snapshot.max_point())
8319 .flatten()
8320 .copied();
8321
8322 // If this line currently begins with the line comment prefix, then record
8323 // the range containing the prefix.
8324 if line_bytes
8325 .by_ref()
8326 .take(comment_prefix.len())
8327 .eq(comment_prefix.bytes())
8328 {
8329 // Include any whitespace that matches the comment prefix.
8330 let matching_whitespace_len = line_bytes
8331 .zip(comment_prefix_whitespace.bytes())
8332 .take_while(|(a, b)| a == b)
8333 .count() as u32;
8334 let end = Point::new(
8335 start.row,
8336 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
8337 );
8338 start..end
8339 } else {
8340 start..start
8341 }
8342 }
8343
8344 fn comment_suffix_range(
8345 snapshot: &MultiBufferSnapshot,
8346 row: MultiBufferRow,
8347 comment_suffix: &str,
8348 comment_suffix_has_leading_space: bool,
8349 ) -> Range<Point> {
8350 let end = Point::new(row.0, snapshot.line_len(row));
8351 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
8352
8353 let mut line_end_bytes = snapshot
8354 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
8355 .flatten()
8356 .copied();
8357
8358 let leading_space_len = if suffix_start_column > 0
8359 && line_end_bytes.next() == Some(b' ')
8360 && comment_suffix_has_leading_space
8361 {
8362 1
8363 } else {
8364 0
8365 };
8366
8367 // If this line currently begins with the line comment prefix, then record
8368 // the range containing the prefix.
8369 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
8370 let start = Point::new(end.row, suffix_start_column - leading_space_len);
8371 start..end
8372 } else {
8373 end..end
8374 }
8375 }
8376
8377 // TODO: Handle selections that cross excerpts
8378 for selection in &mut selections {
8379 let start_column = snapshot
8380 .indent_size_for_line(MultiBufferRow(selection.start.row))
8381 .len;
8382 let language = if let Some(language) =
8383 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
8384 {
8385 language
8386 } else {
8387 continue;
8388 };
8389
8390 selection_edit_ranges.clear();
8391
8392 // If multiple selections contain a given row, avoid processing that
8393 // row more than once.
8394 let mut start_row = MultiBufferRow(selection.start.row);
8395 if last_toggled_row == Some(start_row) {
8396 start_row = start_row.next_row();
8397 }
8398 let end_row =
8399 if selection.end.row > selection.start.row && selection.end.column == 0 {
8400 MultiBufferRow(selection.end.row - 1)
8401 } else {
8402 MultiBufferRow(selection.end.row)
8403 };
8404 last_toggled_row = Some(end_row);
8405
8406 if start_row > end_row {
8407 continue;
8408 }
8409
8410 // If the language has line comments, toggle those.
8411 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
8412
8413 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
8414 if ignore_indent {
8415 full_comment_prefixes = full_comment_prefixes
8416 .into_iter()
8417 .map(|s| Arc::from(s.trim_end()))
8418 .collect();
8419 }
8420
8421 if !full_comment_prefixes.is_empty() {
8422 let first_prefix = full_comment_prefixes
8423 .first()
8424 .expect("prefixes is non-empty");
8425 let prefix_trimmed_lengths = full_comment_prefixes
8426 .iter()
8427 .map(|p| p.trim_end_matches(' ').len())
8428 .collect::<SmallVec<[usize; 4]>>();
8429
8430 let mut all_selection_lines_are_comments = true;
8431
8432 for row in start_row.0..=end_row.0 {
8433 let row = MultiBufferRow(row);
8434 if start_row < end_row && snapshot.is_line_blank(row) {
8435 continue;
8436 }
8437
8438 let prefix_range = full_comment_prefixes
8439 .iter()
8440 .zip(prefix_trimmed_lengths.iter().copied())
8441 .map(|(prefix, trimmed_prefix_len)| {
8442 comment_prefix_range(
8443 snapshot.deref(),
8444 row,
8445 &prefix[..trimmed_prefix_len],
8446 &prefix[trimmed_prefix_len..],
8447 ignore_indent,
8448 )
8449 })
8450 .max_by_key(|range| range.end.column - range.start.column)
8451 .expect("prefixes is non-empty");
8452
8453 if prefix_range.is_empty() {
8454 all_selection_lines_are_comments = false;
8455 }
8456
8457 selection_edit_ranges.push(prefix_range);
8458 }
8459
8460 if all_selection_lines_are_comments {
8461 edits.extend(
8462 selection_edit_ranges
8463 .iter()
8464 .cloned()
8465 .map(|range| (range, empty_str.clone())),
8466 );
8467 } else {
8468 let min_column = selection_edit_ranges
8469 .iter()
8470 .map(|range| range.start.column)
8471 .min()
8472 .unwrap_or(0);
8473 edits.extend(selection_edit_ranges.iter().map(|range| {
8474 let position = Point::new(range.start.row, min_column);
8475 (position..position, first_prefix.clone())
8476 }));
8477 }
8478 } else if let Some((full_comment_prefix, comment_suffix)) =
8479 language.block_comment_delimiters()
8480 {
8481 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
8482 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
8483 let prefix_range = comment_prefix_range(
8484 snapshot.deref(),
8485 start_row,
8486 comment_prefix,
8487 comment_prefix_whitespace,
8488 ignore_indent,
8489 );
8490 let suffix_range = comment_suffix_range(
8491 snapshot.deref(),
8492 end_row,
8493 comment_suffix.trim_start_matches(' '),
8494 comment_suffix.starts_with(' '),
8495 );
8496
8497 if prefix_range.is_empty() || suffix_range.is_empty() {
8498 edits.push((
8499 prefix_range.start..prefix_range.start,
8500 full_comment_prefix.clone(),
8501 ));
8502 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
8503 suffixes_inserted.push((end_row, comment_suffix.len()));
8504 } else {
8505 edits.push((prefix_range, empty_str.clone()));
8506 edits.push((suffix_range, empty_str.clone()));
8507 }
8508 } else {
8509 continue;
8510 }
8511 }
8512
8513 drop(snapshot);
8514 this.buffer.update(cx, |buffer, cx| {
8515 buffer.edit(edits, None, cx);
8516 });
8517
8518 // Adjust selections so that they end before any comment suffixes that
8519 // were inserted.
8520 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
8521 let mut selections = this.selections.all::<Point>(cx);
8522 let snapshot = this.buffer.read(cx).read(cx);
8523 for selection in &mut selections {
8524 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
8525 match row.cmp(&MultiBufferRow(selection.end.row)) {
8526 Ordering::Less => {
8527 suffixes_inserted.next();
8528 continue;
8529 }
8530 Ordering::Greater => break,
8531 Ordering::Equal => {
8532 if selection.end.column == snapshot.line_len(row) {
8533 if selection.is_empty() {
8534 selection.start.column -= suffix_len as u32;
8535 }
8536 selection.end.column -= suffix_len as u32;
8537 }
8538 break;
8539 }
8540 }
8541 }
8542 }
8543
8544 drop(snapshot);
8545 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
8546
8547 let selections = this.selections.all::<Point>(cx);
8548 let selections_on_single_row = selections.windows(2).all(|selections| {
8549 selections[0].start.row == selections[1].start.row
8550 && selections[0].end.row == selections[1].end.row
8551 && selections[0].start.row == selections[0].end.row
8552 });
8553 let selections_selecting = selections
8554 .iter()
8555 .any(|selection| selection.start != selection.end);
8556 let advance_downwards = action.advance_downwards
8557 && selections_on_single_row
8558 && !selections_selecting
8559 && !matches!(this.mode, EditorMode::SingleLine { .. });
8560
8561 if advance_downwards {
8562 let snapshot = this.buffer.read(cx).snapshot(cx);
8563
8564 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8565 s.move_cursors_with(|display_snapshot, display_point, _| {
8566 let mut point = display_point.to_point(display_snapshot);
8567 point.row += 1;
8568 point = snapshot.clip_point(point, Bias::Left);
8569 let display_point = point.to_display_point(display_snapshot);
8570 let goal = SelectionGoal::HorizontalPosition(
8571 display_snapshot
8572 .x_for_display_point(display_point, text_layout_details)
8573 .into(),
8574 );
8575 (display_point, goal)
8576 })
8577 });
8578 }
8579 });
8580 }
8581
8582 pub fn select_enclosing_symbol(
8583 &mut self,
8584 _: &SelectEnclosingSymbol,
8585 cx: &mut ViewContext<Self>,
8586 ) {
8587 let buffer = self.buffer.read(cx).snapshot(cx);
8588 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8589
8590 fn update_selection(
8591 selection: &Selection<usize>,
8592 buffer_snap: &MultiBufferSnapshot,
8593 ) -> Option<Selection<usize>> {
8594 let cursor = selection.head();
8595 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
8596 for symbol in symbols.iter().rev() {
8597 let start = symbol.range.start.to_offset(buffer_snap);
8598 let end = symbol.range.end.to_offset(buffer_snap);
8599 let new_range = start..end;
8600 if start < selection.start || end > selection.end {
8601 return Some(Selection {
8602 id: selection.id,
8603 start: new_range.start,
8604 end: new_range.end,
8605 goal: SelectionGoal::None,
8606 reversed: selection.reversed,
8607 });
8608 }
8609 }
8610 None
8611 }
8612
8613 let mut selected_larger_symbol = false;
8614 let new_selections = old_selections
8615 .iter()
8616 .map(|selection| match update_selection(selection, &buffer) {
8617 Some(new_selection) => {
8618 if new_selection.range() != selection.range() {
8619 selected_larger_symbol = true;
8620 }
8621 new_selection
8622 }
8623 None => selection.clone(),
8624 })
8625 .collect::<Vec<_>>();
8626
8627 if selected_larger_symbol {
8628 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8629 s.select(new_selections);
8630 });
8631 }
8632 }
8633
8634 pub fn select_larger_syntax_node(
8635 &mut self,
8636 _: &SelectLargerSyntaxNode,
8637 cx: &mut ViewContext<Self>,
8638 ) {
8639 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8640 let buffer = self.buffer.read(cx).snapshot(cx);
8641 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8642
8643 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8644 let mut selected_larger_node = false;
8645 let new_selections = old_selections
8646 .iter()
8647 .map(|selection| {
8648 let old_range = selection.start..selection.end;
8649 let mut new_range = old_range.clone();
8650 while let Some(containing_range) =
8651 buffer.range_for_syntax_ancestor(new_range.clone())
8652 {
8653 new_range = containing_range;
8654 if !display_map.intersects_fold(new_range.start)
8655 && !display_map.intersects_fold(new_range.end)
8656 {
8657 break;
8658 }
8659 }
8660
8661 selected_larger_node |= new_range != old_range;
8662 Selection {
8663 id: selection.id,
8664 start: new_range.start,
8665 end: new_range.end,
8666 goal: SelectionGoal::None,
8667 reversed: selection.reversed,
8668 }
8669 })
8670 .collect::<Vec<_>>();
8671
8672 if selected_larger_node {
8673 stack.push(old_selections);
8674 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8675 s.select(new_selections);
8676 });
8677 }
8678 self.select_larger_syntax_node_stack = stack;
8679 }
8680
8681 pub fn select_smaller_syntax_node(
8682 &mut self,
8683 _: &SelectSmallerSyntaxNode,
8684 cx: &mut ViewContext<Self>,
8685 ) {
8686 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8687 if let Some(selections) = stack.pop() {
8688 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8689 s.select(selections.to_vec());
8690 });
8691 }
8692 self.select_larger_syntax_node_stack = stack;
8693 }
8694
8695 fn refresh_runnables(&mut self, cx: &mut ViewContext<Self>) -> Task<()> {
8696 if !EditorSettings::get_global(cx).gutter.runnables {
8697 self.clear_tasks();
8698 return Task::ready(());
8699 }
8700 let project = self.project.as_ref().map(Model::downgrade);
8701 cx.spawn(|this, mut cx| async move {
8702 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
8703 let Some(project) = project.and_then(|p| p.upgrade()) else {
8704 return;
8705 };
8706 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
8707 this.display_map.update(cx, |map, cx| map.snapshot(cx))
8708 }) else {
8709 return;
8710 };
8711
8712 let hide_runnables = project
8713 .update(&mut cx, |project, cx| {
8714 // Do not display any test indicators in non-dev server remote projects.
8715 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
8716 })
8717 .unwrap_or(true);
8718 if hide_runnables {
8719 return;
8720 }
8721 let new_rows =
8722 cx.background_executor()
8723 .spawn({
8724 let snapshot = display_snapshot.clone();
8725 async move {
8726 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
8727 }
8728 })
8729 .await;
8730 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
8731
8732 this.update(&mut cx, |this, _| {
8733 this.clear_tasks();
8734 for (key, value) in rows {
8735 this.insert_tasks(key, value);
8736 }
8737 })
8738 .ok();
8739 })
8740 }
8741 fn fetch_runnable_ranges(
8742 snapshot: &DisplaySnapshot,
8743 range: Range<Anchor>,
8744 ) -> Vec<language::RunnableRange> {
8745 snapshot.buffer_snapshot.runnable_ranges(range).collect()
8746 }
8747
8748 fn runnable_rows(
8749 project: Model<Project>,
8750 snapshot: DisplaySnapshot,
8751 runnable_ranges: Vec<RunnableRange>,
8752 mut cx: AsyncWindowContext,
8753 ) -> Vec<((BufferId, u32), RunnableTasks)> {
8754 runnable_ranges
8755 .into_iter()
8756 .filter_map(|mut runnable| {
8757 let tasks = cx
8758 .update(|cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
8759 .ok()?;
8760 if tasks.is_empty() {
8761 return None;
8762 }
8763
8764 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
8765
8766 let row = snapshot
8767 .buffer_snapshot
8768 .buffer_line_for_row(MultiBufferRow(point.row))?
8769 .1
8770 .start
8771 .row;
8772
8773 let context_range =
8774 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
8775 Some((
8776 (runnable.buffer_id, row),
8777 RunnableTasks {
8778 templates: tasks,
8779 offset: MultiBufferOffset(runnable.run_range.start),
8780 context_range,
8781 column: point.column,
8782 extra_variables: runnable.extra_captures,
8783 },
8784 ))
8785 })
8786 .collect()
8787 }
8788
8789 fn templates_with_tags(
8790 project: &Model<Project>,
8791 runnable: &mut Runnable,
8792 cx: &WindowContext<'_>,
8793 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
8794 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
8795 let (worktree_id, file) = project
8796 .buffer_for_id(runnable.buffer, cx)
8797 .and_then(|buffer| buffer.read(cx).file())
8798 .map(|file| (file.worktree_id(cx), file.clone()))
8799 .unzip();
8800
8801 (
8802 project.task_store().read(cx).task_inventory().cloned(),
8803 worktree_id,
8804 file,
8805 )
8806 });
8807
8808 let tags = mem::take(&mut runnable.tags);
8809 let mut tags: Vec<_> = tags
8810 .into_iter()
8811 .flat_map(|tag| {
8812 let tag = tag.0.clone();
8813 inventory
8814 .as_ref()
8815 .into_iter()
8816 .flat_map(|inventory| {
8817 inventory.read(cx).list_tasks(
8818 file.clone(),
8819 Some(runnable.language.clone()),
8820 worktree_id,
8821 cx,
8822 )
8823 })
8824 .filter(move |(_, template)| {
8825 template.tags.iter().any(|source_tag| source_tag == &tag)
8826 })
8827 })
8828 .sorted_by_key(|(kind, _)| kind.to_owned())
8829 .collect();
8830 if let Some((leading_tag_source, _)) = tags.first() {
8831 // Strongest source wins; if we have worktree tag binding, prefer that to
8832 // global and language bindings;
8833 // if we have a global binding, prefer that to language binding.
8834 let first_mismatch = tags
8835 .iter()
8836 .position(|(tag_source, _)| tag_source != leading_tag_source);
8837 if let Some(index) = first_mismatch {
8838 tags.truncate(index);
8839 }
8840 }
8841
8842 tags
8843 }
8844
8845 pub fn move_to_enclosing_bracket(
8846 &mut self,
8847 _: &MoveToEnclosingBracket,
8848 cx: &mut ViewContext<Self>,
8849 ) {
8850 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8851 s.move_offsets_with(|snapshot, selection| {
8852 let Some(enclosing_bracket_ranges) =
8853 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
8854 else {
8855 return;
8856 };
8857
8858 let mut best_length = usize::MAX;
8859 let mut best_inside = false;
8860 let mut best_in_bracket_range = false;
8861 let mut best_destination = None;
8862 for (open, close) in enclosing_bracket_ranges {
8863 let close = close.to_inclusive();
8864 let length = close.end() - open.start;
8865 let inside = selection.start >= open.end && selection.end <= *close.start();
8866 let in_bracket_range = open.to_inclusive().contains(&selection.head())
8867 || close.contains(&selection.head());
8868
8869 // If best is next to a bracket and current isn't, skip
8870 if !in_bracket_range && best_in_bracket_range {
8871 continue;
8872 }
8873
8874 // Prefer smaller lengths unless best is inside and current isn't
8875 if length > best_length && (best_inside || !inside) {
8876 continue;
8877 }
8878
8879 best_length = length;
8880 best_inside = inside;
8881 best_in_bracket_range = in_bracket_range;
8882 best_destination = Some(
8883 if close.contains(&selection.start) && close.contains(&selection.end) {
8884 if inside {
8885 open.end
8886 } else {
8887 open.start
8888 }
8889 } else if inside {
8890 *close.start()
8891 } else {
8892 *close.end()
8893 },
8894 );
8895 }
8896
8897 if let Some(destination) = best_destination {
8898 selection.collapse_to(destination, SelectionGoal::None);
8899 }
8900 })
8901 });
8902 }
8903
8904 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
8905 self.end_selection(cx);
8906 self.selection_history.mode = SelectionHistoryMode::Undoing;
8907 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
8908 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8909 self.select_next_state = entry.select_next_state;
8910 self.select_prev_state = entry.select_prev_state;
8911 self.add_selections_state = entry.add_selections_state;
8912 self.request_autoscroll(Autoscroll::newest(), cx);
8913 }
8914 self.selection_history.mode = SelectionHistoryMode::Normal;
8915 }
8916
8917 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
8918 self.end_selection(cx);
8919 self.selection_history.mode = SelectionHistoryMode::Redoing;
8920 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
8921 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8922 self.select_next_state = entry.select_next_state;
8923 self.select_prev_state = entry.select_prev_state;
8924 self.add_selections_state = entry.add_selections_state;
8925 self.request_autoscroll(Autoscroll::newest(), cx);
8926 }
8927 self.selection_history.mode = SelectionHistoryMode::Normal;
8928 }
8929
8930 pub fn expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
8931 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
8932 }
8933
8934 pub fn expand_excerpts_down(
8935 &mut self,
8936 action: &ExpandExcerptsDown,
8937 cx: &mut ViewContext<Self>,
8938 ) {
8939 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
8940 }
8941
8942 pub fn expand_excerpts_up(&mut self, action: &ExpandExcerptsUp, cx: &mut ViewContext<Self>) {
8943 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
8944 }
8945
8946 pub fn expand_excerpts_for_direction(
8947 &mut self,
8948 lines: u32,
8949 direction: ExpandExcerptDirection,
8950 cx: &mut ViewContext<Self>,
8951 ) {
8952 let selections = self.selections.disjoint_anchors();
8953
8954 let lines = if lines == 0 {
8955 EditorSettings::get_global(cx).expand_excerpt_lines
8956 } else {
8957 lines
8958 };
8959
8960 self.buffer.update(cx, |buffer, cx| {
8961 buffer.expand_excerpts(
8962 selections
8963 .iter()
8964 .map(|selection| selection.head().excerpt_id)
8965 .dedup(),
8966 lines,
8967 direction,
8968 cx,
8969 )
8970 })
8971 }
8972
8973 pub fn expand_excerpt(
8974 &mut self,
8975 excerpt: ExcerptId,
8976 direction: ExpandExcerptDirection,
8977 cx: &mut ViewContext<Self>,
8978 ) {
8979 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
8980 self.buffer.update(cx, |buffer, cx| {
8981 buffer.expand_excerpts([excerpt], lines, direction, cx)
8982 })
8983 }
8984
8985 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
8986 self.go_to_diagnostic_impl(Direction::Next, cx)
8987 }
8988
8989 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
8990 self.go_to_diagnostic_impl(Direction::Prev, cx)
8991 }
8992
8993 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
8994 let buffer = self.buffer.read(cx).snapshot(cx);
8995 let selection = self.selections.newest::<usize>(cx);
8996
8997 // If there is an active Diagnostic Popover jump to its diagnostic instead.
8998 if direction == Direction::Next {
8999 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
9000 let (group_id, jump_to) = popover.activation_info();
9001 if self.activate_diagnostics(group_id, cx) {
9002 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9003 let mut new_selection = s.newest_anchor().clone();
9004 new_selection.collapse_to(jump_to, SelectionGoal::None);
9005 s.select_anchors(vec![new_selection.clone()]);
9006 });
9007 }
9008 return;
9009 }
9010 }
9011
9012 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
9013 active_diagnostics
9014 .primary_range
9015 .to_offset(&buffer)
9016 .to_inclusive()
9017 });
9018 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
9019 if active_primary_range.contains(&selection.head()) {
9020 *active_primary_range.start()
9021 } else {
9022 selection.head()
9023 }
9024 } else {
9025 selection.head()
9026 };
9027 let snapshot = self.snapshot(cx);
9028 loop {
9029 let diagnostics = if direction == Direction::Prev {
9030 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
9031 } else {
9032 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
9033 }
9034 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start));
9035 let group = diagnostics
9036 // relies on diagnostics_in_range to return diagnostics with the same starting range to
9037 // be sorted in a stable way
9038 // skip until we are at current active diagnostic, if it exists
9039 .skip_while(|entry| {
9040 (match direction {
9041 Direction::Prev => entry.range.start >= search_start,
9042 Direction::Next => entry.range.start <= search_start,
9043 }) && self
9044 .active_diagnostics
9045 .as_ref()
9046 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
9047 })
9048 .find_map(|entry| {
9049 if entry.diagnostic.is_primary
9050 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
9051 && !entry.range.is_empty()
9052 // if we match with the active diagnostic, skip it
9053 && Some(entry.diagnostic.group_id)
9054 != self.active_diagnostics.as_ref().map(|d| d.group_id)
9055 {
9056 Some((entry.range, entry.diagnostic.group_id))
9057 } else {
9058 None
9059 }
9060 });
9061
9062 if let Some((primary_range, group_id)) = group {
9063 if self.activate_diagnostics(group_id, cx) {
9064 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9065 s.select(vec![Selection {
9066 id: selection.id,
9067 start: primary_range.start,
9068 end: primary_range.start,
9069 reversed: false,
9070 goal: SelectionGoal::None,
9071 }]);
9072 });
9073 }
9074 break;
9075 } else {
9076 // Cycle around to the start of the buffer, potentially moving back to the start of
9077 // the currently active diagnostic.
9078 active_primary_range.take();
9079 if direction == Direction::Prev {
9080 if search_start == buffer.len() {
9081 break;
9082 } else {
9083 search_start = buffer.len();
9084 }
9085 } else if search_start == 0 {
9086 break;
9087 } else {
9088 search_start = 0;
9089 }
9090 }
9091 }
9092 }
9093
9094 fn go_to_next_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
9095 let snapshot = self.snapshot(cx);
9096 let selection = self.selections.newest::<Point>(cx);
9097 self.go_to_hunk_after_position(&snapshot, selection.head(), cx);
9098 }
9099
9100 fn go_to_hunk_after_position(
9101 &mut self,
9102 snapshot: &EditorSnapshot,
9103 position: Point,
9104 cx: &mut ViewContext<'_, Editor>,
9105 ) -> Option<MultiBufferDiffHunk> {
9106 for (ix, position) in [position, Point::zero()].into_iter().enumerate() {
9107 if let Some(hunk) = self.go_to_next_hunk_in_direction(
9108 snapshot,
9109 position,
9110 ix > 0,
9111 snapshot.diff_map.diff_hunks_in_range(
9112 position + Point::new(1, 0)..snapshot.buffer_snapshot.max_point(),
9113 &snapshot.buffer_snapshot,
9114 ),
9115 cx,
9116 ) {
9117 return Some(hunk);
9118 }
9119 }
9120 None
9121 }
9122
9123 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
9124 let snapshot = self.snapshot(cx);
9125 let selection = self.selections.newest::<Point>(cx);
9126 self.go_to_hunk_before_position(&snapshot, selection.head(), cx);
9127 }
9128
9129 fn go_to_hunk_before_position(
9130 &mut self,
9131 snapshot: &EditorSnapshot,
9132 position: Point,
9133 cx: &mut ViewContext<'_, Editor>,
9134 ) -> Option<MultiBufferDiffHunk> {
9135 for (ix, position) in [position, snapshot.buffer_snapshot.max_point()]
9136 .into_iter()
9137 .enumerate()
9138 {
9139 if let Some(hunk) = self.go_to_next_hunk_in_direction(
9140 snapshot,
9141 position,
9142 ix > 0,
9143 snapshot
9144 .diff_map
9145 .diff_hunks_in_range_rev(Point::zero()..position, &snapshot.buffer_snapshot),
9146 cx,
9147 ) {
9148 return Some(hunk);
9149 }
9150 }
9151 None
9152 }
9153
9154 fn go_to_next_hunk_in_direction(
9155 &mut self,
9156 snapshot: &DisplaySnapshot,
9157 initial_point: Point,
9158 is_wrapped: bool,
9159 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
9160 cx: &mut ViewContext<Editor>,
9161 ) -> Option<MultiBufferDiffHunk> {
9162 let display_point = initial_point.to_display_point(snapshot);
9163 let mut hunks = hunks
9164 .map(|hunk| (diff_hunk_to_display(&hunk, snapshot), hunk))
9165 .filter(|(display_hunk, _)| {
9166 is_wrapped || !display_hunk.contains_display_row(display_point.row())
9167 })
9168 .dedup();
9169
9170 if let Some((display_hunk, hunk)) = hunks.next() {
9171 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9172 let row = display_hunk.start_display_row();
9173 let point = DisplayPoint::new(row, 0);
9174 s.select_display_ranges([point..point]);
9175 });
9176
9177 Some(hunk)
9178 } else {
9179 None
9180 }
9181 }
9182
9183 pub fn go_to_definition(
9184 &mut self,
9185 _: &GoToDefinition,
9186 cx: &mut ViewContext<Self>,
9187 ) -> Task<Result<Navigated>> {
9188 let definition = self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
9189 cx.spawn(|editor, mut cx| async move {
9190 if definition.await? == Navigated::Yes {
9191 return Ok(Navigated::Yes);
9192 }
9193 match editor.update(&mut cx, |editor, cx| {
9194 editor.find_all_references(&FindAllReferences, cx)
9195 })? {
9196 Some(references) => references.await,
9197 None => Ok(Navigated::No),
9198 }
9199 })
9200 }
9201
9202 pub fn go_to_declaration(
9203 &mut self,
9204 _: &GoToDeclaration,
9205 cx: &mut ViewContext<Self>,
9206 ) -> Task<Result<Navigated>> {
9207 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, cx)
9208 }
9209
9210 pub fn go_to_declaration_split(
9211 &mut self,
9212 _: &GoToDeclaration,
9213 cx: &mut ViewContext<Self>,
9214 ) -> Task<Result<Navigated>> {
9215 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, cx)
9216 }
9217
9218 pub fn go_to_implementation(
9219 &mut self,
9220 _: &GoToImplementation,
9221 cx: &mut ViewContext<Self>,
9222 ) -> Task<Result<Navigated>> {
9223 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx)
9224 }
9225
9226 pub fn go_to_implementation_split(
9227 &mut self,
9228 _: &GoToImplementationSplit,
9229 cx: &mut ViewContext<Self>,
9230 ) -> Task<Result<Navigated>> {
9231 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx)
9232 }
9233
9234 pub fn go_to_type_definition(
9235 &mut self,
9236 _: &GoToTypeDefinition,
9237 cx: &mut ViewContext<Self>,
9238 ) -> Task<Result<Navigated>> {
9239 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx)
9240 }
9241
9242 pub fn go_to_definition_split(
9243 &mut self,
9244 _: &GoToDefinitionSplit,
9245 cx: &mut ViewContext<Self>,
9246 ) -> Task<Result<Navigated>> {
9247 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx)
9248 }
9249
9250 pub fn go_to_type_definition_split(
9251 &mut self,
9252 _: &GoToTypeDefinitionSplit,
9253 cx: &mut ViewContext<Self>,
9254 ) -> Task<Result<Navigated>> {
9255 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx)
9256 }
9257
9258 fn go_to_definition_of_kind(
9259 &mut self,
9260 kind: GotoDefinitionKind,
9261 split: bool,
9262 cx: &mut ViewContext<Self>,
9263 ) -> Task<Result<Navigated>> {
9264 let Some(provider) = self.semantics_provider.clone() else {
9265 return Task::ready(Ok(Navigated::No));
9266 };
9267 let head = self.selections.newest::<usize>(cx).head();
9268 let buffer = self.buffer.read(cx);
9269 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
9270 text_anchor
9271 } else {
9272 return Task::ready(Ok(Navigated::No));
9273 };
9274
9275 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
9276 return Task::ready(Ok(Navigated::No));
9277 };
9278
9279 cx.spawn(|editor, mut cx| async move {
9280 let definitions = definitions.await?;
9281 let navigated = editor
9282 .update(&mut cx, |editor, cx| {
9283 editor.navigate_to_hover_links(
9284 Some(kind),
9285 definitions
9286 .into_iter()
9287 .filter(|location| {
9288 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
9289 })
9290 .map(HoverLink::Text)
9291 .collect::<Vec<_>>(),
9292 split,
9293 cx,
9294 )
9295 })?
9296 .await?;
9297 anyhow::Ok(navigated)
9298 })
9299 }
9300
9301 pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext<Self>) {
9302 let selection = self.selections.newest_anchor();
9303 let head = selection.head();
9304 let tail = selection.tail();
9305
9306 let Some((buffer, start_position)) =
9307 self.buffer.read(cx).text_anchor_for_position(head, cx)
9308 else {
9309 return;
9310 };
9311
9312 let end_position = if head != tail {
9313 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
9314 return;
9315 };
9316 Some(pos)
9317 } else {
9318 None
9319 };
9320
9321 let url_finder = cx.spawn(|editor, mut cx| async move {
9322 let url = if let Some(end_pos) = end_position {
9323 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
9324 } else {
9325 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
9326 };
9327
9328 if let Some(url) = url {
9329 editor.update(&mut cx, |_, cx| {
9330 cx.open_url(&url);
9331 })
9332 } else {
9333 Ok(())
9334 }
9335 });
9336
9337 url_finder.detach();
9338 }
9339
9340 pub fn open_file(&mut self, _: &OpenFile, cx: &mut ViewContext<Self>) {
9341 let Some(workspace) = self.workspace() else {
9342 return;
9343 };
9344
9345 let position = self.selections.newest_anchor().head();
9346
9347 let Some((buffer, buffer_position)) =
9348 self.buffer.read(cx).text_anchor_for_position(position, cx)
9349 else {
9350 return;
9351 };
9352
9353 let project = self.project.clone();
9354
9355 cx.spawn(|_, mut cx| async move {
9356 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
9357
9358 if let Some((_, path)) = result {
9359 workspace
9360 .update(&mut cx, |workspace, cx| {
9361 workspace.open_resolved_path(path, cx)
9362 })?
9363 .await?;
9364 }
9365 anyhow::Ok(())
9366 })
9367 .detach();
9368 }
9369
9370 pub(crate) fn navigate_to_hover_links(
9371 &mut self,
9372 kind: Option<GotoDefinitionKind>,
9373 mut definitions: Vec<HoverLink>,
9374 split: bool,
9375 cx: &mut ViewContext<Editor>,
9376 ) -> Task<Result<Navigated>> {
9377 // If there is one definition, just open it directly
9378 if definitions.len() == 1 {
9379 let definition = definitions.pop().unwrap();
9380
9381 enum TargetTaskResult {
9382 Location(Option<Location>),
9383 AlreadyNavigated,
9384 }
9385
9386 let target_task = match definition {
9387 HoverLink::Text(link) => {
9388 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
9389 }
9390 HoverLink::InlayHint(lsp_location, server_id) => {
9391 let computation = self.compute_target_location(lsp_location, server_id, cx);
9392 cx.background_executor().spawn(async move {
9393 let location = computation.await?;
9394 Ok(TargetTaskResult::Location(location))
9395 })
9396 }
9397 HoverLink::Url(url) => {
9398 cx.open_url(&url);
9399 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
9400 }
9401 HoverLink::File(path) => {
9402 if let Some(workspace) = self.workspace() {
9403 cx.spawn(|_, mut cx| async move {
9404 workspace
9405 .update(&mut cx, |workspace, cx| {
9406 workspace.open_resolved_path(path, cx)
9407 })?
9408 .await
9409 .map(|_| TargetTaskResult::AlreadyNavigated)
9410 })
9411 } else {
9412 Task::ready(Ok(TargetTaskResult::Location(None)))
9413 }
9414 }
9415 };
9416 cx.spawn(|editor, mut cx| async move {
9417 let target = match target_task.await.context("target resolution task")? {
9418 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
9419 TargetTaskResult::Location(None) => return Ok(Navigated::No),
9420 TargetTaskResult::Location(Some(target)) => target,
9421 };
9422
9423 editor.update(&mut cx, |editor, cx| {
9424 let Some(workspace) = editor.workspace() else {
9425 return Navigated::No;
9426 };
9427 let pane = workspace.read(cx).active_pane().clone();
9428
9429 let range = target.range.to_offset(target.buffer.read(cx));
9430 let range = editor.range_for_match(&range);
9431
9432 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
9433 let buffer = target.buffer.read(cx);
9434 let range = check_multiline_range(buffer, range);
9435 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
9436 s.select_ranges([range]);
9437 });
9438 } else {
9439 cx.window_context().defer(move |cx| {
9440 let target_editor: View<Self> =
9441 workspace.update(cx, |workspace, cx| {
9442 let pane = if split {
9443 workspace.adjacent_pane(cx)
9444 } else {
9445 workspace.active_pane().clone()
9446 };
9447
9448 workspace.open_project_item(
9449 pane,
9450 target.buffer.clone(),
9451 true,
9452 true,
9453 cx,
9454 )
9455 });
9456 target_editor.update(cx, |target_editor, cx| {
9457 // When selecting a definition in a different buffer, disable the nav history
9458 // to avoid creating a history entry at the previous cursor location.
9459 pane.update(cx, |pane, _| pane.disable_history());
9460 let buffer = target.buffer.read(cx);
9461 let range = check_multiline_range(buffer, range);
9462 target_editor.change_selections(
9463 Some(Autoscroll::focused()),
9464 cx,
9465 |s| {
9466 s.select_ranges([range]);
9467 },
9468 );
9469 pane.update(cx, |pane, _| pane.enable_history());
9470 });
9471 });
9472 }
9473 Navigated::Yes
9474 })
9475 })
9476 } else if !definitions.is_empty() {
9477 cx.spawn(|editor, mut cx| async move {
9478 let (title, location_tasks, workspace) = editor
9479 .update(&mut cx, |editor, cx| {
9480 let tab_kind = match kind {
9481 Some(GotoDefinitionKind::Implementation) => "Implementations",
9482 _ => "Definitions",
9483 };
9484 let title = definitions
9485 .iter()
9486 .find_map(|definition| match definition {
9487 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
9488 let buffer = origin.buffer.read(cx);
9489 format!(
9490 "{} for {}",
9491 tab_kind,
9492 buffer
9493 .text_for_range(origin.range.clone())
9494 .collect::<String>()
9495 )
9496 }),
9497 HoverLink::InlayHint(_, _) => None,
9498 HoverLink::Url(_) => None,
9499 HoverLink::File(_) => None,
9500 })
9501 .unwrap_or(tab_kind.to_string());
9502 let location_tasks = definitions
9503 .into_iter()
9504 .map(|definition| match definition {
9505 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
9506 HoverLink::InlayHint(lsp_location, server_id) => {
9507 editor.compute_target_location(lsp_location, server_id, cx)
9508 }
9509 HoverLink::Url(_) => Task::ready(Ok(None)),
9510 HoverLink::File(_) => Task::ready(Ok(None)),
9511 })
9512 .collect::<Vec<_>>();
9513 (title, location_tasks, editor.workspace().clone())
9514 })
9515 .context("location tasks preparation")?;
9516
9517 let locations = future::join_all(location_tasks)
9518 .await
9519 .into_iter()
9520 .filter_map(|location| location.transpose())
9521 .collect::<Result<_>>()
9522 .context("location tasks")?;
9523
9524 let Some(workspace) = workspace else {
9525 return Ok(Navigated::No);
9526 };
9527 let opened = workspace
9528 .update(&mut cx, |workspace, cx| {
9529 Self::open_locations_in_multibuffer(workspace, locations, title, split, cx)
9530 })
9531 .ok();
9532
9533 anyhow::Ok(Navigated::from_bool(opened.is_some()))
9534 })
9535 } else {
9536 Task::ready(Ok(Navigated::No))
9537 }
9538 }
9539
9540 fn compute_target_location(
9541 &self,
9542 lsp_location: lsp::Location,
9543 server_id: LanguageServerId,
9544 cx: &mut ViewContext<Self>,
9545 ) -> Task<anyhow::Result<Option<Location>>> {
9546 let Some(project) = self.project.clone() else {
9547 return Task::Ready(Some(Ok(None)));
9548 };
9549
9550 cx.spawn(move |editor, mut cx| async move {
9551 let location_task = editor.update(&mut cx, |_, cx| {
9552 project.update(cx, |project, cx| {
9553 let language_server_name = project
9554 .language_server_statuses(cx)
9555 .find(|(id, _)| server_id == *id)
9556 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
9557 language_server_name.map(|language_server_name| {
9558 project.open_local_buffer_via_lsp(
9559 lsp_location.uri.clone(),
9560 server_id,
9561 language_server_name,
9562 cx,
9563 )
9564 })
9565 })
9566 })?;
9567 let location = match location_task {
9568 Some(task) => Some({
9569 let target_buffer_handle = task.await.context("open local buffer")?;
9570 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
9571 let target_start = target_buffer
9572 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
9573 let target_end = target_buffer
9574 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
9575 target_buffer.anchor_after(target_start)
9576 ..target_buffer.anchor_before(target_end)
9577 })?;
9578 Location {
9579 buffer: target_buffer_handle,
9580 range,
9581 }
9582 }),
9583 None => None,
9584 };
9585 Ok(location)
9586 })
9587 }
9588
9589 pub fn find_all_references(
9590 &mut self,
9591 _: &FindAllReferences,
9592 cx: &mut ViewContext<Self>,
9593 ) -> Option<Task<Result<Navigated>>> {
9594 let selection = self.selections.newest::<usize>(cx);
9595 let multi_buffer = self.buffer.read(cx);
9596 let head = selection.head();
9597
9598 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
9599 let head_anchor = multi_buffer_snapshot.anchor_at(
9600 head,
9601 if head < selection.tail() {
9602 Bias::Right
9603 } else {
9604 Bias::Left
9605 },
9606 );
9607
9608 match self
9609 .find_all_references_task_sources
9610 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
9611 {
9612 Ok(_) => {
9613 log::info!(
9614 "Ignoring repeated FindAllReferences invocation with the position of already running task"
9615 );
9616 return None;
9617 }
9618 Err(i) => {
9619 self.find_all_references_task_sources.insert(i, head_anchor);
9620 }
9621 }
9622
9623 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
9624 let workspace = self.workspace()?;
9625 let project = workspace.read(cx).project().clone();
9626 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
9627 Some(cx.spawn(|editor, mut cx| async move {
9628 let _cleanup = defer({
9629 let mut cx = cx.clone();
9630 move || {
9631 let _ = editor.update(&mut cx, |editor, _| {
9632 if let Ok(i) =
9633 editor
9634 .find_all_references_task_sources
9635 .binary_search_by(|anchor| {
9636 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
9637 })
9638 {
9639 editor.find_all_references_task_sources.remove(i);
9640 }
9641 });
9642 }
9643 });
9644
9645 let locations = references.await?;
9646 if locations.is_empty() {
9647 return anyhow::Ok(Navigated::No);
9648 }
9649
9650 workspace.update(&mut cx, |workspace, cx| {
9651 let title = locations
9652 .first()
9653 .as_ref()
9654 .map(|location| {
9655 let buffer = location.buffer.read(cx);
9656 format!(
9657 "References to `{}`",
9658 buffer
9659 .text_for_range(location.range.clone())
9660 .collect::<String>()
9661 )
9662 })
9663 .unwrap();
9664 Self::open_locations_in_multibuffer(workspace, locations, title, false, cx);
9665 Navigated::Yes
9666 })
9667 }))
9668 }
9669
9670 /// Opens a multibuffer with the given project locations in it
9671 pub fn open_locations_in_multibuffer(
9672 workspace: &mut Workspace,
9673 mut locations: Vec<Location>,
9674 title: String,
9675 split: bool,
9676 cx: &mut ViewContext<Workspace>,
9677 ) {
9678 // If there are multiple definitions, open them in a multibuffer
9679 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
9680 let mut locations = locations.into_iter().peekable();
9681 let mut ranges_to_highlight = Vec::new();
9682 let capability = workspace.project().read(cx).capability();
9683
9684 let excerpt_buffer = cx.new_model(|cx| {
9685 let mut multibuffer = MultiBuffer::new(capability);
9686 while let Some(location) = locations.next() {
9687 let buffer = location.buffer.read(cx);
9688 let mut ranges_for_buffer = Vec::new();
9689 let range = location.range.to_offset(buffer);
9690 ranges_for_buffer.push(range.clone());
9691
9692 while let Some(next_location) = locations.peek() {
9693 if next_location.buffer == location.buffer {
9694 ranges_for_buffer.push(next_location.range.to_offset(buffer));
9695 locations.next();
9696 } else {
9697 break;
9698 }
9699 }
9700
9701 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
9702 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
9703 location.buffer.clone(),
9704 ranges_for_buffer,
9705 DEFAULT_MULTIBUFFER_CONTEXT,
9706 cx,
9707 ))
9708 }
9709
9710 multibuffer.with_title(title)
9711 });
9712
9713 let editor = cx.new_view(|cx| {
9714 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), true, cx)
9715 });
9716 editor.update(cx, |editor, cx| {
9717 if let Some(first_range) = ranges_to_highlight.first() {
9718 editor.change_selections(None, cx, |selections| {
9719 selections.clear_disjoint();
9720 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
9721 });
9722 }
9723 editor.highlight_background::<Self>(
9724 &ranges_to_highlight,
9725 |theme| theme.editor_highlighted_line_background,
9726 cx,
9727 );
9728 editor.register_buffers_with_language_servers(cx);
9729 });
9730
9731 let item = Box::new(editor);
9732 let item_id = item.item_id();
9733
9734 if split {
9735 workspace.split_item(SplitDirection::Right, item.clone(), cx);
9736 } else {
9737 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
9738 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
9739 pane.close_current_preview_item(cx)
9740 } else {
9741 None
9742 }
9743 });
9744 workspace.add_item_to_active_pane(item.clone(), destination_index, true, cx);
9745 }
9746 workspace.active_pane().update(cx, |pane, cx| {
9747 pane.set_preview_item_id(Some(item_id), cx);
9748 });
9749 }
9750
9751 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9752 use language::ToOffset as _;
9753
9754 let provider = self.semantics_provider.clone()?;
9755 let selection = self.selections.newest_anchor().clone();
9756 let (cursor_buffer, cursor_buffer_position) = self
9757 .buffer
9758 .read(cx)
9759 .text_anchor_for_position(selection.head(), cx)?;
9760 let (tail_buffer, cursor_buffer_position_end) = self
9761 .buffer
9762 .read(cx)
9763 .text_anchor_for_position(selection.tail(), cx)?;
9764 if tail_buffer != cursor_buffer {
9765 return None;
9766 }
9767
9768 let snapshot = cursor_buffer.read(cx).snapshot();
9769 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
9770 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
9771 let prepare_rename = provider
9772 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
9773 .unwrap_or_else(|| Task::ready(Ok(None)));
9774 drop(snapshot);
9775
9776 Some(cx.spawn(|this, mut cx| async move {
9777 let rename_range = if let Some(range) = prepare_rename.await? {
9778 Some(range)
9779 } else {
9780 this.update(&mut cx, |this, cx| {
9781 let buffer = this.buffer.read(cx).snapshot(cx);
9782 let mut buffer_highlights = this
9783 .document_highlights_for_position(selection.head(), &buffer)
9784 .filter(|highlight| {
9785 highlight.start.excerpt_id == selection.head().excerpt_id
9786 && highlight.end.excerpt_id == selection.head().excerpt_id
9787 });
9788 buffer_highlights
9789 .next()
9790 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
9791 })?
9792 };
9793 if let Some(rename_range) = rename_range {
9794 this.update(&mut cx, |this, cx| {
9795 let snapshot = cursor_buffer.read(cx).snapshot();
9796 let rename_buffer_range = rename_range.to_offset(&snapshot);
9797 let cursor_offset_in_rename_range =
9798 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
9799 let cursor_offset_in_rename_range_end =
9800 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
9801
9802 this.take_rename(false, cx);
9803 let buffer = this.buffer.read(cx).read(cx);
9804 let cursor_offset = selection.head().to_offset(&buffer);
9805 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
9806 let rename_end = rename_start + rename_buffer_range.len();
9807 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
9808 let mut old_highlight_id = None;
9809 let old_name: Arc<str> = buffer
9810 .chunks(rename_start..rename_end, true)
9811 .map(|chunk| {
9812 if old_highlight_id.is_none() {
9813 old_highlight_id = chunk.syntax_highlight_id;
9814 }
9815 chunk.text
9816 })
9817 .collect::<String>()
9818 .into();
9819
9820 drop(buffer);
9821
9822 // Position the selection in the rename editor so that it matches the current selection.
9823 this.show_local_selections = false;
9824 let rename_editor = cx.new_view(|cx| {
9825 let mut editor = Editor::single_line(cx);
9826 editor.buffer.update(cx, |buffer, cx| {
9827 buffer.edit([(0..0, old_name.clone())], None, cx)
9828 });
9829 let rename_selection_range = match cursor_offset_in_rename_range
9830 .cmp(&cursor_offset_in_rename_range_end)
9831 {
9832 Ordering::Equal => {
9833 editor.select_all(&SelectAll, cx);
9834 return editor;
9835 }
9836 Ordering::Less => {
9837 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
9838 }
9839 Ordering::Greater => {
9840 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
9841 }
9842 };
9843 if rename_selection_range.end > old_name.len() {
9844 editor.select_all(&SelectAll, cx);
9845 } else {
9846 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
9847 s.select_ranges([rename_selection_range]);
9848 });
9849 }
9850 editor
9851 });
9852 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
9853 if e == &EditorEvent::Focused {
9854 cx.emit(EditorEvent::FocusedIn)
9855 }
9856 })
9857 .detach();
9858
9859 let write_highlights =
9860 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
9861 let read_highlights =
9862 this.clear_background_highlights::<DocumentHighlightRead>(cx);
9863 let ranges = write_highlights
9864 .iter()
9865 .flat_map(|(_, ranges)| ranges.iter())
9866 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
9867 .cloned()
9868 .collect();
9869
9870 this.highlight_text::<Rename>(
9871 ranges,
9872 HighlightStyle {
9873 fade_out: Some(0.6),
9874 ..Default::default()
9875 },
9876 cx,
9877 );
9878 let rename_focus_handle = rename_editor.focus_handle(cx);
9879 cx.focus(&rename_focus_handle);
9880 let block_id = this.insert_blocks(
9881 [BlockProperties {
9882 style: BlockStyle::Flex,
9883 placement: BlockPlacement::Below(range.start),
9884 height: 1,
9885 render: Arc::new({
9886 let rename_editor = rename_editor.clone();
9887 move |cx: &mut BlockContext| {
9888 let mut text_style = cx.editor_style.text.clone();
9889 if let Some(highlight_style) = old_highlight_id
9890 .and_then(|h| h.style(&cx.editor_style.syntax))
9891 {
9892 text_style = text_style.highlight(highlight_style);
9893 }
9894 div()
9895 .block_mouse_down()
9896 .pl(cx.anchor_x)
9897 .child(EditorElement::new(
9898 &rename_editor,
9899 EditorStyle {
9900 background: cx.theme().system().transparent,
9901 local_player: cx.editor_style.local_player,
9902 text: text_style,
9903 scrollbar_width: cx.editor_style.scrollbar_width,
9904 syntax: cx.editor_style.syntax.clone(),
9905 status: cx.editor_style.status.clone(),
9906 inlay_hints_style: HighlightStyle {
9907 font_weight: Some(FontWeight::BOLD),
9908 ..make_inlay_hints_style(cx)
9909 },
9910 inline_completion_styles: make_suggestion_styles(
9911 cx,
9912 ),
9913 ..EditorStyle::default()
9914 },
9915 ))
9916 .into_any_element()
9917 }
9918 }),
9919 priority: 0,
9920 }],
9921 Some(Autoscroll::fit()),
9922 cx,
9923 )[0];
9924 this.pending_rename = Some(RenameState {
9925 range,
9926 old_name,
9927 editor: rename_editor,
9928 block_id,
9929 });
9930 })?;
9931 }
9932
9933 Ok(())
9934 }))
9935 }
9936
9937 pub fn confirm_rename(
9938 &mut self,
9939 _: &ConfirmRename,
9940 cx: &mut ViewContext<Self>,
9941 ) -> Option<Task<Result<()>>> {
9942 let rename = self.take_rename(false, cx)?;
9943 let workspace = self.workspace()?.downgrade();
9944 let (buffer, start) = self
9945 .buffer
9946 .read(cx)
9947 .text_anchor_for_position(rename.range.start, cx)?;
9948 let (end_buffer, _) = self
9949 .buffer
9950 .read(cx)
9951 .text_anchor_for_position(rename.range.end, cx)?;
9952 if buffer != end_buffer {
9953 return None;
9954 }
9955
9956 let old_name = rename.old_name;
9957 let new_name = rename.editor.read(cx).text(cx);
9958
9959 let rename = self.semantics_provider.as_ref()?.perform_rename(
9960 &buffer,
9961 start,
9962 new_name.clone(),
9963 cx,
9964 )?;
9965
9966 Some(cx.spawn(|editor, mut cx| async move {
9967 let project_transaction = rename.await?;
9968 Self::open_project_transaction(
9969 &editor,
9970 workspace,
9971 project_transaction,
9972 format!("Rename: {} → {}", old_name, new_name),
9973 cx.clone(),
9974 )
9975 .await?;
9976
9977 editor.update(&mut cx, |editor, cx| {
9978 editor.refresh_document_highlights(cx);
9979 })?;
9980 Ok(())
9981 }))
9982 }
9983
9984 fn take_rename(
9985 &mut self,
9986 moving_cursor: bool,
9987 cx: &mut ViewContext<Self>,
9988 ) -> Option<RenameState> {
9989 let rename = self.pending_rename.take()?;
9990 if rename.editor.focus_handle(cx).is_focused(cx) {
9991 cx.focus(&self.focus_handle);
9992 }
9993
9994 self.remove_blocks(
9995 [rename.block_id].into_iter().collect(),
9996 Some(Autoscroll::fit()),
9997 cx,
9998 );
9999 self.clear_highlights::<Rename>(cx);
10000 self.show_local_selections = true;
10001
10002 if moving_cursor {
10003 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
10004 editor.selections.newest::<usize>(cx).head()
10005 });
10006
10007 // Update the selection to match the position of the selection inside
10008 // the rename editor.
10009 let snapshot = self.buffer.read(cx).read(cx);
10010 let rename_range = rename.range.to_offset(&snapshot);
10011 let cursor_in_editor = snapshot
10012 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
10013 .min(rename_range.end);
10014 drop(snapshot);
10015
10016 self.change_selections(None, cx, |s| {
10017 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
10018 });
10019 } else {
10020 self.refresh_document_highlights(cx);
10021 }
10022
10023 Some(rename)
10024 }
10025
10026 pub fn pending_rename(&self) -> Option<&RenameState> {
10027 self.pending_rename.as_ref()
10028 }
10029
10030 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
10031 let project = match &self.project {
10032 Some(project) => project.clone(),
10033 None => return None,
10034 };
10035
10036 Some(self.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffer, cx))
10037 }
10038
10039 fn format_selections(
10040 &mut self,
10041 _: &FormatSelections,
10042 cx: &mut ViewContext<Self>,
10043 ) -> Option<Task<Result<()>>> {
10044 let project = match &self.project {
10045 Some(project) => project.clone(),
10046 None => return None,
10047 };
10048
10049 let selections = self
10050 .selections
10051 .all_adjusted(cx)
10052 .into_iter()
10053 .filter(|s| !s.is_empty())
10054 .collect_vec();
10055
10056 Some(self.perform_format(
10057 project,
10058 FormatTrigger::Manual,
10059 FormatTarget::Ranges(selections),
10060 cx,
10061 ))
10062 }
10063
10064 fn perform_format(
10065 &mut self,
10066 project: Model<Project>,
10067 trigger: FormatTrigger,
10068 target: FormatTarget,
10069 cx: &mut ViewContext<Self>,
10070 ) -> Task<Result<()>> {
10071 let buffer = self.buffer().clone();
10072 let mut buffers = buffer.read(cx).all_buffers();
10073 if trigger == FormatTrigger::Save {
10074 buffers.retain(|buffer| buffer.read(cx).is_dirty());
10075 }
10076
10077 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
10078 let format = project.update(cx, |project, cx| {
10079 project.format(buffers, true, trigger, target, cx)
10080 });
10081
10082 cx.spawn(|_, mut cx| async move {
10083 let transaction = futures::select_biased! {
10084 () = timeout => {
10085 log::warn!("timed out waiting for formatting");
10086 None
10087 }
10088 transaction = format.log_err().fuse() => transaction,
10089 };
10090
10091 buffer
10092 .update(&mut cx, |buffer, cx| {
10093 if let Some(transaction) = transaction {
10094 if !buffer.is_singleton() {
10095 buffer.push_transaction(&transaction.0, cx);
10096 }
10097 }
10098
10099 cx.notify();
10100 })
10101 .ok();
10102
10103 Ok(())
10104 })
10105 }
10106
10107 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
10108 if let Some(project) = self.project.clone() {
10109 self.buffer.update(cx, |multi_buffer, cx| {
10110 project.update(cx, |project, cx| {
10111 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
10112 });
10113 })
10114 }
10115 }
10116
10117 fn cancel_language_server_work(
10118 &mut self,
10119 _: &actions::CancelLanguageServerWork,
10120 cx: &mut ViewContext<Self>,
10121 ) {
10122 if let Some(project) = self.project.clone() {
10123 self.buffer.update(cx, |multi_buffer, cx| {
10124 project.update(cx, |project, cx| {
10125 project.cancel_language_server_work_for_buffers(multi_buffer.all_buffers(), cx);
10126 });
10127 })
10128 }
10129 }
10130
10131 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
10132 cx.show_character_palette();
10133 }
10134
10135 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
10136 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
10137 let buffer = self.buffer.read(cx).snapshot(cx);
10138 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
10139 let is_valid = buffer
10140 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
10141 .any(|entry| {
10142 entry.diagnostic.is_primary
10143 && !entry.range.is_empty()
10144 && entry.range.start == primary_range_start
10145 && entry.diagnostic.message == active_diagnostics.primary_message
10146 });
10147
10148 if is_valid != active_diagnostics.is_valid {
10149 active_diagnostics.is_valid = is_valid;
10150 let mut new_styles = HashMap::default();
10151 for (block_id, diagnostic) in &active_diagnostics.blocks {
10152 new_styles.insert(
10153 *block_id,
10154 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
10155 );
10156 }
10157 self.display_map.update(cx, |display_map, _cx| {
10158 display_map.replace_blocks(new_styles)
10159 });
10160 }
10161 }
10162 }
10163
10164 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
10165 self.dismiss_diagnostics(cx);
10166 let snapshot = self.snapshot(cx);
10167 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
10168 let buffer = self.buffer.read(cx).snapshot(cx);
10169
10170 let mut primary_range = None;
10171 let mut primary_message = None;
10172 let mut group_end = Point::zero();
10173 let diagnostic_group = buffer
10174 .diagnostic_group::<MultiBufferPoint>(group_id)
10175 .filter_map(|entry| {
10176 if snapshot.is_line_folded(MultiBufferRow(entry.range.start.row))
10177 && (entry.range.start.row == entry.range.end.row
10178 || snapshot.is_line_folded(MultiBufferRow(entry.range.end.row)))
10179 {
10180 return None;
10181 }
10182 if entry.range.end > group_end {
10183 group_end = entry.range.end;
10184 }
10185 if entry.diagnostic.is_primary {
10186 primary_range = Some(entry.range.clone());
10187 primary_message = Some(entry.diagnostic.message.clone());
10188 }
10189 Some(entry)
10190 })
10191 .collect::<Vec<_>>();
10192 let primary_range = primary_range?;
10193 let primary_message = primary_message?;
10194 let primary_range =
10195 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
10196
10197 let blocks = display_map
10198 .insert_blocks(
10199 diagnostic_group.iter().map(|entry| {
10200 let diagnostic = entry.diagnostic.clone();
10201 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
10202 BlockProperties {
10203 style: BlockStyle::Fixed,
10204 placement: BlockPlacement::Below(
10205 buffer.anchor_after(entry.range.start),
10206 ),
10207 height: message_height,
10208 render: diagnostic_block_renderer(diagnostic, None, true, true),
10209 priority: 0,
10210 }
10211 }),
10212 cx,
10213 )
10214 .into_iter()
10215 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
10216 .collect();
10217
10218 Some(ActiveDiagnosticGroup {
10219 primary_range,
10220 primary_message,
10221 group_id,
10222 blocks,
10223 is_valid: true,
10224 })
10225 });
10226 self.active_diagnostics.is_some()
10227 }
10228
10229 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
10230 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
10231 self.display_map.update(cx, |display_map, cx| {
10232 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
10233 });
10234 cx.notify();
10235 }
10236 }
10237
10238 pub fn set_selections_from_remote(
10239 &mut self,
10240 selections: Vec<Selection<Anchor>>,
10241 pending_selection: Option<Selection<Anchor>>,
10242 cx: &mut ViewContext<Self>,
10243 ) {
10244 let old_cursor_position = self.selections.newest_anchor().head();
10245 self.selections.change_with(cx, |s| {
10246 s.select_anchors(selections);
10247 if let Some(pending_selection) = pending_selection {
10248 s.set_pending(pending_selection, SelectMode::Character);
10249 } else {
10250 s.clear_pending();
10251 }
10252 });
10253 self.selections_did_change(false, &old_cursor_position, true, cx);
10254 }
10255
10256 fn push_to_selection_history(&mut self) {
10257 self.selection_history.push(SelectionHistoryEntry {
10258 selections: self.selections.disjoint_anchors(),
10259 select_next_state: self.select_next_state.clone(),
10260 select_prev_state: self.select_prev_state.clone(),
10261 add_selections_state: self.add_selections_state.clone(),
10262 });
10263 }
10264
10265 pub fn transact(
10266 &mut self,
10267 cx: &mut ViewContext<Self>,
10268 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
10269 ) -> Option<TransactionId> {
10270 self.start_transaction_at(Instant::now(), cx);
10271 update(self, cx);
10272 self.end_transaction_at(Instant::now(), cx)
10273 }
10274
10275 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
10276 self.end_selection(cx);
10277 if let Some(tx_id) = self
10278 .buffer
10279 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
10280 {
10281 self.selection_history
10282 .insert_transaction(tx_id, self.selections.disjoint_anchors());
10283 cx.emit(EditorEvent::TransactionBegun {
10284 transaction_id: tx_id,
10285 })
10286 }
10287 }
10288
10289 fn end_transaction_at(
10290 &mut self,
10291 now: Instant,
10292 cx: &mut ViewContext<Self>,
10293 ) -> Option<TransactionId> {
10294 if let Some(transaction_id) = self
10295 .buffer
10296 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
10297 {
10298 if let Some((_, end_selections)) =
10299 self.selection_history.transaction_mut(transaction_id)
10300 {
10301 *end_selections = Some(self.selections.disjoint_anchors());
10302 } else {
10303 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
10304 }
10305
10306 cx.emit(EditorEvent::Edited { transaction_id });
10307 Some(transaction_id)
10308 } else {
10309 None
10310 }
10311 }
10312
10313 pub fn toggle_fold(&mut self, _: &actions::ToggleFold, cx: &mut ViewContext<Self>) {
10314 let selection = self.selections.newest::<Point>(cx);
10315
10316 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10317 let range = if selection.is_empty() {
10318 let point = selection.head().to_display_point(&display_map);
10319 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
10320 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
10321 .to_point(&display_map);
10322 start..end
10323 } else {
10324 selection.range()
10325 };
10326 if display_map.folds_in_range(range).next().is_some() {
10327 self.unfold_lines(&Default::default(), cx)
10328 } else {
10329 self.fold(&Default::default(), cx)
10330 }
10331 }
10332
10333 pub fn toggle_fold_recursive(
10334 &mut self,
10335 _: &actions::ToggleFoldRecursive,
10336 cx: &mut ViewContext<Self>,
10337 ) {
10338 let selection = self.selections.newest::<Point>(cx);
10339
10340 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10341 let range = if selection.is_empty() {
10342 let point = selection.head().to_display_point(&display_map);
10343 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
10344 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
10345 .to_point(&display_map);
10346 start..end
10347 } else {
10348 selection.range()
10349 };
10350 if display_map.folds_in_range(range).next().is_some() {
10351 self.unfold_recursive(&Default::default(), cx)
10352 } else {
10353 self.fold_recursive(&Default::default(), cx)
10354 }
10355 }
10356
10357 pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
10358 let mut to_fold = Vec::new();
10359 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10360 let selections = self.selections.all_adjusted(cx);
10361
10362 for selection in selections {
10363 let range = selection.range().sorted();
10364 let buffer_start_row = range.start.row;
10365
10366 if range.start.row != range.end.row {
10367 let mut found = false;
10368 let mut row = range.start.row;
10369 while row <= range.end.row {
10370 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
10371 found = true;
10372 row = crease.range().end.row + 1;
10373 to_fold.push(crease);
10374 } else {
10375 row += 1
10376 }
10377 }
10378 if found {
10379 continue;
10380 }
10381 }
10382
10383 for row in (0..=range.start.row).rev() {
10384 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
10385 if crease.range().end.row >= buffer_start_row {
10386 to_fold.push(crease);
10387 if row <= range.start.row {
10388 break;
10389 }
10390 }
10391 }
10392 }
10393 }
10394
10395 self.fold_creases(to_fold, true, cx);
10396 }
10397
10398 fn fold_at_level(&mut self, fold_at: &FoldAtLevel, cx: &mut ViewContext<Self>) {
10399 if !self.buffer.read(cx).is_singleton() {
10400 return;
10401 }
10402
10403 let fold_at_level = fold_at.level;
10404 let snapshot = self.buffer.read(cx).snapshot(cx);
10405 let mut to_fold = Vec::new();
10406 let mut stack = vec![(0, snapshot.max_row().0, 1)];
10407
10408 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
10409 while start_row < end_row {
10410 match self
10411 .snapshot(cx)
10412 .crease_for_buffer_row(MultiBufferRow(start_row))
10413 {
10414 Some(crease) => {
10415 let nested_start_row = crease.range().start.row + 1;
10416 let nested_end_row = crease.range().end.row;
10417
10418 if current_level < fold_at_level {
10419 stack.push((nested_start_row, nested_end_row, current_level + 1));
10420 } else if current_level == fold_at_level {
10421 to_fold.push(crease);
10422 }
10423
10424 start_row = nested_end_row + 1;
10425 }
10426 None => start_row += 1,
10427 }
10428 }
10429 }
10430
10431 self.fold_creases(to_fold, true, cx);
10432 }
10433
10434 pub fn fold_all(&mut self, _: &actions::FoldAll, cx: &mut ViewContext<Self>) {
10435 if !self.buffer.read(cx).is_singleton() {
10436 return;
10437 }
10438
10439 let mut fold_ranges = Vec::new();
10440 let snapshot = self.buffer.read(cx).snapshot(cx);
10441
10442 for row in 0..snapshot.max_row().0 {
10443 if let Some(foldable_range) =
10444 self.snapshot(cx).crease_for_buffer_row(MultiBufferRow(row))
10445 {
10446 fold_ranges.push(foldable_range);
10447 }
10448 }
10449
10450 self.fold_creases(fold_ranges, true, cx);
10451 }
10452
10453 pub fn fold_function_bodies(
10454 &mut self,
10455 _: &actions::FoldFunctionBodies,
10456 cx: &mut ViewContext<Self>,
10457 ) {
10458 let snapshot = self.buffer.read(cx).snapshot(cx);
10459 let Some((_, _, buffer)) = snapshot.as_singleton() else {
10460 return;
10461 };
10462 let creases = buffer
10463 .function_body_fold_ranges(0..buffer.len())
10464 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
10465 .collect();
10466
10467 self.fold_creases(creases, true, cx);
10468 }
10469
10470 pub fn fold_recursive(&mut self, _: &actions::FoldRecursive, cx: &mut ViewContext<Self>) {
10471 let mut to_fold = Vec::new();
10472 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10473 let selections = self.selections.all_adjusted(cx);
10474
10475 for selection in selections {
10476 let range = selection.range().sorted();
10477 let buffer_start_row = range.start.row;
10478
10479 if range.start.row != range.end.row {
10480 let mut found = false;
10481 for row in range.start.row..=range.end.row {
10482 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
10483 found = true;
10484 to_fold.push(crease);
10485 }
10486 }
10487 if found {
10488 continue;
10489 }
10490 }
10491
10492 for row in (0..=range.start.row).rev() {
10493 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
10494 if crease.range().end.row >= buffer_start_row {
10495 to_fold.push(crease);
10496 } else {
10497 break;
10498 }
10499 }
10500 }
10501 }
10502
10503 self.fold_creases(to_fold, true, cx);
10504 }
10505
10506 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
10507 let buffer_row = fold_at.buffer_row;
10508 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10509
10510 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
10511 let autoscroll = self
10512 .selections
10513 .all::<Point>(cx)
10514 .iter()
10515 .any(|selection| crease.range().overlaps(&selection.range()));
10516
10517 self.fold_creases(vec![crease], autoscroll, cx);
10518 }
10519 }
10520
10521 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
10522 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10523 let buffer = &display_map.buffer_snapshot;
10524 let selections = self.selections.all::<Point>(cx);
10525 let ranges = selections
10526 .iter()
10527 .map(|s| {
10528 let range = s.display_range(&display_map).sorted();
10529 let mut start = range.start.to_point(&display_map);
10530 let mut end = range.end.to_point(&display_map);
10531 start.column = 0;
10532 end.column = buffer.line_len(MultiBufferRow(end.row));
10533 start..end
10534 })
10535 .collect::<Vec<_>>();
10536
10537 self.unfold_ranges(&ranges, true, true, cx);
10538 }
10539
10540 pub fn unfold_recursive(&mut self, _: &UnfoldRecursive, cx: &mut ViewContext<Self>) {
10541 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10542 let selections = self.selections.all::<Point>(cx);
10543 let ranges = selections
10544 .iter()
10545 .map(|s| {
10546 let mut range = s.display_range(&display_map).sorted();
10547 *range.start.column_mut() = 0;
10548 *range.end.column_mut() = display_map.line_len(range.end.row());
10549 let start = range.start.to_point(&display_map);
10550 let end = range.end.to_point(&display_map);
10551 start..end
10552 })
10553 .collect::<Vec<_>>();
10554
10555 self.unfold_ranges(&ranges, true, true, cx);
10556 }
10557
10558 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
10559 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10560
10561 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
10562 ..Point::new(
10563 unfold_at.buffer_row.0,
10564 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
10565 );
10566
10567 let autoscroll = self
10568 .selections
10569 .all::<Point>(cx)
10570 .iter()
10571 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
10572
10573 self.unfold_ranges(&[intersection_range], true, autoscroll, cx)
10574 }
10575
10576 pub fn unfold_all(&mut self, _: &actions::UnfoldAll, cx: &mut ViewContext<Self>) {
10577 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10578 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
10579 }
10580
10581 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
10582 let selections = self.selections.all::<Point>(cx);
10583 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10584 let line_mode = self.selections.line_mode;
10585 let ranges = selections
10586 .into_iter()
10587 .map(|s| {
10588 if line_mode {
10589 let start = Point::new(s.start.row, 0);
10590 let end = Point::new(
10591 s.end.row,
10592 display_map
10593 .buffer_snapshot
10594 .line_len(MultiBufferRow(s.end.row)),
10595 );
10596 Crease::simple(start..end, display_map.fold_placeholder.clone())
10597 } else {
10598 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
10599 }
10600 })
10601 .collect::<Vec<_>>();
10602 self.fold_creases(ranges, true, cx);
10603 }
10604
10605 pub fn fold_creases<T: ToOffset + Clone>(
10606 &mut self,
10607 creases: Vec<Crease<T>>,
10608 auto_scroll: bool,
10609 cx: &mut ViewContext<Self>,
10610 ) {
10611 if creases.is_empty() {
10612 return;
10613 }
10614
10615 let mut buffers_affected = HashSet::default();
10616 let multi_buffer = self.buffer().read(cx);
10617 for crease in &creases {
10618 if let Some((_, buffer, _)) =
10619 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
10620 {
10621 buffers_affected.insert(buffer.read(cx).remote_id());
10622 };
10623 }
10624
10625 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
10626
10627 if auto_scroll {
10628 self.request_autoscroll(Autoscroll::fit(), cx);
10629 }
10630
10631 for buffer_id in buffers_affected {
10632 Self::sync_expanded_diff_hunks(&mut self.diff_map, buffer_id, cx);
10633 }
10634
10635 cx.notify();
10636
10637 if let Some(active_diagnostics) = self.active_diagnostics.take() {
10638 // Clear diagnostics block when folding a range that contains it.
10639 let snapshot = self.snapshot(cx);
10640 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
10641 drop(snapshot);
10642 self.active_diagnostics = Some(active_diagnostics);
10643 self.dismiss_diagnostics(cx);
10644 } else {
10645 self.active_diagnostics = Some(active_diagnostics);
10646 }
10647 }
10648
10649 self.scrollbar_marker_state.dirty = true;
10650 }
10651
10652 /// Removes any folds whose ranges intersect any of the given ranges.
10653 pub fn unfold_ranges<T: ToOffset + Clone>(
10654 &mut self,
10655 ranges: &[Range<T>],
10656 inclusive: bool,
10657 auto_scroll: bool,
10658 cx: &mut ViewContext<Self>,
10659 ) {
10660 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
10661 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
10662 });
10663 }
10664
10665 /// Removes any folds with the given ranges.
10666 pub fn remove_folds_with_type<T: ToOffset + Clone>(
10667 &mut self,
10668 ranges: &[Range<T>],
10669 type_id: TypeId,
10670 auto_scroll: bool,
10671 cx: &mut ViewContext<Self>,
10672 ) {
10673 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
10674 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
10675 });
10676 }
10677
10678 fn remove_folds_with<T: ToOffset + Clone>(
10679 &mut self,
10680 ranges: &[Range<T>],
10681 auto_scroll: bool,
10682 cx: &mut ViewContext<Self>,
10683 update: impl FnOnce(&mut DisplayMap, &mut ModelContext<DisplayMap>),
10684 ) {
10685 if ranges.is_empty() {
10686 return;
10687 }
10688
10689 let mut buffers_affected = HashSet::default();
10690 let multi_buffer = self.buffer().read(cx);
10691 for range in ranges {
10692 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
10693 buffers_affected.insert(buffer.read(cx).remote_id());
10694 };
10695 }
10696
10697 self.display_map.update(cx, update);
10698
10699 if auto_scroll {
10700 self.request_autoscroll(Autoscroll::fit(), cx);
10701 }
10702
10703 for buffer_id in buffers_affected {
10704 Self::sync_expanded_diff_hunks(&mut self.diff_map, buffer_id, cx);
10705 }
10706
10707 cx.notify();
10708 self.scrollbar_marker_state.dirty = true;
10709 self.active_indent_guides_state.dirty = true;
10710 }
10711
10712 pub fn default_fold_placeholder(&self, cx: &AppContext) -> FoldPlaceholder {
10713 self.display_map.read(cx).fold_placeholder.clone()
10714 }
10715
10716 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
10717 if hovered != self.gutter_hovered {
10718 self.gutter_hovered = hovered;
10719 cx.notify();
10720 }
10721 }
10722
10723 pub fn insert_blocks(
10724 &mut self,
10725 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
10726 autoscroll: Option<Autoscroll>,
10727 cx: &mut ViewContext<Self>,
10728 ) -> Vec<CustomBlockId> {
10729 let blocks = self
10730 .display_map
10731 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
10732 if let Some(autoscroll) = autoscroll {
10733 self.request_autoscroll(autoscroll, cx);
10734 }
10735 cx.notify();
10736 blocks
10737 }
10738
10739 pub fn resize_blocks(
10740 &mut self,
10741 heights: HashMap<CustomBlockId, u32>,
10742 autoscroll: Option<Autoscroll>,
10743 cx: &mut ViewContext<Self>,
10744 ) {
10745 self.display_map
10746 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
10747 if let Some(autoscroll) = autoscroll {
10748 self.request_autoscroll(autoscroll, cx);
10749 }
10750 cx.notify();
10751 }
10752
10753 pub fn replace_blocks(
10754 &mut self,
10755 renderers: HashMap<CustomBlockId, RenderBlock>,
10756 autoscroll: Option<Autoscroll>,
10757 cx: &mut ViewContext<Self>,
10758 ) {
10759 self.display_map
10760 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
10761 if let Some(autoscroll) = autoscroll {
10762 self.request_autoscroll(autoscroll, cx);
10763 }
10764 cx.notify();
10765 }
10766
10767 pub fn remove_blocks(
10768 &mut self,
10769 block_ids: HashSet<CustomBlockId>,
10770 autoscroll: Option<Autoscroll>,
10771 cx: &mut ViewContext<Self>,
10772 ) {
10773 self.display_map.update(cx, |display_map, cx| {
10774 display_map.remove_blocks(block_ids, cx)
10775 });
10776 if let Some(autoscroll) = autoscroll {
10777 self.request_autoscroll(autoscroll, cx);
10778 }
10779 cx.notify();
10780 }
10781
10782 pub fn row_for_block(
10783 &self,
10784 block_id: CustomBlockId,
10785 cx: &mut ViewContext<Self>,
10786 ) -> Option<DisplayRow> {
10787 self.display_map
10788 .update(cx, |map, cx| map.row_for_block(block_id, cx))
10789 }
10790
10791 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
10792 self.focused_block = Some(focused_block);
10793 }
10794
10795 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
10796 self.focused_block.take()
10797 }
10798
10799 pub fn insert_creases(
10800 &mut self,
10801 creases: impl IntoIterator<Item = Crease<Anchor>>,
10802 cx: &mut ViewContext<Self>,
10803 ) -> Vec<CreaseId> {
10804 self.display_map
10805 .update(cx, |map, cx| map.insert_creases(creases, cx))
10806 }
10807
10808 pub fn remove_creases(
10809 &mut self,
10810 ids: impl IntoIterator<Item = CreaseId>,
10811 cx: &mut ViewContext<Self>,
10812 ) {
10813 self.display_map
10814 .update(cx, |map, cx| map.remove_creases(ids, cx));
10815 }
10816
10817 pub fn longest_row(&self, cx: &mut AppContext) -> DisplayRow {
10818 self.display_map
10819 .update(cx, |map, cx| map.snapshot(cx))
10820 .longest_row()
10821 }
10822
10823 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
10824 self.display_map
10825 .update(cx, |map, cx| map.snapshot(cx))
10826 .max_point()
10827 }
10828
10829 pub fn text(&self, cx: &AppContext) -> String {
10830 self.buffer.read(cx).read(cx).text()
10831 }
10832
10833 pub fn text_option(&self, cx: &AppContext) -> Option<String> {
10834 let text = self.text(cx);
10835 let text = text.trim();
10836
10837 if text.is_empty() {
10838 return None;
10839 }
10840
10841 Some(text.to_string())
10842 }
10843
10844 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
10845 self.transact(cx, |this, cx| {
10846 this.buffer
10847 .read(cx)
10848 .as_singleton()
10849 .expect("you can only call set_text on editors for singleton buffers")
10850 .update(cx, |buffer, cx| buffer.set_text(text, cx));
10851 });
10852 }
10853
10854 pub fn display_text(&self, cx: &mut AppContext) -> String {
10855 self.display_map
10856 .update(cx, |map, cx| map.snapshot(cx))
10857 .text()
10858 }
10859
10860 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
10861 let mut wrap_guides = smallvec::smallvec![];
10862
10863 if self.show_wrap_guides == Some(false) {
10864 return wrap_guides;
10865 }
10866
10867 let settings = self.buffer.read(cx).settings_at(0, cx);
10868 if settings.show_wrap_guides {
10869 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
10870 wrap_guides.push((soft_wrap as usize, true));
10871 } else if let SoftWrap::Bounded(soft_wrap) = self.soft_wrap_mode(cx) {
10872 wrap_guides.push((soft_wrap as usize, true));
10873 }
10874 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
10875 }
10876
10877 wrap_guides
10878 }
10879
10880 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
10881 let settings = self.buffer.read(cx).settings_at(0, cx);
10882 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
10883 match mode {
10884 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
10885 SoftWrap::None
10886 }
10887 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
10888 language_settings::SoftWrap::PreferredLineLength => {
10889 SoftWrap::Column(settings.preferred_line_length)
10890 }
10891 language_settings::SoftWrap::Bounded => {
10892 SoftWrap::Bounded(settings.preferred_line_length)
10893 }
10894 }
10895 }
10896
10897 pub fn set_soft_wrap_mode(
10898 &mut self,
10899 mode: language_settings::SoftWrap,
10900 cx: &mut ViewContext<Self>,
10901 ) {
10902 self.soft_wrap_mode_override = Some(mode);
10903 cx.notify();
10904 }
10905
10906 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
10907 self.text_style_refinement = Some(style);
10908 }
10909
10910 /// called by the Element so we know what style we were most recently rendered with.
10911 pub(crate) fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
10912 let rem_size = cx.rem_size();
10913 self.display_map.update(cx, |map, cx| {
10914 map.set_font(
10915 style.text.font(),
10916 style.text.font_size.to_pixels(rem_size),
10917 cx,
10918 )
10919 });
10920 self.style = Some(style);
10921 }
10922
10923 pub fn style(&self) -> Option<&EditorStyle> {
10924 self.style.as_ref()
10925 }
10926
10927 // Called by the element. This method is not designed to be called outside of the editor
10928 // element's layout code because it does not notify when rewrapping is computed synchronously.
10929 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
10930 self.display_map
10931 .update(cx, |map, cx| map.set_wrap_width(width, cx))
10932 }
10933
10934 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
10935 if self.soft_wrap_mode_override.is_some() {
10936 self.soft_wrap_mode_override.take();
10937 } else {
10938 let soft_wrap = match self.soft_wrap_mode(cx) {
10939 SoftWrap::GitDiff => return,
10940 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
10941 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
10942 language_settings::SoftWrap::None
10943 }
10944 };
10945 self.soft_wrap_mode_override = Some(soft_wrap);
10946 }
10947 cx.notify();
10948 }
10949
10950 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, cx: &mut ViewContext<Self>) {
10951 let Some(workspace) = self.workspace() else {
10952 return;
10953 };
10954 let fs = workspace.read(cx).app_state().fs.clone();
10955 let current_show = TabBarSettings::get_global(cx).show;
10956 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
10957 setting.show = Some(!current_show);
10958 });
10959 }
10960
10961 pub fn toggle_indent_guides(&mut self, _: &ToggleIndentGuides, cx: &mut ViewContext<Self>) {
10962 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
10963 self.buffer
10964 .read(cx)
10965 .settings_at(0, cx)
10966 .indent_guides
10967 .enabled
10968 });
10969 self.show_indent_guides = Some(!currently_enabled);
10970 cx.notify();
10971 }
10972
10973 fn should_show_indent_guides(&self) -> Option<bool> {
10974 self.show_indent_guides
10975 }
10976
10977 pub fn toggle_line_numbers(&mut self, _: &ToggleLineNumbers, cx: &mut ViewContext<Self>) {
10978 let mut editor_settings = EditorSettings::get_global(cx).clone();
10979 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
10980 EditorSettings::override_global(editor_settings, cx);
10981 }
10982
10983 pub fn should_use_relative_line_numbers(&self, cx: &WindowContext) -> bool {
10984 self.use_relative_line_numbers
10985 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
10986 }
10987
10988 pub fn toggle_relative_line_numbers(
10989 &mut self,
10990 _: &ToggleRelativeLineNumbers,
10991 cx: &mut ViewContext<Self>,
10992 ) {
10993 let is_relative = self.should_use_relative_line_numbers(cx);
10994 self.set_relative_line_number(Some(!is_relative), cx)
10995 }
10996
10997 pub fn set_relative_line_number(
10998 &mut self,
10999 is_relative: Option<bool>,
11000 cx: &mut ViewContext<Self>,
11001 ) {
11002 self.use_relative_line_numbers = is_relative;
11003 cx.notify();
11004 }
11005
11006 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
11007 self.show_gutter = show_gutter;
11008 cx.notify();
11009 }
11010
11011 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut ViewContext<Self>) {
11012 self.show_line_numbers = Some(show_line_numbers);
11013 cx.notify();
11014 }
11015
11016 pub fn set_show_git_diff_gutter(
11017 &mut self,
11018 show_git_diff_gutter: bool,
11019 cx: &mut ViewContext<Self>,
11020 ) {
11021 self.show_git_diff_gutter = Some(show_git_diff_gutter);
11022 cx.notify();
11023 }
11024
11025 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut ViewContext<Self>) {
11026 self.show_code_actions = Some(show_code_actions);
11027 cx.notify();
11028 }
11029
11030 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut ViewContext<Self>) {
11031 self.show_runnables = Some(show_runnables);
11032 cx.notify();
11033 }
11034
11035 pub fn set_masked(&mut self, masked: bool, cx: &mut ViewContext<Self>) {
11036 if self.display_map.read(cx).masked != masked {
11037 self.display_map.update(cx, |map, _| map.masked = masked);
11038 }
11039 cx.notify()
11040 }
11041
11042 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut ViewContext<Self>) {
11043 self.show_wrap_guides = Some(show_wrap_guides);
11044 cx.notify();
11045 }
11046
11047 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut ViewContext<Self>) {
11048 self.show_indent_guides = Some(show_indent_guides);
11049 cx.notify();
11050 }
11051
11052 pub fn working_directory(&self, cx: &WindowContext) -> Option<PathBuf> {
11053 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
11054 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
11055 if let Some(dir) = file.abs_path(cx).parent() {
11056 return Some(dir.to_owned());
11057 }
11058 }
11059
11060 if let Some(project_path) = buffer.read(cx).project_path(cx) {
11061 return Some(project_path.path.to_path_buf());
11062 }
11063 }
11064
11065 None
11066 }
11067
11068 fn target_file<'a>(&self, cx: &'a AppContext) -> Option<&'a dyn language::LocalFile> {
11069 self.active_excerpt(cx)?
11070 .1
11071 .read(cx)
11072 .file()
11073 .and_then(|f| f.as_local())
11074 }
11075
11076 pub fn reveal_in_finder(&mut self, _: &RevealInFileManager, cx: &mut ViewContext<Self>) {
11077 if let Some(target) = self.target_file(cx) {
11078 cx.reveal_path(&target.abs_path(cx));
11079 }
11080 }
11081
11082 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
11083 if let Some(file) = self.target_file(cx) {
11084 if let Some(path) = file.abs_path(cx).to_str() {
11085 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
11086 }
11087 }
11088 }
11089
11090 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
11091 if let Some(file) = self.target_file(cx) {
11092 if let Some(path) = file.path().to_str() {
11093 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
11094 }
11095 }
11096 }
11097
11098 pub fn toggle_git_blame(&mut self, _: &ToggleGitBlame, cx: &mut ViewContext<Self>) {
11099 self.show_git_blame_gutter = !self.show_git_blame_gutter;
11100
11101 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
11102 self.start_git_blame(true, cx);
11103 }
11104
11105 cx.notify();
11106 }
11107
11108 pub fn toggle_git_blame_inline(
11109 &mut self,
11110 _: &ToggleGitBlameInline,
11111 cx: &mut ViewContext<Self>,
11112 ) {
11113 self.toggle_git_blame_inline_internal(true, cx);
11114 cx.notify();
11115 }
11116
11117 pub fn git_blame_inline_enabled(&self) -> bool {
11118 self.git_blame_inline_enabled
11119 }
11120
11121 pub fn toggle_selection_menu(&mut self, _: &ToggleSelectionMenu, cx: &mut ViewContext<Self>) {
11122 self.show_selection_menu = self
11123 .show_selection_menu
11124 .map(|show_selections_menu| !show_selections_menu)
11125 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
11126
11127 cx.notify();
11128 }
11129
11130 pub fn selection_menu_enabled(&self, cx: &AppContext) -> bool {
11131 self.show_selection_menu
11132 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
11133 }
11134
11135 fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
11136 if let Some(project) = self.project.as_ref() {
11137 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
11138 return;
11139 };
11140
11141 if buffer.read(cx).file().is_none() {
11142 return;
11143 }
11144
11145 let focused = self.focus_handle(cx).contains_focused(cx);
11146
11147 let project = project.clone();
11148 let blame =
11149 cx.new_model(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
11150 self.blame_subscription = Some(cx.observe(&blame, |_, _, cx| cx.notify()));
11151 self.blame = Some(blame);
11152 }
11153 }
11154
11155 fn toggle_git_blame_inline_internal(
11156 &mut self,
11157 user_triggered: bool,
11158 cx: &mut ViewContext<Self>,
11159 ) {
11160 if self.git_blame_inline_enabled {
11161 self.git_blame_inline_enabled = false;
11162 self.show_git_blame_inline = false;
11163 self.show_git_blame_inline_delay_task.take();
11164 } else {
11165 self.git_blame_inline_enabled = true;
11166 self.start_git_blame_inline(user_triggered, cx);
11167 }
11168
11169 cx.notify();
11170 }
11171
11172 fn start_git_blame_inline(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
11173 self.start_git_blame(user_triggered, cx);
11174
11175 if ProjectSettings::get_global(cx)
11176 .git
11177 .inline_blame_delay()
11178 .is_some()
11179 {
11180 self.start_inline_blame_timer(cx);
11181 } else {
11182 self.show_git_blame_inline = true
11183 }
11184 }
11185
11186 pub fn blame(&self) -> Option<&Model<GitBlame>> {
11187 self.blame.as_ref()
11188 }
11189
11190 pub fn show_git_blame_gutter(&self) -> bool {
11191 self.show_git_blame_gutter
11192 }
11193
11194 pub fn render_git_blame_gutter(&mut self, cx: &mut WindowContext) -> bool {
11195 self.show_git_blame_gutter && self.has_blame_entries(cx)
11196 }
11197
11198 pub fn render_git_blame_inline(&mut self, cx: &mut WindowContext) -> bool {
11199 self.show_git_blame_inline
11200 && self.focus_handle.is_focused(cx)
11201 && !self.newest_selection_head_on_empty_line(cx)
11202 && self.has_blame_entries(cx)
11203 }
11204
11205 fn has_blame_entries(&self, cx: &mut WindowContext) -> bool {
11206 self.blame()
11207 .map_or(false, |blame| blame.read(cx).has_generated_entries())
11208 }
11209
11210 fn newest_selection_head_on_empty_line(&mut self, cx: &mut WindowContext) -> bool {
11211 let cursor_anchor = self.selections.newest_anchor().head();
11212
11213 let snapshot = self.buffer.read(cx).snapshot(cx);
11214 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
11215
11216 snapshot.line_len(buffer_row) == 0
11217 }
11218
11219 fn get_permalink_to_line(&mut self, cx: &mut ViewContext<Self>) -> Task<Result<url::Url>> {
11220 let buffer_and_selection = maybe!({
11221 let selection = self.selections.newest::<Point>(cx);
11222 let selection_range = selection.range();
11223
11224 let (buffer, selection) = if let Some(buffer) = self.buffer().read(cx).as_singleton() {
11225 (buffer, selection_range.start.row..selection_range.end.row)
11226 } else {
11227 let buffer_ranges = self
11228 .buffer()
11229 .read(cx)
11230 .range_to_buffer_ranges(selection_range, cx);
11231
11232 let (buffer, range, _) = if selection.reversed {
11233 buffer_ranges.first()
11234 } else {
11235 buffer_ranges.last()
11236 }?;
11237
11238 let snapshot = buffer.read(cx).snapshot();
11239 let selection = text::ToPoint::to_point(&range.start, &snapshot).row
11240 ..text::ToPoint::to_point(&range.end, &snapshot).row;
11241 (buffer.clone(), selection)
11242 };
11243
11244 Some((buffer, selection))
11245 });
11246
11247 let Some((buffer, selection)) = buffer_and_selection else {
11248 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
11249 };
11250
11251 let Some(project) = self.project.as_ref() else {
11252 return Task::ready(Err(anyhow!("editor does not have project")));
11253 };
11254
11255 project.update(cx, |project, cx| {
11256 project.get_permalink_to_line(&buffer, selection, cx)
11257 })
11258 }
11259
11260 pub fn copy_permalink_to_line(&mut self, _: &CopyPermalinkToLine, cx: &mut ViewContext<Self>) {
11261 let permalink_task = self.get_permalink_to_line(cx);
11262 let workspace = self.workspace();
11263
11264 cx.spawn(|_, mut cx| async move {
11265 match permalink_task.await {
11266 Ok(permalink) => {
11267 cx.update(|cx| {
11268 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
11269 })
11270 .ok();
11271 }
11272 Err(err) => {
11273 let message = format!("Failed to copy permalink: {err}");
11274
11275 Err::<(), anyhow::Error>(err).log_err();
11276
11277 if let Some(workspace) = workspace {
11278 workspace
11279 .update(&mut cx, |workspace, cx| {
11280 struct CopyPermalinkToLine;
11281
11282 workspace.show_toast(
11283 Toast::new(
11284 NotificationId::unique::<CopyPermalinkToLine>(),
11285 message,
11286 ),
11287 cx,
11288 )
11289 })
11290 .ok();
11291 }
11292 }
11293 }
11294 })
11295 .detach();
11296 }
11297
11298 pub fn copy_file_location(&mut self, _: &CopyFileLocation, cx: &mut ViewContext<Self>) {
11299 let selection = self.selections.newest::<Point>(cx).start.row + 1;
11300 if let Some(file) = self.target_file(cx) {
11301 if let Some(path) = file.path().to_str() {
11302 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
11303 }
11304 }
11305 }
11306
11307 pub fn open_permalink_to_line(&mut self, _: &OpenPermalinkToLine, cx: &mut ViewContext<Self>) {
11308 let permalink_task = self.get_permalink_to_line(cx);
11309 let workspace = self.workspace();
11310
11311 cx.spawn(|_, mut cx| async move {
11312 match permalink_task.await {
11313 Ok(permalink) => {
11314 cx.update(|cx| {
11315 cx.open_url(permalink.as_ref());
11316 })
11317 .ok();
11318 }
11319 Err(err) => {
11320 let message = format!("Failed to open permalink: {err}");
11321
11322 Err::<(), anyhow::Error>(err).log_err();
11323
11324 if let Some(workspace) = workspace {
11325 workspace
11326 .update(&mut cx, |workspace, cx| {
11327 struct OpenPermalinkToLine;
11328
11329 workspace.show_toast(
11330 Toast::new(
11331 NotificationId::unique::<OpenPermalinkToLine>(),
11332 message,
11333 ),
11334 cx,
11335 )
11336 })
11337 .ok();
11338 }
11339 }
11340 }
11341 })
11342 .detach();
11343 }
11344
11345 pub fn insert_uuid_v4(&mut self, _: &InsertUuidV4, cx: &mut ViewContext<Self>) {
11346 self.insert_uuid(UuidVersion::V4, cx);
11347 }
11348
11349 pub fn insert_uuid_v7(&mut self, _: &InsertUuidV7, cx: &mut ViewContext<Self>) {
11350 self.insert_uuid(UuidVersion::V7, cx);
11351 }
11352
11353 fn insert_uuid(&mut self, version: UuidVersion, cx: &mut ViewContext<Self>) {
11354 self.transact(cx, |this, cx| {
11355 let edits = this
11356 .selections
11357 .all::<Point>(cx)
11358 .into_iter()
11359 .map(|selection| {
11360 let uuid = match version {
11361 UuidVersion::V4 => uuid::Uuid::new_v4(),
11362 UuidVersion::V7 => uuid::Uuid::now_v7(),
11363 };
11364
11365 (selection.range(), uuid.to_string())
11366 });
11367 this.edit(edits, cx);
11368 this.refresh_inline_completion(true, false, cx);
11369 });
11370 }
11371
11372 /// Adds a row highlight for the given range. If a row has multiple highlights, the
11373 /// last highlight added will be used.
11374 ///
11375 /// If the range ends at the beginning of a line, then that line will not be highlighted.
11376 pub fn highlight_rows<T: 'static>(
11377 &mut self,
11378 range: Range<Anchor>,
11379 color: Hsla,
11380 should_autoscroll: bool,
11381 cx: &mut ViewContext<Self>,
11382 ) {
11383 let snapshot = self.buffer().read(cx).snapshot(cx);
11384 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
11385 let ix = row_highlights.binary_search_by(|highlight| {
11386 Ordering::Equal
11387 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
11388 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
11389 });
11390
11391 if let Err(mut ix) = ix {
11392 let index = post_inc(&mut self.highlight_order);
11393
11394 // If this range intersects with the preceding highlight, then merge it with
11395 // the preceding highlight. Otherwise insert a new highlight.
11396 let mut merged = false;
11397 if ix > 0 {
11398 let prev_highlight = &mut row_highlights[ix - 1];
11399 if prev_highlight
11400 .range
11401 .end
11402 .cmp(&range.start, &snapshot)
11403 .is_ge()
11404 {
11405 ix -= 1;
11406 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
11407 prev_highlight.range.end = range.end;
11408 }
11409 merged = true;
11410 prev_highlight.index = index;
11411 prev_highlight.color = color;
11412 prev_highlight.should_autoscroll = should_autoscroll;
11413 }
11414 }
11415
11416 if !merged {
11417 row_highlights.insert(
11418 ix,
11419 RowHighlight {
11420 range: range.clone(),
11421 index,
11422 color,
11423 should_autoscroll,
11424 },
11425 );
11426 }
11427
11428 // If any of the following highlights intersect with this one, merge them.
11429 while let Some(next_highlight) = row_highlights.get(ix + 1) {
11430 let highlight = &row_highlights[ix];
11431 if next_highlight
11432 .range
11433 .start
11434 .cmp(&highlight.range.end, &snapshot)
11435 .is_le()
11436 {
11437 if next_highlight
11438 .range
11439 .end
11440 .cmp(&highlight.range.end, &snapshot)
11441 .is_gt()
11442 {
11443 row_highlights[ix].range.end = next_highlight.range.end;
11444 }
11445 row_highlights.remove(ix + 1);
11446 } else {
11447 break;
11448 }
11449 }
11450 }
11451 }
11452
11453 /// Remove any highlighted row ranges of the given type that intersect the
11454 /// given ranges.
11455 pub fn remove_highlighted_rows<T: 'static>(
11456 &mut self,
11457 ranges_to_remove: Vec<Range<Anchor>>,
11458 cx: &mut ViewContext<Self>,
11459 ) {
11460 let snapshot = self.buffer().read(cx).snapshot(cx);
11461 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
11462 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
11463 row_highlights.retain(|highlight| {
11464 while let Some(range_to_remove) = ranges_to_remove.peek() {
11465 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
11466 Ordering::Less | Ordering::Equal => {
11467 ranges_to_remove.next();
11468 }
11469 Ordering::Greater => {
11470 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
11471 Ordering::Less | Ordering::Equal => {
11472 return false;
11473 }
11474 Ordering::Greater => break,
11475 }
11476 }
11477 }
11478 }
11479
11480 true
11481 })
11482 }
11483
11484 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
11485 pub fn clear_row_highlights<T: 'static>(&mut self) {
11486 self.highlighted_rows.remove(&TypeId::of::<T>());
11487 }
11488
11489 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
11490 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
11491 self.highlighted_rows
11492 .get(&TypeId::of::<T>())
11493 .map_or(&[] as &[_], |vec| vec.as_slice())
11494 .iter()
11495 .map(|highlight| (highlight.range.clone(), highlight.color))
11496 }
11497
11498 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
11499 /// Rerturns a map of display rows that are highlighted and their corresponding highlight color.
11500 /// Allows to ignore certain kinds of highlights.
11501 pub fn highlighted_display_rows(
11502 &mut self,
11503 cx: &mut WindowContext,
11504 ) -> BTreeMap<DisplayRow, Hsla> {
11505 let snapshot = self.snapshot(cx);
11506 let mut used_highlight_orders = HashMap::default();
11507 self.highlighted_rows
11508 .iter()
11509 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
11510 .fold(
11511 BTreeMap::<DisplayRow, Hsla>::new(),
11512 |mut unique_rows, highlight| {
11513 let start = highlight.range.start.to_display_point(&snapshot);
11514 let end = highlight.range.end.to_display_point(&snapshot);
11515 let start_row = start.row().0;
11516 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
11517 && end.column() == 0
11518 {
11519 end.row().0.saturating_sub(1)
11520 } else {
11521 end.row().0
11522 };
11523 for row in start_row..=end_row {
11524 let used_index =
11525 used_highlight_orders.entry(row).or_insert(highlight.index);
11526 if highlight.index >= *used_index {
11527 *used_index = highlight.index;
11528 unique_rows.insert(DisplayRow(row), highlight.color);
11529 }
11530 }
11531 unique_rows
11532 },
11533 )
11534 }
11535
11536 pub fn highlighted_display_row_for_autoscroll(
11537 &self,
11538 snapshot: &DisplaySnapshot,
11539 ) -> Option<DisplayRow> {
11540 self.highlighted_rows
11541 .values()
11542 .flat_map(|highlighted_rows| highlighted_rows.iter())
11543 .filter_map(|highlight| {
11544 if highlight.should_autoscroll {
11545 Some(highlight.range.start.to_display_point(snapshot).row())
11546 } else {
11547 None
11548 }
11549 })
11550 .min()
11551 }
11552
11553 pub fn set_search_within_ranges(
11554 &mut self,
11555 ranges: &[Range<Anchor>],
11556 cx: &mut ViewContext<Self>,
11557 ) {
11558 self.highlight_background::<SearchWithinRange>(
11559 ranges,
11560 |colors| colors.editor_document_highlight_read_background,
11561 cx,
11562 )
11563 }
11564
11565 pub fn set_breadcrumb_header(&mut self, new_header: String) {
11566 self.breadcrumb_header = Some(new_header);
11567 }
11568
11569 pub fn clear_search_within_ranges(&mut self, cx: &mut ViewContext<Self>) {
11570 self.clear_background_highlights::<SearchWithinRange>(cx);
11571 }
11572
11573 pub fn highlight_background<T: 'static>(
11574 &mut self,
11575 ranges: &[Range<Anchor>],
11576 color_fetcher: fn(&ThemeColors) -> Hsla,
11577 cx: &mut ViewContext<Self>,
11578 ) {
11579 self.background_highlights
11580 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
11581 self.scrollbar_marker_state.dirty = true;
11582 cx.notify();
11583 }
11584
11585 pub fn clear_background_highlights<T: 'static>(
11586 &mut self,
11587 cx: &mut ViewContext<Self>,
11588 ) -> Option<BackgroundHighlight> {
11589 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
11590 if !text_highlights.1.is_empty() {
11591 self.scrollbar_marker_state.dirty = true;
11592 cx.notify();
11593 }
11594 Some(text_highlights)
11595 }
11596
11597 pub fn highlight_gutter<T: 'static>(
11598 &mut self,
11599 ranges: &[Range<Anchor>],
11600 color_fetcher: fn(&AppContext) -> Hsla,
11601 cx: &mut ViewContext<Self>,
11602 ) {
11603 self.gutter_highlights
11604 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
11605 cx.notify();
11606 }
11607
11608 pub fn clear_gutter_highlights<T: 'static>(
11609 &mut self,
11610 cx: &mut ViewContext<Self>,
11611 ) -> Option<GutterHighlight> {
11612 cx.notify();
11613 self.gutter_highlights.remove(&TypeId::of::<T>())
11614 }
11615
11616 #[cfg(feature = "test-support")]
11617 pub fn all_text_background_highlights(
11618 &mut self,
11619 cx: &mut ViewContext<Self>,
11620 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11621 let snapshot = self.snapshot(cx);
11622 let buffer = &snapshot.buffer_snapshot;
11623 let start = buffer.anchor_before(0);
11624 let end = buffer.anchor_after(buffer.len());
11625 let theme = cx.theme().colors();
11626 self.background_highlights_in_range(start..end, &snapshot, theme)
11627 }
11628
11629 #[cfg(feature = "test-support")]
11630 pub fn search_background_highlights(
11631 &mut self,
11632 cx: &mut ViewContext<Self>,
11633 ) -> Vec<Range<Point>> {
11634 let snapshot = self.buffer().read(cx).snapshot(cx);
11635
11636 let highlights = self
11637 .background_highlights
11638 .get(&TypeId::of::<items::BufferSearchHighlights>());
11639
11640 if let Some((_color, ranges)) = highlights {
11641 ranges
11642 .iter()
11643 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
11644 .collect_vec()
11645 } else {
11646 vec![]
11647 }
11648 }
11649
11650 fn document_highlights_for_position<'a>(
11651 &'a self,
11652 position: Anchor,
11653 buffer: &'a MultiBufferSnapshot,
11654 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
11655 let read_highlights = self
11656 .background_highlights
11657 .get(&TypeId::of::<DocumentHighlightRead>())
11658 .map(|h| &h.1);
11659 let write_highlights = self
11660 .background_highlights
11661 .get(&TypeId::of::<DocumentHighlightWrite>())
11662 .map(|h| &h.1);
11663 let left_position = position.bias_left(buffer);
11664 let right_position = position.bias_right(buffer);
11665 read_highlights
11666 .into_iter()
11667 .chain(write_highlights)
11668 .flat_map(move |ranges| {
11669 let start_ix = match ranges.binary_search_by(|probe| {
11670 let cmp = probe.end.cmp(&left_position, buffer);
11671 if cmp.is_ge() {
11672 Ordering::Greater
11673 } else {
11674 Ordering::Less
11675 }
11676 }) {
11677 Ok(i) | Err(i) => i,
11678 };
11679
11680 ranges[start_ix..]
11681 .iter()
11682 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
11683 })
11684 }
11685
11686 pub fn has_background_highlights<T: 'static>(&self) -> bool {
11687 self.background_highlights
11688 .get(&TypeId::of::<T>())
11689 .map_or(false, |(_, highlights)| !highlights.is_empty())
11690 }
11691
11692 pub fn background_highlights_in_range(
11693 &self,
11694 search_range: Range<Anchor>,
11695 display_snapshot: &DisplaySnapshot,
11696 theme: &ThemeColors,
11697 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11698 let mut results = Vec::new();
11699 for (color_fetcher, ranges) in self.background_highlights.values() {
11700 let color = color_fetcher(theme);
11701 let start_ix = match ranges.binary_search_by(|probe| {
11702 let cmp = probe
11703 .end
11704 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11705 if cmp.is_gt() {
11706 Ordering::Greater
11707 } else {
11708 Ordering::Less
11709 }
11710 }) {
11711 Ok(i) | Err(i) => i,
11712 };
11713 for range in &ranges[start_ix..] {
11714 if range
11715 .start
11716 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11717 .is_ge()
11718 {
11719 break;
11720 }
11721
11722 let start = range.start.to_display_point(display_snapshot);
11723 let end = range.end.to_display_point(display_snapshot);
11724 results.push((start..end, color))
11725 }
11726 }
11727 results
11728 }
11729
11730 pub fn background_highlight_row_ranges<T: 'static>(
11731 &self,
11732 search_range: Range<Anchor>,
11733 display_snapshot: &DisplaySnapshot,
11734 count: usize,
11735 ) -> Vec<RangeInclusive<DisplayPoint>> {
11736 let mut results = Vec::new();
11737 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
11738 return vec![];
11739 };
11740
11741 let start_ix = match ranges.binary_search_by(|probe| {
11742 let cmp = probe
11743 .end
11744 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11745 if cmp.is_gt() {
11746 Ordering::Greater
11747 } else {
11748 Ordering::Less
11749 }
11750 }) {
11751 Ok(i) | Err(i) => i,
11752 };
11753 let mut push_region = |start: Option<Point>, end: Option<Point>| {
11754 if let (Some(start_display), Some(end_display)) = (start, end) {
11755 results.push(
11756 start_display.to_display_point(display_snapshot)
11757 ..=end_display.to_display_point(display_snapshot),
11758 );
11759 }
11760 };
11761 let mut start_row: Option<Point> = None;
11762 let mut end_row: Option<Point> = None;
11763 if ranges.len() > count {
11764 return Vec::new();
11765 }
11766 for range in &ranges[start_ix..] {
11767 if range
11768 .start
11769 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11770 .is_ge()
11771 {
11772 break;
11773 }
11774 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
11775 if let Some(current_row) = &end_row {
11776 if end.row == current_row.row {
11777 continue;
11778 }
11779 }
11780 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
11781 if start_row.is_none() {
11782 assert_eq!(end_row, None);
11783 start_row = Some(start);
11784 end_row = Some(end);
11785 continue;
11786 }
11787 if let Some(current_end) = end_row.as_mut() {
11788 if start.row > current_end.row + 1 {
11789 push_region(start_row, end_row);
11790 start_row = Some(start);
11791 end_row = Some(end);
11792 } else {
11793 // Merge two hunks.
11794 *current_end = end;
11795 }
11796 } else {
11797 unreachable!();
11798 }
11799 }
11800 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
11801 push_region(start_row, end_row);
11802 results
11803 }
11804
11805 pub fn gutter_highlights_in_range(
11806 &self,
11807 search_range: Range<Anchor>,
11808 display_snapshot: &DisplaySnapshot,
11809 cx: &AppContext,
11810 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11811 let mut results = Vec::new();
11812 for (color_fetcher, ranges) in self.gutter_highlights.values() {
11813 let color = color_fetcher(cx);
11814 let start_ix = match ranges.binary_search_by(|probe| {
11815 let cmp = probe
11816 .end
11817 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11818 if cmp.is_gt() {
11819 Ordering::Greater
11820 } else {
11821 Ordering::Less
11822 }
11823 }) {
11824 Ok(i) | Err(i) => i,
11825 };
11826 for range in &ranges[start_ix..] {
11827 if range
11828 .start
11829 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11830 .is_ge()
11831 {
11832 break;
11833 }
11834
11835 let start = range.start.to_display_point(display_snapshot);
11836 let end = range.end.to_display_point(display_snapshot);
11837 results.push((start..end, color))
11838 }
11839 }
11840 results
11841 }
11842
11843 /// Get the text ranges corresponding to the redaction query
11844 pub fn redacted_ranges(
11845 &self,
11846 search_range: Range<Anchor>,
11847 display_snapshot: &DisplaySnapshot,
11848 cx: &WindowContext,
11849 ) -> Vec<Range<DisplayPoint>> {
11850 display_snapshot
11851 .buffer_snapshot
11852 .redacted_ranges(search_range, |file| {
11853 if let Some(file) = file {
11854 file.is_private()
11855 && EditorSettings::get(
11856 Some(SettingsLocation {
11857 worktree_id: file.worktree_id(cx),
11858 path: file.path().as_ref(),
11859 }),
11860 cx,
11861 )
11862 .redact_private_values
11863 } else {
11864 false
11865 }
11866 })
11867 .map(|range| {
11868 range.start.to_display_point(display_snapshot)
11869 ..range.end.to_display_point(display_snapshot)
11870 })
11871 .collect()
11872 }
11873
11874 pub fn highlight_text<T: 'static>(
11875 &mut self,
11876 ranges: Vec<Range<Anchor>>,
11877 style: HighlightStyle,
11878 cx: &mut ViewContext<Self>,
11879 ) {
11880 self.display_map.update(cx, |map, _| {
11881 map.highlight_text(TypeId::of::<T>(), ranges, style)
11882 });
11883 cx.notify();
11884 }
11885
11886 pub(crate) fn highlight_inlays<T: 'static>(
11887 &mut self,
11888 highlights: Vec<InlayHighlight>,
11889 style: HighlightStyle,
11890 cx: &mut ViewContext<Self>,
11891 ) {
11892 self.display_map.update(cx, |map, _| {
11893 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
11894 });
11895 cx.notify();
11896 }
11897
11898 pub fn text_highlights<'a, T: 'static>(
11899 &'a self,
11900 cx: &'a AppContext,
11901 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
11902 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
11903 }
11904
11905 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
11906 let cleared = self
11907 .display_map
11908 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
11909 if cleared {
11910 cx.notify();
11911 }
11912 }
11913
11914 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
11915 (self.read_only(cx) || self.blink_manager.read(cx).visible())
11916 && self.focus_handle.is_focused(cx)
11917 }
11918
11919 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut ViewContext<Self>) {
11920 self.show_cursor_when_unfocused = is_enabled;
11921 cx.notify();
11922 }
11923
11924 pub fn lsp_store(&self, cx: &AppContext) -> Option<Model<LspStore>> {
11925 self.project
11926 .as_ref()
11927 .map(|project| project.read(cx).lsp_store())
11928 }
11929
11930 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
11931 cx.notify();
11932 }
11933
11934 fn on_buffer_event(
11935 &mut self,
11936 multibuffer: Model<MultiBuffer>,
11937 event: &multi_buffer::Event,
11938 cx: &mut ViewContext<Self>,
11939 ) {
11940 match event {
11941 multi_buffer::Event::Edited {
11942 singleton_buffer_edited,
11943 edited_buffer: buffer_edited,
11944 } => {
11945 self.scrollbar_marker_state.dirty = true;
11946 self.active_indent_guides_state.dirty = true;
11947 self.refresh_active_diagnostics(cx);
11948 self.refresh_code_actions(cx);
11949 if self.has_active_inline_completion() {
11950 self.update_visible_inline_completion(cx);
11951 }
11952 if let Some(buffer) = buffer_edited {
11953 let buffer_id = buffer.read(cx).remote_id();
11954 if !self.registered_buffers.contains_key(&buffer_id) {
11955 if let Some(lsp_store) = self.lsp_store(cx) {
11956 lsp_store.update(cx, |lsp_store, cx| {
11957 self.registered_buffers.insert(
11958 buffer_id,
11959 lsp_store.register_buffer_with_language_servers(&buffer, cx),
11960 );
11961 })
11962 }
11963 }
11964 }
11965 cx.emit(EditorEvent::BufferEdited);
11966 cx.emit(SearchEvent::MatchesInvalidated);
11967 if *singleton_buffer_edited {
11968 if let Some(project) = &self.project {
11969 let project = project.read(cx);
11970 #[allow(clippy::mutable_key_type)]
11971 let languages_affected = multibuffer
11972 .read(cx)
11973 .all_buffers()
11974 .into_iter()
11975 .filter_map(|buffer| {
11976 let buffer = buffer.read(cx);
11977 let language = buffer.language()?;
11978 if project.is_local()
11979 && project
11980 .language_servers_for_local_buffer(buffer, cx)
11981 .count()
11982 == 0
11983 {
11984 None
11985 } else {
11986 Some(language)
11987 }
11988 })
11989 .cloned()
11990 .collect::<HashSet<_>>();
11991 if !languages_affected.is_empty() {
11992 self.refresh_inlay_hints(
11993 InlayHintRefreshReason::BufferEdited(languages_affected),
11994 cx,
11995 );
11996 }
11997 }
11998 }
11999
12000 let Some(project) = &self.project else { return };
12001 let (telemetry, is_via_ssh) = {
12002 let project = project.read(cx);
12003 let telemetry = project.client().telemetry().clone();
12004 let is_via_ssh = project.is_via_ssh();
12005 (telemetry, is_via_ssh)
12006 };
12007 refresh_linked_ranges(self, cx);
12008 telemetry.log_edit_event("editor", is_via_ssh);
12009 }
12010 multi_buffer::Event::ExcerptsAdded {
12011 buffer,
12012 predecessor,
12013 excerpts,
12014 } => {
12015 self.tasks_update_task = Some(self.refresh_runnables(cx));
12016 let buffer_id = buffer.read(cx).remote_id();
12017 if !self.diff_map.diff_bases.contains_key(&buffer_id) {
12018 if let Some(project) = &self.project {
12019 get_unstaged_changes_for_buffers(project, [buffer.clone()], cx);
12020 }
12021 }
12022 cx.emit(EditorEvent::ExcerptsAdded {
12023 buffer: buffer.clone(),
12024 predecessor: *predecessor,
12025 excerpts: excerpts.clone(),
12026 });
12027 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
12028 }
12029 multi_buffer::Event::ExcerptsRemoved { ids } => {
12030 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
12031 let buffer = self.buffer.read(cx);
12032 self.registered_buffers
12033 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
12034 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
12035 }
12036 multi_buffer::Event::ExcerptsEdited { ids } => {
12037 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
12038 }
12039 multi_buffer::Event::ExcerptsExpanded { ids } => {
12040 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
12041 }
12042 multi_buffer::Event::Reparsed(buffer_id) => {
12043 self.tasks_update_task = Some(self.refresh_runnables(cx));
12044
12045 cx.emit(EditorEvent::Reparsed(*buffer_id));
12046 }
12047 multi_buffer::Event::LanguageChanged(buffer_id) => {
12048 linked_editing_ranges::refresh_linked_ranges(self, cx);
12049 cx.emit(EditorEvent::Reparsed(*buffer_id));
12050 cx.notify();
12051 }
12052 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
12053 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
12054 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
12055 cx.emit(EditorEvent::TitleChanged)
12056 }
12057 // multi_buffer::Event::DiffBaseChanged => {
12058 // self.scrollbar_marker_state.dirty = true;
12059 // cx.emit(EditorEvent::DiffBaseChanged);
12060 // cx.notify();
12061 // }
12062 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
12063 multi_buffer::Event::DiagnosticsUpdated => {
12064 self.refresh_active_diagnostics(cx);
12065 self.scrollbar_marker_state.dirty = true;
12066 cx.notify();
12067 }
12068 _ => {}
12069 };
12070 }
12071
12072 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
12073 cx.notify();
12074 }
12075
12076 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
12077 self.tasks_update_task = Some(self.refresh_runnables(cx));
12078 self.refresh_inline_completion(true, false, cx);
12079 self.refresh_inlay_hints(
12080 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
12081 self.selections.newest_anchor().head(),
12082 &self.buffer.read(cx).snapshot(cx),
12083 cx,
12084 )),
12085 cx,
12086 );
12087
12088 let old_cursor_shape = self.cursor_shape;
12089
12090 {
12091 let editor_settings = EditorSettings::get_global(cx);
12092 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
12093 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
12094 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
12095 }
12096
12097 if old_cursor_shape != self.cursor_shape {
12098 cx.emit(EditorEvent::CursorShapeChanged);
12099 }
12100
12101 let project_settings = ProjectSettings::get_global(cx);
12102 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
12103
12104 if self.mode == EditorMode::Full {
12105 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
12106 if self.git_blame_inline_enabled != inline_blame_enabled {
12107 self.toggle_git_blame_inline_internal(false, cx);
12108 }
12109 }
12110
12111 cx.notify();
12112 }
12113
12114 pub fn set_searchable(&mut self, searchable: bool) {
12115 self.searchable = searchable;
12116 }
12117
12118 pub fn searchable(&self) -> bool {
12119 self.searchable
12120 }
12121
12122 fn open_proposed_changes_editor(
12123 &mut self,
12124 _: &OpenProposedChangesEditor,
12125 cx: &mut ViewContext<Self>,
12126 ) {
12127 let Some(workspace) = self.workspace() else {
12128 cx.propagate();
12129 return;
12130 };
12131
12132 let selections = self.selections.all::<usize>(cx);
12133 let buffer = self.buffer.read(cx);
12134 let mut new_selections_by_buffer = HashMap::default();
12135 for selection in selections {
12136 for (buffer, range, _) in
12137 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
12138 {
12139 let mut range = range.to_point(buffer.read(cx));
12140 range.start.column = 0;
12141 range.end.column = buffer.read(cx).line_len(range.end.row);
12142 new_selections_by_buffer
12143 .entry(buffer)
12144 .or_insert(Vec::new())
12145 .push(range)
12146 }
12147 }
12148
12149 let proposed_changes_buffers = new_selections_by_buffer
12150 .into_iter()
12151 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
12152 .collect::<Vec<_>>();
12153 let proposed_changes_editor = cx.new_view(|cx| {
12154 ProposedChangesEditor::new(
12155 "Proposed changes",
12156 proposed_changes_buffers,
12157 self.project.clone(),
12158 cx,
12159 )
12160 });
12161
12162 cx.window_context().defer(move |cx| {
12163 workspace.update(cx, |workspace, cx| {
12164 workspace.active_pane().update(cx, |pane, cx| {
12165 pane.add_item(Box::new(proposed_changes_editor), true, true, None, cx);
12166 });
12167 });
12168 });
12169 }
12170
12171 pub fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext<Self>) {
12172 self.open_excerpts_common(None, true, cx)
12173 }
12174
12175 pub fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
12176 self.open_excerpts_common(None, false, cx)
12177 }
12178
12179 fn open_excerpts_common(
12180 &mut self,
12181 jump_data: Option<JumpData>,
12182 split: bool,
12183 cx: &mut ViewContext<Self>,
12184 ) {
12185 let Some(workspace) = self.workspace() else {
12186 cx.propagate();
12187 return;
12188 };
12189
12190 if self.buffer.read(cx).is_singleton() {
12191 cx.propagate();
12192 return;
12193 }
12194
12195 let mut new_selections_by_buffer = HashMap::default();
12196 match &jump_data {
12197 Some(jump_data) => {
12198 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12199 if let Some(buffer) = multi_buffer_snapshot
12200 .buffer_id_for_excerpt(jump_data.excerpt_id)
12201 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
12202 {
12203 let buffer_snapshot = buffer.read(cx).snapshot();
12204 let jump_to_point = if buffer_snapshot.can_resolve(&jump_data.anchor) {
12205 language::ToPoint::to_point(&jump_data.anchor, &buffer_snapshot)
12206 } else {
12207 buffer_snapshot.clip_point(jump_data.position, Bias::Left)
12208 };
12209 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
12210 new_selections_by_buffer.insert(
12211 buffer,
12212 (
12213 vec![jump_to_offset..jump_to_offset],
12214 Some(jump_data.line_offset_from_top),
12215 ),
12216 );
12217 }
12218 }
12219 None => {
12220 let selections = self.selections.all::<usize>(cx);
12221 let buffer = self.buffer.read(cx);
12222 for selection in selections {
12223 for (mut buffer_handle, mut range, _) in
12224 buffer.range_to_buffer_ranges(selection.range(), cx)
12225 {
12226 // When editing branch buffers, jump to the corresponding location
12227 // in their base buffer.
12228 let buffer = buffer_handle.read(cx);
12229 if let Some(base_buffer) = buffer.base_buffer() {
12230 range = buffer.range_to_version(range, &base_buffer.read(cx).version());
12231 buffer_handle = base_buffer;
12232 }
12233
12234 if selection.reversed {
12235 mem::swap(&mut range.start, &mut range.end);
12236 }
12237 new_selections_by_buffer
12238 .entry(buffer_handle)
12239 .or_insert((Vec::new(), None))
12240 .0
12241 .push(range)
12242 }
12243 }
12244 }
12245 }
12246
12247 if new_selections_by_buffer.is_empty() {
12248 return;
12249 }
12250
12251 // We defer the pane interaction because we ourselves are a workspace item
12252 // and activating a new item causes the pane to call a method on us reentrantly,
12253 // which panics if we're on the stack.
12254 cx.window_context().defer(move |cx| {
12255 workspace.update(cx, |workspace, cx| {
12256 let pane = if split {
12257 workspace.adjacent_pane(cx)
12258 } else {
12259 workspace.active_pane().clone()
12260 };
12261
12262 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
12263 let editor = buffer
12264 .read(cx)
12265 .file()
12266 .is_none()
12267 .then(|| {
12268 // Handle file-less buffers separately: those are not really the project items, so won't have a paroject path or entity id,
12269 // so `workspace.open_project_item` will never find them, always opening a new editor.
12270 // Instead, we try to activate the existing editor in the pane first.
12271 let (editor, pane_item_index) =
12272 pane.read(cx).items().enumerate().find_map(|(i, item)| {
12273 let editor = item.downcast::<Editor>()?;
12274 let singleton_buffer =
12275 editor.read(cx).buffer().read(cx).as_singleton()?;
12276 if singleton_buffer == buffer {
12277 Some((editor, i))
12278 } else {
12279 None
12280 }
12281 })?;
12282 pane.update(cx, |pane, cx| {
12283 pane.activate_item(pane_item_index, true, true, cx)
12284 });
12285 Some(editor)
12286 })
12287 .flatten()
12288 .unwrap_or_else(|| {
12289 workspace.open_project_item::<Self>(
12290 pane.clone(),
12291 buffer,
12292 true,
12293 true,
12294 cx,
12295 )
12296 });
12297
12298 editor.update(cx, |editor, cx| {
12299 let autoscroll = match scroll_offset {
12300 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
12301 None => Autoscroll::newest(),
12302 };
12303 let nav_history = editor.nav_history.take();
12304 editor.change_selections(Some(autoscroll), cx, |s| {
12305 s.select_ranges(ranges);
12306 });
12307 editor.nav_history = nav_history;
12308 });
12309 }
12310 })
12311 });
12312 }
12313
12314 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
12315 let snapshot = self.buffer.read(cx).read(cx);
12316 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
12317 Some(
12318 ranges
12319 .iter()
12320 .map(move |range| {
12321 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
12322 })
12323 .collect(),
12324 )
12325 }
12326
12327 fn selection_replacement_ranges(
12328 &self,
12329 range: Range<OffsetUtf16>,
12330 cx: &mut AppContext,
12331 ) -> Vec<Range<OffsetUtf16>> {
12332 let selections = self.selections.all::<OffsetUtf16>(cx);
12333 let newest_selection = selections
12334 .iter()
12335 .max_by_key(|selection| selection.id)
12336 .unwrap();
12337 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
12338 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
12339 let snapshot = self.buffer.read(cx).read(cx);
12340 selections
12341 .into_iter()
12342 .map(|mut selection| {
12343 selection.start.0 =
12344 (selection.start.0 as isize).saturating_add(start_delta) as usize;
12345 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
12346 snapshot.clip_offset_utf16(selection.start, Bias::Left)
12347 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
12348 })
12349 .collect()
12350 }
12351
12352 fn report_editor_event(
12353 &self,
12354 operation: &'static str,
12355 file_extension: Option<String>,
12356 cx: &AppContext,
12357 ) {
12358 if cfg!(any(test, feature = "test-support")) {
12359 return;
12360 }
12361
12362 let Some(project) = &self.project else { return };
12363
12364 // If None, we are in a file without an extension
12365 let file = self
12366 .buffer
12367 .read(cx)
12368 .as_singleton()
12369 .and_then(|b| b.read(cx).file());
12370 let file_extension = file_extension.or(file
12371 .as_ref()
12372 .and_then(|file| Path::new(file.file_name(cx)).extension())
12373 .and_then(|e| e.to_str())
12374 .map(|a| a.to_string()));
12375
12376 let vim_mode = cx
12377 .global::<SettingsStore>()
12378 .raw_user_settings()
12379 .get("vim_mode")
12380 == Some(&serde_json::Value::Bool(true));
12381
12382 let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
12383 == language::language_settings::InlineCompletionProvider::Copilot;
12384 let copilot_enabled_for_language = self
12385 .buffer
12386 .read(cx)
12387 .settings_at(0, cx)
12388 .show_inline_completions;
12389
12390 let project = project.read(cx);
12391 let telemetry = project.client().telemetry().clone();
12392 telemetry.report_editor_event(
12393 file_extension,
12394 vim_mode,
12395 operation,
12396 copilot_enabled,
12397 copilot_enabled_for_language,
12398 project.is_via_ssh(),
12399 )
12400 }
12401
12402 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
12403 /// with each line being an array of {text, highlight} objects.
12404 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
12405 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
12406 return;
12407 };
12408
12409 #[derive(Serialize)]
12410 struct Chunk<'a> {
12411 text: String,
12412 highlight: Option<&'a str>,
12413 }
12414
12415 let snapshot = buffer.read(cx).snapshot();
12416 let range = self
12417 .selected_text_range(false, cx)
12418 .and_then(|selection| {
12419 if selection.range.is_empty() {
12420 None
12421 } else {
12422 Some(selection.range)
12423 }
12424 })
12425 .unwrap_or_else(|| 0..snapshot.len());
12426
12427 let chunks = snapshot.chunks(range, true);
12428 let mut lines = Vec::new();
12429 let mut line: VecDeque<Chunk> = VecDeque::new();
12430
12431 let Some(style) = self.style.as_ref() else {
12432 return;
12433 };
12434
12435 for chunk in chunks {
12436 let highlight = chunk
12437 .syntax_highlight_id
12438 .and_then(|id| id.name(&style.syntax));
12439 let mut chunk_lines = chunk.text.split('\n').peekable();
12440 while let Some(text) = chunk_lines.next() {
12441 let mut merged_with_last_token = false;
12442 if let Some(last_token) = line.back_mut() {
12443 if last_token.highlight == highlight {
12444 last_token.text.push_str(text);
12445 merged_with_last_token = true;
12446 }
12447 }
12448
12449 if !merged_with_last_token {
12450 line.push_back(Chunk {
12451 text: text.into(),
12452 highlight,
12453 });
12454 }
12455
12456 if chunk_lines.peek().is_some() {
12457 if line.len() > 1 && line.front().unwrap().text.is_empty() {
12458 line.pop_front();
12459 }
12460 if line.len() > 1 && line.back().unwrap().text.is_empty() {
12461 line.pop_back();
12462 }
12463
12464 lines.push(mem::take(&mut line));
12465 }
12466 }
12467 }
12468
12469 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
12470 return;
12471 };
12472 cx.write_to_clipboard(ClipboardItem::new_string(lines));
12473 }
12474
12475 pub fn open_context_menu(&mut self, _: &OpenContextMenu, cx: &mut ViewContext<Self>) {
12476 self.request_autoscroll(Autoscroll::newest(), cx);
12477 let position = self.selections.newest_display(cx).start;
12478 mouse_context_menu::deploy_context_menu(self, None, position, cx);
12479 }
12480
12481 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
12482 &self.inlay_hint_cache
12483 }
12484
12485 pub fn replay_insert_event(
12486 &mut self,
12487 text: &str,
12488 relative_utf16_range: Option<Range<isize>>,
12489 cx: &mut ViewContext<Self>,
12490 ) {
12491 if !self.input_enabled {
12492 cx.emit(EditorEvent::InputIgnored { text: text.into() });
12493 return;
12494 }
12495 if let Some(relative_utf16_range) = relative_utf16_range {
12496 let selections = self.selections.all::<OffsetUtf16>(cx);
12497 self.change_selections(None, cx, |s| {
12498 let new_ranges = selections.into_iter().map(|range| {
12499 let start = OffsetUtf16(
12500 range
12501 .head()
12502 .0
12503 .saturating_add_signed(relative_utf16_range.start),
12504 );
12505 let end = OffsetUtf16(
12506 range
12507 .head()
12508 .0
12509 .saturating_add_signed(relative_utf16_range.end),
12510 );
12511 start..end
12512 });
12513 s.select_ranges(new_ranges);
12514 });
12515 }
12516
12517 self.handle_input(text, cx);
12518 }
12519
12520 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
12521 let Some(provider) = self.semantics_provider.as_ref() else {
12522 return false;
12523 };
12524
12525 let mut supports = false;
12526 self.buffer().read(cx).for_each_buffer(|buffer| {
12527 supports |= provider.supports_inlay_hints(buffer, cx);
12528 });
12529 supports
12530 }
12531
12532 pub fn focus(&self, cx: &mut WindowContext) {
12533 cx.focus(&self.focus_handle)
12534 }
12535
12536 pub fn is_focused(&self, cx: &WindowContext) -> bool {
12537 self.focus_handle.is_focused(cx)
12538 }
12539
12540 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
12541 cx.emit(EditorEvent::Focused);
12542
12543 if let Some(descendant) = self
12544 .last_focused_descendant
12545 .take()
12546 .and_then(|descendant| descendant.upgrade())
12547 {
12548 cx.focus(&descendant);
12549 } else {
12550 if let Some(blame) = self.blame.as_ref() {
12551 blame.update(cx, GitBlame::focus)
12552 }
12553
12554 self.blink_manager.update(cx, BlinkManager::enable);
12555 self.show_cursor_names(cx);
12556 self.buffer.update(cx, |buffer, cx| {
12557 buffer.finalize_last_transaction(cx);
12558 if self.leader_peer_id.is_none() {
12559 buffer.set_active_selections(
12560 &self.selections.disjoint_anchors(),
12561 self.selections.line_mode,
12562 self.cursor_shape,
12563 cx,
12564 );
12565 }
12566 });
12567 }
12568 }
12569
12570 fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
12571 cx.emit(EditorEvent::FocusedIn)
12572 }
12573
12574 fn handle_focus_out(&mut self, event: FocusOutEvent, _cx: &mut ViewContext<Self>) {
12575 if event.blurred != self.focus_handle {
12576 self.last_focused_descendant = Some(event.blurred);
12577 }
12578 }
12579
12580 pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
12581 self.blink_manager.update(cx, BlinkManager::disable);
12582 self.buffer
12583 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
12584
12585 if let Some(blame) = self.blame.as_ref() {
12586 blame.update(cx, GitBlame::blur)
12587 }
12588 if !self.hover_state.focused(cx) {
12589 hide_hover(self, cx);
12590 }
12591
12592 self.hide_context_menu(cx);
12593 cx.emit(EditorEvent::Blurred);
12594 cx.notify();
12595 }
12596
12597 pub fn register_action<A: Action>(
12598 &mut self,
12599 listener: impl Fn(&A, &mut WindowContext) + 'static,
12600 ) -> Subscription {
12601 let id = self.next_editor_action_id.post_inc();
12602 let listener = Arc::new(listener);
12603 self.editor_actions.borrow_mut().insert(
12604 id,
12605 Box::new(move |cx| {
12606 let cx = cx.window_context();
12607 let listener = listener.clone();
12608 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
12609 let action = action.downcast_ref().unwrap();
12610 if phase == DispatchPhase::Bubble {
12611 listener(action, cx)
12612 }
12613 })
12614 }),
12615 );
12616
12617 let editor_actions = self.editor_actions.clone();
12618 Subscription::new(move || {
12619 editor_actions.borrow_mut().remove(&id);
12620 })
12621 }
12622
12623 pub fn file_header_size(&self) -> u32 {
12624 FILE_HEADER_HEIGHT
12625 }
12626
12627 pub fn revert(
12628 &mut self,
12629 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
12630 cx: &mut ViewContext<Self>,
12631 ) {
12632 self.buffer().update(cx, |multi_buffer, cx| {
12633 for (buffer_id, changes) in revert_changes {
12634 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
12635 buffer.update(cx, |buffer, cx| {
12636 buffer.edit(
12637 changes.into_iter().map(|(range, text)| {
12638 (range, text.to_string().map(Arc::<str>::from))
12639 }),
12640 None,
12641 cx,
12642 );
12643 });
12644 }
12645 }
12646 });
12647 self.change_selections(None, cx, |selections| selections.refresh());
12648 }
12649
12650 pub fn to_pixel_point(
12651 &mut self,
12652 source: multi_buffer::Anchor,
12653 editor_snapshot: &EditorSnapshot,
12654 cx: &mut ViewContext<Self>,
12655 ) -> Option<gpui::Point<Pixels>> {
12656 let source_point = source.to_display_point(editor_snapshot);
12657 self.display_to_pixel_point(source_point, editor_snapshot, cx)
12658 }
12659
12660 pub fn display_to_pixel_point(
12661 &self,
12662 source: DisplayPoint,
12663 editor_snapshot: &EditorSnapshot,
12664 cx: &WindowContext,
12665 ) -> Option<gpui::Point<Pixels>> {
12666 let line_height = self.style()?.text.line_height_in_pixels(cx.rem_size());
12667 let text_layout_details = self.text_layout_details(cx);
12668 let scroll_top = text_layout_details
12669 .scroll_anchor
12670 .scroll_position(editor_snapshot)
12671 .y;
12672
12673 if source.row().as_f32() < scroll_top.floor() {
12674 return None;
12675 }
12676 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
12677 let source_y = line_height * (source.row().as_f32() - scroll_top);
12678 Some(gpui::Point::new(source_x, source_y))
12679 }
12680
12681 pub fn has_active_completions_menu(&self) -> bool {
12682 self.context_menu.read().as_ref().map_or(false, |menu| {
12683 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
12684 })
12685 }
12686
12687 pub fn register_addon<T: Addon>(&mut self, instance: T) {
12688 self.addons
12689 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
12690 }
12691
12692 pub fn unregister_addon<T: Addon>(&mut self) {
12693 self.addons.remove(&std::any::TypeId::of::<T>());
12694 }
12695
12696 pub fn addon<T: Addon>(&self) -> Option<&T> {
12697 let type_id = std::any::TypeId::of::<T>();
12698 self.addons
12699 .get(&type_id)
12700 .and_then(|item| item.to_any().downcast_ref::<T>())
12701 }
12702
12703 fn character_size(&self, cx: &mut ViewContext<Self>) -> gpui::Point<Pixels> {
12704 let text_layout_details = self.text_layout_details(cx);
12705 let style = &text_layout_details.editor_style;
12706 let font_id = cx.text_system().resolve_font(&style.text.font());
12707 let font_size = style.text.font_size.to_pixels(cx.rem_size());
12708 let line_height = style.text.line_height_in_pixels(cx.rem_size());
12709
12710 let em_width = cx
12711 .text_system()
12712 .typographic_bounds(font_id, font_size, 'm')
12713 .unwrap()
12714 .size
12715 .width;
12716
12717 gpui::Point::new(em_width, line_height)
12718 }
12719}
12720
12721fn get_unstaged_changes_for_buffers(
12722 project: &Model<Project>,
12723 buffers: impl IntoIterator<Item = Model<Buffer>>,
12724 cx: &mut ViewContext<Editor>,
12725) {
12726 let mut tasks = Vec::new();
12727 project.update(cx, |project, cx| {
12728 for buffer in buffers {
12729 tasks.push(project.open_unstaged_changes(buffer.clone(), cx))
12730 }
12731 });
12732 cx.spawn(|this, mut cx| async move {
12733 let change_sets = futures::future::join_all(tasks).await;
12734 this.update(&mut cx, |this, cx| {
12735 for change_set in change_sets {
12736 if let Some(change_set) = change_set.log_err() {
12737 this.diff_map.add_change_set(change_set, cx);
12738 }
12739 }
12740 })
12741 .ok();
12742 })
12743 .detach();
12744}
12745
12746fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
12747 let tab_size = tab_size.get() as usize;
12748 let mut width = offset;
12749
12750 for ch in text.chars() {
12751 width += if ch == '\t' {
12752 tab_size - (width % tab_size)
12753 } else {
12754 1
12755 };
12756 }
12757
12758 width - offset
12759}
12760
12761#[cfg(test)]
12762mod tests {
12763 use super::*;
12764
12765 #[test]
12766 fn test_string_size_with_expanded_tabs() {
12767 let nz = |val| NonZeroU32::new(val).unwrap();
12768 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
12769 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
12770 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
12771 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
12772 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
12773 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
12774 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
12775 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
12776 }
12777}
12778
12779/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
12780struct WordBreakingTokenizer<'a> {
12781 input: &'a str,
12782}
12783
12784impl<'a> WordBreakingTokenizer<'a> {
12785 fn new(input: &'a str) -> Self {
12786 Self { input }
12787 }
12788}
12789
12790fn is_char_ideographic(ch: char) -> bool {
12791 use unicode_script::Script::*;
12792 use unicode_script::UnicodeScript;
12793 matches!(ch.script(), Han | Tangut | Yi)
12794}
12795
12796fn is_grapheme_ideographic(text: &str) -> bool {
12797 text.chars().any(is_char_ideographic)
12798}
12799
12800fn is_grapheme_whitespace(text: &str) -> bool {
12801 text.chars().any(|x| x.is_whitespace())
12802}
12803
12804fn should_stay_with_preceding_ideograph(text: &str) -> bool {
12805 text.chars().next().map_or(false, |ch| {
12806 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
12807 })
12808}
12809
12810#[derive(PartialEq, Eq, Debug, Clone, Copy)]
12811struct WordBreakToken<'a> {
12812 token: &'a str,
12813 grapheme_len: usize,
12814 is_whitespace: bool,
12815}
12816
12817impl<'a> Iterator for WordBreakingTokenizer<'a> {
12818 /// Yields a span, the count of graphemes in the token, and whether it was
12819 /// whitespace. Note that it also breaks at word boundaries.
12820 type Item = WordBreakToken<'a>;
12821
12822 fn next(&mut self) -> Option<Self::Item> {
12823 use unicode_segmentation::UnicodeSegmentation;
12824 if self.input.is_empty() {
12825 return None;
12826 }
12827
12828 let mut iter = self.input.graphemes(true).peekable();
12829 let mut offset = 0;
12830 let mut graphemes = 0;
12831 if let Some(first_grapheme) = iter.next() {
12832 let is_whitespace = is_grapheme_whitespace(first_grapheme);
12833 offset += first_grapheme.len();
12834 graphemes += 1;
12835 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
12836 if let Some(grapheme) = iter.peek().copied() {
12837 if should_stay_with_preceding_ideograph(grapheme) {
12838 offset += grapheme.len();
12839 graphemes += 1;
12840 }
12841 }
12842 } else {
12843 let mut words = self.input[offset..].split_word_bound_indices().peekable();
12844 let mut next_word_bound = words.peek().copied();
12845 if next_word_bound.map_or(false, |(i, _)| i == 0) {
12846 next_word_bound = words.next();
12847 }
12848 while let Some(grapheme) = iter.peek().copied() {
12849 if next_word_bound.map_or(false, |(i, _)| i == offset) {
12850 break;
12851 };
12852 if is_grapheme_whitespace(grapheme) != is_whitespace {
12853 break;
12854 };
12855 offset += grapheme.len();
12856 graphemes += 1;
12857 iter.next();
12858 }
12859 }
12860 let token = &self.input[..offset];
12861 self.input = &self.input[offset..];
12862 if is_whitespace {
12863 Some(WordBreakToken {
12864 token: " ",
12865 grapheme_len: 1,
12866 is_whitespace: true,
12867 })
12868 } else {
12869 Some(WordBreakToken {
12870 token,
12871 grapheme_len: graphemes,
12872 is_whitespace: false,
12873 })
12874 }
12875 } else {
12876 None
12877 }
12878 }
12879}
12880
12881#[test]
12882fn test_word_breaking_tokenizer() {
12883 let tests: &[(&str, &[(&str, usize, bool)])] = &[
12884 ("", &[]),
12885 (" ", &[(" ", 1, true)]),
12886 ("Ʒ", &[("Ʒ", 1, false)]),
12887 ("Ǽ", &[("Ǽ", 1, false)]),
12888 ("⋑", &[("⋑", 1, false)]),
12889 ("⋑⋑", &[("⋑⋑", 2, false)]),
12890 (
12891 "原理,进而",
12892 &[
12893 ("原", 1, false),
12894 ("理,", 2, false),
12895 ("进", 1, false),
12896 ("而", 1, false),
12897 ],
12898 ),
12899 (
12900 "hello world",
12901 &[("hello", 5, false), (" ", 1, true), ("world", 5, false)],
12902 ),
12903 (
12904 "hello, world",
12905 &[("hello,", 6, false), (" ", 1, true), ("world", 5, false)],
12906 ),
12907 (
12908 " hello world",
12909 &[
12910 (" ", 1, true),
12911 ("hello", 5, false),
12912 (" ", 1, true),
12913 ("world", 5, false),
12914 ],
12915 ),
12916 (
12917 "这是什么 \n 钢笔",
12918 &[
12919 ("这", 1, false),
12920 ("是", 1, false),
12921 ("什", 1, false),
12922 ("么", 1, false),
12923 (" ", 1, true),
12924 ("钢", 1, false),
12925 ("笔", 1, false),
12926 ],
12927 ),
12928 (" mutton", &[(" ", 1, true), ("mutton", 6, false)]),
12929 ];
12930
12931 for (input, result) in tests {
12932 assert_eq!(
12933 WordBreakingTokenizer::new(input).collect::<Vec<_>>(),
12934 result
12935 .iter()
12936 .copied()
12937 .map(|(token, grapheme_len, is_whitespace)| WordBreakToken {
12938 token,
12939 grapheme_len,
12940 is_whitespace,
12941 })
12942 .collect::<Vec<_>>()
12943 );
12944 }
12945}
12946
12947fn wrap_with_prefix(
12948 line_prefix: String,
12949 unwrapped_text: String,
12950 wrap_column: usize,
12951 tab_size: NonZeroU32,
12952) -> String {
12953 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
12954 let mut wrapped_text = String::new();
12955 let mut current_line = line_prefix.clone();
12956
12957 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
12958 let mut current_line_len = line_prefix_len;
12959 for WordBreakToken {
12960 token,
12961 grapheme_len,
12962 is_whitespace,
12963 } in tokenizer
12964 {
12965 if current_line_len + grapheme_len > wrap_column && current_line_len != line_prefix_len {
12966 wrapped_text.push_str(current_line.trim_end());
12967 wrapped_text.push('\n');
12968 current_line.truncate(line_prefix.len());
12969 current_line_len = line_prefix_len;
12970 if !is_whitespace {
12971 current_line.push_str(token);
12972 current_line_len += grapheme_len;
12973 }
12974 } else if !is_whitespace {
12975 current_line.push_str(token);
12976 current_line_len += grapheme_len;
12977 } else if current_line_len != line_prefix_len {
12978 current_line.push(' ');
12979 current_line_len += 1;
12980 }
12981 }
12982
12983 if !current_line.is_empty() {
12984 wrapped_text.push_str(¤t_line);
12985 }
12986 wrapped_text
12987}
12988
12989#[test]
12990fn test_wrap_with_prefix() {
12991 assert_eq!(
12992 wrap_with_prefix(
12993 "# ".to_string(),
12994 "abcdefg".to_string(),
12995 4,
12996 NonZeroU32::new(4).unwrap()
12997 ),
12998 "# abcdefg"
12999 );
13000 assert_eq!(
13001 wrap_with_prefix(
13002 "".to_string(),
13003 "\thello world".to_string(),
13004 8,
13005 NonZeroU32::new(4).unwrap()
13006 ),
13007 "hello\nworld"
13008 );
13009 assert_eq!(
13010 wrap_with_prefix(
13011 "// ".to_string(),
13012 "xx \nyy zz aa bb cc".to_string(),
13013 12,
13014 NonZeroU32::new(4).unwrap()
13015 ),
13016 "// xx yy zz\n// aa bb cc"
13017 );
13018 assert_eq!(
13019 wrap_with_prefix(
13020 String::new(),
13021 "这是什么 \n 钢笔".to_string(),
13022 3,
13023 NonZeroU32::new(4).unwrap()
13024 ),
13025 "这是什\n么 钢\n笔"
13026 );
13027}
13028
13029fn hunks_for_selections(
13030 snapshot: &EditorSnapshot,
13031 selections: &[Selection<Point>],
13032) -> Vec<MultiBufferDiffHunk> {
13033 hunks_for_ranges(
13034 selections.iter().map(|selection| selection.range()),
13035 snapshot,
13036 )
13037}
13038
13039pub fn hunks_for_ranges(
13040 ranges: impl Iterator<Item = Range<Point>>,
13041 snapshot: &EditorSnapshot,
13042) -> Vec<MultiBufferDiffHunk> {
13043 let mut hunks = Vec::new();
13044 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
13045 HashMap::default();
13046 for query_range in ranges {
13047 let query_rows =
13048 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
13049 for hunk in snapshot.diff_map.diff_hunks_in_range(
13050 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
13051 &snapshot.buffer_snapshot,
13052 ) {
13053 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
13054 // when the caret is just above or just below the deleted hunk.
13055 let allow_adjacent = hunk_status(&hunk) == DiffHunkStatus::Removed;
13056 let related_to_selection = if allow_adjacent {
13057 hunk.row_range.overlaps(&query_rows)
13058 || hunk.row_range.start == query_rows.end
13059 || hunk.row_range.end == query_rows.start
13060 } else {
13061 hunk.row_range.overlaps(&query_rows)
13062 };
13063 if related_to_selection {
13064 if !processed_buffer_rows
13065 .entry(hunk.buffer_id)
13066 .or_default()
13067 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
13068 {
13069 continue;
13070 }
13071 hunks.push(hunk);
13072 }
13073 }
13074 }
13075
13076 hunks
13077}
13078
13079pub trait CollaborationHub {
13080 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
13081 fn user_participant_indices<'a>(
13082 &self,
13083 cx: &'a AppContext,
13084 ) -> &'a HashMap<u64, ParticipantIndex>;
13085 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
13086}
13087
13088impl CollaborationHub for Model<Project> {
13089 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
13090 self.read(cx).collaborators()
13091 }
13092
13093 fn user_participant_indices<'a>(
13094 &self,
13095 cx: &'a AppContext,
13096 ) -> &'a HashMap<u64, ParticipantIndex> {
13097 self.read(cx).user_store().read(cx).participant_indices()
13098 }
13099
13100 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
13101 let this = self.read(cx);
13102 let user_ids = this.collaborators().values().map(|c| c.user_id);
13103 this.user_store().read_with(cx, |user_store, cx| {
13104 user_store.participant_names(user_ids, cx)
13105 })
13106 }
13107}
13108
13109pub trait SemanticsProvider {
13110 fn hover(
13111 &self,
13112 buffer: &Model<Buffer>,
13113 position: text::Anchor,
13114 cx: &mut AppContext,
13115 ) -> Option<Task<Vec<project::Hover>>>;
13116
13117 fn inlay_hints(
13118 &self,
13119 buffer_handle: Model<Buffer>,
13120 range: Range<text::Anchor>,
13121 cx: &mut AppContext,
13122 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
13123
13124 fn resolve_inlay_hint(
13125 &self,
13126 hint: InlayHint,
13127 buffer_handle: Model<Buffer>,
13128 server_id: LanguageServerId,
13129 cx: &mut AppContext,
13130 ) -> Option<Task<anyhow::Result<InlayHint>>>;
13131
13132 fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool;
13133
13134 fn document_highlights(
13135 &self,
13136 buffer: &Model<Buffer>,
13137 position: text::Anchor,
13138 cx: &mut AppContext,
13139 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
13140
13141 fn definitions(
13142 &self,
13143 buffer: &Model<Buffer>,
13144 position: text::Anchor,
13145 kind: GotoDefinitionKind,
13146 cx: &mut AppContext,
13147 ) -> Option<Task<Result<Vec<LocationLink>>>>;
13148
13149 fn range_for_rename(
13150 &self,
13151 buffer: &Model<Buffer>,
13152 position: text::Anchor,
13153 cx: &mut AppContext,
13154 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
13155
13156 fn perform_rename(
13157 &self,
13158 buffer: &Model<Buffer>,
13159 position: text::Anchor,
13160 new_name: String,
13161 cx: &mut AppContext,
13162 ) -> Option<Task<Result<ProjectTransaction>>>;
13163}
13164
13165pub trait CompletionProvider {
13166 fn completions(
13167 &self,
13168 buffer: &Model<Buffer>,
13169 buffer_position: text::Anchor,
13170 trigger: CompletionContext,
13171 cx: &mut ViewContext<Editor>,
13172 ) -> Task<Result<Vec<Completion>>>;
13173
13174 fn resolve_completions(
13175 &self,
13176 buffer: Model<Buffer>,
13177 completion_indices: Vec<usize>,
13178 completions: Arc<RwLock<Box<[Completion]>>>,
13179 cx: &mut ViewContext<Editor>,
13180 ) -> Task<Result<bool>>;
13181
13182 fn apply_additional_edits_for_completion(
13183 &self,
13184 buffer: Model<Buffer>,
13185 completion: Completion,
13186 push_to_history: bool,
13187 cx: &mut ViewContext<Editor>,
13188 ) -> Task<Result<Option<language::Transaction>>>;
13189
13190 fn is_completion_trigger(
13191 &self,
13192 buffer: &Model<Buffer>,
13193 position: language::Anchor,
13194 text: &str,
13195 trigger_in_words: bool,
13196 cx: &mut ViewContext<Editor>,
13197 ) -> bool;
13198
13199 fn sort_completions(&self) -> bool {
13200 true
13201 }
13202}
13203
13204pub trait CodeActionProvider {
13205 fn code_actions(
13206 &self,
13207 buffer: &Model<Buffer>,
13208 range: Range<text::Anchor>,
13209 cx: &mut WindowContext,
13210 ) -> Task<Result<Vec<CodeAction>>>;
13211
13212 fn apply_code_action(
13213 &self,
13214 buffer_handle: Model<Buffer>,
13215 action: CodeAction,
13216 excerpt_id: ExcerptId,
13217 push_to_history: bool,
13218 cx: &mut WindowContext,
13219 ) -> Task<Result<ProjectTransaction>>;
13220}
13221
13222impl CodeActionProvider for Model<Project> {
13223 fn code_actions(
13224 &self,
13225 buffer: &Model<Buffer>,
13226 range: Range<text::Anchor>,
13227 cx: &mut WindowContext,
13228 ) -> Task<Result<Vec<CodeAction>>> {
13229 self.update(cx, |project, cx| {
13230 project.code_actions(buffer, range, None, cx)
13231 })
13232 }
13233
13234 fn apply_code_action(
13235 &self,
13236 buffer_handle: Model<Buffer>,
13237 action: CodeAction,
13238 _excerpt_id: ExcerptId,
13239 push_to_history: bool,
13240 cx: &mut WindowContext,
13241 ) -> Task<Result<ProjectTransaction>> {
13242 self.update(cx, |project, cx| {
13243 project.apply_code_action(buffer_handle, action, push_to_history, cx)
13244 })
13245 }
13246}
13247
13248fn snippet_completions(
13249 project: &Project,
13250 buffer: &Model<Buffer>,
13251 buffer_position: text::Anchor,
13252 cx: &mut AppContext,
13253) -> Task<Result<Vec<Completion>>> {
13254 let language = buffer.read(cx).language_at(buffer_position);
13255 let language_name = language.as_ref().map(|language| language.lsp_id());
13256 let snippet_store = project.snippets().read(cx);
13257 let snippets = snippet_store.snippets_for(language_name, cx);
13258
13259 if snippets.is_empty() {
13260 return Task::ready(Ok(vec![]));
13261 }
13262 let snapshot = buffer.read(cx).text_snapshot();
13263 let chars: String = snapshot
13264 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
13265 .collect();
13266
13267 let scope = language.map(|language| language.default_scope());
13268 let executor = cx.background_executor().clone();
13269
13270 cx.background_executor().spawn(async move {
13271 let classifier = CharClassifier::new(scope).for_completion(true);
13272 let mut last_word = chars
13273 .chars()
13274 .take_while(|c| classifier.is_word(*c))
13275 .collect::<String>();
13276 last_word = last_word.chars().rev().collect();
13277
13278 if last_word.is_empty() {
13279 return Ok(vec![]);
13280 }
13281
13282 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
13283 let to_lsp = |point: &text::Anchor| {
13284 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
13285 point_to_lsp(end)
13286 };
13287 let lsp_end = to_lsp(&buffer_position);
13288
13289 let candidates = snippets
13290 .iter()
13291 .enumerate()
13292 .flat_map(|(ix, snippet)| {
13293 snippet
13294 .prefix
13295 .iter()
13296 .map(move |prefix| StringMatchCandidate::new(ix, prefix.clone()))
13297 })
13298 .collect::<Vec<StringMatchCandidate>>();
13299
13300 let mut matches = fuzzy::match_strings(
13301 &candidates,
13302 &last_word,
13303 last_word.chars().any(|c| c.is_uppercase()),
13304 100,
13305 &Default::default(),
13306 executor,
13307 )
13308 .await;
13309
13310 // Remove all candidates where the query's start does not match the start of any word in the candidate
13311 if let Some(query_start) = last_word.chars().next() {
13312 matches.retain(|string_match| {
13313 split_words(&string_match.string).any(|word| {
13314 // Check that the first codepoint of the word as lowercase matches the first
13315 // codepoint of the query as lowercase
13316 word.chars()
13317 .flat_map(|codepoint| codepoint.to_lowercase())
13318 .zip(query_start.to_lowercase())
13319 .all(|(word_cp, query_cp)| word_cp == query_cp)
13320 })
13321 });
13322 }
13323
13324 let matched_strings = matches
13325 .into_iter()
13326 .map(|m| m.string)
13327 .collect::<HashSet<_>>();
13328
13329 let result: Vec<Completion> = snippets
13330 .into_iter()
13331 .filter_map(|snippet| {
13332 let matching_prefix = snippet
13333 .prefix
13334 .iter()
13335 .find(|prefix| matched_strings.contains(*prefix))?;
13336 let start = as_offset - last_word.len();
13337 let start = snapshot.anchor_before(start);
13338 let range = start..buffer_position;
13339 let lsp_start = to_lsp(&start);
13340 let lsp_range = lsp::Range {
13341 start: lsp_start,
13342 end: lsp_end,
13343 };
13344 Some(Completion {
13345 old_range: range,
13346 new_text: snippet.body.clone(),
13347 label: CodeLabel {
13348 text: matching_prefix.clone(),
13349 runs: vec![],
13350 filter_range: 0..matching_prefix.len(),
13351 },
13352 server_id: LanguageServerId(usize::MAX),
13353 documentation: snippet.description.clone().map(Documentation::SingleLine),
13354 lsp_completion: lsp::CompletionItem {
13355 label: snippet.prefix.first().unwrap().clone(),
13356 kind: Some(CompletionItemKind::SNIPPET),
13357 label_details: snippet.description.as_ref().map(|description| {
13358 lsp::CompletionItemLabelDetails {
13359 detail: Some(description.clone()),
13360 description: None,
13361 }
13362 }),
13363 insert_text_format: Some(InsertTextFormat::SNIPPET),
13364 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13365 lsp::InsertReplaceEdit {
13366 new_text: snippet.body.clone(),
13367 insert: lsp_range,
13368 replace: lsp_range,
13369 },
13370 )),
13371 filter_text: Some(snippet.body.clone()),
13372 sort_text: Some(char::MAX.to_string()),
13373 ..Default::default()
13374 },
13375 confirm: None,
13376 })
13377 })
13378 .collect();
13379
13380 Ok(result)
13381 })
13382}
13383
13384impl CompletionProvider for Model<Project> {
13385 fn completions(
13386 &self,
13387 buffer: &Model<Buffer>,
13388 buffer_position: text::Anchor,
13389 options: CompletionContext,
13390 cx: &mut ViewContext<Editor>,
13391 ) -> Task<Result<Vec<Completion>>> {
13392 self.update(cx, |project, cx| {
13393 let snippets = snippet_completions(project, buffer, buffer_position, cx);
13394 let project_completions = project.completions(buffer, buffer_position, options, cx);
13395 cx.background_executor().spawn(async move {
13396 let mut completions = project_completions.await?;
13397 let snippets_completions = snippets.await?;
13398 completions.extend(snippets_completions);
13399 Ok(completions)
13400 })
13401 })
13402 }
13403
13404 fn resolve_completions(
13405 &self,
13406 buffer: Model<Buffer>,
13407 completion_indices: Vec<usize>,
13408 completions: Arc<RwLock<Box<[Completion]>>>,
13409 cx: &mut ViewContext<Editor>,
13410 ) -> Task<Result<bool>> {
13411 self.update(cx, |project, cx| {
13412 project.resolve_completions(buffer, completion_indices, completions, cx)
13413 })
13414 }
13415
13416 fn apply_additional_edits_for_completion(
13417 &self,
13418 buffer: Model<Buffer>,
13419 completion: Completion,
13420 push_to_history: bool,
13421 cx: &mut ViewContext<Editor>,
13422 ) -> Task<Result<Option<language::Transaction>>> {
13423 self.update(cx, |project, cx| {
13424 project.apply_additional_edits_for_completion(buffer, completion, push_to_history, cx)
13425 })
13426 }
13427
13428 fn is_completion_trigger(
13429 &self,
13430 buffer: &Model<Buffer>,
13431 position: language::Anchor,
13432 text: &str,
13433 trigger_in_words: bool,
13434 cx: &mut ViewContext<Editor>,
13435 ) -> bool {
13436 let mut chars = text.chars();
13437 let char = if let Some(char) = chars.next() {
13438 char
13439 } else {
13440 return false;
13441 };
13442 if chars.next().is_some() {
13443 return false;
13444 }
13445
13446 let buffer = buffer.read(cx);
13447 let snapshot = buffer.snapshot();
13448 if !snapshot.settings_at(position, cx).show_completions_on_input {
13449 return false;
13450 }
13451 let classifier = snapshot.char_classifier_at(position).for_completion(true);
13452 if trigger_in_words && classifier.is_word(char) {
13453 return true;
13454 }
13455
13456 buffer.completion_triggers().contains(text)
13457 }
13458}
13459
13460impl SemanticsProvider for Model<Project> {
13461 fn hover(
13462 &self,
13463 buffer: &Model<Buffer>,
13464 position: text::Anchor,
13465 cx: &mut AppContext,
13466 ) -> Option<Task<Vec<project::Hover>>> {
13467 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
13468 }
13469
13470 fn document_highlights(
13471 &self,
13472 buffer: &Model<Buffer>,
13473 position: text::Anchor,
13474 cx: &mut AppContext,
13475 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
13476 Some(self.update(cx, |project, cx| {
13477 project.document_highlights(buffer, position, cx)
13478 }))
13479 }
13480
13481 fn definitions(
13482 &self,
13483 buffer: &Model<Buffer>,
13484 position: text::Anchor,
13485 kind: GotoDefinitionKind,
13486 cx: &mut AppContext,
13487 ) -> Option<Task<Result<Vec<LocationLink>>>> {
13488 Some(self.update(cx, |project, cx| match kind {
13489 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
13490 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
13491 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
13492 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
13493 }))
13494 }
13495
13496 fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool {
13497 // TODO: make this work for remote projects
13498 self.read(cx)
13499 .language_servers_for_local_buffer(buffer.read(cx), cx)
13500 .any(
13501 |(_, server)| match server.capabilities().inlay_hint_provider {
13502 Some(lsp::OneOf::Left(enabled)) => enabled,
13503 Some(lsp::OneOf::Right(_)) => true,
13504 None => false,
13505 },
13506 )
13507 }
13508
13509 fn inlay_hints(
13510 &self,
13511 buffer_handle: Model<Buffer>,
13512 range: Range<text::Anchor>,
13513 cx: &mut AppContext,
13514 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
13515 Some(self.update(cx, |project, cx| {
13516 project.inlay_hints(buffer_handle, range, cx)
13517 }))
13518 }
13519
13520 fn resolve_inlay_hint(
13521 &self,
13522 hint: InlayHint,
13523 buffer_handle: Model<Buffer>,
13524 server_id: LanguageServerId,
13525 cx: &mut AppContext,
13526 ) -> Option<Task<anyhow::Result<InlayHint>>> {
13527 Some(self.update(cx, |project, cx| {
13528 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
13529 }))
13530 }
13531
13532 fn range_for_rename(
13533 &self,
13534 buffer: &Model<Buffer>,
13535 position: text::Anchor,
13536 cx: &mut AppContext,
13537 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
13538 Some(self.update(cx, |project, cx| {
13539 project.prepare_rename(buffer.clone(), position, cx)
13540 }))
13541 }
13542
13543 fn perform_rename(
13544 &self,
13545 buffer: &Model<Buffer>,
13546 position: text::Anchor,
13547 new_name: String,
13548 cx: &mut AppContext,
13549 ) -> Option<Task<Result<ProjectTransaction>>> {
13550 Some(self.update(cx, |project, cx| {
13551 project.perform_rename(buffer.clone(), position, new_name, cx)
13552 }))
13553 }
13554}
13555
13556fn inlay_hint_settings(
13557 location: Anchor,
13558 snapshot: &MultiBufferSnapshot,
13559 cx: &mut ViewContext<'_, Editor>,
13560) -> InlayHintSettings {
13561 let file = snapshot.file_at(location);
13562 let language = snapshot.language_at(location).map(|l| l.name());
13563 language_settings(language, file, cx).inlay_hints
13564}
13565
13566fn consume_contiguous_rows(
13567 contiguous_row_selections: &mut Vec<Selection<Point>>,
13568 selection: &Selection<Point>,
13569 display_map: &DisplaySnapshot,
13570 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
13571) -> (MultiBufferRow, MultiBufferRow) {
13572 contiguous_row_selections.push(selection.clone());
13573 let start_row = MultiBufferRow(selection.start.row);
13574 let mut end_row = ending_row(selection, display_map);
13575
13576 while let Some(next_selection) = selections.peek() {
13577 if next_selection.start.row <= end_row.0 {
13578 end_row = ending_row(next_selection, display_map);
13579 contiguous_row_selections.push(selections.next().unwrap().clone());
13580 } else {
13581 break;
13582 }
13583 }
13584 (start_row, end_row)
13585}
13586
13587fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
13588 if next_selection.end.column > 0 || next_selection.is_empty() {
13589 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
13590 } else {
13591 MultiBufferRow(next_selection.end.row)
13592 }
13593}
13594
13595impl EditorSnapshot {
13596 pub fn remote_selections_in_range<'a>(
13597 &'a self,
13598 range: &'a Range<Anchor>,
13599 collaboration_hub: &dyn CollaborationHub,
13600 cx: &'a AppContext,
13601 ) -> impl 'a + Iterator<Item = RemoteSelection> {
13602 let participant_names = collaboration_hub.user_names(cx);
13603 let participant_indices = collaboration_hub.user_participant_indices(cx);
13604 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
13605 let collaborators_by_replica_id = collaborators_by_peer_id
13606 .iter()
13607 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
13608 .collect::<HashMap<_, _>>();
13609 self.buffer_snapshot
13610 .selections_in_range(range, false)
13611 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
13612 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
13613 let participant_index = participant_indices.get(&collaborator.user_id).copied();
13614 let user_name = participant_names.get(&collaborator.user_id).cloned();
13615 Some(RemoteSelection {
13616 replica_id,
13617 selection,
13618 cursor_shape,
13619 line_mode,
13620 participant_index,
13621 peer_id: collaborator.peer_id,
13622 user_name,
13623 })
13624 })
13625 }
13626
13627 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
13628 self.display_snapshot.buffer_snapshot.language_at(position)
13629 }
13630
13631 pub fn is_focused(&self) -> bool {
13632 self.is_focused
13633 }
13634
13635 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
13636 self.placeholder_text.as_ref()
13637 }
13638
13639 pub fn scroll_position(&self) -> gpui::Point<f32> {
13640 self.scroll_anchor.scroll_position(&self.display_snapshot)
13641 }
13642
13643 fn gutter_dimensions(
13644 &self,
13645 font_id: FontId,
13646 font_size: Pixels,
13647 em_width: Pixels,
13648 em_advance: Pixels,
13649 max_line_number_width: Pixels,
13650 cx: &AppContext,
13651 ) -> GutterDimensions {
13652 if !self.show_gutter {
13653 return GutterDimensions::default();
13654 }
13655 let descent = cx.text_system().descent(font_id, font_size);
13656
13657 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
13658 matches!(
13659 ProjectSettings::get_global(cx).git.git_gutter,
13660 Some(GitGutterSetting::TrackedFiles)
13661 )
13662 });
13663 let gutter_settings = EditorSettings::get_global(cx).gutter;
13664 let show_line_numbers = self
13665 .show_line_numbers
13666 .unwrap_or(gutter_settings.line_numbers);
13667 let line_gutter_width = if show_line_numbers {
13668 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
13669 let min_width_for_number_on_gutter = em_advance * 4.0;
13670 max_line_number_width.max(min_width_for_number_on_gutter)
13671 } else {
13672 0.0.into()
13673 };
13674
13675 let show_code_actions = self
13676 .show_code_actions
13677 .unwrap_or(gutter_settings.code_actions);
13678
13679 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
13680
13681 let git_blame_entries_width =
13682 self.git_blame_gutter_max_author_length
13683 .map(|max_author_length| {
13684 // Length of the author name, but also space for the commit hash,
13685 // the spacing and the timestamp.
13686 let max_char_count = max_author_length
13687 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
13688 + 7 // length of commit sha
13689 + 14 // length of max relative timestamp ("60 minutes ago")
13690 + 4; // gaps and margins
13691
13692 em_advance * max_char_count
13693 });
13694
13695 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
13696 left_padding += if show_code_actions || show_runnables {
13697 em_width * 3.0
13698 } else if show_git_gutter && show_line_numbers {
13699 em_width * 2.0
13700 } else if show_git_gutter || show_line_numbers {
13701 em_width
13702 } else {
13703 px(0.)
13704 };
13705
13706 let right_padding = if gutter_settings.folds && show_line_numbers {
13707 em_width * 4.0
13708 } else if gutter_settings.folds {
13709 em_width * 3.0
13710 } else if show_line_numbers {
13711 em_width
13712 } else {
13713 px(0.)
13714 };
13715
13716 GutterDimensions {
13717 left_padding,
13718 right_padding,
13719 width: line_gutter_width + left_padding + right_padding,
13720 margin: -descent,
13721 git_blame_entries_width,
13722 }
13723 }
13724
13725 pub fn render_crease_toggle(
13726 &self,
13727 buffer_row: MultiBufferRow,
13728 row_contains_cursor: bool,
13729 editor: View<Editor>,
13730 cx: &mut WindowContext,
13731 ) -> Option<AnyElement> {
13732 let folded = self.is_line_folded(buffer_row);
13733 let mut is_foldable = false;
13734
13735 if let Some(crease) = self
13736 .crease_snapshot
13737 .query_row(buffer_row, &self.buffer_snapshot)
13738 {
13739 is_foldable = true;
13740 match crease {
13741 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
13742 if let Some(render_toggle) = render_toggle {
13743 let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
13744 if folded {
13745 editor.update(cx, |editor, cx| {
13746 editor.fold_at(&crate::FoldAt { buffer_row }, cx)
13747 });
13748 } else {
13749 editor.update(cx, |editor, cx| {
13750 editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
13751 });
13752 }
13753 });
13754 return Some((render_toggle)(buffer_row, folded, toggle_callback, cx));
13755 }
13756 }
13757 }
13758 }
13759
13760 is_foldable |= self.starts_indent(buffer_row);
13761
13762 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
13763 Some(
13764 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
13765 .toggle_state(folded)
13766 .on_click(cx.listener_for(&editor, move |this, _e, cx| {
13767 if folded {
13768 this.unfold_at(&UnfoldAt { buffer_row }, cx);
13769 } else {
13770 this.fold_at(&FoldAt { buffer_row }, cx);
13771 }
13772 }))
13773 .into_any_element(),
13774 )
13775 } else {
13776 None
13777 }
13778 }
13779
13780 pub fn render_crease_trailer(
13781 &self,
13782 buffer_row: MultiBufferRow,
13783 cx: &mut WindowContext,
13784 ) -> Option<AnyElement> {
13785 let folded = self.is_line_folded(buffer_row);
13786 if let Crease::Inline { render_trailer, .. } = self
13787 .crease_snapshot
13788 .query_row(buffer_row, &self.buffer_snapshot)?
13789 {
13790 let render_trailer = render_trailer.as_ref()?;
13791 Some(render_trailer(buffer_row, folded, cx))
13792 } else {
13793 None
13794 }
13795 }
13796}
13797
13798impl Deref for EditorSnapshot {
13799 type Target = DisplaySnapshot;
13800
13801 fn deref(&self) -> &Self::Target {
13802 &self.display_snapshot
13803 }
13804}
13805
13806#[derive(Clone, Debug, PartialEq, Eq)]
13807pub enum EditorEvent {
13808 InputIgnored {
13809 text: Arc<str>,
13810 },
13811 InputHandled {
13812 utf16_range_to_replace: Option<Range<isize>>,
13813 text: Arc<str>,
13814 },
13815 ExcerptsAdded {
13816 buffer: Model<Buffer>,
13817 predecessor: ExcerptId,
13818 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
13819 },
13820 ExcerptsRemoved {
13821 ids: Vec<ExcerptId>,
13822 },
13823 ExcerptsEdited {
13824 ids: Vec<ExcerptId>,
13825 },
13826 ExcerptsExpanded {
13827 ids: Vec<ExcerptId>,
13828 },
13829 BufferEdited,
13830 Edited {
13831 transaction_id: clock::Lamport,
13832 },
13833 Reparsed(BufferId),
13834 Focused,
13835 FocusedIn,
13836 Blurred,
13837 DirtyChanged,
13838 Saved,
13839 TitleChanged,
13840 DiffBaseChanged,
13841 SelectionsChanged {
13842 local: bool,
13843 },
13844 ScrollPositionChanged {
13845 local: bool,
13846 autoscroll: bool,
13847 },
13848 Closed,
13849 TransactionUndone {
13850 transaction_id: clock::Lamport,
13851 },
13852 TransactionBegun {
13853 transaction_id: clock::Lamport,
13854 },
13855 Reloaded,
13856 CursorShapeChanged,
13857}
13858
13859impl EventEmitter<EditorEvent> for Editor {}
13860
13861impl FocusableView for Editor {
13862 fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
13863 self.focus_handle.clone()
13864 }
13865}
13866
13867impl Render for Editor {
13868 fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
13869 let settings = ThemeSettings::get_global(cx);
13870
13871 let mut text_style = match self.mode {
13872 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
13873 color: cx.theme().colors().editor_foreground,
13874 font_family: settings.ui_font.family.clone(),
13875 font_features: settings.ui_font.features.clone(),
13876 font_fallbacks: settings.ui_font.fallbacks.clone(),
13877 font_size: rems(0.875).into(),
13878 font_weight: settings.ui_font.weight,
13879 line_height: relative(settings.buffer_line_height.value()),
13880 ..Default::default()
13881 },
13882 EditorMode::Full => TextStyle {
13883 color: cx.theme().colors().editor_foreground,
13884 font_family: settings.buffer_font.family.clone(),
13885 font_features: settings.buffer_font.features.clone(),
13886 font_fallbacks: settings.buffer_font.fallbacks.clone(),
13887 font_size: settings.buffer_font_size(cx).into(),
13888 font_weight: settings.buffer_font.weight,
13889 line_height: relative(settings.buffer_line_height.value()),
13890 ..Default::default()
13891 },
13892 };
13893 if let Some(text_style_refinement) = &self.text_style_refinement {
13894 text_style.refine(text_style_refinement)
13895 }
13896
13897 let background = match self.mode {
13898 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
13899 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
13900 EditorMode::Full => cx.theme().colors().editor_background,
13901 };
13902
13903 EditorElement::new(
13904 cx.view(),
13905 EditorStyle {
13906 background,
13907 local_player: cx.theme().players().local(),
13908 text: text_style,
13909 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
13910 syntax: cx.theme().syntax().clone(),
13911 status: cx.theme().status().clone(),
13912 inlay_hints_style: make_inlay_hints_style(cx),
13913 inline_completion_styles: make_suggestion_styles(cx),
13914 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
13915 },
13916 )
13917 }
13918}
13919
13920impl ViewInputHandler for Editor {
13921 fn text_for_range(
13922 &mut self,
13923 range_utf16: Range<usize>,
13924 adjusted_range: &mut Option<Range<usize>>,
13925 cx: &mut ViewContext<Self>,
13926 ) -> Option<String> {
13927 let snapshot = self.buffer.read(cx).read(cx);
13928 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
13929 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
13930 if (start.0..end.0) != range_utf16 {
13931 adjusted_range.replace(start.0..end.0);
13932 }
13933 Some(snapshot.text_for_range(start..end).collect())
13934 }
13935
13936 fn selected_text_range(
13937 &mut self,
13938 ignore_disabled_input: bool,
13939 cx: &mut ViewContext<Self>,
13940 ) -> Option<UTF16Selection> {
13941 // Prevent the IME menu from appearing when holding down an alphabetic key
13942 // while input is disabled.
13943 if !ignore_disabled_input && !self.input_enabled {
13944 return None;
13945 }
13946
13947 let selection = self.selections.newest::<OffsetUtf16>(cx);
13948 let range = selection.range();
13949
13950 Some(UTF16Selection {
13951 range: range.start.0..range.end.0,
13952 reversed: selection.reversed,
13953 })
13954 }
13955
13956 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
13957 let snapshot = self.buffer.read(cx).read(cx);
13958 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
13959 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
13960 }
13961
13962 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
13963 self.clear_highlights::<InputComposition>(cx);
13964 self.ime_transaction.take();
13965 }
13966
13967 fn replace_text_in_range(
13968 &mut self,
13969 range_utf16: Option<Range<usize>>,
13970 text: &str,
13971 cx: &mut ViewContext<Self>,
13972 ) {
13973 if !self.input_enabled {
13974 cx.emit(EditorEvent::InputIgnored { text: text.into() });
13975 return;
13976 }
13977
13978 self.transact(cx, |this, cx| {
13979 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
13980 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
13981 Some(this.selection_replacement_ranges(range_utf16, cx))
13982 } else {
13983 this.marked_text_ranges(cx)
13984 };
13985
13986 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
13987 let newest_selection_id = this.selections.newest_anchor().id;
13988 this.selections
13989 .all::<OffsetUtf16>(cx)
13990 .iter()
13991 .zip(ranges_to_replace.iter())
13992 .find_map(|(selection, range)| {
13993 if selection.id == newest_selection_id {
13994 Some(
13995 (range.start.0 as isize - selection.head().0 as isize)
13996 ..(range.end.0 as isize - selection.head().0 as isize),
13997 )
13998 } else {
13999 None
14000 }
14001 })
14002 });
14003
14004 cx.emit(EditorEvent::InputHandled {
14005 utf16_range_to_replace: range_to_replace,
14006 text: text.into(),
14007 });
14008
14009 if let Some(new_selected_ranges) = new_selected_ranges {
14010 this.change_selections(None, cx, |selections| {
14011 selections.select_ranges(new_selected_ranges)
14012 });
14013 this.backspace(&Default::default(), cx);
14014 }
14015
14016 this.handle_input(text, cx);
14017 });
14018
14019 if let Some(transaction) = self.ime_transaction {
14020 self.buffer.update(cx, |buffer, cx| {
14021 buffer.group_until_transaction(transaction, cx);
14022 });
14023 }
14024
14025 self.unmark_text(cx);
14026 }
14027
14028 fn replace_and_mark_text_in_range(
14029 &mut self,
14030 range_utf16: Option<Range<usize>>,
14031 text: &str,
14032 new_selected_range_utf16: Option<Range<usize>>,
14033 cx: &mut ViewContext<Self>,
14034 ) {
14035 if !self.input_enabled {
14036 return;
14037 }
14038
14039 let transaction = self.transact(cx, |this, cx| {
14040 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
14041 let snapshot = this.buffer.read(cx).read(cx);
14042 if let Some(relative_range_utf16) = range_utf16.as_ref() {
14043 for marked_range in &mut marked_ranges {
14044 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
14045 marked_range.start.0 += relative_range_utf16.start;
14046 marked_range.start =
14047 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
14048 marked_range.end =
14049 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
14050 }
14051 }
14052 Some(marked_ranges)
14053 } else if let Some(range_utf16) = range_utf16 {
14054 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
14055 Some(this.selection_replacement_ranges(range_utf16, cx))
14056 } else {
14057 None
14058 };
14059
14060 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
14061 let newest_selection_id = this.selections.newest_anchor().id;
14062 this.selections
14063 .all::<OffsetUtf16>(cx)
14064 .iter()
14065 .zip(ranges_to_replace.iter())
14066 .find_map(|(selection, range)| {
14067 if selection.id == newest_selection_id {
14068 Some(
14069 (range.start.0 as isize - selection.head().0 as isize)
14070 ..(range.end.0 as isize - selection.head().0 as isize),
14071 )
14072 } else {
14073 None
14074 }
14075 })
14076 });
14077
14078 cx.emit(EditorEvent::InputHandled {
14079 utf16_range_to_replace: range_to_replace,
14080 text: text.into(),
14081 });
14082
14083 if let Some(ranges) = ranges_to_replace {
14084 this.change_selections(None, cx, |s| s.select_ranges(ranges));
14085 }
14086
14087 let marked_ranges = {
14088 let snapshot = this.buffer.read(cx).read(cx);
14089 this.selections
14090 .disjoint_anchors()
14091 .iter()
14092 .map(|selection| {
14093 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
14094 })
14095 .collect::<Vec<_>>()
14096 };
14097
14098 if text.is_empty() {
14099 this.unmark_text(cx);
14100 } else {
14101 this.highlight_text::<InputComposition>(
14102 marked_ranges.clone(),
14103 HighlightStyle {
14104 underline: Some(UnderlineStyle {
14105 thickness: px(1.),
14106 color: None,
14107 wavy: false,
14108 }),
14109 ..Default::default()
14110 },
14111 cx,
14112 );
14113 }
14114
14115 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
14116 let use_autoclose = this.use_autoclose;
14117 let use_auto_surround = this.use_auto_surround;
14118 this.set_use_autoclose(false);
14119 this.set_use_auto_surround(false);
14120 this.handle_input(text, cx);
14121 this.set_use_autoclose(use_autoclose);
14122 this.set_use_auto_surround(use_auto_surround);
14123
14124 if let Some(new_selected_range) = new_selected_range_utf16 {
14125 let snapshot = this.buffer.read(cx).read(cx);
14126 let new_selected_ranges = marked_ranges
14127 .into_iter()
14128 .map(|marked_range| {
14129 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
14130 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
14131 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
14132 snapshot.clip_offset_utf16(new_start, Bias::Left)
14133 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
14134 })
14135 .collect::<Vec<_>>();
14136
14137 drop(snapshot);
14138 this.change_selections(None, cx, |selections| {
14139 selections.select_ranges(new_selected_ranges)
14140 });
14141 }
14142 });
14143
14144 self.ime_transaction = self.ime_transaction.or(transaction);
14145 if let Some(transaction) = self.ime_transaction {
14146 self.buffer.update(cx, |buffer, cx| {
14147 buffer.group_until_transaction(transaction, cx);
14148 });
14149 }
14150
14151 if self.text_highlights::<InputComposition>(cx).is_none() {
14152 self.ime_transaction.take();
14153 }
14154 }
14155
14156 fn bounds_for_range(
14157 &mut self,
14158 range_utf16: Range<usize>,
14159 element_bounds: gpui::Bounds<Pixels>,
14160 cx: &mut ViewContext<Self>,
14161 ) -> Option<gpui::Bounds<Pixels>> {
14162 let text_layout_details = self.text_layout_details(cx);
14163 let gpui::Point {
14164 x: em_width,
14165 y: line_height,
14166 } = self.character_size(cx);
14167
14168 let snapshot = self.snapshot(cx);
14169 let scroll_position = snapshot.scroll_position();
14170 let scroll_left = scroll_position.x * em_width;
14171
14172 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
14173 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
14174 + self.gutter_dimensions.width
14175 + self.gutter_dimensions.margin;
14176 let y = line_height * (start.row().as_f32() - scroll_position.y);
14177
14178 Some(Bounds {
14179 origin: element_bounds.origin + point(x, y),
14180 size: size(em_width, line_height),
14181 })
14182 }
14183}
14184
14185trait SelectionExt {
14186 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
14187 fn spanned_rows(
14188 &self,
14189 include_end_if_at_line_start: bool,
14190 map: &DisplaySnapshot,
14191 ) -> Range<MultiBufferRow>;
14192}
14193
14194impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
14195 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
14196 let start = self
14197 .start
14198 .to_point(&map.buffer_snapshot)
14199 .to_display_point(map);
14200 let end = self
14201 .end
14202 .to_point(&map.buffer_snapshot)
14203 .to_display_point(map);
14204 if self.reversed {
14205 end..start
14206 } else {
14207 start..end
14208 }
14209 }
14210
14211 fn spanned_rows(
14212 &self,
14213 include_end_if_at_line_start: bool,
14214 map: &DisplaySnapshot,
14215 ) -> Range<MultiBufferRow> {
14216 let start = self.start.to_point(&map.buffer_snapshot);
14217 let mut end = self.end.to_point(&map.buffer_snapshot);
14218 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
14219 end.row -= 1;
14220 }
14221
14222 let buffer_start = map.prev_line_boundary(start).0;
14223 let buffer_end = map.next_line_boundary(end).0;
14224 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
14225 }
14226}
14227
14228impl<T: InvalidationRegion> InvalidationStack<T> {
14229 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
14230 where
14231 S: Clone + ToOffset,
14232 {
14233 while let Some(region) = self.last() {
14234 let all_selections_inside_invalidation_ranges =
14235 if selections.len() == region.ranges().len() {
14236 selections
14237 .iter()
14238 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
14239 .all(|(selection, invalidation_range)| {
14240 let head = selection.head().to_offset(buffer);
14241 invalidation_range.start <= head && invalidation_range.end >= head
14242 })
14243 } else {
14244 false
14245 };
14246
14247 if all_selections_inside_invalidation_ranges {
14248 break;
14249 } else {
14250 self.pop();
14251 }
14252 }
14253 }
14254}
14255
14256impl<T> Default for InvalidationStack<T> {
14257 fn default() -> Self {
14258 Self(Default::default())
14259 }
14260}
14261
14262impl<T> Deref for InvalidationStack<T> {
14263 type Target = Vec<T>;
14264
14265 fn deref(&self) -> &Self::Target {
14266 &self.0
14267 }
14268}
14269
14270impl<T> DerefMut for InvalidationStack<T> {
14271 fn deref_mut(&mut self) -> &mut Self::Target {
14272 &mut self.0
14273 }
14274}
14275
14276impl InvalidationRegion for SnippetState {
14277 fn ranges(&self) -> &[Range<Anchor>] {
14278 &self.ranges[self.active_index]
14279 }
14280}
14281
14282pub fn diagnostic_block_renderer(
14283 diagnostic: Diagnostic,
14284 max_message_rows: Option<u8>,
14285 allow_closing: bool,
14286 _is_valid: bool,
14287) -> RenderBlock {
14288 let (text_without_backticks, code_ranges) =
14289 highlight_diagnostic_message(&diagnostic, max_message_rows);
14290
14291 Arc::new(move |cx: &mut BlockContext| {
14292 let group_id: SharedString = cx.block_id.to_string().into();
14293
14294 let mut text_style = cx.text_style().clone();
14295 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
14296 let theme_settings = ThemeSettings::get_global(cx);
14297 text_style.font_family = theme_settings.buffer_font.family.clone();
14298 text_style.font_style = theme_settings.buffer_font.style;
14299 text_style.font_features = theme_settings.buffer_font.features.clone();
14300 text_style.font_weight = theme_settings.buffer_font.weight;
14301
14302 let multi_line_diagnostic = diagnostic.message.contains('\n');
14303
14304 let buttons = |diagnostic: &Diagnostic| {
14305 if multi_line_diagnostic {
14306 v_flex()
14307 } else {
14308 h_flex()
14309 }
14310 .when(allow_closing, |div| {
14311 div.children(diagnostic.is_primary.then(|| {
14312 IconButton::new("close-block", IconName::XCircle)
14313 .icon_color(Color::Muted)
14314 .size(ButtonSize::Compact)
14315 .style(ButtonStyle::Transparent)
14316 .visible_on_hover(group_id.clone())
14317 .on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
14318 .tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
14319 }))
14320 })
14321 .child(
14322 IconButton::new("copy-block", IconName::Copy)
14323 .icon_color(Color::Muted)
14324 .size(ButtonSize::Compact)
14325 .style(ButtonStyle::Transparent)
14326 .visible_on_hover(group_id.clone())
14327 .on_click({
14328 let message = diagnostic.message.clone();
14329 move |_click, cx| {
14330 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
14331 }
14332 })
14333 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
14334 )
14335 };
14336
14337 let icon_size = buttons(&diagnostic)
14338 .into_any_element()
14339 .layout_as_root(AvailableSpace::min_size(), cx);
14340
14341 h_flex()
14342 .id(cx.block_id)
14343 .group(group_id.clone())
14344 .relative()
14345 .size_full()
14346 .block_mouse_down()
14347 .pl(cx.gutter_dimensions.width)
14348 .w(cx.max_width - cx.gutter_dimensions.full_width())
14349 .child(
14350 div()
14351 .flex()
14352 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
14353 .flex_shrink(),
14354 )
14355 .child(buttons(&diagnostic))
14356 .child(div().flex().flex_shrink_0().child(
14357 StyledText::new(text_without_backticks.clone()).with_highlights(
14358 &text_style,
14359 code_ranges.iter().map(|range| {
14360 (
14361 range.clone(),
14362 HighlightStyle {
14363 font_weight: Some(FontWeight::BOLD),
14364 ..Default::default()
14365 },
14366 )
14367 }),
14368 ),
14369 ))
14370 .into_any_element()
14371 })
14372}
14373
14374pub fn highlight_diagnostic_message(
14375 diagnostic: &Diagnostic,
14376 mut max_message_rows: Option<u8>,
14377) -> (SharedString, Vec<Range<usize>>) {
14378 let mut text_without_backticks = String::new();
14379 let mut code_ranges = Vec::new();
14380
14381 if let Some(source) = &diagnostic.source {
14382 text_without_backticks.push_str(source);
14383 code_ranges.push(0..source.len());
14384 text_without_backticks.push_str(": ");
14385 }
14386
14387 let mut prev_offset = 0;
14388 let mut in_code_block = false;
14389 let has_row_limit = max_message_rows.is_some();
14390 let mut newline_indices = diagnostic
14391 .message
14392 .match_indices('\n')
14393 .filter(|_| has_row_limit)
14394 .map(|(ix, _)| ix)
14395 .fuse()
14396 .peekable();
14397
14398 for (quote_ix, _) in diagnostic
14399 .message
14400 .match_indices('`')
14401 .chain([(diagnostic.message.len(), "")])
14402 {
14403 let mut first_newline_ix = None;
14404 let mut last_newline_ix = None;
14405 while let Some(newline_ix) = newline_indices.peek() {
14406 if *newline_ix < quote_ix {
14407 if first_newline_ix.is_none() {
14408 first_newline_ix = Some(*newline_ix);
14409 }
14410 last_newline_ix = Some(*newline_ix);
14411
14412 if let Some(rows_left) = &mut max_message_rows {
14413 if *rows_left == 0 {
14414 break;
14415 } else {
14416 *rows_left -= 1;
14417 }
14418 }
14419 let _ = newline_indices.next();
14420 } else {
14421 break;
14422 }
14423 }
14424 let prev_len = text_without_backticks.len();
14425 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
14426 text_without_backticks.push_str(new_text);
14427 if in_code_block {
14428 code_ranges.push(prev_len..text_without_backticks.len());
14429 }
14430 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
14431 in_code_block = !in_code_block;
14432 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
14433 text_without_backticks.push_str("...");
14434 break;
14435 }
14436 }
14437
14438 (text_without_backticks.into(), code_ranges)
14439}
14440
14441fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
14442 match severity {
14443 DiagnosticSeverity::ERROR => colors.error,
14444 DiagnosticSeverity::WARNING => colors.warning,
14445 DiagnosticSeverity::INFORMATION => colors.info,
14446 DiagnosticSeverity::HINT => colors.info,
14447 _ => colors.ignored,
14448 }
14449}
14450
14451pub fn styled_runs_for_code_label<'a>(
14452 label: &'a CodeLabel,
14453 syntax_theme: &'a theme::SyntaxTheme,
14454) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
14455 let fade_out = HighlightStyle {
14456 fade_out: Some(0.35),
14457 ..Default::default()
14458 };
14459
14460 let mut prev_end = label.filter_range.end;
14461 label
14462 .runs
14463 .iter()
14464 .enumerate()
14465 .flat_map(move |(ix, (range, highlight_id))| {
14466 let style = if let Some(style) = highlight_id.style(syntax_theme) {
14467 style
14468 } else {
14469 return Default::default();
14470 };
14471 let mut muted_style = style;
14472 muted_style.highlight(fade_out);
14473
14474 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
14475 if range.start >= label.filter_range.end {
14476 if range.start > prev_end {
14477 runs.push((prev_end..range.start, fade_out));
14478 }
14479 runs.push((range.clone(), muted_style));
14480 } else if range.end <= label.filter_range.end {
14481 runs.push((range.clone(), style));
14482 } else {
14483 runs.push((range.start..label.filter_range.end, style));
14484 runs.push((label.filter_range.end..range.end, muted_style));
14485 }
14486 prev_end = cmp::max(prev_end, range.end);
14487
14488 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
14489 runs.push((prev_end..label.text.len(), fade_out));
14490 }
14491
14492 runs
14493 })
14494}
14495
14496pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
14497 let mut prev_index = 0;
14498 let mut prev_codepoint: Option<char> = None;
14499 text.char_indices()
14500 .chain([(text.len(), '\0')])
14501 .filter_map(move |(index, codepoint)| {
14502 let prev_codepoint = prev_codepoint.replace(codepoint)?;
14503 let is_boundary = index == text.len()
14504 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
14505 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
14506 if is_boundary {
14507 let chunk = &text[prev_index..index];
14508 prev_index = index;
14509 Some(chunk)
14510 } else {
14511 None
14512 }
14513 })
14514}
14515
14516pub trait RangeToAnchorExt: Sized {
14517 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
14518
14519 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
14520 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
14521 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
14522 }
14523}
14524
14525impl<T: ToOffset> RangeToAnchorExt for Range<T> {
14526 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
14527 let start_offset = self.start.to_offset(snapshot);
14528 let end_offset = self.end.to_offset(snapshot);
14529 if start_offset == end_offset {
14530 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
14531 } else {
14532 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
14533 }
14534 }
14535}
14536
14537pub trait RowExt {
14538 fn as_f32(&self) -> f32;
14539
14540 fn next_row(&self) -> Self;
14541
14542 fn previous_row(&self) -> Self;
14543
14544 fn minus(&self, other: Self) -> u32;
14545}
14546
14547impl RowExt for DisplayRow {
14548 fn as_f32(&self) -> f32 {
14549 self.0 as f32
14550 }
14551
14552 fn next_row(&self) -> Self {
14553 Self(self.0 + 1)
14554 }
14555
14556 fn previous_row(&self) -> Self {
14557 Self(self.0.saturating_sub(1))
14558 }
14559
14560 fn minus(&self, other: Self) -> u32 {
14561 self.0 - other.0
14562 }
14563}
14564
14565impl RowExt for MultiBufferRow {
14566 fn as_f32(&self) -> f32 {
14567 self.0 as f32
14568 }
14569
14570 fn next_row(&self) -> Self {
14571 Self(self.0 + 1)
14572 }
14573
14574 fn previous_row(&self) -> Self {
14575 Self(self.0.saturating_sub(1))
14576 }
14577
14578 fn minus(&self, other: Self) -> u32 {
14579 self.0 - other.0
14580 }
14581}
14582
14583trait RowRangeExt {
14584 type Row;
14585
14586 fn len(&self) -> usize;
14587
14588 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
14589}
14590
14591impl RowRangeExt for Range<MultiBufferRow> {
14592 type Row = MultiBufferRow;
14593
14594 fn len(&self) -> usize {
14595 (self.end.0 - self.start.0) as usize
14596 }
14597
14598 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
14599 (self.start.0..self.end.0).map(MultiBufferRow)
14600 }
14601}
14602
14603impl RowRangeExt for Range<DisplayRow> {
14604 type Row = DisplayRow;
14605
14606 fn len(&self) -> usize {
14607 (self.end.0 - self.start.0) as usize
14608 }
14609
14610 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
14611 (self.start.0..self.end.0).map(DisplayRow)
14612 }
14613}
14614
14615fn hunk_status(hunk: &MultiBufferDiffHunk) -> DiffHunkStatus {
14616 if hunk.diff_base_byte_range.is_empty() {
14617 DiffHunkStatus::Added
14618 } else if hunk.row_range.is_empty() {
14619 DiffHunkStatus::Removed
14620 } else {
14621 DiffHunkStatus::Modified
14622 }
14623}
14624
14625/// If select range has more than one line, we
14626/// just point the cursor to range.start.
14627fn check_multiline_range(buffer: &Buffer, range: Range<usize>) -> Range<usize> {
14628 if buffer.offset_to_point(range.start).row == buffer.offset_to_point(range.end).row {
14629 range
14630 } else {
14631 range.start..range.start
14632 }
14633}
14634
14635pub struct KillRing(ClipboardItem);
14636impl Global for KillRing {}
14637
14638const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);