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 indent_guides;
29mod inlay_hint_cache;
30pub mod items;
31mod linked_editing_ranges;
32mod lsp_ext;
33mod mouse_context_menu;
34pub mod movement;
35mod persistence;
36mod proposed_changes_editor;
37mod rust_analyzer_ext;
38pub mod scroll;
39mod selections_collection;
40pub mod tasks;
41
42#[cfg(test)]
43mod editor_tests;
44#[cfg(test)]
45mod inline_completion_tests;
46mod signature_help;
47#[cfg(any(test, feature = "test-support"))]
48pub mod test;
49
50use ::git::diff::DiffHunkStatus;
51pub(crate) use actions::*;
52pub use actions::{OpenExcerpts, OpenExcerptsSplit};
53use aho_corasick::AhoCorasick;
54use anyhow::{anyhow, Context as _, Result};
55use blink_manager::BlinkManager;
56use client::{Collaborator, ParticipantIndex};
57use clock::ReplicaId;
58use collections::{BTreeMap, HashMap, HashSet, VecDeque};
59use convert_case::{Case, Casing};
60use display_map::*;
61pub use display_map::{DisplayPoint, FoldPlaceholder};
62pub use editor_settings::{
63 CurrentLineHighlight, EditorSettings, ScrollBeyondLastLine, SearchSettings, ShowScrollbar,
64};
65pub use editor_settings_controls::*;
66use element::LineWithInvisibles;
67pub use element::{
68 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
69};
70use futures::{future, FutureExt};
71use fuzzy::StringMatchCandidate;
72use zed_predict_tos::ZedPredictTos;
73
74use code_context_menus::{
75 AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
76 CompletionEntry, 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};
91use indent_guides::ActiveIndentGuidesState;
92use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
93pub use inline_completion::Direction;
94use inline_completion::{InlineCompletionProvider, InlineCompletionProviderHandle};
95pub use items::MAX_TAB_TITLE_LEN;
96use itertools::Itertools;
97use language::{
98 language_settings::{self, all_language_settings, language_settings, InlayHintSettings},
99 markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
100 CursorShape, Diagnostic, Documentation, EditPreview, HighlightedEdits, IndentKind, IndentSize,
101 Language, OffsetRangeExt, Point, Selection, SelectionGoal, TextObject, TransactionId,
102 TreeSitterOptions,
103};
104use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
105use linked_editing_ranges::refresh_linked_ranges;
106use mouse_context_menu::MouseContextMenu;
107pub use proposed_changes_editor::{
108 ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar,
109};
110use similar::{ChangeTag, TextDiff};
111use std::iter::Peekable;
112use task::{ResolvedTask, TaskTemplate, TaskVariables};
113
114use hover_links::{find_file, HoverLink, HoveredLinkState, InlayHighlight};
115pub use lsp::CompletionContext;
116use lsp::{
117 CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity, InsertTextFormat,
118 LanguageServerId, LanguageServerName,
119};
120
121use language::BufferSnapshot;
122use movement::TextLayoutDetails;
123pub use multi_buffer::{
124 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, RowInfo,
125 ToOffset, ToPoint,
126};
127use multi_buffer::{
128 ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow, ToOffsetUtf16,
129};
130use project::{
131 lsp_store::{FormatTrigger, LspFormatTarget, OpenLspBufferHandle},
132 project_settings::{GitGutterSetting, ProjectSettings},
133 CodeAction, Completion, CompletionIntent, DocumentHighlight, InlayHint, Location, LocationLink,
134 LspStore, PrepareRenameResponse, Project, ProjectItem, ProjectTransaction, TaskSourceKind,
135};
136use rand::prelude::*;
137use rpc::{proto::*, ErrorExt};
138use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
139use selections_collection::{
140 resolve_selections, MutableSelectionsCollection, SelectionsCollection,
141};
142use serde::{Deserialize, Serialize};
143use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
144use smallvec::SmallVec;
145use snippet::Snippet;
146use std::{
147 any::TypeId,
148 borrow::Cow,
149 cell::RefCell,
150 cmp::{self, Ordering, Reverse},
151 mem,
152 num::NonZeroU32,
153 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
154 path::{Path, PathBuf},
155 rc::Rc,
156 sync::Arc,
157 time::{Duration, Instant},
158};
159pub use sum_tree::Bias;
160use sum_tree::TreeMap;
161use text::{BufferId, OffsetUtf16, Rope};
162use theme::{ActiveTheme, PlayerColor, StatusColors, SyntaxTheme, ThemeColors, ThemeSettings};
163use ui::{
164 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize,
165 Tooltip,
166};
167use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
168use workspace::item::{ItemHandle, PreviewTabsSettings};
169use workspace::notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt};
170use workspace::{
171 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
172};
173use workspace::{Item as WorkspaceItem, OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
174
175use crate::hover_links::{find_url, find_url_from_range};
176use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
177
178pub const FILE_HEADER_HEIGHT: u32 = 2;
179pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
180pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u32 = 1;
181pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
182const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
183const MAX_LINE_LEN: usize = 1024;
184const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
185const MAX_SELECTION_HISTORY_LEN: usize = 1024;
186pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
187#[doc(hidden)]
188pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
189
190pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
191pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
192
193pub fn render_parsed_markdown(
194 element_id: impl Into<ElementId>,
195 parsed: &language::ParsedMarkdown,
196 editor_style: &EditorStyle,
197 workspace: Option<WeakView<Workspace>>,
198 cx: &mut WindowContext,
199) -> InteractiveText {
200 let code_span_background_color = cx
201 .theme()
202 .colors()
203 .editor_document_highlight_read_background;
204
205 let highlights = gpui::combine_highlights(
206 parsed.highlights.iter().filter_map(|(range, highlight)| {
207 let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
208 Some((range.clone(), highlight))
209 }),
210 parsed
211 .regions
212 .iter()
213 .zip(&parsed.region_ranges)
214 .filter_map(|(region, range)| {
215 if region.code {
216 Some((
217 range.clone(),
218 HighlightStyle {
219 background_color: Some(code_span_background_color),
220 ..Default::default()
221 },
222 ))
223 } else {
224 None
225 }
226 }),
227 );
228
229 let mut links = Vec::new();
230 let mut link_ranges = Vec::new();
231 for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
232 if let Some(link) = region.link.clone() {
233 links.push(link);
234 link_ranges.push(range.clone());
235 }
236 }
237
238 InteractiveText::new(
239 element_id,
240 StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
241 )
242 .on_click(link_ranges, move |clicked_range_ix, cx| {
243 match &links[clicked_range_ix] {
244 markdown::Link::Web { url } => cx.open_url(url),
245 markdown::Link::Path { path } => {
246 if let Some(workspace) = &workspace {
247 _ = workspace.update(cx, |workspace, cx| {
248 workspace.open_abs_path(path.clone(), false, cx).detach();
249 });
250 }
251 }
252 }
253 })
254}
255
256#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
257pub enum InlayId {
258 InlineCompletion(usize),
259 Hint(usize),
260}
261
262impl InlayId {
263 fn id(&self) -> usize {
264 match self {
265 Self::InlineCompletion(id) => *id,
266 Self::Hint(id) => *id,
267 }
268 }
269}
270
271enum DocumentHighlightRead {}
272enum DocumentHighlightWrite {}
273enum InputComposition {}
274
275#[derive(Debug, Copy, Clone, PartialEq, Eq)]
276pub enum Navigated {
277 Yes,
278 No,
279}
280
281impl Navigated {
282 pub fn from_bool(yes: bool) -> Navigated {
283 if yes {
284 Navigated::Yes
285 } else {
286 Navigated::No
287 }
288 }
289}
290
291pub fn init_settings(cx: &mut AppContext) {
292 EditorSettings::register(cx);
293}
294
295pub fn init(cx: &mut AppContext) {
296 init_settings(cx);
297
298 workspace::register_project_item::<Editor>(cx);
299 workspace::FollowableViewRegistry::register::<Editor>(cx);
300 workspace::register_serializable_item::<Editor>(cx);
301
302 cx.observe_new_views(
303 |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
304 workspace.register_action(Editor::new_file);
305 workspace.register_action(Editor::new_file_vertical);
306 workspace.register_action(Editor::new_file_horizontal);
307 },
308 )
309 .detach();
310
311 cx.on_action(move |_: &workspace::NewFile, cx| {
312 let app_state = workspace::AppState::global(cx);
313 if let Some(app_state) = app_state.upgrade() {
314 workspace::open_new(Default::default(), app_state, cx, |workspace, cx| {
315 Editor::new_file(workspace, &Default::default(), cx)
316 })
317 .detach();
318 }
319 });
320 cx.on_action(move |_: &workspace::NewWindow, cx| {
321 let app_state = workspace::AppState::global(cx);
322 if let Some(app_state) = app_state.upgrade() {
323 workspace::open_new(Default::default(), app_state, cx, |workspace, cx| {
324 Editor::new_file(workspace, &Default::default(), cx)
325 })
326 .detach();
327 }
328 });
329 git::project_diff::init(cx);
330}
331
332pub struct SearchWithinRange;
333
334trait InvalidationRegion {
335 fn ranges(&self) -> &[Range<Anchor>];
336}
337
338#[derive(Clone, Debug, PartialEq)]
339pub enum SelectPhase {
340 Begin {
341 position: DisplayPoint,
342 add: bool,
343 click_count: usize,
344 },
345 BeginColumnar {
346 position: DisplayPoint,
347 reset: bool,
348 goal_column: u32,
349 },
350 Extend {
351 position: DisplayPoint,
352 click_count: usize,
353 },
354 Update {
355 position: DisplayPoint,
356 goal_column: u32,
357 scroll_delta: gpui::Point<f32>,
358 },
359 End,
360}
361
362#[derive(Clone, Debug)]
363pub enum SelectMode {
364 Character,
365 Word(Range<Anchor>),
366 Line(Range<Anchor>),
367 All,
368}
369
370#[derive(Copy, Clone, PartialEq, Eq, Debug)]
371pub enum EditorMode {
372 SingleLine { auto_width: bool },
373 AutoHeight { max_lines: usize },
374 Full,
375}
376
377#[derive(Copy, Clone, Debug)]
378pub enum SoftWrap {
379 /// Prefer not to wrap at all.
380 ///
381 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
382 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
383 GitDiff,
384 /// Prefer a single line generally, unless an overly long line is encountered.
385 None,
386 /// Soft wrap lines that exceed the editor width.
387 EditorWidth,
388 /// Soft wrap lines at the preferred line length.
389 Column(u32),
390 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
391 Bounded(u32),
392}
393
394#[derive(Clone)]
395pub struct EditorStyle {
396 pub background: Hsla,
397 pub local_player: PlayerColor,
398 pub text: TextStyle,
399 pub scrollbar_width: Pixels,
400 pub syntax: Arc<SyntaxTheme>,
401 pub status: StatusColors,
402 pub inlay_hints_style: HighlightStyle,
403 pub inline_completion_styles: InlineCompletionStyles,
404 pub unnecessary_code_fade: f32,
405}
406
407impl Default for EditorStyle {
408 fn default() -> Self {
409 Self {
410 background: Hsla::default(),
411 local_player: PlayerColor::default(),
412 text: TextStyle::default(),
413 scrollbar_width: Pixels::default(),
414 syntax: Default::default(),
415 // HACK: Status colors don't have a real default.
416 // We should look into removing the status colors from the editor
417 // style and retrieve them directly from the theme.
418 status: StatusColors::dark(),
419 inlay_hints_style: HighlightStyle::default(),
420 inline_completion_styles: InlineCompletionStyles {
421 insertion: HighlightStyle::default(),
422 whitespace: HighlightStyle::default(),
423 },
424 unnecessary_code_fade: Default::default(),
425 }
426 }
427}
428
429pub fn make_inlay_hints_style(cx: &WindowContext) -> HighlightStyle {
430 let show_background = language_settings::language_settings(None, None, cx)
431 .inlay_hints
432 .show_background;
433
434 HighlightStyle {
435 color: Some(cx.theme().status().hint),
436 background_color: show_background.then(|| cx.theme().status().hint_background),
437 ..HighlightStyle::default()
438 }
439}
440
441pub fn make_suggestion_styles(cx: &WindowContext) -> InlineCompletionStyles {
442 InlineCompletionStyles {
443 insertion: HighlightStyle {
444 color: Some(cx.theme().status().predictive),
445 ..HighlightStyle::default()
446 },
447 whitespace: HighlightStyle {
448 background_color: Some(cx.theme().status().created_background),
449 ..HighlightStyle::default()
450 },
451 }
452}
453
454type CompletionId = usize;
455
456#[derive(Debug, Clone)]
457enum InlineCompletionMenuHint {
458 Loading,
459 Loaded { text: InlineCompletionText },
460 PendingTermsAcceptance,
461 None,
462}
463
464impl InlineCompletionMenuHint {
465 pub fn label(&self) -> &'static str {
466 match self {
467 InlineCompletionMenuHint::Loading | InlineCompletionMenuHint::Loaded { .. } => {
468 "Edit Prediction"
469 }
470 InlineCompletionMenuHint::PendingTermsAcceptance => "Accept Terms of Service",
471 InlineCompletionMenuHint::None => "No Prediction",
472 }
473 }
474}
475
476#[derive(Clone, Debug)]
477enum InlineCompletionText {
478 Move(SharedString),
479 Edit(HighlightedEdits),
480}
481
482pub(crate) enum EditDisplayMode {
483 TabAccept,
484 DiffPopover,
485 Inline,
486}
487
488enum InlineCompletion {
489 Edit {
490 edits: Vec<(Range<Anchor>, String)>,
491 edit_preview: Option<EditPreview>,
492 display_mode: EditDisplayMode,
493 snapshot: BufferSnapshot,
494 },
495 Move(Anchor),
496}
497
498struct InlineCompletionState {
499 inlay_ids: Vec<InlayId>,
500 completion: InlineCompletion,
501 invalidation_range: Range<Anchor>,
502}
503
504enum InlineCompletionHighlight {}
505
506pub enum MenuInlineCompletionsPolicy {
507 Never,
508 ByProvider,
509}
510
511#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
512struct EditorActionId(usize);
513
514impl EditorActionId {
515 pub fn post_inc(&mut self) -> Self {
516 let answer = self.0;
517
518 *self = Self(answer + 1);
519
520 Self(answer)
521 }
522}
523
524// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
525// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
526
527type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
528type GutterHighlight = (fn(&AppContext) -> Hsla, Arc<[Range<Anchor>]>);
529
530#[derive(Default)]
531struct ScrollbarMarkerState {
532 scrollbar_size: Size<Pixels>,
533 dirty: bool,
534 markers: Arc<[PaintQuad]>,
535 pending_refresh: Option<Task<Result<()>>>,
536}
537
538impl ScrollbarMarkerState {
539 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
540 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
541 }
542}
543
544#[derive(Clone, Debug)]
545struct RunnableTasks {
546 templates: Vec<(TaskSourceKind, TaskTemplate)>,
547 offset: MultiBufferOffset,
548 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
549 column: u32,
550 // Values of all named captures, including those starting with '_'
551 extra_variables: HashMap<String, String>,
552 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
553 context_range: Range<BufferOffset>,
554}
555
556impl RunnableTasks {
557 fn resolve<'a>(
558 &'a self,
559 cx: &'a task::TaskContext,
560 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
561 self.templates.iter().filter_map(|(kind, template)| {
562 template
563 .resolve_task(&kind.to_id_base(), cx)
564 .map(|task| (kind.clone(), task))
565 })
566 }
567}
568
569#[derive(Clone)]
570struct ResolvedTasks {
571 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
572 position: Anchor,
573}
574#[derive(Copy, Clone, Debug)]
575struct MultiBufferOffset(usize);
576#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
577struct BufferOffset(usize);
578
579// Addons allow storing per-editor state in other crates (e.g. Vim)
580pub trait Addon: 'static {
581 fn extend_key_context(&self, _: &mut KeyContext, _: &AppContext) {}
582
583 fn to_any(&self) -> &dyn std::any::Any;
584}
585
586#[derive(Debug, Copy, Clone, PartialEq, Eq)]
587pub enum IsVimMode {
588 Yes,
589 No,
590}
591
592/// Zed's primary text input `View`, allowing users to edit a [`MultiBuffer`]
593///
594/// See the [module level documentation](self) for more information.
595pub struct Editor {
596 focus_handle: FocusHandle,
597 last_focused_descendant: Option<WeakFocusHandle>,
598 /// The text buffer being edited
599 buffer: Model<MultiBuffer>,
600 /// Map of how text in the buffer should be displayed.
601 /// Handles soft wraps, folds, fake inlay text insertions, etc.
602 pub display_map: Model<DisplayMap>,
603 pub selections: SelectionsCollection,
604 pub scroll_manager: ScrollManager,
605 /// When inline assist editors are linked, they all render cursors because
606 /// typing enters text into each of them, even the ones that aren't focused.
607 pub(crate) show_cursor_when_unfocused: bool,
608 columnar_selection_tail: Option<Anchor>,
609 add_selections_state: Option<AddSelectionsState>,
610 select_next_state: Option<SelectNextState>,
611 select_prev_state: Option<SelectNextState>,
612 selection_history: SelectionHistory,
613 autoclose_regions: Vec<AutocloseRegion>,
614 snippet_stack: InvalidationStack<SnippetState>,
615 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
616 ime_transaction: Option<TransactionId>,
617 active_diagnostics: Option<ActiveDiagnosticGroup>,
618 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
619
620 project: Option<Model<Project>>,
621 semantics_provider: Option<Rc<dyn SemanticsProvider>>,
622 completion_provider: Option<Box<dyn CompletionProvider>>,
623 collaboration_hub: Option<Box<dyn CollaborationHub>>,
624 blink_manager: Model<BlinkManager>,
625 show_cursor_names: bool,
626 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
627 pub show_local_selections: bool,
628 mode: EditorMode,
629 show_breadcrumbs: bool,
630 show_gutter: bool,
631 show_scrollbars: bool,
632 show_line_numbers: Option<bool>,
633 use_relative_line_numbers: Option<bool>,
634 show_git_diff_gutter: Option<bool>,
635 show_code_actions: Option<bool>,
636 show_runnables: Option<bool>,
637 show_wrap_guides: Option<bool>,
638 show_indent_guides: Option<bool>,
639 placeholder_text: Option<Arc<str>>,
640 highlight_order: usize,
641 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
642 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
643 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
644 scrollbar_marker_state: ScrollbarMarkerState,
645 active_indent_guides_state: ActiveIndentGuidesState,
646 nav_history: Option<ItemNavHistory>,
647 context_menu: RefCell<Option<CodeContextMenu>>,
648 mouse_context_menu: Option<MouseContextMenu>,
649 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
650 signature_help_state: SignatureHelpState,
651 auto_signature_help: Option<bool>,
652 find_all_references_task_sources: Vec<Anchor>,
653 next_completion_id: CompletionId,
654 available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
655 code_actions_task: Option<Task<Result<()>>>,
656 document_highlights_task: Option<Task<()>>,
657 linked_editing_range_task: Option<Task<Option<()>>>,
658 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
659 pending_rename: Option<RenameState>,
660 searchable: bool,
661 cursor_shape: CursorShape,
662 current_line_highlight: Option<CurrentLineHighlight>,
663 collapse_matches: bool,
664 autoindent_mode: Option<AutoindentMode>,
665 workspace: Option<(WeakView<Workspace>, Option<WorkspaceId>)>,
666 input_enabled: bool,
667 use_modal_editing: bool,
668 read_only: bool,
669 leader_peer_id: Option<PeerId>,
670 remote_id: Option<ViewId>,
671 hover_state: HoverState,
672 gutter_hovered: bool,
673 hovered_link_state: Option<HoveredLinkState>,
674 inline_completion_provider: Option<RegisteredInlineCompletionProvider>,
675 code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
676 active_inline_completion: Option<InlineCompletionState>,
677 // enable_inline_completions is a switch that Vim can use to disable
678 // inline completions based on its mode.
679 enable_inline_completions: bool,
680 show_inline_completions_override: Option<bool>,
681 menu_inline_completions_policy: MenuInlineCompletionsPolicy,
682 inlay_hint_cache: InlayHintCache,
683 next_inlay_id: usize,
684 _subscriptions: Vec<Subscription>,
685 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
686 gutter_dimensions: GutterDimensions,
687 style: Option<EditorStyle>,
688 text_style_refinement: Option<TextStyleRefinement>,
689 next_editor_action_id: EditorActionId,
690 editor_actions: Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut ViewContext<Self>)>>>>,
691 use_autoclose: bool,
692 use_auto_surround: bool,
693 auto_replace_emoji_shortcode: bool,
694 show_git_blame_gutter: bool,
695 show_git_blame_inline: bool,
696 show_git_blame_inline_delay_task: Option<Task<()>>,
697 git_blame_inline_enabled: bool,
698 serialize_dirty_buffers: bool,
699 show_selection_menu: Option<bool>,
700 blame: Option<Model<GitBlame>>,
701 blame_subscription: Option<Subscription>,
702 custom_context_menu: Option<
703 Box<
704 dyn 'static
705 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
706 >,
707 >,
708 last_bounds: Option<Bounds<Pixels>>,
709 expect_bounds_change: Option<Bounds<Pixels>>,
710 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
711 tasks_update_task: Option<Task<()>>,
712 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
713 breadcrumb_header: Option<String>,
714 focused_block: Option<FocusedBlock>,
715 next_scroll_position: NextScrollCursorCenterTopBottom,
716 addons: HashMap<TypeId, Box<dyn Addon>>,
717 registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
718 selection_mark_mode: bool,
719 toggle_fold_multiple_buffers: Task<()>,
720 _scroll_cursor_center_top_bottom_task: Task<()>,
721}
722
723#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
724enum NextScrollCursorCenterTopBottom {
725 #[default]
726 Center,
727 Top,
728 Bottom,
729}
730
731impl NextScrollCursorCenterTopBottom {
732 fn next(&self) -> Self {
733 match self {
734 Self::Center => Self::Top,
735 Self::Top => Self::Bottom,
736 Self::Bottom => Self::Center,
737 }
738 }
739}
740
741#[derive(Clone)]
742pub struct EditorSnapshot {
743 pub mode: EditorMode,
744 show_gutter: bool,
745 show_line_numbers: Option<bool>,
746 show_git_diff_gutter: Option<bool>,
747 show_code_actions: Option<bool>,
748 show_runnables: Option<bool>,
749 git_blame_gutter_max_author_length: Option<usize>,
750 pub display_snapshot: DisplaySnapshot,
751 pub placeholder_text: Option<Arc<str>>,
752 is_focused: bool,
753 scroll_anchor: ScrollAnchor,
754 ongoing_scroll: OngoingScroll,
755 current_line_highlight: CurrentLineHighlight,
756 gutter_hovered: bool,
757}
758
759const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
760
761#[derive(Default, Debug, Clone, Copy)]
762pub struct GutterDimensions {
763 pub left_padding: Pixels,
764 pub right_padding: Pixels,
765 pub width: Pixels,
766 pub margin: Pixels,
767 pub git_blame_entries_width: Option<Pixels>,
768}
769
770impl GutterDimensions {
771 /// The full width of the space taken up by the gutter.
772 pub fn full_width(&self) -> Pixels {
773 self.margin + self.width
774 }
775
776 /// The width of the space reserved for the fold indicators,
777 /// use alongside 'justify_end' and `gutter_width` to
778 /// right align content with the line numbers
779 pub fn fold_area_width(&self) -> Pixels {
780 self.margin + self.right_padding
781 }
782}
783
784#[derive(Debug)]
785pub struct RemoteSelection {
786 pub replica_id: ReplicaId,
787 pub selection: Selection<Anchor>,
788 pub cursor_shape: CursorShape,
789 pub peer_id: PeerId,
790 pub line_mode: bool,
791 pub participant_index: Option<ParticipantIndex>,
792 pub user_name: Option<SharedString>,
793}
794
795#[derive(Clone, Debug)]
796struct SelectionHistoryEntry {
797 selections: Arc<[Selection<Anchor>]>,
798 select_next_state: Option<SelectNextState>,
799 select_prev_state: Option<SelectNextState>,
800 add_selections_state: Option<AddSelectionsState>,
801}
802
803enum SelectionHistoryMode {
804 Normal,
805 Undoing,
806 Redoing,
807}
808
809#[derive(Clone, PartialEq, Eq, Hash)]
810struct HoveredCursor {
811 replica_id: u16,
812 selection_id: usize,
813}
814
815impl Default for SelectionHistoryMode {
816 fn default() -> Self {
817 Self::Normal
818 }
819}
820
821#[derive(Default)]
822struct SelectionHistory {
823 #[allow(clippy::type_complexity)]
824 selections_by_transaction:
825 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
826 mode: SelectionHistoryMode,
827 undo_stack: VecDeque<SelectionHistoryEntry>,
828 redo_stack: VecDeque<SelectionHistoryEntry>,
829}
830
831impl SelectionHistory {
832 fn insert_transaction(
833 &mut self,
834 transaction_id: TransactionId,
835 selections: Arc<[Selection<Anchor>]>,
836 ) {
837 self.selections_by_transaction
838 .insert(transaction_id, (selections, None));
839 }
840
841 #[allow(clippy::type_complexity)]
842 fn transaction(
843 &self,
844 transaction_id: TransactionId,
845 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
846 self.selections_by_transaction.get(&transaction_id)
847 }
848
849 #[allow(clippy::type_complexity)]
850 fn transaction_mut(
851 &mut self,
852 transaction_id: TransactionId,
853 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
854 self.selections_by_transaction.get_mut(&transaction_id)
855 }
856
857 fn push(&mut self, entry: SelectionHistoryEntry) {
858 if !entry.selections.is_empty() {
859 match self.mode {
860 SelectionHistoryMode::Normal => {
861 self.push_undo(entry);
862 self.redo_stack.clear();
863 }
864 SelectionHistoryMode::Undoing => self.push_redo(entry),
865 SelectionHistoryMode::Redoing => self.push_undo(entry),
866 }
867 }
868 }
869
870 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
871 if self
872 .undo_stack
873 .back()
874 .map_or(true, |e| e.selections != entry.selections)
875 {
876 self.undo_stack.push_back(entry);
877 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
878 self.undo_stack.pop_front();
879 }
880 }
881 }
882
883 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
884 if self
885 .redo_stack
886 .back()
887 .map_or(true, |e| e.selections != entry.selections)
888 {
889 self.redo_stack.push_back(entry);
890 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
891 self.redo_stack.pop_front();
892 }
893 }
894 }
895}
896
897struct RowHighlight {
898 index: usize,
899 range: Range<Anchor>,
900 color: Hsla,
901 should_autoscroll: bool,
902}
903
904#[derive(Clone, Debug)]
905struct AddSelectionsState {
906 above: bool,
907 stack: Vec<usize>,
908}
909
910#[derive(Clone)]
911struct SelectNextState {
912 query: AhoCorasick,
913 wordwise: bool,
914 done: bool,
915}
916
917impl std::fmt::Debug for SelectNextState {
918 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
919 f.debug_struct(std::any::type_name::<Self>())
920 .field("wordwise", &self.wordwise)
921 .field("done", &self.done)
922 .finish()
923 }
924}
925
926#[derive(Debug)]
927struct AutocloseRegion {
928 selection_id: usize,
929 range: Range<Anchor>,
930 pair: BracketPair,
931}
932
933#[derive(Debug)]
934struct SnippetState {
935 ranges: Vec<Vec<Range<Anchor>>>,
936 active_index: usize,
937 choices: Vec<Option<Vec<String>>>,
938}
939
940#[doc(hidden)]
941pub struct RenameState {
942 pub range: Range<Anchor>,
943 pub old_name: Arc<str>,
944 pub editor: View<Editor>,
945 block_id: CustomBlockId,
946}
947
948struct InvalidationStack<T>(Vec<T>);
949
950struct RegisteredInlineCompletionProvider {
951 provider: Arc<dyn InlineCompletionProviderHandle>,
952 _subscription: Subscription,
953}
954
955#[derive(Debug)]
956struct ActiveDiagnosticGroup {
957 primary_range: Range<Anchor>,
958 primary_message: String,
959 group_id: usize,
960 blocks: HashMap<CustomBlockId, Diagnostic>,
961 is_valid: bool,
962}
963
964#[derive(Serialize, Deserialize, Clone, Debug)]
965pub struct ClipboardSelection {
966 pub len: usize,
967 pub is_entire_line: bool,
968 pub first_line_indent: u32,
969}
970
971#[derive(Debug)]
972pub(crate) struct NavigationData {
973 cursor_anchor: Anchor,
974 cursor_position: Point,
975 scroll_anchor: ScrollAnchor,
976 scroll_top_row: u32,
977}
978
979#[derive(Debug, Clone, Copy, PartialEq, Eq)]
980pub enum GotoDefinitionKind {
981 Symbol,
982 Declaration,
983 Type,
984 Implementation,
985}
986
987#[derive(Debug, Clone)]
988enum InlayHintRefreshReason {
989 Toggle(bool),
990 SettingsChange(InlayHintSettings),
991 NewLinesShown,
992 BufferEdited(HashSet<Arc<Language>>),
993 RefreshRequested,
994 ExcerptsRemoved(Vec<ExcerptId>),
995}
996
997impl InlayHintRefreshReason {
998 fn description(&self) -> &'static str {
999 match self {
1000 Self::Toggle(_) => "toggle",
1001 Self::SettingsChange(_) => "settings change",
1002 Self::NewLinesShown => "new lines shown",
1003 Self::BufferEdited(_) => "buffer edited",
1004 Self::RefreshRequested => "refresh requested",
1005 Self::ExcerptsRemoved(_) => "excerpts removed",
1006 }
1007 }
1008}
1009
1010pub enum FormatTarget {
1011 Buffers,
1012 Ranges(Vec<Range<MultiBufferPoint>>),
1013}
1014
1015pub(crate) struct FocusedBlock {
1016 id: BlockId,
1017 focus_handle: WeakFocusHandle,
1018}
1019
1020#[derive(Clone)]
1021enum JumpData {
1022 MultiBufferRow {
1023 row: MultiBufferRow,
1024 line_offset_from_top: u32,
1025 },
1026 MultiBufferPoint {
1027 excerpt_id: ExcerptId,
1028 position: Point,
1029 anchor: text::Anchor,
1030 line_offset_from_top: u32,
1031 },
1032}
1033
1034pub enum MultibufferSelectionMode {
1035 First,
1036 All,
1037}
1038
1039impl Editor {
1040 pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
1041 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1042 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1043 Self::new(
1044 EditorMode::SingleLine { auto_width: false },
1045 buffer,
1046 None,
1047 false,
1048 cx,
1049 )
1050 }
1051
1052 pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
1053 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1054 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1055 Self::new(EditorMode::Full, buffer, None, false, cx)
1056 }
1057
1058 pub fn auto_width(cx: &mut ViewContext<Self>) -> Self {
1059 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1060 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1061 Self::new(
1062 EditorMode::SingleLine { auto_width: true },
1063 buffer,
1064 None,
1065 false,
1066 cx,
1067 )
1068 }
1069
1070 pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
1071 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1072 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1073 Self::new(
1074 EditorMode::AutoHeight { max_lines },
1075 buffer,
1076 None,
1077 false,
1078 cx,
1079 )
1080 }
1081
1082 pub fn for_buffer(
1083 buffer: Model<Buffer>,
1084 project: Option<Model<Project>>,
1085 cx: &mut ViewContext<Self>,
1086 ) -> Self {
1087 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1088 Self::new(EditorMode::Full, buffer, project, false, cx)
1089 }
1090
1091 pub fn for_multibuffer(
1092 buffer: Model<MultiBuffer>,
1093 project: Option<Model<Project>>,
1094 show_excerpt_controls: bool,
1095 cx: &mut ViewContext<Self>,
1096 ) -> Self {
1097 Self::new(EditorMode::Full, buffer, project, show_excerpt_controls, cx)
1098 }
1099
1100 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1101 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1102 let mut clone = Self::new(
1103 self.mode,
1104 self.buffer.clone(),
1105 self.project.clone(),
1106 show_excerpt_controls,
1107 cx,
1108 );
1109 self.display_map.update(cx, |display_map, cx| {
1110 let snapshot = display_map.snapshot(cx);
1111 clone.display_map.update(cx, |display_map, cx| {
1112 display_map.set_state(&snapshot, cx);
1113 });
1114 });
1115 clone.selections.clone_state(&self.selections);
1116 clone.scroll_manager.clone_state(&self.scroll_manager);
1117 clone.searchable = self.searchable;
1118 clone
1119 }
1120
1121 pub fn new(
1122 mode: EditorMode,
1123 buffer: Model<MultiBuffer>,
1124 project: Option<Model<Project>>,
1125 show_excerpt_controls: bool,
1126 cx: &mut ViewContext<Self>,
1127 ) -> Self {
1128 let style = cx.text_style();
1129 let font_size = style.font_size.to_pixels(cx.rem_size());
1130 let editor = cx.view().downgrade();
1131 let fold_placeholder = FoldPlaceholder {
1132 constrain_width: true,
1133 render: Arc::new(move |fold_id, fold_range, cx| {
1134 let editor = editor.clone();
1135 div()
1136 .id(fold_id)
1137 .bg(cx.theme().colors().ghost_element_background)
1138 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1139 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1140 .rounded_sm()
1141 .size_full()
1142 .cursor_pointer()
1143 .child("⋯")
1144 .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
1145 .on_click(move |_, cx| {
1146 editor
1147 .update(cx, |editor, cx| {
1148 editor.unfold_ranges(
1149 &[fold_range.start..fold_range.end],
1150 true,
1151 false,
1152 cx,
1153 );
1154 cx.stop_propagation();
1155 })
1156 .ok();
1157 })
1158 .into_any()
1159 }),
1160 merge_adjacent: true,
1161 ..Default::default()
1162 };
1163 let display_map = cx.new_model(|cx| {
1164 DisplayMap::new(
1165 buffer.clone(),
1166 style.font(),
1167 font_size,
1168 None,
1169 show_excerpt_controls,
1170 FILE_HEADER_HEIGHT,
1171 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1172 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1173 fold_placeholder,
1174 cx,
1175 )
1176 });
1177
1178 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1179
1180 let blink_manager = cx.new_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1181
1182 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1183 .then(|| language_settings::SoftWrap::None);
1184
1185 let mut project_subscriptions = Vec::new();
1186 if mode == EditorMode::Full {
1187 if let Some(project) = project.as_ref() {
1188 if buffer.read(cx).is_singleton() {
1189 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1190 cx.emit(EditorEvent::TitleChanged);
1191 }));
1192 }
1193 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1194 if let project::Event::RefreshInlayHints = event {
1195 editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1196 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1197 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1198 let focus_handle = editor.focus_handle(cx);
1199 if focus_handle.is_focused(cx) {
1200 let snapshot = buffer.read(cx).snapshot();
1201 for (range, snippet) in snippet_edits {
1202 let editor_range =
1203 language::range_from_lsp(*range).to_offset(&snapshot);
1204 editor
1205 .insert_snippet(&[editor_range], snippet.clone(), cx)
1206 .ok();
1207 }
1208 }
1209 }
1210 }
1211 }));
1212 if let Some(task_inventory) = project
1213 .read(cx)
1214 .task_store()
1215 .read(cx)
1216 .task_inventory()
1217 .cloned()
1218 {
1219 project_subscriptions.push(cx.observe(&task_inventory, |editor, _, cx| {
1220 editor.tasks_update_task = Some(editor.refresh_runnables(cx));
1221 }));
1222 }
1223 }
1224 }
1225
1226 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1227
1228 let inlay_hint_settings =
1229 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1230 let focus_handle = cx.focus_handle();
1231 cx.on_focus(&focus_handle, Self::handle_focus).detach();
1232 cx.on_focus_in(&focus_handle, Self::handle_focus_in)
1233 .detach();
1234 cx.on_focus_out(&focus_handle, Self::handle_focus_out)
1235 .detach();
1236 cx.on_blur(&focus_handle, Self::handle_blur).detach();
1237
1238 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1239 Some(false)
1240 } else {
1241 None
1242 };
1243
1244 let mut code_action_providers = Vec::new();
1245 if let Some(project) = project.clone() {
1246 get_unstaged_changes_for_buffers(
1247 &project,
1248 buffer.read(cx).all_buffers(),
1249 buffer.clone(),
1250 cx,
1251 );
1252 code_action_providers.push(Rc::new(project) as Rc<_>);
1253 }
1254
1255 let mut this = Self {
1256 focus_handle,
1257 show_cursor_when_unfocused: false,
1258 last_focused_descendant: None,
1259 buffer: buffer.clone(),
1260 display_map: display_map.clone(),
1261 selections,
1262 scroll_manager: ScrollManager::new(cx),
1263 columnar_selection_tail: None,
1264 add_selections_state: None,
1265 select_next_state: None,
1266 select_prev_state: None,
1267 selection_history: Default::default(),
1268 autoclose_regions: Default::default(),
1269 snippet_stack: Default::default(),
1270 select_larger_syntax_node_stack: Vec::new(),
1271 ime_transaction: Default::default(),
1272 active_diagnostics: None,
1273 soft_wrap_mode_override,
1274 completion_provider: project.clone().map(|project| Box::new(project) as _),
1275 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
1276 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1277 project,
1278 blink_manager: blink_manager.clone(),
1279 show_local_selections: true,
1280 show_scrollbars: true,
1281 mode,
1282 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1283 show_gutter: mode == EditorMode::Full,
1284 show_line_numbers: None,
1285 use_relative_line_numbers: None,
1286 show_git_diff_gutter: None,
1287 show_code_actions: None,
1288 show_runnables: None,
1289 show_wrap_guides: None,
1290 show_indent_guides,
1291 placeholder_text: None,
1292 highlight_order: 0,
1293 highlighted_rows: HashMap::default(),
1294 background_highlights: Default::default(),
1295 gutter_highlights: TreeMap::default(),
1296 scrollbar_marker_state: ScrollbarMarkerState::default(),
1297 active_indent_guides_state: ActiveIndentGuidesState::default(),
1298 nav_history: None,
1299 context_menu: RefCell::new(None),
1300 mouse_context_menu: None,
1301 completion_tasks: Default::default(),
1302 signature_help_state: SignatureHelpState::default(),
1303 auto_signature_help: None,
1304 find_all_references_task_sources: Vec::new(),
1305 next_completion_id: 0,
1306 next_inlay_id: 0,
1307 code_action_providers,
1308 available_code_actions: Default::default(),
1309 code_actions_task: Default::default(),
1310 document_highlights_task: Default::default(),
1311 linked_editing_range_task: Default::default(),
1312 pending_rename: Default::default(),
1313 searchable: true,
1314 cursor_shape: EditorSettings::get_global(cx)
1315 .cursor_shape
1316 .unwrap_or_default(),
1317 current_line_highlight: None,
1318 autoindent_mode: Some(AutoindentMode::EachLine),
1319 collapse_matches: false,
1320 workspace: None,
1321 input_enabled: true,
1322 use_modal_editing: mode == EditorMode::Full,
1323 read_only: false,
1324 use_autoclose: true,
1325 use_auto_surround: true,
1326 auto_replace_emoji_shortcode: false,
1327 leader_peer_id: None,
1328 remote_id: None,
1329 hover_state: Default::default(),
1330 hovered_link_state: Default::default(),
1331 inline_completion_provider: None,
1332 active_inline_completion: None,
1333 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1334
1335 gutter_hovered: false,
1336 pixel_position_of_newest_cursor: None,
1337 last_bounds: None,
1338 expect_bounds_change: None,
1339 gutter_dimensions: GutterDimensions::default(),
1340 style: None,
1341 show_cursor_names: false,
1342 hovered_cursors: Default::default(),
1343 next_editor_action_id: EditorActionId::default(),
1344 editor_actions: Rc::default(),
1345 show_inline_completions_override: None,
1346 enable_inline_completions: true,
1347 menu_inline_completions_policy: MenuInlineCompletionsPolicy::ByProvider,
1348 custom_context_menu: None,
1349 show_git_blame_gutter: false,
1350 show_git_blame_inline: false,
1351 show_selection_menu: None,
1352 show_git_blame_inline_delay_task: None,
1353 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1354 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1355 .session
1356 .restore_unsaved_buffers,
1357 blame: None,
1358 blame_subscription: None,
1359 tasks: Default::default(),
1360 _subscriptions: vec![
1361 cx.observe(&buffer, Self::on_buffer_changed),
1362 cx.subscribe(&buffer, Self::on_buffer_event),
1363 cx.observe(&display_map, Self::on_display_map_changed),
1364 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1365 cx.observe_global::<SettingsStore>(Self::settings_changed),
1366 cx.observe_window_activation(|editor, cx| {
1367 let active = cx.is_window_active();
1368 editor.blink_manager.update(cx, |blink_manager, cx| {
1369 if active {
1370 blink_manager.enable(cx);
1371 } else {
1372 blink_manager.disable(cx);
1373 }
1374 });
1375 }),
1376 ],
1377 tasks_update_task: None,
1378 linked_edit_ranges: Default::default(),
1379 previous_search_ranges: None,
1380 breadcrumb_header: None,
1381 focused_block: None,
1382 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1383 addons: HashMap::default(),
1384 registered_buffers: HashMap::default(),
1385 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1386 selection_mark_mode: false,
1387 toggle_fold_multiple_buffers: Task::ready(()),
1388 text_style_refinement: None,
1389 };
1390 this.tasks_update_task = Some(this.refresh_runnables(cx));
1391 this._subscriptions.extend(project_subscriptions);
1392
1393 this.end_selection(cx);
1394 this.scroll_manager.show_scrollbar(cx);
1395
1396 if mode == EditorMode::Full {
1397 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1398 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1399
1400 if this.git_blame_inline_enabled {
1401 this.git_blame_inline_enabled = true;
1402 this.start_git_blame_inline(false, cx);
1403 }
1404
1405 if let Some(buffer) = buffer.read(cx).as_singleton() {
1406 if let Some(project) = this.project.as_ref() {
1407 let lsp_store = project.read(cx).lsp_store();
1408 let handle = lsp_store.update(cx, |lsp_store, cx| {
1409 lsp_store.register_buffer_with_language_servers(&buffer, cx)
1410 });
1411 this.registered_buffers
1412 .insert(buffer.read(cx).remote_id(), handle);
1413 }
1414 }
1415 }
1416
1417 this.report_editor_event("Editor Opened", None, cx);
1418 this
1419 }
1420
1421 pub fn mouse_menu_is_focused(&self, cx: &WindowContext) -> bool {
1422 self.mouse_context_menu
1423 .as_ref()
1424 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(cx))
1425 }
1426
1427 fn key_context(&self, cx: &ViewContext<Self>) -> KeyContext {
1428 let mut key_context = KeyContext::new_with_defaults();
1429 key_context.add("Editor");
1430 let mode = match self.mode {
1431 EditorMode::SingleLine { .. } => "single_line",
1432 EditorMode::AutoHeight { .. } => "auto_height",
1433 EditorMode::Full => "full",
1434 };
1435
1436 if EditorSettings::jupyter_enabled(cx) {
1437 key_context.add("jupyter");
1438 }
1439
1440 key_context.set("mode", mode);
1441 if self.pending_rename.is_some() {
1442 key_context.add("renaming");
1443 }
1444 match self.context_menu.borrow().as_ref() {
1445 Some(CodeContextMenu::Completions(_)) => {
1446 key_context.add("menu");
1447 key_context.add("showing_completions")
1448 }
1449 Some(CodeContextMenu::CodeActions(_)) => {
1450 key_context.add("menu");
1451 key_context.add("showing_code_actions")
1452 }
1453 None => {}
1454 }
1455
1456 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
1457 if !self.focus_handle(cx).contains_focused(cx)
1458 || (self.is_focused(cx) || self.mouse_menu_is_focused(cx))
1459 {
1460 for addon in self.addons.values() {
1461 addon.extend_key_context(&mut key_context, cx)
1462 }
1463 }
1464
1465 if let Some(extension) = self
1466 .buffer
1467 .read(cx)
1468 .as_singleton()
1469 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
1470 {
1471 key_context.set("extension", extension.to_string());
1472 }
1473
1474 if self.has_active_inline_completion() {
1475 key_context.add("copilot_suggestion");
1476 key_context.add("inline_completion");
1477 }
1478
1479 if self.selection_mark_mode {
1480 key_context.add("selection_mode");
1481 }
1482
1483 key_context
1484 }
1485
1486 pub fn new_file(
1487 workspace: &mut Workspace,
1488 _: &workspace::NewFile,
1489 cx: &mut ViewContext<Workspace>,
1490 ) {
1491 Self::new_in_workspace(workspace, cx).detach_and_prompt_err(
1492 "Failed to create buffer",
1493 cx,
1494 |e, _| match e.error_code() {
1495 ErrorCode::RemoteUpgradeRequired => Some(format!(
1496 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1497 e.error_tag("required").unwrap_or("the latest version")
1498 )),
1499 _ => None,
1500 },
1501 );
1502 }
1503
1504 pub fn new_in_workspace(
1505 workspace: &mut Workspace,
1506 cx: &mut ViewContext<Workspace>,
1507 ) -> Task<Result<View<Editor>>> {
1508 let project = workspace.project().clone();
1509 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1510
1511 cx.spawn(|workspace, mut cx| async move {
1512 let buffer = create.await?;
1513 workspace.update(&mut cx, |workspace, cx| {
1514 let editor =
1515 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx));
1516 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
1517 editor
1518 })
1519 })
1520 }
1521
1522 fn new_file_vertical(
1523 workspace: &mut Workspace,
1524 _: &workspace::NewFileSplitVertical,
1525 cx: &mut ViewContext<Workspace>,
1526 ) {
1527 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), cx)
1528 }
1529
1530 fn new_file_horizontal(
1531 workspace: &mut Workspace,
1532 _: &workspace::NewFileSplitHorizontal,
1533 cx: &mut ViewContext<Workspace>,
1534 ) {
1535 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), cx)
1536 }
1537
1538 fn new_file_in_direction(
1539 workspace: &mut Workspace,
1540 direction: SplitDirection,
1541 cx: &mut ViewContext<Workspace>,
1542 ) {
1543 let project = workspace.project().clone();
1544 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1545
1546 cx.spawn(|workspace, mut cx| async move {
1547 let buffer = create.await?;
1548 workspace.update(&mut cx, move |workspace, cx| {
1549 workspace.split_item(
1550 direction,
1551 Box::new(
1552 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)),
1553 ),
1554 cx,
1555 )
1556 })?;
1557 anyhow::Ok(())
1558 })
1559 .detach_and_prompt_err("Failed to create buffer", cx, |e, _| match e.error_code() {
1560 ErrorCode::RemoteUpgradeRequired => Some(format!(
1561 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1562 e.error_tag("required").unwrap_or("the latest version")
1563 )),
1564 _ => None,
1565 });
1566 }
1567
1568 pub fn leader_peer_id(&self) -> Option<PeerId> {
1569 self.leader_peer_id
1570 }
1571
1572 pub fn buffer(&self) -> &Model<MultiBuffer> {
1573 &self.buffer
1574 }
1575
1576 pub fn workspace(&self) -> Option<View<Workspace>> {
1577 self.workspace.as_ref()?.0.upgrade()
1578 }
1579
1580 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
1581 self.buffer().read(cx).title(cx)
1582 }
1583
1584 pub fn snapshot(&self, cx: &mut WindowContext) -> EditorSnapshot {
1585 let git_blame_gutter_max_author_length = self
1586 .render_git_blame_gutter(cx)
1587 .then(|| {
1588 if let Some(blame) = self.blame.as_ref() {
1589 let max_author_length =
1590 blame.update(cx, |blame, cx| blame.max_author_length(cx));
1591 Some(max_author_length)
1592 } else {
1593 None
1594 }
1595 })
1596 .flatten();
1597
1598 EditorSnapshot {
1599 mode: self.mode,
1600 show_gutter: self.show_gutter,
1601 show_line_numbers: self.show_line_numbers,
1602 show_git_diff_gutter: self.show_git_diff_gutter,
1603 show_code_actions: self.show_code_actions,
1604 show_runnables: self.show_runnables,
1605 git_blame_gutter_max_author_length,
1606 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1607 scroll_anchor: self.scroll_manager.anchor(),
1608 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1609 placeholder_text: self.placeholder_text.clone(),
1610 is_focused: self.focus_handle.is_focused(cx),
1611 current_line_highlight: self
1612 .current_line_highlight
1613 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
1614 gutter_hovered: self.gutter_hovered,
1615 }
1616 }
1617
1618 pub fn language_at<T: ToOffset>(&self, point: T, cx: &AppContext) -> Option<Arc<Language>> {
1619 self.buffer.read(cx).language_at(point, cx)
1620 }
1621
1622 pub fn file_at<T: ToOffset>(
1623 &self,
1624 point: T,
1625 cx: &AppContext,
1626 ) -> Option<Arc<dyn language::File>> {
1627 self.buffer.read(cx).read(cx).file_at(point).cloned()
1628 }
1629
1630 pub fn active_excerpt(
1631 &self,
1632 cx: &AppContext,
1633 ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
1634 self.buffer
1635 .read(cx)
1636 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1637 }
1638
1639 pub fn mode(&self) -> EditorMode {
1640 self.mode
1641 }
1642
1643 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1644 self.collaboration_hub.as_deref()
1645 }
1646
1647 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1648 self.collaboration_hub = Some(hub);
1649 }
1650
1651 pub fn set_custom_context_menu(
1652 &mut self,
1653 f: impl 'static
1654 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
1655 ) {
1656 self.custom_context_menu = Some(Box::new(f))
1657 }
1658
1659 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
1660 self.completion_provider = provider;
1661 }
1662
1663 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
1664 self.semantics_provider.clone()
1665 }
1666
1667 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
1668 self.semantics_provider = provider;
1669 }
1670
1671 pub fn set_inline_completion_provider<T>(
1672 &mut self,
1673 provider: Option<Model<T>>,
1674 cx: &mut ViewContext<Self>,
1675 ) where
1676 T: InlineCompletionProvider,
1677 {
1678 self.inline_completion_provider =
1679 provider.map(|provider| RegisteredInlineCompletionProvider {
1680 _subscription: cx.observe(&provider, |this, _, cx| {
1681 if this.focus_handle.is_focused(cx) {
1682 this.update_visible_inline_completion(cx);
1683 }
1684 }),
1685 provider: Arc::new(provider),
1686 });
1687 self.refresh_inline_completion(false, false, cx);
1688 }
1689
1690 pub fn placeholder_text(&self, _cx: &WindowContext) -> Option<&str> {
1691 self.placeholder_text.as_deref()
1692 }
1693
1694 pub fn set_placeholder_text(
1695 &mut self,
1696 placeholder_text: impl Into<Arc<str>>,
1697 cx: &mut ViewContext<Self>,
1698 ) {
1699 let placeholder_text = Some(placeholder_text.into());
1700 if self.placeholder_text != placeholder_text {
1701 self.placeholder_text = placeholder_text;
1702 cx.notify();
1703 }
1704 }
1705
1706 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
1707 self.cursor_shape = cursor_shape;
1708
1709 // Disrupt blink for immediate user feedback that the cursor shape has changed
1710 self.blink_manager.update(cx, BlinkManager::show_cursor);
1711
1712 cx.notify();
1713 }
1714
1715 pub fn set_current_line_highlight(
1716 &mut self,
1717 current_line_highlight: Option<CurrentLineHighlight>,
1718 ) {
1719 self.current_line_highlight = current_line_highlight;
1720 }
1721
1722 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1723 self.collapse_matches = collapse_matches;
1724 }
1725
1726 pub fn register_buffers_with_language_servers(&mut self, cx: &mut ViewContext<Self>) {
1727 let buffers = self.buffer.read(cx).all_buffers();
1728 let Some(lsp_store) = self.lsp_store(cx) else {
1729 return;
1730 };
1731 lsp_store.update(cx, |lsp_store, cx| {
1732 for buffer in buffers {
1733 self.registered_buffers
1734 .entry(buffer.read(cx).remote_id())
1735 .or_insert_with(|| {
1736 lsp_store.register_buffer_with_language_servers(&buffer, cx)
1737 });
1738 }
1739 })
1740 }
1741
1742 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1743 if self.collapse_matches {
1744 return range.start..range.start;
1745 }
1746 range.clone()
1747 }
1748
1749 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
1750 if self.display_map.read(cx).clip_at_line_ends != clip {
1751 self.display_map
1752 .update(cx, |map, _| map.clip_at_line_ends = clip);
1753 }
1754 }
1755
1756 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1757 self.input_enabled = input_enabled;
1758 }
1759
1760 pub fn set_inline_completions_enabled(&mut self, enabled: bool, cx: &mut ViewContext<Self>) {
1761 self.enable_inline_completions = enabled;
1762 if !self.enable_inline_completions {
1763 self.take_active_inline_completion(cx);
1764 cx.notify();
1765 }
1766 }
1767
1768 pub fn set_menu_inline_completions_policy(&mut self, value: MenuInlineCompletionsPolicy) {
1769 self.menu_inline_completions_policy = value;
1770 }
1771
1772 pub fn set_autoindent(&mut self, autoindent: bool) {
1773 if autoindent {
1774 self.autoindent_mode = Some(AutoindentMode::EachLine);
1775 } else {
1776 self.autoindent_mode = None;
1777 }
1778 }
1779
1780 pub fn read_only(&self, cx: &AppContext) -> bool {
1781 self.read_only || self.buffer.read(cx).read_only()
1782 }
1783
1784 pub fn set_read_only(&mut self, read_only: bool) {
1785 self.read_only = read_only;
1786 }
1787
1788 pub fn set_use_autoclose(&mut self, autoclose: bool) {
1789 self.use_autoclose = autoclose;
1790 }
1791
1792 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
1793 self.use_auto_surround = auto_surround;
1794 }
1795
1796 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
1797 self.auto_replace_emoji_shortcode = auto_replace;
1798 }
1799
1800 pub fn toggle_inline_completions(
1801 &mut self,
1802 _: &ToggleInlineCompletions,
1803 cx: &mut ViewContext<Self>,
1804 ) {
1805 if self.show_inline_completions_override.is_some() {
1806 self.set_show_inline_completions(None, cx);
1807 } else {
1808 let cursor = self.selections.newest_anchor().head();
1809 if let Some((buffer, cursor_buffer_position)) =
1810 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
1811 {
1812 let show_inline_completions =
1813 !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx);
1814 self.set_show_inline_completions(Some(show_inline_completions), cx);
1815 }
1816 }
1817 }
1818
1819 pub fn set_show_inline_completions(
1820 &mut self,
1821 show_inline_completions: Option<bool>,
1822 cx: &mut ViewContext<Self>,
1823 ) {
1824 self.show_inline_completions_override = show_inline_completions;
1825 self.refresh_inline_completion(false, true, cx);
1826 }
1827
1828 pub fn inline_completions_enabled(&self, cx: &AppContext) -> bool {
1829 let cursor = self.selections.newest_anchor().head();
1830 if let Some((buffer, buffer_position)) =
1831 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
1832 {
1833 self.should_show_inline_completions(&buffer, buffer_position, cx)
1834 } else {
1835 false
1836 }
1837 }
1838
1839 fn should_show_inline_completions(
1840 &self,
1841 buffer: &Model<Buffer>,
1842 buffer_position: language::Anchor,
1843 cx: &AppContext,
1844 ) -> bool {
1845 if !self.snippet_stack.is_empty() {
1846 return false;
1847 }
1848
1849 if self.inline_completions_disabled_in_scope(buffer, buffer_position, cx) {
1850 return false;
1851 }
1852
1853 if let Some(provider) = self.inline_completion_provider() {
1854 if let Some(show_inline_completions) = self.show_inline_completions_override {
1855 show_inline_completions
1856 } else {
1857 self.mode == EditorMode::Full && provider.is_enabled(buffer, buffer_position, cx)
1858 }
1859 } else {
1860 false
1861 }
1862 }
1863
1864 fn inline_completions_disabled_in_scope(
1865 &self,
1866 buffer: &Model<Buffer>,
1867 buffer_position: language::Anchor,
1868 cx: &AppContext,
1869 ) -> bool {
1870 let snapshot = buffer.read(cx).snapshot();
1871 let settings = snapshot.settings_at(buffer_position, cx);
1872
1873 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
1874 return false;
1875 };
1876
1877 scope.override_name().map_or(false, |scope_name| {
1878 settings
1879 .inline_completions_disabled_in
1880 .iter()
1881 .any(|s| s == scope_name)
1882 })
1883 }
1884
1885 pub fn set_use_modal_editing(&mut self, to: bool) {
1886 self.use_modal_editing = to;
1887 }
1888
1889 pub fn use_modal_editing(&self) -> bool {
1890 self.use_modal_editing
1891 }
1892
1893 fn selections_did_change(
1894 &mut self,
1895 local: bool,
1896 old_cursor_position: &Anchor,
1897 show_completions: bool,
1898 cx: &mut ViewContext<Self>,
1899 ) {
1900 cx.invalidate_character_coordinates();
1901
1902 // Copy selections to primary selection buffer
1903 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
1904 if local {
1905 let selections = self.selections.all::<usize>(cx);
1906 let buffer_handle = self.buffer.read(cx).read(cx);
1907
1908 let mut text = String::new();
1909 for (index, selection) in selections.iter().enumerate() {
1910 let text_for_selection = buffer_handle
1911 .text_for_range(selection.start..selection.end)
1912 .collect::<String>();
1913
1914 text.push_str(&text_for_selection);
1915 if index != selections.len() - 1 {
1916 text.push('\n');
1917 }
1918 }
1919
1920 if !text.is_empty() {
1921 cx.write_to_primary(ClipboardItem::new_string(text));
1922 }
1923 }
1924
1925 if self.focus_handle.is_focused(cx) && self.leader_peer_id.is_none() {
1926 self.buffer.update(cx, |buffer, cx| {
1927 buffer.set_active_selections(
1928 &self.selections.disjoint_anchors(),
1929 self.selections.line_mode,
1930 self.cursor_shape,
1931 cx,
1932 )
1933 });
1934 }
1935 let display_map = self
1936 .display_map
1937 .update(cx, |display_map, cx| display_map.snapshot(cx));
1938 let buffer = &display_map.buffer_snapshot;
1939 self.add_selections_state = None;
1940 self.select_next_state = None;
1941 self.select_prev_state = None;
1942 self.select_larger_syntax_node_stack.clear();
1943 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
1944 self.snippet_stack
1945 .invalidate(&self.selections.disjoint_anchors(), buffer);
1946 self.take_rename(false, cx);
1947
1948 let new_cursor_position = self.selections.newest_anchor().head();
1949
1950 self.push_to_nav_history(
1951 *old_cursor_position,
1952 Some(new_cursor_position.to_point(buffer)),
1953 cx,
1954 );
1955
1956 if local {
1957 let new_cursor_position = self.selections.newest_anchor().head();
1958 let mut context_menu = self.context_menu.borrow_mut();
1959 let completion_menu = match context_menu.as_ref() {
1960 Some(CodeContextMenu::Completions(menu)) => Some(menu),
1961 _ => {
1962 *context_menu = None;
1963 None
1964 }
1965 };
1966
1967 if let Some(completion_menu) = completion_menu {
1968 let cursor_position = new_cursor_position.to_offset(buffer);
1969 let (word_range, kind) =
1970 buffer.surrounding_word(completion_menu.initial_position, true);
1971 if kind == Some(CharKind::Word)
1972 && word_range.to_inclusive().contains(&cursor_position)
1973 {
1974 let mut completion_menu = completion_menu.clone();
1975 drop(context_menu);
1976
1977 let query = Self::completion_query(buffer, cursor_position);
1978 cx.spawn(move |this, mut cx| async move {
1979 completion_menu
1980 .filter(query.as_deref(), cx.background_executor().clone())
1981 .await;
1982
1983 this.update(&mut cx, |this, cx| {
1984 let mut context_menu = this.context_menu.borrow_mut();
1985 let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
1986 else {
1987 return;
1988 };
1989
1990 if menu.id > completion_menu.id {
1991 return;
1992 }
1993
1994 *context_menu = Some(CodeContextMenu::Completions(completion_menu));
1995 drop(context_menu);
1996 cx.notify();
1997 })
1998 })
1999 .detach();
2000
2001 if show_completions {
2002 self.show_completions(&ShowCompletions { trigger: None }, cx);
2003 }
2004 } else {
2005 drop(context_menu);
2006 self.hide_context_menu(cx);
2007 }
2008 } else {
2009 drop(context_menu);
2010 }
2011
2012 hide_hover(self, cx);
2013
2014 if old_cursor_position.to_display_point(&display_map).row()
2015 != new_cursor_position.to_display_point(&display_map).row()
2016 {
2017 self.available_code_actions.take();
2018 }
2019 self.refresh_code_actions(cx);
2020 self.refresh_document_highlights(cx);
2021 refresh_matching_bracket_highlights(self, cx);
2022 self.update_visible_inline_completion(cx);
2023 linked_editing_ranges::refresh_linked_ranges(self, cx);
2024 if self.git_blame_inline_enabled {
2025 self.start_inline_blame_timer(cx);
2026 }
2027 }
2028
2029 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2030 cx.emit(EditorEvent::SelectionsChanged { local });
2031
2032 if self.selections.disjoint_anchors().len() == 1 {
2033 cx.emit(SearchEvent::ActiveMatchChanged)
2034 }
2035 cx.notify();
2036 }
2037
2038 pub fn change_selections<R>(
2039 &mut self,
2040 autoscroll: Option<Autoscroll>,
2041 cx: &mut ViewContext<Self>,
2042 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2043 ) -> R {
2044 self.change_selections_inner(autoscroll, true, cx, change)
2045 }
2046
2047 pub fn change_selections_inner<R>(
2048 &mut self,
2049 autoscroll: Option<Autoscroll>,
2050 request_completions: bool,
2051 cx: &mut ViewContext<Self>,
2052 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2053 ) -> R {
2054 let old_cursor_position = self.selections.newest_anchor().head();
2055 self.push_to_selection_history();
2056
2057 let (changed, result) = self.selections.change_with(cx, change);
2058
2059 if changed {
2060 if let Some(autoscroll) = autoscroll {
2061 self.request_autoscroll(autoscroll, cx);
2062 }
2063 self.selections_did_change(true, &old_cursor_position, request_completions, cx);
2064
2065 if self.should_open_signature_help_automatically(
2066 &old_cursor_position,
2067 self.signature_help_state.backspace_pressed(),
2068 cx,
2069 ) {
2070 self.show_signature_help(&ShowSignatureHelp, cx);
2071 }
2072 self.signature_help_state.set_backspace_pressed(false);
2073 }
2074
2075 result
2076 }
2077
2078 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2079 where
2080 I: IntoIterator<Item = (Range<S>, T)>,
2081 S: ToOffset,
2082 T: Into<Arc<str>>,
2083 {
2084 if self.read_only(cx) {
2085 return;
2086 }
2087
2088 self.buffer
2089 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2090 }
2091
2092 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2093 where
2094 I: IntoIterator<Item = (Range<S>, T)>,
2095 S: ToOffset,
2096 T: Into<Arc<str>>,
2097 {
2098 if self.read_only(cx) {
2099 return;
2100 }
2101
2102 self.buffer.update(cx, |buffer, cx| {
2103 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2104 });
2105 }
2106
2107 pub fn edit_with_block_indent<I, S, T>(
2108 &mut self,
2109 edits: I,
2110 original_indent_columns: Vec<u32>,
2111 cx: &mut ViewContext<Self>,
2112 ) where
2113 I: IntoIterator<Item = (Range<S>, T)>,
2114 S: ToOffset,
2115 T: Into<Arc<str>>,
2116 {
2117 if self.read_only(cx) {
2118 return;
2119 }
2120
2121 self.buffer.update(cx, |buffer, cx| {
2122 buffer.edit(
2123 edits,
2124 Some(AutoindentMode::Block {
2125 original_indent_columns,
2126 }),
2127 cx,
2128 )
2129 });
2130 }
2131
2132 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
2133 self.hide_context_menu(cx);
2134
2135 match phase {
2136 SelectPhase::Begin {
2137 position,
2138 add,
2139 click_count,
2140 } => self.begin_selection(position, add, click_count, cx),
2141 SelectPhase::BeginColumnar {
2142 position,
2143 goal_column,
2144 reset,
2145 } => self.begin_columnar_selection(position, goal_column, reset, cx),
2146 SelectPhase::Extend {
2147 position,
2148 click_count,
2149 } => self.extend_selection(position, click_count, cx),
2150 SelectPhase::Update {
2151 position,
2152 goal_column,
2153 scroll_delta,
2154 } => self.update_selection(position, goal_column, scroll_delta, cx),
2155 SelectPhase::End => self.end_selection(cx),
2156 }
2157 }
2158
2159 fn extend_selection(
2160 &mut self,
2161 position: DisplayPoint,
2162 click_count: usize,
2163 cx: &mut ViewContext<Self>,
2164 ) {
2165 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2166 let tail = self.selections.newest::<usize>(cx).tail();
2167 self.begin_selection(position, false, click_count, cx);
2168
2169 let position = position.to_offset(&display_map, Bias::Left);
2170 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2171
2172 let mut pending_selection = self
2173 .selections
2174 .pending_anchor()
2175 .expect("extend_selection not called with pending selection");
2176 if position >= tail {
2177 pending_selection.start = tail_anchor;
2178 } else {
2179 pending_selection.end = tail_anchor;
2180 pending_selection.reversed = true;
2181 }
2182
2183 let mut pending_mode = self.selections.pending_mode().unwrap();
2184 match &mut pending_mode {
2185 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2186 _ => {}
2187 }
2188
2189 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
2190 s.set_pending(pending_selection, pending_mode)
2191 });
2192 }
2193
2194 fn begin_selection(
2195 &mut self,
2196 position: DisplayPoint,
2197 add: bool,
2198 click_count: usize,
2199 cx: &mut ViewContext<Self>,
2200 ) {
2201 if !self.focus_handle.is_focused(cx) {
2202 self.last_focused_descendant = None;
2203 cx.focus(&self.focus_handle);
2204 }
2205
2206 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2207 let buffer = &display_map.buffer_snapshot;
2208 let newest_selection = self.selections.newest_anchor().clone();
2209 let position = display_map.clip_point(position, Bias::Left);
2210
2211 let start;
2212 let end;
2213 let mode;
2214 let mut auto_scroll;
2215 match click_count {
2216 1 => {
2217 start = buffer.anchor_before(position.to_point(&display_map));
2218 end = start;
2219 mode = SelectMode::Character;
2220 auto_scroll = true;
2221 }
2222 2 => {
2223 let range = movement::surrounding_word(&display_map, position);
2224 start = buffer.anchor_before(range.start.to_point(&display_map));
2225 end = buffer.anchor_before(range.end.to_point(&display_map));
2226 mode = SelectMode::Word(start..end);
2227 auto_scroll = true;
2228 }
2229 3 => {
2230 let position = display_map
2231 .clip_point(position, Bias::Left)
2232 .to_point(&display_map);
2233 let line_start = display_map.prev_line_boundary(position).0;
2234 let next_line_start = buffer.clip_point(
2235 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2236 Bias::Left,
2237 );
2238 start = buffer.anchor_before(line_start);
2239 end = buffer.anchor_before(next_line_start);
2240 mode = SelectMode::Line(start..end);
2241 auto_scroll = true;
2242 }
2243 _ => {
2244 start = buffer.anchor_before(0);
2245 end = buffer.anchor_before(buffer.len());
2246 mode = SelectMode::All;
2247 auto_scroll = false;
2248 }
2249 }
2250 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2251
2252 let point_to_delete: Option<usize> = {
2253 let selected_points: Vec<Selection<Point>> =
2254 self.selections.disjoint_in_range(start..end, cx);
2255
2256 if !add || click_count > 1 {
2257 None
2258 } else if !selected_points.is_empty() {
2259 Some(selected_points[0].id)
2260 } else {
2261 let clicked_point_already_selected =
2262 self.selections.disjoint.iter().find(|selection| {
2263 selection.start.to_point(buffer) == start.to_point(buffer)
2264 || selection.end.to_point(buffer) == end.to_point(buffer)
2265 });
2266
2267 clicked_point_already_selected.map(|selection| selection.id)
2268 }
2269 };
2270
2271 let selections_count = self.selections.count();
2272
2273 self.change_selections(auto_scroll.then(Autoscroll::newest), cx, |s| {
2274 if let Some(point_to_delete) = point_to_delete {
2275 s.delete(point_to_delete);
2276
2277 if selections_count == 1 {
2278 s.set_pending_anchor_range(start..end, mode);
2279 }
2280 } else {
2281 if !add {
2282 s.clear_disjoint();
2283 } else if click_count > 1 {
2284 s.delete(newest_selection.id)
2285 }
2286
2287 s.set_pending_anchor_range(start..end, mode);
2288 }
2289 });
2290 }
2291
2292 fn begin_columnar_selection(
2293 &mut self,
2294 position: DisplayPoint,
2295 goal_column: u32,
2296 reset: bool,
2297 cx: &mut ViewContext<Self>,
2298 ) {
2299 if !self.focus_handle.is_focused(cx) {
2300 self.last_focused_descendant = None;
2301 cx.focus(&self.focus_handle);
2302 }
2303
2304 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2305
2306 if reset {
2307 let pointer_position = display_map
2308 .buffer_snapshot
2309 .anchor_before(position.to_point(&display_map));
2310
2311 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
2312 s.clear_disjoint();
2313 s.set_pending_anchor_range(
2314 pointer_position..pointer_position,
2315 SelectMode::Character,
2316 );
2317 });
2318 }
2319
2320 let tail = self.selections.newest::<Point>(cx).tail();
2321 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2322
2323 if !reset {
2324 self.select_columns(
2325 tail.to_display_point(&display_map),
2326 position,
2327 goal_column,
2328 &display_map,
2329 cx,
2330 );
2331 }
2332 }
2333
2334 fn update_selection(
2335 &mut self,
2336 position: DisplayPoint,
2337 goal_column: u32,
2338 scroll_delta: gpui::Point<f32>,
2339 cx: &mut ViewContext<Self>,
2340 ) {
2341 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2342
2343 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2344 let tail = tail.to_display_point(&display_map);
2345 self.select_columns(tail, position, goal_column, &display_map, cx);
2346 } else if let Some(mut pending) = self.selections.pending_anchor() {
2347 let buffer = self.buffer.read(cx).snapshot(cx);
2348 let head;
2349 let tail;
2350 let mode = self.selections.pending_mode().unwrap();
2351 match &mode {
2352 SelectMode::Character => {
2353 head = position.to_point(&display_map);
2354 tail = pending.tail().to_point(&buffer);
2355 }
2356 SelectMode::Word(original_range) => {
2357 let original_display_range = original_range.start.to_display_point(&display_map)
2358 ..original_range.end.to_display_point(&display_map);
2359 let original_buffer_range = original_display_range.start.to_point(&display_map)
2360 ..original_display_range.end.to_point(&display_map);
2361 if movement::is_inside_word(&display_map, position)
2362 || original_display_range.contains(&position)
2363 {
2364 let word_range = movement::surrounding_word(&display_map, position);
2365 if word_range.start < original_display_range.start {
2366 head = word_range.start.to_point(&display_map);
2367 } else {
2368 head = word_range.end.to_point(&display_map);
2369 }
2370 } else {
2371 head = position.to_point(&display_map);
2372 }
2373
2374 if head <= original_buffer_range.start {
2375 tail = original_buffer_range.end;
2376 } else {
2377 tail = original_buffer_range.start;
2378 }
2379 }
2380 SelectMode::Line(original_range) => {
2381 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2382
2383 let position = display_map
2384 .clip_point(position, Bias::Left)
2385 .to_point(&display_map);
2386 let line_start = display_map.prev_line_boundary(position).0;
2387 let next_line_start = buffer.clip_point(
2388 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2389 Bias::Left,
2390 );
2391
2392 if line_start < original_range.start {
2393 head = line_start
2394 } else {
2395 head = next_line_start
2396 }
2397
2398 if head <= original_range.start {
2399 tail = original_range.end;
2400 } else {
2401 tail = original_range.start;
2402 }
2403 }
2404 SelectMode::All => {
2405 return;
2406 }
2407 };
2408
2409 if head < tail {
2410 pending.start = buffer.anchor_before(head);
2411 pending.end = buffer.anchor_before(tail);
2412 pending.reversed = true;
2413 } else {
2414 pending.start = buffer.anchor_before(tail);
2415 pending.end = buffer.anchor_before(head);
2416 pending.reversed = false;
2417 }
2418
2419 self.change_selections(None, cx, |s| {
2420 s.set_pending(pending, mode);
2421 });
2422 } else {
2423 log::error!("update_selection dispatched with no pending selection");
2424 return;
2425 }
2426
2427 self.apply_scroll_delta(scroll_delta, cx);
2428 cx.notify();
2429 }
2430
2431 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
2432 self.columnar_selection_tail.take();
2433 if self.selections.pending_anchor().is_some() {
2434 let selections = self.selections.all::<usize>(cx);
2435 self.change_selections(None, cx, |s| {
2436 s.select(selections);
2437 s.clear_pending();
2438 });
2439 }
2440 }
2441
2442 fn select_columns(
2443 &mut self,
2444 tail: DisplayPoint,
2445 head: DisplayPoint,
2446 goal_column: u32,
2447 display_map: &DisplaySnapshot,
2448 cx: &mut ViewContext<Self>,
2449 ) {
2450 let start_row = cmp::min(tail.row(), head.row());
2451 let end_row = cmp::max(tail.row(), head.row());
2452 let start_column = cmp::min(tail.column(), goal_column);
2453 let end_column = cmp::max(tail.column(), goal_column);
2454 let reversed = start_column < tail.column();
2455
2456 let selection_ranges = (start_row.0..=end_row.0)
2457 .map(DisplayRow)
2458 .filter_map(|row| {
2459 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2460 let start = display_map
2461 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2462 .to_point(display_map);
2463 let end = display_map
2464 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2465 .to_point(display_map);
2466 if reversed {
2467 Some(end..start)
2468 } else {
2469 Some(start..end)
2470 }
2471 } else {
2472 None
2473 }
2474 })
2475 .collect::<Vec<_>>();
2476
2477 self.change_selections(None, cx, |s| {
2478 s.select_ranges(selection_ranges);
2479 });
2480 cx.notify();
2481 }
2482
2483 pub fn has_pending_nonempty_selection(&self) -> bool {
2484 let pending_nonempty_selection = match self.selections.pending_anchor() {
2485 Some(Selection { start, end, .. }) => start != end,
2486 None => false,
2487 };
2488
2489 pending_nonempty_selection
2490 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2491 }
2492
2493 pub fn has_pending_selection(&self) -> bool {
2494 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2495 }
2496
2497 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
2498 self.selection_mark_mode = false;
2499
2500 if self.clear_expanded_diff_hunks(cx) {
2501 cx.notify();
2502 return;
2503 }
2504 if self.dismiss_menus_and_popups(true, cx) {
2505 return;
2506 }
2507
2508 if self.mode == EditorMode::Full
2509 && self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel())
2510 {
2511 return;
2512 }
2513
2514 cx.propagate();
2515 }
2516
2517 pub fn dismiss_menus_and_popups(
2518 &mut self,
2519 should_report_inline_completion_event: bool,
2520 cx: &mut ViewContext<Self>,
2521 ) -> bool {
2522 if self.take_rename(false, cx).is_some() {
2523 return true;
2524 }
2525
2526 if hide_hover(self, cx) {
2527 return true;
2528 }
2529
2530 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2531 return true;
2532 }
2533
2534 if self.hide_context_menu(cx).is_some() {
2535 if self.show_inline_completions_in_menu(cx) && self.has_active_inline_completion() {
2536 self.update_visible_inline_completion(cx);
2537 }
2538 return true;
2539 }
2540
2541 if self.mouse_context_menu.take().is_some() {
2542 return true;
2543 }
2544
2545 if self.discard_inline_completion(should_report_inline_completion_event, cx) {
2546 return true;
2547 }
2548
2549 if self.snippet_stack.pop().is_some() {
2550 return true;
2551 }
2552
2553 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
2554 self.dismiss_diagnostics(cx);
2555 return true;
2556 }
2557
2558 false
2559 }
2560
2561 fn linked_editing_ranges_for(
2562 &self,
2563 selection: Range<text::Anchor>,
2564 cx: &AppContext,
2565 ) -> Option<HashMap<Model<Buffer>, Vec<Range<text::Anchor>>>> {
2566 if self.linked_edit_ranges.is_empty() {
2567 return None;
2568 }
2569 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2570 selection.end.buffer_id.and_then(|end_buffer_id| {
2571 if selection.start.buffer_id != Some(end_buffer_id) {
2572 return None;
2573 }
2574 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2575 let snapshot = buffer.read(cx).snapshot();
2576 self.linked_edit_ranges
2577 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2578 .map(|ranges| (ranges, snapshot, buffer))
2579 })?;
2580 use text::ToOffset as TO;
2581 // find offset from the start of current range to current cursor position
2582 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2583
2584 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2585 let start_difference = start_offset - start_byte_offset;
2586 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2587 let end_difference = end_offset - start_byte_offset;
2588 // Current range has associated linked ranges.
2589 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2590 for range in linked_ranges.iter() {
2591 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2592 let end_offset = start_offset + end_difference;
2593 let start_offset = start_offset + start_difference;
2594 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2595 continue;
2596 }
2597 if self.selections.disjoint_anchor_ranges().any(|s| {
2598 if s.start.buffer_id != selection.start.buffer_id
2599 || s.end.buffer_id != selection.end.buffer_id
2600 {
2601 return false;
2602 }
2603 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
2604 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
2605 }) {
2606 continue;
2607 }
2608 let start = buffer_snapshot.anchor_after(start_offset);
2609 let end = buffer_snapshot.anchor_after(end_offset);
2610 linked_edits
2611 .entry(buffer.clone())
2612 .or_default()
2613 .push(start..end);
2614 }
2615 Some(linked_edits)
2616 }
2617
2618 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2619 let text: Arc<str> = text.into();
2620
2621 if self.read_only(cx) {
2622 return;
2623 }
2624
2625 let selections = self.selections.all_adjusted(cx);
2626 let mut bracket_inserted = false;
2627 let mut edits = Vec::new();
2628 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2629 let mut new_selections = Vec::with_capacity(selections.len());
2630 let mut new_autoclose_regions = Vec::new();
2631 let snapshot = self.buffer.read(cx).read(cx);
2632
2633 for (selection, autoclose_region) in
2634 self.selections_with_autoclose_regions(selections, &snapshot)
2635 {
2636 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2637 // Determine if the inserted text matches the opening or closing
2638 // bracket of any of this language's bracket pairs.
2639 let mut bracket_pair = None;
2640 let mut is_bracket_pair_start = false;
2641 let mut is_bracket_pair_end = false;
2642 if !text.is_empty() {
2643 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
2644 // and they are removing the character that triggered IME popup.
2645 for (pair, enabled) in scope.brackets() {
2646 if !pair.close && !pair.surround {
2647 continue;
2648 }
2649
2650 if enabled && pair.start.ends_with(text.as_ref()) {
2651 let prefix_len = pair.start.len() - text.len();
2652 let preceding_text_matches_prefix = prefix_len == 0
2653 || (selection.start.column >= (prefix_len as u32)
2654 && snapshot.contains_str_at(
2655 Point::new(
2656 selection.start.row,
2657 selection.start.column - (prefix_len as u32),
2658 ),
2659 &pair.start[..prefix_len],
2660 ));
2661 if preceding_text_matches_prefix {
2662 bracket_pair = Some(pair.clone());
2663 is_bracket_pair_start = true;
2664 break;
2665 }
2666 }
2667 if pair.end.as_str() == text.as_ref() {
2668 bracket_pair = Some(pair.clone());
2669 is_bracket_pair_end = true;
2670 break;
2671 }
2672 }
2673 }
2674
2675 if let Some(bracket_pair) = bracket_pair {
2676 let snapshot_settings = snapshot.settings_at(selection.start, cx);
2677 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
2678 let auto_surround =
2679 self.use_auto_surround && snapshot_settings.use_auto_surround;
2680 if selection.is_empty() {
2681 if is_bracket_pair_start {
2682 // If the inserted text is a suffix of an opening bracket and the
2683 // selection is preceded by the rest of the opening bracket, then
2684 // insert the closing bracket.
2685 let following_text_allows_autoclose = snapshot
2686 .chars_at(selection.start)
2687 .next()
2688 .map_or(true, |c| scope.should_autoclose_before(c));
2689
2690 let is_closing_quote = if bracket_pair.end == bracket_pair.start
2691 && bracket_pair.start.len() == 1
2692 {
2693 let target = bracket_pair.start.chars().next().unwrap();
2694 let current_line_count = snapshot
2695 .reversed_chars_at(selection.start)
2696 .take_while(|&c| c != '\n')
2697 .filter(|&c| c == target)
2698 .count();
2699 current_line_count % 2 == 1
2700 } else {
2701 false
2702 };
2703
2704 if autoclose
2705 && bracket_pair.close
2706 && following_text_allows_autoclose
2707 && !is_closing_quote
2708 {
2709 let anchor = snapshot.anchor_before(selection.end);
2710 new_selections.push((selection.map(|_| anchor), text.len()));
2711 new_autoclose_regions.push((
2712 anchor,
2713 text.len(),
2714 selection.id,
2715 bracket_pair.clone(),
2716 ));
2717 edits.push((
2718 selection.range(),
2719 format!("{}{}", text, bracket_pair.end).into(),
2720 ));
2721 bracket_inserted = true;
2722 continue;
2723 }
2724 }
2725
2726 if let Some(region) = autoclose_region {
2727 // If the selection is followed by an auto-inserted closing bracket,
2728 // then don't insert that closing bracket again; just move the selection
2729 // past the closing bracket.
2730 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2731 && text.as_ref() == region.pair.end.as_str();
2732 if should_skip {
2733 let anchor = snapshot.anchor_after(selection.end);
2734 new_selections
2735 .push((selection.map(|_| anchor), region.pair.end.len()));
2736 continue;
2737 }
2738 }
2739
2740 let always_treat_brackets_as_autoclosed = snapshot
2741 .settings_at(selection.start, cx)
2742 .always_treat_brackets_as_autoclosed;
2743 if always_treat_brackets_as_autoclosed
2744 && is_bracket_pair_end
2745 && snapshot.contains_str_at(selection.end, text.as_ref())
2746 {
2747 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
2748 // and the inserted text is a closing bracket and the selection is followed
2749 // by the closing bracket then move the selection past the closing bracket.
2750 let anchor = snapshot.anchor_after(selection.end);
2751 new_selections.push((selection.map(|_| anchor), text.len()));
2752 continue;
2753 }
2754 }
2755 // If an opening bracket is 1 character long and is typed while
2756 // text is selected, then surround that text with the bracket pair.
2757 else if auto_surround
2758 && bracket_pair.surround
2759 && is_bracket_pair_start
2760 && bracket_pair.start.chars().count() == 1
2761 {
2762 edits.push((selection.start..selection.start, text.clone()));
2763 edits.push((
2764 selection.end..selection.end,
2765 bracket_pair.end.as_str().into(),
2766 ));
2767 bracket_inserted = true;
2768 new_selections.push((
2769 Selection {
2770 id: selection.id,
2771 start: snapshot.anchor_after(selection.start),
2772 end: snapshot.anchor_before(selection.end),
2773 reversed: selection.reversed,
2774 goal: selection.goal,
2775 },
2776 0,
2777 ));
2778 continue;
2779 }
2780 }
2781 }
2782
2783 if self.auto_replace_emoji_shortcode
2784 && selection.is_empty()
2785 && text.as_ref().ends_with(':')
2786 {
2787 if let Some(possible_emoji_short_code) =
2788 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
2789 {
2790 if !possible_emoji_short_code.is_empty() {
2791 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
2792 let emoji_shortcode_start = Point::new(
2793 selection.start.row,
2794 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
2795 );
2796
2797 // Remove shortcode from buffer
2798 edits.push((
2799 emoji_shortcode_start..selection.start,
2800 "".to_string().into(),
2801 ));
2802 new_selections.push((
2803 Selection {
2804 id: selection.id,
2805 start: snapshot.anchor_after(emoji_shortcode_start),
2806 end: snapshot.anchor_before(selection.start),
2807 reversed: selection.reversed,
2808 goal: selection.goal,
2809 },
2810 0,
2811 ));
2812
2813 // Insert emoji
2814 let selection_start_anchor = snapshot.anchor_after(selection.start);
2815 new_selections.push((selection.map(|_| selection_start_anchor), 0));
2816 edits.push((selection.start..selection.end, emoji.to_string().into()));
2817
2818 continue;
2819 }
2820 }
2821 }
2822 }
2823
2824 // If not handling any auto-close operation, then just replace the selected
2825 // text with the given input and move the selection to the end of the
2826 // newly inserted text.
2827 let anchor = snapshot.anchor_after(selection.end);
2828 if !self.linked_edit_ranges.is_empty() {
2829 let start_anchor = snapshot.anchor_before(selection.start);
2830
2831 let is_word_char = text.chars().next().map_or(true, |char| {
2832 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
2833 classifier.is_word(char)
2834 });
2835
2836 if is_word_char {
2837 if let Some(ranges) = self
2838 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
2839 {
2840 for (buffer, edits) in ranges {
2841 linked_edits
2842 .entry(buffer.clone())
2843 .or_default()
2844 .extend(edits.into_iter().map(|range| (range, text.clone())));
2845 }
2846 }
2847 }
2848 }
2849
2850 new_selections.push((selection.map(|_| anchor), 0));
2851 edits.push((selection.start..selection.end, text.clone()));
2852 }
2853
2854 drop(snapshot);
2855
2856 self.transact(cx, |this, cx| {
2857 this.buffer.update(cx, |buffer, cx| {
2858 buffer.edit(edits, this.autoindent_mode.clone(), cx);
2859 });
2860 for (buffer, edits) in linked_edits {
2861 buffer.update(cx, |buffer, cx| {
2862 let snapshot = buffer.snapshot();
2863 let edits = edits
2864 .into_iter()
2865 .map(|(range, text)| {
2866 use text::ToPoint as TP;
2867 let end_point = TP::to_point(&range.end, &snapshot);
2868 let start_point = TP::to_point(&range.start, &snapshot);
2869 (start_point..end_point, text)
2870 })
2871 .sorted_by_key(|(range, _)| range.start)
2872 .collect::<Vec<_>>();
2873 buffer.edit(edits, None, cx);
2874 })
2875 }
2876 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
2877 let new_selection_deltas = new_selections.iter().map(|e| e.1);
2878 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
2879 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
2880 .zip(new_selection_deltas)
2881 .map(|(selection, delta)| Selection {
2882 id: selection.id,
2883 start: selection.start + delta,
2884 end: selection.end + delta,
2885 reversed: selection.reversed,
2886 goal: SelectionGoal::None,
2887 })
2888 .collect::<Vec<_>>();
2889
2890 let mut i = 0;
2891 for (position, delta, selection_id, pair) in new_autoclose_regions {
2892 let position = position.to_offset(&map.buffer_snapshot) + delta;
2893 let start = map.buffer_snapshot.anchor_before(position);
2894 let end = map.buffer_snapshot.anchor_after(position);
2895 while let Some(existing_state) = this.autoclose_regions.get(i) {
2896 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
2897 Ordering::Less => i += 1,
2898 Ordering::Greater => break,
2899 Ordering::Equal => {
2900 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
2901 Ordering::Less => i += 1,
2902 Ordering::Equal => break,
2903 Ordering::Greater => break,
2904 }
2905 }
2906 }
2907 }
2908 this.autoclose_regions.insert(
2909 i,
2910 AutocloseRegion {
2911 selection_id,
2912 range: start..end,
2913 pair,
2914 },
2915 );
2916 }
2917
2918 let had_active_inline_completion = this.has_active_inline_completion();
2919 this.change_selections_inner(Some(Autoscroll::fit()), false, cx, |s| {
2920 s.select(new_selections)
2921 });
2922
2923 if !bracket_inserted {
2924 if let Some(on_type_format_task) =
2925 this.trigger_on_type_formatting(text.to_string(), cx)
2926 {
2927 on_type_format_task.detach_and_log_err(cx);
2928 }
2929 }
2930
2931 let editor_settings = EditorSettings::get_global(cx);
2932 if bracket_inserted
2933 && (editor_settings.auto_signature_help
2934 || editor_settings.show_signature_help_after_edits)
2935 {
2936 this.show_signature_help(&ShowSignatureHelp, cx);
2937 }
2938
2939 let trigger_in_words =
2940 this.show_inline_completions_in_menu(cx) || !had_active_inline_completion;
2941 this.trigger_completion_on_input(&text, trigger_in_words, cx);
2942 linked_editing_ranges::refresh_linked_ranges(this, cx);
2943 this.refresh_inline_completion(true, false, cx);
2944 });
2945 }
2946
2947 fn find_possible_emoji_shortcode_at_position(
2948 snapshot: &MultiBufferSnapshot,
2949 position: Point,
2950 ) -> Option<String> {
2951 let mut chars = Vec::new();
2952 let mut found_colon = false;
2953 for char in snapshot.reversed_chars_at(position).take(100) {
2954 // Found a possible emoji shortcode in the middle of the buffer
2955 if found_colon {
2956 if char.is_whitespace() {
2957 chars.reverse();
2958 return Some(chars.iter().collect());
2959 }
2960 // If the previous character is not a whitespace, we are in the middle of a word
2961 // and we only want to complete the shortcode if the word is made up of other emojis
2962 let mut containing_word = String::new();
2963 for ch in snapshot
2964 .reversed_chars_at(position)
2965 .skip(chars.len() + 1)
2966 .take(100)
2967 {
2968 if ch.is_whitespace() {
2969 break;
2970 }
2971 containing_word.push(ch);
2972 }
2973 let containing_word = containing_word.chars().rev().collect::<String>();
2974 if util::word_consists_of_emojis(containing_word.as_str()) {
2975 chars.reverse();
2976 return Some(chars.iter().collect());
2977 }
2978 }
2979
2980 if char.is_whitespace() || !char.is_ascii() {
2981 return None;
2982 }
2983 if char == ':' {
2984 found_colon = true;
2985 } else {
2986 chars.push(char);
2987 }
2988 }
2989 // Found a possible emoji shortcode at the beginning of the buffer
2990 chars.reverse();
2991 Some(chars.iter().collect())
2992 }
2993
2994 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
2995 self.transact(cx, |this, cx| {
2996 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
2997 let selections = this.selections.all::<usize>(cx);
2998 let multi_buffer = this.buffer.read(cx);
2999 let buffer = multi_buffer.snapshot(cx);
3000 selections
3001 .iter()
3002 .map(|selection| {
3003 let start_point = selection.start.to_point(&buffer);
3004 let mut indent =
3005 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3006 indent.len = cmp::min(indent.len, start_point.column);
3007 let start = selection.start;
3008 let end = selection.end;
3009 let selection_is_empty = start == end;
3010 let language_scope = buffer.language_scope_at(start);
3011 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3012 &language_scope
3013 {
3014 let leading_whitespace_len = buffer
3015 .reversed_chars_at(start)
3016 .take_while(|c| c.is_whitespace() && *c != '\n')
3017 .map(|c| c.len_utf8())
3018 .sum::<usize>();
3019
3020 let trailing_whitespace_len = buffer
3021 .chars_at(end)
3022 .take_while(|c| c.is_whitespace() && *c != '\n')
3023 .map(|c| c.len_utf8())
3024 .sum::<usize>();
3025
3026 let insert_extra_newline =
3027 language.brackets().any(|(pair, enabled)| {
3028 let pair_start = pair.start.trim_end();
3029 let pair_end = pair.end.trim_start();
3030
3031 enabled
3032 && pair.newline
3033 && buffer.contains_str_at(
3034 end + trailing_whitespace_len,
3035 pair_end,
3036 )
3037 && buffer.contains_str_at(
3038 (start - leading_whitespace_len)
3039 .saturating_sub(pair_start.len()),
3040 pair_start,
3041 )
3042 });
3043
3044 // Comment extension on newline is allowed only for cursor selections
3045 let comment_delimiter = maybe!({
3046 if !selection_is_empty {
3047 return None;
3048 }
3049
3050 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
3051 return None;
3052 }
3053
3054 let delimiters = language.line_comment_prefixes();
3055 let max_len_of_delimiter =
3056 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3057 let (snapshot, range) =
3058 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3059
3060 let mut index_of_first_non_whitespace = 0;
3061 let comment_candidate = snapshot
3062 .chars_for_range(range)
3063 .skip_while(|c| {
3064 let should_skip = c.is_whitespace();
3065 if should_skip {
3066 index_of_first_non_whitespace += 1;
3067 }
3068 should_skip
3069 })
3070 .take(max_len_of_delimiter)
3071 .collect::<String>();
3072 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3073 comment_candidate.starts_with(comment_prefix.as_ref())
3074 })?;
3075 let cursor_is_placed_after_comment_marker =
3076 index_of_first_non_whitespace + comment_prefix.len()
3077 <= start_point.column as usize;
3078 if cursor_is_placed_after_comment_marker {
3079 Some(comment_prefix.clone())
3080 } else {
3081 None
3082 }
3083 });
3084 (comment_delimiter, insert_extra_newline)
3085 } else {
3086 (None, false)
3087 };
3088
3089 let capacity_for_delimiter = comment_delimiter
3090 .as_deref()
3091 .map(str::len)
3092 .unwrap_or_default();
3093 let mut new_text =
3094 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3095 new_text.push('\n');
3096 new_text.extend(indent.chars());
3097 if let Some(delimiter) = &comment_delimiter {
3098 new_text.push_str(delimiter);
3099 }
3100 if insert_extra_newline {
3101 new_text = new_text.repeat(2);
3102 }
3103
3104 let anchor = buffer.anchor_after(end);
3105 let new_selection = selection.map(|_| anchor);
3106 (
3107 (start..end, new_text),
3108 (insert_extra_newline, new_selection),
3109 )
3110 })
3111 .unzip()
3112 };
3113
3114 this.edit_with_autoindent(edits, cx);
3115 let buffer = this.buffer.read(cx).snapshot(cx);
3116 let new_selections = selection_fixup_info
3117 .into_iter()
3118 .map(|(extra_newline_inserted, new_selection)| {
3119 let mut cursor = new_selection.end.to_point(&buffer);
3120 if extra_newline_inserted {
3121 cursor.row -= 1;
3122 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3123 }
3124 new_selection.map(|_| cursor)
3125 })
3126 .collect();
3127
3128 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
3129 this.refresh_inline_completion(true, false, cx);
3130 });
3131 }
3132
3133 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
3134 let buffer = self.buffer.read(cx);
3135 let snapshot = buffer.snapshot(cx);
3136
3137 let mut edits = Vec::new();
3138 let mut rows = Vec::new();
3139
3140 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3141 let cursor = selection.head();
3142 let row = cursor.row;
3143
3144 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3145
3146 let newline = "\n".to_string();
3147 edits.push((start_of_line..start_of_line, newline));
3148
3149 rows.push(row + rows_inserted as u32);
3150 }
3151
3152 self.transact(cx, |editor, cx| {
3153 editor.edit(edits, cx);
3154
3155 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3156 let mut index = 0;
3157 s.move_cursors_with(|map, _, _| {
3158 let row = rows[index];
3159 index += 1;
3160
3161 let point = Point::new(row, 0);
3162 let boundary = map.next_line_boundary(point).1;
3163 let clipped = map.clip_point(boundary, Bias::Left);
3164
3165 (clipped, SelectionGoal::None)
3166 });
3167 });
3168
3169 let mut indent_edits = Vec::new();
3170 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3171 for row in rows {
3172 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3173 for (row, indent) in indents {
3174 if indent.len == 0 {
3175 continue;
3176 }
3177
3178 let text = match indent.kind {
3179 IndentKind::Space => " ".repeat(indent.len as usize),
3180 IndentKind::Tab => "\t".repeat(indent.len as usize),
3181 };
3182 let point = Point::new(row.0, 0);
3183 indent_edits.push((point..point, text));
3184 }
3185 }
3186 editor.edit(indent_edits, cx);
3187 });
3188 }
3189
3190 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
3191 let buffer = self.buffer.read(cx);
3192 let snapshot = buffer.snapshot(cx);
3193
3194 let mut edits = Vec::new();
3195 let mut rows = Vec::new();
3196 let mut rows_inserted = 0;
3197
3198 for selection in self.selections.all_adjusted(cx) {
3199 let cursor = selection.head();
3200 let row = cursor.row;
3201
3202 let point = Point::new(row + 1, 0);
3203 let start_of_line = snapshot.clip_point(point, Bias::Left);
3204
3205 let newline = "\n".to_string();
3206 edits.push((start_of_line..start_of_line, newline));
3207
3208 rows_inserted += 1;
3209 rows.push(row + rows_inserted);
3210 }
3211
3212 self.transact(cx, |editor, cx| {
3213 editor.edit(edits, cx);
3214
3215 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3216 let mut index = 0;
3217 s.move_cursors_with(|map, _, _| {
3218 let row = rows[index];
3219 index += 1;
3220
3221 let point = Point::new(row, 0);
3222 let boundary = map.next_line_boundary(point).1;
3223 let clipped = map.clip_point(boundary, Bias::Left);
3224
3225 (clipped, SelectionGoal::None)
3226 });
3227 });
3228
3229 let mut indent_edits = Vec::new();
3230 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3231 for row in rows {
3232 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3233 for (row, indent) in indents {
3234 if indent.len == 0 {
3235 continue;
3236 }
3237
3238 let text = match indent.kind {
3239 IndentKind::Space => " ".repeat(indent.len as usize),
3240 IndentKind::Tab => "\t".repeat(indent.len as usize),
3241 };
3242 let point = Point::new(row.0, 0);
3243 indent_edits.push((point..point, text));
3244 }
3245 }
3246 editor.edit(indent_edits, cx);
3247 });
3248 }
3249
3250 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3251 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3252 original_indent_columns: Vec::new(),
3253 });
3254 self.insert_with_autoindent_mode(text, autoindent, cx);
3255 }
3256
3257 fn insert_with_autoindent_mode(
3258 &mut self,
3259 text: &str,
3260 autoindent_mode: Option<AutoindentMode>,
3261 cx: &mut ViewContext<Self>,
3262 ) {
3263 if self.read_only(cx) {
3264 return;
3265 }
3266
3267 let text: Arc<str> = text.into();
3268 self.transact(cx, |this, cx| {
3269 let old_selections = this.selections.all_adjusted(cx);
3270 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3271 let anchors = {
3272 let snapshot = buffer.read(cx);
3273 old_selections
3274 .iter()
3275 .map(|s| {
3276 let anchor = snapshot.anchor_after(s.head());
3277 s.map(|_| anchor)
3278 })
3279 .collect::<Vec<_>>()
3280 };
3281 buffer.edit(
3282 old_selections
3283 .iter()
3284 .map(|s| (s.start..s.end, text.clone())),
3285 autoindent_mode,
3286 cx,
3287 );
3288 anchors
3289 });
3290
3291 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3292 s.select_anchors(selection_anchors);
3293 })
3294 });
3295 }
3296
3297 fn trigger_completion_on_input(
3298 &mut self,
3299 text: &str,
3300 trigger_in_words: bool,
3301 cx: &mut ViewContext<Self>,
3302 ) {
3303 if self.is_completion_trigger(text, trigger_in_words, cx) {
3304 self.show_completions(
3305 &ShowCompletions {
3306 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3307 },
3308 cx,
3309 );
3310 } else {
3311 self.hide_context_menu(cx);
3312 }
3313 }
3314
3315 fn is_completion_trigger(
3316 &self,
3317 text: &str,
3318 trigger_in_words: bool,
3319 cx: &mut ViewContext<Self>,
3320 ) -> bool {
3321 let position = self.selections.newest_anchor().head();
3322 let multibuffer = self.buffer.read(cx);
3323 let Some(buffer) = position
3324 .buffer_id
3325 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3326 else {
3327 return false;
3328 };
3329
3330 if let Some(completion_provider) = &self.completion_provider {
3331 completion_provider.is_completion_trigger(
3332 &buffer,
3333 position.text_anchor,
3334 text,
3335 trigger_in_words,
3336 cx,
3337 )
3338 } else {
3339 false
3340 }
3341 }
3342
3343 /// If any empty selections is touching the start of its innermost containing autoclose
3344 /// region, expand it to select the brackets.
3345 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
3346 let selections = self.selections.all::<usize>(cx);
3347 let buffer = self.buffer.read(cx).read(cx);
3348 let new_selections = self
3349 .selections_with_autoclose_regions(selections, &buffer)
3350 .map(|(mut selection, region)| {
3351 if !selection.is_empty() {
3352 return selection;
3353 }
3354
3355 if let Some(region) = region {
3356 let mut range = region.range.to_offset(&buffer);
3357 if selection.start == range.start && range.start >= region.pair.start.len() {
3358 range.start -= region.pair.start.len();
3359 if buffer.contains_str_at(range.start, ®ion.pair.start)
3360 && buffer.contains_str_at(range.end, ®ion.pair.end)
3361 {
3362 range.end += region.pair.end.len();
3363 selection.start = range.start;
3364 selection.end = range.end;
3365
3366 return selection;
3367 }
3368 }
3369 }
3370
3371 let always_treat_brackets_as_autoclosed = buffer
3372 .settings_at(selection.start, cx)
3373 .always_treat_brackets_as_autoclosed;
3374
3375 if !always_treat_brackets_as_autoclosed {
3376 return selection;
3377 }
3378
3379 if let Some(scope) = buffer.language_scope_at(selection.start) {
3380 for (pair, enabled) in scope.brackets() {
3381 if !enabled || !pair.close {
3382 continue;
3383 }
3384
3385 if buffer.contains_str_at(selection.start, &pair.end) {
3386 let pair_start_len = pair.start.len();
3387 if buffer.contains_str_at(
3388 selection.start.saturating_sub(pair_start_len),
3389 &pair.start,
3390 ) {
3391 selection.start -= pair_start_len;
3392 selection.end += pair.end.len();
3393
3394 return selection;
3395 }
3396 }
3397 }
3398 }
3399
3400 selection
3401 })
3402 .collect();
3403
3404 drop(buffer);
3405 self.change_selections(None, cx, |selections| selections.select(new_selections));
3406 }
3407
3408 /// Iterate the given selections, and for each one, find the smallest surrounding
3409 /// autoclose region. This uses the ordering of the selections and the autoclose
3410 /// regions to avoid repeated comparisons.
3411 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3412 &'a self,
3413 selections: impl IntoIterator<Item = Selection<D>>,
3414 buffer: &'a MultiBufferSnapshot,
3415 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3416 let mut i = 0;
3417 let mut regions = self.autoclose_regions.as_slice();
3418 selections.into_iter().map(move |selection| {
3419 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3420
3421 let mut enclosing = None;
3422 while let Some(pair_state) = regions.get(i) {
3423 if pair_state.range.end.to_offset(buffer) < range.start {
3424 regions = ®ions[i + 1..];
3425 i = 0;
3426 } else if pair_state.range.start.to_offset(buffer) > range.end {
3427 break;
3428 } else {
3429 if pair_state.selection_id == selection.id {
3430 enclosing = Some(pair_state);
3431 }
3432 i += 1;
3433 }
3434 }
3435
3436 (selection, enclosing)
3437 })
3438 }
3439
3440 /// Remove any autoclose regions that no longer contain their selection.
3441 fn invalidate_autoclose_regions(
3442 &mut self,
3443 mut selections: &[Selection<Anchor>],
3444 buffer: &MultiBufferSnapshot,
3445 ) {
3446 self.autoclose_regions.retain(|state| {
3447 let mut i = 0;
3448 while let Some(selection) = selections.get(i) {
3449 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3450 selections = &selections[1..];
3451 continue;
3452 }
3453 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3454 break;
3455 }
3456 if selection.id == state.selection_id {
3457 return true;
3458 } else {
3459 i += 1;
3460 }
3461 }
3462 false
3463 });
3464 }
3465
3466 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3467 let offset = position.to_offset(buffer);
3468 let (word_range, kind) = buffer.surrounding_word(offset, true);
3469 if offset > word_range.start && kind == Some(CharKind::Word) {
3470 Some(
3471 buffer
3472 .text_for_range(word_range.start..offset)
3473 .collect::<String>(),
3474 )
3475 } else {
3476 None
3477 }
3478 }
3479
3480 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
3481 self.refresh_inlay_hints(
3482 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
3483 cx,
3484 );
3485 }
3486
3487 pub fn inlay_hints_enabled(&self) -> bool {
3488 self.inlay_hint_cache.enabled
3489 }
3490
3491 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
3492 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
3493 return;
3494 }
3495
3496 let reason_description = reason.description();
3497 let ignore_debounce = matches!(
3498 reason,
3499 InlayHintRefreshReason::SettingsChange(_)
3500 | InlayHintRefreshReason::Toggle(_)
3501 | InlayHintRefreshReason::ExcerptsRemoved(_)
3502 );
3503 let (invalidate_cache, required_languages) = match reason {
3504 InlayHintRefreshReason::Toggle(enabled) => {
3505 self.inlay_hint_cache.enabled = enabled;
3506 if enabled {
3507 (InvalidationStrategy::RefreshRequested, None)
3508 } else {
3509 self.inlay_hint_cache.clear();
3510 self.splice_inlays(
3511 self.visible_inlay_hints(cx)
3512 .iter()
3513 .map(|inlay| inlay.id)
3514 .collect(),
3515 Vec::new(),
3516 cx,
3517 );
3518 return;
3519 }
3520 }
3521 InlayHintRefreshReason::SettingsChange(new_settings) => {
3522 match self.inlay_hint_cache.update_settings(
3523 &self.buffer,
3524 new_settings,
3525 self.visible_inlay_hints(cx),
3526 cx,
3527 ) {
3528 ControlFlow::Break(Some(InlaySplice {
3529 to_remove,
3530 to_insert,
3531 })) => {
3532 self.splice_inlays(to_remove, to_insert, cx);
3533 return;
3534 }
3535 ControlFlow::Break(None) => return,
3536 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3537 }
3538 }
3539 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3540 if let Some(InlaySplice {
3541 to_remove,
3542 to_insert,
3543 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3544 {
3545 self.splice_inlays(to_remove, to_insert, cx);
3546 }
3547 return;
3548 }
3549 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3550 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3551 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3552 }
3553 InlayHintRefreshReason::RefreshRequested => {
3554 (InvalidationStrategy::RefreshRequested, None)
3555 }
3556 };
3557
3558 if let Some(InlaySplice {
3559 to_remove,
3560 to_insert,
3561 }) = self.inlay_hint_cache.spawn_hint_refresh(
3562 reason_description,
3563 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3564 invalidate_cache,
3565 ignore_debounce,
3566 cx,
3567 ) {
3568 self.splice_inlays(to_remove, to_insert, cx);
3569 }
3570 }
3571
3572 fn visible_inlay_hints(&self, cx: &ViewContext<Editor>) -> Vec<Inlay> {
3573 self.display_map
3574 .read(cx)
3575 .current_inlays()
3576 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3577 .cloned()
3578 .collect()
3579 }
3580
3581 pub fn excerpts_for_inlay_hints_query(
3582 &self,
3583 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3584 cx: &mut ViewContext<Editor>,
3585 ) -> HashMap<ExcerptId, (Model<Buffer>, clock::Global, Range<usize>)> {
3586 let Some(project) = self.project.as_ref() else {
3587 return HashMap::default();
3588 };
3589 let project = project.read(cx);
3590 let multi_buffer = self.buffer().read(cx);
3591 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3592 let multi_buffer_visible_start = self
3593 .scroll_manager
3594 .anchor()
3595 .anchor
3596 .to_point(&multi_buffer_snapshot);
3597 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3598 multi_buffer_visible_start
3599 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3600 Bias::Left,
3601 );
3602 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3603 multi_buffer_snapshot
3604 .range_to_buffer_ranges(multi_buffer_visible_range)
3605 .into_iter()
3606 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3607 .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
3608 let buffer_file = project::File::from_dyn(buffer.file())?;
3609 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3610 let worktree_entry = buffer_worktree
3611 .read(cx)
3612 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3613 if worktree_entry.is_ignored {
3614 return None;
3615 }
3616
3617 let language = buffer.language()?;
3618 if let Some(restrict_to_languages) = restrict_to_languages {
3619 if !restrict_to_languages.contains(language) {
3620 return None;
3621 }
3622 }
3623 Some((
3624 excerpt_id,
3625 (
3626 multi_buffer.buffer(buffer.remote_id()).unwrap(),
3627 buffer.version().clone(),
3628 excerpt_visible_range,
3629 ),
3630 ))
3631 })
3632 .collect()
3633 }
3634
3635 pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
3636 TextLayoutDetails {
3637 text_system: cx.text_system().clone(),
3638 editor_style: self.style.clone().unwrap(),
3639 rem_size: cx.rem_size(),
3640 scroll_anchor: self.scroll_manager.anchor(),
3641 visible_rows: self.visible_line_count(),
3642 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
3643 }
3644 }
3645
3646 pub fn splice_inlays(
3647 &self,
3648 to_remove: Vec<InlayId>,
3649 to_insert: Vec<Inlay>,
3650 cx: &mut ViewContext<Self>,
3651 ) {
3652 self.display_map.update(cx, |display_map, cx| {
3653 display_map.splice_inlays(to_remove, to_insert, cx)
3654 });
3655 cx.notify();
3656 }
3657
3658 fn trigger_on_type_formatting(
3659 &self,
3660 input: String,
3661 cx: &mut ViewContext<Self>,
3662 ) -> Option<Task<Result<()>>> {
3663 if input.len() != 1 {
3664 return None;
3665 }
3666
3667 let project = self.project.as_ref()?;
3668 let position = self.selections.newest_anchor().head();
3669 let (buffer, buffer_position) = self
3670 .buffer
3671 .read(cx)
3672 .text_anchor_for_position(position, cx)?;
3673
3674 let settings = language_settings::language_settings(
3675 buffer
3676 .read(cx)
3677 .language_at(buffer_position)
3678 .map(|l| l.name()),
3679 buffer.read(cx).file(),
3680 cx,
3681 );
3682 if !settings.use_on_type_format {
3683 return None;
3684 }
3685
3686 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3687 // hence we do LSP request & edit on host side only — add formats to host's history.
3688 let push_to_lsp_host_history = true;
3689 // If this is not the host, append its history with new edits.
3690 let push_to_client_history = project.read(cx).is_via_collab();
3691
3692 let on_type_formatting = project.update(cx, |project, cx| {
3693 project.on_type_format(
3694 buffer.clone(),
3695 buffer_position,
3696 input,
3697 push_to_lsp_host_history,
3698 cx,
3699 )
3700 });
3701 Some(cx.spawn(|editor, mut cx| async move {
3702 if let Some(transaction) = on_type_formatting.await? {
3703 if push_to_client_history {
3704 buffer
3705 .update(&mut cx, |buffer, _| {
3706 buffer.push_transaction(transaction, Instant::now());
3707 })
3708 .ok();
3709 }
3710 editor.update(&mut cx, |editor, cx| {
3711 editor.refresh_document_highlights(cx);
3712 })?;
3713 }
3714 Ok(())
3715 }))
3716 }
3717
3718 pub fn show_completions(&mut self, options: &ShowCompletions, cx: &mut ViewContext<Self>) {
3719 if self.pending_rename.is_some() {
3720 return;
3721 }
3722
3723 let Some(provider) = self.completion_provider.as_ref() else {
3724 return;
3725 };
3726
3727 if !self.snippet_stack.is_empty() && self.context_menu.borrow().as_ref().is_some() {
3728 return;
3729 }
3730
3731 let position = self.selections.newest_anchor().head();
3732 if position.diff_base_anchor.is_some() {
3733 return;
3734 }
3735 let (buffer, buffer_position) =
3736 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
3737 output
3738 } else {
3739 return;
3740 };
3741 let show_completion_documentation = buffer
3742 .read(cx)
3743 .snapshot()
3744 .settings_at(buffer_position, cx)
3745 .show_completion_documentation;
3746
3747 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
3748
3749 let trigger_kind = match &options.trigger {
3750 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
3751 CompletionTriggerKind::TRIGGER_CHARACTER
3752 }
3753 _ => CompletionTriggerKind::INVOKED,
3754 };
3755 let completion_context = CompletionContext {
3756 trigger_character: options.trigger.as_ref().and_then(|trigger| {
3757 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
3758 Some(String::from(trigger))
3759 } else {
3760 None
3761 }
3762 }),
3763 trigger_kind,
3764 };
3765 let completions = provider.completions(&buffer, buffer_position, completion_context, cx);
3766 let sort_completions = provider.sort_completions();
3767
3768 let id = post_inc(&mut self.next_completion_id);
3769 let task = cx.spawn(|editor, mut cx| {
3770 async move {
3771 editor.update(&mut cx, |this, _| {
3772 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
3773 })?;
3774 let completions = completions.await.log_err();
3775 let menu = if let Some(completions) = completions {
3776 let mut menu = CompletionsMenu::new(
3777 id,
3778 sort_completions,
3779 show_completion_documentation,
3780 position,
3781 buffer.clone(),
3782 completions.into(),
3783 );
3784
3785 menu.filter(query.as_deref(), cx.background_executor().clone())
3786 .await;
3787
3788 menu.visible().then_some(menu)
3789 } else {
3790 None
3791 };
3792
3793 editor.update(&mut cx, |editor, cx| {
3794 match editor.context_menu.borrow().as_ref() {
3795 None => {}
3796 Some(CodeContextMenu::Completions(prev_menu)) => {
3797 if prev_menu.id > id {
3798 return;
3799 }
3800 }
3801 _ => return,
3802 }
3803
3804 if editor.focus_handle.is_focused(cx) && menu.is_some() {
3805 let mut menu = menu.unwrap();
3806 menu.resolve_visible_completions(editor.completion_provider.as_deref(), cx);
3807
3808 if editor.show_inline_completions_in_menu(cx) {
3809 if let Some(hint) = editor.inline_completion_menu_hint(cx) {
3810 menu.show_inline_completion_hint(hint);
3811 }
3812 } else {
3813 editor.discard_inline_completion(false, cx);
3814 }
3815
3816 *editor.context_menu.borrow_mut() =
3817 Some(CodeContextMenu::Completions(menu));
3818
3819 cx.notify();
3820 } else if editor.completion_tasks.len() <= 1 {
3821 // If there are no more completion tasks and the last menu was
3822 // empty, we should hide it.
3823 let was_hidden = editor.hide_context_menu(cx).is_none();
3824 // If it was already hidden and we don't show inline
3825 // completions in the menu, we should also show the
3826 // inline-completion when available.
3827 if was_hidden && editor.show_inline_completions_in_menu(cx) {
3828 editor.update_visible_inline_completion(cx);
3829 }
3830 }
3831 })?;
3832
3833 Ok::<_, anyhow::Error>(())
3834 }
3835 .log_err()
3836 });
3837
3838 self.completion_tasks.push((id, task));
3839 }
3840
3841 pub fn confirm_completion(
3842 &mut self,
3843 action: &ConfirmCompletion,
3844 cx: &mut ViewContext<Self>,
3845 ) -> Option<Task<Result<()>>> {
3846 self.do_completion(action.item_ix, CompletionIntent::Complete, cx)
3847 }
3848
3849 pub fn compose_completion(
3850 &mut self,
3851 action: &ComposeCompletion,
3852 cx: &mut ViewContext<Self>,
3853 ) -> Option<Task<Result<()>>> {
3854 self.do_completion(action.item_ix, CompletionIntent::Compose, cx)
3855 }
3856
3857 fn toggle_zed_predict_tos(&mut self, cx: &mut ViewContext<Self>) {
3858 let (Some(workspace), Some(project)) = (self.workspace(), self.project.as_ref()) else {
3859 return;
3860 };
3861
3862 ZedPredictTos::toggle(workspace, project.read(cx).user_store().clone(), cx);
3863 }
3864
3865 fn do_completion(
3866 &mut self,
3867 item_ix: Option<usize>,
3868 intent: CompletionIntent,
3869 cx: &mut ViewContext<Editor>,
3870 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
3871 use language::ToOffset as _;
3872
3873 {
3874 let context_menu = self.context_menu.borrow();
3875 if let CodeContextMenu::Completions(menu) = context_menu.as_ref()? {
3876 let entries = menu.entries.borrow();
3877 let entry = entries.get(item_ix.unwrap_or(menu.selected_item));
3878 match entry {
3879 Some(CompletionEntry::InlineCompletionHint(
3880 InlineCompletionMenuHint::Loading,
3881 )) => return Some(Task::ready(Ok(()))),
3882 Some(CompletionEntry::InlineCompletionHint(InlineCompletionMenuHint::None)) => {
3883 drop(entries);
3884 drop(context_menu);
3885 self.context_menu_next(&Default::default(), cx);
3886 return Some(Task::ready(Ok(())));
3887 }
3888 Some(CompletionEntry::InlineCompletionHint(
3889 InlineCompletionMenuHint::PendingTermsAcceptance,
3890 )) => {
3891 drop(entries);
3892 drop(context_menu);
3893 self.toggle_zed_predict_tos(cx);
3894 return Some(Task::ready(Ok(())));
3895 }
3896 _ => {}
3897 }
3898 }
3899 }
3900
3901 let completions_menu =
3902 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
3903 menu
3904 } else {
3905 return None;
3906 };
3907
3908 let entries = completions_menu.entries.borrow();
3909 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
3910 let mat = match mat {
3911 CompletionEntry::InlineCompletionHint(_) => {
3912 self.accept_inline_completion(&AcceptInlineCompletion, cx);
3913 cx.stop_propagation();
3914 return Some(Task::ready(Ok(())));
3915 }
3916 CompletionEntry::Match(mat) => {
3917 if self.show_inline_completions_in_menu(cx) {
3918 self.discard_inline_completion(true, cx);
3919 }
3920 mat
3921 }
3922 };
3923 let candidate_id = mat.candidate_id;
3924 drop(entries);
3925
3926 let buffer_handle = completions_menu.buffer;
3927 let completion = completions_menu
3928 .completions
3929 .borrow()
3930 .get(candidate_id)?
3931 .clone();
3932 cx.stop_propagation();
3933
3934 let snippet;
3935 let text;
3936
3937 if completion.is_snippet() {
3938 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
3939 text = snippet.as_ref().unwrap().text.clone();
3940 } else {
3941 snippet = None;
3942 text = completion.new_text.clone();
3943 };
3944 let selections = self.selections.all::<usize>(cx);
3945 let buffer = buffer_handle.read(cx);
3946 let old_range = completion.old_range.to_offset(buffer);
3947 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
3948
3949 let newest_selection = self.selections.newest_anchor();
3950 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
3951 return None;
3952 }
3953
3954 let lookbehind = newest_selection
3955 .start
3956 .text_anchor
3957 .to_offset(buffer)
3958 .saturating_sub(old_range.start);
3959 let lookahead = old_range
3960 .end
3961 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
3962 let mut common_prefix_len = old_text
3963 .bytes()
3964 .zip(text.bytes())
3965 .take_while(|(a, b)| a == b)
3966 .count();
3967
3968 let snapshot = self.buffer.read(cx).snapshot(cx);
3969 let mut range_to_replace: Option<Range<isize>> = None;
3970 let mut ranges = Vec::new();
3971 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3972 for selection in &selections {
3973 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
3974 let start = selection.start.saturating_sub(lookbehind);
3975 let end = selection.end + lookahead;
3976 if selection.id == newest_selection.id {
3977 range_to_replace = Some(
3978 ((start + common_prefix_len) as isize - selection.start as isize)
3979 ..(end as isize - selection.start as isize),
3980 );
3981 }
3982 ranges.push(start + common_prefix_len..end);
3983 } else {
3984 common_prefix_len = 0;
3985 ranges.clear();
3986 ranges.extend(selections.iter().map(|s| {
3987 if s.id == newest_selection.id {
3988 range_to_replace = Some(
3989 old_range.start.to_offset_utf16(&snapshot).0 as isize
3990 - selection.start as isize
3991 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
3992 - selection.start as isize,
3993 );
3994 old_range.clone()
3995 } else {
3996 s.start..s.end
3997 }
3998 }));
3999 break;
4000 }
4001 if !self.linked_edit_ranges.is_empty() {
4002 let start_anchor = snapshot.anchor_before(selection.head());
4003 let end_anchor = snapshot.anchor_after(selection.tail());
4004 if let Some(ranges) = self
4005 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4006 {
4007 for (buffer, edits) in ranges {
4008 linked_edits.entry(buffer.clone()).or_default().extend(
4009 edits
4010 .into_iter()
4011 .map(|range| (range, text[common_prefix_len..].to_owned())),
4012 );
4013 }
4014 }
4015 }
4016 }
4017 let text = &text[common_prefix_len..];
4018
4019 cx.emit(EditorEvent::InputHandled {
4020 utf16_range_to_replace: range_to_replace,
4021 text: text.into(),
4022 });
4023
4024 self.transact(cx, |this, cx| {
4025 if let Some(mut snippet) = snippet {
4026 snippet.text = text.to_string();
4027 for tabstop in snippet
4028 .tabstops
4029 .iter_mut()
4030 .flat_map(|tabstop| tabstop.ranges.iter_mut())
4031 {
4032 tabstop.start -= common_prefix_len as isize;
4033 tabstop.end -= common_prefix_len as isize;
4034 }
4035
4036 this.insert_snippet(&ranges, snippet, cx).log_err();
4037 } else {
4038 this.buffer.update(cx, |buffer, cx| {
4039 buffer.edit(
4040 ranges.iter().map(|range| (range.clone(), text)),
4041 this.autoindent_mode.clone(),
4042 cx,
4043 );
4044 });
4045 }
4046 for (buffer, edits) in linked_edits {
4047 buffer.update(cx, |buffer, cx| {
4048 let snapshot = buffer.snapshot();
4049 let edits = edits
4050 .into_iter()
4051 .map(|(range, text)| {
4052 use text::ToPoint as TP;
4053 let end_point = TP::to_point(&range.end, &snapshot);
4054 let start_point = TP::to_point(&range.start, &snapshot);
4055 (start_point..end_point, text)
4056 })
4057 .sorted_by_key(|(range, _)| range.start)
4058 .collect::<Vec<_>>();
4059 buffer.edit(edits, None, cx);
4060 })
4061 }
4062
4063 this.refresh_inline_completion(true, false, cx);
4064 });
4065
4066 let show_new_completions_on_confirm = completion
4067 .confirm
4068 .as_ref()
4069 .map_or(false, |confirm| confirm(intent, cx));
4070 if show_new_completions_on_confirm {
4071 self.show_completions(&ShowCompletions { trigger: None }, cx);
4072 }
4073
4074 let provider = self.completion_provider.as_ref()?;
4075 drop(completion);
4076 let apply_edits = provider.apply_additional_edits_for_completion(
4077 buffer_handle,
4078 completions_menu.completions.clone(),
4079 candidate_id,
4080 true,
4081 cx,
4082 );
4083
4084 let editor_settings = EditorSettings::get_global(cx);
4085 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4086 // After the code completion is finished, users often want to know what signatures are needed.
4087 // so we should automatically call signature_help
4088 self.show_signature_help(&ShowSignatureHelp, cx);
4089 }
4090
4091 Some(cx.foreground_executor().spawn(async move {
4092 apply_edits.await?;
4093 Ok(())
4094 }))
4095 }
4096
4097 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
4098 let mut context_menu = self.context_menu.borrow_mut();
4099 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4100 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4101 // Toggle if we're selecting the same one
4102 *context_menu = None;
4103 cx.notify();
4104 return;
4105 } else {
4106 // Otherwise, clear it and start a new one
4107 *context_menu = None;
4108 cx.notify();
4109 }
4110 }
4111 drop(context_menu);
4112 let snapshot = self.snapshot(cx);
4113 let deployed_from_indicator = action.deployed_from_indicator;
4114 let mut task = self.code_actions_task.take();
4115 let action = action.clone();
4116 cx.spawn(|editor, mut cx| async move {
4117 while let Some(prev_task) = task {
4118 prev_task.await.log_err();
4119 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4120 }
4121
4122 let spawned_test_task = editor.update(&mut cx, |editor, cx| {
4123 if editor.focus_handle.is_focused(cx) {
4124 let multibuffer_point = action
4125 .deployed_from_indicator
4126 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4127 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4128 let (buffer, buffer_row) = snapshot
4129 .buffer_snapshot
4130 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4131 .and_then(|(buffer_snapshot, range)| {
4132 editor
4133 .buffer
4134 .read(cx)
4135 .buffer(buffer_snapshot.remote_id())
4136 .map(|buffer| (buffer, range.start.row))
4137 })?;
4138 let (_, code_actions) = editor
4139 .available_code_actions
4140 .clone()
4141 .and_then(|(location, code_actions)| {
4142 let snapshot = location.buffer.read(cx).snapshot();
4143 let point_range = location.range.to_point(&snapshot);
4144 let point_range = point_range.start.row..=point_range.end.row;
4145 if point_range.contains(&buffer_row) {
4146 Some((location, code_actions))
4147 } else {
4148 None
4149 }
4150 })
4151 .unzip();
4152 let buffer_id = buffer.read(cx).remote_id();
4153 let tasks = editor
4154 .tasks
4155 .get(&(buffer_id, buffer_row))
4156 .map(|t| Arc::new(t.to_owned()));
4157 if tasks.is_none() && code_actions.is_none() {
4158 return None;
4159 }
4160
4161 editor.completion_tasks.clear();
4162 editor.discard_inline_completion(false, cx);
4163 let task_context =
4164 tasks
4165 .as_ref()
4166 .zip(editor.project.clone())
4167 .map(|(tasks, project)| {
4168 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4169 });
4170
4171 Some(cx.spawn(|editor, mut cx| async move {
4172 let task_context = match task_context {
4173 Some(task_context) => task_context.await,
4174 None => None,
4175 };
4176 let resolved_tasks =
4177 tasks.zip(task_context).map(|(tasks, task_context)| {
4178 Rc::new(ResolvedTasks {
4179 templates: tasks.resolve(&task_context).collect(),
4180 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4181 multibuffer_point.row,
4182 tasks.column,
4183 )),
4184 })
4185 });
4186 let spawn_straight_away = resolved_tasks
4187 .as_ref()
4188 .map_or(false, |tasks| tasks.templates.len() == 1)
4189 && code_actions
4190 .as_ref()
4191 .map_or(true, |actions| actions.is_empty());
4192 if let Ok(task) = editor.update(&mut cx, |editor, cx| {
4193 *editor.context_menu.borrow_mut() =
4194 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4195 buffer,
4196 actions: CodeActionContents {
4197 tasks: resolved_tasks,
4198 actions: code_actions,
4199 },
4200 selected_item: Default::default(),
4201 scroll_handle: UniformListScrollHandle::default(),
4202 deployed_from_indicator,
4203 }));
4204 if spawn_straight_away {
4205 if let Some(task) = editor.confirm_code_action(
4206 &ConfirmCodeAction { item_ix: Some(0) },
4207 cx,
4208 ) {
4209 cx.notify();
4210 return task;
4211 }
4212 }
4213 cx.notify();
4214 Task::ready(Ok(()))
4215 }) {
4216 task.await
4217 } else {
4218 Ok(())
4219 }
4220 }))
4221 } else {
4222 Some(Task::ready(Ok(())))
4223 }
4224 })?;
4225 if let Some(task) = spawned_test_task {
4226 task.await?;
4227 }
4228
4229 Ok::<_, anyhow::Error>(())
4230 })
4231 .detach_and_log_err(cx);
4232 }
4233
4234 pub fn confirm_code_action(
4235 &mut self,
4236 action: &ConfirmCodeAction,
4237 cx: &mut ViewContext<Self>,
4238 ) -> Option<Task<Result<()>>> {
4239 let actions_menu = if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
4240 menu
4241 } else {
4242 return None;
4243 };
4244 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4245 let action = actions_menu.actions.get(action_ix)?;
4246 let title = action.label();
4247 let buffer = actions_menu.buffer;
4248 let workspace = self.workspace()?;
4249
4250 match action {
4251 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4252 workspace.update(cx, |workspace, cx| {
4253 workspace::tasks::schedule_resolved_task(
4254 workspace,
4255 task_source_kind,
4256 resolved_task,
4257 false,
4258 cx,
4259 );
4260
4261 Some(Task::ready(Ok(())))
4262 })
4263 }
4264 CodeActionsItem::CodeAction {
4265 excerpt_id,
4266 action,
4267 provider,
4268 } => {
4269 let apply_code_action =
4270 provider.apply_code_action(buffer, action, excerpt_id, true, cx);
4271 let workspace = workspace.downgrade();
4272 Some(cx.spawn(|editor, cx| async move {
4273 let project_transaction = apply_code_action.await?;
4274 Self::open_project_transaction(
4275 &editor,
4276 workspace,
4277 project_transaction,
4278 title,
4279 cx,
4280 )
4281 .await
4282 }))
4283 }
4284 }
4285 }
4286
4287 pub async fn open_project_transaction(
4288 this: &WeakView<Editor>,
4289 workspace: WeakView<Workspace>,
4290 transaction: ProjectTransaction,
4291 title: String,
4292 mut cx: AsyncWindowContext,
4293 ) -> Result<()> {
4294 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4295 cx.update(|cx| {
4296 entries.sort_unstable_by_key(|(buffer, _)| {
4297 buffer.read(cx).file().map(|f| f.path().clone())
4298 });
4299 })?;
4300
4301 // If the project transaction's edits are all contained within this editor, then
4302 // avoid opening a new editor to display them.
4303
4304 if let Some((buffer, transaction)) = entries.first() {
4305 if entries.len() == 1 {
4306 let excerpt = this.update(&mut cx, |editor, cx| {
4307 editor
4308 .buffer()
4309 .read(cx)
4310 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4311 })?;
4312 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4313 if excerpted_buffer == *buffer {
4314 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4315 let excerpt_range = excerpt_range.to_offset(buffer);
4316 buffer
4317 .edited_ranges_for_transaction::<usize>(transaction)
4318 .all(|range| {
4319 excerpt_range.start <= range.start
4320 && excerpt_range.end >= range.end
4321 })
4322 })?;
4323
4324 if all_edits_within_excerpt {
4325 return Ok(());
4326 }
4327 }
4328 }
4329 }
4330 } else {
4331 return Ok(());
4332 }
4333
4334 let mut ranges_to_highlight = Vec::new();
4335 let excerpt_buffer = cx.new_model(|cx| {
4336 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4337 for (buffer_handle, transaction) in &entries {
4338 let buffer = buffer_handle.read(cx);
4339 ranges_to_highlight.extend(
4340 multibuffer.push_excerpts_with_context_lines(
4341 buffer_handle.clone(),
4342 buffer
4343 .edited_ranges_for_transaction::<usize>(transaction)
4344 .collect(),
4345 DEFAULT_MULTIBUFFER_CONTEXT,
4346 cx,
4347 ),
4348 );
4349 }
4350 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4351 multibuffer
4352 })?;
4353
4354 workspace.update(&mut cx, |workspace, cx| {
4355 let project = workspace.project().clone();
4356 let editor =
4357 cx.new_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, cx));
4358 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
4359 editor.update(cx, |editor, cx| {
4360 editor.highlight_background::<Self>(
4361 &ranges_to_highlight,
4362 |theme| theme.editor_highlighted_line_background,
4363 cx,
4364 );
4365 });
4366 })?;
4367
4368 Ok(())
4369 }
4370
4371 pub fn clear_code_action_providers(&mut self) {
4372 self.code_action_providers.clear();
4373 self.available_code_actions.take();
4374 }
4375
4376 pub fn add_code_action_provider(
4377 &mut self,
4378 provider: Rc<dyn CodeActionProvider>,
4379 cx: &mut ViewContext<Self>,
4380 ) {
4381 if self
4382 .code_action_providers
4383 .iter()
4384 .any(|existing_provider| existing_provider.id() == provider.id())
4385 {
4386 return;
4387 }
4388
4389 self.code_action_providers.push(provider);
4390 self.refresh_code_actions(cx);
4391 }
4392
4393 pub fn remove_code_action_provider(&mut self, id: Arc<str>, cx: &mut ViewContext<Self>) {
4394 self.code_action_providers
4395 .retain(|provider| provider.id() != id);
4396 self.refresh_code_actions(cx);
4397 }
4398
4399 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4400 let buffer = self.buffer.read(cx);
4401 let newest_selection = self.selections.newest_anchor().clone();
4402 if newest_selection.head().diff_base_anchor.is_some() {
4403 return None;
4404 }
4405 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4406 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4407 if start_buffer != end_buffer {
4408 return None;
4409 }
4410
4411 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
4412 cx.background_executor()
4413 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4414 .await;
4415
4416 let (providers, tasks) = this.update(&mut cx, |this, cx| {
4417 let providers = this.code_action_providers.clone();
4418 let tasks = this
4419 .code_action_providers
4420 .iter()
4421 .map(|provider| provider.code_actions(&start_buffer, start..end, cx))
4422 .collect::<Vec<_>>();
4423 (providers, tasks)
4424 })?;
4425
4426 let mut actions = Vec::new();
4427 for (provider, provider_actions) in
4428 providers.into_iter().zip(future::join_all(tasks).await)
4429 {
4430 if let Some(provider_actions) = provider_actions.log_err() {
4431 actions.extend(provider_actions.into_iter().map(|action| {
4432 AvailableCodeAction {
4433 excerpt_id: newest_selection.start.excerpt_id,
4434 action,
4435 provider: provider.clone(),
4436 }
4437 }));
4438 }
4439 }
4440
4441 this.update(&mut cx, |this, cx| {
4442 this.available_code_actions = if actions.is_empty() {
4443 None
4444 } else {
4445 Some((
4446 Location {
4447 buffer: start_buffer,
4448 range: start..end,
4449 },
4450 actions.into(),
4451 ))
4452 };
4453 cx.notify();
4454 })
4455 }));
4456 None
4457 }
4458
4459 fn start_inline_blame_timer(&mut self, cx: &mut ViewContext<Self>) {
4460 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4461 self.show_git_blame_inline = false;
4462
4463 self.show_git_blame_inline_delay_task = Some(cx.spawn(|this, mut cx| async move {
4464 cx.background_executor().timer(delay).await;
4465
4466 this.update(&mut cx, |this, cx| {
4467 this.show_git_blame_inline = true;
4468 cx.notify();
4469 })
4470 .log_err();
4471 }));
4472 }
4473 }
4474
4475 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4476 if self.pending_rename.is_some() {
4477 return None;
4478 }
4479
4480 let provider = self.semantics_provider.clone()?;
4481 let buffer = self.buffer.read(cx);
4482 let newest_selection = self.selections.newest_anchor().clone();
4483 let cursor_position = newest_selection.head();
4484 let (cursor_buffer, cursor_buffer_position) =
4485 buffer.text_anchor_for_position(cursor_position, cx)?;
4486 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4487 if cursor_buffer != tail_buffer {
4488 return None;
4489 }
4490 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
4491 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4492 cx.background_executor()
4493 .timer(Duration::from_millis(debounce))
4494 .await;
4495
4496 let highlights = if let Some(highlights) = cx
4497 .update(|cx| {
4498 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4499 })
4500 .ok()
4501 .flatten()
4502 {
4503 highlights.await.log_err()
4504 } else {
4505 None
4506 };
4507
4508 if let Some(highlights) = highlights {
4509 this.update(&mut cx, |this, cx| {
4510 if this.pending_rename.is_some() {
4511 return;
4512 }
4513
4514 let buffer_id = cursor_position.buffer_id;
4515 let buffer = this.buffer.read(cx);
4516 if !buffer
4517 .text_anchor_for_position(cursor_position, cx)
4518 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4519 {
4520 return;
4521 }
4522
4523 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4524 let mut write_ranges = Vec::new();
4525 let mut read_ranges = Vec::new();
4526 for highlight in highlights {
4527 for (excerpt_id, excerpt_range) in
4528 buffer.excerpts_for_buffer(&cursor_buffer, cx)
4529 {
4530 let start = highlight
4531 .range
4532 .start
4533 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4534 let end = highlight
4535 .range
4536 .end
4537 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4538 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4539 continue;
4540 }
4541
4542 let range = Anchor {
4543 buffer_id,
4544 excerpt_id,
4545 text_anchor: start,
4546 diff_base_anchor: None,
4547 }..Anchor {
4548 buffer_id,
4549 excerpt_id,
4550 text_anchor: end,
4551 diff_base_anchor: None,
4552 };
4553 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4554 write_ranges.push(range);
4555 } else {
4556 read_ranges.push(range);
4557 }
4558 }
4559 }
4560
4561 this.highlight_background::<DocumentHighlightRead>(
4562 &read_ranges,
4563 |theme| theme.editor_document_highlight_read_background,
4564 cx,
4565 );
4566 this.highlight_background::<DocumentHighlightWrite>(
4567 &write_ranges,
4568 |theme| theme.editor_document_highlight_write_background,
4569 cx,
4570 );
4571 cx.notify();
4572 })
4573 .log_err();
4574 }
4575 }));
4576 None
4577 }
4578
4579 pub fn refresh_inline_completion(
4580 &mut self,
4581 debounce: bool,
4582 user_requested: bool,
4583 cx: &mut ViewContext<Self>,
4584 ) -> Option<()> {
4585 let provider = self.inline_completion_provider()?;
4586 let cursor = self.selections.newest_anchor().head();
4587 let (buffer, cursor_buffer_position) =
4588 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4589
4590 if !user_requested
4591 && (!self.enable_inline_completions
4592 || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
4593 || !self.is_focused(cx)
4594 || buffer.read(cx).is_empty())
4595 {
4596 self.discard_inline_completion(false, cx);
4597 return None;
4598 }
4599
4600 self.update_visible_inline_completion(cx);
4601 provider.refresh(buffer, cursor_buffer_position, debounce, cx);
4602 Some(())
4603 }
4604
4605 fn cycle_inline_completion(
4606 &mut self,
4607 direction: Direction,
4608 cx: &mut ViewContext<Self>,
4609 ) -> Option<()> {
4610 let provider = self.inline_completion_provider()?;
4611 let cursor = self.selections.newest_anchor().head();
4612 let (buffer, cursor_buffer_position) =
4613 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4614 if !self.enable_inline_completions
4615 || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
4616 {
4617 return None;
4618 }
4619
4620 provider.cycle(buffer, cursor_buffer_position, direction, cx);
4621 self.update_visible_inline_completion(cx);
4622
4623 Some(())
4624 }
4625
4626 pub fn show_inline_completion(&mut self, _: &ShowInlineCompletion, cx: &mut ViewContext<Self>) {
4627 if !self.has_active_inline_completion() {
4628 self.refresh_inline_completion(false, true, cx);
4629 return;
4630 }
4631
4632 self.update_visible_inline_completion(cx);
4633 }
4634
4635 pub fn display_cursor_names(&mut self, _: &DisplayCursorNames, cx: &mut ViewContext<Self>) {
4636 self.show_cursor_names(cx);
4637 }
4638
4639 fn show_cursor_names(&mut self, cx: &mut ViewContext<Self>) {
4640 self.show_cursor_names = true;
4641 cx.notify();
4642 cx.spawn(|this, mut cx| async move {
4643 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
4644 this.update(&mut cx, |this, cx| {
4645 this.show_cursor_names = false;
4646 cx.notify()
4647 })
4648 .ok()
4649 })
4650 .detach();
4651 }
4652
4653 pub fn next_inline_completion(&mut self, _: &NextInlineCompletion, cx: &mut ViewContext<Self>) {
4654 if self.has_active_inline_completion() {
4655 self.cycle_inline_completion(Direction::Next, cx);
4656 } else {
4657 let is_copilot_disabled = self.refresh_inline_completion(false, true, cx).is_none();
4658 if is_copilot_disabled {
4659 cx.propagate();
4660 }
4661 }
4662 }
4663
4664 pub fn previous_inline_completion(
4665 &mut self,
4666 _: &PreviousInlineCompletion,
4667 cx: &mut ViewContext<Self>,
4668 ) {
4669 if self.has_active_inline_completion() {
4670 self.cycle_inline_completion(Direction::Prev, cx);
4671 } else {
4672 let is_copilot_disabled = self.refresh_inline_completion(false, true, cx).is_none();
4673 if is_copilot_disabled {
4674 cx.propagate();
4675 }
4676 }
4677 }
4678
4679 pub fn accept_inline_completion(
4680 &mut self,
4681 _: &AcceptInlineCompletion,
4682 cx: &mut ViewContext<Self>,
4683 ) {
4684 let buffer = self.buffer.read(cx);
4685 let snapshot = buffer.snapshot(cx);
4686 let selection = self.selections.newest_adjusted(cx);
4687 let cursor = selection.head();
4688 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
4689 let suggested_indents = snapshot.suggested_indents([cursor.row], cx);
4690 if let Some(suggested_indent) = suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
4691 {
4692 if cursor.column < suggested_indent.len
4693 && cursor.column <= current_indent.len
4694 && current_indent.len <= suggested_indent.len
4695 {
4696 self.tab(&Default::default(), cx);
4697 return;
4698 }
4699 }
4700
4701 if self.show_inline_completions_in_menu(cx) {
4702 self.hide_context_menu(cx);
4703 }
4704
4705 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
4706 return;
4707 };
4708
4709 self.report_inline_completion_event(true, cx);
4710
4711 match &active_inline_completion.completion {
4712 InlineCompletion::Move(position) => {
4713 let position = *position;
4714 self.change_selections(Some(Autoscroll::newest()), cx, |selections| {
4715 selections.select_anchor_ranges([position..position]);
4716 });
4717 }
4718 InlineCompletion::Edit { edits, .. } => {
4719 if let Some(provider) = self.inline_completion_provider() {
4720 provider.accept(cx);
4721 }
4722
4723 let snapshot = self.buffer.read(cx).snapshot(cx);
4724 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
4725
4726 self.buffer.update(cx, |buffer, cx| {
4727 buffer.edit(edits.iter().cloned(), None, cx)
4728 });
4729
4730 self.change_selections(None, cx, |s| {
4731 s.select_anchor_ranges([last_edit_end..last_edit_end])
4732 });
4733
4734 self.update_visible_inline_completion(cx);
4735 if self.active_inline_completion.is_none() {
4736 self.refresh_inline_completion(true, true, cx);
4737 }
4738
4739 cx.notify();
4740 }
4741 }
4742 }
4743
4744 pub fn accept_partial_inline_completion(
4745 &mut self,
4746 _: &AcceptPartialInlineCompletion,
4747 cx: &mut ViewContext<Self>,
4748 ) {
4749 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
4750 return;
4751 };
4752 if self.selections.count() != 1 {
4753 return;
4754 }
4755
4756 self.report_inline_completion_event(true, cx);
4757
4758 match &active_inline_completion.completion {
4759 InlineCompletion::Move(position) => {
4760 let position = *position;
4761 self.change_selections(Some(Autoscroll::newest()), cx, |selections| {
4762 selections.select_anchor_ranges([position..position]);
4763 });
4764 }
4765 InlineCompletion::Edit { edits, .. } => {
4766 // Find an insertion that starts at the cursor position.
4767 let snapshot = self.buffer.read(cx).snapshot(cx);
4768 let cursor_offset = self.selections.newest::<usize>(cx).head();
4769 let insertion = edits.iter().find_map(|(range, text)| {
4770 let range = range.to_offset(&snapshot);
4771 if range.is_empty() && range.start == cursor_offset {
4772 Some(text)
4773 } else {
4774 None
4775 }
4776 });
4777
4778 if let Some(text) = insertion {
4779 let mut partial_completion = text
4780 .chars()
4781 .by_ref()
4782 .take_while(|c| c.is_alphabetic())
4783 .collect::<String>();
4784 if partial_completion.is_empty() {
4785 partial_completion = text
4786 .chars()
4787 .by_ref()
4788 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
4789 .collect::<String>();
4790 }
4791
4792 cx.emit(EditorEvent::InputHandled {
4793 utf16_range_to_replace: None,
4794 text: partial_completion.clone().into(),
4795 });
4796
4797 self.insert_with_autoindent_mode(&partial_completion, None, cx);
4798
4799 self.refresh_inline_completion(true, true, cx);
4800 cx.notify();
4801 } else {
4802 self.accept_inline_completion(&Default::default(), cx);
4803 }
4804 }
4805 }
4806 }
4807
4808 fn discard_inline_completion(
4809 &mut self,
4810 should_report_inline_completion_event: bool,
4811 cx: &mut ViewContext<Self>,
4812 ) -> bool {
4813 if should_report_inline_completion_event {
4814 self.report_inline_completion_event(false, cx);
4815 }
4816
4817 if let Some(provider) = self.inline_completion_provider() {
4818 provider.discard(cx);
4819 }
4820
4821 self.take_active_inline_completion(cx).is_some()
4822 }
4823
4824 fn report_inline_completion_event(&self, accepted: bool, cx: &AppContext) {
4825 let Some(provider) = self.inline_completion_provider() else {
4826 return;
4827 };
4828
4829 let Some((_, buffer, _)) = self
4830 .buffer
4831 .read(cx)
4832 .excerpt_containing(self.selections.newest_anchor().head(), cx)
4833 else {
4834 return;
4835 };
4836
4837 let extension = buffer
4838 .read(cx)
4839 .file()
4840 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
4841
4842 let event_type = match accepted {
4843 true => "Inline Completion Accepted",
4844 false => "Inline Completion Discarded",
4845 };
4846 telemetry::event!(
4847 event_type,
4848 provider = provider.name(),
4849 suggestion_accepted = accepted,
4850 file_extension = extension,
4851 );
4852 }
4853
4854 pub fn has_active_inline_completion(&self) -> bool {
4855 self.active_inline_completion.is_some()
4856 }
4857
4858 fn take_active_inline_completion(
4859 &mut self,
4860 cx: &mut ViewContext<Self>,
4861 ) -> Option<InlineCompletion> {
4862 let active_inline_completion = self.active_inline_completion.take()?;
4863 self.splice_inlays(active_inline_completion.inlay_ids, Default::default(), cx);
4864 self.clear_highlights::<InlineCompletionHighlight>(cx);
4865 Some(active_inline_completion.completion)
4866 }
4867
4868 fn update_visible_inline_completion(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4869 let selection = self.selections.newest_anchor();
4870 let cursor = selection.head();
4871 let multibuffer = self.buffer.read(cx).snapshot(cx);
4872 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
4873 let excerpt_id = cursor.excerpt_id;
4874
4875 let completions_menu_has_precedence = !self.show_inline_completions_in_menu(cx)
4876 && (self.context_menu.borrow().is_some()
4877 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
4878 if completions_menu_has_precedence
4879 || !offset_selection.is_empty()
4880 || !self.enable_inline_completions
4881 || self
4882 .active_inline_completion
4883 .as_ref()
4884 .map_or(false, |completion| {
4885 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
4886 let invalidation_range = invalidation_range.start..=invalidation_range.end;
4887 !invalidation_range.contains(&offset_selection.head())
4888 })
4889 {
4890 self.discard_inline_completion(false, cx);
4891 return None;
4892 }
4893
4894 self.take_active_inline_completion(cx);
4895 let provider = self.inline_completion_provider()?;
4896
4897 let (buffer, cursor_buffer_position) =
4898 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4899
4900 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
4901 let edits = inline_completion
4902 .edits
4903 .into_iter()
4904 .flat_map(|(range, new_text)| {
4905 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
4906 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
4907 Some((start..end, new_text))
4908 })
4909 .collect::<Vec<_>>();
4910 if edits.is_empty() {
4911 return None;
4912 }
4913
4914 let first_edit_start = edits.first().unwrap().0.start;
4915 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
4916 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
4917
4918 let last_edit_end = edits.last().unwrap().0.end;
4919 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
4920 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
4921
4922 let cursor_row = cursor.to_point(&multibuffer).row;
4923
4924 let mut inlay_ids = Vec::new();
4925 let invalidation_row_range;
4926 let completion = if cursor_row < edit_start_row {
4927 invalidation_row_range = cursor_row..edit_end_row;
4928 InlineCompletion::Move(first_edit_start)
4929 } else if cursor_row > edit_end_row {
4930 invalidation_row_range = edit_start_row..cursor_row;
4931 InlineCompletion::Move(first_edit_start)
4932 } else {
4933 if edits
4934 .iter()
4935 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
4936 {
4937 let mut inlays = Vec::new();
4938 for (range, new_text) in &edits {
4939 let inlay = Inlay::inline_completion(
4940 post_inc(&mut self.next_inlay_id),
4941 range.start,
4942 new_text.as_str(),
4943 );
4944 inlay_ids.push(inlay.id);
4945 inlays.push(inlay);
4946 }
4947
4948 self.splice_inlays(vec![], inlays, cx);
4949 } else {
4950 let background_color = cx.theme().status().deleted_background;
4951 self.highlight_text::<InlineCompletionHighlight>(
4952 edits.iter().map(|(range, _)| range.clone()).collect(),
4953 HighlightStyle {
4954 background_color: Some(background_color),
4955 ..Default::default()
4956 },
4957 cx,
4958 );
4959 }
4960
4961 invalidation_row_range = edit_start_row..edit_end_row;
4962
4963 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
4964 if provider.show_tab_accept_marker()
4965 && first_edit_start_point.row == last_edit_end_point.row
4966 && !edits.iter().any(|(_, edit)| edit.contains('\n'))
4967 {
4968 EditDisplayMode::TabAccept
4969 } else {
4970 EditDisplayMode::Inline
4971 }
4972 } else {
4973 EditDisplayMode::DiffPopover
4974 };
4975
4976 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
4977
4978 InlineCompletion::Edit {
4979 edits,
4980 edit_preview: inline_completion.edit_preview,
4981 display_mode,
4982 snapshot,
4983 }
4984 };
4985
4986 let invalidation_range = multibuffer
4987 .anchor_before(Point::new(invalidation_row_range.start, 0))
4988 ..multibuffer.anchor_after(Point::new(
4989 invalidation_row_range.end,
4990 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
4991 ));
4992
4993 self.active_inline_completion = Some(InlineCompletionState {
4994 inlay_ids,
4995 completion,
4996 invalidation_range,
4997 });
4998
4999 if self.show_inline_completions_in_menu(cx) && self.has_active_completions_menu() {
5000 if let Some(hint) = self.inline_completion_menu_hint(cx) {
5001 match self.context_menu.borrow_mut().as_mut() {
5002 Some(CodeContextMenu::Completions(menu)) => {
5003 menu.show_inline_completion_hint(hint);
5004 }
5005 _ => {}
5006 }
5007 }
5008 }
5009
5010 cx.notify();
5011
5012 Some(())
5013 }
5014
5015 fn inline_completion_menu_hint(
5016 &self,
5017 cx: &mut ViewContext<Self>,
5018 ) -> Option<InlineCompletionMenuHint> {
5019 let provider = self.inline_completion_provider()?;
5020 if self.has_active_inline_completion() {
5021 let editor_snapshot = self.snapshot(cx);
5022
5023 let text = match &self.active_inline_completion.as_ref()?.completion {
5024 InlineCompletion::Edit {
5025 edits,
5026 edit_preview,
5027 display_mode: _,
5028 snapshot,
5029 } => edit_preview
5030 .as_ref()
5031 .and_then(|edit_preview| {
5032 inline_completion_edit_text(&snapshot, &edits, edit_preview, true, cx)
5033 })
5034 .map(InlineCompletionText::Edit),
5035 InlineCompletion::Move(target) => {
5036 let target_point =
5037 target.to_point(&editor_snapshot.display_snapshot.buffer_snapshot);
5038 let target_line = target_point.row + 1;
5039 Some(InlineCompletionText::Move(
5040 format!("Jump to edit in line {}", target_line).into(),
5041 ))
5042 }
5043 };
5044
5045 Some(InlineCompletionMenuHint::Loaded { text: text? })
5046 } else if provider.is_refreshing(cx) {
5047 Some(InlineCompletionMenuHint::Loading)
5048 } else if provider.needs_terms_acceptance(cx) {
5049 Some(InlineCompletionMenuHint::PendingTermsAcceptance)
5050 } else {
5051 Some(InlineCompletionMenuHint::None)
5052 }
5053 }
5054
5055 pub fn inline_completion_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5056 Some(self.inline_completion_provider.as_ref()?.provider.clone())
5057 }
5058
5059 fn show_inline_completions_in_menu(&self, cx: &AppContext) -> bool {
5060 let by_provider = matches!(
5061 self.menu_inline_completions_policy,
5062 MenuInlineCompletionsPolicy::ByProvider
5063 );
5064
5065 by_provider
5066 && EditorSettings::get_global(cx).show_inline_completions_in_menu
5067 && self
5068 .inline_completion_provider()
5069 .map_or(false, |provider| provider.show_completions_in_menu())
5070 }
5071
5072 fn render_code_actions_indicator(
5073 &self,
5074 _style: &EditorStyle,
5075 row: DisplayRow,
5076 is_active: bool,
5077 cx: &mut ViewContext<Self>,
5078 ) -> Option<IconButton> {
5079 if self.available_code_actions.is_some() {
5080 Some(
5081 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5082 .shape(ui::IconButtonShape::Square)
5083 .icon_size(IconSize::XSmall)
5084 .icon_color(Color::Muted)
5085 .toggle_state(is_active)
5086 .tooltip({
5087 let focus_handle = self.focus_handle.clone();
5088 move |cx| {
5089 Tooltip::for_action_in(
5090 "Toggle Code Actions",
5091 &ToggleCodeActions {
5092 deployed_from_indicator: None,
5093 },
5094 &focus_handle,
5095 cx,
5096 )
5097 }
5098 })
5099 .on_click(cx.listener(move |editor, _e, cx| {
5100 editor.focus(cx);
5101 editor.toggle_code_actions(
5102 &ToggleCodeActions {
5103 deployed_from_indicator: Some(row),
5104 },
5105 cx,
5106 );
5107 })),
5108 )
5109 } else {
5110 None
5111 }
5112 }
5113
5114 fn clear_tasks(&mut self) {
5115 self.tasks.clear()
5116 }
5117
5118 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5119 if self.tasks.insert(key, value).is_some() {
5120 // This case should hopefully be rare, but just in case...
5121 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5122 }
5123 }
5124
5125 fn build_tasks_context(
5126 project: &Model<Project>,
5127 buffer: &Model<Buffer>,
5128 buffer_row: u32,
5129 tasks: &Arc<RunnableTasks>,
5130 cx: &mut ViewContext<Self>,
5131 ) -> Task<Option<task::TaskContext>> {
5132 let position = Point::new(buffer_row, tasks.column);
5133 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
5134 let location = Location {
5135 buffer: buffer.clone(),
5136 range: range_start..range_start,
5137 };
5138 // Fill in the environmental variables from the tree-sitter captures
5139 let mut captured_task_variables = TaskVariables::default();
5140 for (capture_name, value) in tasks.extra_variables.clone() {
5141 captured_task_variables.insert(
5142 task::VariableName::Custom(capture_name.into()),
5143 value.clone(),
5144 );
5145 }
5146 project.update(cx, |project, cx| {
5147 project.task_store().update(cx, |task_store, cx| {
5148 task_store.task_context_for_location(captured_task_variables, location, cx)
5149 })
5150 })
5151 }
5152
5153 pub fn spawn_nearest_task(&mut self, action: &SpawnNearestTask, cx: &mut ViewContext<Self>) {
5154 let Some((workspace, _)) = self.workspace.clone() else {
5155 return;
5156 };
5157 let Some(project) = self.project.clone() else {
5158 return;
5159 };
5160
5161 // Try to find a closest, enclosing node using tree-sitter that has a
5162 // task
5163 let Some((buffer, buffer_row, tasks)) = self
5164 .find_enclosing_node_task(cx)
5165 // Or find the task that's closest in row-distance.
5166 .or_else(|| self.find_closest_task(cx))
5167 else {
5168 return;
5169 };
5170
5171 let reveal_strategy = action.reveal;
5172 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
5173 cx.spawn(|_, mut cx| async move {
5174 let context = task_context.await?;
5175 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
5176
5177 let resolved = resolved_task.resolved.as_mut()?;
5178 resolved.reveal = reveal_strategy;
5179
5180 workspace
5181 .update(&mut cx, |workspace, cx| {
5182 workspace::tasks::schedule_resolved_task(
5183 workspace,
5184 task_source_kind,
5185 resolved_task,
5186 false,
5187 cx,
5188 );
5189 })
5190 .ok()
5191 })
5192 .detach();
5193 }
5194
5195 fn find_closest_task(
5196 &mut self,
5197 cx: &mut ViewContext<Self>,
5198 ) -> Option<(Model<Buffer>, u32, Arc<RunnableTasks>)> {
5199 let cursor_row = self.selections.newest_adjusted(cx).head().row;
5200
5201 let ((buffer_id, row), tasks) = self
5202 .tasks
5203 .iter()
5204 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
5205
5206 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
5207 let tasks = Arc::new(tasks.to_owned());
5208 Some((buffer, *row, tasks))
5209 }
5210
5211 fn find_enclosing_node_task(
5212 &mut self,
5213 cx: &mut ViewContext<Self>,
5214 ) -> Option<(Model<Buffer>, u32, Arc<RunnableTasks>)> {
5215 let snapshot = self.buffer.read(cx).snapshot(cx);
5216 let offset = self.selections.newest::<usize>(cx).head();
5217 let excerpt = snapshot.excerpt_containing(offset..offset)?;
5218 let buffer_id = excerpt.buffer().remote_id();
5219
5220 let layer = excerpt.buffer().syntax_layer_at(offset)?;
5221 let mut cursor = layer.node().walk();
5222
5223 while cursor.goto_first_child_for_byte(offset).is_some() {
5224 if cursor.node().end_byte() == offset {
5225 cursor.goto_next_sibling();
5226 }
5227 }
5228
5229 // Ascend to the smallest ancestor that contains the range and has a task.
5230 loop {
5231 let node = cursor.node();
5232 let node_range = node.byte_range();
5233 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
5234
5235 // Check if this node contains our offset
5236 if node_range.start <= offset && node_range.end >= offset {
5237 // If it contains offset, check for task
5238 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
5239 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
5240 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
5241 }
5242 }
5243
5244 if !cursor.goto_parent() {
5245 break;
5246 }
5247 }
5248 None
5249 }
5250
5251 fn render_run_indicator(
5252 &self,
5253 _style: &EditorStyle,
5254 is_active: bool,
5255 row: DisplayRow,
5256 cx: &mut ViewContext<Self>,
5257 ) -> IconButton {
5258 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5259 .shape(ui::IconButtonShape::Square)
5260 .icon_size(IconSize::XSmall)
5261 .icon_color(Color::Muted)
5262 .toggle_state(is_active)
5263 .on_click(cx.listener(move |editor, _e, cx| {
5264 editor.focus(cx);
5265 editor.toggle_code_actions(
5266 &ToggleCodeActions {
5267 deployed_from_indicator: Some(row),
5268 },
5269 cx,
5270 );
5271 }))
5272 }
5273
5274 #[cfg(any(test, feature = "test-support"))]
5275 pub fn context_menu_visible(&self) -> bool {
5276 self.context_menu
5277 .borrow()
5278 .as_ref()
5279 .map_or(false, |menu| menu.visible())
5280 }
5281
5282 #[cfg(feature = "test-support")]
5283 pub fn context_menu_contains_inline_completion(&self) -> bool {
5284 self.context_menu
5285 .borrow()
5286 .as_ref()
5287 .map_or(false, |menu| match menu {
5288 CodeContextMenu::Completions(menu) => {
5289 menu.entries.borrow().first().map_or(false, |entry| {
5290 matches!(entry, CompletionEntry::InlineCompletionHint(_))
5291 })
5292 }
5293 CodeContextMenu::CodeActions(_) => false,
5294 })
5295 }
5296
5297 fn context_menu_origin(&self, cursor_position: DisplayPoint) -> Option<ContextMenuOrigin> {
5298 self.context_menu
5299 .borrow()
5300 .as_ref()
5301 .map(|menu| menu.origin(cursor_position))
5302 }
5303
5304 fn render_context_menu(
5305 &self,
5306 style: &EditorStyle,
5307 max_height_in_lines: u32,
5308 y_flipped: bool,
5309 cx: &mut ViewContext<Editor>,
5310 ) -> Option<AnyElement> {
5311 self.context_menu.borrow().as_ref().and_then(|menu| {
5312 if menu.visible() {
5313 Some(menu.render(style, max_height_in_lines, y_flipped, cx))
5314 } else {
5315 None
5316 }
5317 })
5318 }
5319
5320 fn render_context_menu_aside(
5321 &self,
5322 style: &EditorStyle,
5323 max_size: Size<Pixels>,
5324 cx: &mut ViewContext<Editor>,
5325 ) -> Option<AnyElement> {
5326 self.context_menu.borrow().as_ref().and_then(|menu| {
5327 if menu.visible() {
5328 menu.render_aside(
5329 style,
5330 max_size,
5331 self.workspace.as_ref().map(|(w, _)| w.clone()),
5332 cx,
5333 )
5334 } else {
5335 None
5336 }
5337 })
5338 }
5339
5340 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<CodeContextMenu> {
5341 cx.notify();
5342 self.completion_tasks.clear();
5343 let context_menu = self.context_menu.borrow_mut().take();
5344 if context_menu.is_some() && !self.show_inline_completions_in_menu(cx) {
5345 self.update_visible_inline_completion(cx);
5346 }
5347 context_menu
5348 }
5349
5350 fn show_snippet_choices(
5351 &mut self,
5352 choices: &Vec<String>,
5353 selection: Range<Anchor>,
5354 cx: &mut ViewContext<Self>,
5355 ) {
5356 if selection.start.buffer_id.is_none() {
5357 return;
5358 }
5359 let buffer_id = selection.start.buffer_id.unwrap();
5360 let buffer = self.buffer().read(cx).buffer(buffer_id);
5361 let id = post_inc(&mut self.next_completion_id);
5362
5363 if let Some(buffer) = buffer {
5364 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
5365 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
5366 ));
5367 }
5368 }
5369
5370 pub fn insert_snippet(
5371 &mut self,
5372 insertion_ranges: &[Range<usize>],
5373 snippet: Snippet,
5374 cx: &mut ViewContext<Self>,
5375 ) -> Result<()> {
5376 struct Tabstop<T> {
5377 is_end_tabstop: bool,
5378 ranges: Vec<Range<T>>,
5379 choices: Option<Vec<String>>,
5380 }
5381
5382 let tabstops = self.buffer.update(cx, |buffer, cx| {
5383 let snippet_text: Arc<str> = snippet.text.clone().into();
5384 buffer.edit(
5385 insertion_ranges
5386 .iter()
5387 .cloned()
5388 .map(|range| (range, snippet_text.clone())),
5389 Some(AutoindentMode::EachLine),
5390 cx,
5391 );
5392
5393 let snapshot = &*buffer.read(cx);
5394 let snippet = &snippet;
5395 snippet
5396 .tabstops
5397 .iter()
5398 .map(|tabstop| {
5399 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
5400 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
5401 });
5402 let mut tabstop_ranges = tabstop
5403 .ranges
5404 .iter()
5405 .flat_map(|tabstop_range| {
5406 let mut delta = 0_isize;
5407 insertion_ranges.iter().map(move |insertion_range| {
5408 let insertion_start = insertion_range.start as isize + delta;
5409 delta +=
5410 snippet.text.len() as isize - insertion_range.len() as isize;
5411
5412 let start = ((insertion_start + tabstop_range.start) as usize)
5413 .min(snapshot.len());
5414 let end = ((insertion_start + tabstop_range.end) as usize)
5415 .min(snapshot.len());
5416 snapshot.anchor_before(start)..snapshot.anchor_after(end)
5417 })
5418 })
5419 .collect::<Vec<_>>();
5420 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
5421
5422 Tabstop {
5423 is_end_tabstop,
5424 ranges: tabstop_ranges,
5425 choices: tabstop.choices.clone(),
5426 }
5427 })
5428 .collect::<Vec<_>>()
5429 });
5430 if let Some(tabstop) = tabstops.first() {
5431 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5432 s.select_ranges(tabstop.ranges.iter().cloned());
5433 });
5434
5435 if let Some(choices) = &tabstop.choices {
5436 if let Some(selection) = tabstop.ranges.first() {
5437 self.show_snippet_choices(choices, selection.clone(), cx)
5438 }
5439 }
5440
5441 // If we're already at the last tabstop and it's at the end of the snippet,
5442 // we're done, we don't need to keep the state around.
5443 if !tabstop.is_end_tabstop {
5444 let choices = tabstops
5445 .iter()
5446 .map(|tabstop| tabstop.choices.clone())
5447 .collect();
5448
5449 let ranges = tabstops
5450 .into_iter()
5451 .map(|tabstop| tabstop.ranges)
5452 .collect::<Vec<_>>();
5453
5454 self.snippet_stack.push(SnippetState {
5455 active_index: 0,
5456 ranges,
5457 choices,
5458 });
5459 }
5460
5461 // Check whether the just-entered snippet ends with an auto-closable bracket.
5462 if self.autoclose_regions.is_empty() {
5463 let snapshot = self.buffer.read(cx).snapshot(cx);
5464 for selection in &mut self.selections.all::<Point>(cx) {
5465 let selection_head = selection.head();
5466 let Some(scope) = snapshot.language_scope_at(selection_head) else {
5467 continue;
5468 };
5469
5470 let mut bracket_pair = None;
5471 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
5472 let prev_chars = snapshot
5473 .reversed_chars_at(selection_head)
5474 .collect::<String>();
5475 for (pair, enabled) in scope.brackets() {
5476 if enabled
5477 && pair.close
5478 && prev_chars.starts_with(pair.start.as_str())
5479 && next_chars.starts_with(pair.end.as_str())
5480 {
5481 bracket_pair = Some(pair.clone());
5482 break;
5483 }
5484 }
5485 if let Some(pair) = bracket_pair {
5486 let start = snapshot.anchor_after(selection_head);
5487 let end = snapshot.anchor_after(selection_head);
5488 self.autoclose_regions.push(AutocloseRegion {
5489 selection_id: selection.id,
5490 range: start..end,
5491 pair,
5492 });
5493 }
5494 }
5495 }
5496 }
5497 Ok(())
5498 }
5499
5500 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5501 self.move_to_snippet_tabstop(Bias::Right, cx)
5502 }
5503
5504 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5505 self.move_to_snippet_tabstop(Bias::Left, cx)
5506 }
5507
5508 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
5509 if let Some(mut snippet) = self.snippet_stack.pop() {
5510 match bias {
5511 Bias::Left => {
5512 if snippet.active_index > 0 {
5513 snippet.active_index -= 1;
5514 } else {
5515 self.snippet_stack.push(snippet);
5516 return false;
5517 }
5518 }
5519 Bias::Right => {
5520 if snippet.active_index + 1 < snippet.ranges.len() {
5521 snippet.active_index += 1;
5522 } else {
5523 self.snippet_stack.push(snippet);
5524 return false;
5525 }
5526 }
5527 }
5528 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
5529 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5530 s.select_anchor_ranges(current_ranges.iter().cloned())
5531 });
5532
5533 if let Some(choices) = &snippet.choices[snippet.active_index] {
5534 if let Some(selection) = current_ranges.first() {
5535 self.show_snippet_choices(&choices, selection.clone(), cx);
5536 }
5537 }
5538
5539 // If snippet state is not at the last tabstop, push it back on the stack
5540 if snippet.active_index + 1 < snippet.ranges.len() {
5541 self.snippet_stack.push(snippet);
5542 }
5543 return true;
5544 }
5545 }
5546
5547 false
5548 }
5549
5550 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
5551 self.transact(cx, |this, cx| {
5552 this.select_all(&SelectAll, cx);
5553 this.insert("", cx);
5554 });
5555 }
5556
5557 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
5558 self.transact(cx, |this, cx| {
5559 this.select_autoclose_pair(cx);
5560 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
5561 if !this.linked_edit_ranges.is_empty() {
5562 let selections = this.selections.all::<MultiBufferPoint>(cx);
5563 let snapshot = this.buffer.read(cx).snapshot(cx);
5564
5565 for selection in selections.iter() {
5566 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
5567 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
5568 if selection_start.buffer_id != selection_end.buffer_id {
5569 continue;
5570 }
5571 if let Some(ranges) =
5572 this.linked_editing_ranges_for(selection_start..selection_end, cx)
5573 {
5574 for (buffer, entries) in ranges {
5575 linked_ranges.entry(buffer).or_default().extend(entries);
5576 }
5577 }
5578 }
5579 }
5580
5581 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
5582 if !this.selections.line_mode {
5583 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
5584 for selection in &mut selections {
5585 if selection.is_empty() {
5586 let old_head = selection.head();
5587 let mut new_head =
5588 movement::left(&display_map, old_head.to_display_point(&display_map))
5589 .to_point(&display_map);
5590 if let Some((buffer, line_buffer_range)) = display_map
5591 .buffer_snapshot
5592 .buffer_line_for_row(MultiBufferRow(old_head.row))
5593 {
5594 let indent_size =
5595 buffer.indent_size_for_line(line_buffer_range.start.row);
5596 let indent_len = match indent_size.kind {
5597 IndentKind::Space => {
5598 buffer.settings_at(line_buffer_range.start, cx).tab_size
5599 }
5600 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
5601 };
5602 if old_head.column <= indent_size.len && old_head.column > 0 {
5603 let indent_len = indent_len.get();
5604 new_head = cmp::min(
5605 new_head,
5606 MultiBufferPoint::new(
5607 old_head.row,
5608 ((old_head.column - 1) / indent_len) * indent_len,
5609 ),
5610 );
5611 }
5612 }
5613
5614 selection.set_head(new_head, SelectionGoal::None);
5615 }
5616 }
5617 }
5618
5619 this.signature_help_state.set_backspace_pressed(true);
5620 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5621 this.insert("", cx);
5622 let empty_str: Arc<str> = Arc::from("");
5623 for (buffer, edits) in linked_ranges {
5624 let snapshot = buffer.read(cx).snapshot();
5625 use text::ToPoint as TP;
5626
5627 let edits = edits
5628 .into_iter()
5629 .map(|range| {
5630 let end_point = TP::to_point(&range.end, &snapshot);
5631 let mut start_point = TP::to_point(&range.start, &snapshot);
5632
5633 if end_point == start_point {
5634 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
5635 .saturating_sub(1);
5636 start_point =
5637 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
5638 };
5639
5640 (start_point..end_point, empty_str.clone())
5641 })
5642 .sorted_by_key(|(range, _)| range.start)
5643 .collect::<Vec<_>>();
5644 buffer.update(cx, |this, cx| {
5645 this.edit(edits, None, cx);
5646 })
5647 }
5648 this.refresh_inline_completion(true, false, cx);
5649 linked_editing_ranges::refresh_linked_ranges(this, cx);
5650 });
5651 }
5652
5653 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
5654 self.transact(cx, |this, cx| {
5655 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5656 let line_mode = s.line_mode;
5657 s.move_with(|map, selection| {
5658 if selection.is_empty() && !line_mode {
5659 let cursor = movement::right(map, selection.head());
5660 selection.end = cursor;
5661 selection.reversed = true;
5662 selection.goal = SelectionGoal::None;
5663 }
5664 })
5665 });
5666 this.insert("", cx);
5667 this.refresh_inline_completion(true, false, cx);
5668 });
5669 }
5670
5671 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
5672 if self.move_to_prev_snippet_tabstop(cx) {
5673 return;
5674 }
5675
5676 self.outdent(&Outdent, cx);
5677 }
5678
5679 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
5680 if self.move_to_next_snippet_tabstop(cx) || self.read_only(cx) {
5681 return;
5682 }
5683
5684 let mut selections = self.selections.all_adjusted(cx);
5685 let buffer = self.buffer.read(cx);
5686 let snapshot = buffer.snapshot(cx);
5687 let rows_iter = selections.iter().map(|s| s.head().row);
5688 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
5689
5690 let mut edits = Vec::new();
5691 let mut prev_edited_row = 0;
5692 let mut row_delta = 0;
5693 for selection in &mut selections {
5694 if selection.start.row != prev_edited_row {
5695 row_delta = 0;
5696 }
5697 prev_edited_row = selection.end.row;
5698
5699 // If the selection is non-empty, then increase the indentation of the selected lines.
5700 if !selection.is_empty() {
5701 row_delta =
5702 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5703 continue;
5704 }
5705
5706 // If the selection is empty and the cursor is in the leading whitespace before the
5707 // suggested indentation, then auto-indent the line.
5708 let cursor = selection.head();
5709 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
5710 if let Some(suggested_indent) =
5711 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
5712 {
5713 if cursor.column < suggested_indent.len
5714 && cursor.column <= current_indent.len
5715 && current_indent.len <= suggested_indent.len
5716 {
5717 selection.start = Point::new(cursor.row, suggested_indent.len);
5718 selection.end = selection.start;
5719 if row_delta == 0 {
5720 edits.extend(Buffer::edit_for_indent_size_adjustment(
5721 cursor.row,
5722 current_indent,
5723 suggested_indent,
5724 ));
5725 row_delta = suggested_indent.len - current_indent.len;
5726 }
5727 continue;
5728 }
5729 }
5730
5731 // Otherwise, insert a hard or soft tab.
5732 let settings = buffer.settings_at(cursor, cx);
5733 let tab_size = if settings.hard_tabs {
5734 IndentSize::tab()
5735 } else {
5736 let tab_size = settings.tab_size.get();
5737 let char_column = snapshot
5738 .text_for_range(Point::new(cursor.row, 0)..cursor)
5739 .flat_map(str::chars)
5740 .count()
5741 + row_delta as usize;
5742 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
5743 IndentSize::spaces(chars_to_next_tab_stop)
5744 };
5745 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
5746 selection.end = selection.start;
5747 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
5748 row_delta += tab_size.len;
5749 }
5750
5751 self.transact(cx, |this, cx| {
5752 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5753 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5754 this.refresh_inline_completion(true, false, cx);
5755 });
5756 }
5757
5758 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
5759 if self.read_only(cx) {
5760 return;
5761 }
5762 let mut selections = self.selections.all::<Point>(cx);
5763 let mut prev_edited_row = 0;
5764 let mut row_delta = 0;
5765 let mut edits = Vec::new();
5766 let buffer = self.buffer.read(cx);
5767 let snapshot = buffer.snapshot(cx);
5768 for selection in &mut selections {
5769 if selection.start.row != prev_edited_row {
5770 row_delta = 0;
5771 }
5772 prev_edited_row = selection.end.row;
5773
5774 row_delta =
5775 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5776 }
5777
5778 self.transact(cx, |this, cx| {
5779 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5780 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5781 });
5782 }
5783
5784 fn indent_selection(
5785 buffer: &MultiBuffer,
5786 snapshot: &MultiBufferSnapshot,
5787 selection: &mut Selection<Point>,
5788 edits: &mut Vec<(Range<Point>, String)>,
5789 delta_for_start_row: u32,
5790 cx: &AppContext,
5791 ) -> u32 {
5792 let settings = buffer.settings_at(selection.start, cx);
5793 let tab_size = settings.tab_size.get();
5794 let indent_kind = if settings.hard_tabs {
5795 IndentKind::Tab
5796 } else {
5797 IndentKind::Space
5798 };
5799 let mut start_row = selection.start.row;
5800 let mut end_row = selection.end.row + 1;
5801
5802 // If a selection ends at the beginning of a line, don't indent
5803 // that last line.
5804 if selection.end.column == 0 && selection.end.row > selection.start.row {
5805 end_row -= 1;
5806 }
5807
5808 // Avoid re-indenting a row that has already been indented by a
5809 // previous selection, but still update this selection's column
5810 // to reflect that indentation.
5811 if delta_for_start_row > 0 {
5812 start_row += 1;
5813 selection.start.column += delta_for_start_row;
5814 if selection.end.row == selection.start.row {
5815 selection.end.column += delta_for_start_row;
5816 }
5817 }
5818
5819 let mut delta_for_end_row = 0;
5820 let has_multiple_rows = start_row + 1 != end_row;
5821 for row in start_row..end_row {
5822 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
5823 let indent_delta = match (current_indent.kind, indent_kind) {
5824 (IndentKind::Space, IndentKind::Space) => {
5825 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
5826 IndentSize::spaces(columns_to_next_tab_stop)
5827 }
5828 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
5829 (_, IndentKind::Tab) => IndentSize::tab(),
5830 };
5831
5832 let start = if has_multiple_rows || current_indent.len < selection.start.column {
5833 0
5834 } else {
5835 selection.start.column
5836 };
5837 let row_start = Point::new(row, start);
5838 edits.push((
5839 row_start..row_start,
5840 indent_delta.chars().collect::<String>(),
5841 ));
5842
5843 // Update this selection's endpoints to reflect the indentation.
5844 if row == selection.start.row {
5845 selection.start.column += indent_delta.len;
5846 }
5847 if row == selection.end.row {
5848 selection.end.column += indent_delta.len;
5849 delta_for_end_row = indent_delta.len;
5850 }
5851 }
5852
5853 if selection.start.row == selection.end.row {
5854 delta_for_start_row + delta_for_end_row
5855 } else {
5856 delta_for_end_row
5857 }
5858 }
5859
5860 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
5861 if self.read_only(cx) {
5862 return;
5863 }
5864 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5865 let selections = self.selections.all::<Point>(cx);
5866 let mut deletion_ranges = Vec::new();
5867 let mut last_outdent = None;
5868 {
5869 let buffer = self.buffer.read(cx);
5870 let snapshot = buffer.snapshot(cx);
5871 for selection in &selections {
5872 let settings = buffer.settings_at(selection.start, cx);
5873 let tab_size = settings.tab_size.get();
5874 let mut rows = selection.spanned_rows(false, &display_map);
5875
5876 // Avoid re-outdenting a row that has already been outdented by a
5877 // previous selection.
5878 if let Some(last_row) = last_outdent {
5879 if last_row == rows.start {
5880 rows.start = rows.start.next_row();
5881 }
5882 }
5883 let has_multiple_rows = rows.len() > 1;
5884 for row in rows.iter_rows() {
5885 let indent_size = snapshot.indent_size_for_line(row);
5886 if indent_size.len > 0 {
5887 let deletion_len = match indent_size.kind {
5888 IndentKind::Space => {
5889 let columns_to_prev_tab_stop = indent_size.len % tab_size;
5890 if columns_to_prev_tab_stop == 0 {
5891 tab_size
5892 } else {
5893 columns_to_prev_tab_stop
5894 }
5895 }
5896 IndentKind::Tab => 1,
5897 };
5898 let start = if has_multiple_rows
5899 || deletion_len > selection.start.column
5900 || indent_size.len < selection.start.column
5901 {
5902 0
5903 } else {
5904 selection.start.column - deletion_len
5905 };
5906 deletion_ranges.push(
5907 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
5908 );
5909 last_outdent = Some(row);
5910 }
5911 }
5912 }
5913 }
5914
5915 self.transact(cx, |this, cx| {
5916 this.buffer.update(cx, |buffer, cx| {
5917 let empty_str: Arc<str> = Arc::default();
5918 buffer.edit(
5919 deletion_ranges
5920 .into_iter()
5921 .map(|range| (range, empty_str.clone())),
5922 None,
5923 cx,
5924 );
5925 });
5926 let selections = this.selections.all::<usize>(cx);
5927 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5928 });
5929 }
5930
5931 pub fn autoindent(&mut self, _: &AutoIndent, cx: &mut ViewContext<Self>) {
5932 if self.read_only(cx) {
5933 return;
5934 }
5935 let selections = self
5936 .selections
5937 .all::<usize>(cx)
5938 .into_iter()
5939 .map(|s| s.range());
5940
5941 self.transact(cx, |this, cx| {
5942 this.buffer.update(cx, |buffer, cx| {
5943 buffer.autoindent_ranges(selections, cx);
5944 });
5945 let selections = this.selections.all::<usize>(cx);
5946 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5947 });
5948 }
5949
5950 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
5951 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5952 let selections = self.selections.all::<Point>(cx);
5953
5954 let mut new_cursors = Vec::new();
5955 let mut edit_ranges = Vec::new();
5956 let mut selections = selections.iter().peekable();
5957 while let Some(selection) = selections.next() {
5958 let mut rows = selection.spanned_rows(false, &display_map);
5959 let goal_display_column = selection.head().to_display_point(&display_map).column();
5960
5961 // Accumulate contiguous regions of rows that we want to delete.
5962 while let Some(next_selection) = selections.peek() {
5963 let next_rows = next_selection.spanned_rows(false, &display_map);
5964 if next_rows.start <= rows.end {
5965 rows.end = next_rows.end;
5966 selections.next().unwrap();
5967 } else {
5968 break;
5969 }
5970 }
5971
5972 let buffer = &display_map.buffer_snapshot;
5973 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
5974 let edit_end;
5975 let cursor_buffer_row;
5976 if buffer.max_point().row >= rows.end.0 {
5977 // If there's a line after the range, delete the \n from the end of the row range
5978 // and position the cursor on the next line.
5979 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
5980 cursor_buffer_row = rows.end;
5981 } else {
5982 // If there isn't a line after the range, delete the \n from the line before the
5983 // start of the row range and position the cursor there.
5984 edit_start = edit_start.saturating_sub(1);
5985 edit_end = buffer.len();
5986 cursor_buffer_row = rows.start.previous_row();
5987 }
5988
5989 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
5990 *cursor.column_mut() =
5991 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
5992
5993 new_cursors.push((
5994 selection.id,
5995 buffer.anchor_after(cursor.to_point(&display_map)),
5996 ));
5997 edit_ranges.push(edit_start..edit_end);
5998 }
5999
6000 self.transact(cx, |this, cx| {
6001 let buffer = this.buffer.update(cx, |buffer, cx| {
6002 let empty_str: Arc<str> = Arc::default();
6003 buffer.edit(
6004 edit_ranges
6005 .into_iter()
6006 .map(|range| (range, empty_str.clone())),
6007 None,
6008 cx,
6009 );
6010 buffer.snapshot(cx)
6011 });
6012 let new_selections = new_cursors
6013 .into_iter()
6014 .map(|(id, cursor)| {
6015 let cursor = cursor.to_point(&buffer);
6016 Selection {
6017 id,
6018 start: cursor,
6019 end: cursor,
6020 reversed: false,
6021 goal: SelectionGoal::None,
6022 }
6023 })
6024 .collect();
6025
6026 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6027 s.select(new_selections);
6028 });
6029 });
6030 }
6031
6032 pub fn join_lines_impl(&mut self, insert_whitespace: bool, cx: &mut ViewContext<Self>) {
6033 if self.read_only(cx) {
6034 return;
6035 }
6036 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
6037 for selection in self.selections.all::<Point>(cx) {
6038 let start = MultiBufferRow(selection.start.row);
6039 // Treat single line selections as if they include the next line. Otherwise this action
6040 // would do nothing for single line selections individual cursors.
6041 let end = if selection.start.row == selection.end.row {
6042 MultiBufferRow(selection.start.row + 1)
6043 } else {
6044 MultiBufferRow(selection.end.row)
6045 };
6046
6047 if let Some(last_row_range) = row_ranges.last_mut() {
6048 if start <= last_row_range.end {
6049 last_row_range.end = end;
6050 continue;
6051 }
6052 }
6053 row_ranges.push(start..end);
6054 }
6055
6056 let snapshot = self.buffer.read(cx).snapshot(cx);
6057 let mut cursor_positions = Vec::new();
6058 for row_range in &row_ranges {
6059 let anchor = snapshot.anchor_before(Point::new(
6060 row_range.end.previous_row().0,
6061 snapshot.line_len(row_range.end.previous_row()),
6062 ));
6063 cursor_positions.push(anchor..anchor);
6064 }
6065
6066 self.transact(cx, |this, cx| {
6067 for row_range in row_ranges.into_iter().rev() {
6068 for row in row_range.iter_rows().rev() {
6069 let end_of_line = Point::new(row.0, snapshot.line_len(row));
6070 let next_line_row = row.next_row();
6071 let indent = snapshot.indent_size_for_line(next_line_row);
6072 let start_of_next_line = Point::new(next_line_row.0, indent.len);
6073
6074 let replace =
6075 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
6076 " "
6077 } else {
6078 ""
6079 };
6080
6081 this.buffer.update(cx, |buffer, cx| {
6082 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
6083 });
6084 }
6085 }
6086
6087 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6088 s.select_anchor_ranges(cursor_positions)
6089 });
6090 });
6091 }
6092
6093 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
6094 self.join_lines_impl(true, cx);
6095 }
6096
6097 pub fn sort_lines_case_sensitive(
6098 &mut self,
6099 _: &SortLinesCaseSensitive,
6100 cx: &mut ViewContext<Self>,
6101 ) {
6102 self.manipulate_lines(cx, |lines| lines.sort())
6103 }
6104
6105 pub fn sort_lines_case_insensitive(
6106 &mut self,
6107 _: &SortLinesCaseInsensitive,
6108 cx: &mut ViewContext<Self>,
6109 ) {
6110 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
6111 }
6112
6113 pub fn unique_lines_case_insensitive(
6114 &mut self,
6115 _: &UniqueLinesCaseInsensitive,
6116 cx: &mut ViewContext<Self>,
6117 ) {
6118 self.manipulate_lines(cx, |lines| {
6119 let mut seen = HashSet::default();
6120 lines.retain(|line| seen.insert(line.to_lowercase()));
6121 })
6122 }
6123
6124 pub fn unique_lines_case_sensitive(
6125 &mut self,
6126 _: &UniqueLinesCaseSensitive,
6127 cx: &mut ViewContext<Self>,
6128 ) {
6129 self.manipulate_lines(cx, |lines| {
6130 let mut seen = HashSet::default();
6131 lines.retain(|line| seen.insert(*line));
6132 })
6133 }
6134
6135 pub fn revert_file(&mut self, _: &RevertFile, cx: &mut ViewContext<Self>) {
6136 let mut revert_changes = HashMap::default();
6137 let snapshot = self.snapshot(cx);
6138 for hunk in snapshot
6139 .hunks_for_ranges(Some(Point::zero()..snapshot.buffer_snapshot.max_point()).into_iter())
6140 {
6141 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
6142 }
6143 if !revert_changes.is_empty() {
6144 self.transact(cx, |editor, cx| {
6145 editor.revert(revert_changes, cx);
6146 });
6147 }
6148 }
6149
6150 pub fn reload_file(&mut self, _: &ReloadFile, cx: &mut ViewContext<Self>) {
6151 let Some(project) = self.project.clone() else {
6152 return;
6153 };
6154 self.reload(project, cx).detach_and_notify_err(cx);
6155 }
6156
6157 pub fn revert_selected_hunks(&mut self, _: &RevertSelectedHunks, cx: &mut ViewContext<Self>) {
6158 let selections = self.selections.all(cx).into_iter().map(|s| s.range());
6159 self.revert_hunks_in_ranges(selections, cx);
6160 }
6161
6162 fn revert_hunks_in_ranges(
6163 &mut self,
6164 ranges: impl Iterator<Item = Range<Point>>,
6165 cx: &mut ViewContext<Editor>,
6166 ) {
6167 let mut revert_changes = HashMap::default();
6168 let snapshot = self.snapshot(cx);
6169 for hunk in &snapshot.hunks_for_ranges(ranges) {
6170 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
6171 }
6172 if !revert_changes.is_empty() {
6173 self.transact(cx, |editor, cx| {
6174 editor.revert(revert_changes, cx);
6175 });
6176 }
6177 }
6178
6179 pub fn open_active_item_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
6180 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
6181 let project_path = buffer.read(cx).project_path(cx)?;
6182 let project = self.project.as_ref()?.read(cx);
6183 let entry = project.entry_for_path(&project_path, cx)?;
6184 let parent = match &entry.canonical_path {
6185 Some(canonical_path) => canonical_path.to_path_buf(),
6186 None => project.absolute_path(&project_path, cx)?,
6187 }
6188 .parent()?
6189 .to_path_buf();
6190 Some(parent)
6191 }) {
6192 cx.dispatch_action(OpenTerminal { working_directory }.boxed_clone());
6193 }
6194 }
6195
6196 pub fn prepare_revert_change(
6197 &self,
6198 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
6199 hunk: &MultiBufferDiffHunk,
6200 cx: &mut WindowContext,
6201 ) -> Option<()> {
6202 let buffer = self.buffer.read(cx);
6203 let change_set = buffer.change_set_for(hunk.buffer_id)?;
6204 let buffer = buffer.buffer(hunk.buffer_id)?;
6205 let buffer = buffer.read(cx);
6206 let original_text = change_set
6207 .read(cx)
6208 .base_text
6209 .as_ref()?
6210 .as_rope()
6211 .slice(hunk.diff_base_byte_range.clone());
6212 let buffer_snapshot = buffer.snapshot();
6213 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
6214 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
6215 probe
6216 .0
6217 .start
6218 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
6219 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
6220 }) {
6221 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
6222 Some(())
6223 } else {
6224 None
6225 }
6226 }
6227
6228 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
6229 self.manipulate_lines(cx, |lines| lines.reverse())
6230 }
6231
6232 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
6233 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
6234 }
6235
6236 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6237 where
6238 Fn: FnMut(&mut Vec<&str>),
6239 {
6240 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6241 let buffer = self.buffer.read(cx).snapshot(cx);
6242
6243 let mut edits = Vec::new();
6244
6245 let selections = self.selections.all::<Point>(cx);
6246 let mut selections = selections.iter().peekable();
6247 let mut contiguous_row_selections = Vec::new();
6248 let mut new_selections = Vec::new();
6249 let mut added_lines = 0;
6250 let mut removed_lines = 0;
6251
6252 while let Some(selection) = selections.next() {
6253 let (start_row, end_row) = consume_contiguous_rows(
6254 &mut contiguous_row_selections,
6255 selection,
6256 &display_map,
6257 &mut selections,
6258 );
6259
6260 let start_point = Point::new(start_row.0, 0);
6261 let end_point = Point::new(
6262 end_row.previous_row().0,
6263 buffer.line_len(end_row.previous_row()),
6264 );
6265 let text = buffer
6266 .text_for_range(start_point..end_point)
6267 .collect::<String>();
6268
6269 let mut lines = text.split('\n').collect_vec();
6270
6271 let lines_before = lines.len();
6272 callback(&mut lines);
6273 let lines_after = lines.len();
6274
6275 edits.push((start_point..end_point, lines.join("\n")));
6276
6277 // Selections must change based on added and removed line count
6278 let start_row =
6279 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
6280 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
6281 new_selections.push(Selection {
6282 id: selection.id,
6283 start: start_row,
6284 end: end_row,
6285 goal: SelectionGoal::None,
6286 reversed: selection.reversed,
6287 });
6288
6289 if lines_after > lines_before {
6290 added_lines += lines_after - lines_before;
6291 } else if lines_before > lines_after {
6292 removed_lines += lines_before - lines_after;
6293 }
6294 }
6295
6296 self.transact(cx, |this, cx| {
6297 let buffer = this.buffer.update(cx, |buffer, cx| {
6298 buffer.edit(edits, None, cx);
6299 buffer.snapshot(cx)
6300 });
6301
6302 // Recalculate offsets on newly edited buffer
6303 let new_selections = new_selections
6304 .iter()
6305 .map(|s| {
6306 let start_point = Point::new(s.start.0, 0);
6307 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
6308 Selection {
6309 id: s.id,
6310 start: buffer.point_to_offset(start_point),
6311 end: buffer.point_to_offset(end_point),
6312 goal: s.goal,
6313 reversed: s.reversed,
6314 }
6315 })
6316 .collect();
6317
6318 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6319 s.select(new_selections);
6320 });
6321
6322 this.request_autoscroll(Autoscroll::fit(), cx);
6323 });
6324 }
6325
6326 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
6327 self.manipulate_text(cx, |text| text.to_uppercase())
6328 }
6329
6330 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
6331 self.manipulate_text(cx, |text| text.to_lowercase())
6332 }
6333
6334 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
6335 self.manipulate_text(cx, |text| {
6336 text.split('\n')
6337 .map(|line| line.to_case(Case::Title))
6338 .join("\n")
6339 })
6340 }
6341
6342 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
6343 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
6344 }
6345
6346 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
6347 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
6348 }
6349
6350 pub fn convert_to_upper_camel_case(
6351 &mut self,
6352 _: &ConvertToUpperCamelCase,
6353 cx: &mut ViewContext<Self>,
6354 ) {
6355 self.manipulate_text(cx, |text| {
6356 text.split('\n')
6357 .map(|line| line.to_case(Case::UpperCamel))
6358 .join("\n")
6359 })
6360 }
6361
6362 pub fn convert_to_lower_camel_case(
6363 &mut self,
6364 _: &ConvertToLowerCamelCase,
6365 cx: &mut ViewContext<Self>,
6366 ) {
6367 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
6368 }
6369
6370 pub fn convert_to_opposite_case(
6371 &mut self,
6372 _: &ConvertToOppositeCase,
6373 cx: &mut ViewContext<Self>,
6374 ) {
6375 self.manipulate_text(cx, |text| {
6376 text.chars()
6377 .fold(String::with_capacity(text.len()), |mut t, c| {
6378 if c.is_uppercase() {
6379 t.extend(c.to_lowercase());
6380 } else {
6381 t.extend(c.to_uppercase());
6382 }
6383 t
6384 })
6385 })
6386 }
6387
6388 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6389 where
6390 Fn: FnMut(&str) -> String,
6391 {
6392 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6393 let buffer = self.buffer.read(cx).snapshot(cx);
6394
6395 let mut new_selections = Vec::new();
6396 let mut edits = Vec::new();
6397 let mut selection_adjustment = 0i32;
6398
6399 for selection in self.selections.all::<usize>(cx) {
6400 let selection_is_empty = selection.is_empty();
6401
6402 let (start, end) = if selection_is_empty {
6403 let word_range = movement::surrounding_word(
6404 &display_map,
6405 selection.start.to_display_point(&display_map),
6406 );
6407 let start = word_range.start.to_offset(&display_map, Bias::Left);
6408 let end = word_range.end.to_offset(&display_map, Bias::Left);
6409 (start, end)
6410 } else {
6411 (selection.start, selection.end)
6412 };
6413
6414 let text = buffer.text_for_range(start..end).collect::<String>();
6415 let old_length = text.len() as i32;
6416 let text = callback(&text);
6417
6418 new_selections.push(Selection {
6419 start: (start as i32 - selection_adjustment) as usize,
6420 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
6421 goal: SelectionGoal::None,
6422 ..selection
6423 });
6424
6425 selection_adjustment += old_length - text.len() as i32;
6426
6427 edits.push((start..end, text));
6428 }
6429
6430 self.transact(cx, |this, cx| {
6431 this.buffer.update(cx, |buffer, cx| {
6432 buffer.edit(edits, None, cx);
6433 });
6434
6435 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6436 s.select(new_selections);
6437 });
6438
6439 this.request_autoscroll(Autoscroll::fit(), cx);
6440 });
6441 }
6442
6443 pub fn duplicate(&mut self, upwards: bool, whole_lines: bool, cx: &mut ViewContext<Self>) {
6444 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6445 let buffer = &display_map.buffer_snapshot;
6446 let selections = self.selections.all::<Point>(cx);
6447
6448 let mut edits = Vec::new();
6449 let mut selections_iter = selections.iter().peekable();
6450 while let Some(selection) = selections_iter.next() {
6451 let mut rows = selection.spanned_rows(false, &display_map);
6452 // duplicate line-wise
6453 if whole_lines || selection.start == selection.end {
6454 // Avoid duplicating the same lines twice.
6455 while let Some(next_selection) = selections_iter.peek() {
6456 let next_rows = next_selection.spanned_rows(false, &display_map);
6457 if next_rows.start < rows.end {
6458 rows.end = next_rows.end;
6459 selections_iter.next().unwrap();
6460 } else {
6461 break;
6462 }
6463 }
6464
6465 // Copy the text from the selected row region and splice it either at the start
6466 // or end of the region.
6467 let start = Point::new(rows.start.0, 0);
6468 let end = Point::new(
6469 rows.end.previous_row().0,
6470 buffer.line_len(rows.end.previous_row()),
6471 );
6472 let text = buffer
6473 .text_for_range(start..end)
6474 .chain(Some("\n"))
6475 .collect::<String>();
6476 let insert_location = if upwards {
6477 Point::new(rows.end.0, 0)
6478 } else {
6479 start
6480 };
6481 edits.push((insert_location..insert_location, text));
6482 } else {
6483 // duplicate character-wise
6484 let start = selection.start;
6485 let end = selection.end;
6486 let text = buffer.text_for_range(start..end).collect::<String>();
6487 edits.push((selection.end..selection.end, text));
6488 }
6489 }
6490
6491 self.transact(cx, |this, cx| {
6492 this.buffer.update(cx, |buffer, cx| {
6493 buffer.edit(edits, None, cx);
6494 });
6495
6496 this.request_autoscroll(Autoscroll::fit(), cx);
6497 });
6498 }
6499
6500 pub fn duplicate_line_up(&mut self, _: &DuplicateLineUp, cx: &mut ViewContext<Self>) {
6501 self.duplicate(true, true, cx);
6502 }
6503
6504 pub fn duplicate_line_down(&mut self, _: &DuplicateLineDown, cx: &mut ViewContext<Self>) {
6505 self.duplicate(false, true, cx);
6506 }
6507
6508 pub fn duplicate_selection(&mut self, _: &DuplicateSelection, cx: &mut ViewContext<Self>) {
6509 self.duplicate(false, false, cx);
6510 }
6511
6512 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
6513 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6514 let buffer = self.buffer.read(cx).snapshot(cx);
6515
6516 let mut edits = Vec::new();
6517 let mut unfold_ranges = Vec::new();
6518 let mut refold_creases = Vec::new();
6519
6520 let selections = self.selections.all::<Point>(cx);
6521 let mut selections = selections.iter().peekable();
6522 let mut contiguous_row_selections = Vec::new();
6523 let mut new_selections = Vec::new();
6524
6525 while let Some(selection) = selections.next() {
6526 // Find all the selections that span a contiguous row range
6527 let (start_row, end_row) = consume_contiguous_rows(
6528 &mut contiguous_row_selections,
6529 selection,
6530 &display_map,
6531 &mut selections,
6532 );
6533
6534 // Move the text spanned by the row range to be before the line preceding the row range
6535 if start_row.0 > 0 {
6536 let range_to_move = Point::new(
6537 start_row.previous_row().0,
6538 buffer.line_len(start_row.previous_row()),
6539 )
6540 ..Point::new(
6541 end_row.previous_row().0,
6542 buffer.line_len(end_row.previous_row()),
6543 );
6544 let insertion_point = display_map
6545 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
6546 .0;
6547
6548 // Don't move lines across excerpts
6549 if buffer
6550 .excerpt_containing(insertion_point..range_to_move.end)
6551 .is_some()
6552 {
6553 let text = buffer
6554 .text_for_range(range_to_move.clone())
6555 .flat_map(|s| s.chars())
6556 .skip(1)
6557 .chain(['\n'])
6558 .collect::<String>();
6559
6560 edits.push((
6561 buffer.anchor_after(range_to_move.start)
6562 ..buffer.anchor_before(range_to_move.end),
6563 String::new(),
6564 ));
6565 let insertion_anchor = buffer.anchor_after(insertion_point);
6566 edits.push((insertion_anchor..insertion_anchor, text));
6567
6568 let row_delta = range_to_move.start.row - insertion_point.row + 1;
6569
6570 // Move selections up
6571 new_selections.extend(contiguous_row_selections.drain(..).map(
6572 |mut selection| {
6573 selection.start.row -= row_delta;
6574 selection.end.row -= row_delta;
6575 selection
6576 },
6577 ));
6578
6579 // Move folds up
6580 unfold_ranges.push(range_to_move.clone());
6581 for fold in display_map.folds_in_range(
6582 buffer.anchor_before(range_to_move.start)
6583 ..buffer.anchor_after(range_to_move.end),
6584 ) {
6585 let mut start = fold.range.start.to_point(&buffer);
6586 let mut end = fold.range.end.to_point(&buffer);
6587 start.row -= row_delta;
6588 end.row -= row_delta;
6589 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
6590 }
6591 }
6592 }
6593
6594 // If we didn't move line(s), preserve the existing selections
6595 new_selections.append(&mut contiguous_row_selections);
6596 }
6597
6598 self.transact(cx, |this, cx| {
6599 this.unfold_ranges(&unfold_ranges, true, true, cx);
6600 this.buffer.update(cx, |buffer, cx| {
6601 for (range, text) in edits {
6602 buffer.edit([(range, text)], None, cx);
6603 }
6604 });
6605 this.fold_creases(refold_creases, true, cx);
6606 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6607 s.select(new_selections);
6608 })
6609 });
6610 }
6611
6612 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
6613 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6614 let buffer = self.buffer.read(cx).snapshot(cx);
6615
6616 let mut edits = Vec::new();
6617 let mut unfold_ranges = Vec::new();
6618 let mut refold_creases = Vec::new();
6619
6620 let selections = self.selections.all::<Point>(cx);
6621 let mut selections = selections.iter().peekable();
6622 let mut contiguous_row_selections = Vec::new();
6623 let mut new_selections = Vec::new();
6624
6625 while let Some(selection) = selections.next() {
6626 // Find all the selections that span a contiguous row range
6627 let (start_row, end_row) = consume_contiguous_rows(
6628 &mut contiguous_row_selections,
6629 selection,
6630 &display_map,
6631 &mut selections,
6632 );
6633
6634 // Move the text spanned by the row range to be after the last line of the row range
6635 if end_row.0 <= buffer.max_point().row {
6636 let range_to_move =
6637 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
6638 let insertion_point = display_map
6639 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
6640 .0;
6641
6642 // Don't move lines across excerpt boundaries
6643 if buffer
6644 .excerpt_containing(range_to_move.start..insertion_point)
6645 .is_some()
6646 {
6647 let mut text = String::from("\n");
6648 text.extend(buffer.text_for_range(range_to_move.clone()));
6649 text.pop(); // Drop trailing newline
6650 edits.push((
6651 buffer.anchor_after(range_to_move.start)
6652 ..buffer.anchor_before(range_to_move.end),
6653 String::new(),
6654 ));
6655 let insertion_anchor = buffer.anchor_after(insertion_point);
6656 edits.push((insertion_anchor..insertion_anchor, text));
6657
6658 let row_delta = insertion_point.row - range_to_move.end.row + 1;
6659
6660 // Move selections down
6661 new_selections.extend(contiguous_row_selections.drain(..).map(
6662 |mut selection| {
6663 selection.start.row += row_delta;
6664 selection.end.row += row_delta;
6665 selection
6666 },
6667 ));
6668
6669 // Move folds down
6670 unfold_ranges.push(range_to_move.clone());
6671 for fold in display_map.folds_in_range(
6672 buffer.anchor_before(range_to_move.start)
6673 ..buffer.anchor_after(range_to_move.end),
6674 ) {
6675 let mut start = fold.range.start.to_point(&buffer);
6676 let mut end = fold.range.end.to_point(&buffer);
6677 start.row += row_delta;
6678 end.row += row_delta;
6679 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
6680 }
6681 }
6682 }
6683
6684 // If we didn't move line(s), preserve the existing selections
6685 new_selections.append(&mut contiguous_row_selections);
6686 }
6687
6688 self.transact(cx, |this, cx| {
6689 this.unfold_ranges(&unfold_ranges, true, true, cx);
6690 this.buffer.update(cx, |buffer, cx| {
6691 for (range, text) in edits {
6692 buffer.edit([(range, text)], None, cx);
6693 }
6694 });
6695 this.fold_creases(refold_creases, true, cx);
6696 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
6697 });
6698 }
6699
6700 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
6701 let text_layout_details = &self.text_layout_details(cx);
6702 self.transact(cx, |this, cx| {
6703 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6704 let mut edits: Vec<(Range<usize>, String)> = Default::default();
6705 let line_mode = s.line_mode;
6706 s.move_with(|display_map, selection| {
6707 if !selection.is_empty() || line_mode {
6708 return;
6709 }
6710
6711 let mut head = selection.head();
6712 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
6713 if head.column() == display_map.line_len(head.row()) {
6714 transpose_offset = display_map
6715 .buffer_snapshot
6716 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6717 }
6718
6719 if transpose_offset == 0 {
6720 return;
6721 }
6722
6723 *head.column_mut() += 1;
6724 head = display_map.clip_point(head, Bias::Right);
6725 let goal = SelectionGoal::HorizontalPosition(
6726 display_map
6727 .x_for_display_point(head, text_layout_details)
6728 .into(),
6729 );
6730 selection.collapse_to(head, goal);
6731
6732 let transpose_start = display_map
6733 .buffer_snapshot
6734 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6735 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
6736 let transpose_end = display_map
6737 .buffer_snapshot
6738 .clip_offset(transpose_offset + 1, Bias::Right);
6739 if let Some(ch) =
6740 display_map.buffer_snapshot.chars_at(transpose_start).next()
6741 {
6742 edits.push((transpose_start..transpose_offset, String::new()));
6743 edits.push((transpose_end..transpose_end, ch.to_string()));
6744 }
6745 }
6746 });
6747 edits
6748 });
6749 this.buffer
6750 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
6751 let selections = this.selections.all::<usize>(cx);
6752 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6753 s.select(selections);
6754 });
6755 });
6756 }
6757
6758 pub fn rewrap(&mut self, _: &Rewrap, cx: &mut ViewContext<Self>) {
6759 self.rewrap_impl(IsVimMode::No, cx)
6760 }
6761
6762 pub fn rewrap_impl(&mut self, is_vim_mode: IsVimMode, cx: &mut ViewContext<Self>) {
6763 let buffer = self.buffer.read(cx).snapshot(cx);
6764 let selections = self.selections.all::<Point>(cx);
6765 let mut selections = selections.iter().peekable();
6766
6767 let mut edits = Vec::new();
6768 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
6769
6770 while let Some(selection) = selections.next() {
6771 let mut start_row = selection.start.row;
6772 let mut end_row = selection.end.row;
6773
6774 // Skip selections that overlap with a range that has already been rewrapped.
6775 let selection_range = start_row..end_row;
6776 if rewrapped_row_ranges
6777 .iter()
6778 .any(|range| range.overlaps(&selection_range))
6779 {
6780 continue;
6781 }
6782
6783 let mut should_rewrap = is_vim_mode == IsVimMode::Yes;
6784
6785 if let Some(language_scope) = buffer.language_scope_at(selection.head()) {
6786 match language_scope.language_name().0.as_ref() {
6787 "Markdown" | "Plain Text" => {
6788 should_rewrap = true;
6789 }
6790 _ => {}
6791 }
6792 }
6793
6794 let tab_size = buffer.settings_at(selection.head(), cx).tab_size;
6795
6796 // Since not all lines in the selection may be at the same indent
6797 // level, choose the indent size that is the most common between all
6798 // of the lines.
6799 //
6800 // If there is a tie, we use the deepest indent.
6801 let (indent_size, indent_end) = {
6802 let mut indent_size_occurrences = HashMap::default();
6803 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
6804
6805 for row in start_row..=end_row {
6806 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
6807 rows_by_indent_size.entry(indent).or_default().push(row);
6808 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
6809 }
6810
6811 let indent_size = indent_size_occurrences
6812 .into_iter()
6813 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
6814 .map(|(indent, _)| indent)
6815 .unwrap_or_default();
6816 let row = rows_by_indent_size[&indent_size][0];
6817 let indent_end = Point::new(row, indent_size.len);
6818
6819 (indent_size, indent_end)
6820 };
6821
6822 let mut line_prefix = indent_size.chars().collect::<String>();
6823
6824 if let Some(comment_prefix) =
6825 buffer
6826 .language_scope_at(selection.head())
6827 .and_then(|language| {
6828 language
6829 .line_comment_prefixes()
6830 .iter()
6831 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
6832 .cloned()
6833 })
6834 {
6835 line_prefix.push_str(&comment_prefix);
6836 should_rewrap = true;
6837 }
6838
6839 if !should_rewrap {
6840 continue;
6841 }
6842
6843 if selection.is_empty() {
6844 'expand_upwards: while start_row > 0 {
6845 let prev_row = start_row - 1;
6846 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
6847 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
6848 {
6849 start_row = prev_row;
6850 } else {
6851 break 'expand_upwards;
6852 }
6853 }
6854
6855 'expand_downwards: while end_row < buffer.max_point().row {
6856 let next_row = end_row + 1;
6857 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
6858 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
6859 {
6860 end_row = next_row;
6861 } else {
6862 break 'expand_downwards;
6863 }
6864 }
6865 }
6866
6867 let start = Point::new(start_row, 0);
6868 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
6869 let selection_text = buffer.text_for_range(start..end).collect::<String>();
6870 let Some(lines_without_prefixes) = selection_text
6871 .lines()
6872 .map(|line| {
6873 line.strip_prefix(&line_prefix)
6874 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
6875 .ok_or_else(|| {
6876 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
6877 })
6878 })
6879 .collect::<Result<Vec<_>, _>>()
6880 .log_err()
6881 else {
6882 continue;
6883 };
6884
6885 let wrap_column = buffer
6886 .settings_at(Point::new(start_row, 0), cx)
6887 .preferred_line_length as usize;
6888 let wrapped_text = wrap_with_prefix(
6889 line_prefix,
6890 lines_without_prefixes.join(" "),
6891 wrap_column,
6892 tab_size,
6893 );
6894
6895 // TODO: should always use char-based diff while still supporting cursor behavior that
6896 // matches vim.
6897 let diff = match is_vim_mode {
6898 IsVimMode::Yes => TextDiff::from_lines(&selection_text, &wrapped_text),
6899 IsVimMode::No => TextDiff::from_chars(&selection_text, &wrapped_text),
6900 };
6901 let mut offset = start.to_offset(&buffer);
6902 let mut moved_since_edit = true;
6903
6904 for change in diff.iter_all_changes() {
6905 let value = change.value();
6906 match change.tag() {
6907 ChangeTag::Equal => {
6908 offset += value.len();
6909 moved_since_edit = true;
6910 }
6911 ChangeTag::Delete => {
6912 let start = buffer.anchor_after(offset);
6913 let end = buffer.anchor_before(offset + value.len());
6914
6915 if moved_since_edit {
6916 edits.push((start..end, String::new()));
6917 } else {
6918 edits.last_mut().unwrap().0.end = end;
6919 }
6920
6921 offset += value.len();
6922 moved_since_edit = false;
6923 }
6924 ChangeTag::Insert => {
6925 if moved_since_edit {
6926 let anchor = buffer.anchor_after(offset);
6927 edits.push((anchor..anchor, value.to_string()));
6928 } else {
6929 edits.last_mut().unwrap().1.push_str(value);
6930 }
6931
6932 moved_since_edit = false;
6933 }
6934 }
6935 }
6936
6937 rewrapped_row_ranges.push(start_row..=end_row);
6938 }
6939
6940 self.buffer
6941 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
6942 }
6943
6944 pub fn cut_common(&mut self, cx: &mut ViewContext<Self>) -> ClipboardItem {
6945 let mut text = String::new();
6946 let buffer = self.buffer.read(cx).snapshot(cx);
6947 let mut selections = self.selections.all::<Point>(cx);
6948 let mut clipboard_selections = Vec::with_capacity(selections.len());
6949 {
6950 let max_point = buffer.max_point();
6951 let mut is_first = true;
6952 for selection in &mut selections {
6953 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6954 if is_entire_line {
6955 selection.start = Point::new(selection.start.row, 0);
6956 if !selection.is_empty() && selection.end.column == 0 {
6957 selection.end = cmp::min(max_point, selection.end);
6958 } else {
6959 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
6960 }
6961 selection.goal = SelectionGoal::None;
6962 }
6963 if is_first {
6964 is_first = false;
6965 } else {
6966 text += "\n";
6967 }
6968 let mut len = 0;
6969 for chunk in buffer.text_for_range(selection.start..selection.end) {
6970 text.push_str(chunk);
6971 len += chunk.len();
6972 }
6973 clipboard_selections.push(ClipboardSelection {
6974 len,
6975 is_entire_line,
6976 first_line_indent: buffer
6977 .indent_size_for_line(MultiBufferRow(selection.start.row))
6978 .len,
6979 });
6980 }
6981 }
6982
6983 self.transact(cx, |this, cx| {
6984 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6985 s.select(selections);
6986 });
6987 this.insert("", cx);
6988 });
6989 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
6990 }
6991
6992 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
6993 let item = self.cut_common(cx);
6994 cx.write_to_clipboard(item);
6995 }
6996
6997 pub fn kill_ring_cut(&mut self, _: &KillRingCut, cx: &mut ViewContext<Self>) {
6998 self.change_selections(None, cx, |s| {
6999 s.move_with(|snapshot, sel| {
7000 if sel.is_empty() {
7001 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
7002 }
7003 });
7004 });
7005 let item = self.cut_common(cx);
7006 cx.set_global(KillRing(item))
7007 }
7008
7009 pub fn kill_ring_yank(&mut self, _: &KillRingYank, cx: &mut ViewContext<Self>) {
7010 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
7011 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
7012 (kill_ring.text().to_string(), kill_ring.metadata_json())
7013 } else {
7014 return;
7015 }
7016 } else {
7017 return;
7018 };
7019 self.do_paste(&text, metadata, false, cx);
7020 }
7021
7022 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
7023 let selections = self.selections.all::<Point>(cx);
7024 let buffer = self.buffer.read(cx).read(cx);
7025 let mut text = String::new();
7026
7027 let mut clipboard_selections = Vec::with_capacity(selections.len());
7028 {
7029 let max_point = buffer.max_point();
7030 let mut is_first = true;
7031 for selection in selections.iter() {
7032 let mut start = selection.start;
7033 let mut end = selection.end;
7034 let is_entire_line = selection.is_empty() || self.selections.line_mode;
7035 if is_entire_line {
7036 start = Point::new(start.row, 0);
7037 end = cmp::min(max_point, Point::new(end.row + 1, 0));
7038 }
7039 if is_first {
7040 is_first = false;
7041 } else {
7042 text += "\n";
7043 }
7044 let mut len = 0;
7045 for chunk in buffer.text_for_range(start..end) {
7046 text.push_str(chunk);
7047 len += chunk.len();
7048 }
7049 clipboard_selections.push(ClipboardSelection {
7050 len,
7051 is_entire_line,
7052 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
7053 });
7054 }
7055 }
7056
7057 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
7058 text,
7059 clipboard_selections,
7060 ));
7061 }
7062
7063 pub fn do_paste(
7064 &mut self,
7065 text: &String,
7066 clipboard_selections: Option<Vec<ClipboardSelection>>,
7067 handle_entire_lines: bool,
7068 cx: &mut ViewContext<Self>,
7069 ) {
7070 if self.read_only(cx) {
7071 return;
7072 }
7073
7074 let clipboard_text = Cow::Borrowed(text);
7075
7076 self.transact(cx, |this, cx| {
7077 if let Some(mut clipboard_selections) = clipboard_selections {
7078 let old_selections = this.selections.all::<usize>(cx);
7079 let all_selections_were_entire_line =
7080 clipboard_selections.iter().all(|s| s.is_entire_line);
7081 let first_selection_indent_column =
7082 clipboard_selections.first().map(|s| s.first_line_indent);
7083 if clipboard_selections.len() != old_selections.len() {
7084 clipboard_selections.drain(..);
7085 }
7086 let cursor_offset = this.selections.last::<usize>(cx).head();
7087 let mut auto_indent_on_paste = true;
7088
7089 this.buffer.update(cx, |buffer, cx| {
7090 let snapshot = buffer.read(cx);
7091 auto_indent_on_paste =
7092 snapshot.settings_at(cursor_offset, cx).auto_indent_on_paste;
7093
7094 let mut start_offset = 0;
7095 let mut edits = Vec::new();
7096 let mut original_indent_columns = Vec::new();
7097 for (ix, selection) in old_selections.iter().enumerate() {
7098 let to_insert;
7099 let entire_line;
7100 let original_indent_column;
7101 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
7102 let end_offset = start_offset + clipboard_selection.len;
7103 to_insert = &clipboard_text[start_offset..end_offset];
7104 entire_line = clipboard_selection.is_entire_line;
7105 start_offset = end_offset + 1;
7106 original_indent_column = Some(clipboard_selection.first_line_indent);
7107 } else {
7108 to_insert = clipboard_text.as_str();
7109 entire_line = all_selections_were_entire_line;
7110 original_indent_column = first_selection_indent_column
7111 }
7112
7113 // If the corresponding selection was empty when this slice of the
7114 // clipboard text was written, then the entire line containing the
7115 // selection was copied. If this selection is also currently empty,
7116 // then paste the line before the current line of the buffer.
7117 let range = if selection.is_empty() && handle_entire_lines && entire_line {
7118 let column = selection.start.to_point(&snapshot).column as usize;
7119 let line_start = selection.start - column;
7120 line_start..line_start
7121 } else {
7122 selection.range()
7123 };
7124
7125 edits.push((range, to_insert));
7126 original_indent_columns.extend(original_indent_column);
7127 }
7128 drop(snapshot);
7129
7130 buffer.edit(
7131 edits,
7132 if auto_indent_on_paste {
7133 Some(AutoindentMode::Block {
7134 original_indent_columns,
7135 })
7136 } else {
7137 None
7138 },
7139 cx,
7140 );
7141 });
7142
7143 let selections = this.selections.all::<usize>(cx);
7144 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
7145 } else {
7146 this.insert(&clipboard_text, cx);
7147 }
7148 });
7149 }
7150
7151 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
7152 if let Some(item) = cx.read_from_clipboard() {
7153 let entries = item.entries();
7154
7155 match entries.first() {
7156 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
7157 // of all the pasted entries.
7158 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
7159 .do_paste(
7160 clipboard_string.text(),
7161 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
7162 true,
7163 cx,
7164 ),
7165 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, cx),
7166 }
7167 }
7168 }
7169
7170 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
7171 if self.read_only(cx) {
7172 return;
7173 }
7174
7175 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
7176 if let Some((selections, _)) =
7177 self.selection_history.transaction(transaction_id).cloned()
7178 {
7179 self.change_selections(None, cx, |s| {
7180 s.select_anchors(selections.to_vec());
7181 });
7182 }
7183 self.request_autoscroll(Autoscroll::fit(), cx);
7184 self.unmark_text(cx);
7185 self.refresh_inline_completion(true, false, cx);
7186 cx.emit(EditorEvent::Edited { transaction_id });
7187 cx.emit(EditorEvent::TransactionUndone { transaction_id });
7188 }
7189 }
7190
7191 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
7192 if self.read_only(cx) {
7193 return;
7194 }
7195
7196 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
7197 if let Some((_, Some(selections))) =
7198 self.selection_history.transaction(transaction_id).cloned()
7199 {
7200 self.change_selections(None, cx, |s| {
7201 s.select_anchors(selections.to_vec());
7202 });
7203 }
7204 self.request_autoscroll(Autoscroll::fit(), cx);
7205 self.unmark_text(cx);
7206 self.refresh_inline_completion(true, false, cx);
7207 cx.emit(EditorEvent::Edited { transaction_id });
7208 }
7209 }
7210
7211 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
7212 self.buffer
7213 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
7214 }
7215
7216 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut ViewContext<Self>) {
7217 self.buffer
7218 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
7219 }
7220
7221 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
7222 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7223 let line_mode = s.line_mode;
7224 s.move_with(|map, selection| {
7225 let cursor = if selection.is_empty() && !line_mode {
7226 movement::left(map, selection.start)
7227 } else {
7228 selection.start
7229 };
7230 selection.collapse_to(cursor, SelectionGoal::None);
7231 });
7232 })
7233 }
7234
7235 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
7236 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7237 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
7238 })
7239 }
7240
7241 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
7242 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7243 let line_mode = s.line_mode;
7244 s.move_with(|map, selection| {
7245 let cursor = if selection.is_empty() && !line_mode {
7246 movement::right(map, selection.end)
7247 } else {
7248 selection.end
7249 };
7250 selection.collapse_to(cursor, SelectionGoal::None)
7251 });
7252 })
7253 }
7254
7255 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
7256 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7257 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
7258 })
7259 }
7260
7261 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
7262 if self.take_rename(true, cx).is_some() {
7263 return;
7264 }
7265
7266 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7267 cx.propagate();
7268 return;
7269 }
7270
7271 let text_layout_details = &self.text_layout_details(cx);
7272 let selection_count = self.selections.count();
7273 let first_selection = self.selections.first_anchor();
7274
7275 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7276 let line_mode = s.line_mode;
7277 s.move_with(|map, selection| {
7278 if !selection.is_empty() && !line_mode {
7279 selection.goal = SelectionGoal::None;
7280 }
7281 let (cursor, goal) = movement::up(
7282 map,
7283 selection.start,
7284 selection.goal,
7285 false,
7286 text_layout_details,
7287 );
7288 selection.collapse_to(cursor, goal);
7289 });
7290 });
7291
7292 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7293 {
7294 cx.propagate();
7295 }
7296 }
7297
7298 pub fn move_up_by_lines(&mut self, action: &MoveUpByLines, cx: &mut ViewContext<Self>) {
7299 if self.take_rename(true, cx).is_some() {
7300 return;
7301 }
7302
7303 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7304 cx.propagate();
7305 return;
7306 }
7307
7308 let text_layout_details = &self.text_layout_details(cx);
7309
7310 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7311 let line_mode = s.line_mode;
7312 s.move_with(|map, selection| {
7313 if !selection.is_empty() && !line_mode {
7314 selection.goal = SelectionGoal::None;
7315 }
7316 let (cursor, goal) = movement::up_by_rows(
7317 map,
7318 selection.start,
7319 action.lines,
7320 selection.goal,
7321 false,
7322 text_layout_details,
7323 );
7324 selection.collapse_to(cursor, goal);
7325 });
7326 })
7327 }
7328
7329 pub fn move_down_by_lines(&mut self, action: &MoveDownByLines, cx: &mut ViewContext<Self>) {
7330 if self.take_rename(true, cx).is_some() {
7331 return;
7332 }
7333
7334 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7335 cx.propagate();
7336 return;
7337 }
7338
7339 let text_layout_details = &self.text_layout_details(cx);
7340
7341 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7342 let line_mode = s.line_mode;
7343 s.move_with(|map, selection| {
7344 if !selection.is_empty() && !line_mode {
7345 selection.goal = SelectionGoal::None;
7346 }
7347 let (cursor, goal) = movement::down_by_rows(
7348 map,
7349 selection.start,
7350 action.lines,
7351 selection.goal,
7352 false,
7353 text_layout_details,
7354 );
7355 selection.collapse_to(cursor, goal);
7356 });
7357 })
7358 }
7359
7360 pub fn select_down_by_lines(&mut self, action: &SelectDownByLines, cx: &mut ViewContext<Self>) {
7361 let text_layout_details = &self.text_layout_details(cx);
7362 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7363 s.move_heads_with(|map, head, goal| {
7364 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
7365 })
7366 })
7367 }
7368
7369 pub fn select_up_by_lines(&mut self, action: &SelectUpByLines, cx: &mut ViewContext<Self>) {
7370 let text_layout_details = &self.text_layout_details(cx);
7371 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7372 s.move_heads_with(|map, head, goal| {
7373 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
7374 })
7375 })
7376 }
7377
7378 pub fn select_page_up(&mut self, _: &SelectPageUp, cx: &mut ViewContext<Self>) {
7379 let Some(row_count) = self.visible_row_count() else {
7380 return;
7381 };
7382
7383 let text_layout_details = &self.text_layout_details(cx);
7384
7385 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7386 s.move_heads_with(|map, head, goal| {
7387 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
7388 })
7389 })
7390 }
7391
7392 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
7393 if self.take_rename(true, cx).is_some() {
7394 return;
7395 }
7396
7397 if self
7398 .context_menu
7399 .borrow_mut()
7400 .as_mut()
7401 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
7402 .unwrap_or(false)
7403 {
7404 return;
7405 }
7406
7407 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7408 cx.propagate();
7409 return;
7410 }
7411
7412 let Some(row_count) = self.visible_row_count() else {
7413 return;
7414 };
7415
7416 let autoscroll = if action.center_cursor {
7417 Autoscroll::center()
7418 } else {
7419 Autoscroll::fit()
7420 };
7421
7422 let text_layout_details = &self.text_layout_details(cx);
7423
7424 self.change_selections(Some(autoscroll), cx, |s| {
7425 let line_mode = s.line_mode;
7426 s.move_with(|map, selection| {
7427 if !selection.is_empty() && !line_mode {
7428 selection.goal = SelectionGoal::None;
7429 }
7430 let (cursor, goal) = movement::up_by_rows(
7431 map,
7432 selection.end,
7433 row_count,
7434 selection.goal,
7435 false,
7436 text_layout_details,
7437 );
7438 selection.collapse_to(cursor, goal);
7439 });
7440 });
7441 }
7442
7443 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
7444 let text_layout_details = &self.text_layout_details(cx);
7445 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7446 s.move_heads_with(|map, head, goal| {
7447 movement::up(map, head, goal, false, text_layout_details)
7448 })
7449 })
7450 }
7451
7452 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
7453 self.take_rename(true, cx);
7454
7455 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7456 cx.propagate();
7457 return;
7458 }
7459
7460 let text_layout_details = &self.text_layout_details(cx);
7461 let selection_count = self.selections.count();
7462 let first_selection = self.selections.first_anchor();
7463
7464 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7465 let line_mode = s.line_mode;
7466 s.move_with(|map, selection| {
7467 if !selection.is_empty() && !line_mode {
7468 selection.goal = SelectionGoal::None;
7469 }
7470 let (cursor, goal) = movement::down(
7471 map,
7472 selection.end,
7473 selection.goal,
7474 false,
7475 text_layout_details,
7476 );
7477 selection.collapse_to(cursor, goal);
7478 });
7479 });
7480
7481 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7482 {
7483 cx.propagate();
7484 }
7485 }
7486
7487 pub fn select_page_down(&mut self, _: &SelectPageDown, cx: &mut ViewContext<Self>) {
7488 let Some(row_count) = self.visible_row_count() else {
7489 return;
7490 };
7491
7492 let text_layout_details = &self.text_layout_details(cx);
7493
7494 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7495 s.move_heads_with(|map, head, goal| {
7496 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
7497 })
7498 })
7499 }
7500
7501 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
7502 if self.take_rename(true, cx).is_some() {
7503 return;
7504 }
7505
7506 if self
7507 .context_menu
7508 .borrow_mut()
7509 .as_mut()
7510 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
7511 .unwrap_or(false)
7512 {
7513 return;
7514 }
7515
7516 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7517 cx.propagate();
7518 return;
7519 }
7520
7521 let Some(row_count) = self.visible_row_count() else {
7522 return;
7523 };
7524
7525 let autoscroll = if action.center_cursor {
7526 Autoscroll::center()
7527 } else {
7528 Autoscroll::fit()
7529 };
7530
7531 let text_layout_details = &self.text_layout_details(cx);
7532 self.change_selections(Some(autoscroll), cx, |s| {
7533 let line_mode = s.line_mode;
7534 s.move_with(|map, selection| {
7535 if !selection.is_empty() && !line_mode {
7536 selection.goal = SelectionGoal::None;
7537 }
7538 let (cursor, goal) = movement::down_by_rows(
7539 map,
7540 selection.end,
7541 row_count,
7542 selection.goal,
7543 false,
7544 text_layout_details,
7545 );
7546 selection.collapse_to(cursor, goal);
7547 });
7548 });
7549 }
7550
7551 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
7552 let text_layout_details = &self.text_layout_details(cx);
7553 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7554 s.move_heads_with(|map, head, goal| {
7555 movement::down(map, head, goal, false, text_layout_details)
7556 })
7557 });
7558 }
7559
7560 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
7561 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
7562 context_menu.select_first(self.completion_provider.as_deref(), cx);
7563 }
7564 }
7565
7566 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
7567 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
7568 context_menu.select_prev(self.completion_provider.as_deref(), cx);
7569 }
7570 }
7571
7572 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
7573 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
7574 context_menu.select_next(self.completion_provider.as_deref(), cx);
7575 }
7576 }
7577
7578 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
7579 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
7580 context_menu.select_last(self.completion_provider.as_deref(), cx);
7581 }
7582 }
7583
7584 pub fn move_to_previous_word_start(
7585 &mut self,
7586 _: &MoveToPreviousWordStart,
7587 cx: &mut ViewContext<Self>,
7588 ) {
7589 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7590 s.move_cursors_with(|map, head, _| {
7591 (
7592 movement::previous_word_start(map, head),
7593 SelectionGoal::None,
7594 )
7595 });
7596 })
7597 }
7598
7599 pub fn move_to_previous_subword_start(
7600 &mut self,
7601 _: &MoveToPreviousSubwordStart,
7602 cx: &mut ViewContext<Self>,
7603 ) {
7604 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7605 s.move_cursors_with(|map, head, _| {
7606 (
7607 movement::previous_subword_start(map, head),
7608 SelectionGoal::None,
7609 )
7610 });
7611 })
7612 }
7613
7614 pub fn select_to_previous_word_start(
7615 &mut self,
7616 _: &SelectToPreviousWordStart,
7617 cx: &mut ViewContext<Self>,
7618 ) {
7619 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7620 s.move_heads_with(|map, head, _| {
7621 (
7622 movement::previous_word_start(map, head),
7623 SelectionGoal::None,
7624 )
7625 });
7626 })
7627 }
7628
7629 pub fn select_to_previous_subword_start(
7630 &mut self,
7631 _: &SelectToPreviousSubwordStart,
7632 cx: &mut ViewContext<Self>,
7633 ) {
7634 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7635 s.move_heads_with(|map, head, _| {
7636 (
7637 movement::previous_subword_start(map, head),
7638 SelectionGoal::None,
7639 )
7640 });
7641 })
7642 }
7643
7644 pub fn delete_to_previous_word_start(
7645 &mut self,
7646 action: &DeleteToPreviousWordStart,
7647 cx: &mut ViewContext<Self>,
7648 ) {
7649 self.transact(cx, |this, cx| {
7650 this.select_autoclose_pair(cx);
7651 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7652 let line_mode = s.line_mode;
7653 s.move_with(|map, selection| {
7654 if selection.is_empty() && !line_mode {
7655 let cursor = if action.ignore_newlines {
7656 movement::previous_word_start(map, selection.head())
7657 } else {
7658 movement::previous_word_start_or_newline(map, selection.head())
7659 };
7660 selection.set_head(cursor, SelectionGoal::None);
7661 }
7662 });
7663 });
7664 this.insert("", cx);
7665 });
7666 }
7667
7668 pub fn delete_to_previous_subword_start(
7669 &mut self,
7670 _: &DeleteToPreviousSubwordStart,
7671 cx: &mut ViewContext<Self>,
7672 ) {
7673 self.transact(cx, |this, cx| {
7674 this.select_autoclose_pair(cx);
7675 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7676 let line_mode = s.line_mode;
7677 s.move_with(|map, selection| {
7678 if selection.is_empty() && !line_mode {
7679 let cursor = movement::previous_subword_start(map, selection.head());
7680 selection.set_head(cursor, SelectionGoal::None);
7681 }
7682 });
7683 });
7684 this.insert("", cx);
7685 });
7686 }
7687
7688 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
7689 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7690 s.move_cursors_with(|map, head, _| {
7691 (movement::next_word_end(map, head), SelectionGoal::None)
7692 });
7693 })
7694 }
7695
7696 pub fn move_to_next_subword_end(
7697 &mut self,
7698 _: &MoveToNextSubwordEnd,
7699 cx: &mut ViewContext<Self>,
7700 ) {
7701 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7702 s.move_cursors_with(|map, head, _| {
7703 (movement::next_subword_end(map, head), SelectionGoal::None)
7704 });
7705 })
7706 }
7707
7708 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
7709 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7710 s.move_heads_with(|map, head, _| {
7711 (movement::next_word_end(map, head), SelectionGoal::None)
7712 });
7713 })
7714 }
7715
7716 pub fn select_to_next_subword_end(
7717 &mut self,
7718 _: &SelectToNextSubwordEnd,
7719 cx: &mut ViewContext<Self>,
7720 ) {
7721 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7722 s.move_heads_with(|map, head, _| {
7723 (movement::next_subword_end(map, head), SelectionGoal::None)
7724 });
7725 })
7726 }
7727
7728 pub fn delete_to_next_word_end(
7729 &mut self,
7730 action: &DeleteToNextWordEnd,
7731 cx: &mut ViewContext<Self>,
7732 ) {
7733 self.transact(cx, |this, cx| {
7734 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7735 let line_mode = s.line_mode;
7736 s.move_with(|map, selection| {
7737 if selection.is_empty() && !line_mode {
7738 let cursor = if action.ignore_newlines {
7739 movement::next_word_end(map, selection.head())
7740 } else {
7741 movement::next_word_end_or_newline(map, selection.head())
7742 };
7743 selection.set_head(cursor, SelectionGoal::None);
7744 }
7745 });
7746 });
7747 this.insert("", cx);
7748 });
7749 }
7750
7751 pub fn delete_to_next_subword_end(
7752 &mut self,
7753 _: &DeleteToNextSubwordEnd,
7754 cx: &mut ViewContext<Self>,
7755 ) {
7756 self.transact(cx, |this, cx| {
7757 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7758 s.move_with(|map, selection| {
7759 if selection.is_empty() {
7760 let cursor = movement::next_subword_end(map, selection.head());
7761 selection.set_head(cursor, SelectionGoal::None);
7762 }
7763 });
7764 });
7765 this.insert("", cx);
7766 });
7767 }
7768
7769 pub fn move_to_beginning_of_line(
7770 &mut self,
7771 action: &MoveToBeginningOfLine,
7772 cx: &mut ViewContext<Self>,
7773 ) {
7774 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7775 s.move_cursors_with(|map, head, _| {
7776 (
7777 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7778 SelectionGoal::None,
7779 )
7780 });
7781 })
7782 }
7783
7784 pub fn select_to_beginning_of_line(
7785 &mut self,
7786 action: &SelectToBeginningOfLine,
7787 cx: &mut ViewContext<Self>,
7788 ) {
7789 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7790 s.move_heads_with(|map, head, _| {
7791 (
7792 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7793 SelectionGoal::None,
7794 )
7795 });
7796 });
7797 }
7798
7799 pub fn delete_to_beginning_of_line(
7800 &mut self,
7801 _: &DeleteToBeginningOfLine,
7802 cx: &mut ViewContext<Self>,
7803 ) {
7804 self.transact(cx, |this, cx| {
7805 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7806 s.move_with(|_, selection| {
7807 selection.reversed = true;
7808 });
7809 });
7810
7811 this.select_to_beginning_of_line(
7812 &SelectToBeginningOfLine {
7813 stop_at_soft_wraps: false,
7814 },
7815 cx,
7816 );
7817 this.backspace(&Backspace, cx);
7818 });
7819 }
7820
7821 pub fn move_to_end_of_line(&mut self, action: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
7822 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7823 s.move_cursors_with(|map, head, _| {
7824 (
7825 movement::line_end(map, head, action.stop_at_soft_wraps),
7826 SelectionGoal::None,
7827 )
7828 });
7829 })
7830 }
7831
7832 pub fn select_to_end_of_line(
7833 &mut self,
7834 action: &SelectToEndOfLine,
7835 cx: &mut ViewContext<Self>,
7836 ) {
7837 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7838 s.move_heads_with(|map, head, _| {
7839 (
7840 movement::line_end(map, head, action.stop_at_soft_wraps),
7841 SelectionGoal::None,
7842 )
7843 });
7844 })
7845 }
7846
7847 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
7848 self.transact(cx, |this, cx| {
7849 this.select_to_end_of_line(
7850 &SelectToEndOfLine {
7851 stop_at_soft_wraps: false,
7852 },
7853 cx,
7854 );
7855 this.delete(&Delete, cx);
7856 });
7857 }
7858
7859 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
7860 self.transact(cx, |this, cx| {
7861 this.select_to_end_of_line(
7862 &SelectToEndOfLine {
7863 stop_at_soft_wraps: false,
7864 },
7865 cx,
7866 );
7867 this.cut(&Cut, cx);
7868 });
7869 }
7870
7871 pub fn move_to_start_of_paragraph(
7872 &mut self,
7873 _: &MoveToStartOfParagraph,
7874 cx: &mut ViewContext<Self>,
7875 ) {
7876 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7877 cx.propagate();
7878 return;
7879 }
7880
7881 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7882 s.move_with(|map, selection| {
7883 selection.collapse_to(
7884 movement::start_of_paragraph(map, selection.head(), 1),
7885 SelectionGoal::None,
7886 )
7887 });
7888 })
7889 }
7890
7891 pub fn move_to_end_of_paragraph(
7892 &mut self,
7893 _: &MoveToEndOfParagraph,
7894 cx: &mut ViewContext<Self>,
7895 ) {
7896 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7897 cx.propagate();
7898 return;
7899 }
7900
7901 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7902 s.move_with(|map, selection| {
7903 selection.collapse_to(
7904 movement::end_of_paragraph(map, selection.head(), 1),
7905 SelectionGoal::None,
7906 )
7907 });
7908 })
7909 }
7910
7911 pub fn select_to_start_of_paragraph(
7912 &mut self,
7913 _: &SelectToStartOfParagraph,
7914 cx: &mut ViewContext<Self>,
7915 ) {
7916 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7917 cx.propagate();
7918 return;
7919 }
7920
7921 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7922 s.move_heads_with(|map, head, _| {
7923 (
7924 movement::start_of_paragraph(map, head, 1),
7925 SelectionGoal::None,
7926 )
7927 });
7928 })
7929 }
7930
7931 pub fn select_to_end_of_paragraph(
7932 &mut self,
7933 _: &SelectToEndOfParagraph,
7934 cx: &mut ViewContext<Self>,
7935 ) {
7936 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7937 cx.propagate();
7938 return;
7939 }
7940
7941 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7942 s.move_heads_with(|map, head, _| {
7943 (
7944 movement::end_of_paragraph(map, head, 1),
7945 SelectionGoal::None,
7946 )
7947 });
7948 })
7949 }
7950
7951 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
7952 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7953 cx.propagate();
7954 return;
7955 }
7956
7957 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7958 s.select_ranges(vec![0..0]);
7959 });
7960 }
7961
7962 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
7963 let mut selection = self.selections.last::<Point>(cx);
7964 selection.set_head(Point::zero(), SelectionGoal::None);
7965
7966 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7967 s.select(vec![selection]);
7968 });
7969 }
7970
7971 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
7972 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7973 cx.propagate();
7974 return;
7975 }
7976
7977 let cursor = self.buffer.read(cx).read(cx).len();
7978 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7979 s.select_ranges(vec![cursor..cursor])
7980 });
7981 }
7982
7983 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
7984 self.nav_history = nav_history;
7985 }
7986
7987 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
7988 self.nav_history.as_ref()
7989 }
7990
7991 fn push_to_nav_history(
7992 &mut self,
7993 cursor_anchor: Anchor,
7994 new_position: Option<Point>,
7995 cx: &mut ViewContext<Self>,
7996 ) {
7997 if let Some(nav_history) = self.nav_history.as_mut() {
7998 let buffer = self.buffer.read(cx).read(cx);
7999 let cursor_position = cursor_anchor.to_point(&buffer);
8000 let scroll_state = self.scroll_manager.anchor();
8001 let scroll_top_row = scroll_state.top_row(&buffer);
8002 drop(buffer);
8003
8004 if let Some(new_position) = new_position {
8005 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
8006 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
8007 return;
8008 }
8009 }
8010
8011 nav_history.push(
8012 Some(NavigationData {
8013 cursor_anchor,
8014 cursor_position,
8015 scroll_anchor: scroll_state,
8016 scroll_top_row,
8017 }),
8018 cx,
8019 );
8020 }
8021 }
8022
8023 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
8024 let buffer = self.buffer.read(cx).snapshot(cx);
8025 let mut selection = self.selections.first::<usize>(cx);
8026 selection.set_head(buffer.len(), SelectionGoal::None);
8027 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8028 s.select(vec![selection]);
8029 });
8030 }
8031
8032 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
8033 let end = self.buffer.read(cx).read(cx).len();
8034 self.change_selections(None, cx, |s| {
8035 s.select_ranges(vec![0..end]);
8036 });
8037 }
8038
8039 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
8040 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8041 let mut selections = self.selections.all::<Point>(cx);
8042 let max_point = display_map.buffer_snapshot.max_point();
8043 for selection in &mut selections {
8044 let rows = selection.spanned_rows(true, &display_map);
8045 selection.start = Point::new(rows.start.0, 0);
8046 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
8047 selection.reversed = false;
8048 }
8049 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8050 s.select(selections);
8051 });
8052 }
8053
8054 pub fn split_selection_into_lines(
8055 &mut self,
8056 _: &SplitSelectionIntoLines,
8057 cx: &mut ViewContext<Self>,
8058 ) {
8059 let mut to_unfold = Vec::new();
8060 let mut new_selection_ranges = Vec::new();
8061 {
8062 let selections = self.selections.all::<Point>(cx);
8063 let buffer = self.buffer.read(cx).read(cx);
8064 for selection in selections {
8065 for row in selection.start.row..selection.end.row {
8066 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
8067 new_selection_ranges.push(cursor..cursor);
8068 }
8069 new_selection_ranges.push(selection.end..selection.end);
8070 to_unfold.push(selection.start..selection.end);
8071 }
8072 }
8073 self.unfold_ranges(&to_unfold, true, true, cx);
8074 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8075 s.select_ranges(new_selection_ranges);
8076 });
8077 }
8078
8079 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
8080 self.add_selection(true, cx);
8081 }
8082
8083 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
8084 self.add_selection(false, cx);
8085 }
8086
8087 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
8088 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8089 let mut selections = self.selections.all::<Point>(cx);
8090 let text_layout_details = self.text_layout_details(cx);
8091 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
8092 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
8093 let range = oldest_selection.display_range(&display_map).sorted();
8094
8095 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
8096 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
8097 let positions = start_x.min(end_x)..start_x.max(end_x);
8098
8099 selections.clear();
8100 let mut stack = Vec::new();
8101 for row in range.start.row().0..=range.end.row().0 {
8102 if let Some(selection) = self.selections.build_columnar_selection(
8103 &display_map,
8104 DisplayRow(row),
8105 &positions,
8106 oldest_selection.reversed,
8107 &text_layout_details,
8108 ) {
8109 stack.push(selection.id);
8110 selections.push(selection);
8111 }
8112 }
8113
8114 if above {
8115 stack.reverse();
8116 }
8117
8118 AddSelectionsState { above, stack }
8119 });
8120
8121 let last_added_selection = *state.stack.last().unwrap();
8122 let mut new_selections = Vec::new();
8123 if above == state.above {
8124 let end_row = if above {
8125 DisplayRow(0)
8126 } else {
8127 display_map.max_point().row()
8128 };
8129
8130 'outer: for selection in selections {
8131 if selection.id == last_added_selection {
8132 let range = selection.display_range(&display_map).sorted();
8133 debug_assert_eq!(range.start.row(), range.end.row());
8134 let mut row = range.start.row();
8135 let positions =
8136 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
8137 px(start)..px(end)
8138 } else {
8139 let start_x =
8140 display_map.x_for_display_point(range.start, &text_layout_details);
8141 let end_x =
8142 display_map.x_for_display_point(range.end, &text_layout_details);
8143 start_x.min(end_x)..start_x.max(end_x)
8144 };
8145
8146 while row != end_row {
8147 if above {
8148 row.0 -= 1;
8149 } else {
8150 row.0 += 1;
8151 }
8152
8153 if let Some(new_selection) = self.selections.build_columnar_selection(
8154 &display_map,
8155 row,
8156 &positions,
8157 selection.reversed,
8158 &text_layout_details,
8159 ) {
8160 state.stack.push(new_selection.id);
8161 if above {
8162 new_selections.push(new_selection);
8163 new_selections.push(selection);
8164 } else {
8165 new_selections.push(selection);
8166 new_selections.push(new_selection);
8167 }
8168
8169 continue 'outer;
8170 }
8171 }
8172 }
8173
8174 new_selections.push(selection);
8175 }
8176 } else {
8177 new_selections = selections;
8178 new_selections.retain(|s| s.id != last_added_selection);
8179 state.stack.pop();
8180 }
8181
8182 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8183 s.select(new_selections);
8184 });
8185 if state.stack.len() > 1 {
8186 self.add_selections_state = Some(state);
8187 }
8188 }
8189
8190 pub fn select_next_match_internal(
8191 &mut self,
8192 display_map: &DisplaySnapshot,
8193 replace_newest: bool,
8194 autoscroll: Option<Autoscroll>,
8195 cx: &mut ViewContext<Self>,
8196 ) -> Result<()> {
8197 fn select_next_match_ranges(
8198 this: &mut Editor,
8199 range: Range<usize>,
8200 replace_newest: bool,
8201 auto_scroll: Option<Autoscroll>,
8202 cx: &mut ViewContext<Editor>,
8203 ) {
8204 this.unfold_ranges(&[range.clone()], false, true, cx);
8205 this.change_selections(auto_scroll, cx, |s| {
8206 if replace_newest {
8207 s.delete(s.newest_anchor().id);
8208 }
8209 s.insert_range(range.clone());
8210 });
8211 }
8212
8213 let buffer = &display_map.buffer_snapshot;
8214 let mut selections = self.selections.all::<usize>(cx);
8215 if let Some(mut select_next_state) = self.select_next_state.take() {
8216 let query = &select_next_state.query;
8217 if !select_next_state.done {
8218 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
8219 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
8220 let mut next_selected_range = None;
8221
8222 let bytes_after_last_selection =
8223 buffer.bytes_in_range(last_selection.end..buffer.len());
8224 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
8225 let query_matches = query
8226 .stream_find_iter(bytes_after_last_selection)
8227 .map(|result| (last_selection.end, result))
8228 .chain(
8229 query
8230 .stream_find_iter(bytes_before_first_selection)
8231 .map(|result| (0, result)),
8232 );
8233
8234 for (start_offset, query_match) in query_matches {
8235 let query_match = query_match.unwrap(); // can only fail due to I/O
8236 let offset_range =
8237 start_offset + query_match.start()..start_offset + query_match.end();
8238 let display_range = offset_range.start.to_display_point(display_map)
8239 ..offset_range.end.to_display_point(display_map);
8240
8241 if !select_next_state.wordwise
8242 || (!movement::is_inside_word(display_map, display_range.start)
8243 && !movement::is_inside_word(display_map, display_range.end))
8244 {
8245 // TODO: This is n^2, because we might check all the selections
8246 if !selections
8247 .iter()
8248 .any(|selection| selection.range().overlaps(&offset_range))
8249 {
8250 next_selected_range = Some(offset_range);
8251 break;
8252 }
8253 }
8254 }
8255
8256 if let Some(next_selected_range) = next_selected_range {
8257 select_next_match_ranges(
8258 self,
8259 next_selected_range,
8260 replace_newest,
8261 autoscroll,
8262 cx,
8263 );
8264 } else {
8265 select_next_state.done = true;
8266 }
8267 }
8268
8269 self.select_next_state = Some(select_next_state);
8270 } else {
8271 let mut only_carets = true;
8272 let mut same_text_selected = true;
8273 let mut selected_text = None;
8274
8275 let mut selections_iter = selections.iter().peekable();
8276 while let Some(selection) = selections_iter.next() {
8277 if selection.start != selection.end {
8278 only_carets = false;
8279 }
8280
8281 if same_text_selected {
8282 if selected_text.is_none() {
8283 selected_text =
8284 Some(buffer.text_for_range(selection.range()).collect::<String>());
8285 }
8286
8287 if let Some(next_selection) = selections_iter.peek() {
8288 if next_selection.range().len() == selection.range().len() {
8289 let next_selected_text = buffer
8290 .text_for_range(next_selection.range())
8291 .collect::<String>();
8292 if Some(next_selected_text) != selected_text {
8293 same_text_selected = false;
8294 selected_text = None;
8295 }
8296 } else {
8297 same_text_selected = false;
8298 selected_text = None;
8299 }
8300 }
8301 }
8302 }
8303
8304 if only_carets {
8305 for selection in &mut selections {
8306 let word_range = movement::surrounding_word(
8307 display_map,
8308 selection.start.to_display_point(display_map),
8309 );
8310 selection.start = word_range.start.to_offset(display_map, Bias::Left);
8311 selection.end = word_range.end.to_offset(display_map, Bias::Left);
8312 selection.goal = SelectionGoal::None;
8313 selection.reversed = false;
8314 select_next_match_ranges(
8315 self,
8316 selection.start..selection.end,
8317 replace_newest,
8318 autoscroll,
8319 cx,
8320 );
8321 }
8322
8323 if selections.len() == 1 {
8324 let selection = selections
8325 .last()
8326 .expect("ensured that there's only one selection");
8327 let query = buffer
8328 .text_for_range(selection.start..selection.end)
8329 .collect::<String>();
8330 let is_empty = query.is_empty();
8331 let select_state = SelectNextState {
8332 query: AhoCorasick::new(&[query])?,
8333 wordwise: true,
8334 done: is_empty,
8335 };
8336 self.select_next_state = Some(select_state);
8337 } else {
8338 self.select_next_state = None;
8339 }
8340 } else if let Some(selected_text) = selected_text {
8341 self.select_next_state = Some(SelectNextState {
8342 query: AhoCorasick::new(&[selected_text])?,
8343 wordwise: false,
8344 done: false,
8345 });
8346 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
8347 }
8348 }
8349 Ok(())
8350 }
8351
8352 pub fn select_all_matches(
8353 &mut self,
8354 _action: &SelectAllMatches,
8355 cx: &mut ViewContext<Self>,
8356 ) -> Result<()> {
8357 self.push_to_selection_history();
8358 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8359
8360 self.select_next_match_internal(&display_map, false, None, cx)?;
8361 let Some(select_next_state) = self.select_next_state.as_mut() else {
8362 return Ok(());
8363 };
8364 if select_next_state.done {
8365 return Ok(());
8366 }
8367
8368 let mut new_selections = self.selections.all::<usize>(cx);
8369
8370 let buffer = &display_map.buffer_snapshot;
8371 let query_matches = select_next_state
8372 .query
8373 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
8374
8375 for query_match in query_matches {
8376 let query_match = query_match.unwrap(); // can only fail due to I/O
8377 let offset_range = query_match.start()..query_match.end();
8378 let display_range = offset_range.start.to_display_point(&display_map)
8379 ..offset_range.end.to_display_point(&display_map);
8380
8381 if !select_next_state.wordwise
8382 || (!movement::is_inside_word(&display_map, display_range.start)
8383 && !movement::is_inside_word(&display_map, display_range.end))
8384 {
8385 self.selections.change_with(cx, |selections| {
8386 new_selections.push(Selection {
8387 id: selections.new_selection_id(),
8388 start: offset_range.start,
8389 end: offset_range.end,
8390 reversed: false,
8391 goal: SelectionGoal::None,
8392 });
8393 });
8394 }
8395 }
8396
8397 new_selections.sort_by_key(|selection| selection.start);
8398 let mut ix = 0;
8399 while ix + 1 < new_selections.len() {
8400 let current_selection = &new_selections[ix];
8401 let next_selection = &new_selections[ix + 1];
8402 if current_selection.range().overlaps(&next_selection.range()) {
8403 if current_selection.id < next_selection.id {
8404 new_selections.remove(ix + 1);
8405 } else {
8406 new_selections.remove(ix);
8407 }
8408 } else {
8409 ix += 1;
8410 }
8411 }
8412
8413 select_next_state.done = true;
8414 self.unfold_ranges(
8415 &new_selections
8416 .iter()
8417 .map(|selection| selection.range())
8418 .collect::<Vec<_>>(),
8419 false,
8420 false,
8421 cx,
8422 );
8423 self.change_selections(Some(Autoscroll::fit()), cx, |selections| {
8424 selections.select(new_selections)
8425 });
8426
8427 Ok(())
8428 }
8429
8430 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
8431 self.push_to_selection_history();
8432 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8433 self.select_next_match_internal(
8434 &display_map,
8435 action.replace_newest,
8436 Some(Autoscroll::newest()),
8437 cx,
8438 )?;
8439 Ok(())
8440 }
8441
8442 pub fn select_previous(
8443 &mut self,
8444 action: &SelectPrevious,
8445 cx: &mut ViewContext<Self>,
8446 ) -> Result<()> {
8447 self.push_to_selection_history();
8448 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8449 let buffer = &display_map.buffer_snapshot;
8450 let mut selections = self.selections.all::<usize>(cx);
8451 if let Some(mut select_prev_state) = self.select_prev_state.take() {
8452 let query = &select_prev_state.query;
8453 if !select_prev_state.done {
8454 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
8455 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
8456 let mut next_selected_range = None;
8457 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
8458 let bytes_before_last_selection =
8459 buffer.reversed_bytes_in_range(0..last_selection.start);
8460 let bytes_after_first_selection =
8461 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
8462 let query_matches = query
8463 .stream_find_iter(bytes_before_last_selection)
8464 .map(|result| (last_selection.start, result))
8465 .chain(
8466 query
8467 .stream_find_iter(bytes_after_first_selection)
8468 .map(|result| (buffer.len(), result)),
8469 );
8470 for (end_offset, query_match) in query_matches {
8471 let query_match = query_match.unwrap(); // can only fail due to I/O
8472 let offset_range =
8473 end_offset - query_match.end()..end_offset - query_match.start();
8474 let display_range = offset_range.start.to_display_point(&display_map)
8475 ..offset_range.end.to_display_point(&display_map);
8476
8477 if !select_prev_state.wordwise
8478 || (!movement::is_inside_word(&display_map, display_range.start)
8479 && !movement::is_inside_word(&display_map, display_range.end))
8480 {
8481 next_selected_range = Some(offset_range);
8482 break;
8483 }
8484 }
8485
8486 if let Some(next_selected_range) = next_selected_range {
8487 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
8488 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8489 if action.replace_newest {
8490 s.delete(s.newest_anchor().id);
8491 }
8492 s.insert_range(next_selected_range);
8493 });
8494 } else {
8495 select_prev_state.done = true;
8496 }
8497 }
8498
8499 self.select_prev_state = Some(select_prev_state);
8500 } else {
8501 let mut only_carets = true;
8502 let mut same_text_selected = true;
8503 let mut selected_text = None;
8504
8505 let mut selections_iter = selections.iter().peekable();
8506 while let Some(selection) = selections_iter.next() {
8507 if selection.start != selection.end {
8508 only_carets = false;
8509 }
8510
8511 if same_text_selected {
8512 if selected_text.is_none() {
8513 selected_text =
8514 Some(buffer.text_for_range(selection.range()).collect::<String>());
8515 }
8516
8517 if let Some(next_selection) = selections_iter.peek() {
8518 if next_selection.range().len() == selection.range().len() {
8519 let next_selected_text = buffer
8520 .text_for_range(next_selection.range())
8521 .collect::<String>();
8522 if Some(next_selected_text) != selected_text {
8523 same_text_selected = false;
8524 selected_text = None;
8525 }
8526 } else {
8527 same_text_selected = false;
8528 selected_text = None;
8529 }
8530 }
8531 }
8532 }
8533
8534 if only_carets {
8535 for selection in &mut selections {
8536 let word_range = movement::surrounding_word(
8537 &display_map,
8538 selection.start.to_display_point(&display_map),
8539 );
8540 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
8541 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
8542 selection.goal = SelectionGoal::None;
8543 selection.reversed = false;
8544 }
8545 if selections.len() == 1 {
8546 let selection = selections
8547 .last()
8548 .expect("ensured that there's only one selection");
8549 let query = buffer
8550 .text_for_range(selection.start..selection.end)
8551 .collect::<String>();
8552 let is_empty = query.is_empty();
8553 let select_state = SelectNextState {
8554 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
8555 wordwise: true,
8556 done: is_empty,
8557 };
8558 self.select_prev_state = Some(select_state);
8559 } else {
8560 self.select_prev_state = None;
8561 }
8562
8563 self.unfold_ranges(
8564 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
8565 false,
8566 true,
8567 cx,
8568 );
8569 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8570 s.select(selections);
8571 });
8572 } else if let Some(selected_text) = selected_text {
8573 self.select_prev_state = Some(SelectNextState {
8574 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
8575 wordwise: false,
8576 done: false,
8577 });
8578 self.select_previous(action, cx)?;
8579 }
8580 }
8581 Ok(())
8582 }
8583
8584 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
8585 if self.read_only(cx) {
8586 return;
8587 }
8588 let text_layout_details = &self.text_layout_details(cx);
8589 self.transact(cx, |this, cx| {
8590 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
8591 let mut edits = Vec::new();
8592 let mut selection_edit_ranges = Vec::new();
8593 let mut last_toggled_row = None;
8594 let snapshot = this.buffer.read(cx).read(cx);
8595 let empty_str: Arc<str> = Arc::default();
8596 let mut suffixes_inserted = Vec::new();
8597 let ignore_indent = action.ignore_indent;
8598
8599 fn comment_prefix_range(
8600 snapshot: &MultiBufferSnapshot,
8601 row: MultiBufferRow,
8602 comment_prefix: &str,
8603 comment_prefix_whitespace: &str,
8604 ignore_indent: bool,
8605 ) -> Range<Point> {
8606 let indent_size = if ignore_indent {
8607 0
8608 } else {
8609 snapshot.indent_size_for_line(row).len
8610 };
8611
8612 let start = Point::new(row.0, indent_size);
8613
8614 let mut line_bytes = snapshot
8615 .bytes_in_range(start..snapshot.max_point())
8616 .flatten()
8617 .copied();
8618
8619 // If this line currently begins with the line comment prefix, then record
8620 // the range containing the prefix.
8621 if line_bytes
8622 .by_ref()
8623 .take(comment_prefix.len())
8624 .eq(comment_prefix.bytes())
8625 {
8626 // Include any whitespace that matches the comment prefix.
8627 let matching_whitespace_len = line_bytes
8628 .zip(comment_prefix_whitespace.bytes())
8629 .take_while(|(a, b)| a == b)
8630 .count() as u32;
8631 let end = Point::new(
8632 start.row,
8633 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
8634 );
8635 start..end
8636 } else {
8637 start..start
8638 }
8639 }
8640
8641 fn comment_suffix_range(
8642 snapshot: &MultiBufferSnapshot,
8643 row: MultiBufferRow,
8644 comment_suffix: &str,
8645 comment_suffix_has_leading_space: bool,
8646 ) -> Range<Point> {
8647 let end = Point::new(row.0, snapshot.line_len(row));
8648 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
8649
8650 let mut line_end_bytes = snapshot
8651 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
8652 .flatten()
8653 .copied();
8654
8655 let leading_space_len = if suffix_start_column > 0
8656 && line_end_bytes.next() == Some(b' ')
8657 && comment_suffix_has_leading_space
8658 {
8659 1
8660 } else {
8661 0
8662 };
8663
8664 // If this line currently begins with the line comment prefix, then record
8665 // the range containing the prefix.
8666 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
8667 let start = Point::new(end.row, suffix_start_column - leading_space_len);
8668 start..end
8669 } else {
8670 end..end
8671 }
8672 }
8673
8674 // TODO: Handle selections that cross excerpts
8675 for selection in &mut selections {
8676 let start_column = snapshot
8677 .indent_size_for_line(MultiBufferRow(selection.start.row))
8678 .len;
8679 let language = if let Some(language) =
8680 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
8681 {
8682 language
8683 } else {
8684 continue;
8685 };
8686
8687 selection_edit_ranges.clear();
8688
8689 // If multiple selections contain a given row, avoid processing that
8690 // row more than once.
8691 let mut start_row = MultiBufferRow(selection.start.row);
8692 if last_toggled_row == Some(start_row) {
8693 start_row = start_row.next_row();
8694 }
8695 let end_row =
8696 if selection.end.row > selection.start.row && selection.end.column == 0 {
8697 MultiBufferRow(selection.end.row - 1)
8698 } else {
8699 MultiBufferRow(selection.end.row)
8700 };
8701 last_toggled_row = Some(end_row);
8702
8703 if start_row > end_row {
8704 continue;
8705 }
8706
8707 // If the language has line comments, toggle those.
8708 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
8709
8710 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
8711 if ignore_indent {
8712 full_comment_prefixes = full_comment_prefixes
8713 .into_iter()
8714 .map(|s| Arc::from(s.trim_end()))
8715 .collect();
8716 }
8717
8718 if !full_comment_prefixes.is_empty() {
8719 let first_prefix = full_comment_prefixes
8720 .first()
8721 .expect("prefixes is non-empty");
8722 let prefix_trimmed_lengths = full_comment_prefixes
8723 .iter()
8724 .map(|p| p.trim_end_matches(' ').len())
8725 .collect::<SmallVec<[usize; 4]>>();
8726
8727 let mut all_selection_lines_are_comments = true;
8728
8729 for row in start_row.0..=end_row.0 {
8730 let row = MultiBufferRow(row);
8731 if start_row < end_row && snapshot.is_line_blank(row) {
8732 continue;
8733 }
8734
8735 let prefix_range = full_comment_prefixes
8736 .iter()
8737 .zip(prefix_trimmed_lengths.iter().copied())
8738 .map(|(prefix, trimmed_prefix_len)| {
8739 comment_prefix_range(
8740 snapshot.deref(),
8741 row,
8742 &prefix[..trimmed_prefix_len],
8743 &prefix[trimmed_prefix_len..],
8744 ignore_indent,
8745 )
8746 })
8747 .max_by_key(|range| range.end.column - range.start.column)
8748 .expect("prefixes is non-empty");
8749
8750 if prefix_range.is_empty() {
8751 all_selection_lines_are_comments = false;
8752 }
8753
8754 selection_edit_ranges.push(prefix_range);
8755 }
8756
8757 if all_selection_lines_are_comments {
8758 edits.extend(
8759 selection_edit_ranges
8760 .iter()
8761 .cloned()
8762 .map(|range| (range, empty_str.clone())),
8763 );
8764 } else {
8765 let min_column = selection_edit_ranges
8766 .iter()
8767 .map(|range| range.start.column)
8768 .min()
8769 .unwrap_or(0);
8770 edits.extend(selection_edit_ranges.iter().map(|range| {
8771 let position = Point::new(range.start.row, min_column);
8772 (position..position, first_prefix.clone())
8773 }));
8774 }
8775 } else if let Some((full_comment_prefix, comment_suffix)) =
8776 language.block_comment_delimiters()
8777 {
8778 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
8779 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
8780 let prefix_range = comment_prefix_range(
8781 snapshot.deref(),
8782 start_row,
8783 comment_prefix,
8784 comment_prefix_whitespace,
8785 ignore_indent,
8786 );
8787 let suffix_range = comment_suffix_range(
8788 snapshot.deref(),
8789 end_row,
8790 comment_suffix.trim_start_matches(' '),
8791 comment_suffix.starts_with(' '),
8792 );
8793
8794 if prefix_range.is_empty() || suffix_range.is_empty() {
8795 edits.push((
8796 prefix_range.start..prefix_range.start,
8797 full_comment_prefix.clone(),
8798 ));
8799 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
8800 suffixes_inserted.push((end_row, comment_suffix.len()));
8801 } else {
8802 edits.push((prefix_range, empty_str.clone()));
8803 edits.push((suffix_range, empty_str.clone()));
8804 }
8805 } else {
8806 continue;
8807 }
8808 }
8809
8810 drop(snapshot);
8811 this.buffer.update(cx, |buffer, cx| {
8812 buffer.edit(edits, None, cx);
8813 });
8814
8815 // Adjust selections so that they end before any comment suffixes that
8816 // were inserted.
8817 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
8818 let mut selections = this.selections.all::<Point>(cx);
8819 let snapshot = this.buffer.read(cx).read(cx);
8820 for selection in &mut selections {
8821 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
8822 match row.cmp(&MultiBufferRow(selection.end.row)) {
8823 Ordering::Less => {
8824 suffixes_inserted.next();
8825 continue;
8826 }
8827 Ordering::Greater => break,
8828 Ordering::Equal => {
8829 if selection.end.column == snapshot.line_len(row) {
8830 if selection.is_empty() {
8831 selection.start.column -= suffix_len as u32;
8832 }
8833 selection.end.column -= suffix_len as u32;
8834 }
8835 break;
8836 }
8837 }
8838 }
8839 }
8840
8841 drop(snapshot);
8842 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
8843
8844 let selections = this.selections.all::<Point>(cx);
8845 let selections_on_single_row = selections.windows(2).all(|selections| {
8846 selections[0].start.row == selections[1].start.row
8847 && selections[0].end.row == selections[1].end.row
8848 && selections[0].start.row == selections[0].end.row
8849 });
8850 let selections_selecting = selections
8851 .iter()
8852 .any(|selection| selection.start != selection.end);
8853 let advance_downwards = action.advance_downwards
8854 && selections_on_single_row
8855 && !selections_selecting
8856 && !matches!(this.mode, EditorMode::SingleLine { .. });
8857
8858 if advance_downwards {
8859 let snapshot = this.buffer.read(cx).snapshot(cx);
8860
8861 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8862 s.move_cursors_with(|display_snapshot, display_point, _| {
8863 let mut point = display_point.to_point(display_snapshot);
8864 point.row += 1;
8865 point = snapshot.clip_point(point, Bias::Left);
8866 let display_point = point.to_display_point(display_snapshot);
8867 let goal = SelectionGoal::HorizontalPosition(
8868 display_snapshot
8869 .x_for_display_point(display_point, text_layout_details)
8870 .into(),
8871 );
8872 (display_point, goal)
8873 })
8874 });
8875 }
8876 });
8877 }
8878
8879 pub fn select_enclosing_symbol(
8880 &mut self,
8881 _: &SelectEnclosingSymbol,
8882 cx: &mut ViewContext<Self>,
8883 ) {
8884 let buffer = self.buffer.read(cx).snapshot(cx);
8885 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8886
8887 fn update_selection(
8888 selection: &Selection<usize>,
8889 buffer_snap: &MultiBufferSnapshot,
8890 ) -> Option<Selection<usize>> {
8891 let cursor = selection.head();
8892 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
8893 for symbol in symbols.iter().rev() {
8894 let start = symbol.range.start.to_offset(buffer_snap);
8895 let end = symbol.range.end.to_offset(buffer_snap);
8896 let new_range = start..end;
8897 if start < selection.start || end > selection.end {
8898 return Some(Selection {
8899 id: selection.id,
8900 start: new_range.start,
8901 end: new_range.end,
8902 goal: SelectionGoal::None,
8903 reversed: selection.reversed,
8904 });
8905 }
8906 }
8907 None
8908 }
8909
8910 let mut selected_larger_symbol = false;
8911 let new_selections = old_selections
8912 .iter()
8913 .map(|selection| match update_selection(selection, &buffer) {
8914 Some(new_selection) => {
8915 if new_selection.range() != selection.range() {
8916 selected_larger_symbol = true;
8917 }
8918 new_selection
8919 }
8920 None => selection.clone(),
8921 })
8922 .collect::<Vec<_>>();
8923
8924 if selected_larger_symbol {
8925 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8926 s.select(new_selections);
8927 });
8928 }
8929 }
8930
8931 pub fn select_larger_syntax_node(
8932 &mut self,
8933 _: &SelectLargerSyntaxNode,
8934 cx: &mut ViewContext<Self>,
8935 ) {
8936 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8937 let buffer = self.buffer.read(cx).snapshot(cx);
8938 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8939
8940 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8941 let mut selected_larger_node = false;
8942 let new_selections = old_selections
8943 .iter()
8944 .map(|selection| {
8945 let old_range = selection.start..selection.end;
8946 let mut new_range = old_range.clone();
8947 let mut new_node = None;
8948 while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone())
8949 {
8950 new_node = Some(node);
8951 new_range = containing_range;
8952 if !display_map.intersects_fold(new_range.start)
8953 && !display_map.intersects_fold(new_range.end)
8954 {
8955 break;
8956 }
8957 }
8958
8959 if let Some(node) = new_node {
8960 // Log the ancestor, to support using this action as a way to explore TreeSitter
8961 // nodes. Parent and grandparent are also logged because this operation will not
8962 // visit nodes that have the same range as their parent.
8963 log::info!("Node: {node:?}");
8964 let parent = node.parent();
8965 log::info!("Parent: {parent:?}");
8966 let grandparent = parent.and_then(|x| x.parent());
8967 log::info!("Grandparent: {grandparent:?}");
8968 }
8969
8970 selected_larger_node |= new_range != old_range;
8971 Selection {
8972 id: selection.id,
8973 start: new_range.start,
8974 end: new_range.end,
8975 goal: SelectionGoal::None,
8976 reversed: selection.reversed,
8977 }
8978 })
8979 .collect::<Vec<_>>();
8980
8981 if selected_larger_node {
8982 stack.push(old_selections);
8983 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8984 s.select(new_selections);
8985 });
8986 }
8987 self.select_larger_syntax_node_stack = stack;
8988 }
8989
8990 pub fn select_smaller_syntax_node(
8991 &mut self,
8992 _: &SelectSmallerSyntaxNode,
8993 cx: &mut ViewContext<Self>,
8994 ) {
8995 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8996 if let Some(selections) = stack.pop() {
8997 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8998 s.select(selections.to_vec());
8999 });
9000 }
9001 self.select_larger_syntax_node_stack = stack;
9002 }
9003
9004 fn refresh_runnables(&mut self, cx: &mut ViewContext<Self>) -> Task<()> {
9005 if !EditorSettings::get_global(cx).gutter.runnables {
9006 self.clear_tasks();
9007 return Task::ready(());
9008 }
9009 let project = self.project.as_ref().map(Model::downgrade);
9010 cx.spawn(|this, mut cx| async move {
9011 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
9012 let Some(project) = project.and_then(|p| p.upgrade()) else {
9013 return;
9014 };
9015 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
9016 this.display_map.update(cx, |map, cx| map.snapshot(cx))
9017 }) else {
9018 return;
9019 };
9020
9021 let hide_runnables = project
9022 .update(&mut cx, |project, cx| {
9023 // Do not display any test indicators in non-dev server remote projects.
9024 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
9025 })
9026 .unwrap_or(true);
9027 if hide_runnables {
9028 return;
9029 }
9030 let new_rows =
9031 cx.background_executor()
9032 .spawn({
9033 let snapshot = display_snapshot.clone();
9034 async move {
9035 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
9036 }
9037 })
9038 .await;
9039 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
9040
9041 this.update(&mut cx, |this, _| {
9042 this.clear_tasks();
9043 for (key, value) in rows {
9044 this.insert_tasks(key, value);
9045 }
9046 })
9047 .ok();
9048 })
9049 }
9050 fn fetch_runnable_ranges(
9051 snapshot: &DisplaySnapshot,
9052 range: Range<Anchor>,
9053 ) -> Vec<language::RunnableRange> {
9054 snapshot.buffer_snapshot.runnable_ranges(range).collect()
9055 }
9056
9057 fn runnable_rows(
9058 project: Model<Project>,
9059 snapshot: DisplaySnapshot,
9060 runnable_ranges: Vec<RunnableRange>,
9061 mut cx: AsyncWindowContext,
9062 ) -> Vec<((BufferId, u32), RunnableTasks)> {
9063 runnable_ranges
9064 .into_iter()
9065 .filter_map(|mut runnable| {
9066 let tasks = cx
9067 .update(|cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
9068 .ok()?;
9069 if tasks.is_empty() {
9070 return None;
9071 }
9072
9073 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
9074
9075 let row = snapshot
9076 .buffer_snapshot
9077 .buffer_line_for_row(MultiBufferRow(point.row))?
9078 .1
9079 .start
9080 .row;
9081
9082 let context_range =
9083 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
9084 Some((
9085 (runnable.buffer_id, row),
9086 RunnableTasks {
9087 templates: tasks,
9088 offset: MultiBufferOffset(runnable.run_range.start),
9089 context_range,
9090 column: point.column,
9091 extra_variables: runnable.extra_captures,
9092 },
9093 ))
9094 })
9095 .collect()
9096 }
9097
9098 fn templates_with_tags(
9099 project: &Model<Project>,
9100 runnable: &mut Runnable,
9101 cx: &WindowContext,
9102 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
9103 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
9104 let (worktree_id, file) = project
9105 .buffer_for_id(runnable.buffer, cx)
9106 .and_then(|buffer| buffer.read(cx).file())
9107 .map(|file| (file.worktree_id(cx), file.clone()))
9108 .unzip();
9109
9110 (
9111 project.task_store().read(cx).task_inventory().cloned(),
9112 worktree_id,
9113 file,
9114 )
9115 });
9116
9117 let tags = mem::take(&mut runnable.tags);
9118 let mut tags: Vec<_> = tags
9119 .into_iter()
9120 .flat_map(|tag| {
9121 let tag = tag.0.clone();
9122 inventory
9123 .as_ref()
9124 .into_iter()
9125 .flat_map(|inventory| {
9126 inventory.read(cx).list_tasks(
9127 file.clone(),
9128 Some(runnable.language.clone()),
9129 worktree_id,
9130 cx,
9131 )
9132 })
9133 .filter(move |(_, template)| {
9134 template.tags.iter().any(|source_tag| source_tag == &tag)
9135 })
9136 })
9137 .sorted_by_key(|(kind, _)| kind.to_owned())
9138 .collect();
9139 if let Some((leading_tag_source, _)) = tags.first() {
9140 // Strongest source wins; if we have worktree tag binding, prefer that to
9141 // global and language bindings;
9142 // if we have a global binding, prefer that to language binding.
9143 let first_mismatch = tags
9144 .iter()
9145 .position(|(tag_source, _)| tag_source != leading_tag_source);
9146 if let Some(index) = first_mismatch {
9147 tags.truncate(index);
9148 }
9149 }
9150
9151 tags
9152 }
9153
9154 pub fn move_to_enclosing_bracket(
9155 &mut self,
9156 _: &MoveToEnclosingBracket,
9157 cx: &mut ViewContext<Self>,
9158 ) {
9159 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9160 s.move_offsets_with(|snapshot, selection| {
9161 let Some(enclosing_bracket_ranges) =
9162 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
9163 else {
9164 return;
9165 };
9166
9167 let mut best_length = usize::MAX;
9168 let mut best_inside = false;
9169 let mut best_in_bracket_range = false;
9170 let mut best_destination = None;
9171 for (open, close) in enclosing_bracket_ranges {
9172 let close = close.to_inclusive();
9173 let length = close.end() - open.start;
9174 let inside = selection.start >= open.end && selection.end <= *close.start();
9175 let in_bracket_range = open.to_inclusive().contains(&selection.head())
9176 || close.contains(&selection.head());
9177
9178 // If best is next to a bracket and current isn't, skip
9179 if !in_bracket_range && best_in_bracket_range {
9180 continue;
9181 }
9182
9183 // Prefer smaller lengths unless best is inside and current isn't
9184 if length > best_length && (best_inside || !inside) {
9185 continue;
9186 }
9187
9188 best_length = length;
9189 best_inside = inside;
9190 best_in_bracket_range = in_bracket_range;
9191 best_destination = Some(
9192 if close.contains(&selection.start) && close.contains(&selection.end) {
9193 if inside {
9194 open.end
9195 } else {
9196 open.start
9197 }
9198 } else if inside {
9199 *close.start()
9200 } else {
9201 *close.end()
9202 },
9203 );
9204 }
9205
9206 if let Some(destination) = best_destination {
9207 selection.collapse_to(destination, SelectionGoal::None);
9208 }
9209 })
9210 });
9211 }
9212
9213 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
9214 self.end_selection(cx);
9215 self.selection_history.mode = SelectionHistoryMode::Undoing;
9216 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
9217 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
9218 self.select_next_state = entry.select_next_state;
9219 self.select_prev_state = entry.select_prev_state;
9220 self.add_selections_state = entry.add_selections_state;
9221 self.request_autoscroll(Autoscroll::newest(), cx);
9222 }
9223 self.selection_history.mode = SelectionHistoryMode::Normal;
9224 }
9225
9226 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
9227 self.end_selection(cx);
9228 self.selection_history.mode = SelectionHistoryMode::Redoing;
9229 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
9230 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
9231 self.select_next_state = entry.select_next_state;
9232 self.select_prev_state = entry.select_prev_state;
9233 self.add_selections_state = entry.add_selections_state;
9234 self.request_autoscroll(Autoscroll::newest(), cx);
9235 }
9236 self.selection_history.mode = SelectionHistoryMode::Normal;
9237 }
9238
9239 pub fn expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
9240 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
9241 }
9242
9243 pub fn expand_excerpts_down(
9244 &mut self,
9245 action: &ExpandExcerptsDown,
9246 cx: &mut ViewContext<Self>,
9247 ) {
9248 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
9249 }
9250
9251 pub fn expand_excerpts_up(&mut self, action: &ExpandExcerptsUp, cx: &mut ViewContext<Self>) {
9252 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
9253 }
9254
9255 pub fn expand_excerpts_for_direction(
9256 &mut self,
9257 lines: u32,
9258 direction: ExpandExcerptDirection,
9259 cx: &mut ViewContext<Self>,
9260 ) {
9261 let selections = self.selections.disjoint_anchors();
9262
9263 let lines = if lines == 0 {
9264 EditorSettings::get_global(cx).expand_excerpt_lines
9265 } else {
9266 lines
9267 };
9268
9269 self.buffer.update(cx, |buffer, cx| {
9270 let snapshot = buffer.snapshot(cx);
9271 let mut excerpt_ids = selections
9272 .iter()
9273 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
9274 .collect::<Vec<_>>();
9275 excerpt_ids.sort();
9276 excerpt_ids.dedup();
9277 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
9278 })
9279 }
9280
9281 pub fn expand_excerpt(
9282 &mut self,
9283 excerpt: ExcerptId,
9284 direction: ExpandExcerptDirection,
9285 cx: &mut ViewContext<Self>,
9286 ) {
9287 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
9288 self.buffer.update(cx, |buffer, cx| {
9289 buffer.expand_excerpts([excerpt], lines, direction, cx)
9290 })
9291 }
9292
9293 pub fn go_to_singleton_buffer_point(&mut self, point: Point, cx: &mut ViewContext<Self>) {
9294 self.go_to_singleton_buffer_range(point..point, cx);
9295 }
9296
9297 pub fn go_to_singleton_buffer_range(
9298 &mut self,
9299 range: Range<Point>,
9300 cx: &mut ViewContext<Self>,
9301 ) {
9302 let multibuffer = self.buffer().read(cx);
9303 let Some(buffer) = multibuffer.as_singleton() else {
9304 return;
9305 };
9306 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
9307 return;
9308 };
9309 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
9310 return;
9311 };
9312 self.change_selections(Some(Autoscroll::center()), cx, |s| {
9313 s.select_anchor_ranges([start..end])
9314 });
9315 }
9316
9317 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
9318 self.go_to_diagnostic_impl(Direction::Next, cx)
9319 }
9320
9321 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
9322 self.go_to_diagnostic_impl(Direction::Prev, cx)
9323 }
9324
9325 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
9326 let buffer = self.buffer.read(cx).snapshot(cx);
9327 let selection = self.selections.newest::<usize>(cx);
9328
9329 // If there is an active Diagnostic Popover jump to its diagnostic instead.
9330 if direction == Direction::Next {
9331 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
9332 let Some(buffer_id) = popover.local_diagnostic.range.start.buffer_id else {
9333 return;
9334 };
9335 self.activate_diagnostics(
9336 buffer_id,
9337 popover.local_diagnostic.diagnostic.group_id,
9338 cx,
9339 );
9340 if let Some(active_diagnostics) = self.active_diagnostics.as_ref() {
9341 let primary_range_start = active_diagnostics.primary_range.start;
9342 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9343 let mut new_selection = s.newest_anchor().clone();
9344 new_selection.collapse_to(primary_range_start, SelectionGoal::None);
9345 s.select_anchors(vec![new_selection.clone()]);
9346 });
9347 self.refresh_inline_completion(false, true, cx);
9348 }
9349 return;
9350 }
9351 }
9352
9353 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
9354 active_diagnostics
9355 .primary_range
9356 .to_offset(&buffer)
9357 .to_inclusive()
9358 });
9359 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
9360 if active_primary_range.contains(&selection.head()) {
9361 *active_primary_range.start()
9362 } else {
9363 selection.head()
9364 }
9365 } else {
9366 selection.head()
9367 };
9368 let snapshot = self.snapshot(cx);
9369 loop {
9370 let mut diagnostics;
9371 if direction == Direction::Prev {
9372 diagnostics = buffer
9373 .diagnostics_in_range::<_, usize>(0..search_start)
9374 .collect::<Vec<_>>();
9375 diagnostics.reverse();
9376 } else {
9377 diagnostics = buffer
9378 .diagnostics_in_range::<_, usize>(search_start..buffer.len())
9379 .collect::<Vec<_>>();
9380 };
9381 let group = diagnostics
9382 .into_iter()
9383 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
9384 // relies on diagnostics_in_range to return diagnostics with the same starting range to
9385 // be sorted in a stable way
9386 // skip until we are at current active diagnostic, if it exists
9387 .skip_while(|entry| {
9388 let is_in_range = match direction {
9389 Direction::Prev => entry.range.end > search_start,
9390 Direction::Next => entry.range.start < search_start,
9391 };
9392 is_in_range
9393 && self
9394 .active_diagnostics
9395 .as_ref()
9396 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
9397 })
9398 .find_map(|entry| {
9399 if entry.diagnostic.is_primary
9400 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
9401 && entry.range.start != entry.range.end
9402 // if we match with the active diagnostic, skip it
9403 && Some(entry.diagnostic.group_id)
9404 != self.active_diagnostics.as_ref().map(|d| d.group_id)
9405 {
9406 Some((entry.range, entry.diagnostic.group_id))
9407 } else {
9408 None
9409 }
9410 });
9411
9412 if let Some((primary_range, group_id)) = group {
9413 let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
9414 return;
9415 };
9416 self.activate_diagnostics(buffer_id, group_id, cx);
9417 if self.active_diagnostics.is_some() {
9418 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9419 s.select(vec![Selection {
9420 id: selection.id,
9421 start: primary_range.start,
9422 end: primary_range.start,
9423 reversed: false,
9424 goal: SelectionGoal::None,
9425 }]);
9426 });
9427 self.refresh_inline_completion(false, true, cx);
9428 }
9429 break;
9430 } else {
9431 // Cycle around to the start of the buffer, potentially moving back to the start of
9432 // the currently active diagnostic.
9433 active_primary_range.take();
9434 if direction == Direction::Prev {
9435 if search_start == buffer.len() {
9436 break;
9437 } else {
9438 search_start = buffer.len();
9439 }
9440 } else if search_start == 0 {
9441 break;
9442 } else {
9443 search_start = 0;
9444 }
9445 }
9446 }
9447 }
9448
9449 fn go_to_next_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
9450 let snapshot = self.snapshot(cx);
9451 let selection = self.selections.newest::<Point>(cx);
9452 self.go_to_hunk_after_position(&snapshot, selection.head(), cx);
9453 }
9454
9455 fn go_to_hunk_after_position(
9456 &mut self,
9457 snapshot: &EditorSnapshot,
9458 position: Point,
9459 cx: &mut ViewContext<Editor>,
9460 ) -> Option<MultiBufferDiffHunk> {
9461 let mut hunk = snapshot
9462 .buffer_snapshot
9463 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
9464 .find(|hunk| hunk.row_range.start.0 > position.row);
9465 if hunk.is_none() {
9466 hunk = snapshot
9467 .buffer_snapshot
9468 .diff_hunks_in_range(Point::zero()..position)
9469 .find(|hunk| hunk.row_range.end.0 < position.row)
9470 }
9471 if let Some(hunk) = &hunk {
9472 let destination = Point::new(hunk.row_range.start.0, 0);
9473 self.unfold_ranges(&[destination..destination], false, false, cx);
9474 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9475 s.select_ranges(vec![destination..destination]);
9476 });
9477 }
9478
9479 hunk
9480 }
9481
9482 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
9483 let snapshot = self.snapshot(cx);
9484 let selection = self.selections.newest::<Point>(cx);
9485 self.go_to_hunk_before_position(&snapshot, selection.head(), cx);
9486 }
9487
9488 fn go_to_hunk_before_position(
9489 &mut self,
9490 snapshot: &EditorSnapshot,
9491 position: Point,
9492 cx: &mut ViewContext<Editor>,
9493 ) -> Option<MultiBufferDiffHunk> {
9494 let mut hunk = snapshot.buffer_snapshot.diff_hunk_before(position);
9495 if hunk.is_none() {
9496 hunk = snapshot.buffer_snapshot.diff_hunk_before(Point::MAX);
9497 }
9498 if let Some(hunk) = &hunk {
9499 let destination = Point::new(hunk.row_range.start.0, 0);
9500 self.unfold_ranges(&[destination..destination], false, false, cx);
9501 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9502 s.select_ranges(vec![destination..destination]);
9503 });
9504 }
9505
9506 hunk
9507 }
9508
9509 pub fn go_to_definition(
9510 &mut self,
9511 _: &GoToDefinition,
9512 cx: &mut ViewContext<Self>,
9513 ) -> Task<Result<Navigated>> {
9514 let definition = self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
9515 cx.spawn(|editor, mut cx| async move {
9516 if definition.await? == Navigated::Yes {
9517 return Ok(Navigated::Yes);
9518 }
9519 match editor.update(&mut cx, |editor, cx| {
9520 editor.find_all_references(&FindAllReferences, cx)
9521 })? {
9522 Some(references) => references.await,
9523 None => Ok(Navigated::No),
9524 }
9525 })
9526 }
9527
9528 pub fn go_to_declaration(
9529 &mut self,
9530 _: &GoToDeclaration,
9531 cx: &mut ViewContext<Self>,
9532 ) -> Task<Result<Navigated>> {
9533 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, cx)
9534 }
9535
9536 pub fn go_to_declaration_split(
9537 &mut self,
9538 _: &GoToDeclaration,
9539 cx: &mut ViewContext<Self>,
9540 ) -> Task<Result<Navigated>> {
9541 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, cx)
9542 }
9543
9544 pub fn go_to_implementation(
9545 &mut self,
9546 _: &GoToImplementation,
9547 cx: &mut ViewContext<Self>,
9548 ) -> Task<Result<Navigated>> {
9549 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx)
9550 }
9551
9552 pub fn go_to_implementation_split(
9553 &mut self,
9554 _: &GoToImplementationSplit,
9555 cx: &mut ViewContext<Self>,
9556 ) -> Task<Result<Navigated>> {
9557 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx)
9558 }
9559
9560 pub fn go_to_type_definition(
9561 &mut self,
9562 _: &GoToTypeDefinition,
9563 cx: &mut ViewContext<Self>,
9564 ) -> Task<Result<Navigated>> {
9565 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx)
9566 }
9567
9568 pub fn go_to_definition_split(
9569 &mut self,
9570 _: &GoToDefinitionSplit,
9571 cx: &mut ViewContext<Self>,
9572 ) -> Task<Result<Navigated>> {
9573 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx)
9574 }
9575
9576 pub fn go_to_type_definition_split(
9577 &mut self,
9578 _: &GoToTypeDefinitionSplit,
9579 cx: &mut ViewContext<Self>,
9580 ) -> Task<Result<Navigated>> {
9581 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx)
9582 }
9583
9584 fn go_to_definition_of_kind(
9585 &mut self,
9586 kind: GotoDefinitionKind,
9587 split: bool,
9588 cx: &mut ViewContext<Self>,
9589 ) -> Task<Result<Navigated>> {
9590 let Some(provider) = self.semantics_provider.clone() else {
9591 return Task::ready(Ok(Navigated::No));
9592 };
9593 let head = self.selections.newest::<usize>(cx).head();
9594 let buffer = self.buffer.read(cx);
9595 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
9596 text_anchor
9597 } else {
9598 return Task::ready(Ok(Navigated::No));
9599 };
9600
9601 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
9602 return Task::ready(Ok(Navigated::No));
9603 };
9604
9605 cx.spawn(|editor, mut cx| async move {
9606 let definitions = definitions.await?;
9607 let navigated = editor
9608 .update(&mut cx, |editor, cx| {
9609 editor.navigate_to_hover_links(
9610 Some(kind),
9611 definitions
9612 .into_iter()
9613 .filter(|location| {
9614 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
9615 })
9616 .map(HoverLink::Text)
9617 .collect::<Vec<_>>(),
9618 split,
9619 cx,
9620 )
9621 })?
9622 .await?;
9623 anyhow::Ok(navigated)
9624 })
9625 }
9626
9627 pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext<Self>) {
9628 let selection = self.selections.newest_anchor();
9629 let head = selection.head();
9630 let tail = selection.tail();
9631
9632 let Some((buffer, start_position)) =
9633 self.buffer.read(cx).text_anchor_for_position(head, cx)
9634 else {
9635 return;
9636 };
9637
9638 let end_position = if head != tail {
9639 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
9640 return;
9641 };
9642 Some(pos)
9643 } else {
9644 None
9645 };
9646
9647 let url_finder = cx.spawn(|editor, mut cx| async move {
9648 let url = if let Some(end_pos) = end_position {
9649 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
9650 } else {
9651 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
9652 };
9653
9654 if let Some(url) = url {
9655 editor.update(&mut cx, |_, cx| {
9656 cx.open_url(&url);
9657 })
9658 } else {
9659 Ok(())
9660 }
9661 });
9662
9663 url_finder.detach();
9664 }
9665
9666 pub fn open_selected_filename(&mut self, _: &OpenSelectedFilename, cx: &mut ViewContext<Self>) {
9667 let Some(workspace) = self.workspace() else {
9668 return;
9669 };
9670
9671 let position = self.selections.newest_anchor().head();
9672
9673 let Some((buffer, buffer_position)) =
9674 self.buffer.read(cx).text_anchor_for_position(position, cx)
9675 else {
9676 return;
9677 };
9678
9679 let project = self.project.clone();
9680
9681 cx.spawn(|_, mut cx| async move {
9682 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
9683
9684 if let Some((_, path)) = result {
9685 workspace
9686 .update(&mut cx, |workspace, cx| {
9687 workspace.open_resolved_path(path, cx)
9688 })?
9689 .await?;
9690 }
9691 anyhow::Ok(())
9692 })
9693 .detach();
9694 }
9695
9696 pub(crate) fn navigate_to_hover_links(
9697 &mut self,
9698 kind: Option<GotoDefinitionKind>,
9699 mut definitions: Vec<HoverLink>,
9700 split: bool,
9701 cx: &mut ViewContext<Editor>,
9702 ) -> Task<Result<Navigated>> {
9703 // If there is one definition, just open it directly
9704 if definitions.len() == 1 {
9705 let definition = definitions.pop().unwrap();
9706
9707 enum TargetTaskResult {
9708 Location(Option<Location>),
9709 AlreadyNavigated,
9710 }
9711
9712 let target_task = match definition {
9713 HoverLink::Text(link) => {
9714 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
9715 }
9716 HoverLink::InlayHint(lsp_location, server_id) => {
9717 let computation = self.compute_target_location(lsp_location, server_id, cx);
9718 cx.background_executor().spawn(async move {
9719 let location = computation.await?;
9720 Ok(TargetTaskResult::Location(location))
9721 })
9722 }
9723 HoverLink::Url(url) => {
9724 cx.open_url(&url);
9725 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
9726 }
9727 HoverLink::File(path) => {
9728 if let Some(workspace) = self.workspace() {
9729 cx.spawn(|_, mut cx| async move {
9730 workspace
9731 .update(&mut cx, |workspace, cx| {
9732 workspace.open_resolved_path(path, cx)
9733 })?
9734 .await
9735 .map(|_| TargetTaskResult::AlreadyNavigated)
9736 })
9737 } else {
9738 Task::ready(Ok(TargetTaskResult::Location(None)))
9739 }
9740 }
9741 };
9742 cx.spawn(|editor, mut cx| async move {
9743 let target = match target_task.await.context("target resolution task")? {
9744 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
9745 TargetTaskResult::Location(None) => return Ok(Navigated::No),
9746 TargetTaskResult::Location(Some(target)) => target,
9747 };
9748
9749 editor.update(&mut cx, |editor, cx| {
9750 let Some(workspace) = editor.workspace() else {
9751 return Navigated::No;
9752 };
9753 let pane = workspace.read(cx).active_pane().clone();
9754
9755 let range = target.range.to_point(target.buffer.read(cx));
9756 let range = editor.range_for_match(&range);
9757 let range = collapse_multiline_range(range);
9758
9759 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
9760 editor.go_to_singleton_buffer_range(range.clone(), cx);
9761 } else {
9762 cx.window_context().defer(move |cx| {
9763 let target_editor: View<Self> =
9764 workspace.update(cx, |workspace, cx| {
9765 let pane = if split {
9766 workspace.adjacent_pane(cx)
9767 } else {
9768 workspace.active_pane().clone()
9769 };
9770
9771 workspace.open_project_item(
9772 pane,
9773 target.buffer.clone(),
9774 true,
9775 true,
9776 cx,
9777 )
9778 });
9779 target_editor.update(cx, |target_editor, cx| {
9780 // When selecting a definition in a different buffer, disable the nav history
9781 // to avoid creating a history entry at the previous cursor location.
9782 pane.update(cx, |pane, _| pane.disable_history());
9783 target_editor.go_to_singleton_buffer_range(range, cx);
9784 pane.update(cx, |pane, _| pane.enable_history());
9785 });
9786 });
9787 }
9788 Navigated::Yes
9789 })
9790 })
9791 } else if !definitions.is_empty() {
9792 cx.spawn(|editor, mut cx| async move {
9793 let (title, location_tasks, workspace) = editor
9794 .update(&mut cx, |editor, cx| {
9795 let tab_kind = match kind {
9796 Some(GotoDefinitionKind::Implementation) => "Implementations",
9797 _ => "Definitions",
9798 };
9799 let title = definitions
9800 .iter()
9801 .find_map(|definition| match definition {
9802 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
9803 let buffer = origin.buffer.read(cx);
9804 format!(
9805 "{} for {}",
9806 tab_kind,
9807 buffer
9808 .text_for_range(origin.range.clone())
9809 .collect::<String>()
9810 )
9811 }),
9812 HoverLink::InlayHint(_, _) => None,
9813 HoverLink::Url(_) => None,
9814 HoverLink::File(_) => None,
9815 })
9816 .unwrap_or(tab_kind.to_string());
9817 let location_tasks = definitions
9818 .into_iter()
9819 .map(|definition| match definition {
9820 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
9821 HoverLink::InlayHint(lsp_location, server_id) => {
9822 editor.compute_target_location(lsp_location, server_id, cx)
9823 }
9824 HoverLink::Url(_) => Task::ready(Ok(None)),
9825 HoverLink::File(_) => Task::ready(Ok(None)),
9826 })
9827 .collect::<Vec<_>>();
9828 (title, location_tasks, editor.workspace().clone())
9829 })
9830 .context("location tasks preparation")?;
9831
9832 let locations = future::join_all(location_tasks)
9833 .await
9834 .into_iter()
9835 .filter_map(|location| location.transpose())
9836 .collect::<Result<_>>()
9837 .context("location tasks")?;
9838
9839 let Some(workspace) = workspace else {
9840 return Ok(Navigated::No);
9841 };
9842 let opened = workspace
9843 .update(&mut cx, |workspace, cx| {
9844 Self::open_locations_in_multibuffer(
9845 workspace,
9846 locations,
9847 title,
9848 split,
9849 MultibufferSelectionMode::First,
9850 cx,
9851 )
9852 })
9853 .ok();
9854
9855 anyhow::Ok(Navigated::from_bool(opened.is_some()))
9856 })
9857 } else {
9858 Task::ready(Ok(Navigated::No))
9859 }
9860 }
9861
9862 fn compute_target_location(
9863 &self,
9864 lsp_location: lsp::Location,
9865 server_id: LanguageServerId,
9866 cx: &mut ViewContext<Self>,
9867 ) -> Task<anyhow::Result<Option<Location>>> {
9868 let Some(project) = self.project.clone() else {
9869 return Task::ready(Ok(None));
9870 };
9871
9872 cx.spawn(move |editor, mut cx| async move {
9873 let location_task = editor.update(&mut cx, |_, cx| {
9874 project.update(cx, |project, cx| {
9875 let language_server_name = project
9876 .language_server_statuses(cx)
9877 .find(|(id, _)| server_id == *id)
9878 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
9879 language_server_name.map(|language_server_name| {
9880 project.open_local_buffer_via_lsp(
9881 lsp_location.uri.clone(),
9882 server_id,
9883 language_server_name,
9884 cx,
9885 )
9886 })
9887 })
9888 })?;
9889 let location = match location_task {
9890 Some(task) => Some({
9891 let target_buffer_handle = task.await.context("open local buffer")?;
9892 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
9893 let target_start = target_buffer
9894 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
9895 let target_end = target_buffer
9896 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
9897 target_buffer.anchor_after(target_start)
9898 ..target_buffer.anchor_before(target_end)
9899 })?;
9900 Location {
9901 buffer: target_buffer_handle,
9902 range,
9903 }
9904 }),
9905 None => None,
9906 };
9907 Ok(location)
9908 })
9909 }
9910
9911 pub fn find_all_references(
9912 &mut self,
9913 _: &FindAllReferences,
9914 cx: &mut ViewContext<Self>,
9915 ) -> Option<Task<Result<Navigated>>> {
9916 let selection = self.selections.newest::<usize>(cx);
9917 let multi_buffer = self.buffer.read(cx);
9918 let head = selection.head();
9919
9920 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
9921 let head_anchor = multi_buffer_snapshot.anchor_at(
9922 head,
9923 if head < selection.tail() {
9924 Bias::Right
9925 } else {
9926 Bias::Left
9927 },
9928 );
9929
9930 match self
9931 .find_all_references_task_sources
9932 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
9933 {
9934 Ok(_) => {
9935 log::info!(
9936 "Ignoring repeated FindAllReferences invocation with the position of already running task"
9937 );
9938 return None;
9939 }
9940 Err(i) => {
9941 self.find_all_references_task_sources.insert(i, head_anchor);
9942 }
9943 }
9944
9945 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
9946 let workspace = self.workspace()?;
9947 let project = workspace.read(cx).project().clone();
9948 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
9949 Some(cx.spawn(|editor, mut cx| async move {
9950 let _cleanup = defer({
9951 let mut cx = cx.clone();
9952 move || {
9953 let _ = editor.update(&mut cx, |editor, _| {
9954 if let Ok(i) =
9955 editor
9956 .find_all_references_task_sources
9957 .binary_search_by(|anchor| {
9958 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
9959 })
9960 {
9961 editor.find_all_references_task_sources.remove(i);
9962 }
9963 });
9964 }
9965 });
9966
9967 let locations = references.await?;
9968 if locations.is_empty() {
9969 return anyhow::Ok(Navigated::No);
9970 }
9971
9972 workspace.update(&mut cx, |workspace, cx| {
9973 let title = locations
9974 .first()
9975 .as_ref()
9976 .map(|location| {
9977 let buffer = location.buffer.read(cx);
9978 format!(
9979 "References to `{}`",
9980 buffer
9981 .text_for_range(location.range.clone())
9982 .collect::<String>()
9983 )
9984 })
9985 .unwrap();
9986 Self::open_locations_in_multibuffer(
9987 workspace,
9988 locations,
9989 title,
9990 false,
9991 MultibufferSelectionMode::First,
9992 cx,
9993 );
9994 Navigated::Yes
9995 })
9996 }))
9997 }
9998
9999 /// Opens a multibuffer with the given project locations in it
10000 pub fn open_locations_in_multibuffer(
10001 workspace: &mut Workspace,
10002 mut locations: Vec<Location>,
10003 title: String,
10004 split: bool,
10005 multibuffer_selection_mode: MultibufferSelectionMode,
10006 cx: &mut ViewContext<Workspace>,
10007 ) {
10008 // If there are multiple definitions, open them in a multibuffer
10009 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
10010 let mut locations = locations.into_iter().peekable();
10011 let mut ranges = Vec::new();
10012 let capability = workspace.project().read(cx).capability();
10013
10014 let excerpt_buffer = cx.new_model(|cx| {
10015 let mut multibuffer = MultiBuffer::new(capability);
10016 while let Some(location) = locations.next() {
10017 let buffer = location.buffer.read(cx);
10018 let mut ranges_for_buffer = Vec::new();
10019 let range = location.range.to_offset(buffer);
10020 ranges_for_buffer.push(range.clone());
10021
10022 while let Some(next_location) = locations.peek() {
10023 if next_location.buffer == location.buffer {
10024 ranges_for_buffer.push(next_location.range.to_offset(buffer));
10025 locations.next();
10026 } else {
10027 break;
10028 }
10029 }
10030
10031 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
10032 ranges.extend(multibuffer.push_excerpts_with_context_lines(
10033 location.buffer.clone(),
10034 ranges_for_buffer,
10035 DEFAULT_MULTIBUFFER_CONTEXT,
10036 cx,
10037 ))
10038 }
10039
10040 multibuffer.with_title(title)
10041 });
10042
10043 let editor = cx.new_view(|cx| {
10044 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), true, cx)
10045 });
10046 editor.update(cx, |editor, cx| {
10047 match multibuffer_selection_mode {
10048 MultibufferSelectionMode::First => {
10049 if let Some(first_range) = ranges.first() {
10050 editor.change_selections(None, cx, |selections| {
10051 selections.clear_disjoint();
10052 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
10053 });
10054 }
10055 editor.highlight_background::<Self>(
10056 &ranges,
10057 |theme| theme.editor_highlighted_line_background,
10058 cx,
10059 );
10060 }
10061 MultibufferSelectionMode::All => {
10062 editor.change_selections(None, cx, |selections| {
10063 selections.clear_disjoint();
10064 selections.select_anchor_ranges(ranges);
10065 });
10066 }
10067 }
10068 editor.register_buffers_with_language_servers(cx);
10069 });
10070
10071 let item = Box::new(editor);
10072 let item_id = item.item_id();
10073
10074 if split {
10075 workspace.split_item(SplitDirection::Right, item.clone(), cx);
10076 } else {
10077 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
10078 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
10079 pane.close_current_preview_item(cx)
10080 } else {
10081 None
10082 }
10083 });
10084 workspace.add_item_to_active_pane(item.clone(), destination_index, true, cx);
10085 }
10086 workspace.active_pane().update(cx, |pane, cx| {
10087 pane.set_preview_item_id(Some(item_id), cx);
10088 });
10089 }
10090
10091 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
10092 use language::ToOffset as _;
10093
10094 let provider = self.semantics_provider.clone()?;
10095 let selection = self.selections.newest_anchor().clone();
10096 let (cursor_buffer, cursor_buffer_position) = self
10097 .buffer
10098 .read(cx)
10099 .text_anchor_for_position(selection.head(), cx)?;
10100 let (tail_buffer, cursor_buffer_position_end) = self
10101 .buffer
10102 .read(cx)
10103 .text_anchor_for_position(selection.tail(), cx)?;
10104 if tail_buffer != cursor_buffer {
10105 return None;
10106 }
10107
10108 let snapshot = cursor_buffer.read(cx).snapshot();
10109 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
10110 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
10111 let prepare_rename = provider
10112 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
10113 .unwrap_or_else(|| Task::ready(Ok(None)));
10114 drop(snapshot);
10115
10116 Some(cx.spawn(|this, mut cx| async move {
10117 let rename_range = if let Some(range) = prepare_rename.await? {
10118 Some(range)
10119 } else {
10120 this.update(&mut cx, |this, cx| {
10121 let buffer = this.buffer.read(cx).snapshot(cx);
10122 let mut buffer_highlights = this
10123 .document_highlights_for_position(selection.head(), &buffer)
10124 .filter(|highlight| {
10125 highlight.start.excerpt_id == selection.head().excerpt_id
10126 && highlight.end.excerpt_id == selection.head().excerpt_id
10127 });
10128 buffer_highlights
10129 .next()
10130 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
10131 })?
10132 };
10133 if let Some(rename_range) = rename_range {
10134 this.update(&mut cx, |this, cx| {
10135 let snapshot = cursor_buffer.read(cx).snapshot();
10136 let rename_buffer_range = rename_range.to_offset(&snapshot);
10137 let cursor_offset_in_rename_range =
10138 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
10139 let cursor_offset_in_rename_range_end =
10140 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
10141
10142 this.take_rename(false, cx);
10143 let buffer = this.buffer.read(cx).read(cx);
10144 let cursor_offset = selection.head().to_offset(&buffer);
10145 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
10146 let rename_end = rename_start + rename_buffer_range.len();
10147 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
10148 let mut old_highlight_id = None;
10149 let old_name: Arc<str> = buffer
10150 .chunks(rename_start..rename_end, true)
10151 .map(|chunk| {
10152 if old_highlight_id.is_none() {
10153 old_highlight_id = chunk.syntax_highlight_id;
10154 }
10155 chunk.text
10156 })
10157 .collect::<String>()
10158 .into();
10159
10160 drop(buffer);
10161
10162 // Position the selection in the rename editor so that it matches the current selection.
10163 this.show_local_selections = false;
10164 let rename_editor = cx.new_view(|cx| {
10165 let mut editor = Editor::single_line(cx);
10166 editor.buffer.update(cx, |buffer, cx| {
10167 buffer.edit([(0..0, old_name.clone())], None, cx)
10168 });
10169 let rename_selection_range = match cursor_offset_in_rename_range
10170 .cmp(&cursor_offset_in_rename_range_end)
10171 {
10172 Ordering::Equal => {
10173 editor.select_all(&SelectAll, cx);
10174 return editor;
10175 }
10176 Ordering::Less => {
10177 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
10178 }
10179 Ordering::Greater => {
10180 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
10181 }
10182 };
10183 if rename_selection_range.end > old_name.len() {
10184 editor.select_all(&SelectAll, cx);
10185 } else {
10186 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
10187 s.select_ranges([rename_selection_range]);
10188 });
10189 }
10190 editor
10191 });
10192 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
10193 if e == &EditorEvent::Focused {
10194 cx.emit(EditorEvent::FocusedIn)
10195 }
10196 })
10197 .detach();
10198
10199 let write_highlights =
10200 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
10201 let read_highlights =
10202 this.clear_background_highlights::<DocumentHighlightRead>(cx);
10203 let ranges = write_highlights
10204 .iter()
10205 .flat_map(|(_, ranges)| ranges.iter())
10206 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
10207 .cloned()
10208 .collect();
10209
10210 this.highlight_text::<Rename>(
10211 ranges,
10212 HighlightStyle {
10213 fade_out: Some(0.6),
10214 ..Default::default()
10215 },
10216 cx,
10217 );
10218 let rename_focus_handle = rename_editor.focus_handle(cx);
10219 cx.focus(&rename_focus_handle);
10220 let block_id = this.insert_blocks(
10221 [BlockProperties {
10222 style: BlockStyle::Flex,
10223 placement: BlockPlacement::Below(range.start),
10224 height: 1,
10225 render: Arc::new({
10226 let rename_editor = rename_editor.clone();
10227 move |cx: &mut BlockContext| {
10228 let mut text_style = cx.editor_style.text.clone();
10229 if let Some(highlight_style) = old_highlight_id
10230 .and_then(|h| h.style(&cx.editor_style.syntax))
10231 {
10232 text_style = text_style.highlight(highlight_style);
10233 }
10234 div()
10235 .block_mouse_down()
10236 .pl(cx.anchor_x)
10237 .child(EditorElement::new(
10238 &rename_editor,
10239 EditorStyle {
10240 background: cx.theme().system().transparent,
10241 local_player: cx.editor_style.local_player,
10242 text: text_style,
10243 scrollbar_width: cx.editor_style.scrollbar_width,
10244 syntax: cx.editor_style.syntax.clone(),
10245 status: cx.editor_style.status.clone(),
10246 inlay_hints_style: HighlightStyle {
10247 font_weight: Some(FontWeight::BOLD),
10248 ..make_inlay_hints_style(cx)
10249 },
10250 inline_completion_styles: make_suggestion_styles(
10251 cx,
10252 ),
10253 ..EditorStyle::default()
10254 },
10255 ))
10256 .into_any_element()
10257 }
10258 }),
10259 priority: 0,
10260 }],
10261 Some(Autoscroll::fit()),
10262 cx,
10263 )[0];
10264 this.pending_rename = Some(RenameState {
10265 range,
10266 old_name,
10267 editor: rename_editor,
10268 block_id,
10269 });
10270 })?;
10271 }
10272
10273 Ok(())
10274 }))
10275 }
10276
10277 pub fn confirm_rename(
10278 &mut self,
10279 _: &ConfirmRename,
10280 cx: &mut ViewContext<Self>,
10281 ) -> Option<Task<Result<()>>> {
10282 let rename = self.take_rename(false, cx)?;
10283 let workspace = self.workspace()?.downgrade();
10284 let (buffer, start) = self
10285 .buffer
10286 .read(cx)
10287 .text_anchor_for_position(rename.range.start, cx)?;
10288 let (end_buffer, _) = self
10289 .buffer
10290 .read(cx)
10291 .text_anchor_for_position(rename.range.end, cx)?;
10292 if buffer != end_buffer {
10293 return None;
10294 }
10295
10296 let old_name = rename.old_name;
10297 let new_name = rename.editor.read(cx).text(cx);
10298
10299 let rename = self.semantics_provider.as_ref()?.perform_rename(
10300 &buffer,
10301 start,
10302 new_name.clone(),
10303 cx,
10304 )?;
10305
10306 Some(cx.spawn(|editor, mut cx| async move {
10307 let project_transaction = rename.await?;
10308 Self::open_project_transaction(
10309 &editor,
10310 workspace,
10311 project_transaction,
10312 format!("Rename: {} → {}", old_name, new_name),
10313 cx.clone(),
10314 )
10315 .await?;
10316
10317 editor.update(&mut cx, |editor, cx| {
10318 editor.refresh_document_highlights(cx);
10319 })?;
10320 Ok(())
10321 }))
10322 }
10323
10324 fn take_rename(
10325 &mut self,
10326 moving_cursor: bool,
10327 cx: &mut ViewContext<Self>,
10328 ) -> Option<RenameState> {
10329 let rename = self.pending_rename.take()?;
10330 if rename.editor.focus_handle(cx).is_focused(cx) {
10331 cx.focus(&self.focus_handle);
10332 }
10333
10334 self.remove_blocks(
10335 [rename.block_id].into_iter().collect(),
10336 Some(Autoscroll::fit()),
10337 cx,
10338 );
10339 self.clear_highlights::<Rename>(cx);
10340 self.show_local_selections = true;
10341
10342 if moving_cursor {
10343 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
10344 editor.selections.newest::<usize>(cx).head()
10345 });
10346
10347 // Update the selection to match the position of the selection inside
10348 // the rename editor.
10349 let snapshot = self.buffer.read(cx).read(cx);
10350 let rename_range = rename.range.to_offset(&snapshot);
10351 let cursor_in_editor = snapshot
10352 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
10353 .min(rename_range.end);
10354 drop(snapshot);
10355
10356 self.change_selections(None, cx, |s| {
10357 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
10358 });
10359 } else {
10360 self.refresh_document_highlights(cx);
10361 }
10362
10363 Some(rename)
10364 }
10365
10366 pub fn pending_rename(&self) -> Option<&RenameState> {
10367 self.pending_rename.as_ref()
10368 }
10369
10370 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
10371 let project = match &self.project {
10372 Some(project) => project.clone(),
10373 None => return None,
10374 };
10375
10376 Some(self.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffers, cx))
10377 }
10378
10379 fn format_selections(
10380 &mut self,
10381 _: &FormatSelections,
10382 cx: &mut ViewContext<Self>,
10383 ) -> Option<Task<Result<()>>> {
10384 let project = match &self.project {
10385 Some(project) => project.clone(),
10386 None => return None,
10387 };
10388
10389 let ranges = self
10390 .selections
10391 .all_adjusted(cx)
10392 .into_iter()
10393 .map(|selection| selection.range())
10394 .collect_vec();
10395
10396 Some(self.perform_format(
10397 project,
10398 FormatTrigger::Manual,
10399 FormatTarget::Ranges(ranges),
10400 cx,
10401 ))
10402 }
10403
10404 fn perform_format(
10405 &mut self,
10406 project: Model<Project>,
10407 trigger: FormatTrigger,
10408 target: FormatTarget,
10409 cx: &mut ViewContext<Self>,
10410 ) -> Task<Result<()>> {
10411 let buffer = self.buffer.clone();
10412 let (buffers, target) = match target {
10413 FormatTarget::Buffers => {
10414 let mut buffers = buffer.read(cx).all_buffers();
10415 if trigger == FormatTrigger::Save {
10416 buffers.retain(|buffer| buffer.read(cx).is_dirty());
10417 }
10418 (buffers, LspFormatTarget::Buffers)
10419 }
10420 FormatTarget::Ranges(selection_ranges) => {
10421 let multi_buffer = buffer.read(cx);
10422 let snapshot = multi_buffer.read(cx);
10423 let mut buffers = HashSet::default();
10424 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
10425 BTreeMap::new();
10426 for selection_range in selection_ranges {
10427 for (buffer, buffer_range, _) in
10428 snapshot.range_to_buffer_ranges(selection_range)
10429 {
10430 let buffer_id = buffer.remote_id();
10431 let start = buffer.anchor_before(buffer_range.start);
10432 let end = buffer.anchor_after(buffer_range.end);
10433 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
10434 buffer_id_to_ranges
10435 .entry(buffer_id)
10436 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
10437 .or_insert_with(|| vec![start..end]);
10438 }
10439 }
10440 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
10441 }
10442 };
10443
10444 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
10445 let format = project.update(cx, |project, cx| {
10446 project.format(buffers, target, true, trigger, cx)
10447 });
10448
10449 cx.spawn(|_, mut cx| async move {
10450 let transaction = futures::select_biased! {
10451 () = timeout => {
10452 log::warn!("timed out waiting for formatting");
10453 None
10454 }
10455 transaction = format.log_err().fuse() => transaction,
10456 };
10457
10458 buffer
10459 .update(&mut cx, |buffer, cx| {
10460 if let Some(transaction) = transaction {
10461 if !buffer.is_singleton() {
10462 buffer.push_transaction(&transaction.0, cx);
10463 }
10464 }
10465
10466 cx.notify();
10467 })
10468 .ok();
10469
10470 Ok(())
10471 })
10472 }
10473
10474 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
10475 if let Some(project) = self.project.clone() {
10476 self.buffer.update(cx, |multi_buffer, cx| {
10477 project.update(cx, |project, cx| {
10478 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
10479 });
10480 })
10481 }
10482 }
10483
10484 fn cancel_language_server_work(
10485 &mut self,
10486 _: &actions::CancelLanguageServerWork,
10487 cx: &mut ViewContext<Self>,
10488 ) {
10489 if let Some(project) = self.project.clone() {
10490 self.buffer.update(cx, |multi_buffer, cx| {
10491 project.update(cx, |project, cx| {
10492 project.cancel_language_server_work_for_buffers(multi_buffer.all_buffers(), cx);
10493 });
10494 })
10495 }
10496 }
10497
10498 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
10499 cx.show_character_palette();
10500 }
10501
10502 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
10503 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
10504 let buffer = self.buffer.read(cx).snapshot(cx);
10505 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
10506 let is_valid = buffer
10507 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone())
10508 .any(|entry| {
10509 entry.diagnostic.is_primary
10510 && !entry.range.is_empty()
10511 && entry.range.start == primary_range_start
10512 && entry.diagnostic.message == active_diagnostics.primary_message
10513 });
10514
10515 if is_valid != active_diagnostics.is_valid {
10516 active_diagnostics.is_valid = is_valid;
10517 let mut new_styles = HashMap::default();
10518 for (block_id, diagnostic) in &active_diagnostics.blocks {
10519 new_styles.insert(
10520 *block_id,
10521 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
10522 );
10523 }
10524 self.display_map.update(cx, |display_map, _cx| {
10525 display_map.replace_blocks(new_styles)
10526 });
10527 }
10528 }
10529 }
10530
10531 fn activate_diagnostics(
10532 &mut self,
10533 buffer_id: BufferId,
10534 group_id: usize,
10535 cx: &mut ViewContext<Self>,
10536 ) {
10537 self.dismiss_diagnostics(cx);
10538 let snapshot = self.snapshot(cx);
10539 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
10540 let buffer = self.buffer.read(cx).snapshot(cx);
10541
10542 let mut primary_range = None;
10543 let mut primary_message = None;
10544 let diagnostic_group = buffer
10545 .diagnostic_group(buffer_id, group_id)
10546 .filter_map(|entry| {
10547 let start = entry.range.start;
10548 let end = entry.range.end;
10549 if snapshot.is_line_folded(MultiBufferRow(start.row))
10550 && (start.row == end.row
10551 || snapshot.is_line_folded(MultiBufferRow(end.row)))
10552 {
10553 return None;
10554 }
10555 if entry.diagnostic.is_primary {
10556 primary_range = Some(entry.range.clone());
10557 primary_message = Some(entry.diagnostic.message.clone());
10558 }
10559 Some(entry)
10560 })
10561 .collect::<Vec<_>>();
10562 let primary_range = primary_range?;
10563 let primary_message = primary_message?;
10564
10565 let blocks = display_map
10566 .insert_blocks(
10567 diagnostic_group.iter().map(|entry| {
10568 let diagnostic = entry.diagnostic.clone();
10569 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
10570 BlockProperties {
10571 style: BlockStyle::Fixed,
10572 placement: BlockPlacement::Below(
10573 buffer.anchor_after(entry.range.start),
10574 ),
10575 height: message_height,
10576 render: diagnostic_block_renderer(diagnostic, None, true, true),
10577 priority: 0,
10578 }
10579 }),
10580 cx,
10581 )
10582 .into_iter()
10583 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
10584 .collect();
10585
10586 Some(ActiveDiagnosticGroup {
10587 primary_range: buffer.anchor_before(primary_range.start)
10588 ..buffer.anchor_after(primary_range.end),
10589 primary_message,
10590 group_id,
10591 blocks,
10592 is_valid: true,
10593 })
10594 });
10595 }
10596
10597 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
10598 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
10599 self.display_map.update(cx, |display_map, cx| {
10600 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
10601 });
10602 cx.notify();
10603 }
10604 }
10605
10606 pub fn set_selections_from_remote(
10607 &mut self,
10608 selections: Vec<Selection<Anchor>>,
10609 pending_selection: Option<Selection<Anchor>>,
10610 cx: &mut ViewContext<Self>,
10611 ) {
10612 let old_cursor_position = self.selections.newest_anchor().head();
10613 self.selections.change_with(cx, |s| {
10614 s.select_anchors(selections);
10615 if let Some(pending_selection) = pending_selection {
10616 s.set_pending(pending_selection, SelectMode::Character);
10617 } else {
10618 s.clear_pending();
10619 }
10620 });
10621 self.selections_did_change(false, &old_cursor_position, true, cx);
10622 }
10623
10624 fn push_to_selection_history(&mut self) {
10625 self.selection_history.push(SelectionHistoryEntry {
10626 selections: self.selections.disjoint_anchors(),
10627 select_next_state: self.select_next_state.clone(),
10628 select_prev_state: self.select_prev_state.clone(),
10629 add_selections_state: self.add_selections_state.clone(),
10630 });
10631 }
10632
10633 pub fn transact(
10634 &mut self,
10635 cx: &mut ViewContext<Self>,
10636 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
10637 ) -> Option<TransactionId> {
10638 self.start_transaction_at(Instant::now(), cx);
10639 update(self, cx);
10640 self.end_transaction_at(Instant::now(), cx)
10641 }
10642
10643 pub fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
10644 self.end_selection(cx);
10645 if let Some(tx_id) = self
10646 .buffer
10647 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
10648 {
10649 self.selection_history
10650 .insert_transaction(tx_id, self.selections.disjoint_anchors());
10651 cx.emit(EditorEvent::TransactionBegun {
10652 transaction_id: tx_id,
10653 })
10654 }
10655 }
10656
10657 pub fn end_transaction_at(
10658 &mut self,
10659 now: Instant,
10660 cx: &mut ViewContext<Self>,
10661 ) -> Option<TransactionId> {
10662 if let Some(transaction_id) = self
10663 .buffer
10664 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
10665 {
10666 if let Some((_, end_selections)) =
10667 self.selection_history.transaction_mut(transaction_id)
10668 {
10669 *end_selections = Some(self.selections.disjoint_anchors());
10670 } else {
10671 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
10672 }
10673
10674 cx.emit(EditorEvent::Edited { transaction_id });
10675 Some(transaction_id)
10676 } else {
10677 None
10678 }
10679 }
10680
10681 pub fn set_mark(&mut self, _: &actions::SetMark, cx: &mut ViewContext<Self>) {
10682 if self.selection_mark_mode {
10683 self.change_selections(None, cx, |s| {
10684 s.move_with(|_, sel| {
10685 sel.collapse_to(sel.head(), SelectionGoal::None);
10686 });
10687 })
10688 }
10689 self.selection_mark_mode = true;
10690 cx.notify();
10691 }
10692
10693 pub fn swap_selection_ends(
10694 &mut self,
10695 _: &actions::SwapSelectionEnds,
10696 cx: &mut ViewContext<Self>,
10697 ) {
10698 self.change_selections(None, cx, |s| {
10699 s.move_with(|_, sel| {
10700 if sel.start != sel.end {
10701 sel.reversed = !sel.reversed
10702 }
10703 });
10704 });
10705 self.request_autoscroll(Autoscroll::newest(), cx);
10706 cx.notify();
10707 }
10708
10709 pub fn toggle_fold(&mut self, _: &actions::ToggleFold, cx: &mut ViewContext<Self>) {
10710 if self.is_singleton(cx) {
10711 let selection = self.selections.newest::<Point>(cx);
10712
10713 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10714 let range = if selection.is_empty() {
10715 let point = selection.head().to_display_point(&display_map);
10716 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
10717 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
10718 .to_point(&display_map);
10719 start..end
10720 } else {
10721 selection.range()
10722 };
10723 if display_map.folds_in_range(range).next().is_some() {
10724 self.unfold_lines(&Default::default(), cx)
10725 } else {
10726 self.fold(&Default::default(), cx)
10727 }
10728 } else {
10729 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
10730 let buffer_ids: HashSet<_> = multi_buffer_snapshot
10731 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
10732 .map(|(snapshot, _, _)| snapshot.remote_id())
10733 .collect();
10734
10735 for buffer_id in buffer_ids {
10736 if self.is_buffer_folded(buffer_id, cx) {
10737 self.unfold_buffer(buffer_id, cx);
10738 } else {
10739 self.fold_buffer(buffer_id, cx);
10740 }
10741 }
10742 }
10743 }
10744
10745 pub fn toggle_fold_recursive(
10746 &mut self,
10747 _: &actions::ToggleFoldRecursive,
10748 cx: &mut ViewContext<Self>,
10749 ) {
10750 let selection = self.selections.newest::<Point>(cx);
10751
10752 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10753 let range = if selection.is_empty() {
10754 let point = selection.head().to_display_point(&display_map);
10755 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
10756 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
10757 .to_point(&display_map);
10758 start..end
10759 } else {
10760 selection.range()
10761 };
10762 if display_map.folds_in_range(range).next().is_some() {
10763 self.unfold_recursive(&Default::default(), cx)
10764 } else {
10765 self.fold_recursive(&Default::default(), cx)
10766 }
10767 }
10768
10769 pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
10770 if self.is_singleton(cx) {
10771 let mut to_fold = Vec::new();
10772 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10773 let selections = self.selections.all_adjusted(cx);
10774
10775 for selection in selections {
10776 let range = selection.range().sorted();
10777 let buffer_start_row = range.start.row;
10778
10779 if range.start.row != range.end.row {
10780 let mut found = false;
10781 let mut row = range.start.row;
10782 while row <= range.end.row {
10783 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
10784 {
10785 found = true;
10786 row = crease.range().end.row + 1;
10787 to_fold.push(crease);
10788 } else {
10789 row += 1
10790 }
10791 }
10792 if found {
10793 continue;
10794 }
10795 }
10796
10797 for row in (0..=range.start.row).rev() {
10798 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
10799 if crease.range().end.row >= buffer_start_row {
10800 to_fold.push(crease);
10801 if row <= range.start.row {
10802 break;
10803 }
10804 }
10805 }
10806 }
10807 }
10808
10809 self.fold_creases(to_fold, true, cx);
10810 } else {
10811 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
10812
10813 let buffer_ids: HashSet<_> = multi_buffer_snapshot
10814 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
10815 .map(|(snapshot, _, _)| snapshot.remote_id())
10816 .collect();
10817 for buffer_id in buffer_ids {
10818 self.fold_buffer(buffer_id, cx);
10819 }
10820 }
10821 }
10822
10823 fn fold_at_level(&mut self, fold_at: &FoldAtLevel, cx: &mut ViewContext<Self>) {
10824 if !self.buffer.read(cx).is_singleton() {
10825 return;
10826 }
10827
10828 let fold_at_level = fold_at.level;
10829 let snapshot = self.buffer.read(cx).snapshot(cx);
10830 let mut to_fold = Vec::new();
10831 let mut stack = vec![(0, snapshot.max_row().0, 1)];
10832
10833 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
10834 while start_row < end_row {
10835 match self
10836 .snapshot(cx)
10837 .crease_for_buffer_row(MultiBufferRow(start_row))
10838 {
10839 Some(crease) => {
10840 let nested_start_row = crease.range().start.row + 1;
10841 let nested_end_row = crease.range().end.row;
10842
10843 if current_level < fold_at_level {
10844 stack.push((nested_start_row, nested_end_row, current_level + 1));
10845 } else if current_level == fold_at_level {
10846 to_fold.push(crease);
10847 }
10848
10849 start_row = nested_end_row + 1;
10850 }
10851 None => start_row += 1,
10852 }
10853 }
10854 }
10855
10856 self.fold_creases(to_fold, true, cx);
10857 }
10858
10859 pub fn fold_all(&mut self, _: &actions::FoldAll, cx: &mut ViewContext<Self>) {
10860 if self.buffer.read(cx).is_singleton() {
10861 let mut fold_ranges = Vec::new();
10862 let snapshot = self.buffer.read(cx).snapshot(cx);
10863
10864 for row in 0..snapshot.max_row().0 {
10865 if let Some(foldable_range) =
10866 self.snapshot(cx).crease_for_buffer_row(MultiBufferRow(row))
10867 {
10868 fold_ranges.push(foldable_range);
10869 }
10870 }
10871
10872 self.fold_creases(fold_ranges, true, cx);
10873 } else {
10874 self.toggle_fold_multiple_buffers = cx.spawn(|editor, mut cx| async move {
10875 editor
10876 .update(&mut cx, |editor, cx| {
10877 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
10878 editor.fold_buffer(buffer_id, cx);
10879 }
10880 })
10881 .ok();
10882 });
10883 }
10884 }
10885
10886 pub fn fold_function_bodies(
10887 &mut self,
10888 _: &actions::FoldFunctionBodies,
10889 cx: &mut ViewContext<Self>,
10890 ) {
10891 let snapshot = self.buffer.read(cx).snapshot(cx);
10892
10893 let ranges = snapshot
10894 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
10895 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
10896 .collect::<Vec<_>>();
10897
10898 let creases = ranges
10899 .into_iter()
10900 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
10901 .collect();
10902
10903 self.fold_creases(creases, true, cx);
10904 }
10905
10906 pub fn fold_recursive(&mut self, _: &actions::FoldRecursive, cx: &mut ViewContext<Self>) {
10907 let mut to_fold = Vec::new();
10908 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10909 let selections = self.selections.all_adjusted(cx);
10910
10911 for selection in selections {
10912 let range = selection.range().sorted();
10913 let buffer_start_row = range.start.row;
10914
10915 if range.start.row != range.end.row {
10916 let mut found = false;
10917 for row in range.start.row..=range.end.row {
10918 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
10919 found = true;
10920 to_fold.push(crease);
10921 }
10922 }
10923 if found {
10924 continue;
10925 }
10926 }
10927
10928 for row in (0..=range.start.row).rev() {
10929 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
10930 if crease.range().end.row >= buffer_start_row {
10931 to_fold.push(crease);
10932 } else {
10933 break;
10934 }
10935 }
10936 }
10937 }
10938
10939 self.fold_creases(to_fold, true, cx);
10940 }
10941
10942 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
10943 let buffer_row = fold_at.buffer_row;
10944 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10945
10946 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
10947 let autoscroll = self
10948 .selections
10949 .all::<Point>(cx)
10950 .iter()
10951 .any(|selection| crease.range().overlaps(&selection.range()));
10952
10953 self.fold_creases(vec![crease], autoscroll, cx);
10954 }
10955 }
10956
10957 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
10958 if self.is_singleton(cx) {
10959 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10960 let buffer = &display_map.buffer_snapshot;
10961 let selections = self.selections.all::<Point>(cx);
10962 let ranges = selections
10963 .iter()
10964 .map(|s| {
10965 let range = s.display_range(&display_map).sorted();
10966 let mut start = range.start.to_point(&display_map);
10967 let mut end = range.end.to_point(&display_map);
10968 start.column = 0;
10969 end.column = buffer.line_len(MultiBufferRow(end.row));
10970 start..end
10971 })
10972 .collect::<Vec<_>>();
10973
10974 self.unfold_ranges(&ranges, true, true, cx);
10975 } else {
10976 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
10977 let buffer_ids: HashSet<_> = multi_buffer_snapshot
10978 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
10979 .map(|(snapshot, _, _)| snapshot.remote_id())
10980 .collect();
10981 for buffer_id in buffer_ids {
10982 self.unfold_buffer(buffer_id, cx);
10983 }
10984 }
10985 }
10986
10987 pub fn unfold_recursive(&mut self, _: &UnfoldRecursive, cx: &mut ViewContext<Self>) {
10988 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10989 let selections = self.selections.all::<Point>(cx);
10990 let ranges = selections
10991 .iter()
10992 .map(|s| {
10993 let mut range = s.display_range(&display_map).sorted();
10994 *range.start.column_mut() = 0;
10995 *range.end.column_mut() = display_map.line_len(range.end.row());
10996 let start = range.start.to_point(&display_map);
10997 let end = range.end.to_point(&display_map);
10998 start..end
10999 })
11000 .collect::<Vec<_>>();
11001
11002 self.unfold_ranges(&ranges, true, true, cx);
11003 }
11004
11005 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
11006 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11007
11008 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
11009 ..Point::new(
11010 unfold_at.buffer_row.0,
11011 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
11012 );
11013
11014 let autoscroll = self
11015 .selections
11016 .all::<Point>(cx)
11017 .iter()
11018 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
11019
11020 self.unfold_ranges(&[intersection_range], true, autoscroll, cx)
11021 }
11022
11023 pub fn unfold_all(&mut self, _: &actions::UnfoldAll, cx: &mut ViewContext<Self>) {
11024 if self.buffer.read(cx).is_singleton() {
11025 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11026 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
11027 } else {
11028 self.toggle_fold_multiple_buffers = cx.spawn(|editor, mut cx| async move {
11029 editor
11030 .update(&mut cx, |editor, cx| {
11031 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
11032 editor.unfold_buffer(buffer_id, cx);
11033 }
11034 })
11035 .ok();
11036 });
11037 }
11038 }
11039
11040 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
11041 let selections = self.selections.all::<Point>(cx);
11042 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11043 let line_mode = self.selections.line_mode;
11044 let ranges = selections
11045 .into_iter()
11046 .map(|s| {
11047 if line_mode {
11048 let start = Point::new(s.start.row, 0);
11049 let end = Point::new(
11050 s.end.row,
11051 display_map
11052 .buffer_snapshot
11053 .line_len(MultiBufferRow(s.end.row)),
11054 );
11055 Crease::simple(start..end, display_map.fold_placeholder.clone())
11056 } else {
11057 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
11058 }
11059 })
11060 .collect::<Vec<_>>();
11061 self.fold_creases(ranges, true, cx);
11062 }
11063
11064 pub fn fold_ranges<T: ToOffset + Clone>(
11065 &mut self,
11066 ranges: Vec<Range<T>>,
11067 auto_scroll: bool,
11068 cx: &mut ViewContext<Self>,
11069 ) {
11070 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11071 let ranges = ranges
11072 .into_iter()
11073 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
11074 .collect::<Vec<_>>();
11075 self.fold_creases(ranges, auto_scroll, cx);
11076 }
11077
11078 pub fn fold_creases<T: ToOffset + Clone>(
11079 &mut self,
11080 creases: Vec<Crease<T>>,
11081 auto_scroll: bool,
11082 cx: &mut ViewContext<Self>,
11083 ) {
11084 if creases.is_empty() {
11085 return;
11086 }
11087
11088 let mut buffers_affected = HashSet::default();
11089 let multi_buffer = self.buffer().read(cx);
11090 for crease in &creases {
11091 if let Some((_, buffer, _)) =
11092 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
11093 {
11094 buffers_affected.insert(buffer.read(cx).remote_id());
11095 };
11096 }
11097
11098 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
11099
11100 if auto_scroll {
11101 self.request_autoscroll(Autoscroll::fit(), cx);
11102 }
11103
11104 cx.notify();
11105
11106 if let Some(active_diagnostics) = self.active_diagnostics.take() {
11107 // Clear diagnostics block when folding a range that contains it.
11108 let snapshot = self.snapshot(cx);
11109 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
11110 drop(snapshot);
11111 self.active_diagnostics = Some(active_diagnostics);
11112 self.dismiss_diagnostics(cx);
11113 } else {
11114 self.active_diagnostics = Some(active_diagnostics);
11115 }
11116 }
11117
11118 self.scrollbar_marker_state.dirty = true;
11119 }
11120
11121 /// Removes any folds whose ranges intersect any of the given ranges.
11122 pub fn unfold_ranges<T: ToOffset + Clone>(
11123 &mut self,
11124 ranges: &[Range<T>],
11125 inclusive: bool,
11126 auto_scroll: bool,
11127 cx: &mut ViewContext<Self>,
11128 ) {
11129 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
11130 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
11131 });
11132 }
11133
11134 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut ViewContext<Self>) {
11135 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
11136 return;
11137 }
11138 let Some(buffer) = self.buffer().read(cx).buffer(buffer_id) else {
11139 return;
11140 };
11141 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(&buffer, cx);
11142 self.display_map
11143 .update(cx, |display_map, cx| display_map.fold_buffer(buffer_id, cx));
11144 cx.emit(EditorEvent::BufferFoldToggled {
11145 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
11146 folded: true,
11147 });
11148 cx.notify();
11149 }
11150
11151 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut ViewContext<Self>) {
11152 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
11153 return;
11154 }
11155 let Some(buffer) = self.buffer().read(cx).buffer(buffer_id) else {
11156 return;
11157 };
11158 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(&buffer, cx);
11159 self.display_map.update(cx, |display_map, cx| {
11160 display_map.unfold_buffer(buffer_id, cx);
11161 });
11162 cx.emit(EditorEvent::BufferFoldToggled {
11163 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
11164 folded: false,
11165 });
11166 cx.notify();
11167 }
11168
11169 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &AppContext) -> bool {
11170 self.display_map.read(cx).is_buffer_folded(buffer)
11171 }
11172
11173 pub fn folded_buffers<'a>(&self, cx: &'a AppContext) -> &'a HashSet<BufferId> {
11174 self.display_map.read(cx).folded_buffers()
11175 }
11176
11177 /// Removes any folds with the given ranges.
11178 pub fn remove_folds_with_type<T: ToOffset + Clone>(
11179 &mut self,
11180 ranges: &[Range<T>],
11181 type_id: TypeId,
11182 auto_scroll: bool,
11183 cx: &mut ViewContext<Self>,
11184 ) {
11185 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
11186 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
11187 });
11188 }
11189
11190 fn remove_folds_with<T: ToOffset + Clone>(
11191 &mut self,
11192 ranges: &[Range<T>],
11193 auto_scroll: bool,
11194 cx: &mut ViewContext<Self>,
11195 update: impl FnOnce(&mut DisplayMap, &mut ModelContext<DisplayMap>),
11196 ) {
11197 if ranges.is_empty() {
11198 return;
11199 }
11200
11201 let mut buffers_affected = HashSet::default();
11202 let multi_buffer = self.buffer().read(cx);
11203 for range in ranges {
11204 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
11205 buffers_affected.insert(buffer.read(cx).remote_id());
11206 };
11207 }
11208
11209 self.display_map.update(cx, update);
11210
11211 if auto_scroll {
11212 self.request_autoscroll(Autoscroll::fit(), cx);
11213 }
11214
11215 cx.notify();
11216 self.scrollbar_marker_state.dirty = true;
11217 self.active_indent_guides_state.dirty = true;
11218 }
11219
11220 pub fn default_fold_placeholder(&self, cx: &AppContext) -> FoldPlaceholder {
11221 self.display_map.read(cx).fold_placeholder.clone()
11222 }
11223
11224 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut AppContext) {
11225 self.buffer.update(cx, |buffer, cx| {
11226 buffer.set_all_diff_hunks_expanded(cx);
11227 });
11228 }
11229
11230 pub fn expand_all_diff_hunks(&mut self, _: &ExpandAllHunkDiffs, cx: &mut ViewContext<Self>) {
11231 self.buffer.update(cx, |buffer, cx| {
11232 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
11233 });
11234 }
11235
11236 pub fn toggle_selected_diff_hunks(
11237 &mut self,
11238 _: &ToggleSelectedDiffHunks,
11239 cx: &mut ViewContext<Self>,
11240 ) {
11241 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
11242 self.toggle_diff_hunks_in_ranges(ranges, cx);
11243 }
11244
11245 pub fn expand_selected_diff_hunks(&mut self, cx: &mut ViewContext<Self>) {
11246 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
11247 self.buffer
11248 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
11249 }
11250
11251 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut ViewContext<Self>) -> bool {
11252 self.buffer.update(cx, |buffer, cx| {
11253 let ranges = vec![Anchor::min()..Anchor::max()];
11254 if !buffer.all_diff_hunks_expanded()
11255 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
11256 {
11257 buffer.collapse_diff_hunks(ranges, cx);
11258 true
11259 } else {
11260 false
11261 }
11262 })
11263 }
11264
11265 fn toggle_diff_hunks_in_ranges(
11266 &mut self,
11267 ranges: Vec<Range<Anchor>>,
11268 cx: &mut ViewContext<'_, Editor>,
11269 ) {
11270 self.buffer.update(cx, |buffer, cx| {
11271 if buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx) {
11272 buffer.collapse_diff_hunks(ranges, cx)
11273 } else {
11274 buffer.expand_diff_hunks(ranges, cx)
11275 }
11276 })
11277 }
11278
11279 pub(crate) fn apply_all_diff_hunks(
11280 &mut self,
11281 _: &ApplyAllDiffHunks,
11282 cx: &mut ViewContext<Self>,
11283 ) {
11284 let buffers = self.buffer.read(cx).all_buffers();
11285 for branch_buffer in buffers {
11286 branch_buffer.update(cx, |branch_buffer, cx| {
11287 branch_buffer.merge_into_base(Vec::new(), cx);
11288 });
11289 }
11290
11291 if let Some(project) = self.project.clone() {
11292 self.save(true, project, cx).detach_and_log_err(cx);
11293 }
11294 }
11295
11296 pub(crate) fn apply_selected_diff_hunks(
11297 &mut self,
11298 _: &ApplyDiffHunk,
11299 cx: &mut ViewContext<Self>,
11300 ) {
11301 let snapshot = self.snapshot(cx);
11302 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx).into_iter());
11303 let mut ranges_by_buffer = HashMap::default();
11304 self.transact(cx, |editor, cx| {
11305 for hunk in hunks {
11306 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
11307 ranges_by_buffer
11308 .entry(buffer.clone())
11309 .or_insert_with(Vec::new)
11310 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
11311 }
11312 }
11313
11314 for (buffer, ranges) in ranges_by_buffer {
11315 buffer.update(cx, |buffer, cx| {
11316 buffer.merge_into_base(ranges, cx);
11317 });
11318 }
11319 });
11320
11321 if let Some(project) = self.project.clone() {
11322 self.save(true, project, cx).detach_and_log_err(cx);
11323 }
11324 }
11325
11326 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
11327 if hovered != self.gutter_hovered {
11328 self.gutter_hovered = hovered;
11329 cx.notify();
11330 }
11331 }
11332
11333 pub fn insert_blocks(
11334 &mut self,
11335 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
11336 autoscroll: Option<Autoscroll>,
11337 cx: &mut ViewContext<Self>,
11338 ) -> Vec<CustomBlockId> {
11339 let blocks = self
11340 .display_map
11341 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
11342 if let Some(autoscroll) = autoscroll {
11343 self.request_autoscroll(autoscroll, cx);
11344 }
11345 cx.notify();
11346 blocks
11347 }
11348
11349 pub fn resize_blocks(
11350 &mut self,
11351 heights: HashMap<CustomBlockId, u32>,
11352 autoscroll: Option<Autoscroll>,
11353 cx: &mut ViewContext<Self>,
11354 ) {
11355 self.display_map
11356 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
11357 if let Some(autoscroll) = autoscroll {
11358 self.request_autoscroll(autoscroll, cx);
11359 }
11360 cx.notify();
11361 }
11362
11363 pub fn replace_blocks(
11364 &mut self,
11365 renderers: HashMap<CustomBlockId, RenderBlock>,
11366 autoscroll: Option<Autoscroll>,
11367 cx: &mut ViewContext<Self>,
11368 ) {
11369 self.display_map
11370 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
11371 if let Some(autoscroll) = autoscroll {
11372 self.request_autoscroll(autoscroll, cx);
11373 }
11374 cx.notify();
11375 }
11376
11377 pub fn remove_blocks(
11378 &mut self,
11379 block_ids: HashSet<CustomBlockId>,
11380 autoscroll: Option<Autoscroll>,
11381 cx: &mut ViewContext<Self>,
11382 ) {
11383 self.display_map.update(cx, |display_map, cx| {
11384 display_map.remove_blocks(block_ids, cx)
11385 });
11386 if let Some(autoscroll) = autoscroll {
11387 self.request_autoscroll(autoscroll, cx);
11388 }
11389 cx.notify();
11390 }
11391
11392 pub fn row_for_block(
11393 &self,
11394 block_id: CustomBlockId,
11395 cx: &mut ViewContext<Self>,
11396 ) -> Option<DisplayRow> {
11397 self.display_map
11398 .update(cx, |map, cx| map.row_for_block(block_id, cx))
11399 }
11400
11401 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
11402 self.focused_block = Some(focused_block);
11403 }
11404
11405 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
11406 self.focused_block.take()
11407 }
11408
11409 pub fn insert_creases(
11410 &mut self,
11411 creases: impl IntoIterator<Item = Crease<Anchor>>,
11412 cx: &mut ViewContext<Self>,
11413 ) -> Vec<CreaseId> {
11414 self.display_map
11415 .update(cx, |map, cx| map.insert_creases(creases, cx))
11416 }
11417
11418 pub fn remove_creases(
11419 &mut self,
11420 ids: impl IntoIterator<Item = CreaseId>,
11421 cx: &mut ViewContext<Self>,
11422 ) {
11423 self.display_map
11424 .update(cx, |map, cx| map.remove_creases(ids, cx));
11425 }
11426
11427 pub fn longest_row(&self, cx: &mut AppContext) -> DisplayRow {
11428 self.display_map
11429 .update(cx, |map, cx| map.snapshot(cx))
11430 .longest_row()
11431 }
11432
11433 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
11434 self.display_map
11435 .update(cx, |map, cx| map.snapshot(cx))
11436 .max_point()
11437 }
11438
11439 pub fn text(&self, cx: &AppContext) -> String {
11440 self.buffer.read(cx).read(cx).text()
11441 }
11442
11443 pub fn text_option(&self, cx: &AppContext) -> Option<String> {
11444 let text = self.text(cx);
11445 let text = text.trim();
11446
11447 if text.is_empty() {
11448 return None;
11449 }
11450
11451 Some(text.to_string())
11452 }
11453
11454 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
11455 self.transact(cx, |this, cx| {
11456 this.buffer
11457 .read(cx)
11458 .as_singleton()
11459 .expect("you can only call set_text on editors for singleton buffers")
11460 .update(cx, |buffer, cx| buffer.set_text(text, cx));
11461 });
11462 }
11463
11464 pub fn display_text(&self, cx: &mut AppContext) -> String {
11465 self.display_map
11466 .update(cx, |map, cx| map.snapshot(cx))
11467 .text()
11468 }
11469
11470 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
11471 let mut wrap_guides = smallvec::smallvec![];
11472
11473 if self.show_wrap_guides == Some(false) {
11474 return wrap_guides;
11475 }
11476
11477 let settings = self.buffer.read(cx).settings_at(0, cx);
11478 if settings.show_wrap_guides {
11479 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
11480 wrap_guides.push((soft_wrap as usize, true));
11481 } else if let SoftWrap::Bounded(soft_wrap) = self.soft_wrap_mode(cx) {
11482 wrap_guides.push((soft_wrap as usize, true));
11483 }
11484 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
11485 }
11486
11487 wrap_guides
11488 }
11489
11490 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
11491 let settings = self.buffer.read(cx).settings_at(0, cx);
11492 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
11493 match mode {
11494 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
11495 SoftWrap::None
11496 }
11497 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
11498 language_settings::SoftWrap::PreferredLineLength => {
11499 SoftWrap::Column(settings.preferred_line_length)
11500 }
11501 language_settings::SoftWrap::Bounded => {
11502 SoftWrap::Bounded(settings.preferred_line_length)
11503 }
11504 }
11505 }
11506
11507 pub fn set_soft_wrap_mode(
11508 &mut self,
11509 mode: language_settings::SoftWrap,
11510 cx: &mut ViewContext<Self>,
11511 ) {
11512 self.soft_wrap_mode_override = Some(mode);
11513 cx.notify();
11514 }
11515
11516 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
11517 self.text_style_refinement = Some(style);
11518 }
11519
11520 /// called by the Element so we know what style we were most recently rendered with.
11521 pub(crate) fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
11522 let rem_size = cx.rem_size();
11523 self.display_map.update(cx, |map, cx| {
11524 map.set_font(
11525 style.text.font(),
11526 style.text.font_size.to_pixels(rem_size),
11527 cx,
11528 )
11529 });
11530 self.style = Some(style);
11531 }
11532
11533 pub fn style(&self) -> Option<&EditorStyle> {
11534 self.style.as_ref()
11535 }
11536
11537 // Called by the element. This method is not designed to be called outside of the editor
11538 // element's layout code because it does not notify when rewrapping is computed synchronously.
11539 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
11540 self.display_map
11541 .update(cx, |map, cx| map.set_wrap_width(width, cx))
11542 }
11543
11544 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
11545 if self.soft_wrap_mode_override.is_some() {
11546 self.soft_wrap_mode_override.take();
11547 } else {
11548 let soft_wrap = match self.soft_wrap_mode(cx) {
11549 SoftWrap::GitDiff => return,
11550 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
11551 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
11552 language_settings::SoftWrap::None
11553 }
11554 };
11555 self.soft_wrap_mode_override = Some(soft_wrap);
11556 }
11557 cx.notify();
11558 }
11559
11560 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, cx: &mut ViewContext<Self>) {
11561 let Some(workspace) = self.workspace() else {
11562 return;
11563 };
11564 let fs = workspace.read(cx).app_state().fs.clone();
11565 let current_show = TabBarSettings::get_global(cx).show;
11566 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
11567 setting.show = Some(!current_show);
11568 });
11569 }
11570
11571 pub fn toggle_indent_guides(&mut self, _: &ToggleIndentGuides, cx: &mut ViewContext<Self>) {
11572 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
11573 self.buffer
11574 .read(cx)
11575 .settings_at(0, cx)
11576 .indent_guides
11577 .enabled
11578 });
11579 self.show_indent_guides = Some(!currently_enabled);
11580 cx.notify();
11581 }
11582
11583 fn should_show_indent_guides(&self) -> Option<bool> {
11584 self.show_indent_guides
11585 }
11586
11587 pub fn toggle_line_numbers(&mut self, _: &ToggleLineNumbers, cx: &mut ViewContext<Self>) {
11588 let mut editor_settings = EditorSettings::get_global(cx).clone();
11589 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
11590 EditorSettings::override_global(editor_settings, cx);
11591 }
11592
11593 pub fn should_use_relative_line_numbers(&self, cx: &WindowContext) -> bool {
11594 self.use_relative_line_numbers
11595 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
11596 }
11597
11598 pub fn toggle_relative_line_numbers(
11599 &mut self,
11600 _: &ToggleRelativeLineNumbers,
11601 cx: &mut ViewContext<Self>,
11602 ) {
11603 let is_relative = self.should_use_relative_line_numbers(cx);
11604 self.set_relative_line_number(Some(!is_relative), cx)
11605 }
11606
11607 pub fn set_relative_line_number(
11608 &mut self,
11609 is_relative: Option<bool>,
11610 cx: &mut ViewContext<Self>,
11611 ) {
11612 self.use_relative_line_numbers = is_relative;
11613 cx.notify();
11614 }
11615
11616 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
11617 self.show_gutter = show_gutter;
11618 cx.notify();
11619 }
11620
11621 pub fn set_show_scrollbars(&mut self, show_scrollbars: bool, cx: &mut ViewContext<Self>) {
11622 self.show_scrollbars = show_scrollbars;
11623 cx.notify();
11624 }
11625
11626 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut ViewContext<Self>) {
11627 self.show_line_numbers = Some(show_line_numbers);
11628 cx.notify();
11629 }
11630
11631 pub fn set_show_git_diff_gutter(
11632 &mut self,
11633 show_git_diff_gutter: bool,
11634 cx: &mut ViewContext<Self>,
11635 ) {
11636 self.show_git_diff_gutter = Some(show_git_diff_gutter);
11637 cx.notify();
11638 }
11639
11640 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut ViewContext<Self>) {
11641 self.show_code_actions = Some(show_code_actions);
11642 cx.notify();
11643 }
11644
11645 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut ViewContext<Self>) {
11646 self.show_runnables = Some(show_runnables);
11647 cx.notify();
11648 }
11649
11650 pub fn set_masked(&mut self, masked: bool, cx: &mut ViewContext<Self>) {
11651 if self.display_map.read(cx).masked != masked {
11652 self.display_map.update(cx, |map, _| map.masked = masked);
11653 }
11654 cx.notify()
11655 }
11656
11657 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut ViewContext<Self>) {
11658 self.show_wrap_guides = Some(show_wrap_guides);
11659 cx.notify();
11660 }
11661
11662 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut ViewContext<Self>) {
11663 self.show_indent_guides = Some(show_indent_guides);
11664 cx.notify();
11665 }
11666
11667 pub fn working_directory(&self, cx: &WindowContext) -> Option<PathBuf> {
11668 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
11669 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
11670 if let Some(dir) = file.abs_path(cx).parent() {
11671 return Some(dir.to_owned());
11672 }
11673 }
11674
11675 if let Some(project_path) = buffer.read(cx).project_path(cx) {
11676 return Some(project_path.path.to_path_buf());
11677 }
11678 }
11679
11680 None
11681 }
11682
11683 fn target_file<'a>(&self, cx: &'a AppContext) -> Option<&'a dyn language::LocalFile> {
11684 self.active_excerpt(cx)?
11685 .1
11686 .read(cx)
11687 .file()
11688 .and_then(|f| f.as_local())
11689 }
11690
11691 fn target_file_abs_path(&self, cx: &mut ViewContext<Self>) -> Option<PathBuf> {
11692 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
11693 let project_path = buffer.read(cx).project_path(cx)?;
11694 let project = self.project.as_ref()?.read(cx);
11695 project.absolute_path(&project_path, cx)
11696 })
11697 }
11698
11699 fn target_file_path(&self, cx: &mut ViewContext<Self>) -> Option<PathBuf> {
11700 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
11701 let project_path = buffer.read(cx).project_path(cx)?;
11702 let project = self.project.as_ref()?.read(cx);
11703 let entry = project.entry_for_path(&project_path, cx)?;
11704 let path = entry.path.to_path_buf();
11705 Some(path)
11706 })
11707 }
11708
11709 pub fn reveal_in_finder(&mut self, _: &RevealInFileManager, cx: &mut ViewContext<Self>) {
11710 if let Some(target) = self.target_file(cx) {
11711 cx.reveal_path(&target.abs_path(cx));
11712 }
11713 }
11714
11715 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
11716 if let Some(path) = self.target_file_abs_path(cx) {
11717 if let Some(path) = path.to_str() {
11718 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
11719 }
11720 }
11721 }
11722
11723 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
11724 if let Some(path) = self.target_file_path(cx) {
11725 if let Some(path) = path.to_str() {
11726 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
11727 }
11728 }
11729 }
11730
11731 pub fn toggle_git_blame(&mut self, _: &ToggleGitBlame, cx: &mut ViewContext<Self>) {
11732 self.show_git_blame_gutter = !self.show_git_blame_gutter;
11733
11734 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
11735 self.start_git_blame(true, cx);
11736 }
11737
11738 cx.notify();
11739 }
11740
11741 pub fn toggle_git_blame_inline(
11742 &mut self,
11743 _: &ToggleGitBlameInline,
11744 cx: &mut ViewContext<Self>,
11745 ) {
11746 self.toggle_git_blame_inline_internal(true, cx);
11747 cx.notify();
11748 }
11749
11750 pub fn git_blame_inline_enabled(&self) -> bool {
11751 self.git_blame_inline_enabled
11752 }
11753
11754 pub fn toggle_selection_menu(&mut self, _: &ToggleSelectionMenu, cx: &mut ViewContext<Self>) {
11755 self.show_selection_menu = self
11756 .show_selection_menu
11757 .map(|show_selections_menu| !show_selections_menu)
11758 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
11759
11760 cx.notify();
11761 }
11762
11763 pub fn selection_menu_enabled(&self, cx: &AppContext) -> bool {
11764 self.show_selection_menu
11765 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
11766 }
11767
11768 fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
11769 if let Some(project) = self.project.as_ref() {
11770 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
11771 return;
11772 };
11773
11774 if buffer.read(cx).file().is_none() {
11775 return;
11776 }
11777
11778 let focused = self.focus_handle(cx).contains_focused(cx);
11779
11780 let project = project.clone();
11781 let blame =
11782 cx.new_model(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
11783 self.blame_subscription = Some(cx.observe(&blame, |_, _, cx| cx.notify()));
11784 self.blame = Some(blame);
11785 }
11786 }
11787
11788 fn toggle_git_blame_inline_internal(
11789 &mut self,
11790 user_triggered: bool,
11791 cx: &mut ViewContext<Self>,
11792 ) {
11793 if self.git_blame_inline_enabled {
11794 self.git_blame_inline_enabled = false;
11795 self.show_git_blame_inline = false;
11796 self.show_git_blame_inline_delay_task.take();
11797 } else {
11798 self.git_blame_inline_enabled = true;
11799 self.start_git_blame_inline(user_triggered, cx);
11800 }
11801
11802 cx.notify();
11803 }
11804
11805 fn start_git_blame_inline(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
11806 self.start_git_blame(user_triggered, cx);
11807
11808 if ProjectSettings::get_global(cx)
11809 .git
11810 .inline_blame_delay()
11811 .is_some()
11812 {
11813 self.start_inline_blame_timer(cx);
11814 } else {
11815 self.show_git_blame_inline = true
11816 }
11817 }
11818
11819 pub fn blame(&self) -> Option<&Model<GitBlame>> {
11820 self.blame.as_ref()
11821 }
11822
11823 pub fn show_git_blame_gutter(&self) -> bool {
11824 self.show_git_blame_gutter
11825 }
11826
11827 pub fn render_git_blame_gutter(&self, cx: &WindowContext) -> bool {
11828 self.show_git_blame_gutter && self.has_blame_entries(cx)
11829 }
11830
11831 pub fn render_git_blame_inline(&self, cx: &WindowContext) -> bool {
11832 self.show_git_blame_inline
11833 && self.focus_handle.is_focused(cx)
11834 && !self.newest_selection_head_on_empty_line(cx)
11835 && self.has_blame_entries(cx)
11836 }
11837
11838 fn has_blame_entries(&self, cx: &WindowContext) -> bool {
11839 self.blame()
11840 .map_or(false, |blame| blame.read(cx).has_generated_entries())
11841 }
11842
11843 fn newest_selection_head_on_empty_line(&self, cx: &WindowContext) -> bool {
11844 let cursor_anchor = self.selections.newest_anchor().head();
11845
11846 let snapshot = self.buffer.read(cx).snapshot(cx);
11847 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
11848
11849 snapshot.line_len(buffer_row) == 0
11850 }
11851
11852 fn get_permalink_to_line(&self, cx: &mut ViewContext<Self>) -> Task<Result<url::Url>> {
11853 let buffer_and_selection = maybe!({
11854 let selection = self.selections.newest::<Point>(cx);
11855 let selection_range = selection.range();
11856
11857 let multi_buffer = self.buffer().read(cx);
11858 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
11859 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
11860
11861 let (buffer, range, _) = if selection.reversed {
11862 buffer_ranges.first()
11863 } else {
11864 buffer_ranges.last()
11865 }?;
11866
11867 let selection = text::ToPoint::to_point(&range.start, &buffer).row
11868 ..text::ToPoint::to_point(&range.end, &buffer).row;
11869 Some((
11870 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
11871 selection,
11872 ))
11873 });
11874
11875 let Some((buffer, selection)) = buffer_and_selection else {
11876 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
11877 };
11878
11879 let Some(project) = self.project.as_ref() else {
11880 return Task::ready(Err(anyhow!("editor does not have project")));
11881 };
11882
11883 project.update(cx, |project, cx| {
11884 project.get_permalink_to_line(&buffer, selection, cx)
11885 })
11886 }
11887
11888 pub fn copy_permalink_to_line(&mut self, _: &CopyPermalinkToLine, cx: &mut ViewContext<Self>) {
11889 let permalink_task = self.get_permalink_to_line(cx);
11890 let workspace = self.workspace();
11891
11892 cx.spawn(|_, mut cx| async move {
11893 match permalink_task.await {
11894 Ok(permalink) => {
11895 cx.update(|cx| {
11896 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
11897 })
11898 .ok();
11899 }
11900 Err(err) => {
11901 let message = format!("Failed to copy permalink: {err}");
11902
11903 Err::<(), anyhow::Error>(err).log_err();
11904
11905 if let Some(workspace) = workspace {
11906 workspace
11907 .update(&mut cx, |workspace, cx| {
11908 struct CopyPermalinkToLine;
11909
11910 workspace.show_toast(
11911 Toast::new(
11912 NotificationId::unique::<CopyPermalinkToLine>(),
11913 message,
11914 ),
11915 cx,
11916 )
11917 })
11918 .ok();
11919 }
11920 }
11921 }
11922 })
11923 .detach();
11924 }
11925
11926 pub fn copy_file_location(&mut self, _: &CopyFileLocation, cx: &mut ViewContext<Self>) {
11927 let selection = self.selections.newest::<Point>(cx).start.row + 1;
11928 if let Some(file) = self.target_file(cx) {
11929 if let Some(path) = file.path().to_str() {
11930 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
11931 }
11932 }
11933 }
11934
11935 pub fn open_permalink_to_line(&mut self, _: &OpenPermalinkToLine, cx: &mut ViewContext<Self>) {
11936 let permalink_task = self.get_permalink_to_line(cx);
11937 let workspace = self.workspace();
11938
11939 cx.spawn(|_, mut cx| async move {
11940 match permalink_task.await {
11941 Ok(permalink) => {
11942 cx.update(|cx| {
11943 cx.open_url(permalink.as_ref());
11944 })
11945 .ok();
11946 }
11947 Err(err) => {
11948 let message = format!("Failed to open permalink: {err}");
11949
11950 Err::<(), anyhow::Error>(err).log_err();
11951
11952 if let Some(workspace) = workspace {
11953 workspace
11954 .update(&mut cx, |workspace, cx| {
11955 struct OpenPermalinkToLine;
11956
11957 workspace.show_toast(
11958 Toast::new(
11959 NotificationId::unique::<OpenPermalinkToLine>(),
11960 message,
11961 ),
11962 cx,
11963 )
11964 })
11965 .ok();
11966 }
11967 }
11968 }
11969 })
11970 .detach();
11971 }
11972
11973 pub fn insert_uuid_v4(&mut self, _: &InsertUuidV4, cx: &mut ViewContext<Self>) {
11974 self.insert_uuid(UuidVersion::V4, cx);
11975 }
11976
11977 pub fn insert_uuid_v7(&mut self, _: &InsertUuidV7, cx: &mut ViewContext<Self>) {
11978 self.insert_uuid(UuidVersion::V7, cx);
11979 }
11980
11981 fn insert_uuid(&mut self, version: UuidVersion, cx: &mut ViewContext<Self>) {
11982 self.transact(cx, |this, cx| {
11983 let edits = this
11984 .selections
11985 .all::<Point>(cx)
11986 .into_iter()
11987 .map(|selection| {
11988 let uuid = match version {
11989 UuidVersion::V4 => uuid::Uuid::new_v4(),
11990 UuidVersion::V7 => uuid::Uuid::now_v7(),
11991 };
11992
11993 (selection.range(), uuid.to_string())
11994 });
11995 this.edit(edits, cx);
11996 this.refresh_inline_completion(true, false, cx);
11997 });
11998 }
11999
12000 pub fn open_selections_in_multibuffer(
12001 &mut self,
12002 _: &OpenSelectionsInMultibuffer,
12003 cx: &mut ViewContext<Self>,
12004 ) {
12005 let multibuffer = self.buffer.read(cx);
12006
12007 let Some(buffer) = multibuffer.as_singleton() else {
12008 return;
12009 };
12010
12011 let Some(workspace) = self.workspace() else {
12012 return;
12013 };
12014
12015 let locations = self
12016 .selections
12017 .disjoint_anchors()
12018 .iter()
12019 .map(|range| Location {
12020 buffer: buffer.clone(),
12021 range: range.start.text_anchor..range.end.text_anchor,
12022 })
12023 .collect::<Vec<_>>();
12024
12025 let title = multibuffer.title(cx).to_string();
12026
12027 cx.spawn(|_, mut cx| async move {
12028 workspace.update(&mut cx, |workspace, cx| {
12029 Self::open_locations_in_multibuffer(
12030 workspace,
12031 locations,
12032 format!("Selections for '{title}'"),
12033 false,
12034 MultibufferSelectionMode::All,
12035 cx,
12036 );
12037 })
12038 })
12039 .detach();
12040 }
12041
12042 /// Adds a row highlight for the given range. If a row has multiple highlights, the
12043 /// last highlight added will be used.
12044 ///
12045 /// If the range ends at the beginning of a line, then that line will not be highlighted.
12046 pub fn highlight_rows<T: 'static>(
12047 &mut self,
12048 range: Range<Anchor>,
12049 color: Hsla,
12050 should_autoscroll: bool,
12051 cx: &mut ViewContext<Self>,
12052 ) {
12053 let snapshot = self.buffer().read(cx).snapshot(cx);
12054 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
12055 let ix = row_highlights.binary_search_by(|highlight| {
12056 Ordering::Equal
12057 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
12058 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
12059 });
12060
12061 if let Err(mut ix) = ix {
12062 let index = post_inc(&mut self.highlight_order);
12063
12064 // If this range intersects with the preceding highlight, then merge it with
12065 // the preceding highlight. Otherwise insert a new highlight.
12066 let mut merged = false;
12067 if ix > 0 {
12068 let prev_highlight = &mut row_highlights[ix - 1];
12069 if prev_highlight
12070 .range
12071 .end
12072 .cmp(&range.start, &snapshot)
12073 .is_ge()
12074 {
12075 ix -= 1;
12076 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
12077 prev_highlight.range.end = range.end;
12078 }
12079 merged = true;
12080 prev_highlight.index = index;
12081 prev_highlight.color = color;
12082 prev_highlight.should_autoscroll = should_autoscroll;
12083 }
12084 }
12085
12086 if !merged {
12087 row_highlights.insert(
12088 ix,
12089 RowHighlight {
12090 range: range.clone(),
12091 index,
12092 color,
12093 should_autoscroll,
12094 },
12095 );
12096 }
12097
12098 // If any of the following highlights intersect with this one, merge them.
12099 while let Some(next_highlight) = row_highlights.get(ix + 1) {
12100 let highlight = &row_highlights[ix];
12101 if next_highlight
12102 .range
12103 .start
12104 .cmp(&highlight.range.end, &snapshot)
12105 .is_le()
12106 {
12107 if next_highlight
12108 .range
12109 .end
12110 .cmp(&highlight.range.end, &snapshot)
12111 .is_gt()
12112 {
12113 row_highlights[ix].range.end = next_highlight.range.end;
12114 }
12115 row_highlights.remove(ix + 1);
12116 } else {
12117 break;
12118 }
12119 }
12120 }
12121 }
12122
12123 /// Remove any highlighted row ranges of the given type that intersect the
12124 /// given ranges.
12125 pub fn remove_highlighted_rows<T: 'static>(
12126 &mut self,
12127 ranges_to_remove: Vec<Range<Anchor>>,
12128 cx: &mut ViewContext<Self>,
12129 ) {
12130 let snapshot = self.buffer().read(cx).snapshot(cx);
12131 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
12132 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
12133 row_highlights.retain(|highlight| {
12134 while let Some(range_to_remove) = ranges_to_remove.peek() {
12135 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
12136 Ordering::Less | Ordering::Equal => {
12137 ranges_to_remove.next();
12138 }
12139 Ordering::Greater => {
12140 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
12141 Ordering::Less | Ordering::Equal => {
12142 return false;
12143 }
12144 Ordering::Greater => break,
12145 }
12146 }
12147 }
12148 }
12149
12150 true
12151 })
12152 }
12153
12154 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
12155 pub fn clear_row_highlights<T: 'static>(&mut self) {
12156 self.highlighted_rows.remove(&TypeId::of::<T>());
12157 }
12158
12159 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
12160 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
12161 self.highlighted_rows
12162 .get(&TypeId::of::<T>())
12163 .map_or(&[] as &[_], |vec| vec.as_slice())
12164 .iter()
12165 .map(|highlight| (highlight.range.clone(), highlight.color))
12166 }
12167
12168 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
12169 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
12170 /// Allows to ignore certain kinds of highlights.
12171 pub fn highlighted_display_rows(&self, cx: &mut WindowContext) -> BTreeMap<DisplayRow, Hsla> {
12172 let snapshot = self.snapshot(cx);
12173 let mut used_highlight_orders = HashMap::default();
12174 self.highlighted_rows
12175 .iter()
12176 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
12177 .fold(
12178 BTreeMap::<DisplayRow, Hsla>::new(),
12179 |mut unique_rows, highlight| {
12180 let start = highlight.range.start.to_display_point(&snapshot);
12181 let end = highlight.range.end.to_display_point(&snapshot);
12182 let start_row = start.row().0;
12183 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
12184 && end.column() == 0
12185 {
12186 end.row().0.saturating_sub(1)
12187 } else {
12188 end.row().0
12189 };
12190 for row in start_row..=end_row {
12191 let used_index =
12192 used_highlight_orders.entry(row).or_insert(highlight.index);
12193 if highlight.index >= *used_index {
12194 *used_index = highlight.index;
12195 unique_rows.insert(DisplayRow(row), highlight.color);
12196 }
12197 }
12198 unique_rows
12199 },
12200 )
12201 }
12202
12203 pub fn highlighted_display_row_for_autoscroll(
12204 &self,
12205 snapshot: &DisplaySnapshot,
12206 ) -> Option<DisplayRow> {
12207 self.highlighted_rows
12208 .values()
12209 .flat_map(|highlighted_rows| highlighted_rows.iter())
12210 .filter_map(|highlight| {
12211 if highlight.should_autoscroll {
12212 Some(highlight.range.start.to_display_point(snapshot).row())
12213 } else {
12214 None
12215 }
12216 })
12217 .min()
12218 }
12219
12220 pub fn set_search_within_ranges(
12221 &mut self,
12222 ranges: &[Range<Anchor>],
12223 cx: &mut ViewContext<Self>,
12224 ) {
12225 self.highlight_background::<SearchWithinRange>(
12226 ranges,
12227 |colors| colors.editor_document_highlight_read_background,
12228 cx,
12229 )
12230 }
12231
12232 pub fn set_breadcrumb_header(&mut self, new_header: String) {
12233 self.breadcrumb_header = Some(new_header);
12234 }
12235
12236 pub fn clear_search_within_ranges(&mut self, cx: &mut ViewContext<Self>) {
12237 self.clear_background_highlights::<SearchWithinRange>(cx);
12238 }
12239
12240 pub fn highlight_background<T: 'static>(
12241 &mut self,
12242 ranges: &[Range<Anchor>],
12243 color_fetcher: fn(&ThemeColors) -> Hsla,
12244 cx: &mut ViewContext<Self>,
12245 ) {
12246 self.background_highlights
12247 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
12248 self.scrollbar_marker_state.dirty = true;
12249 cx.notify();
12250 }
12251
12252 pub fn clear_background_highlights<T: 'static>(
12253 &mut self,
12254 cx: &mut ViewContext<Self>,
12255 ) -> Option<BackgroundHighlight> {
12256 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
12257 if !text_highlights.1.is_empty() {
12258 self.scrollbar_marker_state.dirty = true;
12259 cx.notify();
12260 }
12261 Some(text_highlights)
12262 }
12263
12264 pub fn highlight_gutter<T: 'static>(
12265 &mut self,
12266 ranges: &[Range<Anchor>],
12267 color_fetcher: fn(&AppContext) -> Hsla,
12268 cx: &mut ViewContext<Self>,
12269 ) {
12270 self.gutter_highlights
12271 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
12272 cx.notify();
12273 }
12274
12275 pub fn clear_gutter_highlights<T: 'static>(
12276 &mut self,
12277 cx: &mut ViewContext<Self>,
12278 ) -> Option<GutterHighlight> {
12279 cx.notify();
12280 self.gutter_highlights.remove(&TypeId::of::<T>())
12281 }
12282
12283 #[cfg(feature = "test-support")]
12284 pub fn all_text_background_highlights(
12285 &self,
12286 cx: &mut ViewContext<Self>,
12287 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
12288 let snapshot = self.snapshot(cx);
12289 let buffer = &snapshot.buffer_snapshot;
12290 let start = buffer.anchor_before(0);
12291 let end = buffer.anchor_after(buffer.len());
12292 let theme = cx.theme().colors();
12293 self.background_highlights_in_range(start..end, &snapshot, theme)
12294 }
12295
12296 #[cfg(feature = "test-support")]
12297 pub fn search_background_highlights(
12298 &mut self,
12299 cx: &mut ViewContext<Self>,
12300 ) -> Vec<Range<Point>> {
12301 let snapshot = self.buffer().read(cx).snapshot(cx);
12302
12303 let highlights = self
12304 .background_highlights
12305 .get(&TypeId::of::<items::BufferSearchHighlights>());
12306
12307 if let Some((_color, ranges)) = highlights {
12308 ranges
12309 .iter()
12310 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
12311 .collect_vec()
12312 } else {
12313 vec![]
12314 }
12315 }
12316
12317 fn document_highlights_for_position<'a>(
12318 &'a self,
12319 position: Anchor,
12320 buffer: &'a MultiBufferSnapshot,
12321 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
12322 let read_highlights = self
12323 .background_highlights
12324 .get(&TypeId::of::<DocumentHighlightRead>())
12325 .map(|h| &h.1);
12326 let write_highlights = self
12327 .background_highlights
12328 .get(&TypeId::of::<DocumentHighlightWrite>())
12329 .map(|h| &h.1);
12330 let left_position = position.bias_left(buffer);
12331 let right_position = position.bias_right(buffer);
12332 read_highlights
12333 .into_iter()
12334 .chain(write_highlights)
12335 .flat_map(move |ranges| {
12336 let start_ix = match ranges.binary_search_by(|probe| {
12337 let cmp = probe.end.cmp(&left_position, buffer);
12338 if cmp.is_ge() {
12339 Ordering::Greater
12340 } else {
12341 Ordering::Less
12342 }
12343 }) {
12344 Ok(i) | Err(i) => i,
12345 };
12346
12347 ranges[start_ix..]
12348 .iter()
12349 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
12350 })
12351 }
12352
12353 pub fn has_background_highlights<T: 'static>(&self) -> bool {
12354 self.background_highlights
12355 .get(&TypeId::of::<T>())
12356 .map_or(false, |(_, highlights)| !highlights.is_empty())
12357 }
12358
12359 pub fn background_highlights_in_range(
12360 &self,
12361 search_range: Range<Anchor>,
12362 display_snapshot: &DisplaySnapshot,
12363 theme: &ThemeColors,
12364 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
12365 let mut results = Vec::new();
12366 for (color_fetcher, ranges) in self.background_highlights.values() {
12367 let color = color_fetcher(theme);
12368 let start_ix = match ranges.binary_search_by(|probe| {
12369 let cmp = probe
12370 .end
12371 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
12372 if cmp.is_gt() {
12373 Ordering::Greater
12374 } else {
12375 Ordering::Less
12376 }
12377 }) {
12378 Ok(i) | Err(i) => i,
12379 };
12380 for range in &ranges[start_ix..] {
12381 if range
12382 .start
12383 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
12384 .is_ge()
12385 {
12386 break;
12387 }
12388
12389 let start = range.start.to_display_point(display_snapshot);
12390 let end = range.end.to_display_point(display_snapshot);
12391 results.push((start..end, color))
12392 }
12393 }
12394 results
12395 }
12396
12397 pub fn background_highlight_row_ranges<T: 'static>(
12398 &self,
12399 search_range: Range<Anchor>,
12400 display_snapshot: &DisplaySnapshot,
12401 count: usize,
12402 ) -> Vec<RangeInclusive<DisplayPoint>> {
12403 let mut results = Vec::new();
12404 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
12405 return vec![];
12406 };
12407
12408 let start_ix = match ranges.binary_search_by(|probe| {
12409 let cmp = probe
12410 .end
12411 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
12412 if cmp.is_gt() {
12413 Ordering::Greater
12414 } else {
12415 Ordering::Less
12416 }
12417 }) {
12418 Ok(i) | Err(i) => i,
12419 };
12420 let mut push_region = |start: Option<Point>, end: Option<Point>| {
12421 if let (Some(start_display), Some(end_display)) = (start, end) {
12422 results.push(
12423 start_display.to_display_point(display_snapshot)
12424 ..=end_display.to_display_point(display_snapshot),
12425 );
12426 }
12427 };
12428 let mut start_row: Option<Point> = None;
12429 let mut end_row: Option<Point> = None;
12430 if ranges.len() > count {
12431 return Vec::new();
12432 }
12433 for range in &ranges[start_ix..] {
12434 if range
12435 .start
12436 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
12437 .is_ge()
12438 {
12439 break;
12440 }
12441 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
12442 if let Some(current_row) = &end_row {
12443 if end.row == current_row.row {
12444 continue;
12445 }
12446 }
12447 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
12448 if start_row.is_none() {
12449 assert_eq!(end_row, None);
12450 start_row = Some(start);
12451 end_row = Some(end);
12452 continue;
12453 }
12454 if let Some(current_end) = end_row.as_mut() {
12455 if start.row > current_end.row + 1 {
12456 push_region(start_row, end_row);
12457 start_row = Some(start);
12458 end_row = Some(end);
12459 } else {
12460 // Merge two hunks.
12461 *current_end = end;
12462 }
12463 } else {
12464 unreachable!();
12465 }
12466 }
12467 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
12468 push_region(start_row, end_row);
12469 results
12470 }
12471
12472 pub fn gutter_highlights_in_range(
12473 &self,
12474 search_range: Range<Anchor>,
12475 display_snapshot: &DisplaySnapshot,
12476 cx: &AppContext,
12477 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
12478 let mut results = Vec::new();
12479 for (color_fetcher, ranges) in self.gutter_highlights.values() {
12480 let color = color_fetcher(cx);
12481 let start_ix = match ranges.binary_search_by(|probe| {
12482 let cmp = probe
12483 .end
12484 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
12485 if cmp.is_gt() {
12486 Ordering::Greater
12487 } else {
12488 Ordering::Less
12489 }
12490 }) {
12491 Ok(i) | Err(i) => i,
12492 };
12493 for range in &ranges[start_ix..] {
12494 if range
12495 .start
12496 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
12497 .is_ge()
12498 {
12499 break;
12500 }
12501
12502 let start = range.start.to_display_point(display_snapshot);
12503 let end = range.end.to_display_point(display_snapshot);
12504 results.push((start..end, color))
12505 }
12506 }
12507 results
12508 }
12509
12510 /// Get the text ranges corresponding to the redaction query
12511 pub fn redacted_ranges(
12512 &self,
12513 search_range: Range<Anchor>,
12514 display_snapshot: &DisplaySnapshot,
12515 cx: &WindowContext,
12516 ) -> Vec<Range<DisplayPoint>> {
12517 display_snapshot
12518 .buffer_snapshot
12519 .redacted_ranges(search_range, |file| {
12520 if let Some(file) = file {
12521 file.is_private()
12522 && EditorSettings::get(
12523 Some(SettingsLocation {
12524 worktree_id: file.worktree_id(cx),
12525 path: file.path().as_ref(),
12526 }),
12527 cx,
12528 )
12529 .redact_private_values
12530 } else {
12531 false
12532 }
12533 })
12534 .map(|range| {
12535 range.start.to_display_point(display_snapshot)
12536 ..range.end.to_display_point(display_snapshot)
12537 })
12538 .collect()
12539 }
12540
12541 pub fn highlight_text<T: 'static>(
12542 &mut self,
12543 ranges: Vec<Range<Anchor>>,
12544 style: HighlightStyle,
12545 cx: &mut ViewContext<Self>,
12546 ) {
12547 self.display_map.update(cx, |map, _| {
12548 map.highlight_text(TypeId::of::<T>(), ranges, style)
12549 });
12550 cx.notify();
12551 }
12552
12553 pub(crate) fn highlight_inlays<T: 'static>(
12554 &mut self,
12555 highlights: Vec<InlayHighlight>,
12556 style: HighlightStyle,
12557 cx: &mut ViewContext<Self>,
12558 ) {
12559 self.display_map.update(cx, |map, _| {
12560 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
12561 });
12562 cx.notify();
12563 }
12564
12565 pub fn text_highlights<'a, T: 'static>(
12566 &'a self,
12567 cx: &'a AppContext,
12568 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
12569 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
12570 }
12571
12572 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
12573 let cleared = self
12574 .display_map
12575 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
12576 if cleared {
12577 cx.notify();
12578 }
12579 }
12580
12581 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
12582 (self.read_only(cx) || self.blink_manager.read(cx).visible())
12583 && self.focus_handle.is_focused(cx)
12584 }
12585
12586 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut ViewContext<Self>) {
12587 self.show_cursor_when_unfocused = is_enabled;
12588 cx.notify();
12589 }
12590
12591 pub fn lsp_store(&self, cx: &AppContext) -> Option<Model<LspStore>> {
12592 self.project
12593 .as_ref()
12594 .map(|project| project.read(cx).lsp_store())
12595 }
12596
12597 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
12598 cx.notify();
12599 }
12600
12601 fn on_buffer_event(
12602 &mut self,
12603 multibuffer: Model<MultiBuffer>,
12604 event: &multi_buffer::Event,
12605 cx: &mut ViewContext<Self>,
12606 ) {
12607 match event {
12608 multi_buffer::Event::Edited {
12609 singleton_buffer_edited,
12610 edited_buffer: buffer_edited,
12611 } => {
12612 self.scrollbar_marker_state.dirty = true;
12613 self.active_indent_guides_state.dirty = true;
12614 self.refresh_active_diagnostics(cx);
12615 self.refresh_code_actions(cx);
12616 if self.has_active_inline_completion() {
12617 self.update_visible_inline_completion(cx);
12618 }
12619 if let Some(buffer) = buffer_edited {
12620 let buffer_id = buffer.read(cx).remote_id();
12621 if !self.registered_buffers.contains_key(&buffer_id) {
12622 if let Some(lsp_store) = self.lsp_store(cx) {
12623 lsp_store.update(cx, |lsp_store, cx| {
12624 self.registered_buffers.insert(
12625 buffer_id,
12626 lsp_store.register_buffer_with_language_servers(&buffer, cx),
12627 );
12628 })
12629 }
12630 }
12631 }
12632 cx.emit(EditorEvent::BufferEdited);
12633 cx.emit(SearchEvent::MatchesInvalidated);
12634 if *singleton_buffer_edited {
12635 if let Some(project) = &self.project {
12636 let project = project.read(cx);
12637 #[allow(clippy::mutable_key_type)]
12638 let languages_affected = multibuffer
12639 .read(cx)
12640 .all_buffers()
12641 .into_iter()
12642 .filter_map(|buffer| {
12643 let buffer = buffer.read(cx);
12644 let language = buffer.language()?;
12645 if project.is_local()
12646 && project
12647 .language_servers_for_local_buffer(buffer, cx)
12648 .count()
12649 == 0
12650 {
12651 None
12652 } else {
12653 Some(language)
12654 }
12655 })
12656 .cloned()
12657 .collect::<HashSet<_>>();
12658 if !languages_affected.is_empty() {
12659 self.refresh_inlay_hints(
12660 InlayHintRefreshReason::BufferEdited(languages_affected),
12661 cx,
12662 );
12663 }
12664 }
12665 }
12666
12667 let Some(project) = &self.project else { return };
12668 let (telemetry, is_via_ssh) = {
12669 let project = project.read(cx);
12670 let telemetry = project.client().telemetry().clone();
12671 let is_via_ssh = project.is_via_ssh();
12672 (telemetry, is_via_ssh)
12673 };
12674 refresh_linked_ranges(self, cx);
12675 telemetry.log_edit_event("editor", is_via_ssh);
12676 }
12677 multi_buffer::Event::ExcerptsAdded {
12678 buffer,
12679 predecessor,
12680 excerpts,
12681 } => {
12682 self.tasks_update_task = Some(self.refresh_runnables(cx));
12683 let buffer_id = buffer.read(cx).remote_id();
12684 if self.buffer.read(cx).change_set_for(buffer_id).is_none() {
12685 if let Some(project) = &self.project {
12686 get_unstaged_changes_for_buffers(
12687 project,
12688 [buffer.clone()],
12689 self.buffer.clone(),
12690 cx,
12691 );
12692 }
12693 }
12694 cx.emit(EditorEvent::ExcerptsAdded {
12695 buffer: buffer.clone(),
12696 predecessor: *predecessor,
12697 excerpts: excerpts.clone(),
12698 });
12699 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
12700 }
12701 multi_buffer::Event::ExcerptsRemoved { ids } => {
12702 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
12703 let buffer = self.buffer.read(cx);
12704 self.registered_buffers
12705 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
12706 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
12707 }
12708 multi_buffer::Event::ExcerptsEdited { ids } => {
12709 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
12710 }
12711 multi_buffer::Event::ExcerptsExpanded { ids } => {
12712 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
12713 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
12714 }
12715 multi_buffer::Event::Reparsed(buffer_id) => {
12716 self.tasks_update_task = Some(self.refresh_runnables(cx));
12717
12718 cx.emit(EditorEvent::Reparsed(*buffer_id));
12719 }
12720 multi_buffer::Event::LanguageChanged(buffer_id) => {
12721 linked_editing_ranges::refresh_linked_ranges(self, cx);
12722 cx.emit(EditorEvent::Reparsed(*buffer_id));
12723 cx.notify();
12724 }
12725 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
12726 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
12727 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
12728 cx.emit(EditorEvent::TitleChanged)
12729 }
12730 // multi_buffer::Event::DiffBaseChanged => {
12731 // self.scrollbar_marker_state.dirty = true;
12732 // cx.emit(EditorEvent::DiffBaseChanged);
12733 // cx.notify();
12734 // }
12735 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
12736 multi_buffer::Event::DiagnosticsUpdated => {
12737 self.refresh_active_diagnostics(cx);
12738 self.scrollbar_marker_state.dirty = true;
12739 cx.notify();
12740 }
12741 _ => {}
12742 };
12743 }
12744
12745 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
12746 cx.notify();
12747 }
12748
12749 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
12750 self.tasks_update_task = Some(self.refresh_runnables(cx));
12751 self.refresh_inline_completion(true, false, cx);
12752 self.refresh_inlay_hints(
12753 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
12754 self.selections.newest_anchor().head(),
12755 &self.buffer.read(cx).snapshot(cx),
12756 cx,
12757 )),
12758 cx,
12759 );
12760
12761 let old_cursor_shape = self.cursor_shape;
12762
12763 {
12764 let editor_settings = EditorSettings::get_global(cx);
12765 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
12766 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
12767 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
12768 }
12769
12770 if old_cursor_shape != self.cursor_shape {
12771 cx.emit(EditorEvent::CursorShapeChanged);
12772 }
12773
12774 let project_settings = ProjectSettings::get_global(cx);
12775 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
12776
12777 if self.mode == EditorMode::Full {
12778 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
12779 if self.git_blame_inline_enabled != inline_blame_enabled {
12780 self.toggle_git_blame_inline_internal(false, cx);
12781 }
12782 }
12783
12784 cx.notify();
12785 }
12786
12787 pub fn set_searchable(&mut self, searchable: bool) {
12788 self.searchable = searchable;
12789 }
12790
12791 pub fn searchable(&self) -> bool {
12792 self.searchable
12793 }
12794
12795 fn open_proposed_changes_editor(
12796 &mut self,
12797 _: &OpenProposedChangesEditor,
12798 cx: &mut ViewContext<Self>,
12799 ) {
12800 let Some(workspace) = self.workspace() else {
12801 cx.propagate();
12802 return;
12803 };
12804
12805 let selections = self.selections.all::<usize>(cx);
12806 let multi_buffer = self.buffer.read(cx);
12807 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
12808 let mut new_selections_by_buffer = HashMap::default();
12809 for selection in selections {
12810 for (buffer, range, _) in
12811 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
12812 {
12813 let mut range = range.to_point(buffer);
12814 range.start.column = 0;
12815 range.end.column = buffer.line_len(range.end.row);
12816 new_selections_by_buffer
12817 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
12818 .or_insert(Vec::new())
12819 .push(range)
12820 }
12821 }
12822
12823 let proposed_changes_buffers = new_selections_by_buffer
12824 .into_iter()
12825 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
12826 .collect::<Vec<_>>();
12827 let proposed_changes_editor = cx.new_view(|cx| {
12828 ProposedChangesEditor::new(
12829 "Proposed changes",
12830 proposed_changes_buffers,
12831 self.project.clone(),
12832 cx,
12833 )
12834 });
12835
12836 cx.window_context().defer(move |cx| {
12837 workspace.update(cx, |workspace, cx| {
12838 workspace.active_pane().update(cx, |pane, cx| {
12839 pane.add_item(Box::new(proposed_changes_editor), true, true, None, cx);
12840 });
12841 });
12842 });
12843 }
12844
12845 pub fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext<Self>) {
12846 self.open_excerpts_common(None, true, cx)
12847 }
12848
12849 pub fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
12850 self.open_excerpts_common(None, false, cx)
12851 }
12852
12853 fn open_excerpts_common(
12854 &mut self,
12855 jump_data: Option<JumpData>,
12856 split: bool,
12857 cx: &mut ViewContext<Self>,
12858 ) {
12859 let Some(workspace) = self.workspace() else {
12860 cx.propagate();
12861 return;
12862 };
12863
12864 if self.buffer.read(cx).is_singleton() {
12865 cx.propagate();
12866 return;
12867 }
12868
12869 let mut new_selections_by_buffer = HashMap::default();
12870 match &jump_data {
12871 Some(JumpData::MultiBufferPoint {
12872 excerpt_id,
12873 position,
12874 anchor,
12875 line_offset_from_top,
12876 }) => {
12877 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12878 if let Some(buffer) = multi_buffer_snapshot
12879 .buffer_id_for_excerpt(*excerpt_id)
12880 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
12881 {
12882 let buffer_snapshot = buffer.read(cx).snapshot();
12883 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
12884 language::ToPoint::to_point(anchor, &buffer_snapshot)
12885 } else {
12886 buffer_snapshot.clip_point(*position, Bias::Left)
12887 };
12888 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
12889 new_selections_by_buffer.insert(
12890 buffer,
12891 (
12892 vec![jump_to_offset..jump_to_offset],
12893 Some(*line_offset_from_top),
12894 ),
12895 );
12896 }
12897 }
12898 Some(JumpData::MultiBufferRow {
12899 row,
12900 line_offset_from_top,
12901 }) => {
12902 let point = MultiBufferPoint::new(row.0, 0);
12903 if let Some((buffer, buffer_point, _)) =
12904 self.buffer.read(cx).point_to_buffer_point(point, cx)
12905 {
12906 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
12907 new_selections_by_buffer
12908 .entry(buffer)
12909 .or_insert((Vec::new(), Some(*line_offset_from_top)))
12910 .0
12911 .push(buffer_offset..buffer_offset)
12912 }
12913 }
12914 None => {
12915 let selections = self.selections.all::<usize>(cx);
12916 let multi_buffer = self.buffer.read(cx);
12917 for selection in selections {
12918 for (buffer, mut range, _) in multi_buffer
12919 .snapshot(cx)
12920 .range_to_buffer_ranges(selection.range())
12921 {
12922 // When editing branch buffers, jump to the corresponding location
12923 // in their base buffer.
12924 let mut buffer_handle = multi_buffer.buffer(buffer.remote_id()).unwrap();
12925 let buffer = buffer_handle.read(cx);
12926 if let Some(base_buffer) = buffer.base_buffer() {
12927 range = buffer.range_to_version(range, &base_buffer.read(cx).version());
12928 buffer_handle = base_buffer;
12929 }
12930
12931 if selection.reversed {
12932 mem::swap(&mut range.start, &mut range.end);
12933 }
12934 new_selections_by_buffer
12935 .entry(buffer_handle)
12936 .or_insert((Vec::new(), None))
12937 .0
12938 .push(range)
12939 }
12940 }
12941 }
12942 }
12943
12944 if new_selections_by_buffer.is_empty() {
12945 return;
12946 }
12947
12948 // We defer the pane interaction because we ourselves are a workspace item
12949 // and activating a new item causes the pane to call a method on us reentrantly,
12950 // which panics if we're on the stack.
12951 cx.window_context().defer(move |cx| {
12952 workspace.update(cx, |workspace, cx| {
12953 let pane = if split {
12954 workspace.adjacent_pane(cx)
12955 } else {
12956 workspace.active_pane().clone()
12957 };
12958
12959 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
12960 let editor = buffer
12961 .read(cx)
12962 .file()
12963 .is_none()
12964 .then(|| {
12965 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
12966 // so `workspace.open_project_item` will never find them, always opening a new editor.
12967 // Instead, we try to activate the existing editor in the pane first.
12968 let (editor, pane_item_index) =
12969 pane.read(cx).items().enumerate().find_map(|(i, item)| {
12970 let editor = item.downcast::<Editor>()?;
12971 let singleton_buffer =
12972 editor.read(cx).buffer().read(cx).as_singleton()?;
12973 if singleton_buffer == buffer {
12974 Some((editor, i))
12975 } else {
12976 None
12977 }
12978 })?;
12979 pane.update(cx, |pane, cx| {
12980 pane.activate_item(pane_item_index, true, true, cx)
12981 });
12982 Some(editor)
12983 })
12984 .flatten()
12985 .unwrap_or_else(|| {
12986 workspace.open_project_item::<Self>(
12987 pane.clone(),
12988 buffer,
12989 true,
12990 true,
12991 cx,
12992 )
12993 });
12994
12995 editor.update(cx, |editor, cx| {
12996 let autoscroll = match scroll_offset {
12997 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
12998 None => Autoscroll::newest(),
12999 };
13000 let nav_history = editor.nav_history.take();
13001 editor.change_selections(Some(autoscroll), cx, |s| {
13002 s.select_ranges(ranges);
13003 });
13004 editor.nav_history = nav_history;
13005 });
13006 }
13007 })
13008 });
13009 }
13010
13011 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
13012 let snapshot = self.buffer.read(cx).read(cx);
13013 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
13014 Some(
13015 ranges
13016 .iter()
13017 .map(move |range| {
13018 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
13019 })
13020 .collect(),
13021 )
13022 }
13023
13024 fn selection_replacement_ranges(
13025 &self,
13026 range: Range<OffsetUtf16>,
13027 cx: &mut AppContext,
13028 ) -> Vec<Range<OffsetUtf16>> {
13029 let selections = self.selections.all::<OffsetUtf16>(cx);
13030 let newest_selection = selections
13031 .iter()
13032 .max_by_key(|selection| selection.id)
13033 .unwrap();
13034 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
13035 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
13036 let snapshot = self.buffer.read(cx).read(cx);
13037 selections
13038 .into_iter()
13039 .map(|mut selection| {
13040 selection.start.0 =
13041 (selection.start.0 as isize).saturating_add(start_delta) as usize;
13042 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
13043 snapshot.clip_offset_utf16(selection.start, Bias::Left)
13044 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
13045 })
13046 .collect()
13047 }
13048
13049 fn report_editor_event(
13050 &self,
13051 event_type: &'static str,
13052 file_extension: Option<String>,
13053 cx: &AppContext,
13054 ) {
13055 if cfg!(any(test, feature = "test-support")) {
13056 return;
13057 }
13058
13059 let Some(project) = &self.project else { return };
13060
13061 // If None, we are in a file without an extension
13062 let file = self
13063 .buffer
13064 .read(cx)
13065 .as_singleton()
13066 .and_then(|b| b.read(cx).file());
13067 let file_extension = file_extension.or(file
13068 .as_ref()
13069 .and_then(|file| Path::new(file.file_name(cx)).extension())
13070 .and_then(|e| e.to_str())
13071 .map(|a| a.to_string()));
13072
13073 let vim_mode = cx
13074 .global::<SettingsStore>()
13075 .raw_user_settings()
13076 .get("vim_mode")
13077 == Some(&serde_json::Value::Bool(true));
13078
13079 let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
13080 == language::language_settings::InlineCompletionProvider::Copilot;
13081 let copilot_enabled_for_language = self
13082 .buffer
13083 .read(cx)
13084 .settings_at(0, cx)
13085 .show_inline_completions;
13086
13087 let project = project.read(cx);
13088 telemetry::event!(
13089 event_type,
13090 file_extension,
13091 vim_mode,
13092 copilot_enabled,
13093 copilot_enabled_for_language,
13094 is_via_ssh = project.is_via_ssh(),
13095 );
13096 }
13097
13098 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
13099 /// with each line being an array of {text, highlight} objects.
13100 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
13101 #[derive(Serialize)]
13102 struct Chunk<'a> {
13103 text: String,
13104 highlight: Option<&'a str>,
13105 }
13106
13107 let snapshot = self.buffer.read(cx).snapshot(cx);
13108 let range = self
13109 .selected_text_range(false, cx)
13110 .and_then(|selection| {
13111 if selection.range.is_empty() {
13112 None
13113 } else {
13114 Some(selection.range)
13115 }
13116 })
13117 .unwrap_or_else(|| 0..snapshot.len());
13118
13119 let chunks = snapshot.chunks(range, true);
13120 let mut lines = Vec::new();
13121 let mut line: VecDeque<Chunk> = VecDeque::new();
13122
13123 let Some(style) = self.style.as_ref() else {
13124 return;
13125 };
13126
13127 for chunk in chunks {
13128 let highlight = chunk
13129 .syntax_highlight_id
13130 .and_then(|id| id.name(&style.syntax));
13131 let mut chunk_lines = chunk.text.split('\n').peekable();
13132 while let Some(text) = chunk_lines.next() {
13133 let mut merged_with_last_token = false;
13134 if let Some(last_token) = line.back_mut() {
13135 if last_token.highlight == highlight {
13136 last_token.text.push_str(text);
13137 merged_with_last_token = true;
13138 }
13139 }
13140
13141 if !merged_with_last_token {
13142 line.push_back(Chunk {
13143 text: text.into(),
13144 highlight,
13145 });
13146 }
13147
13148 if chunk_lines.peek().is_some() {
13149 if line.len() > 1 && line.front().unwrap().text.is_empty() {
13150 line.pop_front();
13151 }
13152 if line.len() > 1 && line.back().unwrap().text.is_empty() {
13153 line.pop_back();
13154 }
13155
13156 lines.push(mem::take(&mut line));
13157 }
13158 }
13159 }
13160
13161 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
13162 return;
13163 };
13164 cx.write_to_clipboard(ClipboardItem::new_string(lines));
13165 }
13166
13167 pub fn open_context_menu(&mut self, _: &OpenContextMenu, cx: &mut ViewContext<Self>) {
13168 self.request_autoscroll(Autoscroll::newest(), cx);
13169 let position = self.selections.newest_display(cx).start;
13170 mouse_context_menu::deploy_context_menu(self, None, position, cx);
13171 }
13172
13173 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
13174 &self.inlay_hint_cache
13175 }
13176
13177 pub fn replay_insert_event(
13178 &mut self,
13179 text: &str,
13180 relative_utf16_range: Option<Range<isize>>,
13181 cx: &mut ViewContext<Self>,
13182 ) {
13183 if !self.input_enabled {
13184 cx.emit(EditorEvent::InputIgnored { text: text.into() });
13185 return;
13186 }
13187 if let Some(relative_utf16_range) = relative_utf16_range {
13188 let selections = self.selections.all::<OffsetUtf16>(cx);
13189 self.change_selections(None, cx, |s| {
13190 let new_ranges = selections.into_iter().map(|range| {
13191 let start = OffsetUtf16(
13192 range
13193 .head()
13194 .0
13195 .saturating_add_signed(relative_utf16_range.start),
13196 );
13197 let end = OffsetUtf16(
13198 range
13199 .head()
13200 .0
13201 .saturating_add_signed(relative_utf16_range.end),
13202 );
13203 start..end
13204 });
13205 s.select_ranges(new_ranges);
13206 });
13207 }
13208
13209 self.handle_input(text, cx);
13210 }
13211
13212 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
13213 let Some(provider) = self.semantics_provider.as_ref() else {
13214 return false;
13215 };
13216
13217 let mut supports = false;
13218 self.buffer().read(cx).for_each_buffer(|buffer| {
13219 supports |= provider.supports_inlay_hints(buffer, cx);
13220 });
13221 supports
13222 }
13223
13224 pub fn focus(&self, cx: &mut WindowContext) {
13225 cx.focus(&self.focus_handle)
13226 }
13227
13228 pub fn is_focused(&self, cx: &WindowContext) -> bool {
13229 self.focus_handle.is_focused(cx)
13230 }
13231
13232 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
13233 cx.emit(EditorEvent::Focused);
13234
13235 if let Some(descendant) = self
13236 .last_focused_descendant
13237 .take()
13238 .and_then(|descendant| descendant.upgrade())
13239 {
13240 cx.focus(&descendant);
13241 } else {
13242 if let Some(blame) = self.blame.as_ref() {
13243 blame.update(cx, GitBlame::focus)
13244 }
13245
13246 self.blink_manager.update(cx, BlinkManager::enable);
13247 self.show_cursor_names(cx);
13248 self.buffer.update(cx, |buffer, cx| {
13249 buffer.finalize_last_transaction(cx);
13250 if self.leader_peer_id.is_none() {
13251 buffer.set_active_selections(
13252 &self.selections.disjoint_anchors(),
13253 self.selections.line_mode,
13254 self.cursor_shape,
13255 cx,
13256 );
13257 }
13258 });
13259 }
13260 }
13261
13262 fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
13263 cx.emit(EditorEvent::FocusedIn)
13264 }
13265
13266 fn handle_focus_out(&mut self, event: FocusOutEvent, _cx: &mut ViewContext<Self>) {
13267 if event.blurred != self.focus_handle {
13268 self.last_focused_descendant = Some(event.blurred);
13269 }
13270 }
13271
13272 pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
13273 self.blink_manager.update(cx, BlinkManager::disable);
13274 self.buffer
13275 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
13276
13277 if let Some(blame) = self.blame.as_ref() {
13278 blame.update(cx, GitBlame::blur)
13279 }
13280 if !self.hover_state.focused(cx) {
13281 hide_hover(self, cx);
13282 }
13283
13284 self.hide_context_menu(cx);
13285 cx.emit(EditorEvent::Blurred);
13286 cx.notify();
13287 }
13288
13289 pub fn register_action<A: Action>(
13290 &mut self,
13291 listener: impl Fn(&A, &mut WindowContext) + 'static,
13292 ) -> Subscription {
13293 let id = self.next_editor_action_id.post_inc();
13294 let listener = Arc::new(listener);
13295 self.editor_actions.borrow_mut().insert(
13296 id,
13297 Box::new(move |cx| {
13298 let cx = cx.window_context();
13299 let listener = listener.clone();
13300 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
13301 let action = action.downcast_ref().unwrap();
13302 if phase == DispatchPhase::Bubble {
13303 listener(action, cx)
13304 }
13305 })
13306 }),
13307 );
13308
13309 let editor_actions = self.editor_actions.clone();
13310 Subscription::new(move || {
13311 editor_actions.borrow_mut().remove(&id);
13312 })
13313 }
13314
13315 pub fn file_header_size(&self) -> u32 {
13316 FILE_HEADER_HEIGHT
13317 }
13318
13319 pub fn revert(
13320 &mut self,
13321 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
13322 cx: &mut ViewContext<Self>,
13323 ) {
13324 self.buffer().update(cx, |multi_buffer, cx| {
13325 for (buffer_id, changes) in revert_changes {
13326 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
13327 buffer.update(cx, |buffer, cx| {
13328 buffer.edit(
13329 changes.into_iter().map(|(range, text)| {
13330 (range, text.to_string().map(Arc::<str>::from))
13331 }),
13332 None,
13333 cx,
13334 );
13335 });
13336 }
13337 }
13338 });
13339 self.change_selections(None, cx, |selections| selections.refresh());
13340 }
13341
13342 pub fn to_pixel_point(
13343 &self,
13344 source: multi_buffer::Anchor,
13345 editor_snapshot: &EditorSnapshot,
13346 cx: &mut ViewContext<Self>,
13347 ) -> Option<gpui::Point<Pixels>> {
13348 let source_point = source.to_display_point(editor_snapshot);
13349 self.display_to_pixel_point(source_point, editor_snapshot, cx)
13350 }
13351
13352 pub fn display_to_pixel_point(
13353 &self,
13354 source: DisplayPoint,
13355 editor_snapshot: &EditorSnapshot,
13356 cx: &WindowContext,
13357 ) -> Option<gpui::Point<Pixels>> {
13358 let line_height = self.style()?.text.line_height_in_pixels(cx.rem_size());
13359 let text_layout_details = self.text_layout_details(cx);
13360 let scroll_top = text_layout_details
13361 .scroll_anchor
13362 .scroll_position(editor_snapshot)
13363 .y;
13364
13365 if source.row().as_f32() < scroll_top.floor() {
13366 return None;
13367 }
13368 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
13369 let source_y = line_height * (source.row().as_f32() - scroll_top);
13370 Some(gpui::Point::new(source_x, source_y))
13371 }
13372
13373 pub fn has_active_completions_menu(&self) -> bool {
13374 self.context_menu.borrow().as_ref().map_or(false, |menu| {
13375 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
13376 })
13377 }
13378
13379 pub fn register_addon<T: Addon>(&mut self, instance: T) {
13380 self.addons
13381 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
13382 }
13383
13384 pub fn unregister_addon<T: Addon>(&mut self) {
13385 self.addons.remove(&std::any::TypeId::of::<T>());
13386 }
13387
13388 pub fn addon<T: Addon>(&self) -> Option<&T> {
13389 let type_id = std::any::TypeId::of::<T>();
13390 self.addons
13391 .get(&type_id)
13392 .and_then(|item| item.to_any().downcast_ref::<T>())
13393 }
13394
13395 fn character_size(&self, cx: &mut ViewContext<Self>) -> gpui::Point<Pixels> {
13396 let text_layout_details = self.text_layout_details(cx);
13397 let style = &text_layout_details.editor_style;
13398 let font_id = cx.text_system().resolve_font(&style.text.font());
13399 let font_size = style.text.font_size.to_pixels(cx.rem_size());
13400 let line_height = style.text.line_height_in_pixels(cx.rem_size());
13401
13402 let em_width = cx
13403 .text_system()
13404 .typographic_bounds(font_id, font_size, 'm')
13405 .unwrap()
13406 .size
13407 .width;
13408
13409 gpui::Point::new(em_width, line_height)
13410 }
13411}
13412
13413fn get_unstaged_changes_for_buffers(
13414 project: &Model<Project>,
13415 buffers: impl IntoIterator<Item = Model<Buffer>>,
13416 buffer: Model<MultiBuffer>,
13417 cx: &mut AppContext,
13418) {
13419 let mut tasks = Vec::new();
13420 project.update(cx, |project, cx| {
13421 for buffer in buffers {
13422 tasks.push(project.open_unstaged_changes(buffer.clone(), cx))
13423 }
13424 });
13425 cx.spawn(|mut cx| async move {
13426 let change_sets = futures::future::join_all(tasks).await;
13427 buffer
13428 .update(&mut cx, |buffer, cx| {
13429 for change_set in change_sets {
13430 if let Some(change_set) = change_set.log_err() {
13431 buffer.add_change_set(change_set, cx);
13432 }
13433 }
13434 })
13435 .ok();
13436 })
13437 .detach();
13438}
13439
13440fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
13441 let tab_size = tab_size.get() as usize;
13442 let mut width = offset;
13443
13444 for ch in text.chars() {
13445 width += if ch == '\t' {
13446 tab_size - (width % tab_size)
13447 } else {
13448 1
13449 };
13450 }
13451
13452 width - offset
13453}
13454
13455#[cfg(test)]
13456mod tests {
13457 use super::*;
13458
13459 #[test]
13460 fn test_string_size_with_expanded_tabs() {
13461 let nz = |val| NonZeroU32::new(val).unwrap();
13462 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
13463 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
13464 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
13465 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
13466 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
13467 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
13468 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
13469 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
13470 }
13471}
13472
13473/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
13474struct WordBreakingTokenizer<'a> {
13475 input: &'a str,
13476}
13477
13478impl<'a> WordBreakingTokenizer<'a> {
13479 fn new(input: &'a str) -> Self {
13480 Self { input }
13481 }
13482}
13483
13484fn is_char_ideographic(ch: char) -> bool {
13485 use unicode_script::Script::*;
13486 use unicode_script::UnicodeScript;
13487 matches!(ch.script(), Han | Tangut | Yi)
13488}
13489
13490fn is_grapheme_ideographic(text: &str) -> bool {
13491 text.chars().any(is_char_ideographic)
13492}
13493
13494fn is_grapheme_whitespace(text: &str) -> bool {
13495 text.chars().any(|x| x.is_whitespace())
13496}
13497
13498fn should_stay_with_preceding_ideograph(text: &str) -> bool {
13499 text.chars().next().map_or(false, |ch| {
13500 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
13501 })
13502}
13503
13504#[derive(PartialEq, Eq, Debug, Clone, Copy)]
13505struct WordBreakToken<'a> {
13506 token: &'a str,
13507 grapheme_len: usize,
13508 is_whitespace: bool,
13509}
13510
13511impl<'a> Iterator for WordBreakingTokenizer<'a> {
13512 /// Yields a span, the count of graphemes in the token, and whether it was
13513 /// whitespace. Note that it also breaks at word boundaries.
13514 type Item = WordBreakToken<'a>;
13515
13516 fn next(&mut self) -> Option<Self::Item> {
13517 use unicode_segmentation::UnicodeSegmentation;
13518 if self.input.is_empty() {
13519 return None;
13520 }
13521
13522 let mut iter = self.input.graphemes(true).peekable();
13523 let mut offset = 0;
13524 let mut graphemes = 0;
13525 if let Some(first_grapheme) = iter.next() {
13526 let is_whitespace = is_grapheme_whitespace(first_grapheme);
13527 offset += first_grapheme.len();
13528 graphemes += 1;
13529 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
13530 if let Some(grapheme) = iter.peek().copied() {
13531 if should_stay_with_preceding_ideograph(grapheme) {
13532 offset += grapheme.len();
13533 graphemes += 1;
13534 }
13535 }
13536 } else {
13537 let mut words = self.input[offset..].split_word_bound_indices().peekable();
13538 let mut next_word_bound = words.peek().copied();
13539 if next_word_bound.map_or(false, |(i, _)| i == 0) {
13540 next_word_bound = words.next();
13541 }
13542 while let Some(grapheme) = iter.peek().copied() {
13543 if next_word_bound.map_or(false, |(i, _)| i == offset) {
13544 break;
13545 };
13546 if is_grapheme_whitespace(grapheme) != is_whitespace {
13547 break;
13548 };
13549 offset += grapheme.len();
13550 graphemes += 1;
13551 iter.next();
13552 }
13553 }
13554 let token = &self.input[..offset];
13555 self.input = &self.input[offset..];
13556 if is_whitespace {
13557 Some(WordBreakToken {
13558 token: " ",
13559 grapheme_len: 1,
13560 is_whitespace: true,
13561 })
13562 } else {
13563 Some(WordBreakToken {
13564 token,
13565 grapheme_len: graphemes,
13566 is_whitespace: false,
13567 })
13568 }
13569 } else {
13570 None
13571 }
13572 }
13573}
13574
13575#[test]
13576fn test_word_breaking_tokenizer() {
13577 let tests: &[(&str, &[(&str, usize, bool)])] = &[
13578 ("", &[]),
13579 (" ", &[(" ", 1, true)]),
13580 ("Ʒ", &[("Ʒ", 1, false)]),
13581 ("Ǽ", &[("Ǽ", 1, false)]),
13582 ("⋑", &[("⋑", 1, false)]),
13583 ("⋑⋑", &[("⋑⋑", 2, false)]),
13584 (
13585 "原理,进而",
13586 &[
13587 ("原", 1, false),
13588 ("理,", 2, false),
13589 ("进", 1, false),
13590 ("而", 1, false),
13591 ],
13592 ),
13593 (
13594 "hello world",
13595 &[("hello", 5, false), (" ", 1, true), ("world", 5, false)],
13596 ),
13597 (
13598 "hello, world",
13599 &[("hello,", 6, false), (" ", 1, true), ("world", 5, false)],
13600 ),
13601 (
13602 " hello world",
13603 &[
13604 (" ", 1, true),
13605 ("hello", 5, false),
13606 (" ", 1, true),
13607 ("world", 5, false),
13608 ],
13609 ),
13610 (
13611 "这是什么 \n 钢笔",
13612 &[
13613 ("这", 1, false),
13614 ("是", 1, false),
13615 ("什", 1, false),
13616 ("么", 1, false),
13617 (" ", 1, true),
13618 ("钢", 1, false),
13619 ("笔", 1, false),
13620 ],
13621 ),
13622 (" mutton", &[(" ", 1, true), ("mutton", 6, false)]),
13623 ];
13624
13625 for (input, result) in tests {
13626 assert_eq!(
13627 WordBreakingTokenizer::new(input).collect::<Vec<_>>(),
13628 result
13629 .iter()
13630 .copied()
13631 .map(|(token, grapheme_len, is_whitespace)| WordBreakToken {
13632 token,
13633 grapheme_len,
13634 is_whitespace,
13635 })
13636 .collect::<Vec<_>>()
13637 );
13638 }
13639}
13640
13641fn wrap_with_prefix(
13642 line_prefix: String,
13643 unwrapped_text: String,
13644 wrap_column: usize,
13645 tab_size: NonZeroU32,
13646) -> String {
13647 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
13648 let mut wrapped_text = String::new();
13649 let mut current_line = line_prefix.clone();
13650
13651 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
13652 let mut current_line_len = line_prefix_len;
13653 for WordBreakToken {
13654 token,
13655 grapheme_len,
13656 is_whitespace,
13657 } in tokenizer
13658 {
13659 if current_line_len + grapheme_len > wrap_column && current_line_len != line_prefix_len {
13660 wrapped_text.push_str(current_line.trim_end());
13661 wrapped_text.push('\n');
13662 current_line.truncate(line_prefix.len());
13663 current_line_len = line_prefix_len;
13664 if !is_whitespace {
13665 current_line.push_str(token);
13666 current_line_len += grapheme_len;
13667 }
13668 } else if !is_whitespace {
13669 current_line.push_str(token);
13670 current_line_len += grapheme_len;
13671 } else if current_line_len != line_prefix_len {
13672 current_line.push(' ');
13673 current_line_len += 1;
13674 }
13675 }
13676
13677 if !current_line.is_empty() {
13678 wrapped_text.push_str(¤t_line);
13679 }
13680 wrapped_text
13681}
13682
13683#[test]
13684fn test_wrap_with_prefix() {
13685 assert_eq!(
13686 wrap_with_prefix(
13687 "# ".to_string(),
13688 "abcdefg".to_string(),
13689 4,
13690 NonZeroU32::new(4).unwrap()
13691 ),
13692 "# abcdefg"
13693 );
13694 assert_eq!(
13695 wrap_with_prefix(
13696 "".to_string(),
13697 "\thello world".to_string(),
13698 8,
13699 NonZeroU32::new(4).unwrap()
13700 ),
13701 "hello\nworld"
13702 );
13703 assert_eq!(
13704 wrap_with_prefix(
13705 "// ".to_string(),
13706 "xx \nyy zz aa bb cc".to_string(),
13707 12,
13708 NonZeroU32::new(4).unwrap()
13709 ),
13710 "// xx yy zz\n// aa bb cc"
13711 );
13712 assert_eq!(
13713 wrap_with_prefix(
13714 String::new(),
13715 "这是什么 \n 钢笔".to_string(),
13716 3,
13717 NonZeroU32::new(4).unwrap()
13718 ),
13719 "这是什\n么 钢\n笔"
13720 );
13721}
13722
13723pub trait CollaborationHub {
13724 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
13725 fn user_participant_indices<'a>(
13726 &self,
13727 cx: &'a AppContext,
13728 ) -> &'a HashMap<u64, ParticipantIndex>;
13729 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
13730}
13731
13732impl CollaborationHub for Model<Project> {
13733 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
13734 self.read(cx).collaborators()
13735 }
13736
13737 fn user_participant_indices<'a>(
13738 &self,
13739 cx: &'a AppContext,
13740 ) -> &'a HashMap<u64, ParticipantIndex> {
13741 self.read(cx).user_store().read(cx).participant_indices()
13742 }
13743
13744 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
13745 let this = self.read(cx);
13746 let user_ids = this.collaborators().values().map(|c| c.user_id);
13747 this.user_store().read_with(cx, |user_store, cx| {
13748 user_store.participant_names(user_ids, cx)
13749 })
13750 }
13751}
13752
13753pub trait SemanticsProvider {
13754 fn hover(
13755 &self,
13756 buffer: &Model<Buffer>,
13757 position: text::Anchor,
13758 cx: &mut AppContext,
13759 ) -> Option<Task<Vec<project::Hover>>>;
13760
13761 fn inlay_hints(
13762 &self,
13763 buffer_handle: Model<Buffer>,
13764 range: Range<text::Anchor>,
13765 cx: &mut AppContext,
13766 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
13767
13768 fn resolve_inlay_hint(
13769 &self,
13770 hint: InlayHint,
13771 buffer_handle: Model<Buffer>,
13772 server_id: LanguageServerId,
13773 cx: &mut AppContext,
13774 ) -> Option<Task<anyhow::Result<InlayHint>>>;
13775
13776 fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool;
13777
13778 fn document_highlights(
13779 &self,
13780 buffer: &Model<Buffer>,
13781 position: text::Anchor,
13782 cx: &mut AppContext,
13783 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
13784
13785 fn definitions(
13786 &self,
13787 buffer: &Model<Buffer>,
13788 position: text::Anchor,
13789 kind: GotoDefinitionKind,
13790 cx: &mut AppContext,
13791 ) -> Option<Task<Result<Vec<LocationLink>>>>;
13792
13793 fn range_for_rename(
13794 &self,
13795 buffer: &Model<Buffer>,
13796 position: text::Anchor,
13797 cx: &mut AppContext,
13798 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
13799
13800 fn perform_rename(
13801 &self,
13802 buffer: &Model<Buffer>,
13803 position: text::Anchor,
13804 new_name: String,
13805 cx: &mut AppContext,
13806 ) -> Option<Task<Result<ProjectTransaction>>>;
13807}
13808
13809pub trait CompletionProvider {
13810 fn completions(
13811 &self,
13812 buffer: &Model<Buffer>,
13813 buffer_position: text::Anchor,
13814 trigger: CompletionContext,
13815 cx: &mut ViewContext<Editor>,
13816 ) -> Task<Result<Vec<Completion>>>;
13817
13818 fn resolve_completions(
13819 &self,
13820 buffer: Model<Buffer>,
13821 completion_indices: Vec<usize>,
13822 completions: Rc<RefCell<Box<[Completion]>>>,
13823 cx: &mut ViewContext<Editor>,
13824 ) -> Task<Result<bool>>;
13825
13826 fn apply_additional_edits_for_completion(
13827 &self,
13828 _buffer: Model<Buffer>,
13829 _completions: Rc<RefCell<Box<[Completion]>>>,
13830 _completion_index: usize,
13831 _push_to_history: bool,
13832 _cx: &mut ViewContext<Editor>,
13833 ) -> Task<Result<Option<language::Transaction>>> {
13834 Task::ready(Ok(None))
13835 }
13836
13837 fn is_completion_trigger(
13838 &self,
13839 buffer: &Model<Buffer>,
13840 position: language::Anchor,
13841 text: &str,
13842 trigger_in_words: bool,
13843 cx: &mut ViewContext<Editor>,
13844 ) -> bool;
13845
13846 fn sort_completions(&self) -> bool {
13847 true
13848 }
13849}
13850
13851pub trait CodeActionProvider {
13852 fn id(&self) -> Arc<str>;
13853
13854 fn code_actions(
13855 &self,
13856 buffer: &Model<Buffer>,
13857 range: Range<text::Anchor>,
13858 cx: &mut WindowContext,
13859 ) -> Task<Result<Vec<CodeAction>>>;
13860
13861 fn apply_code_action(
13862 &self,
13863 buffer_handle: Model<Buffer>,
13864 action: CodeAction,
13865 excerpt_id: ExcerptId,
13866 push_to_history: bool,
13867 cx: &mut WindowContext,
13868 ) -> Task<Result<ProjectTransaction>>;
13869}
13870
13871impl CodeActionProvider for Model<Project> {
13872 fn id(&self) -> Arc<str> {
13873 "project".into()
13874 }
13875
13876 fn code_actions(
13877 &self,
13878 buffer: &Model<Buffer>,
13879 range: Range<text::Anchor>,
13880 cx: &mut WindowContext,
13881 ) -> Task<Result<Vec<CodeAction>>> {
13882 self.update(cx, |project, cx| {
13883 project.code_actions(buffer, range, None, cx)
13884 })
13885 }
13886
13887 fn apply_code_action(
13888 &self,
13889 buffer_handle: Model<Buffer>,
13890 action: CodeAction,
13891 _excerpt_id: ExcerptId,
13892 push_to_history: bool,
13893 cx: &mut WindowContext,
13894 ) -> Task<Result<ProjectTransaction>> {
13895 self.update(cx, |project, cx| {
13896 project.apply_code_action(buffer_handle, action, push_to_history, cx)
13897 })
13898 }
13899}
13900
13901fn snippet_completions(
13902 project: &Project,
13903 buffer: &Model<Buffer>,
13904 buffer_position: text::Anchor,
13905 cx: &mut AppContext,
13906) -> Task<Result<Vec<Completion>>> {
13907 let language = buffer.read(cx).language_at(buffer_position);
13908 let language_name = language.as_ref().map(|language| language.lsp_id());
13909 let snippet_store = project.snippets().read(cx);
13910 let snippets = snippet_store.snippets_for(language_name, cx);
13911
13912 if snippets.is_empty() {
13913 return Task::ready(Ok(vec![]));
13914 }
13915 let snapshot = buffer.read(cx).text_snapshot();
13916 let chars: String = snapshot
13917 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
13918 .collect();
13919
13920 let scope = language.map(|language| language.default_scope());
13921 let executor = cx.background_executor().clone();
13922
13923 cx.background_executor().spawn(async move {
13924 let classifier = CharClassifier::new(scope).for_completion(true);
13925 let mut last_word = chars
13926 .chars()
13927 .take_while(|c| classifier.is_word(*c))
13928 .collect::<String>();
13929 last_word = last_word.chars().rev().collect();
13930
13931 if last_word.is_empty() {
13932 return Ok(vec![]);
13933 }
13934
13935 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
13936 let to_lsp = |point: &text::Anchor| {
13937 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
13938 point_to_lsp(end)
13939 };
13940 let lsp_end = to_lsp(&buffer_position);
13941
13942 let candidates = snippets
13943 .iter()
13944 .enumerate()
13945 .flat_map(|(ix, snippet)| {
13946 snippet
13947 .prefix
13948 .iter()
13949 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
13950 })
13951 .collect::<Vec<StringMatchCandidate>>();
13952
13953 let mut matches = fuzzy::match_strings(
13954 &candidates,
13955 &last_word,
13956 last_word.chars().any(|c| c.is_uppercase()),
13957 100,
13958 &Default::default(),
13959 executor,
13960 )
13961 .await;
13962
13963 // Remove all candidates where the query's start does not match the start of any word in the candidate
13964 if let Some(query_start) = last_word.chars().next() {
13965 matches.retain(|string_match| {
13966 split_words(&string_match.string).any(|word| {
13967 // Check that the first codepoint of the word as lowercase matches the first
13968 // codepoint of the query as lowercase
13969 word.chars()
13970 .flat_map(|codepoint| codepoint.to_lowercase())
13971 .zip(query_start.to_lowercase())
13972 .all(|(word_cp, query_cp)| word_cp == query_cp)
13973 })
13974 });
13975 }
13976
13977 let matched_strings = matches
13978 .into_iter()
13979 .map(|m| m.string)
13980 .collect::<HashSet<_>>();
13981
13982 let result: Vec<Completion> = snippets
13983 .into_iter()
13984 .filter_map(|snippet| {
13985 let matching_prefix = snippet
13986 .prefix
13987 .iter()
13988 .find(|prefix| matched_strings.contains(*prefix))?;
13989 let start = as_offset - last_word.len();
13990 let start = snapshot.anchor_before(start);
13991 let range = start..buffer_position;
13992 let lsp_start = to_lsp(&start);
13993 let lsp_range = lsp::Range {
13994 start: lsp_start,
13995 end: lsp_end,
13996 };
13997 Some(Completion {
13998 old_range: range,
13999 new_text: snippet.body.clone(),
14000 resolved: false,
14001 label: CodeLabel {
14002 text: matching_prefix.clone(),
14003 runs: vec![],
14004 filter_range: 0..matching_prefix.len(),
14005 },
14006 server_id: LanguageServerId(usize::MAX),
14007 documentation: snippet.description.clone().map(Documentation::SingleLine),
14008 lsp_completion: lsp::CompletionItem {
14009 label: snippet.prefix.first().unwrap().clone(),
14010 kind: Some(CompletionItemKind::SNIPPET),
14011 label_details: snippet.description.as_ref().map(|description| {
14012 lsp::CompletionItemLabelDetails {
14013 detail: Some(description.clone()),
14014 description: None,
14015 }
14016 }),
14017 insert_text_format: Some(InsertTextFormat::SNIPPET),
14018 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
14019 lsp::InsertReplaceEdit {
14020 new_text: snippet.body.clone(),
14021 insert: lsp_range,
14022 replace: lsp_range,
14023 },
14024 )),
14025 filter_text: Some(snippet.body.clone()),
14026 sort_text: Some(char::MAX.to_string()),
14027 ..Default::default()
14028 },
14029 confirm: None,
14030 })
14031 })
14032 .collect();
14033
14034 Ok(result)
14035 })
14036}
14037
14038impl CompletionProvider for Model<Project> {
14039 fn completions(
14040 &self,
14041 buffer: &Model<Buffer>,
14042 buffer_position: text::Anchor,
14043 options: CompletionContext,
14044 cx: &mut ViewContext<Editor>,
14045 ) -> Task<Result<Vec<Completion>>> {
14046 self.update(cx, |project, cx| {
14047 let snippets = snippet_completions(project, buffer, buffer_position, cx);
14048 let project_completions = project.completions(buffer, buffer_position, options, cx);
14049 cx.background_executor().spawn(async move {
14050 let mut completions = project_completions.await?;
14051 let snippets_completions = snippets.await?;
14052 completions.extend(snippets_completions);
14053 Ok(completions)
14054 })
14055 })
14056 }
14057
14058 fn resolve_completions(
14059 &self,
14060 buffer: Model<Buffer>,
14061 completion_indices: Vec<usize>,
14062 completions: Rc<RefCell<Box<[Completion]>>>,
14063 cx: &mut ViewContext<Editor>,
14064 ) -> Task<Result<bool>> {
14065 self.update(cx, |project, cx| {
14066 project.lsp_store().update(cx, |lsp_store, cx| {
14067 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
14068 })
14069 })
14070 }
14071
14072 fn apply_additional_edits_for_completion(
14073 &self,
14074 buffer: Model<Buffer>,
14075 completions: Rc<RefCell<Box<[Completion]>>>,
14076 completion_index: usize,
14077 push_to_history: bool,
14078 cx: &mut ViewContext<Editor>,
14079 ) -> Task<Result<Option<language::Transaction>>> {
14080 self.update(cx, |project, cx| {
14081 project.lsp_store().update(cx, |lsp_store, cx| {
14082 lsp_store.apply_additional_edits_for_completion(
14083 buffer,
14084 completions,
14085 completion_index,
14086 push_to_history,
14087 cx,
14088 )
14089 })
14090 })
14091 }
14092
14093 fn is_completion_trigger(
14094 &self,
14095 buffer: &Model<Buffer>,
14096 position: language::Anchor,
14097 text: &str,
14098 trigger_in_words: bool,
14099 cx: &mut ViewContext<Editor>,
14100 ) -> bool {
14101 let mut chars = text.chars();
14102 let char = if let Some(char) = chars.next() {
14103 char
14104 } else {
14105 return false;
14106 };
14107 if chars.next().is_some() {
14108 return false;
14109 }
14110
14111 let buffer = buffer.read(cx);
14112 let snapshot = buffer.snapshot();
14113 if !snapshot.settings_at(position, cx).show_completions_on_input {
14114 return false;
14115 }
14116 let classifier = snapshot.char_classifier_at(position).for_completion(true);
14117 if trigger_in_words && classifier.is_word(char) {
14118 return true;
14119 }
14120
14121 buffer.completion_triggers().contains(text)
14122 }
14123}
14124
14125impl SemanticsProvider for Model<Project> {
14126 fn hover(
14127 &self,
14128 buffer: &Model<Buffer>,
14129 position: text::Anchor,
14130 cx: &mut AppContext,
14131 ) -> Option<Task<Vec<project::Hover>>> {
14132 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
14133 }
14134
14135 fn document_highlights(
14136 &self,
14137 buffer: &Model<Buffer>,
14138 position: text::Anchor,
14139 cx: &mut AppContext,
14140 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
14141 Some(self.update(cx, |project, cx| {
14142 project.document_highlights(buffer, position, cx)
14143 }))
14144 }
14145
14146 fn definitions(
14147 &self,
14148 buffer: &Model<Buffer>,
14149 position: text::Anchor,
14150 kind: GotoDefinitionKind,
14151 cx: &mut AppContext,
14152 ) -> Option<Task<Result<Vec<LocationLink>>>> {
14153 Some(self.update(cx, |project, cx| match kind {
14154 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
14155 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
14156 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
14157 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
14158 }))
14159 }
14160
14161 fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool {
14162 // TODO: make this work for remote projects
14163 self.read(cx)
14164 .language_servers_for_local_buffer(buffer.read(cx), cx)
14165 .any(
14166 |(_, server)| match server.capabilities().inlay_hint_provider {
14167 Some(lsp::OneOf::Left(enabled)) => enabled,
14168 Some(lsp::OneOf::Right(_)) => true,
14169 None => false,
14170 },
14171 )
14172 }
14173
14174 fn inlay_hints(
14175 &self,
14176 buffer_handle: Model<Buffer>,
14177 range: Range<text::Anchor>,
14178 cx: &mut AppContext,
14179 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
14180 Some(self.update(cx, |project, cx| {
14181 project.inlay_hints(buffer_handle, range, cx)
14182 }))
14183 }
14184
14185 fn resolve_inlay_hint(
14186 &self,
14187 hint: InlayHint,
14188 buffer_handle: Model<Buffer>,
14189 server_id: LanguageServerId,
14190 cx: &mut AppContext,
14191 ) -> Option<Task<anyhow::Result<InlayHint>>> {
14192 Some(self.update(cx, |project, cx| {
14193 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
14194 }))
14195 }
14196
14197 fn range_for_rename(
14198 &self,
14199 buffer: &Model<Buffer>,
14200 position: text::Anchor,
14201 cx: &mut AppContext,
14202 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
14203 Some(self.update(cx, |project, cx| {
14204 let buffer = buffer.clone();
14205 let task = project.prepare_rename(buffer.clone(), position, cx);
14206 cx.spawn(|_, mut cx| async move {
14207 Ok(match task.await? {
14208 PrepareRenameResponse::Success(range) => Some(range),
14209 PrepareRenameResponse::InvalidPosition => None,
14210 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
14211 // Fallback on using TreeSitter info to determine identifier range
14212 buffer.update(&mut cx, |buffer, _| {
14213 let snapshot = buffer.snapshot();
14214 let (range, kind) = snapshot.surrounding_word(position);
14215 if kind != Some(CharKind::Word) {
14216 return None;
14217 }
14218 Some(
14219 snapshot.anchor_before(range.start)
14220 ..snapshot.anchor_after(range.end),
14221 )
14222 })?
14223 }
14224 })
14225 })
14226 }))
14227 }
14228
14229 fn perform_rename(
14230 &self,
14231 buffer: &Model<Buffer>,
14232 position: text::Anchor,
14233 new_name: String,
14234 cx: &mut AppContext,
14235 ) -> Option<Task<Result<ProjectTransaction>>> {
14236 Some(self.update(cx, |project, cx| {
14237 project.perform_rename(buffer.clone(), position, new_name, cx)
14238 }))
14239 }
14240}
14241
14242fn inlay_hint_settings(
14243 location: Anchor,
14244 snapshot: &MultiBufferSnapshot,
14245 cx: &mut ViewContext<Editor>,
14246) -> InlayHintSettings {
14247 let file = snapshot.file_at(location);
14248 let language = snapshot.language_at(location).map(|l| l.name());
14249 language_settings(language, file, cx).inlay_hints
14250}
14251
14252fn consume_contiguous_rows(
14253 contiguous_row_selections: &mut Vec<Selection<Point>>,
14254 selection: &Selection<Point>,
14255 display_map: &DisplaySnapshot,
14256 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
14257) -> (MultiBufferRow, MultiBufferRow) {
14258 contiguous_row_selections.push(selection.clone());
14259 let start_row = MultiBufferRow(selection.start.row);
14260 let mut end_row = ending_row(selection, display_map);
14261
14262 while let Some(next_selection) = selections.peek() {
14263 if next_selection.start.row <= end_row.0 {
14264 end_row = ending_row(next_selection, display_map);
14265 contiguous_row_selections.push(selections.next().unwrap().clone());
14266 } else {
14267 break;
14268 }
14269 }
14270 (start_row, end_row)
14271}
14272
14273fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
14274 if next_selection.end.column > 0 || next_selection.is_empty() {
14275 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
14276 } else {
14277 MultiBufferRow(next_selection.end.row)
14278 }
14279}
14280
14281impl EditorSnapshot {
14282 pub fn remote_selections_in_range<'a>(
14283 &'a self,
14284 range: &'a Range<Anchor>,
14285 collaboration_hub: &dyn CollaborationHub,
14286 cx: &'a AppContext,
14287 ) -> impl 'a + Iterator<Item = RemoteSelection> {
14288 let participant_names = collaboration_hub.user_names(cx);
14289 let participant_indices = collaboration_hub.user_participant_indices(cx);
14290 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
14291 let collaborators_by_replica_id = collaborators_by_peer_id
14292 .iter()
14293 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
14294 .collect::<HashMap<_, _>>();
14295 self.buffer_snapshot
14296 .selections_in_range(range, false)
14297 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
14298 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
14299 let participant_index = participant_indices.get(&collaborator.user_id).copied();
14300 let user_name = participant_names.get(&collaborator.user_id).cloned();
14301 Some(RemoteSelection {
14302 replica_id,
14303 selection,
14304 cursor_shape,
14305 line_mode,
14306 participant_index,
14307 peer_id: collaborator.peer_id,
14308 user_name,
14309 })
14310 })
14311 }
14312
14313 pub fn hunks_for_ranges(
14314 &self,
14315 ranges: impl Iterator<Item = Range<Point>>,
14316 ) -> Vec<MultiBufferDiffHunk> {
14317 let mut hunks = Vec::new();
14318 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
14319 HashMap::default();
14320 for query_range in ranges {
14321 let query_rows =
14322 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
14323 for hunk in self.buffer_snapshot.diff_hunks_in_range(
14324 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
14325 ) {
14326 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
14327 // when the caret is just above or just below the deleted hunk.
14328 let allow_adjacent = hunk.status() == DiffHunkStatus::Removed;
14329 let related_to_selection = if allow_adjacent {
14330 hunk.row_range.overlaps(&query_rows)
14331 || hunk.row_range.start == query_rows.end
14332 || hunk.row_range.end == query_rows.start
14333 } else {
14334 hunk.row_range.overlaps(&query_rows)
14335 };
14336 if related_to_selection {
14337 if !processed_buffer_rows
14338 .entry(hunk.buffer_id)
14339 .or_default()
14340 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
14341 {
14342 continue;
14343 }
14344 hunks.push(hunk);
14345 }
14346 }
14347 }
14348
14349 hunks
14350 }
14351
14352 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
14353 self.display_snapshot.buffer_snapshot.language_at(position)
14354 }
14355
14356 pub fn is_focused(&self) -> bool {
14357 self.is_focused
14358 }
14359
14360 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
14361 self.placeholder_text.as_ref()
14362 }
14363
14364 pub fn scroll_position(&self) -> gpui::Point<f32> {
14365 self.scroll_anchor.scroll_position(&self.display_snapshot)
14366 }
14367
14368 fn gutter_dimensions(
14369 &self,
14370 font_id: FontId,
14371 font_size: Pixels,
14372 em_width: Pixels,
14373 em_advance: Pixels,
14374 max_line_number_width: Pixels,
14375 cx: &AppContext,
14376 ) -> GutterDimensions {
14377 if !self.show_gutter {
14378 return GutterDimensions::default();
14379 }
14380 let descent = cx.text_system().descent(font_id, font_size);
14381
14382 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
14383 matches!(
14384 ProjectSettings::get_global(cx).git.git_gutter,
14385 Some(GitGutterSetting::TrackedFiles)
14386 )
14387 });
14388 let gutter_settings = EditorSettings::get_global(cx).gutter;
14389 let show_line_numbers = self
14390 .show_line_numbers
14391 .unwrap_or(gutter_settings.line_numbers);
14392 let line_gutter_width = if show_line_numbers {
14393 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
14394 let min_width_for_number_on_gutter = em_advance * 4.0;
14395 max_line_number_width.max(min_width_for_number_on_gutter)
14396 } else {
14397 0.0.into()
14398 };
14399
14400 let show_code_actions = self
14401 .show_code_actions
14402 .unwrap_or(gutter_settings.code_actions);
14403
14404 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
14405
14406 let git_blame_entries_width =
14407 self.git_blame_gutter_max_author_length
14408 .map(|max_author_length| {
14409 // Length of the author name, but also space for the commit hash,
14410 // the spacing and the timestamp.
14411 let max_char_count = max_author_length
14412 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
14413 + 7 // length of commit sha
14414 + 14 // length of max relative timestamp ("60 minutes ago")
14415 + 4; // gaps and margins
14416
14417 em_advance * max_char_count
14418 });
14419
14420 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
14421 left_padding += if show_code_actions || show_runnables {
14422 em_width * 3.0
14423 } else if show_git_gutter && show_line_numbers {
14424 em_width * 2.0
14425 } else if show_git_gutter || show_line_numbers {
14426 em_width
14427 } else {
14428 px(0.)
14429 };
14430
14431 let right_padding = if gutter_settings.folds && show_line_numbers {
14432 em_width * 4.0
14433 } else if gutter_settings.folds {
14434 em_width * 3.0
14435 } else if show_line_numbers {
14436 em_width
14437 } else {
14438 px(0.)
14439 };
14440
14441 GutterDimensions {
14442 left_padding,
14443 right_padding,
14444 width: line_gutter_width + left_padding + right_padding,
14445 margin: -descent,
14446 git_blame_entries_width,
14447 }
14448 }
14449
14450 pub fn render_crease_toggle(
14451 &self,
14452 buffer_row: MultiBufferRow,
14453 row_contains_cursor: bool,
14454 editor: View<Editor>,
14455 cx: &mut WindowContext,
14456 ) -> Option<AnyElement> {
14457 let folded = self.is_line_folded(buffer_row);
14458 let mut is_foldable = false;
14459
14460 if let Some(crease) = self
14461 .crease_snapshot
14462 .query_row(buffer_row, &self.buffer_snapshot)
14463 {
14464 is_foldable = true;
14465 match crease {
14466 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
14467 if let Some(render_toggle) = render_toggle {
14468 let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
14469 if folded {
14470 editor.update(cx, |editor, cx| {
14471 editor.fold_at(&crate::FoldAt { buffer_row }, cx)
14472 });
14473 } else {
14474 editor.update(cx, |editor, cx| {
14475 editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
14476 });
14477 }
14478 });
14479 return Some((render_toggle)(buffer_row, folded, toggle_callback, cx));
14480 }
14481 }
14482 }
14483 }
14484
14485 is_foldable |= self.starts_indent(buffer_row);
14486
14487 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
14488 Some(
14489 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
14490 .toggle_state(folded)
14491 .on_click(cx.listener_for(&editor, move |this, _e, cx| {
14492 if folded {
14493 this.unfold_at(&UnfoldAt { buffer_row }, cx);
14494 } else {
14495 this.fold_at(&FoldAt { buffer_row }, cx);
14496 }
14497 }))
14498 .into_any_element(),
14499 )
14500 } else {
14501 None
14502 }
14503 }
14504
14505 pub fn render_crease_trailer(
14506 &self,
14507 buffer_row: MultiBufferRow,
14508 cx: &mut WindowContext,
14509 ) -> Option<AnyElement> {
14510 let folded = self.is_line_folded(buffer_row);
14511 if let Crease::Inline { render_trailer, .. } = self
14512 .crease_snapshot
14513 .query_row(buffer_row, &self.buffer_snapshot)?
14514 {
14515 let render_trailer = render_trailer.as_ref()?;
14516 Some(render_trailer(buffer_row, folded, cx))
14517 } else {
14518 None
14519 }
14520 }
14521}
14522
14523impl Deref for EditorSnapshot {
14524 type Target = DisplaySnapshot;
14525
14526 fn deref(&self) -> &Self::Target {
14527 &self.display_snapshot
14528 }
14529}
14530
14531#[derive(Clone, Debug, PartialEq, Eq)]
14532pub enum EditorEvent {
14533 InputIgnored {
14534 text: Arc<str>,
14535 },
14536 InputHandled {
14537 utf16_range_to_replace: Option<Range<isize>>,
14538 text: Arc<str>,
14539 },
14540 ExcerptsAdded {
14541 buffer: Model<Buffer>,
14542 predecessor: ExcerptId,
14543 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
14544 },
14545 ExcerptsRemoved {
14546 ids: Vec<ExcerptId>,
14547 },
14548 BufferFoldToggled {
14549 ids: Vec<ExcerptId>,
14550 folded: bool,
14551 },
14552 ExcerptsEdited {
14553 ids: Vec<ExcerptId>,
14554 },
14555 ExcerptsExpanded {
14556 ids: Vec<ExcerptId>,
14557 },
14558 BufferEdited,
14559 Edited {
14560 transaction_id: clock::Lamport,
14561 },
14562 Reparsed(BufferId),
14563 Focused,
14564 FocusedIn,
14565 Blurred,
14566 DirtyChanged,
14567 Saved,
14568 TitleChanged,
14569 DiffBaseChanged,
14570 SelectionsChanged {
14571 local: bool,
14572 },
14573 ScrollPositionChanged {
14574 local: bool,
14575 autoscroll: bool,
14576 },
14577 Closed,
14578 TransactionUndone {
14579 transaction_id: clock::Lamport,
14580 },
14581 TransactionBegun {
14582 transaction_id: clock::Lamport,
14583 },
14584 Reloaded,
14585 CursorShapeChanged,
14586}
14587
14588impl EventEmitter<EditorEvent> for Editor {}
14589
14590impl FocusableView for Editor {
14591 fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
14592 self.focus_handle.clone()
14593 }
14594}
14595
14596impl Render for Editor {
14597 fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
14598 let settings = ThemeSettings::get_global(cx);
14599
14600 let mut text_style = match self.mode {
14601 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
14602 color: cx.theme().colors().editor_foreground,
14603 font_family: settings.ui_font.family.clone(),
14604 font_features: settings.ui_font.features.clone(),
14605 font_fallbacks: settings.ui_font.fallbacks.clone(),
14606 font_size: rems(0.875).into(),
14607 font_weight: settings.ui_font.weight,
14608 line_height: relative(settings.buffer_line_height.value()),
14609 ..Default::default()
14610 },
14611 EditorMode::Full => TextStyle {
14612 color: cx.theme().colors().editor_foreground,
14613 font_family: settings.buffer_font.family.clone(),
14614 font_features: settings.buffer_font.features.clone(),
14615 font_fallbacks: settings.buffer_font.fallbacks.clone(),
14616 font_size: settings.buffer_font_size().into(),
14617 font_weight: settings.buffer_font.weight,
14618 line_height: relative(settings.buffer_line_height.value()),
14619 ..Default::default()
14620 },
14621 };
14622 if let Some(text_style_refinement) = &self.text_style_refinement {
14623 text_style.refine(text_style_refinement)
14624 }
14625
14626 let background = match self.mode {
14627 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
14628 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
14629 EditorMode::Full => cx.theme().colors().editor_background,
14630 };
14631
14632 EditorElement::new(
14633 cx.view(),
14634 EditorStyle {
14635 background,
14636 local_player: cx.theme().players().local(),
14637 text: text_style,
14638 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
14639 syntax: cx.theme().syntax().clone(),
14640 status: cx.theme().status().clone(),
14641 inlay_hints_style: make_inlay_hints_style(cx),
14642 inline_completion_styles: make_suggestion_styles(cx),
14643 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
14644 },
14645 )
14646 }
14647}
14648
14649impl ViewInputHandler for Editor {
14650 fn text_for_range(
14651 &mut self,
14652 range_utf16: Range<usize>,
14653 adjusted_range: &mut Option<Range<usize>>,
14654 cx: &mut ViewContext<Self>,
14655 ) -> Option<String> {
14656 let snapshot = self.buffer.read(cx).read(cx);
14657 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
14658 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
14659 if (start.0..end.0) != range_utf16 {
14660 adjusted_range.replace(start.0..end.0);
14661 }
14662 Some(snapshot.text_for_range(start..end).collect())
14663 }
14664
14665 fn selected_text_range(
14666 &mut self,
14667 ignore_disabled_input: bool,
14668 cx: &mut ViewContext<Self>,
14669 ) -> Option<UTF16Selection> {
14670 // Prevent the IME menu from appearing when holding down an alphabetic key
14671 // while input is disabled.
14672 if !ignore_disabled_input && !self.input_enabled {
14673 return None;
14674 }
14675
14676 let selection = self.selections.newest::<OffsetUtf16>(cx);
14677 let range = selection.range();
14678
14679 Some(UTF16Selection {
14680 range: range.start.0..range.end.0,
14681 reversed: selection.reversed,
14682 })
14683 }
14684
14685 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
14686 let snapshot = self.buffer.read(cx).read(cx);
14687 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
14688 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
14689 }
14690
14691 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
14692 self.clear_highlights::<InputComposition>(cx);
14693 self.ime_transaction.take();
14694 }
14695
14696 fn replace_text_in_range(
14697 &mut self,
14698 range_utf16: Option<Range<usize>>,
14699 text: &str,
14700 cx: &mut ViewContext<Self>,
14701 ) {
14702 if !self.input_enabled {
14703 cx.emit(EditorEvent::InputIgnored { text: text.into() });
14704 return;
14705 }
14706
14707 self.transact(cx, |this, cx| {
14708 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
14709 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
14710 Some(this.selection_replacement_ranges(range_utf16, cx))
14711 } else {
14712 this.marked_text_ranges(cx)
14713 };
14714
14715 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
14716 let newest_selection_id = this.selections.newest_anchor().id;
14717 this.selections
14718 .all::<OffsetUtf16>(cx)
14719 .iter()
14720 .zip(ranges_to_replace.iter())
14721 .find_map(|(selection, range)| {
14722 if selection.id == newest_selection_id {
14723 Some(
14724 (range.start.0 as isize - selection.head().0 as isize)
14725 ..(range.end.0 as isize - selection.head().0 as isize),
14726 )
14727 } else {
14728 None
14729 }
14730 })
14731 });
14732
14733 cx.emit(EditorEvent::InputHandled {
14734 utf16_range_to_replace: range_to_replace,
14735 text: text.into(),
14736 });
14737
14738 if let Some(new_selected_ranges) = new_selected_ranges {
14739 this.change_selections(None, cx, |selections| {
14740 selections.select_ranges(new_selected_ranges)
14741 });
14742 this.backspace(&Default::default(), cx);
14743 }
14744
14745 this.handle_input(text, cx);
14746 });
14747
14748 if let Some(transaction) = self.ime_transaction {
14749 self.buffer.update(cx, |buffer, cx| {
14750 buffer.group_until_transaction(transaction, cx);
14751 });
14752 }
14753
14754 self.unmark_text(cx);
14755 }
14756
14757 fn replace_and_mark_text_in_range(
14758 &mut self,
14759 range_utf16: Option<Range<usize>>,
14760 text: &str,
14761 new_selected_range_utf16: Option<Range<usize>>,
14762 cx: &mut ViewContext<Self>,
14763 ) {
14764 if !self.input_enabled {
14765 return;
14766 }
14767
14768 let transaction = self.transact(cx, |this, cx| {
14769 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
14770 let snapshot = this.buffer.read(cx).read(cx);
14771 if let Some(relative_range_utf16) = range_utf16.as_ref() {
14772 for marked_range in &mut marked_ranges {
14773 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
14774 marked_range.start.0 += relative_range_utf16.start;
14775 marked_range.start =
14776 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
14777 marked_range.end =
14778 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
14779 }
14780 }
14781 Some(marked_ranges)
14782 } else if let Some(range_utf16) = range_utf16 {
14783 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
14784 Some(this.selection_replacement_ranges(range_utf16, cx))
14785 } else {
14786 None
14787 };
14788
14789 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
14790 let newest_selection_id = this.selections.newest_anchor().id;
14791 this.selections
14792 .all::<OffsetUtf16>(cx)
14793 .iter()
14794 .zip(ranges_to_replace.iter())
14795 .find_map(|(selection, range)| {
14796 if selection.id == newest_selection_id {
14797 Some(
14798 (range.start.0 as isize - selection.head().0 as isize)
14799 ..(range.end.0 as isize - selection.head().0 as isize),
14800 )
14801 } else {
14802 None
14803 }
14804 })
14805 });
14806
14807 cx.emit(EditorEvent::InputHandled {
14808 utf16_range_to_replace: range_to_replace,
14809 text: text.into(),
14810 });
14811
14812 if let Some(ranges) = ranges_to_replace {
14813 this.change_selections(None, cx, |s| s.select_ranges(ranges));
14814 }
14815
14816 let marked_ranges = {
14817 let snapshot = this.buffer.read(cx).read(cx);
14818 this.selections
14819 .disjoint_anchors()
14820 .iter()
14821 .map(|selection| {
14822 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
14823 })
14824 .collect::<Vec<_>>()
14825 };
14826
14827 if text.is_empty() {
14828 this.unmark_text(cx);
14829 } else {
14830 this.highlight_text::<InputComposition>(
14831 marked_ranges.clone(),
14832 HighlightStyle {
14833 underline: Some(UnderlineStyle {
14834 thickness: px(1.),
14835 color: None,
14836 wavy: false,
14837 }),
14838 ..Default::default()
14839 },
14840 cx,
14841 );
14842 }
14843
14844 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
14845 let use_autoclose = this.use_autoclose;
14846 let use_auto_surround = this.use_auto_surround;
14847 this.set_use_autoclose(false);
14848 this.set_use_auto_surround(false);
14849 this.handle_input(text, cx);
14850 this.set_use_autoclose(use_autoclose);
14851 this.set_use_auto_surround(use_auto_surround);
14852
14853 if let Some(new_selected_range) = new_selected_range_utf16 {
14854 let snapshot = this.buffer.read(cx).read(cx);
14855 let new_selected_ranges = marked_ranges
14856 .into_iter()
14857 .map(|marked_range| {
14858 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
14859 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
14860 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
14861 snapshot.clip_offset_utf16(new_start, Bias::Left)
14862 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
14863 })
14864 .collect::<Vec<_>>();
14865
14866 drop(snapshot);
14867 this.change_selections(None, cx, |selections| {
14868 selections.select_ranges(new_selected_ranges)
14869 });
14870 }
14871 });
14872
14873 self.ime_transaction = self.ime_transaction.or(transaction);
14874 if let Some(transaction) = self.ime_transaction {
14875 self.buffer.update(cx, |buffer, cx| {
14876 buffer.group_until_transaction(transaction, cx);
14877 });
14878 }
14879
14880 if self.text_highlights::<InputComposition>(cx).is_none() {
14881 self.ime_transaction.take();
14882 }
14883 }
14884
14885 fn bounds_for_range(
14886 &mut self,
14887 range_utf16: Range<usize>,
14888 element_bounds: gpui::Bounds<Pixels>,
14889 cx: &mut ViewContext<Self>,
14890 ) -> Option<gpui::Bounds<Pixels>> {
14891 let text_layout_details = self.text_layout_details(cx);
14892 let gpui::Point {
14893 x: em_width,
14894 y: line_height,
14895 } = self.character_size(cx);
14896
14897 let snapshot = self.snapshot(cx);
14898 let scroll_position = snapshot.scroll_position();
14899 let scroll_left = scroll_position.x * em_width;
14900
14901 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
14902 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
14903 + self.gutter_dimensions.width
14904 + self.gutter_dimensions.margin;
14905 let y = line_height * (start.row().as_f32() - scroll_position.y);
14906
14907 Some(Bounds {
14908 origin: element_bounds.origin + point(x, y),
14909 size: size(em_width, line_height),
14910 })
14911 }
14912}
14913
14914trait SelectionExt {
14915 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
14916 fn spanned_rows(
14917 &self,
14918 include_end_if_at_line_start: bool,
14919 map: &DisplaySnapshot,
14920 ) -> Range<MultiBufferRow>;
14921}
14922
14923impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
14924 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
14925 let start = self
14926 .start
14927 .to_point(&map.buffer_snapshot)
14928 .to_display_point(map);
14929 let end = self
14930 .end
14931 .to_point(&map.buffer_snapshot)
14932 .to_display_point(map);
14933 if self.reversed {
14934 end..start
14935 } else {
14936 start..end
14937 }
14938 }
14939
14940 fn spanned_rows(
14941 &self,
14942 include_end_if_at_line_start: bool,
14943 map: &DisplaySnapshot,
14944 ) -> Range<MultiBufferRow> {
14945 let start = self.start.to_point(&map.buffer_snapshot);
14946 let mut end = self.end.to_point(&map.buffer_snapshot);
14947 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
14948 end.row -= 1;
14949 }
14950
14951 let buffer_start = map.prev_line_boundary(start).0;
14952 let buffer_end = map.next_line_boundary(end).0;
14953 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
14954 }
14955}
14956
14957impl<T: InvalidationRegion> InvalidationStack<T> {
14958 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
14959 where
14960 S: Clone + ToOffset,
14961 {
14962 while let Some(region) = self.last() {
14963 let all_selections_inside_invalidation_ranges =
14964 if selections.len() == region.ranges().len() {
14965 selections
14966 .iter()
14967 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
14968 .all(|(selection, invalidation_range)| {
14969 let head = selection.head().to_offset(buffer);
14970 invalidation_range.start <= head && invalidation_range.end >= head
14971 })
14972 } else {
14973 false
14974 };
14975
14976 if all_selections_inside_invalidation_ranges {
14977 break;
14978 } else {
14979 self.pop();
14980 }
14981 }
14982 }
14983}
14984
14985impl<T> Default for InvalidationStack<T> {
14986 fn default() -> Self {
14987 Self(Default::default())
14988 }
14989}
14990
14991impl<T> Deref for InvalidationStack<T> {
14992 type Target = Vec<T>;
14993
14994 fn deref(&self) -> &Self::Target {
14995 &self.0
14996 }
14997}
14998
14999impl<T> DerefMut for InvalidationStack<T> {
15000 fn deref_mut(&mut self) -> &mut Self::Target {
15001 &mut self.0
15002 }
15003}
15004
15005impl InvalidationRegion for SnippetState {
15006 fn ranges(&self) -> &[Range<Anchor>] {
15007 &self.ranges[self.active_index]
15008 }
15009}
15010
15011pub fn diagnostic_block_renderer(
15012 diagnostic: Diagnostic,
15013 max_message_rows: Option<u8>,
15014 allow_closing: bool,
15015 _is_valid: bool,
15016) -> RenderBlock {
15017 let (text_without_backticks, code_ranges) =
15018 highlight_diagnostic_message(&diagnostic, max_message_rows);
15019
15020 Arc::new(move |cx: &mut BlockContext| {
15021 let group_id: SharedString = cx.block_id.to_string().into();
15022
15023 let mut text_style = cx.text_style().clone();
15024 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
15025 let theme_settings = ThemeSettings::get_global(cx);
15026 text_style.font_family = theme_settings.buffer_font.family.clone();
15027 text_style.font_style = theme_settings.buffer_font.style;
15028 text_style.font_features = theme_settings.buffer_font.features.clone();
15029 text_style.font_weight = theme_settings.buffer_font.weight;
15030
15031 let multi_line_diagnostic = diagnostic.message.contains('\n');
15032
15033 let buttons = |diagnostic: &Diagnostic| {
15034 if multi_line_diagnostic {
15035 v_flex()
15036 } else {
15037 h_flex()
15038 }
15039 .when(allow_closing, |div| {
15040 div.children(diagnostic.is_primary.then(|| {
15041 IconButton::new("close-block", IconName::XCircle)
15042 .icon_color(Color::Muted)
15043 .size(ButtonSize::Compact)
15044 .style(ButtonStyle::Transparent)
15045 .visible_on_hover(group_id.clone())
15046 .on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
15047 .tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
15048 }))
15049 })
15050 .child(
15051 IconButton::new("copy-block", IconName::Copy)
15052 .icon_color(Color::Muted)
15053 .size(ButtonSize::Compact)
15054 .style(ButtonStyle::Transparent)
15055 .visible_on_hover(group_id.clone())
15056 .on_click({
15057 let message = diagnostic.message.clone();
15058 move |_click, cx| {
15059 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
15060 }
15061 })
15062 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
15063 )
15064 };
15065
15066 let icon_size = buttons(&diagnostic)
15067 .into_any_element()
15068 .layout_as_root(AvailableSpace::min_size(), cx);
15069
15070 h_flex()
15071 .id(cx.block_id)
15072 .group(group_id.clone())
15073 .relative()
15074 .size_full()
15075 .block_mouse_down()
15076 .pl(cx.gutter_dimensions.width)
15077 .w(cx.max_width - cx.gutter_dimensions.full_width())
15078 .child(
15079 div()
15080 .flex()
15081 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
15082 .flex_shrink(),
15083 )
15084 .child(buttons(&diagnostic))
15085 .child(div().flex().flex_shrink_0().child(
15086 StyledText::new(text_without_backticks.clone()).with_highlights(
15087 &text_style,
15088 code_ranges.iter().map(|range| {
15089 (
15090 range.clone(),
15091 HighlightStyle {
15092 font_weight: Some(FontWeight::BOLD),
15093 ..Default::default()
15094 },
15095 )
15096 }),
15097 ),
15098 ))
15099 .into_any_element()
15100 })
15101}
15102
15103fn inline_completion_edit_text(
15104 current_snapshot: &BufferSnapshot,
15105 edits: &[(Range<Anchor>, String)],
15106 edit_preview: &EditPreview,
15107 include_deletions: bool,
15108 cx: &WindowContext,
15109) -> Option<HighlightedEdits> {
15110 let edits = edits
15111 .iter()
15112 .map(|(anchor, text)| {
15113 (
15114 anchor.start.text_anchor..anchor.end.text_anchor,
15115 text.clone(),
15116 )
15117 })
15118 .collect::<Vec<_>>();
15119
15120 Some(edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx))
15121}
15122
15123pub fn highlight_diagnostic_message(
15124 diagnostic: &Diagnostic,
15125 mut max_message_rows: Option<u8>,
15126) -> (SharedString, Vec<Range<usize>>) {
15127 let mut text_without_backticks = String::new();
15128 let mut code_ranges = Vec::new();
15129
15130 if let Some(source) = &diagnostic.source {
15131 text_without_backticks.push_str(source);
15132 code_ranges.push(0..source.len());
15133 text_without_backticks.push_str(": ");
15134 }
15135
15136 let mut prev_offset = 0;
15137 let mut in_code_block = false;
15138 let has_row_limit = max_message_rows.is_some();
15139 let mut newline_indices = diagnostic
15140 .message
15141 .match_indices('\n')
15142 .filter(|_| has_row_limit)
15143 .map(|(ix, _)| ix)
15144 .fuse()
15145 .peekable();
15146
15147 for (quote_ix, _) in diagnostic
15148 .message
15149 .match_indices('`')
15150 .chain([(diagnostic.message.len(), "")])
15151 {
15152 let mut first_newline_ix = None;
15153 let mut last_newline_ix = None;
15154 while let Some(newline_ix) = newline_indices.peek() {
15155 if *newline_ix < quote_ix {
15156 if first_newline_ix.is_none() {
15157 first_newline_ix = Some(*newline_ix);
15158 }
15159 last_newline_ix = Some(*newline_ix);
15160
15161 if let Some(rows_left) = &mut max_message_rows {
15162 if *rows_left == 0 {
15163 break;
15164 } else {
15165 *rows_left -= 1;
15166 }
15167 }
15168 let _ = newline_indices.next();
15169 } else {
15170 break;
15171 }
15172 }
15173 let prev_len = text_without_backticks.len();
15174 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
15175 text_without_backticks.push_str(new_text);
15176 if in_code_block {
15177 code_ranges.push(prev_len..text_without_backticks.len());
15178 }
15179 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
15180 in_code_block = !in_code_block;
15181 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
15182 text_without_backticks.push_str("...");
15183 break;
15184 }
15185 }
15186
15187 (text_without_backticks.into(), code_ranges)
15188}
15189
15190fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
15191 match severity {
15192 DiagnosticSeverity::ERROR => colors.error,
15193 DiagnosticSeverity::WARNING => colors.warning,
15194 DiagnosticSeverity::INFORMATION => colors.info,
15195 DiagnosticSeverity::HINT => colors.info,
15196 _ => colors.ignored,
15197 }
15198}
15199
15200pub fn styled_runs_for_code_label<'a>(
15201 label: &'a CodeLabel,
15202 syntax_theme: &'a theme::SyntaxTheme,
15203) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
15204 let fade_out = HighlightStyle {
15205 fade_out: Some(0.35),
15206 ..Default::default()
15207 };
15208
15209 let mut prev_end = label.filter_range.end;
15210 label
15211 .runs
15212 .iter()
15213 .enumerate()
15214 .flat_map(move |(ix, (range, highlight_id))| {
15215 let style = if let Some(style) = highlight_id.style(syntax_theme) {
15216 style
15217 } else {
15218 return Default::default();
15219 };
15220 let mut muted_style = style;
15221 muted_style.highlight(fade_out);
15222
15223 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
15224 if range.start >= label.filter_range.end {
15225 if range.start > prev_end {
15226 runs.push((prev_end..range.start, fade_out));
15227 }
15228 runs.push((range.clone(), muted_style));
15229 } else if range.end <= label.filter_range.end {
15230 runs.push((range.clone(), style));
15231 } else {
15232 runs.push((range.start..label.filter_range.end, style));
15233 runs.push((label.filter_range.end..range.end, muted_style));
15234 }
15235 prev_end = cmp::max(prev_end, range.end);
15236
15237 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
15238 runs.push((prev_end..label.text.len(), fade_out));
15239 }
15240
15241 runs
15242 })
15243}
15244
15245pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
15246 let mut prev_index = 0;
15247 let mut prev_codepoint: Option<char> = None;
15248 text.char_indices()
15249 .chain([(text.len(), '\0')])
15250 .filter_map(move |(index, codepoint)| {
15251 let prev_codepoint = prev_codepoint.replace(codepoint)?;
15252 let is_boundary = index == text.len()
15253 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
15254 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
15255 if is_boundary {
15256 let chunk = &text[prev_index..index];
15257 prev_index = index;
15258 Some(chunk)
15259 } else {
15260 None
15261 }
15262 })
15263}
15264
15265pub trait RangeToAnchorExt: Sized {
15266 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
15267
15268 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
15269 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
15270 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
15271 }
15272}
15273
15274impl<T: ToOffset> RangeToAnchorExt for Range<T> {
15275 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
15276 let start_offset = self.start.to_offset(snapshot);
15277 let end_offset = self.end.to_offset(snapshot);
15278 if start_offset == end_offset {
15279 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
15280 } else {
15281 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
15282 }
15283 }
15284}
15285
15286pub trait RowExt {
15287 fn as_f32(&self) -> f32;
15288
15289 fn next_row(&self) -> Self;
15290
15291 fn previous_row(&self) -> Self;
15292
15293 fn minus(&self, other: Self) -> u32;
15294}
15295
15296impl RowExt for DisplayRow {
15297 fn as_f32(&self) -> f32 {
15298 self.0 as f32
15299 }
15300
15301 fn next_row(&self) -> Self {
15302 Self(self.0 + 1)
15303 }
15304
15305 fn previous_row(&self) -> Self {
15306 Self(self.0.saturating_sub(1))
15307 }
15308
15309 fn minus(&self, other: Self) -> u32 {
15310 self.0 - other.0
15311 }
15312}
15313
15314impl RowExt for MultiBufferRow {
15315 fn as_f32(&self) -> f32 {
15316 self.0 as f32
15317 }
15318
15319 fn next_row(&self) -> Self {
15320 Self(self.0 + 1)
15321 }
15322
15323 fn previous_row(&self) -> Self {
15324 Self(self.0.saturating_sub(1))
15325 }
15326
15327 fn minus(&self, other: Self) -> u32 {
15328 self.0 - other.0
15329 }
15330}
15331
15332trait RowRangeExt {
15333 type Row;
15334
15335 fn len(&self) -> usize;
15336
15337 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
15338}
15339
15340impl RowRangeExt for Range<MultiBufferRow> {
15341 type Row = MultiBufferRow;
15342
15343 fn len(&self) -> usize {
15344 (self.end.0 - self.start.0) as usize
15345 }
15346
15347 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
15348 (self.start.0..self.end.0).map(MultiBufferRow)
15349 }
15350}
15351
15352impl RowRangeExt for Range<DisplayRow> {
15353 type Row = DisplayRow;
15354
15355 fn len(&self) -> usize {
15356 (self.end.0 - self.start.0) as usize
15357 }
15358
15359 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
15360 (self.start.0..self.end.0).map(DisplayRow)
15361 }
15362}
15363
15364/// If select range has more than one line, we
15365/// just point the cursor to range.start.
15366fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
15367 if range.start.row == range.end.row {
15368 range
15369 } else {
15370 range.start..range.start
15371 }
15372}
15373pub struct KillRing(ClipboardItem);
15374impl Global for KillRing {}
15375
15376const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
15377
15378fn all_edits_insertions_or_deletions(
15379 edits: &Vec<(Range<Anchor>, String)>,
15380 snapshot: &MultiBufferSnapshot,
15381) -> bool {
15382 let mut all_insertions = true;
15383 let mut all_deletions = true;
15384
15385 for (range, new_text) in edits.iter() {
15386 let range_is_empty = range.to_offset(&snapshot).is_empty();
15387 let text_is_empty = new_text.is_empty();
15388
15389 if range_is_empty != text_is_empty {
15390 if range_is_empty {
15391 all_deletions = false;
15392 } else {
15393 all_insertions = false;
15394 }
15395 } else {
15396 return false;
15397 }
15398
15399 if !all_insertions && !all_deletions {
15400 return false;
15401 }
15402 }
15403 all_insertions || all_deletions
15404}