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(), 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_selection(&mut self, _: &DuplicateSelection, 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 for selection in selections.iter() {
6145 let start = selection.start;
6146 let end = selection.end;
6147 let text = buffer.text_for_range(start..end).collect::<String>();
6148 edits.push((selection.end..selection.end, text));
6149 }
6150
6151 self.transact(cx, |this, cx| {
6152 this.buffer.update(cx, |buffer, cx| {
6153 buffer.edit(edits, None, cx);
6154 });
6155
6156 this.request_autoscroll(Autoscroll::fit(), cx);
6157 });
6158 }
6159
6160 pub fn duplicate_line(&mut self, upwards: bool, cx: &mut ViewContext<Self>) {
6161 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6162 let buffer = &display_map.buffer_snapshot;
6163 let selections = self.selections.all::<Point>(cx);
6164
6165 let mut edits = Vec::new();
6166 let mut selections_iter = selections.iter().peekable();
6167 while let Some(selection) = selections_iter.next() {
6168 // Avoid duplicating the same lines twice.
6169 let mut rows = selection.spanned_rows(false, &display_map);
6170
6171 while let Some(next_selection) = selections_iter.peek() {
6172 let next_rows = next_selection.spanned_rows(false, &display_map);
6173 if next_rows.start < rows.end {
6174 rows.end = next_rows.end;
6175 selections_iter.next().unwrap();
6176 } else {
6177 break;
6178 }
6179 }
6180
6181 // Copy the text from the selected row region and splice it either at the start
6182 // or end of the region.
6183 let start = Point::new(rows.start.0, 0);
6184 let end = Point::new(
6185 rows.end.previous_row().0,
6186 buffer.line_len(rows.end.previous_row()),
6187 );
6188 let text = buffer
6189 .text_for_range(start..end)
6190 .chain(Some("\n"))
6191 .collect::<String>();
6192 let insert_location = if upwards {
6193 Point::new(rows.end.0, 0)
6194 } else {
6195 start
6196 };
6197 edits.push((insert_location..insert_location, text));
6198 }
6199
6200 self.transact(cx, |this, cx| {
6201 this.buffer.update(cx, |buffer, cx| {
6202 buffer.edit(edits, None, cx);
6203 });
6204
6205 this.request_autoscroll(Autoscroll::fit(), cx);
6206 });
6207 }
6208
6209 pub fn duplicate_line_up(&mut self, _: &DuplicateLineUp, cx: &mut ViewContext<Self>) {
6210 self.duplicate_line(true, cx);
6211 }
6212
6213 pub fn duplicate_line_down(&mut self, _: &DuplicateLineDown, cx: &mut ViewContext<Self>) {
6214 self.duplicate_line(false, cx);
6215 }
6216
6217 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
6218 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6219 let buffer = self.buffer.read(cx).snapshot(cx);
6220
6221 let mut edits = Vec::new();
6222 let mut unfold_ranges = Vec::new();
6223 let mut refold_creases = Vec::new();
6224
6225 let selections = self.selections.all::<Point>(cx);
6226 let mut selections = selections.iter().peekable();
6227 let mut contiguous_row_selections = Vec::new();
6228 let mut new_selections = Vec::new();
6229
6230 while let Some(selection) = selections.next() {
6231 // Find all the selections that span a contiguous row range
6232 let (start_row, end_row) = consume_contiguous_rows(
6233 &mut contiguous_row_selections,
6234 selection,
6235 &display_map,
6236 &mut selections,
6237 );
6238
6239 // Move the text spanned by the row range to be before the line preceding the row range
6240 if start_row.0 > 0 {
6241 let range_to_move = Point::new(
6242 start_row.previous_row().0,
6243 buffer.line_len(start_row.previous_row()),
6244 )
6245 ..Point::new(
6246 end_row.previous_row().0,
6247 buffer.line_len(end_row.previous_row()),
6248 );
6249 let insertion_point = display_map
6250 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
6251 .0;
6252
6253 // Don't move lines across excerpts
6254 if buffer
6255 .excerpt_boundaries_in_range((
6256 Bound::Excluded(insertion_point),
6257 Bound::Included(range_to_move.end),
6258 ))
6259 .next()
6260 .is_none()
6261 {
6262 let text = buffer
6263 .text_for_range(range_to_move.clone())
6264 .flat_map(|s| s.chars())
6265 .skip(1)
6266 .chain(['\n'])
6267 .collect::<String>();
6268
6269 edits.push((
6270 buffer.anchor_after(range_to_move.start)
6271 ..buffer.anchor_before(range_to_move.end),
6272 String::new(),
6273 ));
6274 let insertion_anchor = buffer.anchor_after(insertion_point);
6275 edits.push((insertion_anchor..insertion_anchor, text));
6276
6277 let row_delta = range_to_move.start.row - insertion_point.row + 1;
6278
6279 // Move selections up
6280 new_selections.extend(contiguous_row_selections.drain(..).map(
6281 |mut selection| {
6282 selection.start.row -= row_delta;
6283 selection.end.row -= row_delta;
6284 selection
6285 },
6286 ));
6287
6288 // Move folds up
6289 unfold_ranges.push(range_to_move.clone());
6290 for fold in display_map.folds_in_range(
6291 buffer.anchor_before(range_to_move.start)
6292 ..buffer.anchor_after(range_to_move.end),
6293 ) {
6294 let mut start = fold.range.start.to_point(&buffer);
6295 let mut end = fold.range.end.to_point(&buffer);
6296 start.row -= row_delta;
6297 end.row -= row_delta;
6298 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
6299 }
6300 }
6301 }
6302
6303 // If we didn't move line(s), preserve the existing selections
6304 new_selections.append(&mut contiguous_row_selections);
6305 }
6306
6307 self.transact(cx, |this, cx| {
6308 this.unfold_ranges(&unfold_ranges, true, true, cx);
6309 this.buffer.update(cx, |buffer, cx| {
6310 for (range, text) in edits {
6311 buffer.edit([(range, text)], None, cx);
6312 }
6313 });
6314 this.fold_creases(refold_creases, true, cx);
6315 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6316 s.select(new_selections);
6317 })
6318 });
6319 }
6320
6321 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
6322 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6323 let buffer = self.buffer.read(cx).snapshot(cx);
6324
6325 let mut edits = Vec::new();
6326 let mut unfold_ranges = Vec::new();
6327 let mut refold_creases = Vec::new();
6328
6329 let selections = self.selections.all::<Point>(cx);
6330 let mut selections = selections.iter().peekable();
6331 let mut contiguous_row_selections = Vec::new();
6332 let mut new_selections = Vec::new();
6333
6334 while let Some(selection) = selections.next() {
6335 // Find all the selections that span a contiguous row range
6336 let (start_row, end_row) = consume_contiguous_rows(
6337 &mut contiguous_row_selections,
6338 selection,
6339 &display_map,
6340 &mut selections,
6341 );
6342
6343 // Move the text spanned by the row range to be after the last line of the row range
6344 if end_row.0 <= buffer.max_point().row {
6345 let range_to_move =
6346 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
6347 let insertion_point = display_map
6348 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
6349 .0;
6350
6351 // Don't move lines across excerpt boundaries
6352 if buffer
6353 .excerpt_boundaries_in_range((
6354 Bound::Excluded(range_to_move.start),
6355 Bound::Included(insertion_point),
6356 ))
6357 .next()
6358 .is_none()
6359 {
6360 let mut text = String::from("\n");
6361 text.extend(buffer.text_for_range(range_to_move.clone()));
6362 text.pop(); // Drop trailing newline
6363 edits.push((
6364 buffer.anchor_after(range_to_move.start)
6365 ..buffer.anchor_before(range_to_move.end),
6366 String::new(),
6367 ));
6368 let insertion_anchor = buffer.anchor_after(insertion_point);
6369 edits.push((insertion_anchor..insertion_anchor, text));
6370
6371 let row_delta = insertion_point.row - range_to_move.end.row + 1;
6372
6373 // Move selections down
6374 new_selections.extend(contiguous_row_selections.drain(..).map(
6375 |mut selection| {
6376 selection.start.row += row_delta;
6377 selection.end.row += row_delta;
6378 selection
6379 },
6380 ));
6381
6382 // Move folds down
6383 unfold_ranges.push(range_to_move.clone());
6384 for fold in display_map.folds_in_range(
6385 buffer.anchor_before(range_to_move.start)
6386 ..buffer.anchor_after(range_to_move.end),
6387 ) {
6388 let mut start = fold.range.start.to_point(&buffer);
6389 let mut end = fold.range.end.to_point(&buffer);
6390 start.row += row_delta;
6391 end.row += row_delta;
6392 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
6393 }
6394 }
6395 }
6396
6397 // If we didn't move line(s), preserve the existing selections
6398 new_selections.append(&mut contiguous_row_selections);
6399 }
6400
6401 self.transact(cx, |this, cx| {
6402 this.unfold_ranges(&unfold_ranges, true, true, cx);
6403 this.buffer.update(cx, |buffer, cx| {
6404 for (range, text) in edits {
6405 buffer.edit([(range, text)], None, cx);
6406 }
6407 });
6408 this.fold_creases(refold_creases, true, cx);
6409 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
6410 });
6411 }
6412
6413 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
6414 let text_layout_details = &self.text_layout_details(cx);
6415 self.transact(cx, |this, cx| {
6416 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6417 let mut edits: Vec<(Range<usize>, String)> = Default::default();
6418 let line_mode = s.line_mode;
6419 s.move_with(|display_map, selection| {
6420 if !selection.is_empty() || line_mode {
6421 return;
6422 }
6423
6424 let mut head = selection.head();
6425 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
6426 if head.column() == display_map.line_len(head.row()) {
6427 transpose_offset = display_map
6428 .buffer_snapshot
6429 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6430 }
6431
6432 if transpose_offset == 0 {
6433 return;
6434 }
6435
6436 *head.column_mut() += 1;
6437 head = display_map.clip_point(head, Bias::Right);
6438 let goal = SelectionGoal::HorizontalPosition(
6439 display_map
6440 .x_for_display_point(head, text_layout_details)
6441 .into(),
6442 );
6443 selection.collapse_to(head, goal);
6444
6445 let transpose_start = display_map
6446 .buffer_snapshot
6447 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6448 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
6449 let transpose_end = display_map
6450 .buffer_snapshot
6451 .clip_offset(transpose_offset + 1, Bias::Right);
6452 if let Some(ch) =
6453 display_map.buffer_snapshot.chars_at(transpose_start).next()
6454 {
6455 edits.push((transpose_start..transpose_offset, String::new()));
6456 edits.push((transpose_end..transpose_end, ch.to_string()));
6457 }
6458 }
6459 });
6460 edits
6461 });
6462 this.buffer
6463 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
6464 let selections = this.selections.all::<usize>(cx);
6465 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6466 s.select(selections);
6467 });
6468 });
6469 }
6470
6471 pub fn rewrap(&mut self, _: &Rewrap, cx: &mut ViewContext<Self>) {
6472 self.rewrap_impl(IsVimMode::No, cx)
6473 }
6474
6475 pub fn rewrap_impl(&mut self, is_vim_mode: IsVimMode, cx: &mut ViewContext<Self>) {
6476 let buffer = self.buffer.read(cx).snapshot(cx);
6477 let selections = self.selections.all::<Point>(cx);
6478 let mut selections = selections.iter().peekable();
6479
6480 let mut edits = Vec::new();
6481 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
6482
6483 while let Some(selection) = selections.next() {
6484 let mut start_row = selection.start.row;
6485 let mut end_row = selection.end.row;
6486
6487 // Skip selections that overlap with a range that has already been rewrapped.
6488 let selection_range = start_row..end_row;
6489 if rewrapped_row_ranges
6490 .iter()
6491 .any(|range| range.overlaps(&selection_range))
6492 {
6493 continue;
6494 }
6495
6496 let mut should_rewrap = is_vim_mode == IsVimMode::Yes;
6497
6498 if let Some(language_scope) = buffer.language_scope_at(selection.head()) {
6499 match language_scope.language_name().0.as_ref() {
6500 "Markdown" | "Plain Text" => {
6501 should_rewrap = true;
6502 }
6503 _ => {}
6504 }
6505 }
6506
6507 let tab_size = buffer.settings_at(selection.head(), cx).tab_size;
6508
6509 // Since not all lines in the selection may be at the same indent
6510 // level, choose the indent size that is the most common between all
6511 // of the lines.
6512 //
6513 // If there is a tie, we use the deepest indent.
6514 let (indent_size, indent_end) = {
6515 let mut indent_size_occurrences = HashMap::default();
6516 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
6517
6518 for row in start_row..=end_row {
6519 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
6520 rows_by_indent_size.entry(indent).or_default().push(row);
6521 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
6522 }
6523
6524 let indent_size = indent_size_occurrences
6525 .into_iter()
6526 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
6527 .map(|(indent, _)| indent)
6528 .unwrap_or_default();
6529 let row = rows_by_indent_size[&indent_size][0];
6530 let indent_end = Point::new(row, indent_size.len);
6531
6532 (indent_size, indent_end)
6533 };
6534
6535 let mut line_prefix = indent_size.chars().collect::<String>();
6536
6537 if let Some(comment_prefix) =
6538 buffer
6539 .language_scope_at(selection.head())
6540 .and_then(|language| {
6541 language
6542 .line_comment_prefixes()
6543 .iter()
6544 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
6545 .cloned()
6546 })
6547 {
6548 line_prefix.push_str(&comment_prefix);
6549 should_rewrap = true;
6550 }
6551
6552 if !should_rewrap {
6553 continue;
6554 }
6555
6556 if selection.is_empty() {
6557 'expand_upwards: while start_row > 0 {
6558 let prev_row = start_row - 1;
6559 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
6560 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
6561 {
6562 start_row = prev_row;
6563 } else {
6564 break 'expand_upwards;
6565 }
6566 }
6567
6568 'expand_downwards: while end_row < buffer.max_point().row {
6569 let next_row = end_row + 1;
6570 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
6571 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
6572 {
6573 end_row = next_row;
6574 } else {
6575 break 'expand_downwards;
6576 }
6577 }
6578 }
6579
6580 let start = Point::new(start_row, 0);
6581 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
6582 let selection_text = buffer.text_for_range(start..end).collect::<String>();
6583 let Some(lines_without_prefixes) = selection_text
6584 .lines()
6585 .map(|line| {
6586 line.strip_prefix(&line_prefix)
6587 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
6588 .ok_or_else(|| {
6589 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
6590 })
6591 })
6592 .collect::<Result<Vec<_>, _>>()
6593 .log_err()
6594 else {
6595 continue;
6596 };
6597
6598 let wrap_column = buffer
6599 .settings_at(Point::new(start_row, 0), cx)
6600 .preferred_line_length as usize;
6601 let wrapped_text = wrap_with_prefix(
6602 line_prefix,
6603 lines_without_prefixes.join(" "),
6604 wrap_column,
6605 tab_size,
6606 );
6607
6608 // TODO: should always use char-based diff while still supporting cursor behavior that
6609 // matches vim.
6610 let diff = match is_vim_mode {
6611 IsVimMode::Yes => TextDiff::from_lines(&selection_text, &wrapped_text),
6612 IsVimMode::No => TextDiff::from_chars(&selection_text, &wrapped_text),
6613 };
6614 let mut offset = start.to_offset(&buffer);
6615 let mut moved_since_edit = true;
6616
6617 for change in diff.iter_all_changes() {
6618 let value = change.value();
6619 match change.tag() {
6620 ChangeTag::Equal => {
6621 offset += value.len();
6622 moved_since_edit = true;
6623 }
6624 ChangeTag::Delete => {
6625 let start = buffer.anchor_after(offset);
6626 let end = buffer.anchor_before(offset + value.len());
6627
6628 if moved_since_edit {
6629 edits.push((start..end, String::new()));
6630 } else {
6631 edits.last_mut().unwrap().0.end = end;
6632 }
6633
6634 offset += value.len();
6635 moved_since_edit = false;
6636 }
6637 ChangeTag::Insert => {
6638 if moved_since_edit {
6639 let anchor = buffer.anchor_after(offset);
6640 edits.push((anchor..anchor, value.to_string()));
6641 } else {
6642 edits.last_mut().unwrap().1.push_str(value);
6643 }
6644
6645 moved_since_edit = false;
6646 }
6647 }
6648 }
6649
6650 rewrapped_row_ranges.push(start_row..=end_row);
6651 }
6652
6653 self.buffer
6654 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
6655 }
6656
6657 pub fn cut_common(&mut self, cx: &mut ViewContext<Self>) -> ClipboardItem {
6658 let mut text = String::new();
6659 let buffer = self.buffer.read(cx).snapshot(cx);
6660 let mut selections = self.selections.all::<Point>(cx);
6661 let mut clipboard_selections = Vec::with_capacity(selections.len());
6662 {
6663 let max_point = buffer.max_point();
6664 let mut is_first = true;
6665 for selection in &mut selections {
6666 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6667 if is_entire_line {
6668 selection.start = Point::new(selection.start.row, 0);
6669 if !selection.is_empty() && selection.end.column == 0 {
6670 selection.end = cmp::min(max_point, selection.end);
6671 } else {
6672 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
6673 }
6674 selection.goal = SelectionGoal::None;
6675 }
6676 if is_first {
6677 is_first = false;
6678 } else {
6679 text += "\n";
6680 }
6681 let mut len = 0;
6682 for chunk in buffer.text_for_range(selection.start..selection.end) {
6683 text.push_str(chunk);
6684 len += chunk.len();
6685 }
6686 clipboard_selections.push(ClipboardSelection {
6687 len,
6688 is_entire_line,
6689 first_line_indent: buffer
6690 .indent_size_for_line(MultiBufferRow(selection.start.row))
6691 .len,
6692 });
6693 }
6694 }
6695
6696 self.transact(cx, |this, cx| {
6697 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6698 s.select(selections);
6699 });
6700 this.insert("", cx);
6701 });
6702 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
6703 }
6704
6705 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
6706 let item = self.cut_common(cx);
6707 cx.write_to_clipboard(item);
6708 }
6709
6710 pub fn kill_ring_cut(&mut self, _: &KillRingCut, cx: &mut ViewContext<Self>) {
6711 self.change_selections(None, cx, |s| {
6712 s.move_with(|snapshot, sel| {
6713 if sel.is_empty() {
6714 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
6715 }
6716 });
6717 });
6718 let item = self.cut_common(cx);
6719 cx.set_global(KillRing(item))
6720 }
6721
6722 pub fn kill_ring_yank(&mut self, _: &KillRingYank, cx: &mut ViewContext<Self>) {
6723 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
6724 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
6725 (kill_ring.text().to_string(), kill_ring.metadata_json())
6726 } else {
6727 return;
6728 }
6729 } else {
6730 return;
6731 };
6732 self.do_paste(&text, metadata, false, cx);
6733 }
6734
6735 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
6736 let selections = self.selections.all::<Point>(cx);
6737 let buffer = self.buffer.read(cx).read(cx);
6738 let mut text = String::new();
6739
6740 let mut clipboard_selections = Vec::with_capacity(selections.len());
6741 {
6742 let max_point = buffer.max_point();
6743 let mut is_first = true;
6744 for selection in selections.iter() {
6745 let mut start = selection.start;
6746 let mut end = selection.end;
6747 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6748 if is_entire_line {
6749 start = Point::new(start.row, 0);
6750 end = cmp::min(max_point, Point::new(end.row + 1, 0));
6751 }
6752 if is_first {
6753 is_first = false;
6754 } else {
6755 text += "\n";
6756 }
6757 let mut len = 0;
6758 for chunk in buffer.text_for_range(start..end) {
6759 text.push_str(chunk);
6760 len += chunk.len();
6761 }
6762 clipboard_selections.push(ClipboardSelection {
6763 len,
6764 is_entire_line,
6765 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
6766 });
6767 }
6768 }
6769
6770 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
6771 text,
6772 clipboard_selections,
6773 ));
6774 }
6775
6776 pub fn do_paste(
6777 &mut self,
6778 text: &String,
6779 clipboard_selections: Option<Vec<ClipboardSelection>>,
6780 handle_entire_lines: bool,
6781 cx: &mut ViewContext<Self>,
6782 ) {
6783 if self.read_only(cx) {
6784 return;
6785 }
6786
6787 let clipboard_text = Cow::Borrowed(text);
6788
6789 self.transact(cx, |this, cx| {
6790 if let Some(mut clipboard_selections) = clipboard_selections {
6791 let old_selections = this.selections.all::<usize>(cx);
6792 let all_selections_were_entire_line =
6793 clipboard_selections.iter().all(|s| s.is_entire_line);
6794 let first_selection_indent_column =
6795 clipboard_selections.first().map(|s| s.first_line_indent);
6796 if clipboard_selections.len() != old_selections.len() {
6797 clipboard_selections.drain(..);
6798 }
6799 let cursor_offset = this.selections.last::<usize>(cx).head();
6800 let mut auto_indent_on_paste = true;
6801
6802 this.buffer.update(cx, |buffer, cx| {
6803 let snapshot = buffer.read(cx);
6804 auto_indent_on_paste =
6805 snapshot.settings_at(cursor_offset, cx).auto_indent_on_paste;
6806
6807 let mut start_offset = 0;
6808 let mut edits = Vec::new();
6809 let mut original_indent_columns = Vec::new();
6810 for (ix, selection) in old_selections.iter().enumerate() {
6811 let to_insert;
6812 let entire_line;
6813 let original_indent_column;
6814 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
6815 let end_offset = start_offset + clipboard_selection.len;
6816 to_insert = &clipboard_text[start_offset..end_offset];
6817 entire_line = clipboard_selection.is_entire_line;
6818 start_offset = end_offset + 1;
6819 original_indent_column = Some(clipboard_selection.first_line_indent);
6820 } else {
6821 to_insert = clipboard_text.as_str();
6822 entire_line = all_selections_were_entire_line;
6823 original_indent_column = first_selection_indent_column
6824 }
6825
6826 // If the corresponding selection was empty when this slice of the
6827 // clipboard text was written, then the entire line containing the
6828 // selection was copied. If this selection is also currently empty,
6829 // then paste the line before the current line of the buffer.
6830 let range = if selection.is_empty() && handle_entire_lines && entire_line {
6831 let column = selection.start.to_point(&snapshot).column as usize;
6832 let line_start = selection.start - column;
6833 line_start..line_start
6834 } else {
6835 selection.range()
6836 };
6837
6838 edits.push((range, to_insert));
6839 original_indent_columns.extend(original_indent_column);
6840 }
6841 drop(snapshot);
6842
6843 buffer.edit(
6844 edits,
6845 if auto_indent_on_paste {
6846 Some(AutoindentMode::Block {
6847 original_indent_columns,
6848 })
6849 } else {
6850 None
6851 },
6852 cx,
6853 );
6854 });
6855
6856 let selections = this.selections.all::<usize>(cx);
6857 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6858 } else {
6859 this.insert(&clipboard_text, cx);
6860 }
6861 });
6862 }
6863
6864 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
6865 if let Some(item) = cx.read_from_clipboard() {
6866 let entries = item.entries();
6867
6868 match entries.first() {
6869 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
6870 // of all the pasted entries.
6871 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
6872 .do_paste(
6873 clipboard_string.text(),
6874 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
6875 true,
6876 cx,
6877 ),
6878 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, cx),
6879 }
6880 }
6881 }
6882
6883 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
6884 if self.read_only(cx) {
6885 return;
6886 }
6887
6888 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
6889 if let Some((selections, _)) =
6890 self.selection_history.transaction(transaction_id).cloned()
6891 {
6892 self.change_selections(None, cx, |s| {
6893 s.select_anchors(selections.to_vec());
6894 });
6895 }
6896 self.request_autoscroll(Autoscroll::fit(), cx);
6897 self.unmark_text(cx);
6898 self.refresh_inline_completion(true, false, cx);
6899 cx.emit(EditorEvent::Edited { transaction_id });
6900 cx.emit(EditorEvent::TransactionUndone { transaction_id });
6901 }
6902 }
6903
6904 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
6905 if self.read_only(cx) {
6906 return;
6907 }
6908
6909 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
6910 if let Some((_, Some(selections))) =
6911 self.selection_history.transaction(transaction_id).cloned()
6912 {
6913 self.change_selections(None, cx, |s| {
6914 s.select_anchors(selections.to_vec());
6915 });
6916 }
6917 self.request_autoscroll(Autoscroll::fit(), cx);
6918 self.unmark_text(cx);
6919 self.refresh_inline_completion(true, false, cx);
6920 cx.emit(EditorEvent::Edited { transaction_id });
6921 }
6922 }
6923
6924 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
6925 self.buffer
6926 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
6927 }
6928
6929 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut ViewContext<Self>) {
6930 self.buffer
6931 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
6932 }
6933
6934 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
6935 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6936 let line_mode = s.line_mode;
6937 s.move_with(|map, selection| {
6938 let cursor = if selection.is_empty() && !line_mode {
6939 movement::left(map, selection.start)
6940 } else {
6941 selection.start
6942 };
6943 selection.collapse_to(cursor, SelectionGoal::None);
6944 });
6945 })
6946 }
6947
6948 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
6949 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6950 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
6951 })
6952 }
6953
6954 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
6955 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6956 let line_mode = s.line_mode;
6957 s.move_with(|map, selection| {
6958 let cursor = if selection.is_empty() && !line_mode {
6959 movement::right(map, selection.end)
6960 } else {
6961 selection.end
6962 };
6963 selection.collapse_to(cursor, SelectionGoal::None)
6964 });
6965 })
6966 }
6967
6968 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
6969 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6970 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
6971 })
6972 }
6973
6974 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
6975 if self.take_rename(true, cx).is_some() {
6976 return;
6977 }
6978
6979 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6980 cx.propagate();
6981 return;
6982 }
6983
6984 let text_layout_details = &self.text_layout_details(cx);
6985 let selection_count = self.selections.count();
6986 let first_selection = self.selections.first_anchor();
6987
6988 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6989 let line_mode = s.line_mode;
6990 s.move_with(|map, selection| {
6991 if !selection.is_empty() && !line_mode {
6992 selection.goal = SelectionGoal::None;
6993 }
6994 let (cursor, goal) = movement::up(
6995 map,
6996 selection.start,
6997 selection.goal,
6998 false,
6999 text_layout_details,
7000 );
7001 selection.collapse_to(cursor, goal);
7002 });
7003 });
7004
7005 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7006 {
7007 cx.propagate();
7008 }
7009 }
7010
7011 pub fn move_up_by_lines(&mut self, action: &MoveUpByLines, cx: &mut ViewContext<Self>) {
7012 if self.take_rename(true, cx).is_some() {
7013 return;
7014 }
7015
7016 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7017 cx.propagate();
7018 return;
7019 }
7020
7021 let text_layout_details = &self.text_layout_details(cx);
7022
7023 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7024 let line_mode = s.line_mode;
7025 s.move_with(|map, selection| {
7026 if !selection.is_empty() && !line_mode {
7027 selection.goal = SelectionGoal::None;
7028 }
7029 let (cursor, goal) = movement::up_by_rows(
7030 map,
7031 selection.start,
7032 action.lines,
7033 selection.goal,
7034 false,
7035 text_layout_details,
7036 );
7037 selection.collapse_to(cursor, goal);
7038 });
7039 })
7040 }
7041
7042 pub fn move_down_by_lines(&mut self, action: &MoveDownByLines, cx: &mut ViewContext<Self>) {
7043 if self.take_rename(true, cx).is_some() {
7044 return;
7045 }
7046
7047 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7048 cx.propagate();
7049 return;
7050 }
7051
7052 let text_layout_details = &self.text_layout_details(cx);
7053
7054 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7055 let line_mode = s.line_mode;
7056 s.move_with(|map, selection| {
7057 if !selection.is_empty() && !line_mode {
7058 selection.goal = SelectionGoal::None;
7059 }
7060 let (cursor, goal) = movement::down_by_rows(
7061 map,
7062 selection.start,
7063 action.lines,
7064 selection.goal,
7065 false,
7066 text_layout_details,
7067 );
7068 selection.collapse_to(cursor, goal);
7069 });
7070 })
7071 }
7072
7073 pub fn select_down_by_lines(&mut self, action: &SelectDownByLines, cx: &mut ViewContext<Self>) {
7074 let text_layout_details = &self.text_layout_details(cx);
7075 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7076 s.move_heads_with(|map, head, goal| {
7077 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
7078 })
7079 })
7080 }
7081
7082 pub fn select_up_by_lines(&mut self, action: &SelectUpByLines, cx: &mut ViewContext<Self>) {
7083 let text_layout_details = &self.text_layout_details(cx);
7084 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7085 s.move_heads_with(|map, head, goal| {
7086 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
7087 })
7088 })
7089 }
7090
7091 pub fn select_page_up(&mut self, _: &SelectPageUp, cx: &mut ViewContext<Self>) {
7092 let Some(row_count) = self.visible_row_count() else {
7093 return;
7094 };
7095
7096 let text_layout_details = &self.text_layout_details(cx);
7097
7098 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7099 s.move_heads_with(|map, head, goal| {
7100 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
7101 })
7102 })
7103 }
7104
7105 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
7106 if self.take_rename(true, cx).is_some() {
7107 return;
7108 }
7109
7110 if self
7111 .context_menu
7112 .write()
7113 .as_mut()
7114 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
7115 .unwrap_or(false)
7116 {
7117 return;
7118 }
7119
7120 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7121 cx.propagate();
7122 return;
7123 }
7124
7125 let Some(row_count) = self.visible_row_count() else {
7126 return;
7127 };
7128
7129 let autoscroll = if action.center_cursor {
7130 Autoscroll::center()
7131 } else {
7132 Autoscroll::fit()
7133 };
7134
7135 let text_layout_details = &self.text_layout_details(cx);
7136
7137 self.change_selections(Some(autoscroll), cx, |s| {
7138 let line_mode = s.line_mode;
7139 s.move_with(|map, selection| {
7140 if !selection.is_empty() && !line_mode {
7141 selection.goal = SelectionGoal::None;
7142 }
7143 let (cursor, goal) = movement::up_by_rows(
7144 map,
7145 selection.end,
7146 row_count,
7147 selection.goal,
7148 false,
7149 text_layout_details,
7150 );
7151 selection.collapse_to(cursor, goal);
7152 });
7153 });
7154 }
7155
7156 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
7157 let text_layout_details = &self.text_layout_details(cx);
7158 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7159 s.move_heads_with(|map, head, goal| {
7160 movement::up(map, head, goal, false, text_layout_details)
7161 })
7162 })
7163 }
7164
7165 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
7166 self.take_rename(true, cx);
7167
7168 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7169 cx.propagate();
7170 return;
7171 }
7172
7173 let text_layout_details = &self.text_layout_details(cx);
7174 let selection_count = self.selections.count();
7175 let first_selection = self.selections.first_anchor();
7176
7177 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7178 let line_mode = s.line_mode;
7179 s.move_with(|map, selection| {
7180 if !selection.is_empty() && !line_mode {
7181 selection.goal = SelectionGoal::None;
7182 }
7183 let (cursor, goal) = movement::down(
7184 map,
7185 selection.end,
7186 selection.goal,
7187 false,
7188 text_layout_details,
7189 );
7190 selection.collapse_to(cursor, goal);
7191 });
7192 });
7193
7194 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7195 {
7196 cx.propagate();
7197 }
7198 }
7199
7200 pub fn select_page_down(&mut self, _: &SelectPageDown, cx: &mut ViewContext<Self>) {
7201 let Some(row_count) = self.visible_row_count() else {
7202 return;
7203 };
7204
7205 let text_layout_details = &self.text_layout_details(cx);
7206
7207 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7208 s.move_heads_with(|map, head, goal| {
7209 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
7210 })
7211 })
7212 }
7213
7214 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
7215 if self.take_rename(true, cx).is_some() {
7216 return;
7217 }
7218
7219 if self
7220 .context_menu
7221 .write()
7222 .as_mut()
7223 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
7224 .unwrap_or(false)
7225 {
7226 return;
7227 }
7228
7229 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7230 cx.propagate();
7231 return;
7232 }
7233
7234 let Some(row_count) = self.visible_row_count() else {
7235 return;
7236 };
7237
7238 let autoscroll = if action.center_cursor {
7239 Autoscroll::center()
7240 } else {
7241 Autoscroll::fit()
7242 };
7243
7244 let text_layout_details = &self.text_layout_details(cx);
7245 self.change_selections(Some(autoscroll), cx, |s| {
7246 let line_mode = s.line_mode;
7247 s.move_with(|map, selection| {
7248 if !selection.is_empty() && !line_mode {
7249 selection.goal = SelectionGoal::None;
7250 }
7251 let (cursor, goal) = movement::down_by_rows(
7252 map,
7253 selection.end,
7254 row_count,
7255 selection.goal,
7256 false,
7257 text_layout_details,
7258 );
7259 selection.collapse_to(cursor, goal);
7260 });
7261 });
7262 }
7263
7264 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
7265 let text_layout_details = &self.text_layout_details(cx);
7266 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7267 s.move_heads_with(|map, head, goal| {
7268 movement::down(map, head, goal, false, text_layout_details)
7269 })
7270 });
7271 }
7272
7273 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
7274 if let Some(context_menu) = self.context_menu.write().as_mut() {
7275 context_menu.select_first(self.completion_provider.as_deref(), cx);
7276 }
7277 }
7278
7279 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
7280 if let Some(context_menu) = self.context_menu.write().as_mut() {
7281 context_menu.select_prev(self.completion_provider.as_deref(), cx);
7282 }
7283 }
7284
7285 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
7286 if let Some(context_menu) = self.context_menu.write().as_mut() {
7287 context_menu.select_next(self.completion_provider.as_deref(), cx);
7288 }
7289 }
7290
7291 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
7292 if let Some(context_menu) = self.context_menu.write().as_mut() {
7293 context_menu.select_last(self.completion_provider.as_deref(), cx);
7294 }
7295 }
7296
7297 pub fn move_to_previous_word_start(
7298 &mut self,
7299 _: &MoveToPreviousWordStart,
7300 cx: &mut ViewContext<Self>,
7301 ) {
7302 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7303 s.move_cursors_with(|map, head, _| {
7304 (
7305 movement::previous_word_start(map, head),
7306 SelectionGoal::None,
7307 )
7308 });
7309 })
7310 }
7311
7312 pub fn move_to_previous_subword_start(
7313 &mut self,
7314 _: &MoveToPreviousSubwordStart,
7315 cx: &mut ViewContext<Self>,
7316 ) {
7317 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7318 s.move_cursors_with(|map, head, _| {
7319 (
7320 movement::previous_subword_start(map, head),
7321 SelectionGoal::None,
7322 )
7323 });
7324 })
7325 }
7326
7327 pub fn select_to_previous_word_start(
7328 &mut self,
7329 _: &SelectToPreviousWordStart,
7330 cx: &mut ViewContext<Self>,
7331 ) {
7332 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7333 s.move_heads_with(|map, head, _| {
7334 (
7335 movement::previous_word_start(map, head),
7336 SelectionGoal::None,
7337 )
7338 });
7339 })
7340 }
7341
7342 pub fn select_to_previous_subword_start(
7343 &mut self,
7344 _: &SelectToPreviousSubwordStart,
7345 cx: &mut ViewContext<Self>,
7346 ) {
7347 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7348 s.move_heads_with(|map, head, _| {
7349 (
7350 movement::previous_subword_start(map, head),
7351 SelectionGoal::None,
7352 )
7353 });
7354 })
7355 }
7356
7357 pub fn delete_to_previous_word_start(
7358 &mut self,
7359 action: &DeleteToPreviousWordStart,
7360 cx: &mut ViewContext<Self>,
7361 ) {
7362 self.transact(cx, |this, cx| {
7363 this.select_autoclose_pair(cx);
7364 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7365 let line_mode = s.line_mode;
7366 s.move_with(|map, selection| {
7367 if selection.is_empty() && !line_mode {
7368 let cursor = if action.ignore_newlines {
7369 movement::previous_word_start(map, selection.head())
7370 } else {
7371 movement::previous_word_start_or_newline(map, selection.head())
7372 };
7373 selection.set_head(cursor, SelectionGoal::None);
7374 }
7375 });
7376 });
7377 this.insert("", cx);
7378 });
7379 }
7380
7381 pub fn delete_to_previous_subword_start(
7382 &mut self,
7383 _: &DeleteToPreviousSubwordStart,
7384 cx: &mut ViewContext<Self>,
7385 ) {
7386 self.transact(cx, |this, cx| {
7387 this.select_autoclose_pair(cx);
7388 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7389 let line_mode = s.line_mode;
7390 s.move_with(|map, selection| {
7391 if selection.is_empty() && !line_mode {
7392 let cursor = movement::previous_subword_start(map, selection.head());
7393 selection.set_head(cursor, SelectionGoal::None);
7394 }
7395 });
7396 });
7397 this.insert("", cx);
7398 });
7399 }
7400
7401 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
7402 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7403 s.move_cursors_with(|map, head, _| {
7404 (movement::next_word_end(map, head), SelectionGoal::None)
7405 });
7406 })
7407 }
7408
7409 pub fn move_to_next_subword_end(
7410 &mut self,
7411 _: &MoveToNextSubwordEnd,
7412 cx: &mut ViewContext<Self>,
7413 ) {
7414 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7415 s.move_cursors_with(|map, head, _| {
7416 (movement::next_subword_end(map, head), SelectionGoal::None)
7417 });
7418 })
7419 }
7420
7421 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
7422 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7423 s.move_heads_with(|map, head, _| {
7424 (movement::next_word_end(map, head), SelectionGoal::None)
7425 });
7426 })
7427 }
7428
7429 pub fn select_to_next_subword_end(
7430 &mut self,
7431 _: &SelectToNextSubwordEnd,
7432 cx: &mut ViewContext<Self>,
7433 ) {
7434 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7435 s.move_heads_with(|map, head, _| {
7436 (movement::next_subword_end(map, head), SelectionGoal::None)
7437 });
7438 })
7439 }
7440
7441 pub fn delete_to_next_word_end(
7442 &mut self,
7443 action: &DeleteToNextWordEnd,
7444 cx: &mut ViewContext<Self>,
7445 ) {
7446 self.transact(cx, |this, cx| {
7447 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7448 let line_mode = s.line_mode;
7449 s.move_with(|map, selection| {
7450 if selection.is_empty() && !line_mode {
7451 let cursor = if action.ignore_newlines {
7452 movement::next_word_end(map, selection.head())
7453 } else {
7454 movement::next_word_end_or_newline(map, selection.head())
7455 };
7456 selection.set_head(cursor, SelectionGoal::None);
7457 }
7458 });
7459 });
7460 this.insert("", cx);
7461 });
7462 }
7463
7464 pub fn delete_to_next_subword_end(
7465 &mut self,
7466 _: &DeleteToNextSubwordEnd,
7467 cx: &mut ViewContext<Self>,
7468 ) {
7469 self.transact(cx, |this, cx| {
7470 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7471 s.move_with(|map, selection| {
7472 if selection.is_empty() {
7473 let cursor = movement::next_subword_end(map, selection.head());
7474 selection.set_head(cursor, SelectionGoal::None);
7475 }
7476 });
7477 });
7478 this.insert("", cx);
7479 });
7480 }
7481
7482 pub fn move_to_beginning_of_line(
7483 &mut self,
7484 action: &MoveToBeginningOfLine,
7485 cx: &mut ViewContext<Self>,
7486 ) {
7487 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7488 s.move_cursors_with(|map, head, _| {
7489 (
7490 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7491 SelectionGoal::None,
7492 )
7493 });
7494 })
7495 }
7496
7497 pub fn select_to_beginning_of_line(
7498 &mut self,
7499 action: &SelectToBeginningOfLine,
7500 cx: &mut ViewContext<Self>,
7501 ) {
7502 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7503 s.move_heads_with(|map, head, _| {
7504 (
7505 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7506 SelectionGoal::None,
7507 )
7508 });
7509 });
7510 }
7511
7512 pub fn delete_to_beginning_of_line(
7513 &mut self,
7514 _: &DeleteToBeginningOfLine,
7515 cx: &mut ViewContext<Self>,
7516 ) {
7517 self.transact(cx, |this, cx| {
7518 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7519 s.move_with(|_, selection| {
7520 selection.reversed = true;
7521 });
7522 });
7523
7524 this.select_to_beginning_of_line(
7525 &SelectToBeginningOfLine {
7526 stop_at_soft_wraps: false,
7527 },
7528 cx,
7529 );
7530 this.backspace(&Backspace, cx);
7531 });
7532 }
7533
7534 pub fn move_to_end_of_line(&mut self, action: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
7535 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7536 s.move_cursors_with(|map, head, _| {
7537 (
7538 movement::line_end(map, head, action.stop_at_soft_wraps),
7539 SelectionGoal::None,
7540 )
7541 });
7542 })
7543 }
7544
7545 pub fn select_to_end_of_line(
7546 &mut self,
7547 action: &SelectToEndOfLine,
7548 cx: &mut ViewContext<Self>,
7549 ) {
7550 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7551 s.move_heads_with(|map, head, _| {
7552 (
7553 movement::line_end(map, head, action.stop_at_soft_wraps),
7554 SelectionGoal::None,
7555 )
7556 });
7557 })
7558 }
7559
7560 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
7561 self.transact(cx, |this, cx| {
7562 this.select_to_end_of_line(
7563 &SelectToEndOfLine {
7564 stop_at_soft_wraps: false,
7565 },
7566 cx,
7567 );
7568 this.delete(&Delete, cx);
7569 });
7570 }
7571
7572 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
7573 self.transact(cx, |this, cx| {
7574 this.select_to_end_of_line(
7575 &SelectToEndOfLine {
7576 stop_at_soft_wraps: false,
7577 },
7578 cx,
7579 );
7580 this.cut(&Cut, cx);
7581 });
7582 }
7583
7584 pub fn move_to_start_of_paragraph(
7585 &mut self,
7586 _: &MoveToStartOfParagraph,
7587 cx: &mut ViewContext<Self>,
7588 ) {
7589 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7590 cx.propagate();
7591 return;
7592 }
7593
7594 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7595 s.move_with(|map, selection| {
7596 selection.collapse_to(
7597 movement::start_of_paragraph(map, selection.head(), 1),
7598 SelectionGoal::None,
7599 )
7600 });
7601 })
7602 }
7603
7604 pub fn move_to_end_of_paragraph(
7605 &mut self,
7606 _: &MoveToEndOfParagraph,
7607 cx: &mut ViewContext<Self>,
7608 ) {
7609 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7610 cx.propagate();
7611 return;
7612 }
7613
7614 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7615 s.move_with(|map, selection| {
7616 selection.collapse_to(
7617 movement::end_of_paragraph(map, selection.head(), 1),
7618 SelectionGoal::None,
7619 )
7620 });
7621 })
7622 }
7623
7624 pub fn select_to_start_of_paragraph(
7625 &mut self,
7626 _: &SelectToStartOfParagraph,
7627 cx: &mut ViewContext<Self>,
7628 ) {
7629 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7630 cx.propagate();
7631 return;
7632 }
7633
7634 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7635 s.move_heads_with(|map, head, _| {
7636 (
7637 movement::start_of_paragraph(map, head, 1),
7638 SelectionGoal::None,
7639 )
7640 });
7641 })
7642 }
7643
7644 pub fn select_to_end_of_paragraph(
7645 &mut self,
7646 _: &SelectToEndOfParagraph,
7647 cx: &mut ViewContext<Self>,
7648 ) {
7649 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7650 cx.propagate();
7651 return;
7652 }
7653
7654 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7655 s.move_heads_with(|map, head, _| {
7656 (
7657 movement::end_of_paragraph(map, head, 1),
7658 SelectionGoal::None,
7659 )
7660 });
7661 })
7662 }
7663
7664 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
7665 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7666 cx.propagate();
7667 return;
7668 }
7669
7670 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7671 s.select_ranges(vec![0..0]);
7672 });
7673 }
7674
7675 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
7676 let mut selection = self.selections.last::<Point>(cx);
7677 selection.set_head(Point::zero(), SelectionGoal::None);
7678
7679 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7680 s.select(vec![selection]);
7681 });
7682 }
7683
7684 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
7685 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7686 cx.propagate();
7687 return;
7688 }
7689
7690 let cursor = self.buffer.read(cx).read(cx).len();
7691 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7692 s.select_ranges(vec![cursor..cursor])
7693 });
7694 }
7695
7696 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
7697 self.nav_history = nav_history;
7698 }
7699
7700 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
7701 self.nav_history.as_ref()
7702 }
7703
7704 fn push_to_nav_history(
7705 &mut self,
7706 cursor_anchor: Anchor,
7707 new_position: Option<Point>,
7708 cx: &mut ViewContext<Self>,
7709 ) {
7710 if let Some(nav_history) = self.nav_history.as_mut() {
7711 let buffer = self.buffer.read(cx).read(cx);
7712 let cursor_position = cursor_anchor.to_point(&buffer);
7713 let scroll_state = self.scroll_manager.anchor();
7714 let scroll_top_row = scroll_state.top_row(&buffer);
7715 drop(buffer);
7716
7717 if let Some(new_position) = new_position {
7718 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
7719 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
7720 return;
7721 }
7722 }
7723
7724 nav_history.push(
7725 Some(NavigationData {
7726 cursor_anchor,
7727 cursor_position,
7728 scroll_anchor: scroll_state,
7729 scroll_top_row,
7730 }),
7731 cx,
7732 );
7733 }
7734 }
7735
7736 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
7737 let buffer = self.buffer.read(cx).snapshot(cx);
7738 let mut selection = self.selections.first::<usize>(cx);
7739 selection.set_head(buffer.len(), SelectionGoal::None);
7740 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7741 s.select(vec![selection]);
7742 });
7743 }
7744
7745 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
7746 let end = self.buffer.read(cx).read(cx).len();
7747 self.change_selections(None, cx, |s| {
7748 s.select_ranges(vec![0..end]);
7749 });
7750 }
7751
7752 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
7753 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7754 let mut selections = self.selections.all::<Point>(cx);
7755 let max_point = display_map.buffer_snapshot.max_point();
7756 for selection in &mut selections {
7757 let rows = selection.spanned_rows(true, &display_map);
7758 selection.start = Point::new(rows.start.0, 0);
7759 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
7760 selection.reversed = false;
7761 }
7762 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7763 s.select(selections);
7764 });
7765 }
7766
7767 pub fn split_selection_into_lines(
7768 &mut self,
7769 _: &SplitSelectionIntoLines,
7770 cx: &mut ViewContext<Self>,
7771 ) {
7772 let mut to_unfold = Vec::new();
7773 let mut new_selection_ranges = Vec::new();
7774 {
7775 let selections = self.selections.all::<Point>(cx);
7776 let buffer = self.buffer.read(cx).read(cx);
7777 for selection in selections {
7778 for row in selection.start.row..selection.end.row {
7779 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
7780 new_selection_ranges.push(cursor..cursor);
7781 }
7782 new_selection_ranges.push(selection.end..selection.end);
7783 to_unfold.push(selection.start..selection.end);
7784 }
7785 }
7786 self.unfold_ranges(&to_unfold, true, true, cx);
7787 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7788 s.select_ranges(new_selection_ranges);
7789 });
7790 }
7791
7792 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
7793 self.add_selection(true, cx);
7794 }
7795
7796 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
7797 self.add_selection(false, cx);
7798 }
7799
7800 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
7801 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7802 let mut selections = self.selections.all::<Point>(cx);
7803 let text_layout_details = self.text_layout_details(cx);
7804 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
7805 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
7806 let range = oldest_selection.display_range(&display_map).sorted();
7807
7808 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
7809 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
7810 let positions = start_x.min(end_x)..start_x.max(end_x);
7811
7812 selections.clear();
7813 let mut stack = Vec::new();
7814 for row in range.start.row().0..=range.end.row().0 {
7815 if let Some(selection) = self.selections.build_columnar_selection(
7816 &display_map,
7817 DisplayRow(row),
7818 &positions,
7819 oldest_selection.reversed,
7820 &text_layout_details,
7821 ) {
7822 stack.push(selection.id);
7823 selections.push(selection);
7824 }
7825 }
7826
7827 if above {
7828 stack.reverse();
7829 }
7830
7831 AddSelectionsState { above, stack }
7832 });
7833
7834 let last_added_selection = *state.stack.last().unwrap();
7835 let mut new_selections = Vec::new();
7836 if above == state.above {
7837 let end_row = if above {
7838 DisplayRow(0)
7839 } else {
7840 display_map.max_point().row()
7841 };
7842
7843 'outer: for selection in selections {
7844 if selection.id == last_added_selection {
7845 let range = selection.display_range(&display_map).sorted();
7846 debug_assert_eq!(range.start.row(), range.end.row());
7847 let mut row = range.start.row();
7848 let positions =
7849 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
7850 px(start)..px(end)
7851 } else {
7852 let start_x =
7853 display_map.x_for_display_point(range.start, &text_layout_details);
7854 let end_x =
7855 display_map.x_for_display_point(range.end, &text_layout_details);
7856 start_x.min(end_x)..start_x.max(end_x)
7857 };
7858
7859 while row != end_row {
7860 if above {
7861 row.0 -= 1;
7862 } else {
7863 row.0 += 1;
7864 }
7865
7866 if let Some(new_selection) = self.selections.build_columnar_selection(
7867 &display_map,
7868 row,
7869 &positions,
7870 selection.reversed,
7871 &text_layout_details,
7872 ) {
7873 state.stack.push(new_selection.id);
7874 if above {
7875 new_selections.push(new_selection);
7876 new_selections.push(selection);
7877 } else {
7878 new_selections.push(selection);
7879 new_selections.push(new_selection);
7880 }
7881
7882 continue 'outer;
7883 }
7884 }
7885 }
7886
7887 new_selections.push(selection);
7888 }
7889 } else {
7890 new_selections = selections;
7891 new_selections.retain(|s| s.id != last_added_selection);
7892 state.stack.pop();
7893 }
7894
7895 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7896 s.select(new_selections);
7897 });
7898 if state.stack.len() > 1 {
7899 self.add_selections_state = Some(state);
7900 }
7901 }
7902
7903 pub fn select_next_match_internal(
7904 &mut self,
7905 display_map: &DisplaySnapshot,
7906 replace_newest: bool,
7907 autoscroll: Option<Autoscroll>,
7908 cx: &mut ViewContext<Self>,
7909 ) -> Result<()> {
7910 fn select_next_match_ranges(
7911 this: &mut Editor,
7912 range: Range<usize>,
7913 replace_newest: bool,
7914 auto_scroll: Option<Autoscroll>,
7915 cx: &mut ViewContext<Editor>,
7916 ) {
7917 this.unfold_ranges(&[range.clone()], false, true, cx);
7918 this.change_selections(auto_scroll, cx, |s| {
7919 if replace_newest {
7920 s.delete(s.newest_anchor().id);
7921 }
7922 s.insert_range(range.clone());
7923 });
7924 }
7925
7926 let buffer = &display_map.buffer_snapshot;
7927 let mut selections = self.selections.all::<usize>(cx);
7928 if let Some(mut select_next_state) = self.select_next_state.take() {
7929 let query = &select_next_state.query;
7930 if !select_next_state.done {
7931 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
7932 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
7933 let mut next_selected_range = None;
7934
7935 let bytes_after_last_selection =
7936 buffer.bytes_in_range(last_selection.end..buffer.len());
7937 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
7938 let query_matches = query
7939 .stream_find_iter(bytes_after_last_selection)
7940 .map(|result| (last_selection.end, result))
7941 .chain(
7942 query
7943 .stream_find_iter(bytes_before_first_selection)
7944 .map(|result| (0, result)),
7945 );
7946
7947 for (start_offset, query_match) in query_matches {
7948 let query_match = query_match.unwrap(); // can only fail due to I/O
7949 let offset_range =
7950 start_offset + query_match.start()..start_offset + query_match.end();
7951 let display_range = offset_range.start.to_display_point(display_map)
7952 ..offset_range.end.to_display_point(display_map);
7953
7954 if !select_next_state.wordwise
7955 || (!movement::is_inside_word(display_map, display_range.start)
7956 && !movement::is_inside_word(display_map, display_range.end))
7957 {
7958 // TODO: This is n^2, because we might check all the selections
7959 if !selections
7960 .iter()
7961 .any(|selection| selection.range().overlaps(&offset_range))
7962 {
7963 next_selected_range = Some(offset_range);
7964 break;
7965 }
7966 }
7967 }
7968
7969 if let Some(next_selected_range) = next_selected_range {
7970 select_next_match_ranges(
7971 self,
7972 next_selected_range,
7973 replace_newest,
7974 autoscroll,
7975 cx,
7976 );
7977 } else {
7978 select_next_state.done = true;
7979 }
7980 }
7981
7982 self.select_next_state = Some(select_next_state);
7983 } else {
7984 let mut only_carets = true;
7985 let mut same_text_selected = true;
7986 let mut selected_text = None;
7987
7988 let mut selections_iter = selections.iter().peekable();
7989 while let Some(selection) = selections_iter.next() {
7990 if selection.start != selection.end {
7991 only_carets = false;
7992 }
7993
7994 if same_text_selected {
7995 if selected_text.is_none() {
7996 selected_text =
7997 Some(buffer.text_for_range(selection.range()).collect::<String>());
7998 }
7999
8000 if let Some(next_selection) = selections_iter.peek() {
8001 if next_selection.range().len() == selection.range().len() {
8002 let next_selected_text = buffer
8003 .text_for_range(next_selection.range())
8004 .collect::<String>();
8005 if Some(next_selected_text) != selected_text {
8006 same_text_selected = false;
8007 selected_text = None;
8008 }
8009 } else {
8010 same_text_selected = false;
8011 selected_text = None;
8012 }
8013 }
8014 }
8015 }
8016
8017 if only_carets {
8018 for selection in &mut selections {
8019 let word_range = movement::surrounding_word(
8020 display_map,
8021 selection.start.to_display_point(display_map),
8022 );
8023 selection.start = word_range.start.to_offset(display_map, Bias::Left);
8024 selection.end = word_range.end.to_offset(display_map, Bias::Left);
8025 selection.goal = SelectionGoal::None;
8026 selection.reversed = false;
8027 select_next_match_ranges(
8028 self,
8029 selection.start..selection.end,
8030 replace_newest,
8031 autoscroll,
8032 cx,
8033 );
8034 }
8035
8036 if selections.len() == 1 {
8037 let selection = selections
8038 .last()
8039 .expect("ensured that there's only one selection");
8040 let query = buffer
8041 .text_for_range(selection.start..selection.end)
8042 .collect::<String>();
8043 let is_empty = query.is_empty();
8044 let select_state = SelectNextState {
8045 query: AhoCorasick::new(&[query])?,
8046 wordwise: true,
8047 done: is_empty,
8048 };
8049 self.select_next_state = Some(select_state);
8050 } else {
8051 self.select_next_state = None;
8052 }
8053 } else if let Some(selected_text) = selected_text {
8054 self.select_next_state = Some(SelectNextState {
8055 query: AhoCorasick::new(&[selected_text])?,
8056 wordwise: false,
8057 done: false,
8058 });
8059 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
8060 }
8061 }
8062 Ok(())
8063 }
8064
8065 pub fn select_all_matches(
8066 &mut self,
8067 _action: &SelectAllMatches,
8068 cx: &mut ViewContext<Self>,
8069 ) -> Result<()> {
8070 self.push_to_selection_history();
8071 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8072
8073 self.select_next_match_internal(&display_map, false, None, cx)?;
8074 let Some(select_next_state) = self.select_next_state.as_mut() else {
8075 return Ok(());
8076 };
8077 if select_next_state.done {
8078 return Ok(());
8079 }
8080
8081 let mut new_selections = self.selections.all::<usize>(cx);
8082
8083 let buffer = &display_map.buffer_snapshot;
8084 let query_matches = select_next_state
8085 .query
8086 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
8087
8088 for query_match in query_matches {
8089 let query_match = query_match.unwrap(); // can only fail due to I/O
8090 let offset_range = query_match.start()..query_match.end();
8091 let display_range = offset_range.start.to_display_point(&display_map)
8092 ..offset_range.end.to_display_point(&display_map);
8093
8094 if !select_next_state.wordwise
8095 || (!movement::is_inside_word(&display_map, display_range.start)
8096 && !movement::is_inside_word(&display_map, display_range.end))
8097 {
8098 self.selections.change_with(cx, |selections| {
8099 new_selections.push(Selection {
8100 id: selections.new_selection_id(),
8101 start: offset_range.start,
8102 end: offset_range.end,
8103 reversed: false,
8104 goal: SelectionGoal::None,
8105 });
8106 });
8107 }
8108 }
8109
8110 new_selections.sort_by_key(|selection| selection.start);
8111 let mut ix = 0;
8112 while ix + 1 < new_selections.len() {
8113 let current_selection = &new_selections[ix];
8114 let next_selection = &new_selections[ix + 1];
8115 if current_selection.range().overlaps(&next_selection.range()) {
8116 if current_selection.id < next_selection.id {
8117 new_selections.remove(ix + 1);
8118 } else {
8119 new_selections.remove(ix);
8120 }
8121 } else {
8122 ix += 1;
8123 }
8124 }
8125
8126 select_next_state.done = true;
8127 self.unfold_ranges(
8128 &new_selections
8129 .iter()
8130 .map(|selection| selection.range())
8131 .collect::<Vec<_>>(),
8132 false,
8133 false,
8134 cx,
8135 );
8136 self.change_selections(Some(Autoscroll::fit()), cx, |selections| {
8137 selections.select(new_selections)
8138 });
8139
8140 Ok(())
8141 }
8142
8143 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
8144 self.push_to_selection_history();
8145 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8146 self.select_next_match_internal(
8147 &display_map,
8148 action.replace_newest,
8149 Some(Autoscroll::newest()),
8150 cx,
8151 )?;
8152 Ok(())
8153 }
8154
8155 pub fn select_previous(
8156 &mut self,
8157 action: &SelectPrevious,
8158 cx: &mut ViewContext<Self>,
8159 ) -> Result<()> {
8160 self.push_to_selection_history();
8161 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8162 let buffer = &display_map.buffer_snapshot;
8163 let mut selections = self.selections.all::<usize>(cx);
8164 if let Some(mut select_prev_state) = self.select_prev_state.take() {
8165 let query = &select_prev_state.query;
8166 if !select_prev_state.done {
8167 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
8168 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
8169 let mut next_selected_range = None;
8170 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
8171 let bytes_before_last_selection =
8172 buffer.reversed_bytes_in_range(0..last_selection.start);
8173 let bytes_after_first_selection =
8174 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
8175 let query_matches = query
8176 .stream_find_iter(bytes_before_last_selection)
8177 .map(|result| (last_selection.start, result))
8178 .chain(
8179 query
8180 .stream_find_iter(bytes_after_first_selection)
8181 .map(|result| (buffer.len(), result)),
8182 );
8183 for (end_offset, query_match) in query_matches {
8184 let query_match = query_match.unwrap(); // can only fail due to I/O
8185 let offset_range =
8186 end_offset - query_match.end()..end_offset - query_match.start();
8187 let display_range = offset_range.start.to_display_point(&display_map)
8188 ..offset_range.end.to_display_point(&display_map);
8189
8190 if !select_prev_state.wordwise
8191 || (!movement::is_inside_word(&display_map, display_range.start)
8192 && !movement::is_inside_word(&display_map, display_range.end))
8193 {
8194 next_selected_range = Some(offset_range);
8195 break;
8196 }
8197 }
8198
8199 if let Some(next_selected_range) = next_selected_range {
8200 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
8201 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8202 if action.replace_newest {
8203 s.delete(s.newest_anchor().id);
8204 }
8205 s.insert_range(next_selected_range);
8206 });
8207 } else {
8208 select_prev_state.done = true;
8209 }
8210 }
8211
8212 self.select_prev_state = Some(select_prev_state);
8213 } else {
8214 let mut only_carets = true;
8215 let mut same_text_selected = true;
8216 let mut selected_text = None;
8217
8218 let mut selections_iter = selections.iter().peekable();
8219 while let Some(selection) = selections_iter.next() {
8220 if selection.start != selection.end {
8221 only_carets = false;
8222 }
8223
8224 if same_text_selected {
8225 if selected_text.is_none() {
8226 selected_text =
8227 Some(buffer.text_for_range(selection.range()).collect::<String>());
8228 }
8229
8230 if let Some(next_selection) = selections_iter.peek() {
8231 if next_selection.range().len() == selection.range().len() {
8232 let next_selected_text = buffer
8233 .text_for_range(next_selection.range())
8234 .collect::<String>();
8235 if Some(next_selected_text) != selected_text {
8236 same_text_selected = false;
8237 selected_text = None;
8238 }
8239 } else {
8240 same_text_selected = false;
8241 selected_text = None;
8242 }
8243 }
8244 }
8245 }
8246
8247 if only_carets {
8248 for selection in &mut selections {
8249 let word_range = movement::surrounding_word(
8250 &display_map,
8251 selection.start.to_display_point(&display_map),
8252 );
8253 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
8254 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
8255 selection.goal = SelectionGoal::None;
8256 selection.reversed = false;
8257 }
8258 if selections.len() == 1 {
8259 let selection = selections
8260 .last()
8261 .expect("ensured that there's only one selection");
8262 let query = buffer
8263 .text_for_range(selection.start..selection.end)
8264 .collect::<String>();
8265 let is_empty = query.is_empty();
8266 let select_state = SelectNextState {
8267 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
8268 wordwise: true,
8269 done: is_empty,
8270 };
8271 self.select_prev_state = Some(select_state);
8272 } else {
8273 self.select_prev_state = None;
8274 }
8275
8276 self.unfold_ranges(
8277 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
8278 false,
8279 true,
8280 cx,
8281 );
8282 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8283 s.select(selections);
8284 });
8285 } else if let Some(selected_text) = selected_text {
8286 self.select_prev_state = Some(SelectNextState {
8287 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
8288 wordwise: false,
8289 done: false,
8290 });
8291 self.select_previous(action, cx)?;
8292 }
8293 }
8294 Ok(())
8295 }
8296
8297 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
8298 if self.read_only(cx) {
8299 return;
8300 }
8301 let text_layout_details = &self.text_layout_details(cx);
8302 self.transact(cx, |this, cx| {
8303 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
8304 let mut edits = Vec::new();
8305 let mut selection_edit_ranges = Vec::new();
8306 let mut last_toggled_row = None;
8307 let snapshot = this.buffer.read(cx).read(cx);
8308 let empty_str: Arc<str> = Arc::default();
8309 let mut suffixes_inserted = Vec::new();
8310 let ignore_indent = action.ignore_indent;
8311
8312 fn comment_prefix_range(
8313 snapshot: &MultiBufferSnapshot,
8314 row: MultiBufferRow,
8315 comment_prefix: &str,
8316 comment_prefix_whitespace: &str,
8317 ignore_indent: bool,
8318 ) -> Range<Point> {
8319 let indent_size = if ignore_indent {
8320 0
8321 } else {
8322 snapshot.indent_size_for_line(row).len
8323 };
8324
8325 let start = Point::new(row.0, indent_size);
8326
8327 let mut line_bytes = snapshot
8328 .bytes_in_range(start..snapshot.max_point())
8329 .flatten()
8330 .copied();
8331
8332 // If this line currently begins with the line comment prefix, then record
8333 // the range containing the prefix.
8334 if line_bytes
8335 .by_ref()
8336 .take(comment_prefix.len())
8337 .eq(comment_prefix.bytes())
8338 {
8339 // Include any whitespace that matches the comment prefix.
8340 let matching_whitespace_len = line_bytes
8341 .zip(comment_prefix_whitespace.bytes())
8342 .take_while(|(a, b)| a == b)
8343 .count() as u32;
8344 let end = Point::new(
8345 start.row,
8346 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
8347 );
8348 start..end
8349 } else {
8350 start..start
8351 }
8352 }
8353
8354 fn comment_suffix_range(
8355 snapshot: &MultiBufferSnapshot,
8356 row: MultiBufferRow,
8357 comment_suffix: &str,
8358 comment_suffix_has_leading_space: bool,
8359 ) -> Range<Point> {
8360 let end = Point::new(row.0, snapshot.line_len(row));
8361 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
8362
8363 let mut line_end_bytes = snapshot
8364 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
8365 .flatten()
8366 .copied();
8367
8368 let leading_space_len = if suffix_start_column > 0
8369 && line_end_bytes.next() == Some(b' ')
8370 && comment_suffix_has_leading_space
8371 {
8372 1
8373 } else {
8374 0
8375 };
8376
8377 // If this line currently begins with the line comment prefix, then record
8378 // the range containing the prefix.
8379 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
8380 let start = Point::new(end.row, suffix_start_column - leading_space_len);
8381 start..end
8382 } else {
8383 end..end
8384 }
8385 }
8386
8387 // TODO: Handle selections that cross excerpts
8388 for selection in &mut selections {
8389 let start_column = snapshot
8390 .indent_size_for_line(MultiBufferRow(selection.start.row))
8391 .len;
8392 let language = if let Some(language) =
8393 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
8394 {
8395 language
8396 } else {
8397 continue;
8398 };
8399
8400 selection_edit_ranges.clear();
8401
8402 // If multiple selections contain a given row, avoid processing that
8403 // row more than once.
8404 let mut start_row = MultiBufferRow(selection.start.row);
8405 if last_toggled_row == Some(start_row) {
8406 start_row = start_row.next_row();
8407 }
8408 let end_row =
8409 if selection.end.row > selection.start.row && selection.end.column == 0 {
8410 MultiBufferRow(selection.end.row - 1)
8411 } else {
8412 MultiBufferRow(selection.end.row)
8413 };
8414 last_toggled_row = Some(end_row);
8415
8416 if start_row > end_row {
8417 continue;
8418 }
8419
8420 // If the language has line comments, toggle those.
8421 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
8422
8423 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
8424 if ignore_indent {
8425 full_comment_prefixes = full_comment_prefixes
8426 .into_iter()
8427 .map(|s| Arc::from(s.trim_end()))
8428 .collect();
8429 }
8430
8431 if !full_comment_prefixes.is_empty() {
8432 let first_prefix = full_comment_prefixes
8433 .first()
8434 .expect("prefixes is non-empty");
8435 let prefix_trimmed_lengths = full_comment_prefixes
8436 .iter()
8437 .map(|p| p.trim_end_matches(' ').len())
8438 .collect::<SmallVec<[usize; 4]>>();
8439
8440 let mut all_selection_lines_are_comments = true;
8441
8442 for row in start_row.0..=end_row.0 {
8443 let row = MultiBufferRow(row);
8444 if start_row < end_row && snapshot.is_line_blank(row) {
8445 continue;
8446 }
8447
8448 let prefix_range = full_comment_prefixes
8449 .iter()
8450 .zip(prefix_trimmed_lengths.iter().copied())
8451 .map(|(prefix, trimmed_prefix_len)| {
8452 comment_prefix_range(
8453 snapshot.deref(),
8454 row,
8455 &prefix[..trimmed_prefix_len],
8456 &prefix[trimmed_prefix_len..],
8457 ignore_indent,
8458 )
8459 })
8460 .max_by_key(|range| range.end.column - range.start.column)
8461 .expect("prefixes is non-empty");
8462
8463 if prefix_range.is_empty() {
8464 all_selection_lines_are_comments = false;
8465 }
8466
8467 selection_edit_ranges.push(prefix_range);
8468 }
8469
8470 if all_selection_lines_are_comments {
8471 edits.extend(
8472 selection_edit_ranges
8473 .iter()
8474 .cloned()
8475 .map(|range| (range, empty_str.clone())),
8476 );
8477 } else {
8478 let min_column = selection_edit_ranges
8479 .iter()
8480 .map(|range| range.start.column)
8481 .min()
8482 .unwrap_or(0);
8483 edits.extend(selection_edit_ranges.iter().map(|range| {
8484 let position = Point::new(range.start.row, min_column);
8485 (position..position, first_prefix.clone())
8486 }));
8487 }
8488 } else if let Some((full_comment_prefix, comment_suffix)) =
8489 language.block_comment_delimiters()
8490 {
8491 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
8492 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
8493 let prefix_range = comment_prefix_range(
8494 snapshot.deref(),
8495 start_row,
8496 comment_prefix,
8497 comment_prefix_whitespace,
8498 ignore_indent,
8499 );
8500 let suffix_range = comment_suffix_range(
8501 snapshot.deref(),
8502 end_row,
8503 comment_suffix.trim_start_matches(' '),
8504 comment_suffix.starts_with(' '),
8505 );
8506
8507 if prefix_range.is_empty() || suffix_range.is_empty() {
8508 edits.push((
8509 prefix_range.start..prefix_range.start,
8510 full_comment_prefix.clone(),
8511 ));
8512 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
8513 suffixes_inserted.push((end_row, comment_suffix.len()));
8514 } else {
8515 edits.push((prefix_range, empty_str.clone()));
8516 edits.push((suffix_range, empty_str.clone()));
8517 }
8518 } else {
8519 continue;
8520 }
8521 }
8522
8523 drop(snapshot);
8524 this.buffer.update(cx, |buffer, cx| {
8525 buffer.edit(edits, None, cx);
8526 });
8527
8528 // Adjust selections so that they end before any comment suffixes that
8529 // were inserted.
8530 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
8531 let mut selections = this.selections.all::<Point>(cx);
8532 let snapshot = this.buffer.read(cx).read(cx);
8533 for selection in &mut selections {
8534 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
8535 match row.cmp(&MultiBufferRow(selection.end.row)) {
8536 Ordering::Less => {
8537 suffixes_inserted.next();
8538 continue;
8539 }
8540 Ordering::Greater => break,
8541 Ordering::Equal => {
8542 if selection.end.column == snapshot.line_len(row) {
8543 if selection.is_empty() {
8544 selection.start.column -= suffix_len as u32;
8545 }
8546 selection.end.column -= suffix_len as u32;
8547 }
8548 break;
8549 }
8550 }
8551 }
8552 }
8553
8554 drop(snapshot);
8555 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
8556
8557 let selections = this.selections.all::<Point>(cx);
8558 let selections_on_single_row = selections.windows(2).all(|selections| {
8559 selections[0].start.row == selections[1].start.row
8560 && selections[0].end.row == selections[1].end.row
8561 && selections[0].start.row == selections[0].end.row
8562 });
8563 let selections_selecting = selections
8564 .iter()
8565 .any(|selection| selection.start != selection.end);
8566 let advance_downwards = action.advance_downwards
8567 && selections_on_single_row
8568 && !selections_selecting
8569 && !matches!(this.mode, EditorMode::SingleLine { .. });
8570
8571 if advance_downwards {
8572 let snapshot = this.buffer.read(cx).snapshot(cx);
8573
8574 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8575 s.move_cursors_with(|display_snapshot, display_point, _| {
8576 let mut point = display_point.to_point(display_snapshot);
8577 point.row += 1;
8578 point = snapshot.clip_point(point, Bias::Left);
8579 let display_point = point.to_display_point(display_snapshot);
8580 let goal = SelectionGoal::HorizontalPosition(
8581 display_snapshot
8582 .x_for_display_point(display_point, text_layout_details)
8583 .into(),
8584 );
8585 (display_point, goal)
8586 })
8587 });
8588 }
8589 });
8590 }
8591
8592 pub fn select_enclosing_symbol(
8593 &mut self,
8594 _: &SelectEnclosingSymbol,
8595 cx: &mut ViewContext<Self>,
8596 ) {
8597 let buffer = self.buffer.read(cx).snapshot(cx);
8598 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8599
8600 fn update_selection(
8601 selection: &Selection<usize>,
8602 buffer_snap: &MultiBufferSnapshot,
8603 ) -> Option<Selection<usize>> {
8604 let cursor = selection.head();
8605 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
8606 for symbol in symbols.iter().rev() {
8607 let start = symbol.range.start.to_offset(buffer_snap);
8608 let end = symbol.range.end.to_offset(buffer_snap);
8609 let new_range = start..end;
8610 if start < selection.start || end > selection.end {
8611 return Some(Selection {
8612 id: selection.id,
8613 start: new_range.start,
8614 end: new_range.end,
8615 goal: SelectionGoal::None,
8616 reversed: selection.reversed,
8617 });
8618 }
8619 }
8620 None
8621 }
8622
8623 let mut selected_larger_symbol = false;
8624 let new_selections = old_selections
8625 .iter()
8626 .map(|selection| match update_selection(selection, &buffer) {
8627 Some(new_selection) => {
8628 if new_selection.range() != selection.range() {
8629 selected_larger_symbol = true;
8630 }
8631 new_selection
8632 }
8633 None => selection.clone(),
8634 })
8635 .collect::<Vec<_>>();
8636
8637 if selected_larger_symbol {
8638 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8639 s.select(new_selections);
8640 });
8641 }
8642 }
8643
8644 pub fn select_larger_syntax_node(
8645 &mut self,
8646 _: &SelectLargerSyntaxNode,
8647 cx: &mut ViewContext<Self>,
8648 ) {
8649 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8650 let buffer = self.buffer.read(cx).snapshot(cx);
8651 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8652
8653 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8654 let mut selected_larger_node = false;
8655 let new_selections = old_selections
8656 .iter()
8657 .map(|selection| {
8658 let old_range = selection.start..selection.end;
8659 let mut new_range = old_range.clone();
8660 while let Some(containing_range) =
8661 buffer.range_for_syntax_ancestor(new_range.clone())
8662 {
8663 new_range = containing_range;
8664 if !display_map.intersects_fold(new_range.start)
8665 && !display_map.intersects_fold(new_range.end)
8666 {
8667 break;
8668 }
8669 }
8670
8671 selected_larger_node |= new_range != old_range;
8672 Selection {
8673 id: selection.id,
8674 start: new_range.start,
8675 end: new_range.end,
8676 goal: SelectionGoal::None,
8677 reversed: selection.reversed,
8678 }
8679 })
8680 .collect::<Vec<_>>();
8681
8682 if selected_larger_node {
8683 stack.push(old_selections);
8684 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8685 s.select(new_selections);
8686 });
8687 }
8688 self.select_larger_syntax_node_stack = stack;
8689 }
8690
8691 pub fn select_smaller_syntax_node(
8692 &mut self,
8693 _: &SelectSmallerSyntaxNode,
8694 cx: &mut ViewContext<Self>,
8695 ) {
8696 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8697 if let Some(selections) = stack.pop() {
8698 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8699 s.select(selections.to_vec());
8700 });
8701 }
8702 self.select_larger_syntax_node_stack = stack;
8703 }
8704
8705 fn refresh_runnables(&mut self, cx: &mut ViewContext<Self>) -> Task<()> {
8706 if !EditorSettings::get_global(cx).gutter.runnables {
8707 self.clear_tasks();
8708 return Task::ready(());
8709 }
8710 let project = self.project.as_ref().map(Model::downgrade);
8711 cx.spawn(|this, mut cx| async move {
8712 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
8713 let Some(project) = project.and_then(|p| p.upgrade()) else {
8714 return;
8715 };
8716 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
8717 this.display_map.update(cx, |map, cx| map.snapshot(cx))
8718 }) else {
8719 return;
8720 };
8721
8722 let hide_runnables = project
8723 .update(&mut cx, |project, cx| {
8724 // Do not display any test indicators in non-dev server remote projects.
8725 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
8726 })
8727 .unwrap_or(true);
8728 if hide_runnables {
8729 return;
8730 }
8731 let new_rows =
8732 cx.background_executor()
8733 .spawn({
8734 let snapshot = display_snapshot.clone();
8735 async move {
8736 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
8737 }
8738 })
8739 .await;
8740 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
8741
8742 this.update(&mut cx, |this, _| {
8743 this.clear_tasks();
8744 for (key, value) in rows {
8745 this.insert_tasks(key, value);
8746 }
8747 })
8748 .ok();
8749 })
8750 }
8751 fn fetch_runnable_ranges(
8752 snapshot: &DisplaySnapshot,
8753 range: Range<Anchor>,
8754 ) -> Vec<language::RunnableRange> {
8755 snapshot.buffer_snapshot.runnable_ranges(range).collect()
8756 }
8757
8758 fn runnable_rows(
8759 project: Model<Project>,
8760 snapshot: DisplaySnapshot,
8761 runnable_ranges: Vec<RunnableRange>,
8762 mut cx: AsyncWindowContext,
8763 ) -> Vec<((BufferId, u32), RunnableTasks)> {
8764 runnable_ranges
8765 .into_iter()
8766 .filter_map(|mut runnable| {
8767 let tasks = cx
8768 .update(|cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
8769 .ok()?;
8770 if tasks.is_empty() {
8771 return None;
8772 }
8773
8774 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
8775
8776 let row = snapshot
8777 .buffer_snapshot
8778 .buffer_line_for_row(MultiBufferRow(point.row))?
8779 .1
8780 .start
8781 .row;
8782
8783 let context_range =
8784 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
8785 Some((
8786 (runnable.buffer_id, row),
8787 RunnableTasks {
8788 templates: tasks,
8789 offset: MultiBufferOffset(runnable.run_range.start),
8790 context_range,
8791 column: point.column,
8792 extra_variables: runnable.extra_captures,
8793 },
8794 ))
8795 })
8796 .collect()
8797 }
8798
8799 fn templates_with_tags(
8800 project: &Model<Project>,
8801 runnable: &mut Runnable,
8802 cx: &WindowContext<'_>,
8803 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
8804 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
8805 let (worktree_id, file) = project
8806 .buffer_for_id(runnable.buffer, cx)
8807 .and_then(|buffer| buffer.read(cx).file())
8808 .map(|file| (file.worktree_id(cx), file.clone()))
8809 .unzip();
8810
8811 (
8812 project.task_store().read(cx).task_inventory().cloned(),
8813 worktree_id,
8814 file,
8815 )
8816 });
8817
8818 let tags = mem::take(&mut runnable.tags);
8819 let mut tags: Vec<_> = tags
8820 .into_iter()
8821 .flat_map(|tag| {
8822 let tag = tag.0.clone();
8823 inventory
8824 .as_ref()
8825 .into_iter()
8826 .flat_map(|inventory| {
8827 inventory.read(cx).list_tasks(
8828 file.clone(),
8829 Some(runnable.language.clone()),
8830 worktree_id,
8831 cx,
8832 )
8833 })
8834 .filter(move |(_, template)| {
8835 template.tags.iter().any(|source_tag| source_tag == &tag)
8836 })
8837 })
8838 .sorted_by_key(|(kind, _)| kind.to_owned())
8839 .collect();
8840 if let Some((leading_tag_source, _)) = tags.first() {
8841 // Strongest source wins; if we have worktree tag binding, prefer that to
8842 // global and language bindings;
8843 // if we have a global binding, prefer that to language binding.
8844 let first_mismatch = tags
8845 .iter()
8846 .position(|(tag_source, _)| tag_source != leading_tag_source);
8847 if let Some(index) = first_mismatch {
8848 tags.truncate(index);
8849 }
8850 }
8851
8852 tags
8853 }
8854
8855 pub fn move_to_enclosing_bracket(
8856 &mut self,
8857 _: &MoveToEnclosingBracket,
8858 cx: &mut ViewContext<Self>,
8859 ) {
8860 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8861 s.move_offsets_with(|snapshot, selection| {
8862 let Some(enclosing_bracket_ranges) =
8863 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
8864 else {
8865 return;
8866 };
8867
8868 let mut best_length = usize::MAX;
8869 let mut best_inside = false;
8870 let mut best_in_bracket_range = false;
8871 let mut best_destination = None;
8872 for (open, close) in enclosing_bracket_ranges {
8873 let close = close.to_inclusive();
8874 let length = close.end() - open.start;
8875 let inside = selection.start >= open.end && selection.end <= *close.start();
8876 let in_bracket_range = open.to_inclusive().contains(&selection.head())
8877 || close.contains(&selection.head());
8878
8879 // If best is next to a bracket and current isn't, skip
8880 if !in_bracket_range && best_in_bracket_range {
8881 continue;
8882 }
8883
8884 // Prefer smaller lengths unless best is inside and current isn't
8885 if length > best_length && (best_inside || !inside) {
8886 continue;
8887 }
8888
8889 best_length = length;
8890 best_inside = inside;
8891 best_in_bracket_range = in_bracket_range;
8892 best_destination = Some(
8893 if close.contains(&selection.start) && close.contains(&selection.end) {
8894 if inside {
8895 open.end
8896 } else {
8897 open.start
8898 }
8899 } else if inside {
8900 *close.start()
8901 } else {
8902 *close.end()
8903 },
8904 );
8905 }
8906
8907 if let Some(destination) = best_destination {
8908 selection.collapse_to(destination, SelectionGoal::None);
8909 }
8910 })
8911 });
8912 }
8913
8914 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
8915 self.end_selection(cx);
8916 self.selection_history.mode = SelectionHistoryMode::Undoing;
8917 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
8918 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8919 self.select_next_state = entry.select_next_state;
8920 self.select_prev_state = entry.select_prev_state;
8921 self.add_selections_state = entry.add_selections_state;
8922 self.request_autoscroll(Autoscroll::newest(), cx);
8923 }
8924 self.selection_history.mode = SelectionHistoryMode::Normal;
8925 }
8926
8927 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
8928 self.end_selection(cx);
8929 self.selection_history.mode = SelectionHistoryMode::Redoing;
8930 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
8931 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8932 self.select_next_state = entry.select_next_state;
8933 self.select_prev_state = entry.select_prev_state;
8934 self.add_selections_state = entry.add_selections_state;
8935 self.request_autoscroll(Autoscroll::newest(), cx);
8936 }
8937 self.selection_history.mode = SelectionHistoryMode::Normal;
8938 }
8939
8940 pub fn expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
8941 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
8942 }
8943
8944 pub fn expand_excerpts_down(
8945 &mut self,
8946 action: &ExpandExcerptsDown,
8947 cx: &mut ViewContext<Self>,
8948 ) {
8949 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
8950 }
8951
8952 pub fn expand_excerpts_up(&mut self, action: &ExpandExcerptsUp, cx: &mut ViewContext<Self>) {
8953 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
8954 }
8955
8956 pub fn expand_excerpts_for_direction(
8957 &mut self,
8958 lines: u32,
8959 direction: ExpandExcerptDirection,
8960 cx: &mut ViewContext<Self>,
8961 ) {
8962 let selections = self.selections.disjoint_anchors();
8963
8964 let lines = if lines == 0 {
8965 EditorSettings::get_global(cx).expand_excerpt_lines
8966 } else {
8967 lines
8968 };
8969
8970 self.buffer.update(cx, |buffer, cx| {
8971 buffer.expand_excerpts(
8972 selections
8973 .iter()
8974 .map(|selection| selection.head().excerpt_id)
8975 .dedup(),
8976 lines,
8977 direction,
8978 cx,
8979 )
8980 })
8981 }
8982
8983 pub fn expand_excerpt(
8984 &mut self,
8985 excerpt: ExcerptId,
8986 direction: ExpandExcerptDirection,
8987 cx: &mut ViewContext<Self>,
8988 ) {
8989 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
8990 self.buffer.update(cx, |buffer, cx| {
8991 buffer.expand_excerpts([excerpt], lines, direction, cx)
8992 })
8993 }
8994
8995 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
8996 self.go_to_diagnostic_impl(Direction::Next, cx)
8997 }
8998
8999 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
9000 self.go_to_diagnostic_impl(Direction::Prev, cx)
9001 }
9002
9003 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
9004 let buffer = self.buffer.read(cx).snapshot(cx);
9005 let selection = self.selections.newest::<usize>(cx);
9006
9007 // If there is an active Diagnostic Popover jump to its diagnostic instead.
9008 if direction == Direction::Next {
9009 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
9010 let (group_id, jump_to) = popover.activation_info();
9011 if self.activate_diagnostics(group_id, cx) {
9012 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9013 let mut new_selection = s.newest_anchor().clone();
9014 new_selection.collapse_to(jump_to, SelectionGoal::None);
9015 s.select_anchors(vec![new_selection.clone()]);
9016 });
9017 }
9018 return;
9019 }
9020 }
9021
9022 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
9023 active_diagnostics
9024 .primary_range
9025 .to_offset(&buffer)
9026 .to_inclusive()
9027 });
9028 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
9029 if active_primary_range.contains(&selection.head()) {
9030 *active_primary_range.start()
9031 } else {
9032 selection.head()
9033 }
9034 } else {
9035 selection.head()
9036 };
9037 let snapshot = self.snapshot(cx);
9038 loop {
9039 let diagnostics = if direction == Direction::Prev {
9040 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
9041 } else {
9042 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
9043 }
9044 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start));
9045 let group = diagnostics
9046 // relies on diagnostics_in_range to return diagnostics with the same starting range to
9047 // be sorted in a stable way
9048 // skip until we are at current active diagnostic, if it exists
9049 .skip_while(|entry| {
9050 (match direction {
9051 Direction::Prev => entry.range.start >= search_start,
9052 Direction::Next => entry.range.start <= search_start,
9053 }) && self
9054 .active_diagnostics
9055 .as_ref()
9056 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
9057 })
9058 .find_map(|entry| {
9059 if entry.diagnostic.is_primary
9060 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
9061 && !entry.range.is_empty()
9062 // if we match with the active diagnostic, skip it
9063 && Some(entry.diagnostic.group_id)
9064 != self.active_diagnostics.as_ref().map(|d| d.group_id)
9065 {
9066 Some((entry.range, entry.diagnostic.group_id))
9067 } else {
9068 None
9069 }
9070 });
9071
9072 if let Some((primary_range, group_id)) = group {
9073 if self.activate_diagnostics(group_id, cx) {
9074 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9075 s.select(vec![Selection {
9076 id: selection.id,
9077 start: primary_range.start,
9078 end: primary_range.start,
9079 reversed: false,
9080 goal: SelectionGoal::None,
9081 }]);
9082 });
9083 }
9084 break;
9085 } else {
9086 // Cycle around to the start of the buffer, potentially moving back to the start of
9087 // the currently active diagnostic.
9088 active_primary_range.take();
9089 if direction == Direction::Prev {
9090 if search_start == buffer.len() {
9091 break;
9092 } else {
9093 search_start = buffer.len();
9094 }
9095 } else if search_start == 0 {
9096 break;
9097 } else {
9098 search_start = 0;
9099 }
9100 }
9101 }
9102 }
9103
9104 fn go_to_next_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
9105 let snapshot = self.snapshot(cx);
9106 let selection = self.selections.newest::<Point>(cx);
9107 self.go_to_hunk_after_position(&snapshot, selection.head(), cx);
9108 }
9109
9110 fn go_to_hunk_after_position(
9111 &mut self,
9112 snapshot: &EditorSnapshot,
9113 position: Point,
9114 cx: &mut ViewContext<'_, Editor>,
9115 ) -> Option<MultiBufferDiffHunk> {
9116 for (ix, position) in [position, Point::zero()].into_iter().enumerate() {
9117 if let Some(hunk) = self.go_to_next_hunk_in_direction(
9118 snapshot,
9119 position,
9120 ix > 0,
9121 snapshot.diff_map.diff_hunks_in_range(
9122 position + Point::new(1, 0)..snapshot.buffer_snapshot.max_point(),
9123 &snapshot.buffer_snapshot,
9124 ),
9125 cx,
9126 ) {
9127 return Some(hunk);
9128 }
9129 }
9130 None
9131 }
9132
9133 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
9134 let snapshot = self.snapshot(cx);
9135 let selection = self.selections.newest::<Point>(cx);
9136 self.go_to_hunk_before_position(&snapshot, selection.head(), cx);
9137 }
9138
9139 fn go_to_hunk_before_position(
9140 &mut self,
9141 snapshot: &EditorSnapshot,
9142 position: Point,
9143 cx: &mut ViewContext<'_, Editor>,
9144 ) -> Option<MultiBufferDiffHunk> {
9145 for (ix, position) in [position, snapshot.buffer_snapshot.max_point()]
9146 .into_iter()
9147 .enumerate()
9148 {
9149 if let Some(hunk) = self.go_to_next_hunk_in_direction(
9150 snapshot,
9151 position,
9152 ix > 0,
9153 snapshot
9154 .diff_map
9155 .diff_hunks_in_range_rev(Point::zero()..position, &snapshot.buffer_snapshot),
9156 cx,
9157 ) {
9158 return Some(hunk);
9159 }
9160 }
9161 None
9162 }
9163
9164 fn go_to_next_hunk_in_direction(
9165 &mut self,
9166 snapshot: &DisplaySnapshot,
9167 initial_point: Point,
9168 is_wrapped: bool,
9169 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
9170 cx: &mut ViewContext<Editor>,
9171 ) -> Option<MultiBufferDiffHunk> {
9172 let display_point = initial_point.to_display_point(snapshot);
9173 let mut hunks = hunks
9174 .map(|hunk| (diff_hunk_to_display(&hunk, snapshot), hunk))
9175 .filter(|(display_hunk, _)| {
9176 is_wrapped || !display_hunk.contains_display_row(display_point.row())
9177 })
9178 .dedup();
9179
9180 if let Some((display_hunk, hunk)) = hunks.next() {
9181 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9182 let row = display_hunk.start_display_row();
9183 let point = DisplayPoint::new(row, 0);
9184 s.select_display_ranges([point..point]);
9185 });
9186
9187 Some(hunk)
9188 } else {
9189 None
9190 }
9191 }
9192
9193 pub fn go_to_definition(
9194 &mut self,
9195 _: &GoToDefinition,
9196 cx: &mut ViewContext<Self>,
9197 ) -> Task<Result<Navigated>> {
9198 let definition = self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
9199 cx.spawn(|editor, mut cx| async move {
9200 if definition.await? == Navigated::Yes {
9201 return Ok(Navigated::Yes);
9202 }
9203 match editor.update(&mut cx, |editor, cx| {
9204 editor.find_all_references(&FindAllReferences, cx)
9205 })? {
9206 Some(references) => references.await,
9207 None => Ok(Navigated::No),
9208 }
9209 })
9210 }
9211
9212 pub fn go_to_declaration(
9213 &mut self,
9214 _: &GoToDeclaration,
9215 cx: &mut ViewContext<Self>,
9216 ) -> Task<Result<Navigated>> {
9217 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, cx)
9218 }
9219
9220 pub fn go_to_declaration_split(
9221 &mut self,
9222 _: &GoToDeclaration,
9223 cx: &mut ViewContext<Self>,
9224 ) -> Task<Result<Navigated>> {
9225 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, cx)
9226 }
9227
9228 pub fn go_to_implementation(
9229 &mut self,
9230 _: &GoToImplementation,
9231 cx: &mut ViewContext<Self>,
9232 ) -> Task<Result<Navigated>> {
9233 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx)
9234 }
9235
9236 pub fn go_to_implementation_split(
9237 &mut self,
9238 _: &GoToImplementationSplit,
9239 cx: &mut ViewContext<Self>,
9240 ) -> Task<Result<Navigated>> {
9241 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx)
9242 }
9243
9244 pub fn go_to_type_definition(
9245 &mut self,
9246 _: &GoToTypeDefinition,
9247 cx: &mut ViewContext<Self>,
9248 ) -> Task<Result<Navigated>> {
9249 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx)
9250 }
9251
9252 pub fn go_to_definition_split(
9253 &mut self,
9254 _: &GoToDefinitionSplit,
9255 cx: &mut ViewContext<Self>,
9256 ) -> Task<Result<Navigated>> {
9257 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx)
9258 }
9259
9260 pub fn go_to_type_definition_split(
9261 &mut self,
9262 _: &GoToTypeDefinitionSplit,
9263 cx: &mut ViewContext<Self>,
9264 ) -> Task<Result<Navigated>> {
9265 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx)
9266 }
9267
9268 fn go_to_definition_of_kind(
9269 &mut self,
9270 kind: GotoDefinitionKind,
9271 split: bool,
9272 cx: &mut ViewContext<Self>,
9273 ) -> Task<Result<Navigated>> {
9274 let Some(provider) = self.semantics_provider.clone() else {
9275 return Task::ready(Ok(Navigated::No));
9276 };
9277 let head = self.selections.newest::<usize>(cx).head();
9278 let buffer = self.buffer.read(cx);
9279 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
9280 text_anchor
9281 } else {
9282 return Task::ready(Ok(Navigated::No));
9283 };
9284
9285 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
9286 return Task::ready(Ok(Navigated::No));
9287 };
9288
9289 cx.spawn(|editor, mut cx| async move {
9290 let definitions = definitions.await?;
9291 let navigated = editor
9292 .update(&mut cx, |editor, cx| {
9293 editor.navigate_to_hover_links(
9294 Some(kind),
9295 definitions
9296 .into_iter()
9297 .filter(|location| {
9298 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
9299 })
9300 .map(HoverLink::Text)
9301 .collect::<Vec<_>>(),
9302 split,
9303 cx,
9304 )
9305 })?
9306 .await?;
9307 anyhow::Ok(navigated)
9308 })
9309 }
9310
9311 pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext<Self>) {
9312 let selection = self.selections.newest_anchor();
9313 let head = selection.head();
9314 let tail = selection.tail();
9315
9316 let Some((buffer, start_position)) =
9317 self.buffer.read(cx).text_anchor_for_position(head, cx)
9318 else {
9319 return;
9320 };
9321
9322 let end_position = if head != tail {
9323 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
9324 return;
9325 };
9326 Some(pos)
9327 } else {
9328 None
9329 };
9330
9331 let url_finder = cx.spawn(|editor, mut cx| async move {
9332 let url = if let Some(end_pos) = end_position {
9333 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
9334 } else {
9335 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
9336 };
9337
9338 if let Some(url) = url {
9339 editor.update(&mut cx, |_, cx| {
9340 cx.open_url(&url);
9341 })
9342 } else {
9343 Ok(())
9344 }
9345 });
9346
9347 url_finder.detach();
9348 }
9349
9350 pub fn open_file(&mut self, _: &OpenFile, cx: &mut ViewContext<Self>) {
9351 let Some(workspace) = self.workspace() else {
9352 return;
9353 };
9354
9355 let position = self.selections.newest_anchor().head();
9356
9357 let Some((buffer, buffer_position)) =
9358 self.buffer.read(cx).text_anchor_for_position(position, cx)
9359 else {
9360 return;
9361 };
9362
9363 let project = self.project.clone();
9364
9365 cx.spawn(|_, mut cx| async move {
9366 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
9367
9368 if let Some((_, path)) = result {
9369 workspace
9370 .update(&mut cx, |workspace, cx| {
9371 workspace.open_resolved_path(path, cx)
9372 })?
9373 .await?;
9374 }
9375 anyhow::Ok(())
9376 })
9377 .detach();
9378 }
9379
9380 pub(crate) fn navigate_to_hover_links(
9381 &mut self,
9382 kind: Option<GotoDefinitionKind>,
9383 mut definitions: Vec<HoverLink>,
9384 split: bool,
9385 cx: &mut ViewContext<Editor>,
9386 ) -> Task<Result<Navigated>> {
9387 // If there is one definition, just open it directly
9388 if definitions.len() == 1 {
9389 let definition = definitions.pop().unwrap();
9390
9391 enum TargetTaskResult {
9392 Location(Option<Location>),
9393 AlreadyNavigated,
9394 }
9395
9396 let target_task = match definition {
9397 HoverLink::Text(link) => {
9398 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
9399 }
9400 HoverLink::InlayHint(lsp_location, server_id) => {
9401 let computation = self.compute_target_location(lsp_location, server_id, cx);
9402 cx.background_executor().spawn(async move {
9403 let location = computation.await?;
9404 Ok(TargetTaskResult::Location(location))
9405 })
9406 }
9407 HoverLink::Url(url) => {
9408 cx.open_url(&url);
9409 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
9410 }
9411 HoverLink::File(path) => {
9412 if let Some(workspace) = self.workspace() {
9413 cx.spawn(|_, mut cx| async move {
9414 workspace
9415 .update(&mut cx, |workspace, cx| {
9416 workspace.open_resolved_path(path, cx)
9417 })?
9418 .await
9419 .map(|_| TargetTaskResult::AlreadyNavigated)
9420 })
9421 } else {
9422 Task::ready(Ok(TargetTaskResult::Location(None)))
9423 }
9424 }
9425 };
9426 cx.spawn(|editor, mut cx| async move {
9427 let target = match target_task.await.context("target resolution task")? {
9428 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
9429 TargetTaskResult::Location(None) => return Ok(Navigated::No),
9430 TargetTaskResult::Location(Some(target)) => target,
9431 };
9432
9433 editor.update(&mut cx, |editor, cx| {
9434 let Some(workspace) = editor.workspace() else {
9435 return Navigated::No;
9436 };
9437 let pane = workspace.read(cx).active_pane().clone();
9438
9439 let range = target.range.to_offset(target.buffer.read(cx));
9440 let range = editor.range_for_match(&range);
9441
9442 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
9443 let buffer = target.buffer.read(cx);
9444 let range = check_multiline_range(buffer, range);
9445 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
9446 s.select_ranges([range]);
9447 });
9448 } else {
9449 cx.window_context().defer(move |cx| {
9450 let target_editor: View<Self> =
9451 workspace.update(cx, |workspace, cx| {
9452 let pane = if split {
9453 workspace.adjacent_pane(cx)
9454 } else {
9455 workspace.active_pane().clone()
9456 };
9457
9458 workspace.open_project_item(
9459 pane,
9460 target.buffer.clone(),
9461 true,
9462 true,
9463 cx,
9464 )
9465 });
9466 target_editor.update(cx, |target_editor, cx| {
9467 // When selecting a definition in a different buffer, disable the nav history
9468 // to avoid creating a history entry at the previous cursor location.
9469 pane.update(cx, |pane, _| pane.disable_history());
9470 let buffer = target.buffer.read(cx);
9471 let range = check_multiline_range(buffer, range);
9472 target_editor.change_selections(
9473 Some(Autoscroll::focused()),
9474 cx,
9475 |s| {
9476 s.select_ranges([range]);
9477 },
9478 );
9479 pane.update(cx, |pane, _| pane.enable_history());
9480 });
9481 });
9482 }
9483 Navigated::Yes
9484 })
9485 })
9486 } else if !definitions.is_empty() {
9487 cx.spawn(|editor, mut cx| async move {
9488 let (title, location_tasks, workspace) = editor
9489 .update(&mut cx, |editor, cx| {
9490 let tab_kind = match kind {
9491 Some(GotoDefinitionKind::Implementation) => "Implementations",
9492 _ => "Definitions",
9493 };
9494 let title = definitions
9495 .iter()
9496 .find_map(|definition| match definition {
9497 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
9498 let buffer = origin.buffer.read(cx);
9499 format!(
9500 "{} for {}",
9501 tab_kind,
9502 buffer
9503 .text_for_range(origin.range.clone())
9504 .collect::<String>()
9505 )
9506 }),
9507 HoverLink::InlayHint(_, _) => None,
9508 HoverLink::Url(_) => None,
9509 HoverLink::File(_) => None,
9510 })
9511 .unwrap_or(tab_kind.to_string());
9512 let location_tasks = definitions
9513 .into_iter()
9514 .map(|definition| match definition {
9515 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
9516 HoverLink::InlayHint(lsp_location, server_id) => {
9517 editor.compute_target_location(lsp_location, server_id, cx)
9518 }
9519 HoverLink::Url(_) => Task::ready(Ok(None)),
9520 HoverLink::File(_) => Task::ready(Ok(None)),
9521 })
9522 .collect::<Vec<_>>();
9523 (title, location_tasks, editor.workspace().clone())
9524 })
9525 .context("location tasks preparation")?;
9526
9527 let locations = future::join_all(location_tasks)
9528 .await
9529 .into_iter()
9530 .filter_map(|location| location.transpose())
9531 .collect::<Result<_>>()
9532 .context("location tasks")?;
9533
9534 let Some(workspace) = workspace else {
9535 return Ok(Navigated::No);
9536 };
9537 let opened = workspace
9538 .update(&mut cx, |workspace, cx| {
9539 Self::open_locations_in_multibuffer(workspace, locations, title, split, cx)
9540 })
9541 .ok();
9542
9543 anyhow::Ok(Navigated::from_bool(opened.is_some()))
9544 })
9545 } else {
9546 Task::ready(Ok(Navigated::No))
9547 }
9548 }
9549
9550 fn compute_target_location(
9551 &self,
9552 lsp_location: lsp::Location,
9553 server_id: LanguageServerId,
9554 cx: &mut ViewContext<Self>,
9555 ) -> Task<anyhow::Result<Option<Location>>> {
9556 let Some(project) = self.project.clone() else {
9557 return Task::Ready(Some(Ok(None)));
9558 };
9559
9560 cx.spawn(move |editor, mut cx| async move {
9561 let location_task = editor.update(&mut cx, |_, cx| {
9562 project.update(cx, |project, cx| {
9563 let language_server_name = project
9564 .language_server_statuses(cx)
9565 .find(|(id, _)| server_id == *id)
9566 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
9567 language_server_name.map(|language_server_name| {
9568 project.open_local_buffer_via_lsp(
9569 lsp_location.uri.clone(),
9570 server_id,
9571 language_server_name,
9572 cx,
9573 )
9574 })
9575 })
9576 })?;
9577 let location = match location_task {
9578 Some(task) => Some({
9579 let target_buffer_handle = task.await.context("open local buffer")?;
9580 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
9581 let target_start = target_buffer
9582 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
9583 let target_end = target_buffer
9584 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
9585 target_buffer.anchor_after(target_start)
9586 ..target_buffer.anchor_before(target_end)
9587 })?;
9588 Location {
9589 buffer: target_buffer_handle,
9590 range,
9591 }
9592 }),
9593 None => None,
9594 };
9595 Ok(location)
9596 })
9597 }
9598
9599 pub fn find_all_references(
9600 &mut self,
9601 _: &FindAllReferences,
9602 cx: &mut ViewContext<Self>,
9603 ) -> Option<Task<Result<Navigated>>> {
9604 let selection = self.selections.newest::<usize>(cx);
9605 let multi_buffer = self.buffer.read(cx);
9606 let head = selection.head();
9607
9608 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
9609 let head_anchor = multi_buffer_snapshot.anchor_at(
9610 head,
9611 if head < selection.tail() {
9612 Bias::Right
9613 } else {
9614 Bias::Left
9615 },
9616 );
9617
9618 match self
9619 .find_all_references_task_sources
9620 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
9621 {
9622 Ok(_) => {
9623 log::info!(
9624 "Ignoring repeated FindAllReferences invocation with the position of already running task"
9625 );
9626 return None;
9627 }
9628 Err(i) => {
9629 self.find_all_references_task_sources.insert(i, head_anchor);
9630 }
9631 }
9632
9633 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
9634 let workspace = self.workspace()?;
9635 let project = workspace.read(cx).project().clone();
9636 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
9637 Some(cx.spawn(|editor, mut cx| async move {
9638 let _cleanup = defer({
9639 let mut cx = cx.clone();
9640 move || {
9641 let _ = editor.update(&mut cx, |editor, _| {
9642 if let Ok(i) =
9643 editor
9644 .find_all_references_task_sources
9645 .binary_search_by(|anchor| {
9646 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
9647 })
9648 {
9649 editor.find_all_references_task_sources.remove(i);
9650 }
9651 });
9652 }
9653 });
9654
9655 let locations = references.await?;
9656 if locations.is_empty() {
9657 return anyhow::Ok(Navigated::No);
9658 }
9659
9660 workspace.update(&mut cx, |workspace, cx| {
9661 let title = locations
9662 .first()
9663 .as_ref()
9664 .map(|location| {
9665 let buffer = location.buffer.read(cx);
9666 format!(
9667 "References to `{}`",
9668 buffer
9669 .text_for_range(location.range.clone())
9670 .collect::<String>()
9671 )
9672 })
9673 .unwrap();
9674 Self::open_locations_in_multibuffer(workspace, locations, title, false, cx);
9675 Navigated::Yes
9676 })
9677 }))
9678 }
9679
9680 /// Opens a multibuffer with the given project locations in it
9681 pub fn open_locations_in_multibuffer(
9682 workspace: &mut Workspace,
9683 mut locations: Vec<Location>,
9684 title: String,
9685 split: bool,
9686 cx: &mut ViewContext<Workspace>,
9687 ) {
9688 // If there are multiple definitions, open them in a multibuffer
9689 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
9690 let mut locations = locations.into_iter().peekable();
9691 let mut ranges_to_highlight = Vec::new();
9692 let capability = workspace.project().read(cx).capability();
9693
9694 let excerpt_buffer = cx.new_model(|cx| {
9695 let mut multibuffer = MultiBuffer::new(capability);
9696 while let Some(location) = locations.next() {
9697 let buffer = location.buffer.read(cx);
9698 let mut ranges_for_buffer = Vec::new();
9699 let range = location.range.to_offset(buffer);
9700 ranges_for_buffer.push(range.clone());
9701
9702 while let Some(next_location) = locations.peek() {
9703 if next_location.buffer == location.buffer {
9704 ranges_for_buffer.push(next_location.range.to_offset(buffer));
9705 locations.next();
9706 } else {
9707 break;
9708 }
9709 }
9710
9711 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
9712 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
9713 location.buffer.clone(),
9714 ranges_for_buffer,
9715 DEFAULT_MULTIBUFFER_CONTEXT,
9716 cx,
9717 ))
9718 }
9719
9720 multibuffer.with_title(title)
9721 });
9722
9723 let editor = cx.new_view(|cx| {
9724 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), true, cx)
9725 });
9726 editor.update(cx, |editor, cx| {
9727 if let Some(first_range) = ranges_to_highlight.first() {
9728 editor.change_selections(None, cx, |selections| {
9729 selections.clear_disjoint();
9730 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
9731 });
9732 }
9733 editor.highlight_background::<Self>(
9734 &ranges_to_highlight,
9735 |theme| theme.editor_highlighted_line_background,
9736 cx,
9737 );
9738 editor.register_buffers_with_language_servers(cx);
9739 });
9740
9741 let item = Box::new(editor);
9742 let item_id = item.item_id();
9743
9744 if split {
9745 workspace.split_item(SplitDirection::Right, item.clone(), cx);
9746 } else {
9747 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
9748 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
9749 pane.close_current_preview_item(cx)
9750 } else {
9751 None
9752 }
9753 });
9754 workspace.add_item_to_active_pane(item.clone(), destination_index, true, cx);
9755 }
9756 workspace.active_pane().update(cx, |pane, cx| {
9757 pane.set_preview_item_id(Some(item_id), cx);
9758 });
9759 }
9760
9761 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9762 use language::ToOffset as _;
9763
9764 let provider = self.semantics_provider.clone()?;
9765 let selection = self.selections.newest_anchor().clone();
9766 let (cursor_buffer, cursor_buffer_position) = self
9767 .buffer
9768 .read(cx)
9769 .text_anchor_for_position(selection.head(), cx)?;
9770 let (tail_buffer, cursor_buffer_position_end) = self
9771 .buffer
9772 .read(cx)
9773 .text_anchor_for_position(selection.tail(), cx)?;
9774 if tail_buffer != cursor_buffer {
9775 return None;
9776 }
9777
9778 let snapshot = cursor_buffer.read(cx).snapshot();
9779 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
9780 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
9781 let prepare_rename = provider
9782 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
9783 .unwrap_or_else(|| Task::ready(Ok(None)));
9784 drop(snapshot);
9785
9786 Some(cx.spawn(|this, mut cx| async move {
9787 let rename_range = if let Some(range) = prepare_rename.await? {
9788 Some(range)
9789 } else {
9790 this.update(&mut cx, |this, cx| {
9791 let buffer = this.buffer.read(cx).snapshot(cx);
9792 let mut buffer_highlights = this
9793 .document_highlights_for_position(selection.head(), &buffer)
9794 .filter(|highlight| {
9795 highlight.start.excerpt_id == selection.head().excerpt_id
9796 && highlight.end.excerpt_id == selection.head().excerpt_id
9797 });
9798 buffer_highlights
9799 .next()
9800 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
9801 })?
9802 };
9803 if let Some(rename_range) = rename_range {
9804 this.update(&mut cx, |this, cx| {
9805 let snapshot = cursor_buffer.read(cx).snapshot();
9806 let rename_buffer_range = rename_range.to_offset(&snapshot);
9807 let cursor_offset_in_rename_range =
9808 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
9809 let cursor_offset_in_rename_range_end =
9810 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
9811
9812 this.take_rename(false, cx);
9813 let buffer = this.buffer.read(cx).read(cx);
9814 let cursor_offset = selection.head().to_offset(&buffer);
9815 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
9816 let rename_end = rename_start + rename_buffer_range.len();
9817 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
9818 let mut old_highlight_id = None;
9819 let old_name: Arc<str> = buffer
9820 .chunks(rename_start..rename_end, true)
9821 .map(|chunk| {
9822 if old_highlight_id.is_none() {
9823 old_highlight_id = chunk.syntax_highlight_id;
9824 }
9825 chunk.text
9826 })
9827 .collect::<String>()
9828 .into();
9829
9830 drop(buffer);
9831
9832 // Position the selection in the rename editor so that it matches the current selection.
9833 this.show_local_selections = false;
9834 let rename_editor = cx.new_view(|cx| {
9835 let mut editor = Editor::single_line(cx);
9836 editor.buffer.update(cx, |buffer, cx| {
9837 buffer.edit([(0..0, old_name.clone())], None, cx)
9838 });
9839 let rename_selection_range = match cursor_offset_in_rename_range
9840 .cmp(&cursor_offset_in_rename_range_end)
9841 {
9842 Ordering::Equal => {
9843 editor.select_all(&SelectAll, cx);
9844 return editor;
9845 }
9846 Ordering::Less => {
9847 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
9848 }
9849 Ordering::Greater => {
9850 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
9851 }
9852 };
9853 if rename_selection_range.end > old_name.len() {
9854 editor.select_all(&SelectAll, cx);
9855 } else {
9856 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
9857 s.select_ranges([rename_selection_range]);
9858 });
9859 }
9860 editor
9861 });
9862 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
9863 if e == &EditorEvent::Focused {
9864 cx.emit(EditorEvent::FocusedIn)
9865 }
9866 })
9867 .detach();
9868
9869 let write_highlights =
9870 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
9871 let read_highlights =
9872 this.clear_background_highlights::<DocumentHighlightRead>(cx);
9873 let ranges = write_highlights
9874 .iter()
9875 .flat_map(|(_, ranges)| ranges.iter())
9876 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
9877 .cloned()
9878 .collect();
9879
9880 this.highlight_text::<Rename>(
9881 ranges,
9882 HighlightStyle {
9883 fade_out: Some(0.6),
9884 ..Default::default()
9885 },
9886 cx,
9887 );
9888 let rename_focus_handle = rename_editor.focus_handle(cx);
9889 cx.focus(&rename_focus_handle);
9890 let block_id = this.insert_blocks(
9891 [BlockProperties {
9892 style: BlockStyle::Flex,
9893 placement: BlockPlacement::Below(range.start),
9894 height: 1,
9895 render: Arc::new({
9896 let rename_editor = rename_editor.clone();
9897 move |cx: &mut BlockContext| {
9898 let mut text_style = cx.editor_style.text.clone();
9899 if let Some(highlight_style) = old_highlight_id
9900 .and_then(|h| h.style(&cx.editor_style.syntax))
9901 {
9902 text_style = text_style.highlight(highlight_style);
9903 }
9904 div()
9905 .block_mouse_down()
9906 .pl(cx.anchor_x)
9907 .child(EditorElement::new(
9908 &rename_editor,
9909 EditorStyle {
9910 background: cx.theme().system().transparent,
9911 local_player: cx.editor_style.local_player,
9912 text: text_style,
9913 scrollbar_width: cx.editor_style.scrollbar_width,
9914 syntax: cx.editor_style.syntax.clone(),
9915 status: cx.editor_style.status.clone(),
9916 inlay_hints_style: HighlightStyle {
9917 font_weight: Some(FontWeight::BOLD),
9918 ..make_inlay_hints_style(cx)
9919 },
9920 inline_completion_styles: make_suggestion_styles(
9921 cx,
9922 ),
9923 ..EditorStyle::default()
9924 },
9925 ))
9926 .into_any_element()
9927 }
9928 }),
9929 priority: 0,
9930 }],
9931 Some(Autoscroll::fit()),
9932 cx,
9933 )[0];
9934 this.pending_rename = Some(RenameState {
9935 range,
9936 old_name,
9937 editor: rename_editor,
9938 block_id,
9939 });
9940 })?;
9941 }
9942
9943 Ok(())
9944 }))
9945 }
9946
9947 pub fn confirm_rename(
9948 &mut self,
9949 _: &ConfirmRename,
9950 cx: &mut ViewContext<Self>,
9951 ) -> Option<Task<Result<()>>> {
9952 let rename = self.take_rename(false, cx)?;
9953 let workspace = self.workspace()?.downgrade();
9954 let (buffer, start) = self
9955 .buffer
9956 .read(cx)
9957 .text_anchor_for_position(rename.range.start, cx)?;
9958 let (end_buffer, _) = self
9959 .buffer
9960 .read(cx)
9961 .text_anchor_for_position(rename.range.end, cx)?;
9962 if buffer != end_buffer {
9963 return None;
9964 }
9965
9966 let old_name = rename.old_name;
9967 let new_name = rename.editor.read(cx).text(cx);
9968
9969 let rename = self.semantics_provider.as_ref()?.perform_rename(
9970 &buffer,
9971 start,
9972 new_name.clone(),
9973 cx,
9974 )?;
9975
9976 Some(cx.spawn(|editor, mut cx| async move {
9977 let project_transaction = rename.await?;
9978 Self::open_project_transaction(
9979 &editor,
9980 workspace,
9981 project_transaction,
9982 format!("Rename: {} → {}", old_name, new_name),
9983 cx.clone(),
9984 )
9985 .await?;
9986
9987 editor.update(&mut cx, |editor, cx| {
9988 editor.refresh_document_highlights(cx);
9989 })?;
9990 Ok(())
9991 }))
9992 }
9993
9994 fn take_rename(
9995 &mut self,
9996 moving_cursor: bool,
9997 cx: &mut ViewContext<Self>,
9998 ) -> Option<RenameState> {
9999 let rename = self.pending_rename.take()?;
10000 if rename.editor.focus_handle(cx).is_focused(cx) {
10001 cx.focus(&self.focus_handle);
10002 }
10003
10004 self.remove_blocks(
10005 [rename.block_id].into_iter().collect(),
10006 Some(Autoscroll::fit()),
10007 cx,
10008 );
10009 self.clear_highlights::<Rename>(cx);
10010 self.show_local_selections = true;
10011
10012 if moving_cursor {
10013 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
10014 editor.selections.newest::<usize>(cx).head()
10015 });
10016
10017 // Update the selection to match the position of the selection inside
10018 // the rename editor.
10019 let snapshot = self.buffer.read(cx).read(cx);
10020 let rename_range = rename.range.to_offset(&snapshot);
10021 let cursor_in_editor = snapshot
10022 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
10023 .min(rename_range.end);
10024 drop(snapshot);
10025
10026 self.change_selections(None, cx, |s| {
10027 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
10028 });
10029 } else {
10030 self.refresh_document_highlights(cx);
10031 }
10032
10033 Some(rename)
10034 }
10035
10036 pub fn pending_rename(&self) -> Option<&RenameState> {
10037 self.pending_rename.as_ref()
10038 }
10039
10040 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
10041 let project = match &self.project {
10042 Some(project) => project.clone(),
10043 None => return None,
10044 };
10045
10046 Some(self.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffer, cx))
10047 }
10048
10049 fn format_selections(
10050 &mut self,
10051 _: &FormatSelections,
10052 cx: &mut ViewContext<Self>,
10053 ) -> Option<Task<Result<()>>> {
10054 let project = match &self.project {
10055 Some(project) => project.clone(),
10056 None => return None,
10057 };
10058
10059 let selections = self
10060 .selections
10061 .all_adjusted(cx)
10062 .into_iter()
10063 .filter(|s| !s.is_empty())
10064 .collect_vec();
10065
10066 Some(self.perform_format(
10067 project,
10068 FormatTrigger::Manual,
10069 FormatTarget::Ranges(selections),
10070 cx,
10071 ))
10072 }
10073
10074 fn perform_format(
10075 &mut self,
10076 project: Model<Project>,
10077 trigger: FormatTrigger,
10078 target: FormatTarget,
10079 cx: &mut ViewContext<Self>,
10080 ) -> Task<Result<()>> {
10081 let buffer = self.buffer().clone();
10082 let mut buffers = buffer.read(cx).all_buffers();
10083 if trigger == FormatTrigger::Save {
10084 buffers.retain(|buffer| buffer.read(cx).is_dirty());
10085 }
10086
10087 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
10088 let format = project.update(cx, |project, cx| {
10089 project.format(buffers, true, trigger, target, cx)
10090 });
10091
10092 cx.spawn(|_, mut cx| async move {
10093 let transaction = futures::select_biased! {
10094 () = timeout => {
10095 log::warn!("timed out waiting for formatting");
10096 None
10097 }
10098 transaction = format.log_err().fuse() => transaction,
10099 };
10100
10101 buffer
10102 .update(&mut cx, |buffer, cx| {
10103 if let Some(transaction) = transaction {
10104 if !buffer.is_singleton() {
10105 buffer.push_transaction(&transaction.0, cx);
10106 }
10107 }
10108
10109 cx.notify();
10110 })
10111 .ok();
10112
10113 Ok(())
10114 })
10115 }
10116
10117 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
10118 if let Some(project) = self.project.clone() {
10119 self.buffer.update(cx, |multi_buffer, cx| {
10120 project.update(cx, |project, cx| {
10121 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
10122 });
10123 })
10124 }
10125 }
10126
10127 fn cancel_language_server_work(
10128 &mut self,
10129 _: &actions::CancelLanguageServerWork,
10130 cx: &mut ViewContext<Self>,
10131 ) {
10132 if let Some(project) = self.project.clone() {
10133 self.buffer.update(cx, |multi_buffer, cx| {
10134 project.update(cx, |project, cx| {
10135 project.cancel_language_server_work_for_buffers(multi_buffer.all_buffers(), cx);
10136 });
10137 })
10138 }
10139 }
10140
10141 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
10142 cx.show_character_palette();
10143 }
10144
10145 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
10146 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
10147 let buffer = self.buffer.read(cx).snapshot(cx);
10148 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
10149 let is_valid = buffer
10150 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
10151 .any(|entry| {
10152 entry.diagnostic.is_primary
10153 && !entry.range.is_empty()
10154 && entry.range.start == primary_range_start
10155 && entry.diagnostic.message == active_diagnostics.primary_message
10156 });
10157
10158 if is_valid != active_diagnostics.is_valid {
10159 active_diagnostics.is_valid = is_valid;
10160 let mut new_styles = HashMap::default();
10161 for (block_id, diagnostic) in &active_diagnostics.blocks {
10162 new_styles.insert(
10163 *block_id,
10164 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
10165 );
10166 }
10167 self.display_map.update(cx, |display_map, _cx| {
10168 display_map.replace_blocks(new_styles)
10169 });
10170 }
10171 }
10172 }
10173
10174 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
10175 self.dismiss_diagnostics(cx);
10176 let snapshot = self.snapshot(cx);
10177 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
10178 let buffer = self.buffer.read(cx).snapshot(cx);
10179
10180 let mut primary_range = None;
10181 let mut primary_message = None;
10182 let mut group_end = Point::zero();
10183 let diagnostic_group = buffer
10184 .diagnostic_group::<MultiBufferPoint>(group_id)
10185 .filter_map(|entry| {
10186 if snapshot.is_line_folded(MultiBufferRow(entry.range.start.row))
10187 && (entry.range.start.row == entry.range.end.row
10188 || snapshot.is_line_folded(MultiBufferRow(entry.range.end.row)))
10189 {
10190 return None;
10191 }
10192 if entry.range.end > group_end {
10193 group_end = entry.range.end;
10194 }
10195 if entry.diagnostic.is_primary {
10196 primary_range = Some(entry.range.clone());
10197 primary_message = Some(entry.diagnostic.message.clone());
10198 }
10199 Some(entry)
10200 })
10201 .collect::<Vec<_>>();
10202 let primary_range = primary_range?;
10203 let primary_message = primary_message?;
10204 let primary_range =
10205 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
10206
10207 let blocks = display_map
10208 .insert_blocks(
10209 diagnostic_group.iter().map(|entry| {
10210 let diagnostic = entry.diagnostic.clone();
10211 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
10212 BlockProperties {
10213 style: BlockStyle::Fixed,
10214 placement: BlockPlacement::Below(
10215 buffer.anchor_after(entry.range.start),
10216 ),
10217 height: message_height,
10218 render: diagnostic_block_renderer(diagnostic, None, true, true),
10219 priority: 0,
10220 }
10221 }),
10222 cx,
10223 )
10224 .into_iter()
10225 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
10226 .collect();
10227
10228 Some(ActiveDiagnosticGroup {
10229 primary_range,
10230 primary_message,
10231 group_id,
10232 blocks,
10233 is_valid: true,
10234 })
10235 });
10236 self.active_diagnostics.is_some()
10237 }
10238
10239 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
10240 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
10241 self.display_map.update(cx, |display_map, cx| {
10242 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
10243 });
10244 cx.notify();
10245 }
10246 }
10247
10248 pub fn set_selections_from_remote(
10249 &mut self,
10250 selections: Vec<Selection<Anchor>>,
10251 pending_selection: Option<Selection<Anchor>>,
10252 cx: &mut ViewContext<Self>,
10253 ) {
10254 let old_cursor_position = self.selections.newest_anchor().head();
10255 self.selections.change_with(cx, |s| {
10256 s.select_anchors(selections);
10257 if let Some(pending_selection) = pending_selection {
10258 s.set_pending(pending_selection, SelectMode::Character);
10259 } else {
10260 s.clear_pending();
10261 }
10262 });
10263 self.selections_did_change(false, &old_cursor_position, true, cx);
10264 }
10265
10266 fn push_to_selection_history(&mut self) {
10267 self.selection_history.push(SelectionHistoryEntry {
10268 selections: self.selections.disjoint_anchors(),
10269 select_next_state: self.select_next_state.clone(),
10270 select_prev_state: self.select_prev_state.clone(),
10271 add_selections_state: self.add_selections_state.clone(),
10272 });
10273 }
10274
10275 pub fn transact(
10276 &mut self,
10277 cx: &mut ViewContext<Self>,
10278 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
10279 ) -> Option<TransactionId> {
10280 self.start_transaction_at(Instant::now(), cx);
10281 update(self, cx);
10282 self.end_transaction_at(Instant::now(), cx)
10283 }
10284
10285 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
10286 self.end_selection(cx);
10287 if let Some(tx_id) = self
10288 .buffer
10289 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
10290 {
10291 self.selection_history
10292 .insert_transaction(tx_id, self.selections.disjoint_anchors());
10293 cx.emit(EditorEvent::TransactionBegun {
10294 transaction_id: tx_id,
10295 })
10296 }
10297 }
10298
10299 fn end_transaction_at(
10300 &mut self,
10301 now: Instant,
10302 cx: &mut ViewContext<Self>,
10303 ) -> Option<TransactionId> {
10304 if let Some(transaction_id) = self
10305 .buffer
10306 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
10307 {
10308 if let Some((_, end_selections)) =
10309 self.selection_history.transaction_mut(transaction_id)
10310 {
10311 *end_selections = Some(self.selections.disjoint_anchors());
10312 } else {
10313 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
10314 }
10315
10316 cx.emit(EditorEvent::Edited { transaction_id });
10317 Some(transaction_id)
10318 } else {
10319 None
10320 }
10321 }
10322
10323 pub fn toggle_fold(&mut self, _: &actions::ToggleFold, cx: &mut ViewContext<Self>) {
10324 let selection = self.selections.newest::<Point>(cx);
10325
10326 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10327 let range = if selection.is_empty() {
10328 let point = selection.head().to_display_point(&display_map);
10329 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
10330 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
10331 .to_point(&display_map);
10332 start..end
10333 } else {
10334 selection.range()
10335 };
10336 if display_map.folds_in_range(range).next().is_some() {
10337 self.unfold_lines(&Default::default(), cx)
10338 } else {
10339 self.fold(&Default::default(), cx)
10340 }
10341 }
10342
10343 pub fn toggle_fold_recursive(
10344 &mut self,
10345 _: &actions::ToggleFoldRecursive,
10346 cx: &mut ViewContext<Self>,
10347 ) {
10348 let selection = self.selections.newest::<Point>(cx);
10349
10350 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10351 let range = if selection.is_empty() {
10352 let point = selection.head().to_display_point(&display_map);
10353 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
10354 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
10355 .to_point(&display_map);
10356 start..end
10357 } else {
10358 selection.range()
10359 };
10360 if display_map.folds_in_range(range).next().is_some() {
10361 self.unfold_recursive(&Default::default(), cx)
10362 } else {
10363 self.fold_recursive(&Default::default(), cx)
10364 }
10365 }
10366
10367 pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
10368 let mut to_fold = Vec::new();
10369 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10370 let selections = self.selections.all_adjusted(cx);
10371
10372 for selection in selections {
10373 let range = selection.range().sorted();
10374 let buffer_start_row = range.start.row;
10375
10376 if range.start.row != range.end.row {
10377 let mut found = false;
10378 let mut row = range.start.row;
10379 while row <= range.end.row {
10380 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
10381 found = true;
10382 row = crease.range().end.row + 1;
10383 to_fold.push(crease);
10384 } else {
10385 row += 1
10386 }
10387 }
10388 if found {
10389 continue;
10390 }
10391 }
10392
10393 for row in (0..=range.start.row).rev() {
10394 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
10395 if crease.range().end.row >= buffer_start_row {
10396 to_fold.push(crease);
10397 if row <= range.start.row {
10398 break;
10399 }
10400 }
10401 }
10402 }
10403 }
10404
10405 self.fold_creases(to_fold, true, cx);
10406 }
10407
10408 fn fold_at_level(&mut self, fold_at: &FoldAtLevel, cx: &mut ViewContext<Self>) {
10409 if !self.buffer.read(cx).is_singleton() {
10410 return;
10411 }
10412
10413 let fold_at_level = fold_at.level;
10414 let snapshot = self.buffer.read(cx).snapshot(cx);
10415 let mut to_fold = Vec::new();
10416 let mut stack = vec![(0, snapshot.max_row().0, 1)];
10417
10418 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
10419 while start_row < end_row {
10420 match self
10421 .snapshot(cx)
10422 .crease_for_buffer_row(MultiBufferRow(start_row))
10423 {
10424 Some(crease) => {
10425 let nested_start_row = crease.range().start.row + 1;
10426 let nested_end_row = crease.range().end.row;
10427
10428 if current_level < fold_at_level {
10429 stack.push((nested_start_row, nested_end_row, current_level + 1));
10430 } else if current_level == fold_at_level {
10431 to_fold.push(crease);
10432 }
10433
10434 start_row = nested_end_row + 1;
10435 }
10436 None => start_row += 1,
10437 }
10438 }
10439 }
10440
10441 self.fold_creases(to_fold, true, cx);
10442 }
10443
10444 pub fn fold_all(&mut self, _: &actions::FoldAll, cx: &mut ViewContext<Self>) {
10445 if !self.buffer.read(cx).is_singleton() {
10446 return;
10447 }
10448
10449 let mut fold_ranges = Vec::new();
10450 let snapshot = self.buffer.read(cx).snapshot(cx);
10451
10452 for row in 0..snapshot.max_row().0 {
10453 if let Some(foldable_range) =
10454 self.snapshot(cx).crease_for_buffer_row(MultiBufferRow(row))
10455 {
10456 fold_ranges.push(foldable_range);
10457 }
10458 }
10459
10460 self.fold_creases(fold_ranges, true, cx);
10461 }
10462
10463 pub fn fold_function_bodies(
10464 &mut self,
10465 _: &actions::FoldFunctionBodies,
10466 cx: &mut ViewContext<Self>,
10467 ) {
10468 let snapshot = self.buffer.read(cx).snapshot(cx);
10469 let Some((_, _, buffer)) = snapshot.as_singleton() else {
10470 return;
10471 };
10472 let creases = buffer
10473 .function_body_fold_ranges(0..buffer.len())
10474 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
10475 .collect();
10476
10477 self.fold_creases(creases, true, cx);
10478 }
10479
10480 pub fn fold_recursive(&mut self, _: &actions::FoldRecursive, cx: &mut ViewContext<Self>) {
10481 let mut to_fold = Vec::new();
10482 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10483 let selections = self.selections.all_adjusted(cx);
10484
10485 for selection in selections {
10486 let range = selection.range().sorted();
10487 let buffer_start_row = range.start.row;
10488
10489 if range.start.row != range.end.row {
10490 let mut found = false;
10491 for row in range.start.row..=range.end.row {
10492 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
10493 found = true;
10494 to_fold.push(crease);
10495 }
10496 }
10497 if found {
10498 continue;
10499 }
10500 }
10501
10502 for row in (0..=range.start.row).rev() {
10503 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
10504 if crease.range().end.row >= buffer_start_row {
10505 to_fold.push(crease);
10506 } else {
10507 break;
10508 }
10509 }
10510 }
10511 }
10512
10513 self.fold_creases(to_fold, true, cx);
10514 }
10515
10516 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
10517 let buffer_row = fold_at.buffer_row;
10518 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10519
10520 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
10521 let autoscroll = self
10522 .selections
10523 .all::<Point>(cx)
10524 .iter()
10525 .any(|selection| crease.range().overlaps(&selection.range()));
10526
10527 self.fold_creases(vec![crease], autoscroll, cx);
10528 }
10529 }
10530
10531 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
10532 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10533 let buffer = &display_map.buffer_snapshot;
10534 let selections = self.selections.all::<Point>(cx);
10535 let ranges = selections
10536 .iter()
10537 .map(|s| {
10538 let range = s.display_range(&display_map).sorted();
10539 let mut start = range.start.to_point(&display_map);
10540 let mut end = range.end.to_point(&display_map);
10541 start.column = 0;
10542 end.column = buffer.line_len(MultiBufferRow(end.row));
10543 start..end
10544 })
10545 .collect::<Vec<_>>();
10546
10547 self.unfold_ranges(&ranges, true, true, cx);
10548 }
10549
10550 pub fn unfold_recursive(&mut self, _: &UnfoldRecursive, cx: &mut ViewContext<Self>) {
10551 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10552 let selections = self.selections.all::<Point>(cx);
10553 let ranges = selections
10554 .iter()
10555 .map(|s| {
10556 let mut range = s.display_range(&display_map).sorted();
10557 *range.start.column_mut() = 0;
10558 *range.end.column_mut() = display_map.line_len(range.end.row());
10559 let start = range.start.to_point(&display_map);
10560 let end = range.end.to_point(&display_map);
10561 start..end
10562 })
10563 .collect::<Vec<_>>();
10564
10565 self.unfold_ranges(&ranges, true, true, cx);
10566 }
10567
10568 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
10569 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10570
10571 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
10572 ..Point::new(
10573 unfold_at.buffer_row.0,
10574 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
10575 );
10576
10577 let autoscroll = self
10578 .selections
10579 .all::<Point>(cx)
10580 .iter()
10581 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
10582
10583 self.unfold_ranges(&[intersection_range], true, autoscroll, cx)
10584 }
10585
10586 pub fn unfold_all(&mut self, _: &actions::UnfoldAll, cx: &mut ViewContext<Self>) {
10587 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10588 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
10589 }
10590
10591 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
10592 let selections = self.selections.all::<Point>(cx);
10593 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10594 let line_mode = self.selections.line_mode;
10595 let ranges = selections
10596 .into_iter()
10597 .map(|s| {
10598 if line_mode {
10599 let start = Point::new(s.start.row, 0);
10600 let end = Point::new(
10601 s.end.row,
10602 display_map
10603 .buffer_snapshot
10604 .line_len(MultiBufferRow(s.end.row)),
10605 );
10606 Crease::simple(start..end, display_map.fold_placeholder.clone())
10607 } else {
10608 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
10609 }
10610 })
10611 .collect::<Vec<_>>();
10612 self.fold_creases(ranges, true, cx);
10613 }
10614
10615 pub fn fold_creases<T: ToOffset + Clone>(
10616 &mut self,
10617 creases: Vec<Crease<T>>,
10618 auto_scroll: bool,
10619 cx: &mut ViewContext<Self>,
10620 ) {
10621 if creases.is_empty() {
10622 return;
10623 }
10624
10625 let mut buffers_affected = HashSet::default();
10626 let multi_buffer = self.buffer().read(cx);
10627 for crease in &creases {
10628 if let Some((_, buffer, _)) =
10629 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
10630 {
10631 buffers_affected.insert(buffer.read(cx).remote_id());
10632 };
10633 }
10634
10635 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
10636
10637 if auto_scroll {
10638 self.request_autoscroll(Autoscroll::fit(), cx);
10639 }
10640
10641 for buffer_id in buffers_affected {
10642 Self::sync_expanded_diff_hunks(&mut self.diff_map, buffer_id, cx);
10643 }
10644
10645 cx.notify();
10646
10647 if let Some(active_diagnostics) = self.active_diagnostics.take() {
10648 // Clear diagnostics block when folding a range that contains it.
10649 let snapshot = self.snapshot(cx);
10650 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
10651 drop(snapshot);
10652 self.active_diagnostics = Some(active_diagnostics);
10653 self.dismiss_diagnostics(cx);
10654 } else {
10655 self.active_diagnostics = Some(active_diagnostics);
10656 }
10657 }
10658
10659 self.scrollbar_marker_state.dirty = true;
10660 }
10661
10662 /// Removes any folds whose ranges intersect any of the given ranges.
10663 pub fn unfold_ranges<T: ToOffset + Clone>(
10664 &mut self,
10665 ranges: &[Range<T>],
10666 inclusive: bool,
10667 auto_scroll: bool,
10668 cx: &mut ViewContext<Self>,
10669 ) {
10670 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
10671 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
10672 });
10673 }
10674
10675 /// Removes any folds with the given ranges.
10676 pub fn remove_folds_with_type<T: ToOffset + Clone>(
10677 &mut self,
10678 ranges: &[Range<T>],
10679 type_id: TypeId,
10680 auto_scroll: bool,
10681 cx: &mut ViewContext<Self>,
10682 ) {
10683 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
10684 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
10685 });
10686 }
10687
10688 fn remove_folds_with<T: ToOffset + Clone>(
10689 &mut self,
10690 ranges: &[Range<T>],
10691 auto_scroll: bool,
10692 cx: &mut ViewContext<Self>,
10693 update: impl FnOnce(&mut DisplayMap, &mut ModelContext<DisplayMap>),
10694 ) {
10695 if ranges.is_empty() {
10696 return;
10697 }
10698
10699 let mut buffers_affected = HashSet::default();
10700 let multi_buffer = self.buffer().read(cx);
10701 for range in ranges {
10702 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
10703 buffers_affected.insert(buffer.read(cx).remote_id());
10704 };
10705 }
10706
10707 self.display_map.update(cx, update);
10708
10709 if auto_scroll {
10710 self.request_autoscroll(Autoscroll::fit(), cx);
10711 }
10712
10713 for buffer_id in buffers_affected {
10714 Self::sync_expanded_diff_hunks(&mut self.diff_map, buffer_id, cx);
10715 }
10716
10717 cx.notify();
10718 self.scrollbar_marker_state.dirty = true;
10719 self.active_indent_guides_state.dirty = true;
10720 }
10721
10722 pub fn default_fold_placeholder(&self, cx: &AppContext) -> FoldPlaceholder {
10723 self.display_map.read(cx).fold_placeholder.clone()
10724 }
10725
10726 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
10727 if hovered != self.gutter_hovered {
10728 self.gutter_hovered = hovered;
10729 cx.notify();
10730 }
10731 }
10732
10733 pub fn insert_blocks(
10734 &mut self,
10735 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
10736 autoscroll: Option<Autoscroll>,
10737 cx: &mut ViewContext<Self>,
10738 ) -> Vec<CustomBlockId> {
10739 let blocks = self
10740 .display_map
10741 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
10742 if let Some(autoscroll) = autoscroll {
10743 self.request_autoscroll(autoscroll, cx);
10744 }
10745 cx.notify();
10746 blocks
10747 }
10748
10749 pub fn resize_blocks(
10750 &mut self,
10751 heights: HashMap<CustomBlockId, u32>,
10752 autoscroll: Option<Autoscroll>,
10753 cx: &mut ViewContext<Self>,
10754 ) {
10755 self.display_map
10756 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
10757 if let Some(autoscroll) = autoscroll {
10758 self.request_autoscroll(autoscroll, cx);
10759 }
10760 cx.notify();
10761 }
10762
10763 pub fn replace_blocks(
10764 &mut self,
10765 renderers: HashMap<CustomBlockId, RenderBlock>,
10766 autoscroll: Option<Autoscroll>,
10767 cx: &mut ViewContext<Self>,
10768 ) {
10769 self.display_map
10770 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
10771 if let Some(autoscroll) = autoscroll {
10772 self.request_autoscroll(autoscroll, cx);
10773 }
10774 cx.notify();
10775 }
10776
10777 pub fn remove_blocks(
10778 &mut self,
10779 block_ids: HashSet<CustomBlockId>,
10780 autoscroll: Option<Autoscroll>,
10781 cx: &mut ViewContext<Self>,
10782 ) {
10783 self.display_map.update(cx, |display_map, cx| {
10784 display_map.remove_blocks(block_ids, cx)
10785 });
10786 if let Some(autoscroll) = autoscroll {
10787 self.request_autoscroll(autoscroll, cx);
10788 }
10789 cx.notify();
10790 }
10791
10792 pub fn row_for_block(
10793 &self,
10794 block_id: CustomBlockId,
10795 cx: &mut ViewContext<Self>,
10796 ) -> Option<DisplayRow> {
10797 self.display_map
10798 .update(cx, |map, cx| map.row_for_block(block_id, cx))
10799 }
10800
10801 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
10802 self.focused_block = Some(focused_block);
10803 }
10804
10805 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
10806 self.focused_block.take()
10807 }
10808
10809 pub fn insert_creases(
10810 &mut self,
10811 creases: impl IntoIterator<Item = Crease<Anchor>>,
10812 cx: &mut ViewContext<Self>,
10813 ) -> Vec<CreaseId> {
10814 self.display_map
10815 .update(cx, |map, cx| map.insert_creases(creases, cx))
10816 }
10817
10818 pub fn remove_creases(
10819 &mut self,
10820 ids: impl IntoIterator<Item = CreaseId>,
10821 cx: &mut ViewContext<Self>,
10822 ) {
10823 self.display_map
10824 .update(cx, |map, cx| map.remove_creases(ids, cx));
10825 }
10826
10827 pub fn longest_row(&self, cx: &mut AppContext) -> DisplayRow {
10828 self.display_map
10829 .update(cx, |map, cx| map.snapshot(cx))
10830 .longest_row()
10831 }
10832
10833 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
10834 self.display_map
10835 .update(cx, |map, cx| map.snapshot(cx))
10836 .max_point()
10837 }
10838
10839 pub fn text(&self, cx: &AppContext) -> String {
10840 self.buffer.read(cx).read(cx).text()
10841 }
10842
10843 pub fn text_option(&self, cx: &AppContext) -> Option<String> {
10844 let text = self.text(cx);
10845 let text = text.trim();
10846
10847 if text.is_empty() {
10848 return None;
10849 }
10850
10851 Some(text.to_string())
10852 }
10853
10854 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
10855 self.transact(cx, |this, cx| {
10856 this.buffer
10857 .read(cx)
10858 .as_singleton()
10859 .expect("you can only call set_text on editors for singleton buffers")
10860 .update(cx, |buffer, cx| buffer.set_text(text, cx));
10861 });
10862 }
10863
10864 pub fn display_text(&self, cx: &mut AppContext) -> String {
10865 self.display_map
10866 .update(cx, |map, cx| map.snapshot(cx))
10867 .text()
10868 }
10869
10870 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
10871 let mut wrap_guides = smallvec::smallvec![];
10872
10873 if self.show_wrap_guides == Some(false) {
10874 return wrap_guides;
10875 }
10876
10877 let settings = self.buffer.read(cx).settings_at(0, cx);
10878 if settings.show_wrap_guides {
10879 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
10880 wrap_guides.push((soft_wrap as usize, true));
10881 } else if let SoftWrap::Bounded(soft_wrap) = self.soft_wrap_mode(cx) {
10882 wrap_guides.push((soft_wrap as usize, true));
10883 }
10884 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
10885 }
10886
10887 wrap_guides
10888 }
10889
10890 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
10891 let settings = self.buffer.read(cx).settings_at(0, cx);
10892 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
10893 match mode {
10894 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
10895 SoftWrap::None
10896 }
10897 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
10898 language_settings::SoftWrap::PreferredLineLength => {
10899 SoftWrap::Column(settings.preferred_line_length)
10900 }
10901 language_settings::SoftWrap::Bounded => {
10902 SoftWrap::Bounded(settings.preferred_line_length)
10903 }
10904 }
10905 }
10906
10907 pub fn set_soft_wrap_mode(
10908 &mut self,
10909 mode: language_settings::SoftWrap,
10910 cx: &mut ViewContext<Self>,
10911 ) {
10912 self.soft_wrap_mode_override = Some(mode);
10913 cx.notify();
10914 }
10915
10916 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
10917 self.text_style_refinement = Some(style);
10918 }
10919
10920 /// called by the Element so we know what style we were most recently rendered with.
10921 pub(crate) fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
10922 let rem_size = cx.rem_size();
10923 self.display_map.update(cx, |map, cx| {
10924 map.set_font(
10925 style.text.font(),
10926 style.text.font_size.to_pixels(rem_size),
10927 cx,
10928 )
10929 });
10930 self.style = Some(style);
10931 }
10932
10933 pub fn style(&self) -> Option<&EditorStyle> {
10934 self.style.as_ref()
10935 }
10936
10937 // Called by the element. This method is not designed to be called outside of the editor
10938 // element's layout code because it does not notify when rewrapping is computed synchronously.
10939 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
10940 self.display_map
10941 .update(cx, |map, cx| map.set_wrap_width(width, cx))
10942 }
10943
10944 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
10945 if self.soft_wrap_mode_override.is_some() {
10946 self.soft_wrap_mode_override.take();
10947 } else {
10948 let soft_wrap = match self.soft_wrap_mode(cx) {
10949 SoftWrap::GitDiff => return,
10950 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
10951 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
10952 language_settings::SoftWrap::None
10953 }
10954 };
10955 self.soft_wrap_mode_override = Some(soft_wrap);
10956 }
10957 cx.notify();
10958 }
10959
10960 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, cx: &mut ViewContext<Self>) {
10961 let Some(workspace) = self.workspace() else {
10962 return;
10963 };
10964 let fs = workspace.read(cx).app_state().fs.clone();
10965 let current_show = TabBarSettings::get_global(cx).show;
10966 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
10967 setting.show = Some(!current_show);
10968 });
10969 }
10970
10971 pub fn toggle_indent_guides(&mut self, _: &ToggleIndentGuides, cx: &mut ViewContext<Self>) {
10972 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
10973 self.buffer
10974 .read(cx)
10975 .settings_at(0, cx)
10976 .indent_guides
10977 .enabled
10978 });
10979 self.show_indent_guides = Some(!currently_enabled);
10980 cx.notify();
10981 }
10982
10983 fn should_show_indent_guides(&self) -> Option<bool> {
10984 self.show_indent_guides
10985 }
10986
10987 pub fn toggle_line_numbers(&mut self, _: &ToggleLineNumbers, cx: &mut ViewContext<Self>) {
10988 let mut editor_settings = EditorSettings::get_global(cx).clone();
10989 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
10990 EditorSettings::override_global(editor_settings, cx);
10991 }
10992
10993 pub fn should_use_relative_line_numbers(&self, cx: &WindowContext) -> bool {
10994 self.use_relative_line_numbers
10995 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
10996 }
10997
10998 pub fn toggle_relative_line_numbers(
10999 &mut self,
11000 _: &ToggleRelativeLineNumbers,
11001 cx: &mut ViewContext<Self>,
11002 ) {
11003 let is_relative = self.should_use_relative_line_numbers(cx);
11004 self.set_relative_line_number(Some(!is_relative), cx)
11005 }
11006
11007 pub fn set_relative_line_number(
11008 &mut self,
11009 is_relative: Option<bool>,
11010 cx: &mut ViewContext<Self>,
11011 ) {
11012 self.use_relative_line_numbers = is_relative;
11013 cx.notify();
11014 }
11015
11016 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
11017 self.show_gutter = show_gutter;
11018 cx.notify();
11019 }
11020
11021 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut ViewContext<Self>) {
11022 self.show_line_numbers = Some(show_line_numbers);
11023 cx.notify();
11024 }
11025
11026 pub fn set_show_git_diff_gutter(
11027 &mut self,
11028 show_git_diff_gutter: bool,
11029 cx: &mut ViewContext<Self>,
11030 ) {
11031 self.show_git_diff_gutter = Some(show_git_diff_gutter);
11032 cx.notify();
11033 }
11034
11035 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut ViewContext<Self>) {
11036 self.show_code_actions = Some(show_code_actions);
11037 cx.notify();
11038 }
11039
11040 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut ViewContext<Self>) {
11041 self.show_runnables = Some(show_runnables);
11042 cx.notify();
11043 }
11044
11045 pub fn set_masked(&mut self, masked: bool, cx: &mut ViewContext<Self>) {
11046 if self.display_map.read(cx).masked != masked {
11047 self.display_map.update(cx, |map, _| map.masked = masked);
11048 }
11049 cx.notify()
11050 }
11051
11052 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut ViewContext<Self>) {
11053 self.show_wrap_guides = Some(show_wrap_guides);
11054 cx.notify();
11055 }
11056
11057 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut ViewContext<Self>) {
11058 self.show_indent_guides = Some(show_indent_guides);
11059 cx.notify();
11060 }
11061
11062 pub fn working_directory(&self, cx: &WindowContext) -> Option<PathBuf> {
11063 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
11064 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
11065 if let Some(dir) = file.abs_path(cx).parent() {
11066 return Some(dir.to_owned());
11067 }
11068 }
11069
11070 if let Some(project_path) = buffer.read(cx).project_path(cx) {
11071 return Some(project_path.path.to_path_buf());
11072 }
11073 }
11074
11075 None
11076 }
11077
11078 fn target_file<'a>(&self, cx: &'a AppContext) -> Option<&'a dyn language::LocalFile> {
11079 self.active_excerpt(cx)?
11080 .1
11081 .read(cx)
11082 .file()
11083 .and_then(|f| f.as_local())
11084 }
11085
11086 pub fn reveal_in_finder(&mut self, _: &RevealInFileManager, cx: &mut ViewContext<Self>) {
11087 if let Some(target) = self.target_file(cx) {
11088 cx.reveal_path(&target.abs_path(cx));
11089 }
11090 }
11091
11092 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
11093 if let Some(file) = self.target_file(cx) {
11094 if let Some(path) = file.abs_path(cx).to_str() {
11095 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
11096 }
11097 }
11098 }
11099
11100 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
11101 if let Some(file) = self.target_file(cx) {
11102 if let Some(path) = file.path().to_str() {
11103 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
11104 }
11105 }
11106 }
11107
11108 pub fn toggle_git_blame(&mut self, _: &ToggleGitBlame, cx: &mut ViewContext<Self>) {
11109 self.show_git_blame_gutter = !self.show_git_blame_gutter;
11110
11111 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
11112 self.start_git_blame(true, cx);
11113 }
11114
11115 cx.notify();
11116 }
11117
11118 pub fn toggle_git_blame_inline(
11119 &mut self,
11120 _: &ToggleGitBlameInline,
11121 cx: &mut ViewContext<Self>,
11122 ) {
11123 self.toggle_git_blame_inline_internal(true, cx);
11124 cx.notify();
11125 }
11126
11127 pub fn git_blame_inline_enabled(&self) -> bool {
11128 self.git_blame_inline_enabled
11129 }
11130
11131 pub fn toggle_selection_menu(&mut self, _: &ToggleSelectionMenu, cx: &mut ViewContext<Self>) {
11132 self.show_selection_menu = self
11133 .show_selection_menu
11134 .map(|show_selections_menu| !show_selections_menu)
11135 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
11136
11137 cx.notify();
11138 }
11139
11140 pub fn selection_menu_enabled(&self, cx: &AppContext) -> bool {
11141 self.show_selection_menu
11142 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
11143 }
11144
11145 fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
11146 if let Some(project) = self.project.as_ref() {
11147 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
11148 return;
11149 };
11150
11151 if buffer.read(cx).file().is_none() {
11152 return;
11153 }
11154
11155 let focused = self.focus_handle(cx).contains_focused(cx);
11156
11157 let project = project.clone();
11158 let blame =
11159 cx.new_model(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
11160 self.blame_subscription = Some(cx.observe(&blame, |_, _, cx| cx.notify()));
11161 self.blame = Some(blame);
11162 }
11163 }
11164
11165 fn toggle_git_blame_inline_internal(
11166 &mut self,
11167 user_triggered: bool,
11168 cx: &mut ViewContext<Self>,
11169 ) {
11170 if self.git_blame_inline_enabled {
11171 self.git_blame_inline_enabled = false;
11172 self.show_git_blame_inline = false;
11173 self.show_git_blame_inline_delay_task.take();
11174 } else {
11175 self.git_blame_inline_enabled = true;
11176 self.start_git_blame_inline(user_triggered, cx);
11177 }
11178
11179 cx.notify();
11180 }
11181
11182 fn start_git_blame_inline(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
11183 self.start_git_blame(user_triggered, cx);
11184
11185 if ProjectSettings::get_global(cx)
11186 .git
11187 .inline_blame_delay()
11188 .is_some()
11189 {
11190 self.start_inline_blame_timer(cx);
11191 } else {
11192 self.show_git_blame_inline = true
11193 }
11194 }
11195
11196 pub fn blame(&self) -> Option<&Model<GitBlame>> {
11197 self.blame.as_ref()
11198 }
11199
11200 pub fn show_git_blame_gutter(&self) -> bool {
11201 self.show_git_blame_gutter
11202 }
11203
11204 pub fn render_git_blame_gutter(&mut self, cx: &mut WindowContext) -> bool {
11205 self.show_git_blame_gutter && self.has_blame_entries(cx)
11206 }
11207
11208 pub fn render_git_blame_inline(&mut self, cx: &mut WindowContext) -> bool {
11209 self.show_git_blame_inline
11210 && self.focus_handle.is_focused(cx)
11211 && !self.newest_selection_head_on_empty_line(cx)
11212 && self.has_blame_entries(cx)
11213 }
11214
11215 fn has_blame_entries(&self, cx: &mut WindowContext) -> bool {
11216 self.blame()
11217 .map_or(false, |blame| blame.read(cx).has_generated_entries())
11218 }
11219
11220 fn newest_selection_head_on_empty_line(&mut self, cx: &mut WindowContext) -> bool {
11221 let cursor_anchor = self.selections.newest_anchor().head();
11222
11223 let snapshot = self.buffer.read(cx).snapshot(cx);
11224 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
11225
11226 snapshot.line_len(buffer_row) == 0
11227 }
11228
11229 fn get_permalink_to_line(&mut self, cx: &mut ViewContext<Self>) -> Task<Result<url::Url>> {
11230 let buffer_and_selection = maybe!({
11231 let selection = self.selections.newest::<Point>(cx);
11232 let selection_range = selection.range();
11233
11234 let (buffer, selection) = if let Some(buffer) = self.buffer().read(cx).as_singleton() {
11235 (buffer, selection_range.start.row..selection_range.end.row)
11236 } else {
11237 let buffer_ranges = self
11238 .buffer()
11239 .read(cx)
11240 .range_to_buffer_ranges(selection_range, cx);
11241
11242 let (buffer, range, _) = if selection.reversed {
11243 buffer_ranges.first()
11244 } else {
11245 buffer_ranges.last()
11246 }?;
11247
11248 let snapshot = buffer.read(cx).snapshot();
11249 let selection = text::ToPoint::to_point(&range.start, &snapshot).row
11250 ..text::ToPoint::to_point(&range.end, &snapshot).row;
11251 (buffer.clone(), selection)
11252 };
11253
11254 Some((buffer, selection))
11255 });
11256
11257 let Some((buffer, selection)) = buffer_and_selection else {
11258 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
11259 };
11260
11261 let Some(project) = self.project.as_ref() else {
11262 return Task::ready(Err(anyhow!("editor does not have project")));
11263 };
11264
11265 project.update(cx, |project, cx| {
11266 project.get_permalink_to_line(&buffer, selection, cx)
11267 })
11268 }
11269
11270 pub fn copy_permalink_to_line(&mut self, _: &CopyPermalinkToLine, cx: &mut ViewContext<Self>) {
11271 let permalink_task = self.get_permalink_to_line(cx);
11272 let workspace = self.workspace();
11273
11274 cx.spawn(|_, mut cx| async move {
11275 match permalink_task.await {
11276 Ok(permalink) => {
11277 cx.update(|cx| {
11278 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
11279 })
11280 .ok();
11281 }
11282 Err(err) => {
11283 let message = format!("Failed to copy permalink: {err}");
11284
11285 Err::<(), anyhow::Error>(err).log_err();
11286
11287 if let Some(workspace) = workspace {
11288 workspace
11289 .update(&mut cx, |workspace, cx| {
11290 struct CopyPermalinkToLine;
11291
11292 workspace.show_toast(
11293 Toast::new(
11294 NotificationId::unique::<CopyPermalinkToLine>(),
11295 message,
11296 ),
11297 cx,
11298 )
11299 })
11300 .ok();
11301 }
11302 }
11303 }
11304 })
11305 .detach();
11306 }
11307
11308 pub fn copy_file_location(&mut self, _: &CopyFileLocation, cx: &mut ViewContext<Self>) {
11309 let selection = self.selections.newest::<Point>(cx).start.row + 1;
11310 if let Some(file) = self.target_file(cx) {
11311 if let Some(path) = file.path().to_str() {
11312 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
11313 }
11314 }
11315 }
11316
11317 pub fn open_permalink_to_line(&mut self, _: &OpenPermalinkToLine, cx: &mut ViewContext<Self>) {
11318 let permalink_task = self.get_permalink_to_line(cx);
11319 let workspace = self.workspace();
11320
11321 cx.spawn(|_, mut cx| async move {
11322 match permalink_task.await {
11323 Ok(permalink) => {
11324 cx.update(|cx| {
11325 cx.open_url(permalink.as_ref());
11326 })
11327 .ok();
11328 }
11329 Err(err) => {
11330 let message = format!("Failed to open permalink: {err}");
11331
11332 Err::<(), anyhow::Error>(err).log_err();
11333
11334 if let Some(workspace) = workspace {
11335 workspace
11336 .update(&mut cx, |workspace, cx| {
11337 struct OpenPermalinkToLine;
11338
11339 workspace.show_toast(
11340 Toast::new(
11341 NotificationId::unique::<OpenPermalinkToLine>(),
11342 message,
11343 ),
11344 cx,
11345 )
11346 })
11347 .ok();
11348 }
11349 }
11350 }
11351 })
11352 .detach();
11353 }
11354
11355 pub fn insert_uuid_v4(&mut self, _: &InsertUuidV4, cx: &mut ViewContext<Self>) {
11356 self.insert_uuid(UuidVersion::V4, cx);
11357 }
11358
11359 pub fn insert_uuid_v7(&mut self, _: &InsertUuidV7, cx: &mut ViewContext<Self>) {
11360 self.insert_uuid(UuidVersion::V7, cx);
11361 }
11362
11363 fn insert_uuid(&mut self, version: UuidVersion, cx: &mut ViewContext<Self>) {
11364 self.transact(cx, |this, cx| {
11365 let edits = this
11366 .selections
11367 .all::<Point>(cx)
11368 .into_iter()
11369 .map(|selection| {
11370 let uuid = match version {
11371 UuidVersion::V4 => uuid::Uuid::new_v4(),
11372 UuidVersion::V7 => uuid::Uuid::now_v7(),
11373 };
11374
11375 (selection.range(), uuid.to_string())
11376 });
11377 this.edit(edits, cx);
11378 this.refresh_inline_completion(true, false, cx);
11379 });
11380 }
11381
11382 /// Adds a row highlight for the given range. If a row has multiple highlights, the
11383 /// last highlight added will be used.
11384 ///
11385 /// If the range ends at the beginning of a line, then that line will not be highlighted.
11386 pub fn highlight_rows<T: 'static>(
11387 &mut self,
11388 range: Range<Anchor>,
11389 color: Hsla,
11390 should_autoscroll: bool,
11391 cx: &mut ViewContext<Self>,
11392 ) {
11393 let snapshot = self.buffer().read(cx).snapshot(cx);
11394 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
11395 let ix = row_highlights.binary_search_by(|highlight| {
11396 Ordering::Equal
11397 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
11398 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
11399 });
11400
11401 if let Err(mut ix) = ix {
11402 let index = post_inc(&mut self.highlight_order);
11403
11404 // If this range intersects with the preceding highlight, then merge it with
11405 // the preceding highlight. Otherwise insert a new highlight.
11406 let mut merged = false;
11407 if ix > 0 {
11408 let prev_highlight = &mut row_highlights[ix - 1];
11409 if prev_highlight
11410 .range
11411 .end
11412 .cmp(&range.start, &snapshot)
11413 .is_ge()
11414 {
11415 ix -= 1;
11416 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
11417 prev_highlight.range.end = range.end;
11418 }
11419 merged = true;
11420 prev_highlight.index = index;
11421 prev_highlight.color = color;
11422 prev_highlight.should_autoscroll = should_autoscroll;
11423 }
11424 }
11425
11426 if !merged {
11427 row_highlights.insert(
11428 ix,
11429 RowHighlight {
11430 range: range.clone(),
11431 index,
11432 color,
11433 should_autoscroll,
11434 },
11435 );
11436 }
11437
11438 // If any of the following highlights intersect with this one, merge them.
11439 while let Some(next_highlight) = row_highlights.get(ix + 1) {
11440 let highlight = &row_highlights[ix];
11441 if next_highlight
11442 .range
11443 .start
11444 .cmp(&highlight.range.end, &snapshot)
11445 .is_le()
11446 {
11447 if next_highlight
11448 .range
11449 .end
11450 .cmp(&highlight.range.end, &snapshot)
11451 .is_gt()
11452 {
11453 row_highlights[ix].range.end = next_highlight.range.end;
11454 }
11455 row_highlights.remove(ix + 1);
11456 } else {
11457 break;
11458 }
11459 }
11460 }
11461 }
11462
11463 /// Remove any highlighted row ranges of the given type that intersect the
11464 /// given ranges.
11465 pub fn remove_highlighted_rows<T: 'static>(
11466 &mut self,
11467 ranges_to_remove: Vec<Range<Anchor>>,
11468 cx: &mut ViewContext<Self>,
11469 ) {
11470 let snapshot = self.buffer().read(cx).snapshot(cx);
11471 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
11472 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
11473 row_highlights.retain(|highlight| {
11474 while let Some(range_to_remove) = ranges_to_remove.peek() {
11475 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
11476 Ordering::Less | Ordering::Equal => {
11477 ranges_to_remove.next();
11478 }
11479 Ordering::Greater => {
11480 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
11481 Ordering::Less | Ordering::Equal => {
11482 return false;
11483 }
11484 Ordering::Greater => break,
11485 }
11486 }
11487 }
11488 }
11489
11490 true
11491 })
11492 }
11493
11494 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
11495 pub fn clear_row_highlights<T: 'static>(&mut self) {
11496 self.highlighted_rows.remove(&TypeId::of::<T>());
11497 }
11498
11499 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
11500 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
11501 self.highlighted_rows
11502 .get(&TypeId::of::<T>())
11503 .map_or(&[] as &[_], |vec| vec.as_slice())
11504 .iter()
11505 .map(|highlight| (highlight.range.clone(), highlight.color))
11506 }
11507
11508 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
11509 /// Rerturns a map of display rows that are highlighted and their corresponding highlight color.
11510 /// Allows to ignore certain kinds of highlights.
11511 pub fn highlighted_display_rows(
11512 &mut self,
11513 cx: &mut WindowContext,
11514 ) -> BTreeMap<DisplayRow, Hsla> {
11515 let snapshot = self.snapshot(cx);
11516 let mut used_highlight_orders = HashMap::default();
11517 self.highlighted_rows
11518 .iter()
11519 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
11520 .fold(
11521 BTreeMap::<DisplayRow, Hsla>::new(),
11522 |mut unique_rows, highlight| {
11523 let start = highlight.range.start.to_display_point(&snapshot);
11524 let end = highlight.range.end.to_display_point(&snapshot);
11525 let start_row = start.row().0;
11526 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
11527 && end.column() == 0
11528 {
11529 end.row().0.saturating_sub(1)
11530 } else {
11531 end.row().0
11532 };
11533 for row in start_row..=end_row {
11534 let used_index =
11535 used_highlight_orders.entry(row).or_insert(highlight.index);
11536 if highlight.index >= *used_index {
11537 *used_index = highlight.index;
11538 unique_rows.insert(DisplayRow(row), highlight.color);
11539 }
11540 }
11541 unique_rows
11542 },
11543 )
11544 }
11545
11546 pub fn highlighted_display_row_for_autoscroll(
11547 &self,
11548 snapshot: &DisplaySnapshot,
11549 ) -> Option<DisplayRow> {
11550 self.highlighted_rows
11551 .values()
11552 .flat_map(|highlighted_rows| highlighted_rows.iter())
11553 .filter_map(|highlight| {
11554 if highlight.should_autoscroll {
11555 Some(highlight.range.start.to_display_point(snapshot).row())
11556 } else {
11557 None
11558 }
11559 })
11560 .min()
11561 }
11562
11563 pub fn set_search_within_ranges(
11564 &mut self,
11565 ranges: &[Range<Anchor>],
11566 cx: &mut ViewContext<Self>,
11567 ) {
11568 self.highlight_background::<SearchWithinRange>(
11569 ranges,
11570 |colors| colors.editor_document_highlight_read_background,
11571 cx,
11572 )
11573 }
11574
11575 pub fn set_breadcrumb_header(&mut self, new_header: String) {
11576 self.breadcrumb_header = Some(new_header);
11577 }
11578
11579 pub fn clear_search_within_ranges(&mut self, cx: &mut ViewContext<Self>) {
11580 self.clear_background_highlights::<SearchWithinRange>(cx);
11581 }
11582
11583 pub fn highlight_background<T: 'static>(
11584 &mut self,
11585 ranges: &[Range<Anchor>],
11586 color_fetcher: fn(&ThemeColors) -> Hsla,
11587 cx: &mut ViewContext<Self>,
11588 ) {
11589 self.background_highlights
11590 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
11591 self.scrollbar_marker_state.dirty = true;
11592 cx.notify();
11593 }
11594
11595 pub fn clear_background_highlights<T: 'static>(
11596 &mut self,
11597 cx: &mut ViewContext<Self>,
11598 ) -> Option<BackgroundHighlight> {
11599 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
11600 if !text_highlights.1.is_empty() {
11601 self.scrollbar_marker_state.dirty = true;
11602 cx.notify();
11603 }
11604 Some(text_highlights)
11605 }
11606
11607 pub fn highlight_gutter<T: 'static>(
11608 &mut self,
11609 ranges: &[Range<Anchor>],
11610 color_fetcher: fn(&AppContext) -> Hsla,
11611 cx: &mut ViewContext<Self>,
11612 ) {
11613 self.gutter_highlights
11614 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
11615 cx.notify();
11616 }
11617
11618 pub fn clear_gutter_highlights<T: 'static>(
11619 &mut self,
11620 cx: &mut ViewContext<Self>,
11621 ) -> Option<GutterHighlight> {
11622 cx.notify();
11623 self.gutter_highlights.remove(&TypeId::of::<T>())
11624 }
11625
11626 #[cfg(feature = "test-support")]
11627 pub fn all_text_background_highlights(
11628 &mut self,
11629 cx: &mut ViewContext<Self>,
11630 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11631 let snapshot = self.snapshot(cx);
11632 let buffer = &snapshot.buffer_snapshot;
11633 let start = buffer.anchor_before(0);
11634 let end = buffer.anchor_after(buffer.len());
11635 let theme = cx.theme().colors();
11636 self.background_highlights_in_range(start..end, &snapshot, theme)
11637 }
11638
11639 #[cfg(feature = "test-support")]
11640 pub fn search_background_highlights(
11641 &mut self,
11642 cx: &mut ViewContext<Self>,
11643 ) -> Vec<Range<Point>> {
11644 let snapshot = self.buffer().read(cx).snapshot(cx);
11645
11646 let highlights = self
11647 .background_highlights
11648 .get(&TypeId::of::<items::BufferSearchHighlights>());
11649
11650 if let Some((_color, ranges)) = highlights {
11651 ranges
11652 .iter()
11653 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
11654 .collect_vec()
11655 } else {
11656 vec![]
11657 }
11658 }
11659
11660 fn document_highlights_for_position<'a>(
11661 &'a self,
11662 position: Anchor,
11663 buffer: &'a MultiBufferSnapshot,
11664 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
11665 let read_highlights = self
11666 .background_highlights
11667 .get(&TypeId::of::<DocumentHighlightRead>())
11668 .map(|h| &h.1);
11669 let write_highlights = self
11670 .background_highlights
11671 .get(&TypeId::of::<DocumentHighlightWrite>())
11672 .map(|h| &h.1);
11673 let left_position = position.bias_left(buffer);
11674 let right_position = position.bias_right(buffer);
11675 read_highlights
11676 .into_iter()
11677 .chain(write_highlights)
11678 .flat_map(move |ranges| {
11679 let start_ix = match ranges.binary_search_by(|probe| {
11680 let cmp = probe.end.cmp(&left_position, buffer);
11681 if cmp.is_ge() {
11682 Ordering::Greater
11683 } else {
11684 Ordering::Less
11685 }
11686 }) {
11687 Ok(i) | Err(i) => i,
11688 };
11689
11690 ranges[start_ix..]
11691 .iter()
11692 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
11693 })
11694 }
11695
11696 pub fn has_background_highlights<T: 'static>(&self) -> bool {
11697 self.background_highlights
11698 .get(&TypeId::of::<T>())
11699 .map_or(false, |(_, highlights)| !highlights.is_empty())
11700 }
11701
11702 pub fn background_highlights_in_range(
11703 &self,
11704 search_range: Range<Anchor>,
11705 display_snapshot: &DisplaySnapshot,
11706 theme: &ThemeColors,
11707 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11708 let mut results = Vec::new();
11709 for (color_fetcher, ranges) in self.background_highlights.values() {
11710 let color = color_fetcher(theme);
11711 let start_ix = match ranges.binary_search_by(|probe| {
11712 let cmp = probe
11713 .end
11714 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11715 if cmp.is_gt() {
11716 Ordering::Greater
11717 } else {
11718 Ordering::Less
11719 }
11720 }) {
11721 Ok(i) | Err(i) => i,
11722 };
11723 for range in &ranges[start_ix..] {
11724 if range
11725 .start
11726 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11727 .is_ge()
11728 {
11729 break;
11730 }
11731
11732 let start = range.start.to_display_point(display_snapshot);
11733 let end = range.end.to_display_point(display_snapshot);
11734 results.push((start..end, color))
11735 }
11736 }
11737 results
11738 }
11739
11740 pub fn background_highlight_row_ranges<T: 'static>(
11741 &self,
11742 search_range: Range<Anchor>,
11743 display_snapshot: &DisplaySnapshot,
11744 count: usize,
11745 ) -> Vec<RangeInclusive<DisplayPoint>> {
11746 let mut results = Vec::new();
11747 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
11748 return vec![];
11749 };
11750
11751 let start_ix = match ranges.binary_search_by(|probe| {
11752 let cmp = probe
11753 .end
11754 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11755 if cmp.is_gt() {
11756 Ordering::Greater
11757 } else {
11758 Ordering::Less
11759 }
11760 }) {
11761 Ok(i) | Err(i) => i,
11762 };
11763 let mut push_region = |start: Option<Point>, end: Option<Point>| {
11764 if let (Some(start_display), Some(end_display)) = (start, end) {
11765 results.push(
11766 start_display.to_display_point(display_snapshot)
11767 ..=end_display.to_display_point(display_snapshot),
11768 );
11769 }
11770 };
11771 let mut start_row: Option<Point> = None;
11772 let mut end_row: Option<Point> = None;
11773 if ranges.len() > count {
11774 return Vec::new();
11775 }
11776 for range in &ranges[start_ix..] {
11777 if range
11778 .start
11779 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11780 .is_ge()
11781 {
11782 break;
11783 }
11784 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
11785 if let Some(current_row) = &end_row {
11786 if end.row == current_row.row {
11787 continue;
11788 }
11789 }
11790 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
11791 if start_row.is_none() {
11792 assert_eq!(end_row, None);
11793 start_row = Some(start);
11794 end_row = Some(end);
11795 continue;
11796 }
11797 if let Some(current_end) = end_row.as_mut() {
11798 if start.row > current_end.row + 1 {
11799 push_region(start_row, end_row);
11800 start_row = Some(start);
11801 end_row = Some(end);
11802 } else {
11803 // Merge two hunks.
11804 *current_end = end;
11805 }
11806 } else {
11807 unreachable!();
11808 }
11809 }
11810 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
11811 push_region(start_row, end_row);
11812 results
11813 }
11814
11815 pub fn gutter_highlights_in_range(
11816 &self,
11817 search_range: Range<Anchor>,
11818 display_snapshot: &DisplaySnapshot,
11819 cx: &AppContext,
11820 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11821 let mut results = Vec::new();
11822 for (color_fetcher, ranges) in self.gutter_highlights.values() {
11823 let color = color_fetcher(cx);
11824 let start_ix = match ranges.binary_search_by(|probe| {
11825 let cmp = probe
11826 .end
11827 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11828 if cmp.is_gt() {
11829 Ordering::Greater
11830 } else {
11831 Ordering::Less
11832 }
11833 }) {
11834 Ok(i) | Err(i) => i,
11835 };
11836 for range in &ranges[start_ix..] {
11837 if range
11838 .start
11839 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11840 .is_ge()
11841 {
11842 break;
11843 }
11844
11845 let start = range.start.to_display_point(display_snapshot);
11846 let end = range.end.to_display_point(display_snapshot);
11847 results.push((start..end, color))
11848 }
11849 }
11850 results
11851 }
11852
11853 /// Get the text ranges corresponding to the redaction query
11854 pub fn redacted_ranges(
11855 &self,
11856 search_range: Range<Anchor>,
11857 display_snapshot: &DisplaySnapshot,
11858 cx: &WindowContext,
11859 ) -> Vec<Range<DisplayPoint>> {
11860 display_snapshot
11861 .buffer_snapshot
11862 .redacted_ranges(search_range, |file| {
11863 if let Some(file) = file {
11864 file.is_private()
11865 && EditorSettings::get(
11866 Some(SettingsLocation {
11867 worktree_id: file.worktree_id(cx),
11868 path: file.path().as_ref(),
11869 }),
11870 cx,
11871 )
11872 .redact_private_values
11873 } else {
11874 false
11875 }
11876 })
11877 .map(|range| {
11878 range.start.to_display_point(display_snapshot)
11879 ..range.end.to_display_point(display_snapshot)
11880 })
11881 .collect()
11882 }
11883
11884 pub fn highlight_text<T: 'static>(
11885 &mut self,
11886 ranges: Vec<Range<Anchor>>,
11887 style: HighlightStyle,
11888 cx: &mut ViewContext<Self>,
11889 ) {
11890 self.display_map.update(cx, |map, _| {
11891 map.highlight_text(TypeId::of::<T>(), ranges, style)
11892 });
11893 cx.notify();
11894 }
11895
11896 pub(crate) fn highlight_inlays<T: 'static>(
11897 &mut self,
11898 highlights: Vec<InlayHighlight>,
11899 style: HighlightStyle,
11900 cx: &mut ViewContext<Self>,
11901 ) {
11902 self.display_map.update(cx, |map, _| {
11903 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
11904 });
11905 cx.notify();
11906 }
11907
11908 pub fn text_highlights<'a, T: 'static>(
11909 &'a self,
11910 cx: &'a AppContext,
11911 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
11912 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
11913 }
11914
11915 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
11916 let cleared = self
11917 .display_map
11918 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
11919 if cleared {
11920 cx.notify();
11921 }
11922 }
11923
11924 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
11925 (self.read_only(cx) || self.blink_manager.read(cx).visible())
11926 && self.focus_handle.is_focused(cx)
11927 }
11928
11929 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut ViewContext<Self>) {
11930 self.show_cursor_when_unfocused = is_enabled;
11931 cx.notify();
11932 }
11933
11934 pub fn lsp_store(&self, cx: &AppContext) -> Option<Model<LspStore>> {
11935 self.project
11936 .as_ref()
11937 .map(|project| project.read(cx).lsp_store())
11938 }
11939
11940 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
11941 cx.notify();
11942 }
11943
11944 fn on_buffer_event(
11945 &mut self,
11946 multibuffer: Model<MultiBuffer>,
11947 event: &multi_buffer::Event,
11948 cx: &mut ViewContext<Self>,
11949 ) {
11950 match event {
11951 multi_buffer::Event::Edited {
11952 singleton_buffer_edited,
11953 edited_buffer: buffer_edited,
11954 } => {
11955 self.scrollbar_marker_state.dirty = true;
11956 self.active_indent_guides_state.dirty = true;
11957 self.refresh_active_diagnostics(cx);
11958 self.refresh_code_actions(cx);
11959 if self.has_active_inline_completion() {
11960 self.update_visible_inline_completion(cx);
11961 }
11962 if let Some(buffer) = buffer_edited {
11963 let buffer_id = buffer.read(cx).remote_id();
11964 if !self.registered_buffers.contains_key(&buffer_id) {
11965 if let Some(lsp_store) = self.lsp_store(cx) {
11966 lsp_store.update(cx, |lsp_store, cx| {
11967 self.registered_buffers.insert(
11968 buffer_id,
11969 lsp_store.register_buffer_with_language_servers(&buffer, cx),
11970 );
11971 })
11972 }
11973 }
11974 }
11975 cx.emit(EditorEvent::BufferEdited);
11976 cx.emit(SearchEvent::MatchesInvalidated);
11977 if *singleton_buffer_edited {
11978 if let Some(project) = &self.project {
11979 let project = project.read(cx);
11980 #[allow(clippy::mutable_key_type)]
11981 let languages_affected = multibuffer
11982 .read(cx)
11983 .all_buffers()
11984 .into_iter()
11985 .filter_map(|buffer| {
11986 let buffer = buffer.read(cx);
11987 let language = buffer.language()?;
11988 if project.is_local()
11989 && project
11990 .language_servers_for_local_buffer(buffer, cx)
11991 .count()
11992 == 0
11993 {
11994 None
11995 } else {
11996 Some(language)
11997 }
11998 })
11999 .cloned()
12000 .collect::<HashSet<_>>();
12001 if !languages_affected.is_empty() {
12002 self.refresh_inlay_hints(
12003 InlayHintRefreshReason::BufferEdited(languages_affected),
12004 cx,
12005 );
12006 }
12007 }
12008 }
12009
12010 let Some(project) = &self.project else { return };
12011 let (telemetry, is_via_ssh) = {
12012 let project = project.read(cx);
12013 let telemetry = project.client().telemetry().clone();
12014 let is_via_ssh = project.is_via_ssh();
12015 (telemetry, is_via_ssh)
12016 };
12017 refresh_linked_ranges(self, cx);
12018 telemetry.log_edit_event("editor", is_via_ssh);
12019 }
12020 multi_buffer::Event::ExcerptsAdded {
12021 buffer,
12022 predecessor,
12023 excerpts,
12024 } => {
12025 self.tasks_update_task = Some(self.refresh_runnables(cx));
12026 let buffer_id = buffer.read(cx).remote_id();
12027 if !self.diff_map.diff_bases.contains_key(&buffer_id) {
12028 if let Some(project) = &self.project {
12029 get_unstaged_changes_for_buffers(project, [buffer.clone()], cx);
12030 }
12031 }
12032 cx.emit(EditorEvent::ExcerptsAdded {
12033 buffer: buffer.clone(),
12034 predecessor: *predecessor,
12035 excerpts: excerpts.clone(),
12036 });
12037 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
12038 }
12039 multi_buffer::Event::ExcerptsRemoved { ids } => {
12040 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
12041 let buffer = self.buffer.read(cx);
12042 self.registered_buffers
12043 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
12044 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
12045 }
12046 multi_buffer::Event::ExcerptsEdited { ids } => {
12047 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
12048 }
12049 multi_buffer::Event::ExcerptsExpanded { ids } => {
12050 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
12051 }
12052 multi_buffer::Event::Reparsed(buffer_id) => {
12053 self.tasks_update_task = Some(self.refresh_runnables(cx));
12054
12055 cx.emit(EditorEvent::Reparsed(*buffer_id));
12056 }
12057 multi_buffer::Event::LanguageChanged(buffer_id) => {
12058 linked_editing_ranges::refresh_linked_ranges(self, cx);
12059 cx.emit(EditorEvent::Reparsed(*buffer_id));
12060 cx.notify();
12061 }
12062 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
12063 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
12064 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
12065 cx.emit(EditorEvent::TitleChanged)
12066 }
12067 // multi_buffer::Event::DiffBaseChanged => {
12068 // self.scrollbar_marker_state.dirty = true;
12069 // cx.emit(EditorEvent::DiffBaseChanged);
12070 // cx.notify();
12071 // }
12072 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
12073 multi_buffer::Event::DiagnosticsUpdated => {
12074 self.refresh_active_diagnostics(cx);
12075 self.scrollbar_marker_state.dirty = true;
12076 cx.notify();
12077 }
12078 _ => {}
12079 };
12080 }
12081
12082 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
12083 cx.notify();
12084 }
12085
12086 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
12087 self.tasks_update_task = Some(self.refresh_runnables(cx));
12088 self.refresh_inline_completion(true, false, cx);
12089 self.refresh_inlay_hints(
12090 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
12091 self.selections.newest_anchor().head(),
12092 &self.buffer.read(cx).snapshot(cx),
12093 cx,
12094 )),
12095 cx,
12096 );
12097
12098 let old_cursor_shape = self.cursor_shape;
12099
12100 {
12101 let editor_settings = EditorSettings::get_global(cx);
12102 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
12103 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
12104 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
12105 }
12106
12107 if old_cursor_shape != self.cursor_shape {
12108 cx.emit(EditorEvent::CursorShapeChanged);
12109 }
12110
12111 let project_settings = ProjectSettings::get_global(cx);
12112 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
12113
12114 if self.mode == EditorMode::Full {
12115 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
12116 if self.git_blame_inline_enabled != inline_blame_enabled {
12117 self.toggle_git_blame_inline_internal(false, cx);
12118 }
12119 }
12120
12121 cx.notify();
12122 }
12123
12124 pub fn set_searchable(&mut self, searchable: bool) {
12125 self.searchable = searchable;
12126 }
12127
12128 pub fn searchable(&self) -> bool {
12129 self.searchable
12130 }
12131
12132 fn open_proposed_changes_editor(
12133 &mut self,
12134 _: &OpenProposedChangesEditor,
12135 cx: &mut ViewContext<Self>,
12136 ) {
12137 let Some(workspace) = self.workspace() else {
12138 cx.propagate();
12139 return;
12140 };
12141
12142 let selections = self.selections.all::<usize>(cx);
12143 let buffer = self.buffer.read(cx);
12144 let mut new_selections_by_buffer = HashMap::default();
12145 for selection in selections {
12146 for (buffer, range, _) in
12147 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
12148 {
12149 let mut range = range.to_point(buffer.read(cx));
12150 range.start.column = 0;
12151 range.end.column = buffer.read(cx).line_len(range.end.row);
12152 new_selections_by_buffer
12153 .entry(buffer)
12154 .or_insert(Vec::new())
12155 .push(range)
12156 }
12157 }
12158
12159 let proposed_changes_buffers = new_selections_by_buffer
12160 .into_iter()
12161 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
12162 .collect::<Vec<_>>();
12163 let proposed_changes_editor = cx.new_view(|cx| {
12164 ProposedChangesEditor::new(
12165 "Proposed changes",
12166 proposed_changes_buffers,
12167 self.project.clone(),
12168 cx,
12169 )
12170 });
12171
12172 cx.window_context().defer(move |cx| {
12173 workspace.update(cx, |workspace, cx| {
12174 workspace.active_pane().update(cx, |pane, cx| {
12175 pane.add_item(Box::new(proposed_changes_editor), true, true, None, cx);
12176 });
12177 });
12178 });
12179 }
12180
12181 pub fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext<Self>) {
12182 self.open_excerpts_common(None, true, cx)
12183 }
12184
12185 pub fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
12186 self.open_excerpts_common(None, false, cx)
12187 }
12188
12189 fn open_excerpts_common(
12190 &mut self,
12191 jump_data: Option<JumpData>,
12192 split: bool,
12193 cx: &mut ViewContext<Self>,
12194 ) {
12195 let Some(workspace) = self.workspace() else {
12196 cx.propagate();
12197 return;
12198 };
12199
12200 if self.buffer.read(cx).is_singleton() {
12201 cx.propagate();
12202 return;
12203 }
12204
12205 let mut new_selections_by_buffer = HashMap::default();
12206 match &jump_data {
12207 Some(jump_data) => {
12208 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12209 if let Some(buffer) = multi_buffer_snapshot
12210 .buffer_id_for_excerpt(jump_data.excerpt_id)
12211 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
12212 {
12213 let buffer_snapshot = buffer.read(cx).snapshot();
12214 let jump_to_point = if buffer_snapshot.can_resolve(&jump_data.anchor) {
12215 language::ToPoint::to_point(&jump_data.anchor, &buffer_snapshot)
12216 } else {
12217 buffer_snapshot.clip_point(jump_data.position, Bias::Left)
12218 };
12219 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
12220 new_selections_by_buffer.insert(
12221 buffer,
12222 (
12223 vec![jump_to_offset..jump_to_offset],
12224 Some(jump_data.line_offset_from_top),
12225 ),
12226 );
12227 }
12228 }
12229 None => {
12230 let selections = self.selections.all::<usize>(cx);
12231 let buffer = self.buffer.read(cx);
12232 for selection in selections {
12233 for (mut buffer_handle, mut range, _) in
12234 buffer.range_to_buffer_ranges(selection.range(), cx)
12235 {
12236 // When editing branch buffers, jump to the corresponding location
12237 // in their base buffer.
12238 let buffer = buffer_handle.read(cx);
12239 if let Some(base_buffer) = buffer.base_buffer() {
12240 range = buffer.range_to_version(range, &base_buffer.read(cx).version());
12241 buffer_handle = base_buffer;
12242 }
12243
12244 if selection.reversed {
12245 mem::swap(&mut range.start, &mut range.end);
12246 }
12247 new_selections_by_buffer
12248 .entry(buffer_handle)
12249 .or_insert((Vec::new(), None))
12250 .0
12251 .push(range)
12252 }
12253 }
12254 }
12255 }
12256
12257 if new_selections_by_buffer.is_empty() {
12258 return;
12259 }
12260
12261 // We defer the pane interaction because we ourselves are a workspace item
12262 // and activating a new item causes the pane to call a method on us reentrantly,
12263 // which panics if we're on the stack.
12264 cx.window_context().defer(move |cx| {
12265 workspace.update(cx, |workspace, cx| {
12266 let pane = if split {
12267 workspace.adjacent_pane(cx)
12268 } else {
12269 workspace.active_pane().clone()
12270 };
12271
12272 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
12273 let editor = buffer
12274 .read(cx)
12275 .file()
12276 .is_none()
12277 .then(|| {
12278 // Handle file-less buffers separately: those are not really the project items, so won't have a paroject path or entity id,
12279 // so `workspace.open_project_item` will never find them, always opening a new editor.
12280 // Instead, we try to activate the existing editor in the pane first.
12281 let (editor, pane_item_index) =
12282 pane.read(cx).items().enumerate().find_map(|(i, item)| {
12283 let editor = item.downcast::<Editor>()?;
12284 let singleton_buffer =
12285 editor.read(cx).buffer().read(cx).as_singleton()?;
12286 if singleton_buffer == buffer {
12287 Some((editor, i))
12288 } else {
12289 None
12290 }
12291 })?;
12292 pane.update(cx, |pane, cx| {
12293 pane.activate_item(pane_item_index, true, true, cx)
12294 });
12295 Some(editor)
12296 })
12297 .flatten()
12298 .unwrap_or_else(|| {
12299 workspace.open_project_item::<Self>(
12300 pane.clone(),
12301 buffer,
12302 true,
12303 true,
12304 cx,
12305 )
12306 });
12307
12308 editor.update(cx, |editor, cx| {
12309 let autoscroll = match scroll_offset {
12310 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
12311 None => Autoscroll::newest(),
12312 };
12313 let nav_history = editor.nav_history.take();
12314 editor.change_selections(Some(autoscroll), cx, |s| {
12315 s.select_ranges(ranges);
12316 });
12317 editor.nav_history = nav_history;
12318 });
12319 }
12320 })
12321 });
12322 }
12323
12324 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
12325 let snapshot = self.buffer.read(cx).read(cx);
12326 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
12327 Some(
12328 ranges
12329 .iter()
12330 .map(move |range| {
12331 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
12332 })
12333 .collect(),
12334 )
12335 }
12336
12337 fn selection_replacement_ranges(
12338 &self,
12339 range: Range<OffsetUtf16>,
12340 cx: &mut AppContext,
12341 ) -> Vec<Range<OffsetUtf16>> {
12342 let selections = self.selections.all::<OffsetUtf16>(cx);
12343 let newest_selection = selections
12344 .iter()
12345 .max_by_key(|selection| selection.id)
12346 .unwrap();
12347 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
12348 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
12349 let snapshot = self.buffer.read(cx).read(cx);
12350 selections
12351 .into_iter()
12352 .map(|mut selection| {
12353 selection.start.0 =
12354 (selection.start.0 as isize).saturating_add(start_delta) as usize;
12355 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
12356 snapshot.clip_offset_utf16(selection.start, Bias::Left)
12357 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
12358 })
12359 .collect()
12360 }
12361
12362 fn report_editor_event(
12363 &self,
12364 operation: &'static str,
12365 file_extension: Option<String>,
12366 cx: &AppContext,
12367 ) {
12368 if cfg!(any(test, feature = "test-support")) {
12369 return;
12370 }
12371
12372 let Some(project) = &self.project else { return };
12373
12374 // If None, we are in a file without an extension
12375 let file = self
12376 .buffer
12377 .read(cx)
12378 .as_singleton()
12379 .and_then(|b| b.read(cx).file());
12380 let file_extension = file_extension.or(file
12381 .as_ref()
12382 .and_then(|file| Path::new(file.file_name(cx)).extension())
12383 .and_then(|e| e.to_str())
12384 .map(|a| a.to_string()));
12385
12386 let vim_mode = cx
12387 .global::<SettingsStore>()
12388 .raw_user_settings()
12389 .get("vim_mode")
12390 == Some(&serde_json::Value::Bool(true));
12391
12392 let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
12393 == language::language_settings::InlineCompletionProvider::Copilot;
12394 let copilot_enabled_for_language = self
12395 .buffer
12396 .read(cx)
12397 .settings_at(0, cx)
12398 .show_inline_completions;
12399
12400 let project = project.read(cx);
12401 let telemetry = project.client().telemetry().clone();
12402 telemetry.report_editor_event(
12403 file_extension,
12404 vim_mode,
12405 operation,
12406 copilot_enabled,
12407 copilot_enabled_for_language,
12408 project.is_via_ssh(),
12409 )
12410 }
12411
12412 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
12413 /// with each line being an array of {text, highlight} objects.
12414 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
12415 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
12416 return;
12417 };
12418
12419 #[derive(Serialize)]
12420 struct Chunk<'a> {
12421 text: String,
12422 highlight: Option<&'a str>,
12423 }
12424
12425 let snapshot = buffer.read(cx).snapshot();
12426 let range = self
12427 .selected_text_range(false, cx)
12428 .and_then(|selection| {
12429 if selection.range.is_empty() {
12430 None
12431 } else {
12432 Some(selection.range)
12433 }
12434 })
12435 .unwrap_or_else(|| 0..snapshot.len());
12436
12437 let chunks = snapshot.chunks(range, true);
12438 let mut lines = Vec::new();
12439 let mut line: VecDeque<Chunk> = VecDeque::new();
12440
12441 let Some(style) = self.style.as_ref() else {
12442 return;
12443 };
12444
12445 for chunk in chunks {
12446 let highlight = chunk
12447 .syntax_highlight_id
12448 .and_then(|id| id.name(&style.syntax));
12449 let mut chunk_lines = chunk.text.split('\n').peekable();
12450 while let Some(text) = chunk_lines.next() {
12451 let mut merged_with_last_token = false;
12452 if let Some(last_token) = line.back_mut() {
12453 if last_token.highlight == highlight {
12454 last_token.text.push_str(text);
12455 merged_with_last_token = true;
12456 }
12457 }
12458
12459 if !merged_with_last_token {
12460 line.push_back(Chunk {
12461 text: text.into(),
12462 highlight,
12463 });
12464 }
12465
12466 if chunk_lines.peek().is_some() {
12467 if line.len() > 1 && line.front().unwrap().text.is_empty() {
12468 line.pop_front();
12469 }
12470 if line.len() > 1 && line.back().unwrap().text.is_empty() {
12471 line.pop_back();
12472 }
12473
12474 lines.push(mem::take(&mut line));
12475 }
12476 }
12477 }
12478
12479 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
12480 return;
12481 };
12482 cx.write_to_clipboard(ClipboardItem::new_string(lines));
12483 }
12484
12485 pub fn open_context_menu(&mut self, _: &OpenContextMenu, cx: &mut ViewContext<Self>) {
12486 self.request_autoscroll(Autoscroll::newest(), cx);
12487 let position = self.selections.newest_display(cx).start;
12488 mouse_context_menu::deploy_context_menu(self, None, position, cx);
12489 }
12490
12491 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
12492 &self.inlay_hint_cache
12493 }
12494
12495 pub fn replay_insert_event(
12496 &mut self,
12497 text: &str,
12498 relative_utf16_range: Option<Range<isize>>,
12499 cx: &mut ViewContext<Self>,
12500 ) {
12501 if !self.input_enabled {
12502 cx.emit(EditorEvent::InputIgnored { text: text.into() });
12503 return;
12504 }
12505 if let Some(relative_utf16_range) = relative_utf16_range {
12506 let selections = self.selections.all::<OffsetUtf16>(cx);
12507 self.change_selections(None, cx, |s| {
12508 let new_ranges = selections.into_iter().map(|range| {
12509 let start = OffsetUtf16(
12510 range
12511 .head()
12512 .0
12513 .saturating_add_signed(relative_utf16_range.start),
12514 );
12515 let end = OffsetUtf16(
12516 range
12517 .head()
12518 .0
12519 .saturating_add_signed(relative_utf16_range.end),
12520 );
12521 start..end
12522 });
12523 s.select_ranges(new_ranges);
12524 });
12525 }
12526
12527 self.handle_input(text, cx);
12528 }
12529
12530 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
12531 let Some(provider) = self.semantics_provider.as_ref() else {
12532 return false;
12533 };
12534
12535 let mut supports = false;
12536 self.buffer().read(cx).for_each_buffer(|buffer| {
12537 supports |= provider.supports_inlay_hints(buffer, cx);
12538 });
12539 supports
12540 }
12541
12542 pub fn focus(&self, cx: &mut WindowContext) {
12543 cx.focus(&self.focus_handle)
12544 }
12545
12546 pub fn is_focused(&self, cx: &WindowContext) -> bool {
12547 self.focus_handle.is_focused(cx)
12548 }
12549
12550 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
12551 cx.emit(EditorEvent::Focused);
12552
12553 if let Some(descendant) = self
12554 .last_focused_descendant
12555 .take()
12556 .and_then(|descendant| descendant.upgrade())
12557 {
12558 cx.focus(&descendant);
12559 } else {
12560 if let Some(blame) = self.blame.as_ref() {
12561 blame.update(cx, GitBlame::focus)
12562 }
12563
12564 self.blink_manager.update(cx, BlinkManager::enable);
12565 self.show_cursor_names(cx);
12566 self.buffer.update(cx, |buffer, cx| {
12567 buffer.finalize_last_transaction(cx);
12568 if self.leader_peer_id.is_none() {
12569 buffer.set_active_selections(
12570 &self.selections.disjoint_anchors(),
12571 self.selections.line_mode,
12572 self.cursor_shape,
12573 cx,
12574 );
12575 }
12576 });
12577 }
12578 }
12579
12580 fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
12581 cx.emit(EditorEvent::FocusedIn)
12582 }
12583
12584 fn handle_focus_out(&mut self, event: FocusOutEvent, _cx: &mut ViewContext<Self>) {
12585 if event.blurred != self.focus_handle {
12586 self.last_focused_descendant = Some(event.blurred);
12587 }
12588 }
12589
12590 pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
12591 self.blink_manager.update(cx, BlinkManager::disable);
12592 self.buffer
12593 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
12594
12595 if let Some(blame) = self.blame.as_ref() {
12596 blame.update(cx, GitBlame::blur)
12597 }
12598 if !self.hover_state.focused(cx) {
12599 hide_hover(self, cx);
12600 }
12601
12602 self.hide_context_menu(cx);
12603 cx.emit(EditorEvent::Blurred);
12604 cx.notify();
12605 }
12606
12607 pub fn register_action<A: Action>(
12608 &mut self,
12609 listener: impl Fn(&A, &mut WindowContext) + 'static,
12610 ) -> Subscription {
12611 let id = self.next_editor_action_id.post_inc();
12612 let listener = Arc::new(listener);
12613 self.editor_actions.borrow_mut().insert(
12614 id,
12615 Box::new(move |cx| {
12616 let cx = cx.window_context();
12617 let listener = listener.clone();
12618 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
12619 let action = action.downcast_ref().unwrap();
12620 if phase == DispatchPhase::Bubble {
12621 listener(action, cx)
12622 }
12623 })
12624 }),
12625 );
12626
12627 let editor_actions = self.editor_actions.clone();
12628 Subscription::new(move || {
12629 editor_actions.borrow_mut().remove(&id);
12630 })
12631 }
12632
12633 pub fn file_header_size(&self) -> u32 {
12634 FILE_HEADER_HEIGHT
12635 }
12636
12637 pub fn revert(
12638 &mut self,
12639 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
12640 cx: &mut ViewContext<Self>,
12641 ) {
12642 self.buffer().update(cx, |multi_buffer, cx| {
12643 for (buffer_id, changes) in revert_changes {
12644 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
12645 buffer.update(cx, |buffer, cx| {
12646 buffer.edit(
12647 changes.into_iter().map(|(range, text)| {
12648 (range, text.to_string().map(Arc::<str>::from))
12649 }),
12650 None,
12651 cx,
12652 );
12653 });
12654 }
12655 }
12656 });
12657 self.change_selections(None, cx, |selections| selections.refresh());
12658 }
12659
12660 pub fn to_pixel_point(
12661 &mut self,
12662 source: multi_buffer::Anchor,
12663 editor_snapshot: &EditorSnapshot,
12664 cx: &mut ViewContext<Self>,
12665 ) -> Option<gpui::Point<Pixels>> {
12666 let source_point = source.to_display_point(editor_snapshot);
12667 self.display_to_pixel_point(source_point, editor_snapshot, cx)
12668 }
12669
12670 pub fn display_to_pixel_point(
12671 &self,
12672 source: DisplayPoint,
12673 editor_snapshot: &EditorSnapshot,
12674 cx: &WindowContext,
12675 ) -> Option<gpui::Point<Pixels>> {
12676 let line_height = self.style()?.text.line_height_in_pixels(cx.rem_size());
12677 let text_layout_details = self.text_layout_details(cx);
12678 let scroll_top = text_layout_details
12679 .scroll_anchor
12680 .scroll_position(editor_snapshot)
12681 .y;
12682
12683 if source.row().as_f32() < scroll_top.floor() {
12684 return None;
12685 }
12686 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
12687 let source_y = line_height * (source.row().as_f32() - scroll_top);
12688 Some(gpui::Point::new(source_x, source_y))
12689 }
12690
12691 pub fn has_active_completions_menu(&self) -> bool {
12692 self.context_menu.read().as_ref().map_or(false, |menu| {
12693 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
12694 })
12695 }
12696
12697 pub fn register_addon<T: Addon>(&mut self, instance: T) {
12698 self.addons
12699 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
12700 }
12701
12702 pub fn unregister_addon<T: Addon>(&mut self) {
12703 self.addons.remove(&std::any::TypeId::of::<T>());
12704 }
12705
12706 pub fn addon<T: Addon>(&self) -> Option<&T> {
12707 let type_id = std::any::TypeId::of::<T>();
12708 self.addons
12709 .get(&type_id)
12710 .and_then(|item| item.to_any().downcast_ref::<T>())
12711 }
12712
12713 fn character_size(&self, cx: &mut ViewContext<Self>) -> gpui::Point<Pixels> {
12714 let text_layout_details = self.text_layout_details(cx);
12715 let style = &text_layout_details.editor_style;
12716 let font_id = cx.text_system().resolve_font(&style.text.font());
12717 let font_size = style.text.font_size.to_pixels(cx.rem_size());
12718 let line_height = style.text.line_height_in_pixels(cx.rem_size());
12719
12720 let em_width = cx
12721 .text_system()
12722 .typographic_bounds(font_id, font_size, 'm')
12723 .unwrap()
12724 .size
12725 .width;
12726
12727 gpui::Point::new(em_width, line_height)
12728 }
12729}
12730
12731fn get_unstaged_changes_for_buffers(
12732 project: &Model<Project>,
12733 buffers: impl IntoIterator<Item = Model<Buffer>>,
12734 cx: &mut ViewContext<Editor>,
12735) {
12736 let mut tasks = Vec::new();
12737 project.update(cx, |project, cx| {
12738 for buffer in buffers {
12739 tasks.push(project.open_unstaged_changes(buffer.clone(), cx))
12740 }
12741 });
12742 cx.spawn(|this, mut cx| async move {
12743 let change_sets = futures::future::join_all(tasks).await;
12744 this.update(&mut cx, |this, cx| {
12745 for change_set in change_sets {
12746 if let Some(change_set) = change_set.log_err() {
12747 this.diff_map.add_change_set(change_set, cx);
12748 }
12749 }
12750 })
12751 .ok();
12752 })
12753 .detach();
12754}
12755
12756fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
12757 let tab_size = tab_size.get() as usize;
12758 let mut width = offset;
12759
12760 for ch in text.chars() {
12761 width += if ch == '\t' {
12762 tab_size - (width % tab_size)
12763 } else {
12764 1
12765 };
12766 }
12767
12768 width - offset
12769}
12770
12771#[cfg(test)]
12772mod tests {
12773 use super::*;
12774
12775 #[test]
12776 fn test_string_size_with_expanded_tabs() {
12777 let nz = |val| NonZeroU32::new(val).unwrap();
12778 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
12779 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
12780 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
12781 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
12782 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
12783 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
12784 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
12785 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
12786 }
12787}
12788
12789/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
12790struct WordBreakingTokenizer<'a> {
12791 input: &'a str,
12792}
12793
12794impl<'a> WordBreakingTokenizer<'a> {
12795 fn new(input: &'a str) -> Self {
12796 Self { input }
12797 }
12798}
12799
12800fn is_char_ideographic(ch: char) -> bool {
12801 use unicode_script::Script::*;
12802 use unicode_script::UnicodeScript;
12803 matches!(ch.script(), Han | Tangut | Yi)
12804}
12805
12806fn is_grapheme_ideographic(text: &str) -> bool {
12807 text.chars().any(is_char_ideographic)
12808}
12809
12810fn is_grapheme_whitespace(text: &str) -> bool {
12811 text.chars().any(|x| x.is_whitespace())
12812}
12813
12814fn should_stay_with_preceding_ideograph(text: &str) -> bool {
12815 text.chars().next().map_or(false, |ch| {
12816 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
12817 })
12818}
12819
12820#[derive(PartialEq, Eq, Debug, Clone, Copy)]
12821struct WordBreakToken<'a> {
12822 token: &'a str,
12823 grapheme_len: usize,
12824 is_whitespace: bool,
12825}
12826
12827impl<'a> Iterator for WordBreakingTokenizer<'a> {
12828 /// Yields a span, the count of graphemes in the token, and whether it was
12829 /// whitespace. Note that it also breaks at word boundaries.
12830 type Item = WordBreakToken<'a>;
12831
12832 fn next(&mut self) -> Option<Self::Item> {
12833 use unicode_segmentation::UnicodeSegmentation;
12834 if self.input.is_empty() {
12835 return None;
12836 }
12837
12838 let mut iter = self.input.graphemes(true).peekable();
12839 let mut offset = 0;
12840 let mut graphemes = 0;
12841 if let Some(first_grapheme) = iter.next() {
12842 let is_whitespace = is_grapheme_whitespace(first_grapheme);
12843 offset += first_grapheme.len();
12844 graphemes += 1;
12845 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
12846 if let Some(grapheme) = iter.peek().copied() {
12847 if should_stay_with_preceding_ideograph(grapheme) {
12848 offset += grapheme.len();
12849 graphemes += 1;
12850 }
12851 }
12852 } else {
12853 let mut words = self.input[offset..].split_word_bound_indices().peekable();
12854 let mut next_word_bound = words.peek().copied();
12855 if next_word_bound.map_or(false, |(i, _)| i == 0) {
12856 next_word_bound = words.next();
12857 }
12858 while let Some(grapheme) = iter.peek().copied() {
12859 if next_word_bound.map_or(false, |(i, _)| i == offset) {
12860 break;
12861 };
12862 if is_grapheme_whitespace(grapheme) != is_whitespace {
12863 break;
12864 };
12865 offset += grapheme.len();
12866 graphemes += 1;
12867 iter.next();
12868 }
12869 }
12870 let token = &self.input[..offset];
12871 self.input = &self.input[offset..];
12872 if is_whitespace {
12873 Some(WordBreakToken {
12874 token: " ",
12875 grapheme_len: 1,
12876 is_whitespace: true,
12877 })
12878 } else {
12879 Some(WordBreakToken {
12880 token,
12881 grapheme_len: graphemes,
12882 is_whitespace: false,
12883 })
12884 }
12885 } else {
12886 None
12887 }
12888 }
12889}
12890
12891#[test]
12892fn test_word_breaking_tokenizer() {
12893 let tests: &[(&str, &[(&str, usize, bool)])] = &[
12894 ("", &[]),
12895 (" ", &[(" ", 1, true)]),
12896 ("Ʒ", &[("Ʒ", 1, false)]),
12897 ("Ǽ", &[("Ǽ", 1, false)]),
12898 ("⋑", &[("⋑", 1, false)]),
12899 ("⋑⋑", &[("⋑⋑", 2, false)]),
12900 (
12901 "原理,进而",
12902 &[
12903 ("原", 1, false),
12904 ("理,", 2, false),
12905 ("进", 1, false),
12906 ("而", 1, false),
12907 ],
12908 ),
12909 (
12910 "hello world",
12911 &[("hello", 5, false), (" ", 1, true), ("world", 5, false)],
12912 ),
12913 (
12914 "hello, world",
12915 &[("hello,", 6, false), (" ", 1, true), ("world", 5, false)],
12916 ),
12917 (
12918 " hello world",
12919 &[
12920 (" ", 1, true),
12921 ("hello", 5, false),
12922 (" ", 1, true),
12923 ("world", 5, false),
12924 ],
12925 ),
12926 (
12927 "这是什么 \n 钢笔",
12928 &[
12929 ("这", 1, false),
12930 ("是", 1, false),
12931 ("什", 1, false),
12932 ("么", 1, false),
12933 (" ", 1, true),
12934 ("钢", 1, false),
12935 ("笔", 1, false),
12936 ],
12937 ),
12938 (" mutton", &[(" ", 1, true), ("mutton", 6, false)]),
12939 ];
12940
12941 for (input, result) in tests {
12942 assert_eq!(
12943 WordBreakingTokenizer::new(input).collect::<Vec<_>>(),
12944 result
12945 .iter()
12946 .copied()
12947 .map(|(token, grapheme_len, is_whitespace)| WordBreakToken {
12948 token,
12949 grapheme_len,
12950 is_whitespace,
12951 })
12952 .collect::<Vec<_>>()
12953 );
12954 }
12955}
12956
12957fn wrap_with_prefix(
12958 line_prefix: String,
12959 unwrapped_text: String,
12960 wrap_column: usize,
12961 tab_size: NonZeroU32,
12962) -> String {
12963 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
12964 let mut wrapped_text = String::new();
12965 let mut current_line = line_prefix.clone();
12966
12967 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
12968 let mut current_line_len = line_prefix_len;
12969 for WordBreakToken {
12970 token,
12971 grapheme_len,
12972 is_whitespace,
12973 } in tokenizer
12974 {
12975 if current_line_len + grapheme_len > wrap_column && current_line_len != line_prefix_len {
12976 wrapped_text.push_str(current_line.trim_end());
12977 wrapped_text.push('\n');
12978 current_line.truncate(line_prefix.len());
12979 current_line_len = line_prefix_len;
12980 if !is_whitespace {
12981 current_line.push_str(token);
12982 current_line_len += grapheme_len;
12983 }
12984 } else if !is_whitespace {
12985 current_line.push_str(token);
12986 current_line_len += grapheme_len;
12987 } else if current_line_len != line_prefix_len {
12988 current_line.push(' ');
12989 current_line_len += 1;
12990 }
12991 }
12992
12993 if !current_line.is_empty() {
12994 wrapped_text.push_str(¤t_line);
12995 }
12996 wrapped_text
12997}
12998
12999#[test]
13000fn test_wrap_with_prefix() {
13001 assert_eq!(
13002 wrap_with_prefix(
13003 "# ".to_string(),
13004 "abcdefg".to_string(),
13005 4,
13006 NonZeroU32::new(4).unwrap()
13007 ),
13008 "# abcdefg"
13009 );
13010 assert_eq!(
13011 wrap_with_prefix(
13012 "".to_string(),
13013 "\thello world".to_string(),
13014 8,
13015 NonZeroU32::new(4).unwrap()
13016 ),
13017 "hello\nworld"
13018 );
13019 assert_eq!(
13020 wrap_with_prefix(
13021 "// ".to_string(),
13022 "xx \nyy zz aa bb cc".to_string(),
13023 12,
13024 NonZeroU32::new(4).unwrap()
13025 ),
13026 "// xx yy zz\n// aa bb cc"
13027 );
13028 assert_eq!(
13029 wrap_with_prefix(
13030 String::new(),
13031 "这是什么 \n 钢笔".to_string(),
13032 3,
13033 NonZeroU32::new(4).unwrap()
13034 ),
13035 "这是什\n么 钢\n笔"
13036 );
13037}
13038
13039fn hunks_for_selections(
13040 snapshot: &EditorSnapshot,
13041 selections: &[Selection<Point>],
13042) -> Vec<MultiBufferDiffHunk> {
13043 hunks_for_ranges(
13044 selections.iter().map(|selection| selection.range()),
13045 snapshot,
13046 )
13047}
13048
13049pub fn hunks_for_ranges(
13050 ranges: impl Iterator<Item = Range<Point>>,
13051 snapshot: &EditorSnapshot,
13052) -> Vec<MultiBufferDiffHunk> {
13053 let mut hunks = Vec::new();
13054 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
13055 HashMap::default();
13056 for query_range in ranges {
13057 let query_rows =
13058 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
13059 for hunk in snapshot.diff_map.diff_hunks_in_range(
13060 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
13061 &snapshot.buffer_snapshot,
13062 ) {
13063 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
13064 // when the caret is just above or just below the deleted hunk.
13065 let allow_adjacent = hunk_status(&hunk) == DiffHunkStatus::Removed;
13066 let related_to_selection = if allow_adjacent {
13067 hunk.row_range.overlaps(&query_rows)
13068 || hunk.row_range.start == query_rows.end
13069 || hunk.row_range.end == query_rows.start
13070 } else {
13071 hunk.row_range.overlaps(&query_rows)
13072 };
13073 if related_to_selection {
13074 if !processed_buffer_rows
13075 .entry(hunk.buffer_id)
13076 .or_default()
13077 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
13078 {
13079 continue;
13080 }
13081 hunks.push(hunk);
13082 }
13083 }
13084 }
13085
13086 hunks
13087}
13088
13089pub trait CollaborationHub {
13090 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
13091 fn user_participant_indices<'a>(
13092 &self,
13093 cx: &'a AppContext,
13094 ) -> &'a HashMap<u64, ParticipantIndex>;
13095 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
13096}
13097
13098impl CollaborationHub for Model<Project> {
13099 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
13100 self.read(cx).collaborators()
13101 }
13102
13103 fn user_participant_indices<'a>(
13104 &self,
13105 cx: &'a AppContext,
13106 ) -> &'a HashMap<u64, ParticipantIndex> {
13107 self.read(cx).user_store().read(cx).participant_indices()
13108 }
13109
13110 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
13111 let this = self.read(cx);
13112 let user_ids = this.collaborators().values().map(|c| c.user_id);
13113 this.user_store().read_with(cx, |user_store, cx| {
13114 user_store.participant_names(user_ids, cx)
13115 })
13116 }
13117}
13118
13119pub trait SemanticsProvider {
13120 fn hover(
13121 &self,
13122 buffer: &Model<Buffer>,
13123 position: text::Anchor,
13124 cx: &mut AppContext,
13125 ) -> Option<Task<Vec<project::Hover>>>;
13126
13127 fn inlay_hints(
13128 &self,
13129 buffer_handle: Model<Buffer>,
13130 range: Range<text::Anchor>,
13131 cx: &mut AppContext,
13132 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
13133
13134 fn resolve_inlay_hint(
13135 &self,
13136 hint: InlayHint,
13137 buffer_handle: Model<Buffer>,
13138 server_id: LanguageServerId,
13139 cx: &mut AppContext,
13140 ) -> Option<Task<anyhow::Result<InlayHint>>>;
13141
13142 fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool;
13143
13144 fn document_highlights(
13145 &self,
13146 buffer: &Model<Buffer>,
13147 position: text::Anchor,
13148 cx: &mut AppContext,
13149 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
13150
13151 fn definitions(
13152 &self,
13153 buffer: &Model<Buffer>,
13154 position: text::Anchor,
13155 kind: GotoDefinitionKind,
13156 cx: &mut AppContext,
13157 ) -> Option<Task<Result<Vec<LocationLink>>>>;
13158
13159 fn range_for_rename(
13160 &self,
13161 buffer: &Model<Buffer>,
13162 position: text::Anchor,
13163 cx: &mut AppContext,
13164 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
13165
13166 fn perform_rename(
13167 &self,
13168 buffer: &Model<Buffer>,
13169 position: text::Anchor,
13170 new_name: String,
13171 cx: &mut AppContext,
13172 ) -> Option<Task<Result<ProjectTransaction>>>;
13173}
13174
13175pub trait CompletionProvider {
13176 fn completions(
13177 &self,
13178 buffer: &Model<Buffer>,
13179 buffer_position: text::Anchor,
13180 trigger: CompletionContext,
13181 cx: &mut ViewContext<Editor>,
13182 ) -> Task<Result<Vec<Completion>>>;
13183
13184 fn resolve_completions(
13185 &self,
13186 buffer: Model<Buffer>,
13187 completion_indices: Vec<usize>,
13188 completions: Arc<RwLock<Box<[Completion]>>>,
13189 cx: &mut ViewContext<Editor>,
13190 ) -> Task<Result<bool>>;
13191
13192 fn apply_additional_edits_for_completion(
13193 &self,
13194 buffer: Model<Buffer>,
13195 completion: Completion,
13196 push_to_history: bool,
13197 cx: &mut ViewContext<Editor>,
13198 ) -> Task<Result<Option<language::Transaction>>>;
13199
13200 fn is_completion_trigger(
13201 &self,
13202 buffer: &Model<Buffer>,
13203 position: language::Anchor,
13204 text: &str,
13205 trigger_in_words: bool,
13206 cx: &mut ViewContext<Editor>,
13207 ) -> bool;
13208
13209 fn sort_completions(&self) -> bool {
13210 true
13211 }
13212}
13213
13214pub trait CodeActionProvider {
13215 fn code_actions(
13216 &self,
13217 buffer: &Model<Buffer>,
13218 range: Range<text::Anchor>,
13219 cx: &mut WindowContext,
13220 ) -> Task<Result<Vec<CodeAction>>>;
13221
13222 fn apply_code_action(
13223 &self,
13224 buffer_handle: Model<Buffer>,
13225 action: CodeAction,
13226 excerpt_id: ExcerptId,
13227 push_to_history: bool,
13228 cx: &mut WindowContext,
13229 ) -> Task<Result<ProjectTransaction>>;
13230}
13231
13232impl CodeActionProvider for Model<Project> {
13233 fn code_actions(
13234 &self,
13235 buffer: &Model<Buffer>,
13236 range: Range<text::Anchor>,
13237 cx: &mut WindowContext,
13238 ) -> Task<Result<Vec<CodeAction>>> {
13239 self.update(cx, |project, cx| {
13240 project.code_actions(buffer, range, None, cx)
13241 })
13242 }
13243
13244 fn apply_code_action(
13245 &self,
13246 buffer_handle: Model<Buffer>,
13247 action: CodeAction,
13248 _excerpt_id: ExcerptId,
13249 push_to_history: bool,
13250 cx: &mut WindowContext,
13251 ) -> Task<Result<ProjectTransaction>> {
13252 self.update(cx, |project, cx| {
13253 project.apply_code_action(buffer_handle, action, push_to_history, cx)
13254 })
13255 }
13256}
13257
13258fn snippet_completions(
13259 project: &Project,
13260 buffer: &Model<Buffer>,
13261 buffer_position: text::Anchor,
13262 cx: &mut AppContext,
13263) -> Task<Result<Vec<Completion>>> {
13264 let language = buffer.read(cx).language_at(buffer_position);
13265 let language_name = language.as_ref().map(|language| language.lsp_id());
13266 let snippet_store = project.snippets().read(cx);
13267 let snippets = snippet_store.snippets_for(language_name, cx);
13268
13269 if snippets.is_empty() {
13270 return Task::ready(Ok(vec![]));
13271 }
13272 let snapshot = buffer.read(cx).text_snapshot();
13273 let chars: String = snapshot
13274 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
13275 .collect();
13276
13277 let scope = language.map(|language| language.default_scope());
13278 let executor = cx.background_executor().clone();
13279
13280 cx.background_executor().spawn(async move {
13281 let classifier = CharClassifier::new(scope).for_completion(true);
13282 let mut last_word = chars
13283 .chars()
13284 .take_while(|c| classifier.is_word(*c))
13285 .collect::<String>();
13286 last_word = last_word.chars().rev().collect();
13287
13288 if last_word.is_empty() {
13289 return Ok(vec![]);
13290 }
13291
13292 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
13293 let to_lsp = |point: &text::Anchor| {
13294 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
13295 point_to_lsp(end)
13296 };
13297 let lsp_end = to_lsp(&buffer_position);
13298
13299 let candidates = snippets
13300 .iter()
13301 .enumerate()
13302 .flat_map(|(ix, snippet)| {
13303 snippet
13304 .prefix
13305 .iter()
13306 .map(move |prefix| StringMatchCandidate::new(ix, prefix.clone()))
13307 })
13308 .collect::<Vec<StringMatchCandidate>>();
13309
13310 let mut matches = fuzzy::match_strings(
13311 &candidates,
13312 &last_word,
13313 last_word.chars().any(|c| c.is_uppercase()),
13314 100,
13315 &Default::default(),
13316 executor,
13317 )
13318 .await;
13319
13320 // Remove all candidates where the query's start does not match the start of any word in the candidate
13321 if let Some(query_start) = last_word.chars().next() {
13322 matches.retain(|string_match| {
13323 split_words(&string_match.string).any(|word| {
13324 // Check that the first codepoint of the word as lowercase matches the first
13325 // codepoint of the query as lowercase
13326 word.chars()
13327 .flat_map(|codepoint| codepoint.to_lowercase())
13328 .zip(query_start.to_lowercase())
13329 .all(|(word_cp, query_cp)| word_cp == query_cp)
13330 })
13331 });
13332 }
13333
13334 let matched_strings = matches
13335 .into_iter()
13336 .map(|m| m.string)
13337 .collect::<HashSet<_>>();
13338
13339 let result: Vec<Completion> = snippets
13340 .into_iter()
13341 .filter_map(|snippet| {
13342 let matching_prefix = snippet
13343 .prefix
13344 .iter()
13345 .find(|prefix| matched_strings.contains(*prefix))?;
13346 let start = as_offset - last_word.len();
13347 let start = snapshot.anchor_before(start);
13348 let range = start..buffer_position;
13349 let lsp_start = to_lsp(&start);
13350 let lsp_range = lsp::Range {
13351 start: lsp_start,
13352 end: lsp_end,
13353 };
13354 Some(Completion {
13355 old_range: range,
13356 new_text: snippet.body.clone(),
13357 label: CodeLabel {
13358 text: matching_prefix.clone(),
13359 runs: vec![],
13360 filter_range: 0..matching_prefix.len(),
13361 },
13362 server_id: LanguageServerId(usize::MAX),
13363 documentation: snippet.description.clone().map(Documentation::SingleLine),
13364 lsp_completion: lsp::CompletionItem {
13365 label: snippet.prefix.first().unwrap().clone(),
13366 kind: Some(CompletionItemKind::SNIPPET),
13367 label_details: snippet.description.as_ref().map(|description| {
13368 lsp::CompletionItemLabelDetails {
13369 detail: Some(description.clone()),
13370 description: None,
13371 }
13372 }),
13373 insert_text_format: Some(InsertTextFormat::SNIPPET),
13374 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13375 lsp::InsertReplaceEdit {
13376 new_text: snippet.body.clone(),
13377 insert: lsp_range,
13378 replace: lsp_range,
13379 },
13380 )),
13381 filter_text: Some(snippet.body.clone()),
13382 sort_text: Some(char::MAX.to_string()),
13383 ..Default::default()
13384 },
13385 confirm: None,
13386 })
13387 })
13388 .collect();
13389
13390 Ok(result)
13391 })
13392}
13393
13394impl CompletionProvider for Model<Project> {
13395 fn completions(
13396 &self,
13397 buffer: &Model<Buffer>,
13398 buffer_position: text::Anchor,
13399 options: CompletionContext,
13400 cx: &mut ViewContext<Editor>,
13401 ) -> Task<Result<Vec<Completion>>> {
13402 self.update(cx, |project, cx| {
13403 let snippets = snippet_completions(project, buffer, buffer_position, cx);
13404 let project_completions = project.completions(buffer, buffer_position, options, cx);
13405 cx.background_executor().spawn(async move {
13406 let mut completions = project_completions.await?;
13407 let snippets_completions = snippets.await?;
13408 completions.extend(snippets_completions);
13409 Ok(completions)
13410 })
13411 })
13412 }
13413
13414 fn resolve_completions(
13415 &self,
13416 buffer: Model<Buffer>,
13417 completion_indices: Vec<usize>,
13418 completions: Arc<RwLock<Box<[Completion]>>>,
13419 cx: &mut ViewContext<Editor>,
13420 ) -> Task<Result<bool>> {
13421 self.update(cx, |project, cx| {
13422 project.resolve_completions(buffer, completion_indices, completions, cx)
13423 })
13424 }
13425
13426 fn apply_additional_edits_for_completion(
13427 &self,
13428 buffer: Model<Buffer>,
13429 completion: Completion,
13430 push_to_history: bool,
13431 cx: &mut ViewContext<Editor>,
13432 ) -> Task<Result<Option<language::Transaction>>> {
13433 self.update(cx, |project, cx| {
13434 project.apply_additional_edits_for_completion(buffer, completion, push_to_history, cx)
13435 })
13436 }
13437
13438 fn is_completion_trigger(
13439 &self,
13440 buffer: &Model<Buffer>,
13441 position: language::Anchor,
13442 text: &str,
13443 trigger_in_words: bool,
13444 cx: &mut ViewContext<Editor>,
13445 ) -> bool {
13446 let mut chars = text.chars();
13447 let char = if let Some(char) = chars.next() {
13448 char
13449 } else {
13450 return false;
13451 };
13452 if chars.next().is_some() {
13453 return false;
13454 }
13455
13456 let buffer = buffer.read(cx);
13457 let snapshot = buffer.snapshot();
13458 if !snapshot.settings_at(position, cx).show_completions_on_input {
13459 return false;
13460 }
13461 let classifier = snapshot.char_classifier_at(position).for_completion(true);
13462 if trigger_in_words && classifier.is_word(char) {
13463 return true;
13464 }
13465
13466 buffer.completion_triggers().contains(text)
13467 }
13468}
13469
13470impl SemanticsProvider for Model<Project> {
13471 fn hover(
13472 &self,
13473 buffer: &Model<Buffer>,
13474 position: text::Anchor,
13475 cx: &mut AppContext,
13476 ) -> Option<Task<Vec<project::Hover>>> {
13477 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
13478 }
13479
13480 fn document_highlights(
13481 &self,
13482 buffer: &Model<Buffer>,
13483 position: text::Anchor,
13484 cx: &mut AppContext,
13485 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
13486 Some(self.update(cx, |project, cx| {
13487 project.document_highlights(buffer, position, cx)
13488 }))
13489 }
13490
13491 fn definitions(
13492 &self,
13493 buffer: &Model<Buffer>,
13494 position: text::Anchor,
13495 kind: GotoDefinitionKind,
13496 cx: &mut AppContext,
13497 ) -> Option<Task<Result<Vec<LocationLink>>>> {
13498 Some(self.update(cx, |project, cx| match kind {
13499 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
13500 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
13501 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
13502 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
13503 }))
13504 }
13505
13506 fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool {
13507 // TODO: make this work for remote projects
13508 self.read(cx)
13509 .language_servers_for_local_buffer(buffer.read(cx), cx)
13510 .any(
13511 |(_, server)| match server.capabilities().inlay_hint_provider {
13512 Some(lsp::OneOf::Left(enabled)) => enabled,
13513 Some(lsp::OneOf::Right(_)) => true,
13514 None => false,
13515 },
13516 )
13517 }
13518
13519 fn inlay_hints(
13520 &self,
13521 buffer_handle: Model<Buffer>,
13522 range: Range<text::Anchor>,
13523 cx: &mut AppContext,
13524 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
13525 Some(self.update(cx, |project, cx| {
13526 project.inlay_hints(buffer_handle, range, cx)
13527 }))
13528 }
13529
13530 fn resolve_inlay_hint(
13531 &self,
13532 hint: InlayHint,
13533 buffer_handle: Model<Buffer>,
13534 server_id: LanguageServerId,
13535 cx: &mut AppContext,
13536 ) -> Option<Task<anyhow::Result<InlayHint>>> {
13537 Some(self.update(cx, |project, cx| {
13538 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
13539 }))
13540 }
13541
13542 fn range_for_rename(
13543 &self,
13544 buffer: &Model<Buffer>,
13545 position: text::Anchor,
13546 cx: &mut AppContext,
13547 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
13548 Some(self.update(cx, |project, cx| {
13549 project.prepare_rename(buffer.clone(), position, cx)
13550 }))
13551 }
13552
13553 fn perform_rename(
13554 &self,
13555 buffer: &Model<Buffer>,
13556 position: text::Anchor,
13557 new_name: String,
13558 cx: &mut AppContext,
13559 ) -> Option<Task<Result<ProjectTransaction>>> {
13560 Some(self.update(cx, |project, cx| {
13561 project.perform_rename(buffer.clone(), position, new_name, cx)
13562 }))
13563 }
13564}
13565
13566fn inlay_hint_settings(
13567 location: Anchor,
13568 snapshot: &MultiBufferSnapshot,
13569 cx: &mut ViewContext<'_, Editor>,
13570) -> InlayHintSettings {
13571 let file = snapshot.file_at(location);
13572 let language = snapshot.language_at(location).map(|l| l.name());
13573 language_settings(language, file, cx).inlay_hints
13574}
13575
13576fn consume_contiguous_rows(
13577 contiguous_row_selections: &mut Vec<Selection<Point>>,
13578 selection: &Selection<Point>,
13579 display_map: &DisplaySnapshot,
13580 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
13581) -> (MultiBufferRow, MultiBufferRow) {
13582 contiguous_row_selections.push(selection.clone());
13583 let start_row = MultiBufferRow(selection.start.row);
13584 let mut end_row = ending_row(selection, display_map);
13585
13586 while let Some(next_selection) = selections.peek() {
13587 if next_selection.start.row <= end_row.0 {
13588 end_row = ending_row(next_selection, display_map);
13589 contiguous_row_selections.push(selections.next().unwrap().clone());
13590 } else {
13591 break;
13592 }
13593 }
13594 (start_row, end_row)
13595}
13596
13597fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
13598 if next_selection.end.column > 0 || next_selection.is_empty() {
13599 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
13600 } else {
13601 MultiBufferRow(next_selection.end.row)
13602 }
13603}
13604
13605impl EditorSnapshot {
13606 pub fn remote_selections_in_range<'a>(
13607 &'a self,
13608 range: &'a Range<Anchor>,
13609 collaboration_hub: &dyn CollaborationHub,
13610 cx: &'a AppContext,
13611 ) -> impl 'a + Iterator<Item = RemoteSelection> {
13612 let participant_names = collaboration_hub.user_names(cx);
13613 let participant_indices = collaboration_hub.user_participant_indices(cx);
13614 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
13615 let collaborators_by_replica_id = collaborators_by_peer_id
13616 .iter()
13617 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
13618 .collect::<HashMap<_, _>>();
13619 self.buffer_snapshot
13620 .selections_in_range(range, false)
13621 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
13622 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
13623 let participant_index = participant_indices.get(&collaborator.user_id).copied();
13624 let user_name = participant_names.get(&collaborator.user_id).cloned();
13625 Some(RemoteSelection {
13626 replica_id,
13627 selection,
13628 cursor_shape,
13629 line_mode,
13630 participant_index,
13631 peer_id: collaborator.peer_id,
13632 user_name,
13633 })
13634 })
13635 }
13636
13637 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
13638 self.display_snapshot.buffer_snapshot.language_at(position)
13639 }
13640
13641 pub fn is_focused(&self) -> bool {
13642 self.is_focused
13643 }
13644
13645 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
13646 self.placeholder_text.as_ref()
13647 }
13648
13649 pub fn scroll_position(&self) -> gpui::Point<f32> {
13650 self.scroll_anchor.scroll_position(&self.display_snapshot)
13651 }
13652
13653 fn gutter_dimensions(
13654 &self,
13655 font_id: FontId,
13656 font_size: Pixels,
13657 em_width: Pixels,
13658 em_advance: Pixels,
13659 max_line_number_width: Pixels,
13660 cx: &AppContext,
13661 ) -> GutterDimensions {
13662 if !self.show_gutter {
13663 return GutterDimensions::default();
13664 }
13665 let descent = cx.text_system().descent(font_id, font_size);
13666
13667 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
13668 matches!(
13669 ProjectSettings::get_global(cx).git.git_gutter,
13670 Some(GitGutterSetting::TrackedFiles)
13671 )
13672 });
13673 let gutter_settings = EditorSettings::get_global(cx).gutter;
13674 let show_line_numbers = self
13675 .show_line_numbers
13676 .unwrap_or(gutter_settings.line_numbers);
13677 let line_gutter_width = if show_line_numbers {
13678 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
13679 let min_width_for_number_on_gutter = em_advance * 4.0;
13680 max_line_number_width.max(min_width_for_number_on_gutter)
13681 } else {
13682 0.0.into()
13683 };
13684
13685 let show_code_actions = self
13686 .show_code_actions
13687 .unwrap_or(gutter_settings.code_actions);
13688
13689 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
13690
13691 let git_blame_entries_width =
13692 self.git_blame_gutter_max_author_length
13693 .map(|max_author_length| {
13694 // Length of the author name, but also space for the commit hash,
13695 // the spacing and the timestamp.
13696 let max_char_count = max_author_length
13697 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
13698 + 7 // length of commit sha
13699 + 14 // length of max relative timestamp ("60 minutes ago")
13700 + 4; // gaps and margins
13701
13702 em_advance * max_char_count
13703 });
13704
13705 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
13706 left_padding += if show_code_actions || show_runnables {
13707 em_width * 3.0
13708 } else if show_git_gutter && show_line_numbers {
13709 em_width * 2.0
13710 } else if show_git_gutter || show_line_numbers {
13711 em_width
13712 } else {
13713 px(0.)
13714 };
13715
13716 let right_padding = if gutter_settings.folds && show_line_numbers {
13717 em_width * 4.0
13718 } else if gutter_settings.folds {
13719 em_width * 3.0
13720 } else if show_line_numbers {
13721 em_width
13722 } else {
13723 px(0.)
13724 };
13725
13726 GutterDimensions {
13727 left_padding,
13728 right_padding,
13729 width: line_gutter_width + left_padding + right_padding,
13730 margin: -descent,
13731 git_blame_entries_width,
13732 }
13733 }
13734
13735 pub fn render_crease_toggle(
13736 &self,
13737 buffer_row: MultiBufferRow,
13738 row_contains_cursor: bool,
13739 editor: View<Editor>,
13740 cx: &mut WindowContext,
13741 ) -> Option<AnyElement> {
13742 let folded = self.is_line_folded(buffer_row);
13743 let mut is_foldable = false;
13744
13745 if let Some(crease) = self
13746 .crease_snapshot
13747 .query_row(buffer_row, &self.buffer_snapshot)
13748 {
13749 is_foldable = true;
13750 match crease {
13751 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
13752 if let Some(render_toggle) = render_toggle {
13753 let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
13754 if folded {
13755 editor.update(cx, |editor, cx| {
13756 editor.fold_at(&crate::FoldAt { buffer_row }, cx)
13757 });
13758 } else {
13759 editor.update(cx, |editor, cx| {
13760 editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
13761 });
13762 }
13763 });
13764 return Some((render_toggle)(buffer_row, folded, toggle_callback, cx));
13765 }
13766 }
13767 }
13768 }
13769
13770 is_foldable |= self.starts_indent(buffer_row);
13771
13772 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
13773 Some(
13774 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
13775 .toggle_state(folded)
13776 .on_click(cx.listener_for(&editor, move |this, _e, cx| {
13777 if folded {
13778 this.unfold_at(&UnfoldAt { buffer_row }, cx);
13779 } else {
13780 this.fold_at(&FoldAt { buffer_row }, cx);
13781 }
13782 }))
13783 .into_any_element(),
13784 )
13785 } else {
13786 None
13787 }
13788 }
13789
13790 pub fn render_crease_trailer(
13791 &self,
13792 buffer_row: MultiBufferRow,
13793 cx: &mut WindowContext,
13794 ) -> Option<AnyElement> {
13795 let folded = self.is_line_folded(buffer_row);
13796 if let Crease::Inline { render_trailer, .. } = self
13797 .crease_snapshot
13798 .query_row(buffer_row, &self.buffer_snapshot)?
13799 {
13800 let render_trailer = render_trailer.as_ref()?;
13801 Some(render_trailer(buffer_row, folded, cx))
13802 } else {
13803 None
13804 }
13805 }
13806}
13807
13808impl Deref for EditorSnapshot {
13809 type Target = DisplaySnapshot;
13810
13811 fn deref(&self) -> &Self::Target {
13812 &self.display_snapshot
13813 }
13814}
13815
13816#[derive(Clone, Debug, PartialEq, Eq)]
13817pub enum EditorEvent {
13818 InputIgnored {
13819 text: Arc<str>,
13820 },
13821 InputHandled {
13822 utf16_range_to_replace: Option<Range<isize>>,
13823 text: Arc<str>,
13824 },
13825 ExcerptsAdded {
13826 buffer: Model<Buffer>,
13827 predecessor: ExcerptId,
13828 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
13829 },
13830 ExcerptsRemoved {
13831 ids: Vec<ExcerptId>,
13832 },
13833 ExcerptsEdited {
13834 ids: Vec<ExcerptId>,
13835 },
13836 ExcerptsExpanded {
13837 ids: Vec<ExcerptId>,
13838 },
13839 BufferEdited,
13840 Edited {
13841 transaction_id: clock::Lamport,
13842 },
13843 Reparsed(BufferId),
13844 Focused,
13845 FocusedIn,
13846 Blurred,
13847 DirtyChanged,
13848 Saved,
13849 TitleChanged,
13850 DiffBaseChanged,
13851 SelectionsChanged {
13852 local: bool,
13853 },
13854 ScrollPositionChanged {
13855 local: bool,
13856 autoscroll: bool,
13857 },
13858 Closed,
13859 TransactionUndone {
13860 transaction_id: clock::Lamport,
13861 },
13862 TransactionBegun {
13863 transaction_id: clock::Lamport,
13864 },
13865 Reloaded,
13866 CursorShapeChanged,
13867}
13868
13869impl EventEmitter<EditorEvent> for Editor {}
13870
13871impl FocusableView for Editor {
13872 fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
13873 self.focus_handle.clone()
13874 }
13875}
13876
13877impl Render for Editor {
13878 fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
13879 let settings = ThemeSettings::get_global(cx);
13880
13881 let mut text_style = match self.mode {
13882 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
13883 color: cx.theme().colors().editor_foreground,
13884 font_family: settings.ui_font.family.clone(),
13885 font_features: settings.ui_font.features.clone(),
13886 font_fallbacks: settings.ui_font.fallbacks.clone(),
13887 font_size: rems(0.875).into(),
13888 font_weight: settings.ui_font.weight,
13889 line_height: relative(settings.buffer_line_height.value()),
13890 ..Default::default()
13891 },
13892 EditorMode::Full => TextStyle {
13893 color: cx.theme().colors().editor_foreground,
13894 font_family: settings.buffer_font.family.clone(),
13895 font_features: settings.buffer_font.features.clone(),
13896 font_fallbacks: settings.buffer_font.fallbacks.clone(),
13897 font_size: settings.buffer_font_size(cx).into(),
13898 font_weight: settings.buffer_font.weight,
13899 line_height: relative(settings.buffer_line_height.value()),
13900 ..Default::default()
13901 },
13902 };
13903 if let Some(text_style_refinement) = &self.text_style_refinement {
13904 text_style.refine(text_style_refinement)
13905 }
13906
13907 let background = match self.mode {
13908 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
13909 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
13910 EditorMode::Full => cx.theme().colors().editor_background,
13911 };
13912
13913 EditorElement::new(
13914 cx.view(),
13915 EditorStyle {
13916 background,
13917 local_player: cx.theme().players().local(),
13918 text: text_style,
13919 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
13920 syntax: cx.theme().syntax().clone(),
13921 status: cx.theme().status().clone(),
13922 inlay_hints_style: make_inlay_hints_style(cx),
13923 inline_completion_styles: make_suggestion_styles(cx),
13924 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
13925 },
13926 )
13927 }
13928}
13929
13930impl ViewInputHandler for Editor {
13931 fn text_for_range(
13932 &mut self,
13933 range_utf16: Range<usize>,
13934 adjusted_range: &mut Option<Range<usize>>,
13935 cx: &mut ViewContext<Self>,
13936 ) -> Option<String> {
13937 let snapshot = self.buffer.read(cx).read(cx);
13938 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
13939 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
13940 if (start.0..end.0) != range_utf16 {
13941 adjusted_range.replace(start.0..end.0);
13942 }
13943 Some(snapshot.text_for_range(start..end).collect())
13944 }
13945
13946 fn selected_text_range(
13947 &mut self,
13948 ignore_disabled_input: bool,
13949 cx: &mut ViewContext<Self>,
13950 ) -> Option<UTF16Selection> {
13951 // Prevent the IME menu from appearing when holding down an alphabetic key
13952 // while input is disabled.
13953 if !ignore_disabled_input && !self.input_enabled {
13954 return None;
13955 }
13956
13957 let selection = self.selections.newest::<OffsetUtf16>(cx);
13958 let range = selection.range();
13959
13960 Some(UTF16Selection {
13961 range: range.start.0..range.end.0,
13962 reversed: selection.reversed,
13963 })
13964 }
13965
13966 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
13967 let snapshot = self.buffer.read(cx).read(cx);
13968 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
13969 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
13970 }
13971
13972 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
13973 self.clear_highlights::<InputComposition>(cx);
13974 self.ime_transaction.take();
13975 }
13976
13977 fn replace_text_in_range(
13978 &mut self,
13979 range_utf16: Option<Range<usize>>,
13980 text: &str,
13981 cx: &mut ViewContext<Self>,
13982 ) {
13983 if !self.input_enabled {
13984 cx.emit(EditorEvent::InputIgnored { text: text.into() });
13985 return;
13986 }
13987
13988 self.transact(cx, |this, cx| {
13989 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
13990 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
13991 Some(this.selection_replacement_ranges(range_utf16, cx))
13992 } else {
13993 this.marked_text_ranges(cx)
13994 };
13995
13996 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
13997 let newest_selection_id = this.selections.newest_anchor().id;
13998 this.selections
13999 .all::<OffsetUtf16>(cx)
14000 .iter()
14001 .zip(ranges_to_replace.iter())
14002 .find_map(|(selection, range)| {
14003 if selection.id == newest_selection_id {
14004 Some(
14005 (range.start.0 as isize - selection.head().0 as isize)
14006 ..(range.end.0 as isize - selection.head().0 as isize),
14007 )
14008 } else {
14009 None
14010 }
14011 })
14012 });
14013
14014 cx.emit(EditorEvent::InputHandled {
14015 utf16_range_to_replace: range_to_replace,
14016 text: text.into(),
14017 });
14018
14019 if let Some(new_selected_ranges) = new_selected_ranges {
14020 this.change_selections(None, cx, |selections| {
14021 selections.select_ranges(new_selected_ranges)
14022 });
14023 this.backspace(&Default::default(), cx);
14024 }
14025
14026 this.handle_input(text, cx);
14027 });
14028
14029 if let Some(transaction) = self.ime_transaction {
14030 self.buffer.update(cx, |buffer, cx| {
14031 buffer.group_until_transaction(transaction, cx);
14032 });
14033 }
14034
14035 self.unmark_text(cx);
14036 }
14037
14038 fn replace_and_mark_text_in_range(
14039 &mut self,
14040 range_utf16: Option<Range<usize>>,
14041 text: &str,
14042 new_selected_range_utf16: Option<Range<usize>>,
14043 cx: &mut ViewContext<Self>,
14044 ) {
14045 if !self.input_enabled {
14046 return;
14047 }
14048
14049 let transaction = self.transact(cx, |this, cx| {
14050 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
14051 let snapshot = this.buffer.read(cx).read(cx);
14052 if let Some(relative_range_utf16) = range_utf16.as_ref() {
14053 for marked_range in &mut marked_ranges {
14054 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
14055 marked_range.start.0 += relative_range_utf16.start;
14056 marked_range.start =
14057 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
14058 marked_range.end =
14059 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
14060 }
14061 }
14062 Some(marked_ranges)
14063 } else if let Some(range_utf16) = range_utf16 {
14064 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
14065 Some(this.selection_replacement_ranges(range_utf16, cx))
14066 } else {
14067 None
14068 };
14069
14070 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
14071 let newest_selection_id = this.selections.newest_anchor().id;
14072 this.selections
14073 .all::<OffsetUtf16>(cx)
14074 .iter()
14075 .zip(ranges_to_replace.iter())
14076 .find_map(|(selection, range)| {
14077 if selection.id == newest_selection_id {
14078 Some(
14079 (range.start.0 as isize - selection.head().0 as isize)
14080 ..(range.end.0 as isize - selection.head().0 as isize),
14081 )
14082 } else {
14083 None
14084 }
14085 })
14086 });
14087
14088 cx.emit(EditorEvent::InputHandled {
14089 utf16_range_to_replace: range_to_replace,
14090 text: text.into(),
14091 });
14092
14093 if let Some(ranges) = ranges_to_replace {
14094 this.change_selections(None, cx, |s| s.select_ranges(ranges));
14095 }
14096
14097 let marked_ranges = {
14098 let snapshot = this.buffer.read(cx).read(cx);
14099 this.selections
14100 .disjoint_anchors()
14101 .iter()
14102 .map(|selection| {
14103 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
14104 })
14105 .collect::<Vec<_>>()
14106 };
14107
14108 if text.is_empty() {
14109 this.unmark_text(cx);
14110 } else {
14111 this.highlight_text::<InputComposition>(
14112 marked_ranges.clone(),
14113 HighlightStyle {
14114 underline: Some(UnderlineStyle {
14115 thickness: px(1.),
14116 color: None,
14117 wavy: false,
14118 }),
14119 ..Default::default()
14120 },
14121 cx,
14122 );
14123 }
14124
14125 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
14126 let use_autoclose = this.use_autoclose;
14127 let use_auto_surround = this.use_auto_surround;
14128 this.set_use_autoclose(false);
14129 this.set_use_auto_surround(false);
14130 this.handle_input(text, cx);
14131 this.set_use_autoclose(use_autoclose);
14132 this.set_use_auto_surround(use_auto_surround);
14133
14134 if let Some(new_selected_range) = new_selected_range_utf16 {
14135 let snapshot = this.buffer.read(cx).read(cx);
14136 let new_selected_ranges = marked_ranges
14137 .into_iter()
14138 .map(|marked_range| {
14139 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
14140 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
14141 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
14142 snapshot.clip_offset_utf16(new_start, Bias::Left)
14143 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
14144 })
14145 .collect::<Vec<_>>();
14146
14147 drop(snapshot);
14148 this.change_selections(None, cx, |selections| {
14149 selections.select_ranges(new_selected_ranges)
14150 });
14151 }
14152 });
14153
14154 self.ime_transaction = self.ime_transaction.or(transaction);
14155 if let Some(transaction) = self.ime_transaction {
14156 self.buffer.update(cx, |buffer, cx| {
14157 buffer.group_until_transaction(transaction, cx);
14158 });
14159 }
14160
14161 if self.text_highlights::<InputComposition>(cx).is_none() {
14162 self.ime_transaction.take();
14163 }
14164 }
14165
14166 fn bounds_for_range(
14167 &mut self,
14168 range_utf16: Range<usize>,
14169 element_bounds: gpui::Bounds<Pixels>,
14170 cx: &mut ViewContext<Self>,
14171 ) -> Option<gpui::Bounds<Pixels>> {
14172 let text_layout_details = self.text_layout_details(cx);
14173 let gpui::Point {
14174 x: em_width,
14175 y: line_height,
14176 } = self.character_size(cx);
14177
14178 let snapshot = self.snapshot(cx);
14179 let scroll_position = snapshot.scroll_position();
14180 let scroll_left = scroll_position.x * em_width;
14181
14182 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
14183 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
14184 + self.gutter_dimensions.width
14185 + self.gutter_dimensions.margin;
14186 let y = line_height * (start.row().as_f32() - scroll_position.y);
14187
14188 Some(Bounds {
14189 origin: element_bounds.origin + point(x, y),
14190 size: size(em_width, line_height),
14191 })
14192 }
14193}
14194
14195trait SelectionExt {
14196 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
14197 fn spanned_rows(
14198 &self,
14199 include_end_if_at_line_start: bool,
14200 map: &DisplaySnapshot,
14201 ) -> Range<MultiBufferRow>;
14202}
14203
14204impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
14205 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
14206 let start = self
14207 .start
14208 .to_point(&map.buffer_snapshot)
14209 .to_display_point(map);
14210 let end = self
14211 .end
14212 .to_point(&map.buffer_snapshot)
14213 .to_display_point(map);
14214 if self.reversed {
14215 end..start
14216 } else {
14217 start..end
14218 }
14219 }
14220
14221 fn spanned_rows(
14222 &self,
14223 include_end_if_at_line_start: bool,
14224 map: &DisplaySnapshot,
14225 ) -> Range<MultiBufferRow> {
14226 let start = self.start.to_point(&map.buffer_snapshot);
14227 let mut end = self.end.to_point(&map.buffer_snapshot);
14228 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
14229 end.row -= 1;
14230 }
14231
14232 let buffer_start = map.prev_line_boundary(start).0;
14233 let buffer_end = map.next_line_boundary(end).0;
14234 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
14235 }
14236}
14237
14238impl<T: InvalidationRegion> InvalidationStack<T> {
14239 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
14240 where
14241 S: Clone + ToOffset,
14242 {
14243 while let Some(region) = self.last() {
14244 let all_selections_inside_invalidation_ranges =
14245 if selections.len() == region.ranges().len() {
14246 selections
14247 .iter()
14248 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
14249 .all(|(selection, invalidation_range)| {
14250 let head = selection.head().to_offset(buffer);
14251 invalidation_range.start <= head && invalidation_range.end >= head
14252 })
14253 } else {
14254 false
14255 };
14256
14257 if all_selections_inside_invalidation_ranges {
14258 break;
14259 } else {
14260 self.pop();
14261 }
14262 }
14263 }
14264}
14265
14266impl<T> Default for InvalidationStack<T> {
14267 fn default() -> Self {
14268 Self(Default::default())
14269 }
14270}
14271
14272impl<T> Deref for InvalidationStack<T> {
14273 type Target = Vec<T>;
14274
14275 fn deref(&self) -> &Self::Target {
14276 &self.0
14277 }
14278}
14279
14280impl<T> DerefMut for InvalidationStack<T> {
14281 fn deref_mut(&mut self) -> &mut Self::Target {
14282 &mut self.0
14283 }
14284}
14285
14286impl InvalidationRegion for SnippetState {
14287 fn ranges(&self) -> &[Range<Anchor>] {
14288 &self.ranges[self.active_index]
14289 }
14290}
14291
14292pub fn diagnostic_block_renderer(
14293 diagnostic: Diagnostic,
14294 max_message_rows: Option<u8>,
14295 allow_closing: bool,
14296 _is_valid: bool,
14297) -> RenderBlock {
14298 let (text_without_backticks, code_ranges) =
14299 highlight_diagnostic_message(&diagnostic, max_message_rows);
14300
14301 Arc::new(move |cx: &mut BlockContext| {
14302 let group_id: SharedString = cx.block_id.to_string().into();
14303
14304 let mut text_style = cx.text_style().clone();
14305 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
14306 let theme_settings = ThemeSettings::get_global(cx);
14307 text_style.font_family = theme_settings.buffer_font.family.clone();
14308 text_style.font_style = theme_settings.buffer_font.style;
14309 text_style.font_features = theme_settings.buffer_font.features.clone();
14310 text_style.font_weight = theme_settings.buffer_font.weight;
14311
14312 let multi_line_diagnostic = diagnostic.message.contains('\n');
14313
14314 let buttons = |diagnostic: &Diagnostic| {
14315 if multi_line_diagnostic {
14316 v_flex()
14317 } else {
14318 h_flex()
14319 }
14320 .when(allow_closing, |div| {
14321 div.children(diagnostic.is_primary.then(|| {
14322 IconButton::new("close-block", IconName::XCircle)
14323 .icon_color(Color::Muted)
14324 .size(ButtonSize::Compact)
14325 .style(ButtonStyle::Transparent)
14326 .visible_on_hover(group_id.clone())
14327 .on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
14328 .tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
14329 }))
14330 })
14331 .child(
14332 IconButton::new("copy-block", IconName::Copy)
14333 .icon_color(Color::Muted)
14334 .size(ButtonSize::Compact)
14335 .style(ButtonStyle::Transparent)
14336 .visible_on_hover(group_id.clone())
14337 .on_click({
14338 let message = diagnostic.message.clone();
14339 move |_click, cx| {
14340 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
14341 }
14342 })
14343 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
14344 )
14345 };
14346
14347 let icon_size = buttons(&diagnostic)
14348 .into_any_element()
14349 .layout_as_root(AvailableSpace::min_size(), cx);
14350
14351 h_flex()
14352 .id(cx.block_id)
14353 .group(group_id.clone())
14354 .relative()
14355 .size_full()
14356 .block_mouse_down()
14357 .pl(cx.gutter_dimensions.width)
14358 .w(cx.max_width - cx.gutter_dimensions.full_width())
14359 .child(
14360 div()
14361 .flex()
14362 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
14363 .flex_shrink(),
14364 )
14365 .child(buttons(&diagnostic))
14366 .child(div().flex().flex_shrink_0().child(
14367 StyledText::new(text_without_backticks.clone()).with_highlights(
14368 &text_style,
14369 code_ranges.iter().map(|range| {
14370 (
14371 range.clone(),
14372 HighlightStyle {
14373 font_weight: Some(FontWeight::BOLD),
14374 ..Default::default()
14375 },
14376 )
14377 }),
14378 ),
14379 ))
14380 .into_any_element()
14381 })
14382}
14383
14384pub fn highlight_diagnostic_message(
14385 diagnostic: &Diagnostic,
14386 mut max_message_rows: Option<u8>,
14387) -> (SharedString, Vec<Range<usize>>) {
14388 let mut text_without_backticks = String::new();
14389 let mut code_ranges = Vec::new();
14390
14391 if let Some(source) = &diagnostic.source {
14392 text_without_backticks.push_str(source);
14393 code_ranges.push(0..source.len());
14394 text_without_backticks.push_str(": ");
14395 }
14396
14397 let mut prev_offset = 0;
14398 let mut in_code_block = false;
14399 let has_row_limit = max_message_rows.is_some();
14400 let mut newline_indices = diagnostic
14401 .message
14402 .match_indices('\n')
14403 .filter(|_| has_row_limit)
14404 .map(|(ix, _)| ix)
14405 .fuse()
14406 .peekable();
14407
14408 for (quote_ix, _) in diagnostic
14409 .message
14410 .match_indices('`')
14411 .chain([(diagnostic.message.len(), "")])
14412 {
14413 let mut first_newline_ix = None;
14414 let mut last_newline_ix = None;
14415 while let Some(newline_ix) = newline_indices.peek() {
14416 if *newline_ix < quote_ix {
14417 if first_newline_ix.is_none() {
14418 first_newline_ix = Some(*newline_ix);
14419 }
14420 last_newline_ix = Some(*newline_ix);
14421
14422 if let Some(rows_left) = &mut max_message_rows {
14423 if *rows_left == 0 {
14424 break;
14425 } else {
14426 *rows_left -= 1;
14427 }
14428 }
14429 let _ = newline_indices.next();
14430 } else {
14431 break;
14432 }
14433 }
14434 let prev_len = text_without_backticks.len();
14435 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
14436 text_without_backticks.push_str(new_text);
14437 if in_code_block {
14438 code_ranges.push(prev_len..text_without_backticks.len());
14439 }
14440 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
14441 in_code_block = !in_code_block;
14442 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
14443 text_without_backticks.push_str("...");
14444 break;
14445 }
14446 }
14447
14448 (text_without_backticks.into(), code_ranges)
14449}
14450
14451fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
14452 match severity {
14453 DiagnosticSeverity::ERROR => colors.error,
14454 DiagnosticSeverity::WARNING => colors.warning,
14455 DiagnosticSeverity::INFORMATION => colors.info,
14456 DiagnosticSeverity::HINT => colors.info,
14457 _ => colors.ignored,
14458 }
14459}
14460
14461pub fn styled_runs_for_code_label<'a>(
14462 label: &'a CodeLabel,
14463 syntax_theme: &'a theme::SyntaxTheme,
14464) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
14465 let fade_out = HighlightStyle {
14466 fade_out: Some(0.35),
14467 ..Default::default()
14468 };
14469
14470 let mut prev_end = label.filter_range.end;
14471 label
14472 .runs
14473 .iter()
14474 .enumerate()
14475 .flat_map(move |(ix, (range, highlight_id))| {
14476 let style = if let Some(style) = highlight_id.style(syntax_theme) {
14477 style
14478 } else {
14479 return Default::default();
14480 };
14481 let mut muted_style = style;
14482 muted_style.highlight(fade_out);
14483
14484 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
14485 if range.start >= label.filter_range.end {
14486 if range.start > prev_end {
14487 runs.push((prev_end..range.start, fade_out));
14488 }
14489 runs.push((range.clone(), muted_style));
14490 } else if range.end <= label.filter_range.end {
14491 runs.push((range.clone(), style));
14492 } else {
14493 runs.push((range.start..label.filter_range.end, style));
14494 runs.push((label.filter_range.end..range.end, muted_style));
14495 }
14496 prev_end = cmp::max(prev_end, range.end);
14497
14498 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
14499 runs.push((prev_end..label.text.len(), fade_out));
14500 }
14501
14502 runs
14503 })
14504}
14505
14506pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
14507 let mut prev_index = 0;
14508 let mut prev_codepoint: Option<char> = None;
14509 text.char_indices()
14510 .chain([(text.len(), '\0')])
14511 .filter_map(move |(index, codepoint)| {
14512 let prev_codepoint = prev_codepoint.replace(codepoint)?;
14513 let is_boundary = index == text.len()
14514 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
14515 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
14516 if is_boundary {
14517 let chunk = &text[prev_index..index];
14518 prev_index = index;
14519 Some(chunk)
14520 } else {
14521 None
14522 }
14523 })
14524}
14525
14526pub trait RangeToAnchorExt: Sized {
14527 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
14528
14529 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
14530 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
14531 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
14532 }
14533}
14534
14535impl<T: ToOffset> RangeToAnchorExt for Range<T> {
14536 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
14537 let start_offset = self.start.to_offset(snapshot);
14538 let end_offset = self.end.to_offset(snapshot);
14539 if start_offset == end_offset {
14540 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
14541 } else {
14542 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
14543 }
14544 }
14545}
14546
14547pub trait RowExt {
14548 fn as_f32(&self) -> f32;
14549
14550 fn next_row(&self) -> Self;
14551
14552 fn previous_row(&self) -> Self;
14553
14554 fn minus(&self, other: Self) -> u32;
14555}
14556
14557impl RowExt for DisplayRow {
14558 fn as_f32(&self) -> f32 {
14559 self.0 as f32
14560 }
14561
14562 fn next_row(&self) -> Self {
14563 Self(self.0 + 1)
14564 }
14565
14566 fn previous_row(&self) -> Self {
14567 Self(self.0.saturating_sub(1))
14568 }
14569
14570 fn minus(&self, other: Self) -> u32 {
14571 self.0 - other.0
14572 }
14573}
14574
14575impl RowExt for MultiBufferRow {
14576 fn as_f32(&self) -> f32 {
14577 self.0 as f32
14578 }
14579
14580 fn next_row(&self) -> Self {
14581 Self(self.0 + 1)
14582 }
14583
14584 fn previous_row(&self) -> Self {
14585 Self(self.0.saturating_sub(1))
14586 }
14587
14588 fn minus(&self, other: Self) -> u32 {
14589 self.0 - other.0
14590 }
14591}
14592
14593trait RowRangeExt {
14594 type Row;
14595
14596 fn len(&self) -> usize;
14597
14598 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
14599}
14600
14601impl RowRangeExt for Range<MultiBufferRow> {
14602 type Row = MultiBufferRow;
14603
14604 fn len(&self) -> usize {
14605 (self.end.0 - self.start.0) as usize
14606 }
14607
14608 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
14609 (self.start.0..self.end.0).map(MultiBufferRow)
14610 }
14611}
14612
14613impl RowRangeExt for Range<DisplayRow> {
14614 type Row = DisplayRow;
14615
14616 fn len(&self) -> usize {
14617 (self.end.0 - self.start.0) as usize
14618 }
14619
14620 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
14621 (self.start.0..self.end.0).map(DisplayRow)
14622 }
14623}
14624
14625fn hunk_status(hunk: &MultiBufferDiffHunk) -> DiffHunkStatus {
14626 if hunk.diff_base_byte_range.is_empty() {
14627 DiffHunkStatus::Added
14628 } else if hunk.row_range.is_empty() {
14629 DiffHunkStatus::Removed
14630 } else {
14631 DiffHunkStatus::Modified
14632 }
14633}
14634
14635/// If select range has more than one line, we
14636/// just point the cursor to range.start.
14637fn check_multiline_range(buffer: &Buffer, range: Range<usize>) -> Range<usize> {
14638 if buffer.offset_to_point(range.start).row == buffer.offset_to_point(range.end).row {
14639 range
14640 } else {
14641 range.start..range.start
14642 }
14643}
14644
14645pub struct KillRing(ClipboardItem);
14646impl Global for KillRing {}
14647
14648const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);