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_onboarding::ZedPredictModal;
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, App,
81 AsyncWindowContext, AvailableSpace, Bounds, ClipboardEntry, ClipboardItem, Context,
82 DispatchPhase, ElementId, Entity, EntityInputHandler, EventEmitter, FocusHandle, FocusOutEvent,
83 Focusable, FontId, FontWeight, Global, HighlightStyle, Hsla, InteractiveText, KeyContext,
84 MouseButton, MouseDownEvent, PaintQuad, ParentElement, Pixels, Render, SharedString, Size,
85 Styled, StyledText, Subscription, Task, TextStyle, TextStyleRefinement, UTF16Selection,
86 UnderlineStyle, UniformListScrollHandle, WeakEntity, WeakFocusHandle, Window,
87};
88use highlight_matching_bracket::refresh_matching_bracket_highlights;
89use hover_popover::{hide_hover, HoverState};
90use indent_guides::ActiveIndentGuidesState;
91use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
92pub use inline_completion::Direction;
93use inline_completion::{InlineCompletionProvider, InlineCompletionProviderHandle};
94pub use items::MAX_TAB_TITLE_LEN;
95use itertools::Itertools;
96use language::{
97 language_settings::{self, all_language_settings, language_settings, InlayHintSettings},
98 markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
99 CompletionDocumentation, CursorShape, Diagnostic, EditPreview, HighlightedText, IndentKind,
100 IndentSize, Language, OffsetRangeExt, Point, Selection, SelectionGoal, TextObject,
101 TransactionId, TreeSitterOptions,
102};
103use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
104use linked_editing_ranges::refresh_linked_ranges;
105use mouse_context_menu::MouseContextMenu;
106pub use proposed_changes_editor::{
107 ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar,
108};
109use similar::{ChangeTag, TextDiff};
110use std::iter::Peekable;
111use task::{ResolvedTask, TaskTemplate, TaskVariables};
112
113use hover_links::{find_file, HoverLink, HoveredLinkState, InlayHighlight};
114pub use lsp::CompletionContext;
115use lsp::{
116 CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity, InsertTextFormat,
117 LanguageServerId, LanguageServerName,
118};
119
120use language::BufferSnapshot;
121use movement::TextLayoutDetails;
122pub use multi_buffer::{
123 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, RowInfo,
124 ToOffset, ToPoint,
125};
126use multi_buffer::{
127 ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow, ToOffsetUtf16,
128};
129use project::{
130 lsp_store::{FormatTrigger, LspFormatTarget, OpenLspBufferHandle},
131 project_settings::{GitGutterSetting, ProjectSettings},
132 CodeAction, Completion, CompletionIntent, DocumentHighlight, InlayHint, Location, LocationLink,
133 LspStore, PrepareRenameResponse, Project, ProjectItem, ProjectTransaction, TaskSourceKind,
134};
135use rand::prelude::*;
136use rpc::{proto::*, ErrorExt};
137use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
138use selections_collection::{
139 resolve_selections, MutableSelectionsCollection, SelectionsCollection,
140};
141use serde::{Deserialize, Serialize};
142use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
143use smallvec::SmallVec;
144use snippet::Snippet;
145use std::{
146 any::TypeId,
147 borrow::Cow,
148 cell::RefCell,
149 cmp::{self, Ordering, Reverse},
150 mem,
151 num::NonZeroU32,
152 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
153 path::{Path, PathBuf},
154 rc::Rc,
155 sync::Arc,
156 time::{Duration, Instant},
157};
158pub use sum_tree::Bias;
159use sum_tree::TreeMap;
160use text::{BufferId, OffsetUtf16, Rope};
161use theme::{ActiveTheme, PlayerColor, StatusColors, SyntaxTheme, ThemeColors, ThemeSettings};
162use ui::{
163 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize,
164 Tooltip,
165};
166use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
167use workspace::item::{ItemHandle, PreviewTabsSettings};
168use workspace::notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt};
169use workspace::{
170 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
171};
172use workspace::{Item as WorkspaceItem, OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
173
174use crate::hover_links::{find_url, find_url_from_range};
175use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
176
177pub const FILE_HEADER_HEIGHT: u32 = 2;
178pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
179pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u32 = 1;
180pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
181const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
182const MAX_LINE_LEN: usize = 1024;
183const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
184const MAX_SELECTION_HISTORY_LEN: usize = 1024;
185pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
186#[doc(hidden)]
187pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
188
189pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
190pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
191
192pub fn render_parsed_markdown(
193 element_id: impl Into<ElementId>,
194 parsed: &language::ParsedMarkdown,
195 editor_style: &EditorStyle,
196 workspace: Option<WeakEntity<Workspace>>,
197 cx: &mut App,
198) -> InteractiveText {
199 let code_span_background_color = cx
200 .theme()
201 .colors()
202 .editor_document_highlight_read_background;
203
204 let highlights = gpui::combine_highlights(
205 parsed.highlights.iter().filter_map(|(range, highlight)| {
206 let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
207 Some((range.clone(), highlight))
208 }),
209 parsed
210 .regions
211 .iter()
212 .zip(&parsed.region_ranges)
213 .filter_map(|(region, range)| {
214 if region.code {
215 Some((
216 range.clone(),
217 HighlightStyle {
218 background_color: Some(code_span_background_color),
219 ..Default::default()
220 },
221 ))
222 } else {
223 None
224 }
225 }),
226 );
227
228 let mut links = Vec::new();
229 let mut link_ranges = Vec::new();
230 for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
231 if let Some(link) = region.link.clone() {
232 links.push(link);
233 link_ranges.push(range.clone());
234 }
235 }
236
237 InteractiveText::new(
238 element_id,
239 StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
240 )
241 .on_click(
242 link_ranges,
243 move |clicked_range_ix, window, cx| 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
249 .open_abs_path(path.clone(), false, window, cx)
250 .detach();
251 });
252 }
253 }
254 },
255 )
256}
257
258#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
259pub enum InlayId {
260 InlineCompletion(usize),
261 Hint(usize),
262}
263
264impl InlayId {
265 fn id(&self) -> usize {
266 match self {
267 Self::InlineCompletion(id) => *id,
268 Self::Hint(id) => *id,
269 }
270 }
271}
272
273enum DocumentHighlightRead {}
274enum DocumentHighlightWrite {}
275enum InputComposition {}
276
277#[derive(Debug, Copy, Clone, PartialEq, Eq)]
278pub enum Navigated {
279 Yes,
280 No,
281}
282
283impl Navigated {
284 pub fn from_bool(yes: bool) -> Navigated {
285 if yes {
286 Navigated::Yes
287 } else {
288 Navigated::No
289 }
290 }
291}
292
293pub fn init_settings(cx: &mut App) {
294 EditorSettings::register(cx);
295}
296
297pub fn init(cx: &mut App) {
298 init_settings(cx);
299
300 workspace::register_project_item::<Editor>(cx);
301 workspace::FollowableViewRegistry::register::<Editor>(cx);
302 workspace::register_serializable_item::<Editor>(cx);
303
304 cx.observe_new(
305 |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
306 workspace.register_action(Editor::new_file);
307 workspace.register_action(Editor::new_file_vertical);
308 workspace.register_action(Editor::new_file_horizontal);
309 },
310 )
311 .detach();
312
313 cx.on_action(move |_: &workspace::NewFile, cx| {
314 let app_state = workspace::AppState::global(cx);
315 if let Some(app_state) = app_state.upgrade() {
316 workspace::open_new(
317 Default::default(),
318 app_state,
319 cx,
320 |workspace, window, cx| {
321 Editor::new_file(workspace, &Default::default(), window, cx)
322 },
323 )
324 .detach();
325 }
326 });
327 cx.on_action(move |_: &workspace::NewWindow, cx| {
328 let app_state = workspace::AppState::global(cx);
329 if let Some(app_state) = app_state.upgrade() {
330 workspace::open_new(
331 Default::default(),
332 app_state,
333 cx,
334 |workspace, window, cx| {
335 cx.activate(true);
336 Editor::new_file(workspace, &Default::default(), window, cx)
337 },
338 )
339 .detach();
340 }
341 });
342}
343
344pub struct SearchWithinRange;
345
346trait InvalidationRegion {
347 fn ranges(&self) -> &[Range<Anchor>];
348}
349
350#[derive(Clone, Debug, PartialEq)]
351pub enum SelectPhase {
352 Begin {
353 position: DisplayPoint,
354 add: bool,
355 click_count: usize,
356 },
357 BeginColumnar {
358 position: DisplayPoint,
359 reset: bool,
360 goal_column: u32,
361 },
362 Extend {
363 position: DisplayPoint,
364 click_count: usize,
365 },
366 Update {
367 position: DisplayPoint,
368 goal_column: u32,
369 scroll_delta: gpui::Point<f32>,
370 },
371 End,
372}
373
374#[derive(Clone, Debug)]
375pub enum SelectMode {
376 Character,
377 Word(Range<Anchor>),
378 Line(Range<Anchor>),
379 All,
380}
381
382#[derive(Copy, Clone, PartialEq, Eq, Debug)]
383pub enum EditorMode {
384 SingleLine { auto_width: bool },
385 AutoHeight { max_lines: usize },
386 Full,
387}
388
389#[derive(Copy, Clone, Debug)]
390pub enum SoftWrap {
391 /// Prefer not to wrap at all.
392 ///
393 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
394 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
395 GitDiff,
396 /// Prefer a single line generally, unless an overly long line is encountered.
397 None,
398 /// Soft wrap lines that exceed the editor width.
399 EditorWidth,
400 /// Soft wrap lines at the preferred line length.
401 Column(u32),
402 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
403 Bounded(u32),
404}
405
406#[derive(Clone)]
407pub struct EditorStyle {
408 pub background: Hsla,
409 pub local_player: PlayerColor,
410 pub text: TextStyle,
411 pub scrollbar_width: Pixels,
412 pub syntax: Arc<SyntaxTheme>,
413 pub status: StatusColors,
414 pub inlay_hints_style: HighlightStyle,
415 pub inline_completion_styles: InlineCompletionStyles,
416 pub unnecessary_code_fade: f32,
417}
418
419impl Default for EditorStyle {
420 fn default() -> Self {
421 Self {
422 background: Hsla::default(),
423 local_player: PlayerColor::default(),
424 text: TextStyle::default(),
425 scrollbar_width: Pixels::default(),
426 syntax: Default::default(),
427 // HACK: Status colors don't have a real default.
428 // We should look into removing the status colors from the editor
429 // style and retrieve them directly from the theme.
430 status: StatusColors::dark(),
431 inlay_hints_style: HighlightStyle::default(),
432 inline_completion_styles: InlineCompletionStyles {
433 insertion: HighlightStyle::default(),
434 whitespace: HighlightStyle::default(),
435 },
436 unnecessary_code_fade: Default::default(),
437 }
438 }
439}
440
441pub fn make_inlay_hints_style(cx: &mut App) -> HighlightStyle {
442 let show_background = language_settings::language_settings(None, None, cx)
443 .inlay_hints
444 .show_background;
445
446 HighlightStyle {
447 color: Some(cx.theme().status().hint),
448 background_color: show_background.then(|| cx.theme().status().hint_background),
449 ..HighlightStyle::default()
450 }
451}
452
453pub fn make_suggestion_styles(cx: &mut App) -> InlineCompletionStyles {
454 InlineCompletionStyles {
455 insertion: HighlightStyle {
456 color: Some(cx.theme().status().predictive),
457 ..HighlightStyle::default()
458 },
459 whitespace: HighlightStyle {
460 background_color: Some(cx.theme().status().created_background),
461 ..HighlightStyle::default()
462 },
463 }
464}
465
466type CompletionId = usize;
467
468#[derive(Debug, Clone)]
469enum InlineCompletionMenuHint {
470 Loading,
471 Loaded { text: InlineCompletionText },
472 PendingTermsAcceptance,
473 None,
474}
475
476impl InlineCompletionMenuHint {
477 pub fn label(&self) -> &'static str {
478 match self {
479 InlineCompletionMenuHint::Loading | InlineCompletionMenuHint::Loaded { .. } => {
480 "Edit Prediction"
481 }
482 InlineCompletionMenuHint::PendingTermsAcceptance => "Accept Terms of Service",
483 InlineCompletionMenuHint::None => "No Prediction",
484 }
485 }
486}
487
488#[derive(Clone, Debug)]
489enum InlineCompletionText {
490 Move(SharedString),
491 Edit(HighlightedText),
492}
493
494pub(crate) enum EditDisplayMode {
495 TabAccept,
496 DiffPopover,
497 Inline,
498}
499
500enum InlineCompletion {
501 Edit {
502 edits: Vec<(Range<Anchor>, String)>,
503 edit_preview: Option<EditPreview>,
504 display_mode: EditDisplayMode,
505 snapshot: BufferSnapshot,
506 },
507 Move(Anchor),
508}
509
510struct InlineCompletionState {
511 inlay_ids: Vec<InlayId>,
512 completion: InlineCompletion,
513 invalidation_range: Range<Anchor>,
514}
515
516enum InlineCompletionHighlight {}
517
518pub enum MenuInlineCompletionsPolicy {
519 Never,
520 ByProvider,
521}
522
523#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
524struct EditorActionId(usize);
525
526impl EditorActionId {
527 pub fn post_inc(&mut self) -> Self {
528 let answer = self.0;
529
530 *self = Self(answer + 1);
531
532 Self(answer)
533 }
534}
535
536// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
537// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
538
539type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
540type GutterHighlight = (fn(&App) -> Hsla, Arc<[Range<Anchor>]>);
541
542#[derive(Default)]
543struct ScrollbarMarkerState {
544 scrollbar_size: Size<Pixels>,
545 dirty: bool,
546 markers: Arc<[PaintQuad]>,
547 pending_refresh: Option<Task<Result<()>>>,
548}
549
550impl ScrollbarMarkerState {
551 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
552 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
553 }
554}
555
556#[derive(Clone, Debug)]
557struct RunnableTasks {
558 templates: Vec<(TaskSourceKind, TaskTemplate)>,
559 offset: MultiBufferOffset,
560 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
561 column: u32,
562 // Values of all named captures, including those starting with '_'
563 extra_variables: HashMap<String, String>,
564 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
565 context_range: Range<BufferOffset>,
566}
567
568impl RunnableTasks {
569 fn resolve<'a>(
570 &'a self,
571 cx: &'a task::TaskContext,
572 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
573 self.templates.iter().filter_map(|(kind, template)| {
574 template
575 .resolve_task(&kind.to_id_base(), cx)
576 .map(|task| (kind.clone(), task))
577 })
578 }
579}
580
581#[derive(Clone)]
582struct ResolvedTasks {
583 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
584 position: Anchor,
585}
586#[derive(Copy, Clone, Debug)]
587struct MultiBufferOffset(usize);
588#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
589struct BufferOffset(usize);
590
591// Addons allow storing per-editor state in other crates (e.g. Vim)
592pub trait Addon: 'static {
593 fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
594
595 fn to_any(&self) -> &dyn std::any::Any;
596}
597
598#[derive(Debug, Copy, Clone, PartialEq, Eq)]
599pub enum IsVimMode {
600 Yes,
601 No,
602}
603
604/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
605///
606/// See the [module level documentation](self) for more information.
607pub struct Editor {
608 focus_handle: FocusHandle,
609 last_focused_descendant: Option<WeakFocusHandle>,
610 /// The text buffer being edited
611 buffer: Entity<MultiBuffer>,
612 /// Map of how text in the buffer should be displayed.
613 /// Handles soft wraps, folds, fake inlay text insertions, etc.
614 pub display_map: Entity<DisplayMap>,
615 pub selections: SelectionsCollection,
616 pub scroll_manager: ScrollManager,
617 /// When inline assist editors are linked, they all render cursors because
618 /// typing enters text into each of them, even the ones that aren't focused.
619 pub(crate) show_cursor_when_unfocused: bool,
620 columnar_selection_tail: Option<Anchor>,
621 add_selections_state: Option<AddSelectionsState>,
622 select_next_state: Option<SelectNextState>,
623 select_prev_state: Option<SelectNextState>,
624 selection_history: SelectionHistory,
625 autoclose_regions: Vec<AutocloseRegion>,
626 snippet_stack: InvalidationStack<SnippetState>,
627 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
628 ime_transaction: Option<TransactionId>,
629 active_diagnostics: Option<ActiveDiagnosticGroup>,
630 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
631
632 project: Option<Entity<Project>>,
633 semantics_provider: Option<Rc<dyn SemanticsProvider>>,
634 completion_provider: Option<Box<dyn CompletionProvider>>,
635 collaboration_hub: Option<Box<dyn CollaborationHub>>,
636 blink_manager: Entity<BlinkManager>,
637 show_cursor_names: bool,
638 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
639 pub show_local_selections: bool,
640 mode: EditorMode,
641 show_breadcrumbs: bool,
642 show_gutter: bool,
643 show_scrollbars: bool,
644 show_line_numbers: Option<bool>,
645 use_relative_line_numbers: Option<bool>,
646 show_git_diff_gutter: Option<bool>,
647 show_code_actions: Option<bool>,
648 show_runnables: Option<bool>,
649 show_wrap_guides: Option<bool>,
650 show_indent_guides: Option<bool>,
651 placeholder_text: Option<Arc<str>>,
652 highlight_order: usize,
653 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
654 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
655 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
656 scrollbar_marker_state: ScrollbarMarkerState,
657 active_indent_guides_state: ActiveIndentGuidesState,
658 nav_history: Option<ItemNavHistory>,
659 context_menu: RefCell<Option<CodeContextMenu>>,
660 mouse_context_menu: Option<MouseContextMenu>,
661 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
662 signature_help_state: SignatureHelpState,
663 auto_signature_help: Option<bool>,
664 find_all_references_task_sources: Vec<Anchor>,
665 next_completion_id: CompletionId,
666 available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
667 code_actions_task: Option<Task<Result<()>>>,
668 document_highlights_task: Option<Task<()>>,
669 linked_editing_range_task: Option<Task<Option<()>>>,
670 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
671 pending_rename: Option<RenameState>,
672 searchable: bool,
673 cursor_shape: CursorShape,
674 current_line_highlight: Option<CurrentLineHighlight>,
675 collapse_matches: bool,
676 autoindent_mode: Option<AutoindentMode>,
677 workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
678 input_enabled: bool,
679 use_modal_editing: bool,
680 read_only: bool,
681 leader_peer_id: Option<PeerId>,
682 remote_id: Option<ViewId>,
683 hover_state: HoverState,
684 pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
685 gutter_hovered: bool,
686 hovered_link_state: Option<HoveredLinkState>,
687 inline_completion_provider: Option<RegisteredInlineCompletionProvider>,
688 code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
689 active_inline_completion: Option<InlineCompletionState>,
690 // enable_inline_completions is a switch that Vim can use to disable
691 // inline completions based on its mode.
692 enable_inline_completions: bool,
693 show_inline_completions_override: Option<bool>,
694 menu_inline_completions_policy: MenuInlineCompletionsPolicy,
695 inlay_hint_cache: InlayHintCache,
696 next_inlay_id: usize,
697 _subscriptions: Vec<Subscription>,
698 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
699 gutter_dimensions: GutterDimensions,
700 style: Option<EditorStyle>,
701 text_style_refinement: Option<TextStyleRefinement>,
702 next_editor_action_id: EditorActionId,
703 editor_actions:
704 Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut Window, &mut Context<Self>)>>>>,
705 use_autoclose: bool,
706 use_auto_surround: bool,
707 auto_replace_emoji_shortcode: bool,
708 show_git_blame_gutter: bool,
709 show_git_blame_inline: bool,
710 show_git_blame_inline_delay_task: Option<Task<()>>,
711 git_blame_inline_enabled: bool,
712 serialize_dirty_buffers: bool,
713 show_selection_menu: Option<bool>,
714 blame: Option<Entity<GitBlame>>,
715 blame_subscription: Option<Subscription>,
716 custom_context_menu: Option<
717 Box<
718 dyn 'static
719 + Fn(
720 &mut Self,
721 DisplayPoint,
722 &mut Window,
723 &mut Context<Self>,
724 ) -> Option<Entity<ui::ContextMenu>>,
725 >,
726 >,
727 last_bounds: Option<Bounds<Pixels>>,
728 expect_bounds_change: Option<Bounds<Pixels>>,
729 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
730 tasks_update_task: Option<Task<()>>,
731 in_project_search: bool,
732 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
733 breadcrumb_header: Option<String>,
734 focused_block: Option<FocusedBlock>,
735 next_scroll_position: NextScrollCursorCenterTopBottom,
736 addons: HashMap<TypeId, Box<dyn Addon>>,
737 registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
738 selection_mark_mode: bool,
739 toggle_fold_multiple_buffers: Task<()>,
740 _scroll_cursor_center_top_bottom_task: Task<()>,
741}
742
743#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
744enum NextScrollCursorCenterTopBottom {
745 #[default]
746 Center,
747 Top,
748 Bottom,
749}
750
751impl NextScrollCursorCenterTopBottom {
752 fn next(&self) -> Self {
753 match self {
754 Self::Center => Self::Top,
755 Self::Top => Self::Bottom,
756 Self::Bottom => Self::Center,
757 }
758 }
759}
760
761#[derive(Clone)]
762pub struct EditorSnapshot {
763 pub mode: EditorMode,
764 show_gutter: bool,
765 show_line_numbers: Option<bool>,
766 show_git_diff_gutter: Option<bool>,
767 show_code_actions: Option<bool>,
768 show_runnables: Option<bool>,
769 git_blame_gutter_max_author_length: Option<usize>,
770 pub display_snapshot: DisplaySnapshot,
771 pub placeholder_text: Option<Arc<str>>,
772 is_focused: bool,
773 scroll_anchor: ScrollAnchor,
774 ongoing_scroll: OngoingScroll,
775 current_line_highlight: CurrentLineHighlight,
776 gutter_hovered: bool,
777}
778
779const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
780
781#[derive(Default, Debug, Clone, Copy)]
782pub struct GutterDimensions {
783 pub left_padding: Pixels,
784 pub right_padding: Pixels,
785 pub width: Pixels,
786 pub margin: Pixels,
787 pub git_blame_entries_width: Option<Pixels>,
788}
789
790impl GutterDimensions {
791 /// The full width of the space taken up by the gutter.
792 pub fn full_width(&self) -> Pixels {
793 self.margin + self.width
794 }
795
796 /// The width of the space reserved for the fold indicators,
797 /// use alongside 'justify_end' and `gutter_width` to
798 /// right align content with the line numbers
799 pub fn fold_area_width(&self) -> Pixels {
800 self.margin + self.right_padding
801 }
802}
803
804#[derive(Debug)]
805pub struct RemoteSelection {
806 pub replica_id: ReplicaId,
807 pub selection: Selection<Anchor>,
808 pub cursor_shape: CursorShape,
809 pub peer_id: PeerId,
810 pub line_mode: bool,
811 pub participant_index: Option<ParticipantIndex>,
812 pub user_name: Option<SharedString>,
813}
814
815#[derive(Clone, Debug)]
816struct SelectionHistoryEntry {
817 selections: Arc<[Selection<Anchor>]>,
818 select_next_state: Option<SelectNextState>,
819 select_prev_state: Option<SelectNextState>,
820 add_selections_state: Option<AddSelectionsState>,
821}
822
823enum SelectionHistoryMode {
824 Normal,
825 Undoing,
826 Redoing,
827}
828
829#[derive(Clone, PartialEq, Eq, Hash)]
830struct HoveredCursor {
831 replica_id: u16,
832 selection_id: usize,
833}
834
835impl Default for SelectionHistoryMode {
836 fn default() -> Self {
837 Self::Normal
838 }
839}
840
841#[derive(Default)]
842struct SelectionHistory {
843 #[allow(clippy::type_complexity)]
844 selections_by_transaction:
845 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
846 mode: SelectionHistoryMode,
847 undo_stack: VecDeque<SelectionHistoryEntry>,
848 redo_stack: VecDeque<SelectionHistoryEntry>,
849}
850
851impl SelectionHistory {
852 fn insert_transaction(
853 &mut self,
854 transaction_id: TransactionId,
855 selections: Arc<[Selection<Anchor>]>,
856 ) {
857 self.selections_by_transaction
858 .insert(transaction_id, (selections, None));
859 }
860
861 #[allow(clippy::type_complexity)]
862 fn transaction(
863 &self,
864 transaction_id: TransactionId,
865 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
866 self.selections_by_transaction.get(&transaction_id)
867 }
868
869 #[allow(clippy::type_complexity)]
870 fn transaction_mut(
871 &mut self,
872 transaction_id: TransactionId,
873 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
874 self.selections_by_transaction.get_mut(&transaction_id)
875 }
876
877 fn push(&mut self, entry: SelectionHistoryEntry) {
878 if !entry.selections.is_empty() {
879 match self.mode {
880 SelectionHistoryMode::Normal => {
881 self.push_undo(entry);
882 self.redo_stack.clear();
883 }
884 SelectionHistoryMode::Undoing => self.push_redo(entry),
885 SelectionHistoryMode::Redoing => self.push_undo(entry),
886 }
887 }
888 }
889
890 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
891 if self
892 .undo_stack
893 .back()
894 .map_or(true, |e| e.selections != entry.selections)
895 {
896 self.undo_stack.push_back(entry);
897 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
898 self.undo_stack.pop_front();
899 }
900 }
901 }
902
903 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
904 if self
905 .redo_stack
906 .back()
907 .map_or(true, |e| e.selections != entry.selections)
908 {
909 self.redo_stack.push_back(entry);
910 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
911 self.redo_stack.pop_front();
912 }
913 }
914 }
915}
916
917struct RowHighlight {
918 index: usize,
919 range: Range<Anchor>,
920 color: Hsla,
921 should_autoscroll: bool,
922}
923
924#[derive(Clone, Debug)]
925struct AddSelectionsState {
926 above: bool,
927 stack: Vec<usize>,
928}
929
930#[derive(Clone)]
931struct SelectNextState {
932 query: AhoCorasick,
933 wordwise: bool,
934 done: bool,
935}
936
937impl std::fmt::Debug for SelectNextState {
938 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
939 f.debug_struct(std::any::type_name::<Self>())
940 .field("wordwise", &self.wordwise)
941 .field("done", &self.done)
942 .finish()
943 }
944}
945
946#[derive(Debug)]
947struct AutocloseRegion {
948 selection_id: usize,
949 range: Range<Anchor>,
950 pair: BracketPair,
951}
952
953#[derive(Debug)]
954struct SnippetState {
955 ranges: Vec<Vec<Range<Anchor>>>,
956 active_index: usize,
957 choices: Vec<Option<Vec<String>>>,
958}
959
960#[doc(hidden)]
961pub struct RenameState {
962 pub range: Range<Anchor>,
963 pub old_name: Arc<str>,
964 pub editor: Entity<Editor>,
965 block_id: CustomBlockId,
966}
967
968struct InvalidationStack<T>(Vec<T>);
969
970struct RegisteredInlineCompletionProvider {
971 provider: Arc<dyn InlineCompletionProviderHandle>,
972 _subscription: Subscription,
973}
974
975#[derive(Debug)]
976struct ActiveDiagnosticGroup {
977 primary_range: Range<Anchor>,
978 primary_message: String,
979 group_id: usize,
980 blocks: HashMap<CustomBlockId, Diagnostic>,
981 is_valid: bool,
982}
983
984#[derive(Serialize, Deserialize, Clone, Debug)]
985pub struct ClipboardSelection {
986 pub len: usize,
987 pub is_entire_line: bool,
988 pub first_line_indent: u32,
989}
990
991#[derive(Debug)]
992pub(crate) struct NavigationData {
993 cursor_anchor: Anchor,
994 cursor_position: Point,
995 scroll_anchor: ScrollAnchor,
996 scroll_top_row: u32,
997}
998
999#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1000pub enum GotoDefinitionKind {
1001 Symbol,
1002 Declaration,
1003 Type,
1004 Implementation,
1005}
1006
1007#[derive(Debug, Clone)]
1008enum InlayHintRefreshReason {
1009 Toggle(bool),
1010 SettingsChange(InlayHintSettings),
1011 NewLinesShown,
1012 BufferEdited(HashSet<Arc<Language>>),
1013 RefreshRequested,
1014 ExcerptsRemoved(Vec<ExcerptId>),
1015}
1016
1017impl InlayHintRefreshReason {
1018 fn description(&self) -> &'static str {
1019 match self {
1020 Self::Toggle(_) => "toggle",
1021 Self::SettingsChange(_) => "settings change",
1022 Self::NewLinesShown => "new lines shown",
1023 Self::BufferEdited(_) => "buffer edited",
1024 Self::RefreshRequested => "refresh requested",
1025 Self::ExcerptsRemoved(_) => "excerpts removed",
1026 }
1027 }
1028}
1029
1030pub enum FormatTarget {
1031 Buffers,
1032 Ranges(Vec<Range<MultiBufferPoint>>),
1033}
1034
1035pub(crate) struct FocusedBlock {
1036 id: BlockId,
1037 focus_handle: WeakFocusHandle,
1038}
1039
1040#[derive(Clone)]
1041enum JumpData {
1042 MultiBufferRow {
1043 row: MultiBufferRow,
1044 line_offset_from_top: u32,
1045 },
1046 MultiBufferPoint {
1047 excerpt_id: ExcerptId,
1048 position: Point,
1049 anchor: text::Anchor,
1050 line_offset_from_top: u32,
1051 },
1052}
1053
1054pub enum MultibufferSelectionMode {
1055 First,
1056 All,
1057}
1058
1059impl Editor {
1060 pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1061 let buffer = cx.new(|cx| Buffer::local("", cx));
1062 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1063 Self::new(
1064 EditorMode::SingleLine { auto_width: false },
1065 buffer,
1066 None,
1067 false,
1068 window,
1069 cx,
1070 )
1071 }
1072
1073 pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1074 let buffer = cx.new(|cx| Buffer::local("", cx));
1075 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1076 Self::new(EditorMode::Full, buffer, None, false, window, cx)
1077 }
1078
1079 pub fn auto_width(window: &mut Window, cx: &mut Context<Self>) -> Self {
1080 let buffer = cx.new(|cx| Buffer::local("", cx));
1081 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1082 Self::new(
1083 EditorMode::SingleLine { auto_width: true },
1084 buffer,
1085 None,
1086 false,
1087 window,
1088 cx,
1089 )
1090 }
1091
1092 pub fn auto_height(max_lines: usize, window: &mut Window, cx: &mut Context<Self>) -> Self {
1093 let buffer = cx.new(|cx| Buffer::local("", cx));
1094 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1095 Self::new(
1096 EditorMode::AutoHeight { max_lines },
1097 buffer,
1098 None,
1099 false,
1100 window,
1101 cx,
1102 )
1103 }
1104
1105 pub fn for_buffer(
1106 buffer: Entity<Buffer>,
1107 project: Option<Entity<Project>>,
1108 window: &mut Window,
1109 cx: &mut Context<Self>,
1110 ) -> Self {
1111 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1112 Self::new(EditorMode::Full, buffer, project, false, window, cx)
1113 }
1114
1115 pub fn for_multibuffer(
1116 buffer: Entity<MultiBuffer>,
1117 project: Option<Entity<Project>>,
1118 show_excerpt_controls: bool,
1119 window: &mut Window,
1120 cx: &mut Context<Self>,
1121 ) -> Self {
1122 Self::new(
1123 EditorMode::Full,
1124 buffer,
1125 project,
1126 show_excerpt_controls,
1127 window,
1128 cx,
1129 )
1130 }
1131
1132 pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
1133 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1134 let mut clone = Self::new(
1135 self.mode,
1136 self.buffer.clone(),
1137 self.project.clone(),
1138 show_excerpt_controls,
1139 window,
1140 cx,
1141 );
1142 self.display_map.update(cx, |display_map, cx| {
1143 let snapshot = display_map.snapshot(cx);
1144 clone.display_map.update(cx, |display_map, cx| {
1145 display_map.set_state(&snapshot, cx);
1146 });
1147 });
1148 clone.selections.clone_state(&self.selections);
1149 clone.scroll_manager.clone_state(&self.scroll_manager);
1150 clone.searchable = self.searchable;
1151 clone
1152 }
1153
1154 pub fn new(
1155 mode: EditorMode,
1156 buffer: Entity<MultiBuffer>,
1157 project: Option<Entity<Project>>,
1158 show_excerpt_controls: bool,
1159 window: &mut Window,
1160 cx: &mut Context<Self>,
1161 ) -> Self {
1162 let style = window.text_style();
1163 let font_size = style.font_size.to_pixels(window.rem_size());
1164 let editor = cx.entity().downgrade();
1165 let fold_placeholder = FoldPlaceholder {
1166 constrain_width: true,
1167 render: Arc::new(move |fold_id, fold_range, _, cx| {
1168 let editor = editor.clone();
1169 div()
1170 .id(fold_id)
1171 .bg(cx.theme().colors().ghost_element_background)
1172 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1173 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1174 .rounded_sm()
1175 .size_full()
1176 .cursor_pointer()
1177 .child("⋯")
1178 .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
1179 .on_click(move |_, _window, cx| {
1180 editor
1181 .update(cx, |editor, cx| {
1182 editor.unfold_ranges(
1183 &[fold_range.start..fold_range.end],
1184 true,
1185 false,
1186 cx,
1187 );
1188 cx.stop_propagation();
1189 })
1190 .ok();
1191 })
1192 .into_any()
1193 }),
1194 merge_adjacent: true,
1195 ..Default::default()
1196 };
1197 let display_map = cx.new(|cx| {
1198 DisplayMap::new(
1199 buffer.clone(),
1200 style.font(),
1201 font_size,
1202 None,
1203 show_excerpt_controls,
1204 FILE_HEADER_HEIGHT,
1205 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1206 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1207 fold_placeholder,
1208 cx,
1209 )
1210 });
1211
1212 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1213
1214 let blink_manager = cx.new(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1215
1216 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1217 .then(|| language_settings::SoftWrap::None);
1218
1219 let mut project_subscriptions = Vec::new();
1220 if mode == EditorMode::Full {
1221 if let Some(project) = project.as_ref() {
1222 if buffer.read(cx).is_singleton() {
1223 project_subscriptions.push(cx.observe_in(project, window, |_, _, _, cx| {
1224 cx.emit(EditorEvent::TitleChanged);
1225 }));
1226 }
1227 project_subscriptions.push(cx.subscribe_in(
1228 project,
1229 window,
1230 |editor, _, event, window, cx| {
1231 if let project::Event::RefreshInlayHints = event {
1232 editor
1233 .refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1234 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1235 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1236 let focus_handle = editor.focus_handle(cx);
1237 if focus_handle.is_focused(window) {
1238 let snapshot = buffer.read(cx).snapshot();
1239 for (range, snippet) in snippet_edits {
1240 let editor_range =
1241 language::range_from_lsp(*range).to_offset(&snapshot);
1242 editor
1243 .insert_snippet(
1244 &[editor_range],
1245 snippet.clone(),
1246 window,
1247 cx,
1248 )
1249 .ok();
1250 }
1251 }
1252 }
1253 }
1254 },
1255 ));
1256 if let Some(task_inventory) = project
1257 .read(cx)
1258 .task_store()
1259 .read(cx)
1260 .task_inventory()
1261 .cloned()
1262 {
1263 project_subscriptions.push(cx.observe_in(
1264 &task_inventory,
1265 window,
1266 |editor, _, window, cx| {
1267 editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
1268 },
1269 ));
1270 }
1271 }
1272 }
1273
1274 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1275
1276 let inlay_hint_settings =
1277 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1278 let focus_handle = cx.focus_handle();
1279 cx.on_focus(&focus_handle, window, Self::handle_focus)
1280 .detach();
1281 cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
1282 .detach();
1283 cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
1284 .detach();
1285 cx.on_blur(&focus_handle, window, Self::handle_blur)
1286 .detach();
1287
1288 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1289 Some(false)
1290 } else {
1291 None
1292 };
1293
1294 let mut code_action_providers = Vec::new();
1295 if let Some(project) = project.clone() {
1296 get_unstaged_changes_for_buffers(
1297 &project,
1298 buffer.read(cx).all_buffers(),
1299 buffer.clone(),
1300 cx,
1301 );
1302 code_action_providers.push(Rc::new(project) as Rc<_>);
1303 }
1304
1305 let mut this = Self {
1306 focus_handle,
1307 show_cursor_when_unfocused: false,
1308 last_focused_descendant: None,
1309 buffer: buffer.clone(),
1310 display_map: display_map.clone(),
1311 selections,
1312 scroll_manager: ScrollManager::new(cx),
1313 columnar_selection_tail: None,
1314 add_selections_state: None,
1315 select_next_state: None,
1316 select_prev_state: None,
1317 selection_history: Default::default(),
1318 autoclose_regions: Default::default(),
1319 snippet_stack: Default::default(),
1320 select_larger_syntax_node_stack: Vec::new(),
1321 ime_transaction: Default::default(),
1322 active_diagnostics: None,
1323 soft_wrap_mode_override,
1324 completion_provider: project.clone().map(|project| Box::new(project) as _),
1325 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
1326 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1327 project,
1328 blink_manager: blink_manager.clone(),
1329 show_local_selections: true,
1330 show_scrollbars: true,
1331 mode,
1332 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1333 show_gutter: mode == EditorMode::Full,
1334 show_line_numbers: None,
1335 use_relative_line_numbers: None,
1336 show_git_diff_gutter: None,
1337 show_code_actions: None,
1338 show_runnables: None,
1339 show_wrap_guides: None,
1340 show_indent_guides,
1341 placeholder_text: None,
1342 highlight_order: 0,
1343 highlighted_rows: HashMap::default(),
1344 background_highlights: Default::default(),
1345 gutter_highlights: TreeMap::default(),
1346 scrollbar_marker_state: ScrollbarMarkerState::default(),
1347 active_indent_guides_state: ActiveIndentGuidesState::default(),
1348 nav_history: None,
1349 context_menu: RefCell::new(None),
1350 mouse_context_menu: None,
1351 completion_tasks: Default::default(),
1352 signature_help_state: SignatureHelpState::default(),
1353 auto_signature_help: None,
1354 find_all_references_task_sources: Vec::new(),
1355 next_completion_id: 0,
1356 next_inlay_id: 0,
1357 code_action_providers,
1358 available_code_actions: Default::default(),
1359 code_actions_task: Default::default(),
1360 document_highlights_task: Default::default(),
1361 linked_editing_range_task: Default::default(),
1362 pending_rename: Default::default(),
1363 searchable: true,
1364 cursor_shape: EditorSettings::get_global(cx)
1365 .cursor_shape
1366 .unwrap_or_default(),
1367 current_line_highlight: None,
1368 autoindent_mode: Some(AutoindentMode::EachLine),
1369 collapse_matches: false,
1370 workspace: None,
1371 input_enabled: true,
1372 use_modal_editing: mode == EditorMode::Full,
1373 read_only: false,
1374 use_autoclose: true,
1375 use_auto_surround: true,
1376 auto_replace_emoji_shortcode: false,
1377 leader_peer_id: None,
1378 remote_id: None,
1379 hover_state: Default::default(),
1380 pending_mouse_down: None,
1381 hovered_link_state: Default::default(),
1382 inline_completion_provider: None,
1383 active_inline_completion: None,
1384 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1385
1386 gutter_hovered: false,
1387 pixel_position_of_newest_cursor: None,
1388 last_bounds: None,
1389 expect_bounds_change: None,
1390 gutter_dimensions: GutterDimensions::default(),
1391 style: None,
1392 show_cursor_names: false,
1393 hovered_cursors: Default::default(),
1394 next_editor_action_id: EditorActionId::default(),
1395 editor_actions: Rc::default(),
1396 show_inline_completions_override: None,
1397 enable_inline_completions: true,
1398 menu_inline_completions_policy: MenuInlineCompletionsPolicy::ByProvider,
1399 custom_context_menu: None,
1400 show_git_blame_gutter: false,
1401 show_git_blame_inline: false,
1402 show_selection_menu: None,
1403 show_git_blame_inline_delay_task: None,
1404 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1405 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1406 .session
1407 .restore_unsaved_buffers,
1408 blame: None,
1409 blame_subscription: None,
1410 tasks: Default::default(),
1411 _subscriptions: vec![
1412 cx.observe(&buffer, Self::on_buffer_changed),
1413 cx.subscribe_in(&buffer, window, Self::on_buffer_event),
1414 cx.observe_in(&display_map, window, Self::on_display_map_changed),
1415 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1416 cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
1417 cx.observe_window_activation(window, |editor, window, cx| {
1418 let active = window.is_window_active();
1419 editor.blink_manager.update(cx, |blink_manager, cx| {
1420 if active {
1421 blink_manager.enable(cx);
1422 } else {
1423 blink_manager.disable(cx);
1424 }
1425 });
1426 }),
1427 ],
1428 tasks_update_task: None,
1429 linked_edit_ranges: Default::default(),
1430 in_project_search: false,
1431 previous_search_ranges: None,
1432 breadcrumb_header: None,
1433 focused_block: None,
1434 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1435 addons: HashMap::default(),
1436 registered_buffers: HashMap::default(),
1437 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1438 selection_mark_mode: false,
1439 toggle_fold_multiple_buffers: Task::ready(()),
1440 text_style_refinement: None,
1441 };
1442 this.tasks_update_task = Some(this.refresh_runnables(window, cx));
1443 this._subscriptions.extend(project_subscriptions);
1444
1445 this.end_selection(window, cx);
1446 this.scroll_manager.show_scrollbar(window, cx);
1447
1448 if mode == EditorMode::Full {
1449 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1450 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1451
1452 if this.git_blame_inline_enabled {
1453 this.git_blame_inline_enabled = true;
1454 this.start_git_blame_inline(false, window, cx);
1455 }
1456
1457 if let Some(buffer) = buffer.read(cx).as_singleton() {
1458 if let Some(project) = this.project.as_ref() {
1459 let lsp_store = project.read(cx).lsp_store();
1460 let handle = lsp_store.update(cx, |lsp_store, cx| {
1461 lsp_store.register_buffer_with_language_servers(&buffer, cx)
1462 });
1463 this.registered_buffers
1464 .insert(buffer.read(cx).remote_id(), handle);
1465 }
1466 }
1467 }
1468
1469 this.report_editor_event("Editor Opened", None, cx);
1470 this
1471 }
1472
1473 pub fn mouse_menu_is_focused(&self, window: &mut Window, cx: &mut App) -> bool {
1474 self.mouse_context_menu
1475 .as_ref()
1476 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
1477 }
1478
1479 fn key_context(&self, window: &mut Window, cx: &mut Context<Self>) -> KeyContext {
1480 let mut key_context = KeyContext::new_with_defaults();
1481 key_context.add("Editor");
1482 let mode = match self.mode {
1483 EditorMode::SingleLine { .. } => "single_line",
1484 EditorMode::AutoHeight { .. } => "auto_height",
1485 EditorMode::Full => "full",
1486 };
1487
1488 if EditorSettings::jupyter_enabled(cx) {
1489 key_context.add("jupyter");
1490 }
1491
1492 key_context.set("mode", mode);
1493 if self.pending_rename.is_some() {
1494 key_context.add("renaming");
1495 }
1496 match self.context_menu.borrow().as_ref() {
1497 Some(CodeContextMenu::Completions(_)) => {
1498 key_context.add("menu");
1499 key_context.add("showing_completions")
1500 }
1501 Some(CodeContextMenu::CodeActions(_)) => {
1502 key_context.add("menu");
1503 key_context.add("showing_code_actions")
1504 }
1505 None => {}
1506 }
1507
1508 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
1509 if !self.focus_handle(cx).contains_focused(window, cx)
1510 || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
1511 {
1512 for addon in self.addons.values() {
1513 addon.extend_key_context(&mut key_context, cx)
1514 }
1515 }
1516
1517 if let Some(extension) = self
1518 .buffer
1519 .read(cx)
1520 .as_singleton()
1521 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
1522 {
1523 key_context.set("extension", extension.to_string());
1524 }
1525
1526 if self.has_active_inline_completion() {
1527 key_context.add("copilot_suggestion");
1528 key_context.add("inline_completion");
1529 }
1530
1531 if self.selection_mark_mode {
1532 key_context.add("selection_mode");
1533 }
1534
1535 key_context
1536 }
1537
1538 pub fn new_file(
1539 workspace: &mut Workspace,
1540 _: &workspace::NewFile,
1541 window: &mut Window,
1542 cx: &mut Context<Workspace>,
1543 ) {
1544 Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
1545 "Failed to create buffer",
1546 window,
1547 cx,
1548 |e, _, _| match e.error_code() {
1549 ErrorCode::RemoteUpgradeRequired => Some(format!(
1550 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1551 e.error_tag("required").unwrap_or("the latest version")
1552 )),
1553 _ => None,
1554 },
1555 );
1556 }
1557
1558 pub fn new_in_workspace(
1559 workspace: &mut Workspace,
1560 window: &mut Window,
1561 cx: &mut Context<Workspace>,
1562 ) -> Task<Result<Entity<Editor>>> {
1563 let project = workspace.project().clone();
1564 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1565
1566 cx.spawn_in(window, |workspace, mut cx| async move {
1567 let buffer = create.await?;
1568 workspace.update_in(&mut cx, |workspace, window, cx| {
1569 let editor =
1570 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
1571 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
1572 editor
1573 })
1574 })
1575 }
1576
1577 fn new_file_vertical(
1578 workspace: &mut Workspace,
1579 _: &workspace::NewFileSplitVertical,
1580 window: &mut Window,
1581 cx: &mut Context<Workspace>,
1582 ) {
1583 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
1584 }
1585
1586 fn new_file_horizontal(
1587 workspace: &mut Workspace,
1588 _: &workspace::NewFileSplitHorizontal,
1589 window: &mut Window,
1590 cx: &mut Context<Workspace>,
1591 ) {
1592 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
1593 }
1594
1595 fn new_file_in_direction(
1596 workspace: &mut Workspace,
1597 direction: SplitDirection,
1598 window: &mut Window,
1599 cx: &mut Context<Workspace>,
1600 ) {
1601 let project = workspace.project().clone();
1602 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1603
1604 cx.spawn_in(window, |workspace, mut cx| async move {
1605 let buffer = create.await?;
1606 workspace.update_in(&mut cx, move |workspace, window, cx| {
1607 workspace.split_item(
1608 direction,
1609 Box::new(
1610 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
1611 ),
1612 window,
1613 cx,
1614 )
1615 })?;
1616 anyhow::Ok(())
1617 })
1618 .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
1619 match e.error_code() {
1620 ErrorCode::RemoteUpgradeRequired => Some(format!(
1621 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1622 e.error_tag("required").unwrap_or("the latest version")
1623 )),
1624 _ => None,
1625 }
1626 });
1627 }
1628
1629 pub fn leader_peer_id(&self) -> Option<PeerId> {
1630 self.leader_peer_id
1631 }
1632
1633 pub fn buffer(&self) -> &Entity<MultiBuffer> {
1634 &self.buffer
1635 }
1636
1637 pub fn workspace(&self) -> Option<Entity<Workspace>> {
1638 self.workspace.as_ref()?.0.upgrade()
1639 }
1640
1641 pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
1642 self.buffer().read(cx).title(cx)
1643 }
1644
1645 pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
1646 let git_blame_gutter_max_author_length = self
1647 .render_git_blame_gutter(cx)
1648 .then(|| {
1649 if let Some(blame) = self.blame.as_ref() {
1650 let max_author_length =
1651 blame.update(cx, |blame, cx| blame.max_author_length(cx));
1652 Some(max_author_length)
1653 } else {
1654 None
1655 }
1656 })
1657 .flatten();
1658
1659 EditorSnapshot {
1660 mode: self.mode,
1661 show_gutter: self.show_gutter,
1662 show_line_numbers: self.show_line_numbers,
1663 show_git_diff_gutter: self.show_git_diff_gutter,
1664 show_code_actions: self.show_code_actions,
1665 show_runnables: self.show_runnables,
1666 git_blame_gutter_max_author_length,
1667 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1668 scroll_anchor: self.scroll_manager.anchor(),
1669 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1670 placeholder_text: self.placeholder_text.clone(),
1671 is_focused: self.focus_handle.is_focused(window),
1672 current_line_highlight: self
1673 .current_line_highlight
1674 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
1675 gutter_hovered: self.gutter_hovered,
1676 }
1677 }
1678
1679 pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
1680 self.buffer.read(cx).language_at(point, cx)
1681 }
1682
1683 pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
1684 self.buffer.read(cx).read(cx).file_at(point).cloned()
1685 }
1686
1687 pub fn active_excerpt(
1688 &self,
1689 cx: &App,
1690 ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
1691 self.buffer
1692 .read(cx)
1693 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1694 }
1695
1696 pub fn mode(&self) -> EditorMode {
1697 self.mode
1698 }
1699
1700 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1701 self.collaboration_hub.as_deref()
1702 }
1703
1704 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1705 self.collaboration_hub = Some(hub);
1706 }
1707
1708 pub fn set_in_project_search(&mut self, in_project_search: bool) {
1709 self.in_project_search = in_project_search;
1710 }
1711
1712 pub fn set_custom_context_menu(
1713 &mut self,
1714 f: impl 'static
1715 + Fn(
1716 &mut Self,
1717 DisplayPoint,
1718 &mut Window,
1719 &mut Context<Self>,
1720 ) -> Option<Entity<ui::ContextMenu>>,
1721 ) {
1722 self.custom_context_menu = Some(Box::new(f))
1723 }
1724
1725 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
1726 self.completion_provider = provider;
1727 }
1728
1729 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
1730 self.semantics_provider.clone()
1731 }
1732
1733 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
1734 self.semantics_provider = provider;
1735 }
1736
1737 pub fn set_inline_completion_provider<T>(
1738 &mut self,
1739 provider: Option<Entity<T>>,
1740 window: &mut Window,
1741 cx: &mut Context<Self>,
1742 ) where
1743 T: InlineCompletionProvider,
1744 {
1745 self.inline_completion_provider =
1746 provider.map(|provider| RegisteredInlineCompletionProvider {
1747 _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
1748 if this.focus_handle.is_focused(window) {
1749 this.update_visible_inline_completion(window, cx);
1750 }
1751 }),
1752 provider: Arc::new(provider),
1753 });
1754 self.refresh_inline_completion(false, false, window, cx);
1755 }
1756
1757 pub fn placeholder_text(&self) -> Option<&str> {
1758 self.placeholder_text.as_deref()
1759 }
1760
1761 pub fn set_placeholder_text(
1762 &mut self,
1763 placeholder_text: impl Into<Arc<str>>,
1764 cx: &mut Context<Self>,
1765 ) {
1766 let placeholder_text = Some(placeholder_text.into());
1767 if self.placeholder_text != placeholder_text {
1768 self.placeholder_text = placeholder_text;
1769 cx.notify();
1770 }
1771 }
1772
1773 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
1774 self.cursor_shape = cursor_shape;
1775
1776 // Disrupt blink for immediate user feedback that the cursor shape has changed
1777 self.blink_manager.update(cx, BlinkManager::show_cursor);
1778
1779 cx.notify();
1780 }
1781
1782 pub fn set_current_line_highlight(
1783 &mut self,
1784 current_line_highlight: Option<CurrentLineHighlight>,
1785 ) {
1786 self.current_line_highlight = current_line_highlight;
1787 }
1788
1789 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1790 self.collapse_matches = collapse_matches;
1791 }
1792
1793 pub fn register_buffers_with_language_servers(&mut self, cx: &mut Context<Self>) {
1794 let buffers = self.buffer.read(cx).all_buffers();
1795 let Some(lsp_store) = self.lsp_store(cx) else {
1796 return;
1797 };
1798 lsp_store.update(cx, |lsp_store, cx| {
1799 for buffer in buffers {
1800 self.registered_buffers
1801 .entry(buffer.read(cx).remote_id())
1802 .or_insert_with(|| {
1803 lsp_store.register_buffer_with_language_servers(&buffer, cx)
1804 });
1805 }
1806 })
1807 }
1808
1809 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1810 if self.collapse_matches {
1811 return range.start..range.start;
1812 }
1813 range.clone()
1814 }
1815
1816 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
1817 if self.display_map.read(cx).clip_at_line_ends != clip {
1818 self.display_map
1819 .update(cx, |map, _| map.clip_at_line_ends = clip);
1820 }
1821 }
1822
1823 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1824 self.input_enabled = input_enabled;
1825 }
1826
1827 pub fn set_inline_completions_enabled(&mut self, enabled: bool, cx: &mut Context<Self>) {
1828 self.enable_inline_completions = enabled;
1829 if !self.enable_inline_completions {
1830 self.take_active_inline_completion(cx);
1831 cx.notify();
1832 }
1833 }
1834
1835 pub fn set_menu_inline_completions_policy(&mut self, value: MenuInlineCompletionsPolicy) {
1836 self.menu_inline_completions_policy = value;
1837 }
1838
1839 pub fn set_autoindent(&mut self, autoindent: bool) {
1840 if autoindent {
1841 self.autoindent_mode = Some(AutoindentMode::EachLine);
1842 } else {
1843 self.autoindent_mode = None;
1844 }
1845 }
1846
1847 pub fn read_only(&self, cx: &App) -> bool {
1848 self.read_only || self.buffer.read(cx).read_only()
1849 }
1850
1851 pub fn set_read_only(&mut self, read_only: bool) {
1852 self.read_only = read_only;
1853 }
1854
1855 pub fn set_use_autoclose(&mut self, autoclose: bool) {
1856 self.use_autoclose = autoclose;
1857 }
1858
1859 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
1860 self.use_auto_surround = auto_surround;
1861 }
1862
1863 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
1864 self.auto_replace_emoji_shortcode = auto_replace;
1865 }
1866
1867 pub fn toggle_inline_completions(
1868 &mut self,
1869 _: &ToggleInlineCompletions,
1870 window: &mut Window,
1871 cx: &mut Context<Self>,
1872 ) {
1873 if self.show_inline_completions_override.is_some() {
1874 self.set_show_inline_completions(None, window, cx);
1875 } else {
1876 let cursor = self.selections.newest_anchor().head();
1877 if let Some((buffer, cursor_buffer_position)) =
1878 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
1879 {
1880 let show_inline_completions =
1881 !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx);
1882 self.set_show_inline_completions(Some(show_inline_completions), window, cx);
1883 }
1884 }
1885 }
1886
1887 pub fn set_show_inline_completions(
1888 &mut self,
1889 show_inline_completions: Option<bool>,
1890 window: &mut Window,
1891 cx: &mut Context<Self>,
1892 ) {
1893 self.show_inline_completions_override = show_inline_completions;
1894 self.refresh_inline_completion(false, true, window, cx);
1895 }
1896
1897 pub fn inline_completions_enabled(&self, cx: &App) -> bool {
1898 let cursor = self.selections.newest_anchor().head();
1899 if let Some((buffer, buffer_position)) =
1900 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
1901 {
1902 self.should_show_inline_completions(&buffer, buffer_position, cx)
1903 } else {
1904 false
1905 }
1906 }
1907
1908 fn should_show_inline_completions(
1909 &self,
1910 buffer: &Entity<Buffer>,
1911 buffer_position: language::Anchor,
1912 cx: &App,
1913 ) -> bool {
1914 if !self.snippet_stack.is_empty() {
1915 return false;
1916 }
1917
1918 if self.inline_completions_disabled_in_scope(buffer, buffer_position, cx) {
1919 return false;
1920 }
1921
1922 if let Some(provider) = self.inline_completion_provider() {
1923 if let Some(show_inline_completions) = self.show_inline_completions_override {
1924 show_inline_completions
1925 } else {
1926 self.mode == EditorMode::Full && provider.is_enabled(buffer, buffer_position, cx)
1927 }
1928 } else {
1929 false
1930 }
1931 }
1932
1933 fn inline_completions_disabled_in_scope(
1934 &self,
1935 buffer: &Entity<Buffer>,
1936 buffer_position: language::Anchor,
1937 cx: &App,
1938 ) -> bool {
1939 let snapshot = buffer.read(cx).snapshot();
1940 let settings = snapshot.settings_at(buffer_position, cx);
1941
1942 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
1943 return false;
1944 };
1945
1946 scope.override_name().map_or(false, |scope_name| {
1947 settings
1948 .inline_completions_disabled_in
1949 .iter()
1950 .any(|s| s == scope_name)
1951 })
1952 }
1953
1954 pub fn set_use_modal_editing(&mut self, to: bool) {
1955 self.use_modal_editing = to;
1956 }
1957
1958 pub fn use_modal_editing(&self) -> bool {
1959 self.use_modal_editing
1960 }
1961
1962 fn selections_did_change(
1963 &mut self,
1964 local: bool,
1965 old_cursor_position: &Anchor,
1966 show_completions: bool,
1967 window: &mut Window,
1968 cx: &mut Context<Self>,
1969 ) {
1970 window.invalidate_character_coordinates();
1971
1972 // Copy selections to primary selection buffer
1973 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
1974 if local {
1975 let selections = self.selections.all::<usize>(cx);
1976 let buffer_handle = self.buffer.read(cx).read(cx);
1977
1978 let mut text = String::new();
1979 for (index, selection) in selections.iter().enumerate() {
1980 let text_for_selection = buffer_handle
1981 .text_for_range(selection.start..selection.end)
1982 .collect::<String>();
1983
1984 text.push_str(&text_for_selection);
1985 if index != selections.len() - 1 {
1986 text.push('\n');
1987 }
1988 }
1989
1990 if !text.is_empty() {
1991 cx.write_to_primary(ClipboardItem::new_string(text));
1992 }
1993 }
1994
1995 if self.focus_handle.is_focused(window) && self.leader_peer_id.is_none() {
1996 self.buffer.update(cx, |buffer, cx| {
1997 buffer.set_active_selections(
1998 &self.selections.disjoint_anchors(),
1999 self.selections.line_mode,
2000 self.cursor_shape,
2001 cx,
2002 )
2003 });
2004 }
2005 let display_map = self
2006 .display_map
2007 .update(cx, |display_map, cx| display_map.snapshot(cx));
2008 let buffer = &display_map.buffer_snapshot;
2009 self.add_selections_state = None;
2010 self.select_next_state = None;
2011 self.select_prev_state = None;
2012 self.select_larger_syntax_node_stack.clear();
2013 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2014 self.snippet_stack
2015 .invalidate(&self.selections.disjoint_anchors(), buffer);
2016 self.take_rename(false, window, cx);
2017
2018 let new_cursor_position = self.selections.newest_anchor().head();
2019
2020 self.push_to_nav_history(
2021 *old_cursor_position,
2022 Some(new_cursor_position.to_point(buffer)),
2023 cx,
2024 );
2025
2026 if local {
2027 let new_cursor_position = self.selections.newest_anchor().head();
2028 let mut context_menu = self.context_menu.borrow_mut();
2029 let completion_menu = match context_menu.as_ref() {
2030 Some(CodeContextMenu::Completions(menu)) => Some(menu),
2031 _ => {
2032 *context_menu = None;
2033 None
2034 }
2035 };
2036
2037 if let Some(completion_menu) = completion_menu {
2038 let cursor_position = new_cursor_position.to_offset(buffer);
2039 let (word_range, kind) =
2040 buffer.surrounding_word(completion_menu.initial_position, true);
2041 if kind == Some(CharKind::Word)
2042 && word_range.to_inclusive().contains(&cursor_position)
2043 {
2044 let mut completion_menu = completion_menu.clone();
2045 drop(context_menu);
2046
2047 let query = Self::completion_query(buffer, cursor_position);
2048 cx.spawn(move |this, mut cx| async move {
2049 completion_menu
2050 .filter(query.as_deref(), cx.background_executor().clone())
2051 .await;
2052
2053 this.update(&mut cx, |this, cx| {
2054 let mut context_menu = this.context_menu.borrow_mut();
2055 let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
2056 else {
2057 return;
2058 };
2059
2060 if menu.id > completion_menu.id {
2061 return;
2062 }
2063
2064 *context_menu = Some(CodeContextMenu::Completions(completion_menu));
2065 drop(context_menu);
2066 cx.notify();
2067 })
2068 })
2069 .detach();
2070
2071 if show_completions {
2072 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
2073 }
2074 } else {
2075 drop(context_menu);
2076 self.hide_context_menu(window, cx);
2077 }
2078 } else {
2079 drop(context_menu);
2080 }
2081
2082 hide_hover(self, cx);
2083
2084 if old_cursor_position.to_display_point(&display_map).row()
2085 != new_cursor_position.to_display_point(&display_map).row()
2086 {
2087 self.available_code_actions.take();
2088 }
2089 self.refresh_code_actions(window, cx);
2090 self.refresh_document_highlights(cx);
2091 refresh_matching_bracket_highlights(self, window, cx);
2092 self.update_visible_inline_completion(window, cx);
2093 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
2094 if self.git_blame_inline_enabled {
2095 self.start_inline_blame_timer(window, cx);
2096 }
2097 }
2098
2099 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2100 cx.emit(EditorEvent::SelectionsChanged { local });
2101
2102 if self.selections.disjoint_anchors().len() == 1 {
2103 cx.emit(SearchEvent::ActiveMatchChanged)
2104 }
2105 cx.notify();
2106 }
2107
2108 pub fn change_selections<R>(
2109 &mut self,
2110 autoscroll: Option<Autoscroll>,
2111 window: &mut Window,
2112 cx: &mut Context<Self>,
2113 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2114 ) -> R {
2115 self.change_selections_inner(autoscroll, true, window, cx, change)
2116 }
2117
2118 pub fn change_selections_inner<R>(
2119 &mut self,
2120 autoscroll: Option<Autoscroll>,
2121 request_completions: bool,
2122 window: &mut Window,
2123 cx: &mut Context<Self>,
2124 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2125 ) -> R {
2126 let old_cursor_position = self.selections.newest_anchor().head();
2127 self.push_to_selection_history();
2128
2129 let (changed, result) = self.selections.change_with(cx, change);
2130
2131 if changed {
2132 if let Some(autoscroll) = autoscroll {
2133 self.request_autoscroll(autoscroll, cx);
2134 }
2135 self.selections_did_change(true, &old_cursor_position, request_completions, window, cx);
2136
2137 if self.should_open_signature_help_automatically(
2138 &old_cursor_position,
2139 self.signature_help_state.backspace_pressed(),
2140 cx,
2141 ) {
2142 self.show_signature_help(&ShowSignatureHelp, window, cx);
2143 }
2144 self.signature_help_state.set_backspace_pressed(false);
2145 }
2146
2147 result
2148 }
2149
2150 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2151 where
2152 I: IntoIterator<Item = (Range<S>, T)>,
2153 S: ToOffset,
2154 T: Into<Arc<str>>,
2155 {
2156 if self.read_only(cx) {
2157 return;
2158 }
2159
2160 self.buffer
2161 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2162 }
2163
2164 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2165 where
2166 I: IntoIterator<Item = (Range<S>, T)>,
2167 S: ToOffset,
2168 T: Into<Arc<str>>,
2169 {
2170 if self.read_only(cx) {
2171 return;
2172 }
2173
2174 self.buffer.update(cx, |buffer, cx| {
2175 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2176 });
2177 }
2178
2179 pub fn edit_with_block_indent<I, S, T>(
2180 &mut self,
2181 edits: I,
2182 original_indent_columns: Vec<u32>,
2183 cx: &mut Context<Self>,
2184 ) where
2185 I: IntoIterator<Item = (Range<S>, T)>,
2186 S: ToOffset,
2187 T: Into<Arc<str>>,
2188 {
2189 if self.read_only(cx) {
2190 return;
2191 }
2192
2193 self.buffer.update(cx, |buffer, cx| {
2194 buffer.edit(
2195 edits,
2196 Some(AutoindentMode::Block {
2197 original_indent_columns,
2198 }),
2199 cx,
2200 )
2201 });
2202 }
2203
2204 fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
2205 self.hide_context_menu(window, cx);
2206
2207 match phase {
2208 SelectPhase::Begin {
2209 position,
2210 add,
2211 click_count,
2212 } => self.begin_selection(position, add, click_count, window, cx),
2213 SelectPhase::BeginColumnar {
2214 position,
2215 goal_column,
2216 reset,
2217 } => self.begin_columnar_selection(position, goal_column, reset, window, cx),
2218 SelectPhase::Extend {
2219 position,
2220 click_count,
2221 } => self.extend_selection(position, click_count, window, cx),
2222 SelectPhase::Update {
2223 position,
2224 goal_column,
2225 scroll_delta,
2226 } => self.update_selection(position, goal_column, scroll_delta, window, cx),
2227 SelectPhase::End => self.end_selection(window, cx),
2228 }
2229 }
2230
2231 fn extend_selection(
2232 &mut self,
2233 position: DisplayPoint,
2234 click_count: usize,
2235 window: &mut Window,
2236 cx: &mut Context<Self>,
2237 ) {
2238 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2239 let tail = self.selections.newest::<usize>(cx).tail();
2240 self.begin_selection(position, false, click_count, window, cx);
2241
2242 let position = position.to_offset(&display_map, Bias::Left);
2243 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2244
2245 let mut pending_selection = self
2246 .selections
2247 .pending_anchor()
2248 .expect("extend_selection not called with pending selection");
2249 if position >= tail {
2250 pending_selection.start = tail_anchor;
2251 } else {
2252 pending_selection.end = tail_anchor;
2253 pending_selection.reversed = true;
2254 }
2255
2256 let mut pending_mode = self.selections.pending_mode().unwrap();
2257 match &mut pending_mode {
2258 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2259 _ => {}
2260 }
2261
2262 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
2263 s.set_pending(pending_selection, pending_mode)
2264 });
2265 }
2266
2267 fn begin_selection(
2268 &mut self,
2269 position: DisplayPoint,
2270 add: bool,
2271 click_count: usize,
2272 window: &mut Window,
2273 cx: &mut Context<Self>,
2274 ) {
2275 if !self.focus_handle.is_focused(window) {
2276 self.last_focused_descendant = None;
2277 window.focus(&self.focus_handle);
2278 }
2279
2280 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2281 let buffer = &display_map.buffer_snapshot;
2282 let newest_selection = self.selections.newest_anchor().clone();
2283 let position = display_map.clip_point(position, Bias::Left);
2284
2285 let start;
2286 let end;
2287 let mode;
2288 let mut auto_scroll;
2289 match click_count {
2290 1 => {
2291 start = buffer.anchor_before(position.to_point(&display_map));
2292 end = start;
2293 mode = SelectMode::Character;
2294 auto_scroll = true;
2295 }
2296 2 => {
2297 let range = movement::surrounding_word(&display_map, position);
2298 start = buffer.anchor_before(range.start.to_point(&display_map));
2299 end = buffer.anchor_before(range.end.to_point(&display_map));
2300 mode = SelectMode::Word(start..end);
2301 auto_scroll = true;
2302 }
2303 3 => {
2304 let position = display_map
2305 .clip_point(position, Bias::Left)
2306 .to_point(&display_map);
2307 let line_start = display_map.prev_line_boundary(position).0;
2308 let next_line_start = buffer.clip_point(
2309 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2310 Bias::Left,
2311 );
2312 start = buffer.anchor_before(line_start);
2313 end = buffer.anchor_before(next_line_start);
2314 mode = SelectMode::Line(start..end);
2315 auto_scroll = true;
2316 }
2317 _ => {
2318 start = buffer.anchor_before(0);
2319 end = buffer.anchor_before(buffer.len());
2320 mode = SelectMode::All;
2321 auto_scroll = false;
2322 }
2323 }
2324 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2325
2326 let point_to_delete: Option<usize> = {
2327 let selected_points: Vec<Selection<Point>> =
2328 self.selections.disjoint_in_range(start..end, cx);
2329
2330 if !add || click_count > 1 {
2331 None
2332 } else if !selected_points.is_empty() {
2333 Some(selected_points[0].id)
2334 } else {
2335 let clicked_point_already_selected =
2336 self.selections.disjoint.iter().find(|selection| {
2337 selection.start.to_point(buffer) == start.to_point(buffer)
2338 || selection.end.to_point(buffer) == end.to_point(buffer)
2339 });
2340
2341 clicked_point_already_selected.map(|selection| selection.id)
2342 }
2343 };
2344
2345 let selections_count = self.selections.count();
2346
2347 self.change_selections(auto_scroll.then(Autoscroll::newest), window, cx, |s| {
2348 if let Some(point_to_delete) = point_to_delete {
2349 s.delete(point_to_delete);
2350
2351 if selections_count == 1 {
2352 s.set_pending_anchor_range(start..end, mode);
2353 }
2354 } else {
2355 if !add {
2356 s.clear_disjoint();
2357 } else if click_count > 1 {
2358 s.delete(newest_selection.id)
2359 }
2360
2361 s.set_pending_anchor_range(start..end, mode);
2362 }
2363 });
2364 }
2365
2366 fn begin_columnar_selection(
2367 &mut self,
2368 position: DisplayPoint,
2369 goal_column: u32,
2370 reset: bool,
2371 window: &mut Window,
2372 cx: &mut Context<Self>,
2373 ) {
2374 if !self.focus_handle.is_focused(window) {
2375 self.last_focused_descendant = None;
2376 window.focus(&self.focus_handle);
2377 }
2378
2379 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2380
2381 if reset {
2382 let pointer_position = display_map
2383 .buffer_snapshot
2384 .anchor_before(position.to_point(&display_map));
2385
2386 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
2387 s.clear_disjoint();
2388 s.set_pending_anchor_range(
2389 pointer_position..pointer_position,
2390 SelectMode::Character,
2391 );
2392 });
2393 }
2394
2395 let tail = self.selections.newest::<Point>(cx).tail();
2396 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2397
2398 if !reset {
2399 self.select_columns(
2400 tail.to_display_point(&display_map),
2401 position,
2402 goal_column,
2403 &display_map,
2404 window,
2405 cx,
2406 );
2407 }
2408 }
2409
2410 fn update_selection(
2411 &mut self,
2412 position: DisplayPoint,
2413 goal_column: u32,
2414 scroll_delta: gpui::Point<f32>,
2415 window: &mut Window,
2416 cx: &mut Context<Self>,
2417 ) {
2418 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2419
2420 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2421 let tail = tail.to_display_point(&display_map);
2422 self.select_columns(tail, position, goal_column, &display_map, window, cx);
2423 } else if let Some(mut pending) = self.selections.pending_anchor() {
2424 let buffer = self.buffer.read(cx).snapshot(cx);
2425 let head;
2426 let tail;
2427 let mode = self.selections.pending_mode().unwrap();
2428 match &mode {
2429 SelectMode::Character => {
2430 head = position.to_point(&display_map);
2431 tail = pending.tail().to_point(&buffer);
2432 }
2433 SelectMode::Word(original_range) => {
2434 let original_display_range = original_range.start.to_display_point(&display_map)
2435 ..original_range.end.to_display_point(&display_map);
2436 let original_buffer_range = original_display_range.start.to_point(&display_map)
2437 ..original_display_range.end.to_point(&display_map);
2438 if movement::is_inside_word(&display_map, position)
2439 || original_display_range.contains(&position)
2440 {
2441 let word_range = movement::surrounding_word(&display_map, position);
2442 if word_range.start < original_display_range.start {
2443 head = word_range.start.to_point(&display_map);
2444 } else {
2445 head = word_range.end.to_point(&display_map);
2446 }
2447 } else {
2448 head = position.to_point(&display_map);
2449 }
2450
2451 if head <= original_buffer_range.start {
2452 tail = original_buffer_range.end;
2453 } else {
2454 tail = original_buffer_range.start;
2455 }
2456 }
2457 SelectMode::Line(original_range) => {
2458 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2459
2460 let position = display_map
2461 .clip_point(position, Bias::Left)
2462 .to_point(&display_map);
2463 let line_start = display_map.prev_line_boundary(position).0;
2464 let next_line_start = buffer.clip_point(
2465 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2466 Bias::Left,
2467 );
2468
2469 if line_start < original_range.start {
2470 head = line_start
2471 } else {
2472 head = next_line_start
2473 }
2474
2475 if head <= original_range.start {
2476 tail = original_range.end;
2477 } else {
2478 tail = original_range.start;
2479 }
2480 }
2481 SelectMode::All => {
2482 return;
2483 }
2484 };
2485
2486 if head < tail {
2487 pending.start = buffer.anchor_before(head);
2488 pending.end = buffer.anchor_before(tail);
2489 pending.reversed = true;
2490 } else {
2491 pending.start = buffer.anchor_before(tail);
2492 pending.end = buffer.anchor_before(head);
2493 pending.reversed = false;
2494 }
2495
2496 self.change_selections(None, window, cx, |s| {
2497 s.set_pending(pending, mode);
2498 });
2499 } else {
2500 log::error!("update_selection dispatched with no pending selection");
2501 return;
2502 }
2503
2504 self.apply_scroll_delta(scroll_delta, window, cx);
2505 cx.notify();
2506 }
2507
2508 fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
2509 self.columnar_selection_tail.take();
2510 if self.selections.pending_anchor().is_some() {
2511 let selections = self.selections.all::<usize>(cx);
2512 self.change_selections(None, window, cx, |s| {
2513 s.select(selections);
2514 s.clear_pending();
2515 });
2516 }
2517 }
2518
2519 fn select_columns(
2520 &mut self,
2521 tail: DisplayPoint,
2522 head: DisplayPoint,
2523 goal_column: u32,
2524 display_map: &DisplaySnapshot,
2525 window: &mut Window,
2526 cx: &mut Context<Self>,
2527 ) {
2528 let start_row = cmp::min(tail.row(), head.row());
2529 let end_row = cmp::max(tail.row(), head.row());
2530 let start_column = cmp::min(tail.column(), goal_column);
2531 let end_column = cmp::max(tail.column(), goal_column);
2532 let reversed = start_column < tail.column();
2533
2534 let selection_ranges = (start_row.0..=end_row.0)
2535 .map(DisplayRow)
2536 .filter_map(|row| {
2537 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2538 let start = display_map
2539 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2540 .to_point(display_map);
2541 let end = display_map
2542 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2543 .to_point(display_map);
2544 if reversed {
2545 Some(end..start)
2546 } else {
2547 Some(start..end)
2548 }
2549 } else {
2550 None
2551 }
2552 })
2553 .collect::<Vec<_>>();
2554
2555 self.change_selections(None, window, cx, |s| {
2556 s.select_ranges(selection_ranges);
2557 });
2558 cx.notify();
2559 }
2560
2561 pub fn has_pending_nonempty_selection(&self) -> bool {
2562 let pending_nonempty_selection = match self.selections.pending_anchor() {
2563 Some(Selection { start, end, .. }) => start != end,
2564 None => false,
2565 };
2566
2567 pending_nonempty_selection
2568 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2569 }
2570
2571 pub fn has_pending_selection(&self) -> bool {
2572 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2573 }
2574
2575 pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
2576 self.selection_mark_mode = false;
2577
2578 if self.clear_expanded_diff_hunks(cx) {
2579 cx.notify();
2580 return;
2581 }
2582 if self.dismiss_menus_and_popups(true, window, cx) {
2583 return;
2584 }
2585
2586 if self.mode == EditorMode::Full
2587 && self.change_selections(Some(Autoscroll::fit()), window, cx, |s| s.try_cancel())
2588 {
2589 return;
2590 }
2591
2592 cx.propagate();
2593 }
2594
2595 pub fn dismiss_menus_and_popups(
2596 &mut self,
2597 should_report_inline_completion_event: bool,
2598 window: &mut Window,
2599 cx: &mut Context<Self>,
2600 ) -> bool {
2601 if self.take_rename(false, window, cx).is_some() {
2602 return true;
2603 }
2604
2605 if hide_hover(self, cx) {
2606 return true;
2607 }
2608
2609 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2610 return true;
2611 }
2612
2613 if self.hide_context_menu(window, cx).is_some() {
2614 if self.show_inline_completions_in_menu(cx) && self.has_active_inline_completion() {
2615 self.update_visible_inline_completion(window, cx);
2616 }
2617 return true;
2618 }
2619
2620 if self.mouse_context_menu.take().is_some() {
2621 return true;
2622 }
2623
2624 if self.discard_inline_completion(should_report_inline_completion_event, cx) {
2625 return true;
2626 }
2627
2628 if self.snippet_stack.pop().is_some() {
2629 return true;
2630 }
2631
2632 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
2633 self.dismiss_diagnostics(cx);
2634 return true;
2635 }
2636
2637 false
2638 }
2639
2640 fn linked_editing_ranges_for(
2641 &self,
2642 selection: Range<text::Anchor>,
2643 cx: &App,
2644 ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
2645 if self.linked_edit_ranges.is_empty() {
2646 return None;
2647 }
2648 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2649 selection.end.buffer_id.and_then(|end_buffer_id| {
2650 if selection.start.buffer_id != Some(end_buffer_id) {
2651 return None;
2652 }
2653 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2654 let snapshot = buffer.read(cx).snapshot();
2655 self.linked_edit_ranges
2656 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2657 .map(|ranges| (ranges, snapshot, buffer))
2658 })?;
2659 use text::ToOffset as TO;
2660 // find offset from the start of current range to current cursor position
2661 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2662
2663 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2664 let start_difference = start_offset - start_byte_offset;
2665 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2666 let end_difference = end_offset - start_byte_offset;
2667 // Current range has associated linked ranges.
2668 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2669 for range in linked_ranges.iter() {
2670 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2671 let end_offset = start_offset + end_difference;
2672 let start_offset = start_offset + start_difference;
2673 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2674 continue;
2675 }
2676 if self.selections.disjoint_anchor_ranges().any(|s| {
2677 if s.start.buffer_id != selection.start.buffer_id
2678 || s.end.buffer_id != selection.end.buffer_id
2679 {
2680 return false;
2681 }
2682 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
2683 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
2684 }) {
2685 continue;
2686 }
2687 let start = buffer_snapshot.anchor_after(start_offset);
2688 let end = buffer_snapshot.anchor_after(end_offset);
2689 linked_edits
2690 .entry(buffer.clone())
2691 .or_default()
2692 .push(start..end);
2693 }
2694 Some(linked_edits)
2695 }
2696
2697 pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
2698 let text: Arc<str> = text.into();
2699
2700 if self.read_only(cx) {
2701 return;
2702 }
2703
2704 let selections = self.selections.all_adjusted(cx);
2705 let mut bracket_inserted = false;
2706 let mut edits = Vec::new();
2707 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2708 let mut new_selections = Vec::with_capacity(selections.len());
2709 let mut new_autoclose_regions = Vec::new();
2710 let snapshot = self.buffer.read(cx).read(cx);
2711
2712 for (selection, autoclose_region) in
2713 self.selections_with_autoclose_regions(selections, &snapshot)
2714 {
2715 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2716 // Determine if the inserted text matches the opening or closing
2717 // bracket of any of this language's bracket pairs.
2718 let mut bracket_pair = None;
2719 let mut is_bracket_pair_start = false;
2720 let mut is_bracket_pair_end = false;
2721 if !text.is_empty() {
2722 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
2723 // and they are removing the character that triggered IME popup.
2724 for (pair, enabled) in scope.brackets() {
2725 if !pair.close && !pair.surround {
2726 continue;
2727 }
2728
2729 if enabled && pair.start.ends_with(text.as_ref()) {
2730 let prefix_len = pair.start.len() - text.len();
2731 let preceding_text_matches_prefix = prefix_len == 0
2732 || (selection.start.column >= (prefix_len as u32)
2733 && snapshot.contains_str_at(
2734 Point::new(
2735 selection.start.row,
2736 selection.start.column - (prefix_len as u32),
2737 ),
2738 &pair.start[..prefix_len],
2739 ));
2740 if preceding_text_matches_prefix {
2741 bracket_pair = Some(pair.clone());
2742 is_bracket_pair_start = true;
2743 break;
2744 }
2745 }
2746 if pair.end.as_str() == text.as_ref() {
2747 bracket_pair = Some(pair.clone());
2748 is_bracket_pair_end = true;
2749 break;
2750 }
2751 }
2752 }
2753
2754 if let Some(bracket_pair) = bracket_pair {
2755 let snapshot_settings = snapshot.settings_at(selection.start, cx);
2756 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
2757 let auto_surround =
2758 self.use_auto_surround && snapshot_settings.use_auto_surround;
2759 if selection.is_empty() {
2760 if is_bracket_pair_start {
2761 // If the inserted text is a suffix of an opening bracket and the
2762 // selection is preceded by the rest of the opening bracket, then
2763 // insert the closing bracket.
2764 let following_text_allows_autoclose = snapshot
2765 .chars_at(selection.start)
2766 .next()
2767 .map_or(true, |c| scope.should_autoclose_before(c));
2768
2769 let is_closing_quote = if bracket_pair.end == bracket_pair.start
2770 && bracket_pair.start.len() == 1
2771 {
2772 let target = bracket_pair.start.chars().next().unwrap();
2773 let current_line_count = snapshot
2774 .reversed_chars_at(selection.start)
2775 .take_while(|&c| c != '\n')
2776 .filter(|&c| c == target)
2777 .count();
2778 current_line_count % 2 == 1
2779 } else {
2780 false
2781 };
2782
2783 if autoclose
2784 && bracket_pair.close
2785 && following_text_allows_autoclose
2786 && !is_closing_quote
2787 {
2788 let anchor = snapshot.anchor_before(selection.end);
2789 new_selections.push((selection.map(|_| anchor), text.len()));
2790 new_autoclose_regions.push((
2791 anchor,
2792 text.len(),
2793 selection.id,
2794 bracket_pair.clone(),
2795 ));
2796 edits.push((
2797 selection.range(),
2798 format!("{}{}", text, bracket_pair.end).into(),
2799 ));
2800 bracket_inserted = true;
2801 continue;
2802 }
2803 }
2804
2805 if let Some(region) = autoclose_region {
2806 // If the selection is followed by an auto-inserted closing bracket,
2807 // then don't insert that closing bracket again; just move the selection
2808 // past the closing bracket.
2809 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2810 && text.as_ref() == region.pair.end.as_str();
2811 if should_skip {
2812 let anchor = snapshot.anchor_after(selection.end);
2813 new_selections
2814 .push((selection.map(|_| anchor), region.pair.end.len()));
2815 continue;
2816 }
2817 }
2818
2819 let always_treat_brackets_as_autoclosed = snapshot
2820 .settings_at(selection.start, cx)
2821 .always_treat_brackets_as_autoclosed;
2822 if always_treat_brackets_as_autoclosed
2823 && is_bracket_pair_end
2824 && snapshot.contains_str_at(selection.end, text.as_ref())
2825 {
2826 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
2827 // and the inserted text is a closing bracket and the selection is followed
2828 // by the closing bracket then move the selection past the closing bracket.
2829 let anchor = snapshot.anchor_after(selection.end);
2830 new_selections.push((selection.map(|_| anchor), text.len()));
2831 continue;
2832 }
2833 }
2834 // If an opening bracket is 1 character long and is typed while
2835 // text is selected, then surround that text with the bracket pair.
2836 else if auto_surround
2837 && bracket_pair.surround
2838 && is_bracket_pair_start
2839 && bracket_pair.start.chars().count() == 1
2840 {
2841 edits.push((selection.start..selection.start, text.clone()));
2842 edits.push((
2843 selection.end..selection.end,
2844 bracket_pair.end.as_str().into(),
2845 ));
2846 bracket_inserted = true;
2847 new_selections.push((
2848 Selection {
2849 id: selection.id,
2850 start: snapshot.anchor_after(selection.start),
2851 end: snapshot.anchor_before(selection.end),
2852 reversed: selection.reversed,
2853 goal: selection.goal,
2854 },
2855 0,
2856 ));
2857 continue;
2858 }
2859 }
2860 }
2861
2862 if self.auto_replace_emoji_shortcode
2863 && selection.is_empty()
2864 && text.as_ref().ends_with(':')
2865 {
2866 if let Some(possible_emoji_short_code) =
2867 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
2868 {
2869 if !possible_emoji_short_code.is_empty() {
2870 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
2871 let emoji_shortcode_start = Point::new(
2872 selection.start.row,
2873 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
2874 );
2875
2876 // Remove shortcode from buffer
2877 edits.push((
2878 emoji_shortcode_start..selection.start,
2879 "".to_string().into(),
2880 ));
2881 new_selections.push((
2882 Selection {
2883 id: selection.id,
2884 start: snapshot.anchor_after(emoji_shortcode_start),
2885 end: snapshot.anchor_before(selection.start),
2886 reversed: selection.reversed,
2887 goal: selection.goal,
2888 },
2889 0,
2890 ));
2891
2892 // Insert emoji
2893 let selection_start_anchor = snapshot.anchor_after(selection.start);
2894 new_selections.push((selection.map(|_| selection_start_anchor), 0));
2895 edits.push((selection.start..selection.end, emoji.to_string().into()));
2896
2897 continue;
2898 }
2899 }
2900 }
2901 }
2902
2903 // If not handling any auto-close operation, then just replace the selected
2904 // text with the given input and move the selection to the end of the
2905 // newly inserted text.
2906 let anchor = snapshot.anchor_after(selection.end);
2907 if !self.linked_edit_ranges.is_empty() {
2908 let start_anchor = snapshot.anchor_before(selection.start);
2909
2910 let is_word_char = text.chars().next().map_or(true, |char| {
2911 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
2912 classifier.is_word(char)
2913 });
2914
2915 if is_word_char {
2916 if let Some(ranges) = self
2917 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
2918 {
2919 for (buffer, edits) in ranges {
2920 linked_edits
2921 .entry(buffer.clone())
2922 .or_default()
2923 .extend(edits.into_iter().map(|range| (range, text.clone())));
2924 }
2925 }
2926 }
2927 }
2928
2929 new_selections.push((selection.map(|_| anchor), 0));
2930 edits.push((selection.start..selection.end, text.clone()));
2931 }
2932
2933 drop(snapshot);
2934
2935 self.transact(window, cx, |this, window, cx| {
2936 this.buffer.update(cx, |buffer, cx| {
2937 buffer.edit(edits, this.autoindent_mode.clone(), cx);
2938 });
2939 for (buffer, edits) in linked_edits {
2940 buffer.update(cx, |buffer, cx| {
2941 let snapshot = buffer.snapshot();
2942 let edits = edits
2943 .into_iter()
2944 .map(|(range, text)| {
2945 use text::ToPoint as TP;
2946 let end_point = TP::to_point(&range.end, &snapshot);
2947 let start_point = TP::to_point(&range.start, &snapshot);
2948 (start_point..end_point, text)
2949 })
2950 .sorted_by_key(|(range, _)| range.start)
2951 .collect::<Vec<_>>();
2952 buffer.edit(edits, None, cx);
2953 })
2954 }
2955 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
2956 let new_selection_deltas = new_selections.iter().map(|e| e.1);
2957 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
2958 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
2959 .zip(new_selection_deltas)
2960 .map(|(selection, delta)| Selection {
2961 id: selection.id,
2962 start: selection.start + delta,
2963 end: selection.end + delta,
2964 reversed: selection.reversed,
2965 goal: SelectionGoal::None,
2966 })
2967 .collect::<Vec<_>>();
2968
2969 let mut i = 0;
2970 for (position, delta, selection_id, pair) in new_autoclose_regions {
2971 let position = position.to_offset(&map.buffer_snapshot) + delta;
2972 let start = map.buffer_snapshot.anchor_before(position);
2973 let end = map.buffer_snapshot.anchor_after(position);
2974 while let Some(existing_state) = this.autoclose_regions.get(i) {
2975 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
2976 Ordering::Less => i += 1,
2977 Ordering::Greater => break,
2978 Ordering::Equal => {
2979 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
2980 Ordering::Less => i += 1,
2981 Ordering::Equal => break,
2982 Ordering::Greater => break,
2983 }
2984 }
2985 }
2986 }
2987 this.autoclose_regions.insert(
2988 i,
2989 AutocloseRegion {
2990 selection_id,
2991 range: start..end,
2992 pair,
2993 },
2994 );
2995 }
2996
2997 let had_active_inline_completion = this.has_active_inline_completion();
2998 this.change_selections_inner(Some(Autoscroll::fit()), false, window, cx, |s| {
2999 s.select(new_selections)
3000 });
3001
3002 if !bracket_inserted {
3003 if let Some(on_type_format_task) =
3004 this.trigger_on_type_formatting(text.to_string(), window, cx)
3005 {
3006 on_type_format_task.detach_and_log_err(cx);
3007 }
3008 }
3009
3010 let editor_settings = EditorSettings::get_global(cx);
3011 if bracket_inserted
3012 && (editor_settings.auto_signature_help
3013 || editor_settings.show_signature_help_after_edits)
3014 {
3015 this.show_signature_help(&ShowSignatureHelp, window, cx);
3016 }
3017
3018 let trigger_in_words =
3019 this.show_inline_completions_in_menu(cx) || !had_active_inline_completion;
3020 this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
3021 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
3022 this.refresh_inline_completion(true, false, window, cx);
3023 });
3024 }
3025
3026 fn find_possible_emoji_shortcode_at_position(
3027 snapshot: &MultiBufferSnapshot,
3028 position: Point,
3029 ) -> Option<String> {
3030 let mut chars = Vec::new();
3031 let mut found_colon = false;
3032 for char in snapshot.reversed_chars_at(position).take(100) {
3033 // Found a possible emoji shortcode in the middle of the buffer
3034 if found_colon {
3035 if char.is_whitespace() {
3036 chars.reverse();
3037 return Some(chars.iter().collect());
3038 }
3039 // If the previous character is not a whitespace, we are in the middle of a word
3040 // and we only want to complete the shortcode if the word is made up of other emojis
3041 let mut containing_word = String::new();
3042 for ch in snapshot
3043 .reversed_chars_at(position)
3044 .skip(chars.len() + 1)
3045 .take(100)
3046 {
3047 if ch.is_whitespace() {
3048 break;
3049 }
3050 containing_word.push(ch);
3051 }
3052 let containing_word = containing_word.chars().rev().collect::<String>();
3053 if util::word_consists_of_emojis(containing_word.as_str()) {
3054 chars.reverse();
3055 return Some(chars.iter().collect());
3056 }
3057 }
3058
3059 if char.is_whitespace() || !char.is_ascii() {
3060 return None;
3061 }
3062 if char == ':' {
3063 found_colon = true;
3064 } else {
3065 chars.push(char);
3066 }
3067 }
3068 // Found a possible emoji shortcode at the beginning of the buffer
3069 chars.reverse();
3070 Some(chars.iter().collect())
3071 }
3072
3073 pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
3074 self.transact(window, cx, |this, window, cx| {
3075 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3076 let selections = this.selections.all::<usize>(cx);
3077 let multi_buffer = this.buffer.read(cx);
3078 let buffer = multi_buffer.snapshot(cx);
3079 selections
3080 .iter()
3081 .map(|selection| {
3082 let start_point = selection.start.to_point(&buffer);
3083 let mut indent =
3084 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3085 indent.len = cmp::min(indent.len, start_point.column);
3086 let start = selection.start;
3087 let end = selection.end;
3088 let selection_is_empty = start == end;
3089 let language_scope = buffer.language_scope_at(start);
3090 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3091 &language_scope
3092 {
3093 let leading_whitespace_len = buffer
3094 .reversed_chars_at(start)
3095 .take_while(|c| c.is_whitespace() && *c != '\n')
3096 .map(|c| c.len_utf8())
3097 .sum::<usize>();
3098
3099 let trailing_whitespace_len = buffer
3100 .chars_at(end)
3101 .take_while(|c| c.is_whitespace() && *c != '\n')
3102 .map(|c| c.len_utf8())
3103 .sum::<usize>();
3104
3105 let insert_extra_newline =
3106 language.brackets().any(|(pair, enabled)| {
3107 let pair_start = pair.start.trim_end();
3108 let pair_end = pair.end.trim_start();
3109
3110 enabled
3111 && pair.newline
3112 && buffer.contains_str_at(
3113 end + trailing_whitespace_len,
3114 pair_end,
3115 )
3116 && buffer.contains_str_at(
3117 (start - leading_whitespace_len)
3118 .saturating_sub(pair_start.len()),
3119 pair_start,
3120 )
3121 });
3122
3123 // Comment extension on newline is allowed only for cursor selections
3124 let comment_delimiter = maybe!({
3125 if !selection_is_empty {
3126 return None;
3127 }
3128
3129 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
3130 return None;
3131 }
3132
3133 let delimiters = language.line_comment_prefixes();
3134 let max_len_of_delimiter =
3135 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3136 let (snapshot, range) =
3137 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3138
3139 let mut index_of_first_non_whitespace = 0;
3140 let comment_candidate = snapshot
3141 .chars_for_range(range)
3142 .skip_while(|c| {
3143 let should_skip = c.is_whitespace();
3144 if should_skip {
3145 index_of_first_non_whitespace += 1;
3146 }
3147 should_skip
3148 })
3149 .take(max_len_of_delimiter)
3150 .collect::<String>();
3151 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3152 comment_candidate.starts_with(comment_prefix.as_ref())
3153 })?;
3154 let cursor_is_placed_after_comment_marker =
3155 index_of_first_non_whitespace + comment_prefix.len()
3156 <= start_point.column as usize;
3157 if cursor_is_placed_after_comment_marker {
3158 Some(comment_prefix.clone())
3159 } else {
3160 None
3161 }
3162 });
3163 (comment_delimiter, insert_extra_newline)
3164 } else {
3165 (None, false)
3166 };
3167
3168 let capacity_for_delimiter = comment_delimiter
3169 .as_deref()
3170 .map(str::len)
3171 .unwrap_or_default();
3172 let mut new_text =
3173 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3174 new_text.push('\n');
3175 new_text.extend(indent.chars());
3176 if let Some(delimiter) = &comment_delimiter {
3177 new_text.push_str(delimiter);
3178 }
3179 if insert_extra_newline {
3180 new_text = new_text.repeat(2);
3181 }
3182
3183 let anchor = buffer.anchor_after(end);
3184 let new_selection = selection.map(|_| anchor);
3185 (
3186 (start..end, new_text),
3187 (insert_extra_newline, new_selection),
3188 )
3189 })
3190 .unzip()
3191 };
3192
3193 this.edit_with_autoindent(edits, cx);
3194 let buffer = this.buffer.read(cx).snapshot(cx);
3195 let new_selections = selection_fixup_info
3196 .into_iter()
3197 .map(|(extra_newline_inserted, new_selection)| {
3198 let mut cursor = new_selection.end.to_point(&buffer);
3199 if extra_newline_inserted {
3200 cursor.row -= 1;
3201 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3202 }
3203 new_selection.map(|_| cursor)
3204 })
3205 .collect();
3206
3207 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3208 s.select(new_selections)
3209 });
3210 this.refresh_inline_completion(true, false, window, cx);
3211 });
3212 }
3213
3214 pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
3215 let buffer = self.buffer.read(cx);
3216 let snapshot = buffer.snapshot(cx);
3217
3218 let mut edits = Vec::new();
3219 let mut rows = Vec::new();
3220
3221 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3222 let cursor = selection.head();
3223 let row = cursor.row;
3224
3225 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3226
3227 let newline = "\n".to_string();
3228 edits.push((start_of_line..start_of_line, newline));
3229
3230 rows.push(row + rows_inserted as u32);
3231 }
3232
3233 self.transact(window, cx, |editor, window, cx| {
3234 editor.edit(edits, cx);
3235
3236 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3237 let mut index = 0;
3238 s.move_cursors_with(|map, _, _| {
3239 let row = rows[index];
3240 index += 1;
3241
3242 let point = Point::new(row, 0);
3243 let boundary = map.next_line_boundary(point).1;
3244 let clipped = map.clip_point(boundary, Bias::Left);
3245
3246 (clipped, SelectionGoal::None)
3247 });
3248 });
3249
3250 let mut indent_edits = Vec::new();
3251 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3252 for row in rows {
3253 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3254 for (row, indent) in indents {
3255 if indent.len == 0 {
3256 continue;
3257 }
3258
3259 let text = match indent.kind {
3260 IndentKind::Space => " ".repeat(indent.len as usize),
3261 IndentKind::Tab => "\t".repeat(indent.len as usize),
3262 };
3263 let point = Point::new(row.0, 0);
3264 indent_edits.push((point..point, text));
3265 }
3266 }
3267 editor.edit(indent_edits, cx);
3268 });
3269 }
3270
3271 pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
3272 let buffer = self.buffer.read(cx);
3273 let snapshot = buffer.snapshot(cx);
3274
3275 let mut edits = Vec::new();
3276 let mut rows = Vec::new();
3277 let mut rows_inserted = 0;
3278
3279 for selection in self.selections.all_adjusted(cx) {
3280 let cursor = selection.head();
3281 let row = cursor.row;
3282
3283 let point = Point::new(row + 1, 0);
3284 let start_of_line = snapshot.clip_point(point, Bias::Left);
3285
3286 let newline = "\n".to_string();
3287 edits.push((start_of_line..start_of_line, newline));
3288
3289 rows_inserted += 1;
3290 rows.push(row + rows_inserted);
3291 }
3292
3293 self.transact(window, cx, |editor, window, cx| {
3294 editor.edit(edits, cx);
3295
3296 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3297 let mut index = 0;
3298 s.move_cursors_with(|map, _, _| {
3299 let row = rows[index];
3300 index += 1;
3301
3302 let point = Point::new(row, 0);
3303 let boundary = map.next_line_boundary(point).1;
3304 let clipped = map.clip_point(boundary, Bias::Left);
3305
3306 (clipped, SelectionGoal::None)
3307 });
3308 });
3309
3310 let mut indent_edits = Vec::new();
3311 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3312 for row in rows {
3313 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3314 for (row, indent) in indents {
3315 if indent.len == 0 {
3316 continue;
3317 }
3318
3319 let text = match indent.kind {
3320 IndentKind::Space => " ".repeat(indent.len as usize),
3321 IndentKind::Tab => "\t".repeat(indent.len as usize),
3322 };
3323 let point = Point::new(row.0, 0);
3324 indent_edits.push((point..point, text));
3325 }
3326 }
3327 editor.edit(indent_edits, cx);
3328 });
3329 }
3330
3331 pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3332 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3333 original_indent_columns: Vec::new(),
3334 });
3335 self.insert_with_autoindent_mode(text, autoindent, window, cx);
3336 }
3337
3338 fn insert_with_autoindent_mode(
3339 &mut self,
3340 text: &str,
3341 autoindent_mode: Option<AutoindentMode>,
3342 window: &mut Window,
3343 cx: &mut Context<Self>,
3344 ) {
3345 if self.read_only(cx) {
3346 return;
3347 }
3348
3349 let text: Arc<str> = text.into();
3350 self.transact(window, cx, |this, window, cx| {
3351 let old_selections = this.selections.all_adjusted(cx);
3352 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3353 let anchors = {
3354 let snapshot = buffer.read(cx);
3355 old_selections
3356 .iter()
3357 .map(|s| {
3358 let anchor = snapshot.anchor_after(s.head());
3359 s.map(|_| anchor)
3360 })
3361 .collect::<Vec<_>>()
3362 };
3363 buffer.edit(
3364 old_selections
3365 .iter()
3366 .map(|s| (s.start..s.end, text.clone())),
3367 autoindent_mode,
3368 cx,
3369 );
3370 anchors
3371 });
3372
3373 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3374 s.select_anchors(selection_anchors);
3375 });
3376
3377 cx.notify();
3378 });
3379 }
3380
3381 fn trigger_completion_on_input(
3382 &mut self,
3383 text: &str,
3384 trigger_in_words: bool,
3385 window: &mut Window,
3386 cx: &mut Context<Self>,
3387 ) {
3388 if self.is_completion_trigger(text, trigger_in_words, cx) {
3389 self.show_completions(
3390 &ShowCompletions {
3391 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3392 },
3393 window,
3394 cx,
3395 );
3396 } else {
3397 self.hide_context_menu(window, cx);
3398 }
3399 }
3400
3401 fn is_completion_trigger(
3402 &self,
3403 text: &str,
3404 trigger_in_words: bool,
3405 cx: &mut Context<Self>,
3406 ) -> bool {
3407 let position = self.selections.newest_anchor().head();
3408 let multibuffer = self.buffer.read(cx);
3409 let Some(buffer) = position
3410 .buffer_id
3411 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3412 else {
3413 return false;
3414 };
3415
3416 if let Some(completion_provider) = &self.completion_provider {
3417 completion_provider.is_completion_trigger(
3418 &buffer,
3419 position.text_anchor,
3420 text,
3421 trigger_in_words,
3422 cx,
3423 )
3424 } else {
3425 false
3426 }
3427 }
3428
3429 /// If any empty selections is touching the start of its innermost containing autoclose
3430 /// region, expand it to select the brackets.
3431 fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3432 let selections = self.selections.all::<usize>(cx);
3433 let buffer = self.buffer.read(cx).read(cx);
3434 let new_selections = self
3435 .selections_with_autoclose_regions(selections, &buffer)
3436 .map(|(mut selection, region)| {
3437 if !selection.is_empty() {
3438 return selection;
3439 }
3440
3441 if let Some(region) = region {
3442 let mut range = region.range.to_offset(&buffer);
3443 if selection.start == range.start && range.start >= region.pair.start.len() {
3444 range.start -= region.pair.start.len();
3445 if buffer.contains_str_at(range.start, ®ion.pair.start)
3446 && buffer.contains_str_at(range.end, ®ion.pair.end)
3447 {
3448 range.end += region.pair.end.len();
3449 selection.start = range.start;
3450 selection.end = range.end;
3451
3452 return selection;
3453 }
3454 }
3455 }
3456
3457 let always_treat_brackets_as_autoclosed = buffer
3458 .settings_at(selection.start, cx)
3459 .always_treat_brackets_as_autoclosed;
3460
3461 if !always_treat_brackets_as_autoclosed {
3462 return selection;
3463 }
3464
3465 if let Some(scope) = buffer.language_scope_at(selection.start) {
3466 for (pair, enabled) in scope.brackets() {
3467 if !enabled || !pair.close {
3468 continue;
3469 }
3470
3471 if buffer.contains_str_at(selection.start, &pair.end) {
3472 let pair_start_len = pair.start.len();
3473 if buffer.contains_str_at(
3474 selection.start.saturating_sub(pair_start_len),
3475 &pair.start,
3476 ) {
3477 selection.start -= pair_start_len;
3478 selection.end += pair.end.len();
3479
3480 return selection;
3481 }
3482 }
3483 }
3484 }
3485
3486 selection
3487 })
3488 .collect();
3489
3490 drop(buffer);
3491 self.change_selections(None, window, cx, |selections| {
3492 selections.select(new_selections)
3493 });
3494 }
3495
3496 /// Iterate the given selections, and for each one, find the smallest surrounding
3497 /// autoclose region. This uses the ordering of the selections and the autoclose
3498 /// regions to avoid repeated comparisons.
3499 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3500 &'a self,
3501 selections: impl IntoIterator<Item = Selection<D>>,
3502 buffer: &'a MultiBufferSnapshot,
3503 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3504 let mut i = 0;
3505 let mut regions = self.autoclose_regions.as_slice();
3506 selections.into_iter().map(move |selection| {
3507 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3508
3509 let mut enclosing = None;
3510 while let Some(pair_state) = regions.get(i) {
3511 if pair_state.range.end.to_offset(buffer) < range.start {
3512 regions = ®ions[i + 1..];
3513 i = 0;
3514 } else if pair_state.range.start.to_offset(buffer) > range.end {
3515 break;
3516 } else {
3517 if pair_state.selection_id == selection.id {
3518 enclosing = Some(pair_state);
3519 }
3520 i += 1;
3521 }
3522 }
3523
3524 (selection, enclosing)
3525 })
3526 }
3527
3528 /// Remove any autoclose regions that no longer contain their selection.
3529 fn invalidate_autoclose_regions(
3530 &mut self,
3531 mut selections: &[Selection<Anchor>],
3532 buffer: &MultiBufferSnapshot,
3533 ) {
3534 self.autoclose_regions.retain(|state| {
3535 let mut i = 0;
3536 while let Some(selection) = selections.get(i) {
3537 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3538 selections = &selections[1..];
3539 continue;
3540 }
3541 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3542 break;
3543 }
3544 if selection.id == state.selection_id {
3545 return true;
3546 } else {
3547 i += 1;
3548 }
3549 }
3550 false
3551 });
3552 }
3553
3554 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3555 let offset = position.to_offset(buffer);
3556 let (word_range, kind) = buffer.surrounding_word(offset, true);
3557 if offset > word_range.start && kind == Some(CharKind::Word) {
3558 Some(
3559 buffer
3560 .text_for_range(word_range.start..offset)
3561 .collect::<String>(),
3562 )
3563 } else {
3564 None
3565 }
3566 }
3567
3568 pub fn toggle_inlay_hints(
3569 &mut self,
3570 _: &ToggleInlayHints,
3571 _: &mut Window,
3572 cx: &mut Context<Self>,
3573 ) {
3574 self.refresh_inlay_hints(
3575 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
3576 cx,
3577 );
3578 }
3579
3580 pub fn inlay_hints_enabled(&self) -> bool {
3581 self.inlay_hint_cache.enabled
3582 }
3583
3584 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut Context<Self>) {
3585 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
3586 return;
3587 }
3588
3589 let reason_description = reason.description();
3590 let ignore_debounce = matches!(
3591 reason,
3592 InlayHintRefreshReason::SettingsChange(_)
3593 | InlayHintRefreshReason::Toggle(_)
3594 | InlayHintRefreshReason::ExcerptsRemoved(_)
3595 );
3596 let (invalidate_cache, required_languages) = match reason {
3597 InlayHintRefreshReason::Toggle(enabled) => {
3598 self.inlay_hint_cache.enabled = enabled;
3599 if enabled {
3600 (InvalidationStrategy::RefreshRequested, None)
3601 } else {
3602 self.inlay_hint_cache.clear();
3603 self.splice_inlays(
3604 self.visible_inlay_hints(cx)
3605 .iter()
3606 .map(|inlay| inlay.id)
3607 .collect(),
3608 Vec::new(),
3609 cx,
3610 );
3611 return;
3612 }
3613 }
3614 InlayHintRefreshReason::SettingsChange(new_settings) => {
3615 match self.inlay_hint_cache.update_settings(
3616 &self.buffer,
3617 new_settings,
3618 self.visible_inlay_hints(cx),
3619 cx,
3620 ) {
3621 ControlFlow::Break(Some(InlaySplice {
3622 to_remove,
3623 to_insert,
3624 })) => {
3625 self.splice_inlays(to_remove, to_insert, cx);
3626 return;
3627 }
3628 ControlFlow::Break(None) => return,
3629 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3630 }
3631 }
3632 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3633 if let Some(InlaySplice {
3634 to_remove,
3635 to_insert,
3636 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3637 {
3638 self.splice_inlays(to_remove, to_insert, cx);
3639 }
3640 return;
3641 }
3642 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3643 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3644 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3645 }
3646 InlayHintRefreshReason::RefreshRequested => {
3647 (InvalidationStrategy::RefreshRequested, None)
3648 }
3649 };
3650
3651 if let Some(InlaySplice {
3652 to_remove,
3653 to_insert,
3654 }) = self.inlay_hint_cache.spawn_hint_refresh(
3655 reason_description,
3656 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3657 invalidate_cache,
3658 ignore_debounce,
3659 cx,
3660 ) {
3661 self.splice_inlays(to_remove, to_insert, cx);
3662 }
3663 }
3664
3665 fn visible_inlay_hints(&self, cx: &Context<Editor>) -> Vec<Inlay> {
3666 self.display_map
3667 .read(cx)
3668 .current_inlays()
3669 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3670 .cloned()
3671 .collect()
3672 }
3673
3674 pub fn excerpts_for_inlay_hints_query(
3675 &self,
3676 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3677 cx: &mut Context<Editor>,
3678 ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
3679 let Some(project) = self.project.as_ref() else {
3680 return HashMap::default();
3681 };
3682 let project = project.read(cx);
3683 let multi_buffer = self.buffer().read(cx);
3684 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3685 let multi_buffer_visible_start = self
3686 .scroll_manager
3687 .anchor()
3688 .anchor
3689 .to_point(&multi_buffer_snapshot);
3690 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3691 multi_buffer_visible_start
3692 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3693 Bias::Left,
3694 );
3695 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3696 multi_buffer_snapshot
3697 .range_to_buffer_ranges(multi_buffer_visible_range)
3698 .into_iter()
3699 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3700 .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
3701 let buffer_file = project::File::from_dyn(buffer.file())?;
3702 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3703 let worktree_entry = buffer_worktree
3704 .read(cx)
3705 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3706 if worktree_entry.is_ignored {
3707 return None;
3708 }
3709
3710 let language = buffer.language()?;
3711 if let Some(restrict_to_languages) = restrict_to_languages {
3712 if !restrict_to_languages.contains(language) {
3713 return None;
3714 }
3715 }
3716 Some((
3717 excerpt_id,
3718 (
3719 multi_buffer.buffer(buffer.remote_id()).unwrap(),
3720 buffer.version().clone(),
3721 excerpt_visible_range,
3722 ),
3723 ))
3724 })
3725 .collect()
3726 }
3727
3728 pub fn text_layout_details(&self, window: &mut Window) -> TextLayoutDetails {
3729 TextLayoutDetails {
3730 text_system: window.text_system().clone(),
3731 editor_style: self.style.clone().unwrap(),
3732 rem_size: window.rem_size(),
3733 scroll_anchor: self.scroll_manager.anchor(),
3734 visible_rows: self.visible_line_count(),
3735 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
3736 }
3737 }
3738
3739 pub fn splice_inlays(
3740 &self,
3741 to_remove: Vec<InlayId>,
3742 to_insert: Vec<Inlay>,
3743 cx: &mut Context<Self>,
3744 ) {
3745 self.display_map.update(cx, |display_map, cx| {
3746 display_map.splice_inlays(to_remove, to_insert, cx)
3747 });
3748 cx.notify();
3749 }
3750
3751 fn trigger_on_type_formatting(
3752 &self,
3753 input: String,
3754 window: &mut Window,
3755 cx: &mut Context<Self>,
3756 ) -> Option<Task<Result<()>>> {
3757 if input.len() != 1 {
3758 return None;
3759 }
3760
3761 let project = self.project.as_ref()?;
3762 let position = self.selections.newest_anchor().head();
3763 let (buffer, buffer_position) = self
3764 .buffer
3765 .read(cx)
3766 .text_anchor_for_position(position, cx)?;
3767
3768 let settings = language_settings::language_settings(
3769 buffer
3770 .read(cx)
3771 .language_at(buffer_position)
3772 .map(|l| l.name()),
3773 buffer.read(cx).file(),
3774 cx,
3775 );
3776 if !settings.use_on_type_format {
3777 return None;
3778 }
3779
3780 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3781 // hence we do LSP request & edit on host side only — add formats to host's history.
3782 let push_to_lsp_host_history = true;
3783 // If this is not the host, append its history with new edits.
3784 let push_to_client_history = project.read(cx).is_via_collab();
3785
3786 let on_type_formatting = project.update(cx, |project, cx| {
3787 project.on_type_format(
3788 buffer.clone(),
3789 buffer_position,
3790 input,
3791 push_to_lsp_host_history,
3792 cx,
3793 )
3794 });
3795 Some(cx.spawn_in(window, |editor, mut cx| async move {
3796 if let Some(transaction) = on_type_formatting.await? {
3797 if push_to_client_history {
3798 buffer
3799 .update(&mut cx, |buffer, _| {
3800 buffer.push_transaction(transaction, Instant::now());
3801 })
3802 .ok();
3803 }
3804 editor.update(&mut cx, |editor, cx| {
3805 editor.refresh_document_highlights(cx);
3806 })?;
3807 }
3808 Ok(())
3809 }))
3810 }
3811
3812 pub fn show_completions(
3813 &mut self,
3814 options: &ShowCompletions,
3815 window: &mut Window,
3816 cx: &mut Context<Self>,
3817 ) {
3818 if self.pending_rename.is_some() {
3819 return;
3820 }
3821
3822 let Some(provider) = self.completion_provider.as_ref() else {
3823 return;
3824 };
3825
3826 if !self.snippet_stack.is_empty() && self.context_menu.borrow().as_ref().is_some() {
3827 return;
3828 }
3829
3830 let position = self.selections.newest_anchor().head();
3831 if position.diff_base_anchor.is_some() {
3832 return;
3833 }
3834 let (buffer, buffer_position) =
3835 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
3836 output
3837 } else {
3838 return;
3839 };
3840 let show_completion_documentation = buffer
3841 .read(cx)
3842 .snapshot()
3843 .settings_at(buffer_position, cx)
3844 .show_completion_documentation;
3845
3846 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
3847
3848 let trigger_kind = match &options.trigger {
3849 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
3850 CompletionTriggerKind::TRIGGER_CHARACTER
3851 }
3852 _ => CompletionTriggerKind::INVOKED,
3853 };
3854 let completion_context = CompletionContext {
3855 trigger_character: options.trigger.as_ref().and_then(|trigger| {
3856 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
3857 Some(String::from(trigger))
3858 } else {
3859 None
3860 }
3861 }),
3862 trigger_kind,
3863 };
3864 let completions =
3865 provider.completions(&buffer, buffer_position, completion_context, window, cx);
3866 let sort_completions = provider.sort_completions();
3867
3868 let id = post_inc(&mut self.next_completion_id);
3869 let task = cx.spawn_in(window, |editor, mut cx| {
3870 async move {
3871 editor.update(&mut cx, |this, _| {
3872 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
3873 })?;
3874 let completions = completions.await.log_err();
3875 let menu = if let Some(completions) = completions {
3876 let mut menu = CompletionsMenu::new(
3877 id,
3878 sort_completions,
3879 show_completion_documentation,
3880 position,
3881 buffer.clone(),
3882 completions.into(),
3883 );
3884
3885 menu.filter(query.as_deref(), cx.background_executor().clone())
3886 .await;
3887
3888 menu.visible().then_some(menu)
3889 } else {
3890 None
3891 };
3892
3893 editor.update_in(&mut cx, |editor, window, cx| {
3894 match editor.context_menu.borrow().as_ref() {
3895 None => {}
3896 Some(CodeContextMenu::Completions(prev_menu)) => {
3897 if prev_menu.id > id {
3898 return;
3899 }
3900 }
3901 _ => return,
3902 }
3903
3904 if editor.focus_handle.is_focused(window) && menu.is_some() {
3905 let mut menu = menu.unwrap();
3906 menu.resolve_visible_completions(editor.completion_provider.as_deref(), cx);
3907
3908 if editor.show_inline_completions_in_menu(cx) {
3909 if let Some(hint) = editor.inline_completion_menu_hint(window, cx) {
3910 menu.show_inline_completion_hint(hint);
3911 }
3912 } else {
3913 editor.discard_inline_completion(false, cx);
3914 }
3915
3916 *editor.context_menu.borrow_mut() =
3917 Some(CodeContextMenu::Completions(menu));
3918
3919 cx.notify();
3920 } else if editor.completion_tasks.len() <= 1 {
3921 // If there are no more completion tasks and the last menu was
3922 // empty, we should hide it.
3923 let was_hidden = editor.hide_context_menu(window, cx).is_none();
3924 // If it was already hidden and we don't show inline
3925 // completions in the menu, we should also show the
3926 // inline-completion when available.
3927 if was_hidden && editor.show_inline_completions_in_menu(cx) {
3928 editor.update_visible_inline_completion(window, cx);
3929 }
3930 }
3931 })?;
3932
3933 Ok::<_, anyhow::Error>(())
3934 }
3935 .log_err()
3936 });
3937
3938 self.completion_tasks.push((id, task));
3939 }
3940
3941 pub fn confirm_completion(
3942 &mut self,
3943 action: &ConfirmCompletion,
3944 window: &mut Window,
3945 cx: &mut Context<Self>,
3946 ) -> Option<Task<Result<()>>> {
3947 self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
3948 }
3949
3950 pub fn compose_completion(
3951 &mut self,
3952 action: &ComposeCompletion,
3953 window: &mut Window,
3954 cx: &mut Context<Self>,
3955 ) -> Option<Task<Result<()>>> {
3956 self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
3957 }
3958
3959 fn toggle_zed_predict_onboarding(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3960 let (Some(workspace), Some(project)) = (self.workspace(), self.project.as_ref()) else {
3961 return;
3962 };
3963
3964 let project = project.read(cx);
3965
3966 ZedPredictModal::toggle(
3967 workspace,
3968 project.user_store().clone(),
3969 project.client().clone(),
3970 project.fs().clone(),
3971 window,
3972 cx,
3973 );
3974 }
3975
3976 fn do_completion(
3977 &mut self,
3978 item_ix: Option<usize>,
3979 intent: CompletionIntent,
3980 window: &mut Window,
3981 cx: &mut Context<Editor>,
3982 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
3983 use language::ToOffset as _;
3984
3985 {
3986 let context_menu = self.context_menu.borrow();
3987 if let CodeContextMenu::Completions(menu) = context_menu.as_ref()? {
3988 let entries = menu.entries.borrow();
3989 let entry = entries.get(item_ix.unwrap_or(menu.selected_item));
3990 match entry {
3991 Some(CompletionEntry::InlineCompletionHint(
3992 InlineCompletionMenuHint::Loading,
3993 )) => return Some(Task::ready(Ok(()))),
3994 Some(CompletionEntry::InlineCompletionHint(InlineCompletionMenuHint::None)) => {
3995 drop(entries);
3996 drop(context_menu);
3997 self.context_menu_next(&Default::default(), window, cx);
3998 return Some(Task::ready(Ok(())));
3999 }
4000 Some(CompletionEntry::InlineCompletionHint(
4001 InlineCompletionMenuHint::PendingTermsAcceptance,
4002 )) => {
4003 drop(entries);
4004 drop(context_menu);
4005 self.toggle_zed_predict_onboarding(window, cx);
4006 return Some(Task::ready(Ok(())));
4007 }
4008 _ => {}
4009 }
4010 }
4011 }
4012
4013 let completions_menu =
4014 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(window, cx)? {
4015 menu
4016 } else {
4017 return None;
4018 };
4019
4020 let entries = completions_menu.entries.borrow();
4021 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
4022 let mat = match mat {
4023 CompletionEntry::InlineCompletionHint(_) => {
4024 self.accept_inline_completion(&AcceptInlineCompletion, window, cx);
4025 cx.stop_propagation();
4026 return Some(Task::ready(Ok(())));
4027 }
4028 CompletionEntry::Match(mat) => {
4029 if self.show_inline_completions_in_menu(cx) {
4030 self.discard_inline_completion(true, cx);
4031 }
4032 mat
4033 }
4034 };
4035 let candidate_id = mat.candidate_id;
4036 drop(entries);
4037
4038 let buffer_handle = completions_menu.buffer;
4039 let completion = completions_menu
4040 .completions
4041 .borrow()
4042 .get(candidate_id)?
4043 .clone();
4044 cx.stop_propagation();
4045
4046 let snippet;
4047 let text;
4048
4049 if completion.is_snippet() {
4050 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4051 text = snippet.as_ref().unwrap().text.clone();
4052 } else {
4053 snippet = None;
4054 text = completion.new_text.clone();
4055 };
4056 let selections = self.selections.all::<usize>(cx);
4057 let buffer = buffer_handle.read(cx);
4058 let old_range = completion.old_range.to_offset(buffer);
4059 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4060
4061 let newest_selection = self.selections.newest_anchor();
4062 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4063 return None;
4064 }
4065
4066 let lookbehind = newest_selection
4067 .start
4068 .text_anchor
4069 .to_offset(buffer)
4070 .saturating_sub(old_range.start);
4071 let lookahead = old_range
4072 .end
4073 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4074 let mut common_prefix_len = old_text
4075 .bytes()
4076 .zip(text.bytes())
4077 .take_while(|(a, b)| a == b)
4078 .count();
4079
4080 let snapshot = self.buffer.read(cx).snapshot(cx);
4081 let mut range_to_replace: Option<Range<isize>> = None;
4082 let mut ranges = Vec::new();
4083 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4084 for selection in &selections {
4085 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4086 let start = selection.start.saturating_sub(lookbehind);
4087 let end = selection.end + lookahead;
4088 if selection.id == newest_selection.id {
4089 range_to_replace = Some(
4090 ((start + common_prefix_len) as isize - selection.start as isize)
4091 ..(end as isize - selection.start as isize),
4092 );
4093 }
4094 ranges.push(start + common_prefix_len..end);
4095 } else {
4096 common_prefix_len = 0;
4097 ranges.clear();
4098 ranges.extend(selections.iter().map(|s| {
4099 if s.id == newest_selection.id {
4100 range_to_replace = Some(
4101 old_range.start.to_offset_utf16(&snapshot).0 as isize
4102 - selection.start as isize
4103 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4104 - selection.start as isize,
4105 );
4106 old_range.clone()
4107 } else {
4108 s.start..s.end
4109 }
4110 }));
4111 break;
4112 }
4113 if !self.linked_edit_ranges.is_empty() {
4114 let start_anchor = snapshot.anchor_before(selection.head());
4115 let end_anchor = snapshot.anchor_after(selection.tail());
4116 if let Some(ranges) = self
4117 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4118 {
4119 for (buffer, edits) in ranges {
4120 linked_edits.entry(buffer.clone()).or_default().extend(
4121 edits
4122 .into_iter()
4123 .map(|range| (range, text[common_prefix_len..].to_owned())),
4124 );
4125 }
4126 }
4127 }
4128 }
4129 let text = &text[common_prefix_len..];
4130
4131 cx.emit(EditorEvent::InputHandled {
4132 utf16_range_to_replace: range_to_replace,
4133 text: text.into(),
4134 });
4135
4136 self.transact(window, cx, |this, window, cx| {
4137 if let Some(mut snippet) = snippet {
4138 snippet.text = text.to_string();
4139 for tabstop in snippet
4140 .tabstops
4141 .iter_mut()
4142 .flat_map(|tabstop| tabstop.ranges.iter_mut())
4143 {
4144 tabstop.start -= common_prefix_len as isize;
4145 tabstop.end -= common_prefix_len as isize;
4146 }
4147
4148 this.insert_snippet(&ranges, snippet, window, cx).log_err();
4149 } else {
4150 this.buffer.update(cx, |buffer, cx| {
4151 buffer.edit(
4152 ranges.iter().map(|range| (range.clone(), text)),
4153 this.autoindent_mode.clone(),
4154 cx,
4155 );
4156 });
4157 }
4158 for (buffer, edits) in linked_edits {
4159 buffer.update(cx, |buffer, cx| {
4160 let snapshot = buffer.snapshot();
4161 let edits = edits
4162 .into_iter()
4163 .map(|(range, text)| {
4164 use text::ToPoint as TP;
4165 let end_point = TP::to_point(&range.end, &snapshot);
4166 let start_point = TP::to_point(&range.start, &snapshot);
4167 (start_point..end_point, text)
4168 })
4169 .sorted_by_key(|(range, _)| range.start)
4170 .collect::<Vec<_>>();
4171 buffer.edit(edits, None, cx);
4172 })
4173 }
4174
4175 this.refresh_inline_completion(true, false, window, cx);
4176 });
4177
4178 let show_new_completions_on_confirm = completion
4179 .confirm
4180 .as_ref()
4181 .map_or(false, |confirm| confirm(intent, window, cx));
4182 if show_new_completions_on_confirm {
4183 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
4184 }
4185
4186 let provider = self.completion_provider.as_ref()?;
4187 drop(completion);
4188 let apply_edits = provider.apply_additional_edits_for_completion(
4189 buffer_handle,
4190 completions_menu.completions.clone(),
4191 candidate_id,
4192 true,
4193 cx,
4194 );
4195
4196 let editor_settings = EditorSettings::get_global(cx);
4197 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4198 // After the code completion is finished, users often want to know what signatures are needed.
4199 // so we should automatically call signature_help
4200 self.show_signature_help(&ShowSignatureHelp, window, cx);
4201 }
4202
4203 Some(cx.foreground_executor().spawn(async move {
4204 apply_edits.await?;
4205 Ok(())
4206 }))
4207 }
4208
4209 pub fn toggle_code_actions(
4210 &mut self,
4211 action: &ToggleCodeActions,
4212 window: &mut Window,
4213 cx: &mut Context<Self>,
4214 ) {
4215 let mut context_menu = self.context_menu.borrow_mut();
4216 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4217 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4218 // Toggle if we're selecting the same one
4219 *context_menu = None;
4220 cx.notify();
4221 return;
4222 } else {
4223 // Otherwise, clear it and start a new one
4224 *context_menu = None;
4225 cx.notify();
4226 }
4227 }
4228 drop(context_menu);
4229 let snapshot = self.snapshot(window, cx);
4230 let deployed_from_indicator = action.deployed_from_indicator;
4231 let mut task = self.code_actions_task.take();
4232 let action = action.clone();
4233 cx.spawn_in(window, |editor, mut cx| async move {
4234 while let Some(prev_task) = task {
4235 prev_task.await.log_err();
4236 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4237 }
4238
4239 let spawned_test_task = editor.update_in(&mut cx, |editor, window, cx| {
4240 if editor.focus_handle.is_focused(window) {
4241 let multibuffer_point = action
4242 .deployed_from_indicator
4243 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4244 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4245 let (buffer, buffer_row) = snapshot
4246 .buffer_snapshot
4247 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4248 .and_then(|(buffer_snapshot, range)| {
4249 editor
4250 .buffer
4251 .read(cx)
4252 .buffer(buffer_snapshot.remote_id())
4253 .map(|buffer| (buffer, range.start.row))
4254 })?;
4255 let (_, code_actions) = editor
4256 .available_code_actions
4257 .clone()
4258 .and_then(|(location, code_actions)| {
4259 let snapshot = location.buffer.read(cx).snapshot();
4260 let point_range = location.range.to_point(&snapshot);
4261 let point_range = point_range.start.row..=point_range.end.row;
4262 if point_range.contains(&buffer_row) {
4263 Some((location, code_actions))
4264 } else {
4265 None
4266 }
4267 })
4268 .unzip();
4269 let buffer_id = buffer.read(cx).remote_id();
4270 let tasks = editor
4271 .tasks
4272 .get(&(buffer_id, buffer_row))
4273 .map(|t| Arc::new(t.to_owned()));
4274 if tasks.is_none() && code_actions.is_none() {
4275 return None;
4276 }
4277
4278 editor.completion_tasks.clear();
4279 editor.discard_inline_completion(false, cx);
4280 let task_context =
4281 tasks
4282 .as_ref()
4283 .zip(editor.project.clone())
4284 .map(|(tasks, project)| {
4285 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4286 });
4287
4288 Some(cx.spawn_in(window, |editor, mut cx| async move {
4289 let task_context = match task_context {
4290 Some(task_context) => task_context.await,
4291 None => None,
4292 };
4293 let resolved_tasks =
4294 tasks.zip(task_context).map(|(tasks, task_context)| {
4295 Rc::new(ResolvedTasks {
4296 templates: tasks.resolve(&task_context).collect(),
4297 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4298 multibuffer_point.row,
4299 tasks.column,
4300 )),
4301 })
4302 });
4303 let spawn_straight_away = resolved_tasks
4304 .as_ref()
4305 .map_or(false, |tasks| tasks.templates.len() == 1)
4306 && code_actions
4307 .as_ref()
4308 .map_or(true, |actions| actions.is_empty());
4309 if let Ok(task) = editor.update_in(&mut cx, |editor, window, cx| {
4310 *editor.context_menu.borrow_mut() =
4311 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4312 buffer,
4313 actions: CodeActionContents {
4314 tasks: resolved_tasks,
4315 actions: code_actions,
4316 },
4317 selected_item: Default::default(),
4318 scroll_handle: UniformListScrollHandle::default(),
4319 deployed_from_indicator,
4320 }));
4321 if spawn_straight_away {
4322 if let Some(task) = editor.confirm_code_action(
4323 &ConfirmCodeAction { item_ix: Some(0) },
4324 window,
4325 cx,
4326 ) {
4327 cx.notify();
4328 return task;
4329 }
4330 }
4331 cx.notify();
4332 Task::ready(Ok(()))
4333 }) {
4334 task.await
4335 } else {
4336 Ok(())
4337 }
4338 }))
4339 } else {
4340 Some(Task::ready(Ok(())))
4341 }
4342 })?;
4343 if let Some(task) = spawned_test_task {
4344 task.await?;
4345 }
4346
4347 Ok::<_, anyhow::Error>(())
4348 })
4349 .detach_and_log_err(cx);
4350 }
4351
4352 pub fn confirm_code_action(
4353 &mut self,
4354 action: &ConfirmCodeAction,
4355 window: &mut Window,
4356 cx: &mut Context<Self>,
4357 ) -> Option<Task<Result<()>>> {
4358 let actions_menu =
4359 if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
4360 menu
4361 } else {
4362 return None;
4363 };
4364 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4365 let action = actions_menu.actions.get(action_ix)?;
4366 let title = action.label();
4367 let buffer = actions_menu.buffer;
4368 let workspace = self.workspace()?;
4369
4370 match action {
4371 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4372 workspace.update(cx, |workspace, cx| {
4373 workspace::tasks::schedule_resolved_task(
4374 workspace,
4375 task_source_kind,
4376 resolved_task,
4377 false,
4378 cx,
4379 );
4380
4381 Some(Task::ready(Ok(())))
4382 })
4383 }
4384 CodeActionsItem::CodeAction {
4385 excerpt_id,
4386 action,
4387 provider,
4388 } => {
4389 let apply_code_action =
4390 provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
4391 let workspace = workspace.downgrade();
4392 Some(cx.spawn_in(window, |editor, cx| async move {
4393 let project_transaction = apply_code_action.await?;
4394 Self::open_project_transaction(
4395 &editor,
4396 workspace,
4397 project_transaction,
4398 title,
4399 cx,
4400 )
4401 .await
4402 }))
4403 }
4404 }
4405 }
4406
4407 pub async fn open_project_transaction(
4408 this: &WeakEntity<Editor>,
4409 workspace: WeakEntity<Workspace>,
4410 transaction: ProjectTransaction,
4411 title: String,
4412 mut cx: AsyncWindowContext,
4413 ) -> Result<()> {
4414 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4415 cx.update(|_, cx| {
4416 entries.sort_unstable_by_key(|(buffer, _)| {
4417 buffer.read(cx).file().map(|f| f.path().clone())
4418 });
4419 })?;
4420
4421 // If the project transaction's edits are all contained within this editor, then
4422 // avoid opening a new editor to display them.
4423
4424 if let Some((buffer, transaction)) = entries.first() {
4425 if entries.len() == 1 {
4426 let excerpt = this.update(&mut cx, |editor, cx| {
4427 editor
4428 .buffer()
4429 .read(cx)
4430 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4431 })?;
4432 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4433 if excerpted_buffer == *buffer {
4434 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4435 let excerpt_range = excerpt_range.to_offset(buffer);
4436 buffer
4437 .edited_ranges_for_transaction::<usize>(transaction)
4438 .all(|range| {
4439 excerpt_range.start <= range.start
4440 && excerpt_range.end >= range.end
4441 })
4442 })?;
4443
4444 if all_edits_within_excerpt {
4445 return Ok(());
4446 }
4447 }
4448 }
4449 }
4450 } else {
4451 return Ok(());
4452 }
4453
4454 let mut ranges_to_highlight = Vec::new();
4455 let excerpt_buffer = cx.new(|cx| {
4456 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4457 for (buffer_handle, transaction) in &entries {
4458 let buffer = buffer_handle.read(cx);
4459 ranges_to_highlight.extend(
4460 multibuffer.push_excerpts_with_context_lines(
4461 buffer_handle.clone(),
4462 buffer
4463 .edited_ranges_for_transaction::<usize>(transaction)
4464 .collect(),
4465 DEFAULT_MULTIBUFFER_CONTEXT,
4466 cx,
4467 ),
4468 );
4469 }
4470 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4471 multibuffer
4472 })?;
4473
4474 workspace.update_in(&mut cx, |workspace, window, cx| {
4475 let project = workspace.project().clone();
4476 let editor = cx
4477 .new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, window, cx));
4478 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
4479 editor.update(cx, |editor, cx| {
4480 editor.highlight_background::<Self>(
4481 &ranges_to_highlight,
4482 |theme| theme.editor_highlighted_line_background,
4483 cx,
4484 );
4485 });
4486 })?;
4487
4488 Ok(())
4489 }
4490
4491 pub fn clear_code_action_providers(&mut self) {
4492 self.code_action_providers.clear();
4493 self.available_code_actions.take();
4494 }
4495
4496 pub fn add_code_action_provider(
4497 &mut self,
4498 provider: Rc<dyn CodeActionProvider>,
4499 window: &mut Window,
4500 cx: &mut Context<Self>,
4501 ) {
4502 if self
4503 .code_action_providers
4504 .iter()
4505 .any(|existing_provider| existing_provider.id() == provider.id())
4506 {
4507 return;
4508 }
4509
4510 self.code_action_providers.push(provider);
4511 self.refresh_code_actions(window, cx);
4512 }
4513
4514 pub fn remove_code_action_provider(
4515 &mut self,
4516 id: Arc<str>,
4517 window: &mut Window,
4518 cx: &mut Context<Self>,
4519 ) {
4520 self.code_action_providers
4521 .retain(|provider| provider.id() != id);
4522 self.refresh_code_actions(window, cx);
4523 }
4524
4525 fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
4526 let buffer = self.buffer.read(cx);
4527 let newest_selection = self.selections.newest_anchor().clone();
4528 if newest_selection.head().diff_base_anchor.is_some() {
4529 return None;
4530 }
4531 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4532 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4533 if start_buffer != end_buffer {
4534 return None;
4535 }
4536
4537 self.code_actions_task = Some(cx.spawn_in(window, |this, mut cx| async move {
4538 cx.background_executor()
4539 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4540 .await;
4541
4542 let (providers, tasks) = this.update_in(&mut cx, |this, window, cx| {
4543 let providers = this.code_action_providers.clone();
4544 let tasks = this
4545 .code_action_providers
4546 .iter()
4547 .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
4548 .collect::<Vec<_>>();
4549 (providers, tasks)
4550 })?;
4551
4552 let mut actions = Vec::new();
4553 for (provider, provider_actions) in
4554 providers.into_iter().zip(future::join_all(tasks).await)
4555 {
4556 if let Some(provider_actions) = provider_actions.log_err() {
4557 actions.extend(provider_actions.into_iter().map(|action| {
4558 AvailableCodeAction {
4559 excerpt_id: newest_selection.start.excerpt_id,
4560 action,
4561 provider: provider.clone(),
4562 }
4563 }));
4564 }
4565 }
4566
4567 this.update(&mut cx, |this, cx| {
4568 this.available_code_actions = if actions.is_empty() {
4569 None
4570 } else {
4571 Some((
4572 Location {
4573 buffer: start_buffer,
4574 range: start..end,
4575 },
4576 actions.into(),
4577 ))
4578 };
4579 cx.notify();
4580 })
4581 }));
4582 None
4583 }
4584
4585 fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
4586 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4587 self.show_git_blame_inline = false;
4588
4589 self.show_git_blame_inline_delay_task =
4590 Some(cx.spawn_in(window, |this, mut cx| async move {
4591 cx.background_executor().timer(delay).await;
4592
4593 this.update(&mut cx, |this, cx| {
4594 this.show_git_blame_inline = true;
4595 cx.notify();
4596 })
4597 .log_err();
4598 }));
4599 }
4600 }
4601
4602 fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
4603 if self.pending_rename.is_some() {
4604 return None;
4605 }
4606
4607 let provider = self.semantics_provider.clone()?;
4608 let buffer = self.buffer.read(cx);
4609 let newest_selection = self.selections.newest_anchor().clone();
4610 let cursor_position = newest_selection.head();
4611 let (cursor_buffer, cursor_buffer_position) =
4612 buffer.text_anchor_for_position(cursor_position, cx)?;
4613 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4614 if cursor_buffer != tail_buffer {
4615 return None;
4616 }
4617 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
4618 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4619 cx.background_executor()
4620 .timer(Duration::from_millis(debounce))
4621 .await;
4622
4623 let highlights = if let Some(highlights) = cx
4624 .update(|cx| {
4625 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4626 })
4627 .ok()
4628 .flatten()
4629 {
4630 highlights.await.log_err()
4631 } else {
4632 None
4633 };
4634
4635 if let Some(highlights) = highlights {
4636 this.update(&mut cx, |this, cx| {
4637 if this.pending_rename.is_some() {
4638 return;
4639 }
4640
4641 let buffer_id = cursor_position.buffer_id;
4642 let buffer = this.buffer.read(cx);
4643 if !buffer
4644 .text_anchor_for_position(cursor_position, cx)
4645 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4646 {
4647 return;
4648 }
4649
4650 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4651 let mut write_ranges = Vec::new();
4652 let mut read_ranges = Vec::new();
4653 for highlight in highlights {
4654 for (excerpt_id, excerpt_range) in
4655 buffer.excerpts_for_buffer(cursor_buffer.read(cx).remote_id(), cx)
4656 {
4657 let start = highlight
4658 .range
4659 .start
4660 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4661 let end = highlight
4662 .range
4663 .end
4664 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4665 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4666 continue;
4667 }
4668
4669 let range = Anchor {
4670 buffer_id,
4671 excerpt_id,
4672 text_anchor: start,
4673 diff_base_anchor: None,
4674 }..Anchor {
4675 buffer_id,
4676 excerpt_id,
4677 text_anchor: end,
4678 diff_base_anchor: None,
4679 };
4680 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4681 write_ranges.push(range);
4682 } else {
4683 read_ranges.push(range);
4684 }
4685 }
4686 }
4687
4688 this.highlight_background::<DocumentHighlightRead>(
4689 &read_ranges,
4690 |theme| theme.editor_document_highlight_read_background,
4691 cx,
4692 );
4693 this.highlight_background::<DocumentHighlightWrite>(
4694 &write_ranges,
4695 |theme| theme.editor_document_highlight_write_background,
4696 cx,
4697 );
4698 cx.notify();
4699 })
4700 .log_err();
4701 }
4702 }));
4703 None
4704 }
4705
4706 pub fn refresh_inline_completion(
4707 &mut self,
4708 debounce: bool,
4709 user_requested: bool,
4710 window: &mut Window,
4711 cx: &mut Context<Self>,
4712 ) -> Option<()> {
4713 let provider = self.inline_completion_provider()?;
4714 let cursor = self.selections.newest_anchor().head();
4715 let (buffer, cursor_buffer_position) =
4716 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4717
4718 if !user_requested
4719 && (!self.enable_inline_completions
4720 || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
4721 || !self.is_focused(window)
4722 || buffer.read(cx).is_empty())
4723 {
4724 self.discard_inline_completion(false, cx);
4725 return None;
4726 }
4727
4728 self.update_visible_inline_completion(window, cx);
4729 provider.refresh(buffer, cursor_buffer_position, debounce, cx);
4730 Some(())
4731 }
4732
4733 fn cycle_inline_completion(
4734 &mut self,
4735 direction: Direction,
4736 window: &mut Window,
4737 cx: &mut Context<Self>,
4738 ) -> Option<()> {
4739 let provider = self.inline_completion_provider()?;
4740 let cursor = self.selections.newest_anchor().head();
4741 let (buffer, cursor_buffer_position) =
4742 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4743 if !self.enable_inline_completions
4744 || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
4745 {
4746 return None;
4747 }
4748
4749 provider.cycle(buffer, cursor_buffer_position, direction, cx);
4750 self.update_visible_inline_completion(window, cx);
4751
4752 Some(())
4753 }
4754
4755 pub fn show_inline_completion(
4756 &mut self,
4757 _: &ShowInlineCompletion,
4758 window: &mut Window,
4759 cx: &mut Context<Self>,
4760 ) {
4761 if !self.inline_completions_enabled(cx) {
4762 return;
4763 }
4764
4765 if !self.has_active_inline_completion() {
4766 self.refresh_inline_completion(false, true, window, cx);
4767 return;
4768 }
4769
4770 self.update_visible_inline_completion(window, cx);
4771 }
4772
4773 pub fn display_cursor_names(
4774 &mut self,
4775 _: &DisplayCursorNames,
4776 window: &mut Window,
4777 cx: &mut Context<Self>,
4778 ) {
4779 self.show_cursor_names(window, cx);
4780 }
4781
4782 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
4783 self.show_cursor_names = true;
4784 cx.notify();
4785 cx.spawn_in(window, |this, mut cx| async move {
4786 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
4787 this.update(&mut cx, |this, cx| {
4788 this.show_cursor_names = false;
4789 cx.notify()
4790 })
4791 .ok()
4792 })
4793 .detach();
4794 }
4795
4796 pub fn next_inline_completion(
4797 &mut self,
4798 _: &NextInlineCompletion,
4799 window: &mut Window,
4800 cx: &mut Context<Self>,
4801 ) {
4802 if self.has_active_inline_completion() {
4803 self.cycle_inline_completion(Direction::Next, window, cx);
4804 } else {
4805 let is_copilot_disabled = self
4806 .refresh_inline_completion(false, true, window, cx)
4807 .is_none();
4808 if is_copilot_disabled {
4809 cx.propagate();
4810 }
4811 }
4812 }
4813
4814 pub fn previous_inline_completion(
4815 &mut self,
4816 _: &PreviousInlineCompletion,
4817 window: &mut Window,
4818 cx: &mut Context<Self>,
4819 ) {
4820 if self.has_active_inline_completion() {
4821 self.cycle_inline_completion(Direction::Prev, window, cx);
4822 } else {
4823 let is_copilot_disabled = self
4824 .refresh_inline_completion(false, true, window, cx)
4825 .is_none();
4826 if is_copilot_disabled {
4827 cx.propagate();
4828 }
4829 }
4830 }
4831
4832 pub fn accept_inline_completion(
4833 &mut self,
4834 _: &AcceptInlineCompletion,
4835 window: &mut Window,
4836 cx: &mut Context<Self>,
4837 ) {
4838 let buffer = self.buffer.read(cx);
4839 let snapshot = buffer.snapshot(cx);
4840 let selection = self.selections.newest_adjusted(cx);
4841 let cursor = selection.head();
4842 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
4843 let suggested_indents = snapshot.suggested_indents([cursor.row], cx);
4844 if let Some(suggested_indent) = suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
4845 {
4846 if cursor.column < suggested_indent.len
4847 && cursor.column <= current_indent.len
4848 && current_indent.len <= suggested_indent.len
4849 {
4850 self.tab(&Default::default(), window, cx);
4851 return;
4852 }
4853 }
4854
4855 if self.show_inline_completions_in_menu(cx) {
4856 self.hide_context_menu(window, cx);
4857 }
4858
4859 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
4860 return;
4861 };
4862
4863 self.report_inline_completion_event(true, cx);
4864
4865 match &active_inline_completion.completion {
4866 InlineCompletion::Move(position) => {
4867 let position = *position;
4868 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
4869 selections.select_anchor_ranges([position..position]);
4870 });
4871 }
4872 InlineCompletion::Edit { edits, .. } => {
4873 if let Some(provider) = self.inline_completion_provider() {
4874 provider.accept(cx);
4875 }
4876
4877 let snapshot = self.buffer.read(cx).snapshot(cx);
4878 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
4879
4880 self.buffer.update(cx, |buffer, cx| {
4881 buffer.edit(edits.iter().cloned(), None, cx)
4882 });
4883
4884 self.change_selections(None, window, cx, |s| {
4885 s.select_anchor_ranges([last_edit_end..last_edit_end])
4886 });
4887
4888 self.update_visible_inline_completion(window, cx);
4889 if self.active_inline_completion.is_none() {
4890 self.refresh_inline_completion(true, true, window, cx);
4891 }
4892
4893 cx.notify();
4894 }
4895 }
4896 }
4897
4898 pub fn accept_partial_inline_completion(
4899 &mut self,
4900 _: &AcceptPartialInlineCompletion,
4901 window: &mut Window,
4902 cx: &mut Context<Self>,
4903 ) {
4904 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
4905 return;
4906 };
4907 if self.selections.count() != 1 {
4908 return;
4909 }
4910
4911 self.report_inline_completion_event(true, cx);
4912
4913 match &active_inline_completion.completion {
4914 InlineCompletion::Move(position) => {
4915 let position = *position;
4916 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
4917 selections.select_anchor_ranges([position..position]);
4918 });
4919 }
4920 InlineCompletion::Edit { edits, .. } => {
4921 // Find an insertion that starts at the cursor position.
4922 let snapshot = self.buffer.read(cx).snapshot(cx);
4923 let cursor_offset = self.selections.newest::<usize>(cx).head();
4924 let insertion = edits.iter().find_map(|(range, text)| {
4925 let range = range.to_offset(&snapshot);
4926 if range.is_empty() && range.start == cursor_offset {
4927 Some(text)
4928 } else {
4929 None
4930 }
4931 });
4932
4933 if let Some(text) = insertion {
4934 let mut partial_completion = text
4935 .chars()
4936 .by_ref()
4937 .take_while(|c| c.is_alphabetic())
4938 .collect::<String>();
4939 if partial_completion.is_empty() {
4940 partial_completion = text
4941 .chars()
4942 .by_ref()
4943 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
4944 .collect::<String>();
4945 }
4946
4947 cx.emit(EditorEvent::InputHandled {
4948 utf16_range_to_replace: None,
4949 text: partial_completion.clone().into(),
4950 });
4951
4952 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
4953
4954 self.refresh_inline_completion(true, true, window, cx);
4955 cx.notify();
4956 } else {
4957 self.accept_inline_completion(&Default::default(), window, cx);
4958 }
4959 }
4960 }
4961 }
4962
4963 fn discard_inline_completion(
4964 &mut self,
4965 should_report_inline_completion_event: bool,
4966 cx: &mut Context<Self>,
4967 ) -> bool {
4968 if should_report_inline_completion_event {
4969 self.report_inline_completion_event(false, cx);
4970 }
4971
4972 if let Some(provider) = self.inline_completion_provider() {
4973 provider.discard(cx);
4974 }
4975
4976 self.take_active_inline_completion(cx).is_some()
4977 }
4978
4979 fn report_inline_completion_event(&self, accepted: bool, cx: &App) {
4980 let Some(provider) = self.inline_completion_provider() else {
4981 return;
4982 };
4983
4984 let Some((_, buffer, _)) = self
4985 .buffer
4986 .read(cx)
4987 .excerpt_containing(self.selections.newest_anchor().head(), cx)
4988 else {
4989 return;
4990 };
4991
4992 let extension = buffer
4993 .read(cx)
4994 .file()
4995 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
4996
4997 let event_type = match accepted {
4998 true => "Inline Completion Accepted",
4999 false => "Inline Completion Discarded",
5000 };
5001 telemetry::event!(
5002 event_type,
5003 provider = provider.name(),
5004 suggestion_accepted = accepted,
5005 file_extension = extension,
5006 );
5007 }
5008
5009 pub fn has_active_inline_completion(&self) -> bool {
5010 self.active_inline_completion.is_some()
5011 }
5012
5013 fn take_active_inline_completion(
5014 &mut self,
5015 cx: &mut Context<Self>,
5016 ) -> Option<InlineCompletion> {
5017 let active_inline_completion = self.active_inline_completion.take()?;
5018 self.splice_inlays(active_inline_completion.inlay_ids, Default::default(), cx);
5019 self.clear_highlights::<InlineCompletionHighlight>(cx);
5020 Some(active_inline_completion.completion)
5021 }
5022
5023 fn update_visible_inline_completion(
5024 &mut self,
5025 window: &mut Window,
5026 cx: &mut Context<Self>,
5027 ) -> Option<()> {
5028 let selection = self.selections.newest_anchor();
5029 let cursor = selection.head();
5030 let multibuffer = self.buffer.read(cx).snapshot(cx);
5031 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
5032 let excerpt_id = cursor.excerpt_id;
5033
5034 let completions_menu_has_precedence = !self.show_inline_completions_in_menu(cx)
5035 && (self.context_menu.borrow().is_some()
5036 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
5037 if completions_menu_has_precedence
5038 || !offset_selection.is_empty()
5039 || !self.enable_inline_completions
5040 || self
5041 .active_inline_completion
5042 .as_ref()
5043 .map_or(false, |completion| {
5044 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
5045 let invalidation_range = invalidation_range.start..=invalidation_range.end;
5046 !invalidation_range.contains(&offset_selection.head())
5047 })
5048 {
5049 self.discard_inline_completion(false, cx);
5050 return None;
5051 }
5052
5053 self.take_active_inline_completion(cx);
5054 let provider = self.inline_completion_provider()?;
5055
5056 let (buffer, cursor_buffer_position) =
5057 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5058
5059 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
5060 let edits = inline_completion
5061 .edits
5062 .into_iter()
5063 .flat_map(|(range, new_text)| {
5064 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
5065 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
5066 Some((start..end, new_text))
5067 })
5068 .collect::<Vec<_>>();
5069 if edits.is_empty() {
5070 return None;
5071 }
5072
5073 let first_edit_start = edits.first().unwrap().0.start;
5074 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
5075 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
5076
5077 let last_edit_end = edits.last().unwrap().0.end;
5078 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
5079 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
5080
5081 let cursor_row = cursor.to_point(&multibuffer).row;
5082
5083 let mut inlay_ids = Vec::new();
5084 let invalidation_row_range;
5085 let completion = if cursor_row < edit_start_row {
5086 invalidation_row_range = cursor_row..edit_end_row;
5087 InlineCompletion::Move(first_edit_start)
5088 } else if cursor_row > edit_end_row {
5089 invalidation_row_range = edit_start_row..cursor_row;
5090 InlineCompletion::Move(first_edit_start)
5091 } else {
5092 if edits
5093 .iter()
5094 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
5095 {
5096 let mut inlays = Vec::new();
5097 for (range, new_text) in &edits {
5098 let inlay = Inlay::inline_completion(
5099 post_inc(&mut self.next_inlay_id),
5100 range.start,
5101 new_text.as_str(),
5102 );
5103 inlay_ids.push(inlay.id);
5104 inlays.push(inlay);
5105 }
5106
5107 self.splice_inlays(vec![], inlays, cx);
5108 } else {
5109 let background_color = cx.theme().status().deleted_background;
5110 self.highlight_text::<InlineCompletionHighlight>(
5111 edits.iter().map(|(range, _)| range.clone()).collect(),
5112 HighlightStyle {
5113 background_color: Some(background_color),
5114 ..Default::default()
5115 },
5116 cx,
5117 );
5118 }
5119
5120 invalidation_row_range = edit_start_row..edit_end_row;
5121
5122 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
5123 if provider.show_tab_accept_marker()
5124 && first_edit_start_point.row == last_edit_end_point.row
5125 && !edits.iter().any(|(_, edit)| edit.contains('\n'))
5126 {
5127 EditDisplayMode::TabAccept
5128 } else {
5129 EditDisplayMode::Inline
5130 }
5131 } else {
5132 EditDisplayMode::DiffPopover
5133 };
5134
5135 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
5136
5137 InlineCompletion::Edit {
5138 edits,
5139 edit_preview: inline_completion.edit_preview,
5140 display_mode,
5141 snapshot,
5142 }
5143 };
5144
5145 let invalidation_range = multibuffer
5146 .anchor_before(Point::new(invalidation_row_range.start, 0))
5147 ..multibuffer.anchor_after(Point::new(
5148 invalidation_row_range.end,
5149 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
5150 ));
5151
5152 self.active_inline_completion = Some(InlineCompletionState {
5153 inlay_ids,
5154 completion,
5155 invalidation_range,
5156 });
5157
5158 if self.show_inline_completions_in_menu(cx) && self.has_active_completions_menu() {
5159 if let Some(hint) = self.inline_completion_menu_hint(window, cx) {
5160 match self.context_menu.borrow_mut().as_mut() {
5161 Some(CodeContextMenu::Completions(menu)) => {
5162 menu.show_inline_completion_hint(hint);
5163 }
5164 _ => {}
5165 }
5166 }
5167 }
5168
5169 cx.notify();
5170
5171 Some(())
5172 }
5173
5174 fn inline_completion_menu_hint(
5175 &self,
5176 window: &mut Window,
5177 cx: &mut Context<Self>,
5178 ) -> Option<InlineCompletionMenuHint> {
5179 let provider = self.inline_completion_provider()?;
5180 if self.has_active_inline_completion() {
5181 let editor_snapshot = self.snapshot(window, cx);
5182
5183 let text = match &self.active_inline_completion.as_ref()?.completion {
5184 InlineCompletion::Edit {
5185 edits,
5186 edit_preview,
5187 display_mode: _,
5188 snapshot,
5189 } => edit_preview
5190 .as_ref()
5191 .and_then(|edit_preview| {
5192 inline_completion_edit_text(&snapshot, &edits, edit_preview, true, cx)
5193 })
5194 .map(InlineCompletionText::Edit),
5195 InlineCompletion::Move(target) => {
5196 let target_point =
5197 target.to_point(&editor_snapshot.display_snapshot.buffer_snapshot);
5198 let target_line = target_point.row + 1;
5199 Some(InlineCompletionText::Move(
5200 format!("Jump to edit in line {}", target_line).into(),
5201 ))
5202 }
5203 };
5204
5205 Some(InlineCompletionMenuHint::Loaded { text: text? })
5206 } else if provider.is_refreshing(cx) {
5207 Some(InlineCompletionMenuHint::Loading)
5208 } else if provider.needs_terms_acceptance(cx) {
5209 Some(InlineCompletionMenuHint::PendingTermsAcceptance)
5210 } else {
5211 Some(InlineCompletionMenuHint::None)
5212 }
5213 }
5214
5215 pub fn inline_completion_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5216 Some(self.inline_completion_provider.as_ref()?.provider.clone())
5217 }
5218
5219 fn show_inline_completions_in_menu(&self, cx: &App) -> bool {
5220 let by_provider = matches!(
5221 self.menu_inline_completions_policy,
5222 MenuInlineCompletionsPolicy::ByProvider
5223 );
5224
5225 by_provider
5226 && EditorSettings::get_global(cx).show_inline_completions_in_menu
5227 && self
5228 .inline_completion_provider()
5229 .map_or(false, |provider| provider.show_completions_in_menu())
5230 }
5231
5232 fn render_code_actions_indicator(
5233 &self,
5234 _style: &EditorStyle,
5235 row: DisplayRow,
5236 is_active: bool,
5237 cx: &mut Context<Self>,
5238 ) -> Option<IconButton> {
5239 if self.available_code_actions.is_some() {
5240 Some(
5241 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5242 .shape(ui::IconButtonShape::Square)
5243 .icon_size(IconSize::XSmall)
5244 .icon_color(Color::Muted)
5245 .toggle_state(is_active)
5246 .tooltip({
5247 let focus_handle = self.focus_handle.clone();
5248 move |window, cx| {
5249 Tooltip::for_action_in(
5250 "Toggle Code Actions",
5251 &ToggleCodeActions {
5252 deployed_from_indicator: None,
5253 },
5254 &focus_handle,
5255 window,
5256 cx,
5257 )
5258 }
5259 })
5260 .on_click(cx.listener(move |editor, _e, window, cx| {
5261 window.focus(&editor.focus_handle(cx));
5262 editor.toggle_code_actions(
5263 &ToggleCodeActions {
5264 deployed_from_indicator: Some(row),
5265 },
5266 window,
5267 cx,
5268 );
5269 })),
5270 )
5271 } else {
5272 None
5273 }
5274 }
5275
5276 fn clear_tasks(&mut self) {
5277 self.tasks.clear()
5278 }
5279
5280 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5281 if self.tasks.insert(key, value).is_some() {
5282 // This case should hopefully be rare, but just in case...
5283 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5284 }
5285 }
5286
5287 fn build_tasks_context(
5288 project: &Entity<Project>,
5289 buffer: &Entity<Buffer>,
5290 buffer_row: u32,
5291 tasks: &Arc<RunnableTasks>,
5292 cx: &mut Context<Self>,
5293 ) -> Task<Option<task::TaskContext>> {
5294 let position = Point::new(buffer_row, tasks.column);
5295 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
5296 let location = Location {
5297 buffer: buffer.clone(),
5298 range: range_start..range_start,
5299 };
5300 // Fill in the environmental variables from the tree-sitter captures
5301 let mut captured_task_variables = TaskVariables::default();
5302 for (capture_name, value) in tasks.extra_variables.clone() {
5303 captured_task_variables.insert(
5304 task::VariableName::Custom(capture_name.into()),
5305 value.clone(),
5306 );
5307 }
5308 project.update(cx, |project, cx| {
5309 project.task_store().update(cx, |task_store, cx| {
5310 task_store.task_context_for_location(captured_task_variables, location, cx)
5311 })
5312 })
5313 }
5314
5315 pub fn spawn_nearest_task(
5316 &mut self,
5317 action: &SpawnNearestTask,
5318 window: &mut Window,
5319 cx: &mut Context<Self>,
5320 ) {
5321 let Some((workspace, _)) = self.workspace.clone() else {
5322 return;
5323 };
5324 let Some(project) = self.project.clone() else {
5325 return;
5326 };
5327
5328 // Try to find a closest, enclosing node using tree-sitter that has a
5329 // task
5330 let Some((buffer, buffer_row, tasks)) = self
5331 .find_enclosing_node_task(cx)
5332 // Or find the task that's closest in row-distance.
5333 .or_else(|| self.find_closest_task(cx))
5334 else {
5335 return;
5336 };
5337
5338 let reveal_strategy = action.reveal;
5339 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
5340 cx.spawn_in(window, |_, mut cx| async move {
5341 let context = task_context.await?;
5342 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
5343
5344 let resolved = resolved_task.resolved.as_mut()?;
5345 resolved.reveal = reveal_strategy;
5346
5347 workspace
5348 .update(&mut cx, |workspace, cx| {
5349 workspace::tasks::schedule_resolved_task(
5350 workspace,
5351 task_source_kind,
5352 resolved_task,
5353 false,
5354 cx,
5355 );
5356 })
5357 .ok()
5358 })
5359 .detach();
5360 }
5361
5362 fn find_closest_task(
5363 &mut self,
5364 cx: &mut Context<Self>,
5365 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5366 let cursor_row = self.selections.newest_adjusted(cx).head().row;
5367
5368 let ((buffer_id, row), tasks) = self
5369 .tasks
5370 .iter()
5371 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
5372
5373 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
5374 let tasks = Arc::new(tasks.to_owned());
5375 Some((buffer, *row, tasks))
5376 }
5377
5378 fn find_enclosing_node_task(
5379 &mut self,
5380 cx: &mut Context<Self>,
5381 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5382 let snapshot = self.buffer.read(cx).snapshot(cx);
5383 let offset = self.selections.newest::<usize>(cx).head();
5384 let excerpt = snapshot.excerpt_containing(offset..offset)?;
5385 let buffer_id = excerpt.buffer().remote_id();
5386
5387 let layer = excerpt.buffer().syntax_layer_at(offset)?;
5388 let mut cursor = layer.node().walk();
5389
5390 while cursor.goto_first_child_for_byte(offset).is_some() {
5391 if cursor.node().end_byte() == offset {
5392 cursor.goto_next_sibling();
5393 }
5394 }
5395
5396 // Ascend to the smallest ancestor that contains the range and has a task.
5397 loop {
5398 let node = cursor.node();
5399 let node_range = node.byte_range();
5400 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
5401
5402 // Check if this node contains our offset
5403 if node_range.start <= offset && node_range.end >= offset {
5404 // If it contains offset, check for task
5405 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
5406 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
5407 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
5408 }
5409 }
5410
5411 if !cursor.goto_parent() {
5412 break;
5413 }
5414 }
5415 None
5416 }
5417
5418 fn render_run_indicator(
5419 &self,
5420 _style: &EditorStyle,
5421 is_active: bool,
5422 row: DisplayRow,
5423 cx: &mut Context<Self>,
5424 ) -> IconButton {
5425 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5426 .shape(ui::IconButtonShape::Square)
5427 .icon_size(IconSize::XSmall)
5428 .icon_color(Color::Muted)
5429 .toggle_state(is_active)
5430 .on_click(cx.listener(move |editor, _e, window, cx| {
5431 window.focus(&editor.focus_handle(cx));
5432 editor.toggle_code_actions(
5433 &ToggleCodeActions {
5434 deployed_from_indicator: Some(row),
5435 },
5436 window,
5437 cx,
5438 );
5439 }))
5440 }
5441
5442 #[cfg(any(test, feature = "test-support"))]
5443 pub fn context_menu_visible(&self) -> bool {
5444 self.context_menu
5445 .borrow()
5446 .as_ref()
5447 .map_or(false, |menu| menu.visible())
5448 }
5449
5450 #[cfg(feature = "test-support")]
5451 pub fn context_menu_contains_inline_completion(&self) -> bool {
5452 self.context_menu
5453 .borrow()
5454 .as_ref()
5455 .map_or(false, |menu| match menu {
5456 CodeContextMenu::Completions(menu) => {
5457 menu.entries.borrow().first().map_or(false, |entry| {
5458 matches!(entry, CompletionEntry::InlineCompletionHint(_))
5459 })
5460 }
5461 CodeContextMenu::CodeActions(_) => false,
5462 })
5463 }
5464
5465 fn context_menu_origin(&self, cursor_position: DisplayPoint) -> Option<ContextMenuOrigin> {
5466 self.context_menu
5467 .borrow()
5468 .as_ref()
5469 .map(|menu| menu.origin(cursor_position))
5470 }
5471
5472 fn render_context_menu(
5473 &self,
5474 style: &EditorStyle,
5475 max_height_in_lines: u32,
5476 y_flipped: bool,
5477 window: &mut Window,
5478 cx: &mut Context<Editor>,
5479 ) -> Option<AnyElement> {
5480 self.context_menu.borrow().as_ref().and_then(|menu| {
5481 if menu.visible() {
5482 Some(menu.render(style, max_height_in_lines, y_flipped, window, cx))
5483 } else {
5484 None
5485 }
5486 })
5487 }
5488
5489 fn render_context_menu_aside(
5490 &self,
5491 style: &EditorStyle,
5492 max_size: Size<Pixels>,
5493 cx: &mut Context<Editor>,
5494 ) -> Option<AnyElement> {
5495 self.context_menu.borrow().as_ref().and_then(|menu| {
5496 if menu.visible() {
5497 menu.render_aside(
5498 style,
5499 max_size,
5500 self.workspace.as_ref().map(|(w, _)| w.clone()),
5501 cx,
5502 )
5503 } else {
5504 None
5505 }
5506 })
5507 }
5508
5509 fn hide_context_menu(
5510 &mut self,
5511 window: &mut Window,
5512 cx: &mut Context<Self>,
5513 ) -> Option<CodeContextMenu> {
5514 cx.notify();
5515 self.completion_tasks.clear();
5516 let context_menu = self.context_menu.borrow_mut().take();
5517 if context_menu.is_some() && !self.show_inline_completions_in_menu(cx) {
5518 self.update_visible_inline_completion(window, cx);
5519 }
5520 context_menu
5521 }
5522
5523 fn show_snippet_choices(
5524 &mut self,
5525 choices: &Vec<String>,
5526 selection: Range<Anchor>,
5527 cx: &mut Context<Self>,
5528 ) {
5529 if selection.start.buffer_id.is_none() {
5530 return;
5531 }
5532 let buffer_id = selection.start.buffer_id.unwrap();
5533 let buffer = self.buffer().read(cx).buffer(buffer_id);
5534 let id = post_inc(&mut self.next_completion_id);
5535
5536 if let Some(buffer) = buffer {
5537 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
5538 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
5539 ));
5540 }
5541 }
5542
5543 pub fn insert_snippet(
5544 &mut self,
5545 insertion_ranges: &[Range<usize>],
5546 snippet: Snippet,
5547 window: &mut Window,
5548 cx: &mut Context<Self>,
5549 ) -> Result<()> {
5550 struct Tabstop<T> {
5551 is_end_tabstop: bool,
5552 ranges: Vec<Range<T>>,
5553 choices: Option<Vec<String>>,
5554 }
5555
5556 let tabstops = self.buffer.update(cx, |buffer, cx| {
5557 let snippet_text: Arc<str> = snippet.text.clone().into();
5558 buffer.edit(
5559 insertion_ranges
5560 .iter()
5561 .cloned()
5562 .map(|range| (range, snippet_text.clone())),
5563 Some(AutoindentMode::EachLine),
5564 cx,
5565 );
5566
5567 let snapshot = &*buffer.read(cx);
5568 let snippet = &snippet;
5569 snippet
5570 .tabstops
5571 .iter()
5572 .map(|tabstop| {
5573 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
5574 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
5575 });
5576 let mut tabstop_ranges = tabstop
5577 .ranges
5578 .iter()
5579 .flat_map(|tabstop_range| {
5580 let mut delta = 0_isize;
5581 insertion_ranges.iter().map(move |insertion_range| {
5582 let insertion_start = insertion_range.start as isize + delta;
5583 delta +=
5584 snippet.text.len() as isize - insertion_range.len() as isize;
5585
5586 let start = ((insertion_start + tabstop_range.start) as usize)
5587 .min(snapshot.len());
5588 let end = ((insertion_start + tabstop_range.end) as usize)
5589 .min(snapshot.len());
5590 snapshot.anchor_before(start)..snapshot.anchor_after(end)
5591 })
5592 })
5593 .collect::<Vec<_>>();
5594 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
5595
5596 Tabstop {
5597 is_end_tabstop,
5598 ranges: tabstop_ranges,
5599 choices: tabstop.choices.clone(),
5600 }
5601 })
5602 .collect::<Vec<_>>()
5603 });
5604 if let Some(tabstop) = tabstops.first() {
5605 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
5606 s.select_ranges(tabstop.ranges.iter().cloned());
5607 });
5608
5609 if let Some(choices) = &tabstop.choices {
5610 if let Some(selection) = tabstop.ranges.first() {
5611 self.show_snippet_choices(choices, selection.clone(), cx)
5612 }
5613 }
5614
5615 // If we're already at the last tabstop and it's at the end of the snippet,
5616 // we're done, we don't need to keep the state around.
5617 if !tabstop.is_end_tabstop {
5618 let choices = tabstops
5619 .iter()
5620 .map(|tabstop| tabstop.choices.clone())
5621 .collect();
5622
5623 let ranges = tabstops
5624 .into_iter()
5625 .map(|tabstop| tabstop.ranges)
5626 .collect::<Vec<_>>();
5627
5628 self.snippet_stack.push(SnippetState {
5629 active_index: 0,
5630 ranges,
5631 choices,
5632 });
5633 }
5634
5635 // Check whether the just-entered snippet ends with an auto-closable bracket.
5636 if self.autoclose_regions.is_empty() {
5637 let snapshot = self.buffer.read(cx).snapshot(cx);
5638 for selection in &mut self.selections.all::<Point>(cx) {
5639 let selection_head = selection.head();
5640 let Some(scope) = snapshot.language_scope_at(selection_head) else {
5641 continue;
5642 };
5643
5644 let mut bracket_pair = None;
5645 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
5646 let prev_chars = snapshot
5647 .reversed_chars_at(selection_head)
5648 .collect::<String>();
5649 for (pair, enabled) in scope.brackets() {
5650 if enabled
5651 && pair.close
5652 && prev_chars.starts_with(pair.start.as_str())
5653 && next_chars.starts_with(pair.end.as_str())
5654 {
5655 bracket_pair = Some(pair.clone());
5656 break;
5657 }
5658 }
5659 if let Some(pair) = bracket_pair {
5660 let start = snapshot.anchor_after(selection_head);
5661 let end = snapshot.anchor_after(selection_head);
5662 self.autoclose_regions.push(AutocloseRegion {
5663 selection_id: selection.id,
5664 range: start..end,
5665 pair,
5666 });
5667 }
5668 }
5669 }
5670 }
5671 Ok(())
5672 }
5673
5674 pub fn move_to_next_snippet_tabstop(
5675 &mut self,
5676 window: &mut Window,
5677 cx: &mut Context<Self>,
5678 ) -> bool {
5679 self.move_to_snippet_tabstop(Bias::Right, window, cx)
5680 }
5681
5682 pub fn move_to_prev_snippet_tabstop(
5683 &mut self,
5684 window: &mut Window,
5685 cx: &mut Context<Self>,
5686 ) -> bool {
5687 self.move_to_snippet_tabstop(Bias::Left, window, cx)
5688 }
5689
5690 pub fn move_to_snippet_tabstop(
5691 &mut self,
5692 bias: Bias,
5693 window: &mut Window,
5694 cx: &mut Context<Self>,
5695 ) -> bool {
5696 if let Some(mut snippet) = self.snippet_stack.pop() {
5697 match bias {
5698 Bias::Left => {
5699 if snippet.active_index > 0 {
5700 snippet.active_index -= 1;
5701 } else {
5702 self.snippet_stack.push(snippet);
5703 return false;
5704 }
5705 }
5706 Bias::Right => {
5707 if snippet.active_index + 1 < snippet.ranges.len() {
5708 snippet.active_index += 1;
5709 } else {
5710 self.snippet_stack.push(snippet);
5711 return false;
5712 }
5713 }
5714 }
5715 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
5716 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
5717 s.select_anchor_ranges(current_ranges.iter().cloned())
5718 });
5719
5720 if let Some(choices) = &snippet.choices[snippet.active_index] {
5721 if let Some(selection) = current_ranges.first() {
5722 self.show_snippet_choices(&choices, selection.clone(), cx);
5723 }
5724 }
5725
5726 // If snippet state is not at the last tabstop, push it back on the stack
5727 if snippet.active_index + 1 < snippet.ranges.len() {
5728 self.snippet_stack.push(snippet);
5729 }
5730 return true;
5731 }
5732 }
5733
5734 false
5735 }
5736
5737 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
5738 self.transact(window, cx, |this, window, cx| {
5739 this.select_all(&SelectAll, window, cx);
5740 this.insert("", window, cx);
5741 });
5742 }
5743
5744 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
5745 self.transact(window, cx, |this, window, cx| {
5746 this.select_autoclose_pair(window, cx);
5747 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
5748 if !this.linked_edit_ranges.is_empty() {
5749 let selections = this.selections.all::<MultiBufferPoint>(cx);
5750 let snapshot = this.buffer.read(cx).snapshot(cx);
5751
5752 for selection in selections.iter() {
5753 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
5754 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
5755 if selection_start.buffer_id != selection_end.buffer_id {
5756 continue;
5757 }
5758 if let Some(ranges) =
5759 this.linked_editing_ranges_for(selection_start..selection_end, cx)
5760 {
5761 for (buffer, entries) in ranges {
5762 linked_ranges.entry(buffer).or_default().extend(entries);
5763 }
5764 }
5765 }
5766 }
5767
5768 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
5769 if !this.selections.line_mode {
5770 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
5771 for selection in &mut selections {
5772 if selection.is_empty() {
5773 let old_head = selection.head();
5774 let mut new_head =
5775 movement::left(&display_map, old_head.to_display_point(&display_map))
5776 .to_point(&display_map);
5777 if let Some((buffer, line_buffer_range)) = display_map
5778 .buffer_snapshot
5779 .buffer_line_for_row(MultiBufferRow(old_head.row))
5780 {
5781 let indent_size =
5782 buffer.indent_size_for_line(line_buffer_range.start.row);
5783 let indent_len = match indent_size.kind {
5784 IndentKind::Space => {
5785 buffer.settings_at(line_buffer_range.start, cx).tab_size
5786 }
5787 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
5788 };
5789 if old_head.column <= indent_size.len && old_head.column > 0 {
5790 let indent_len = indent_len.get();
5791 new_head = cmp::min(
5792 new_head,
5793 MultiBufferPoint::new(
5794 old_head.row,
5795 ((old_head.column - 1) / indent_len) * indent_len,
5796 ),
5797 );
5798 }
5799 }
5800
5801 selection.set_head(new_head, SelectionGoal::None);
5802 }
5803 }
5804 }
5805
5806 this.signature_help_state.set_backspace_pressed(true);
5807 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
5808 s.select(selections)
5809 });
5810 this.insert("", window, cx);
5811 let empty_str: Arc<str> = Arc::from("");
5812 for (buffer, edits) in linked_ranges {
5813 let snapshot = buffer.read(cx).snapshot();
5814 use text::ToPoint as TP;
5815
5816 let edits = edits
5817 .into_iter()
5818 .map(|range| {
5819 let end_point = TP::to_point(&range.end, &snapshot);
5820 let mut start_point = TP::to_point(&range.start, &snapshot);
5821
5822 if end_point == start_point {
5823 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
5824 .saturating_sub(1);
5825 start_point =
5826 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
5827 };
5828
5829 (start_point..end_point, empty_str.clone())
5830 })
5831 .sorted_by_key(|(range, _)| range.start)
5832 .collect::<Vec<_>>();
5833 buffer.update(cx, |this, cx| {
5834 this.edit(edits, None, cx);
5835 })
5836 }
5837 this.refresh_inline_completion(true, false, window, cx);
5838 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
5839 });
5840 }
5841
5842 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
5843 self.transact(window, cx, |this, window, cx| {
5844 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
5845 let line_mode = s.line_mode;
5846 s.move_with(|map, selection| {
5847 if selection.is_empty() && !line_mode {
5848 let cursor = movement::right(map, selection.head());
5849 selection.end = cursor;
5850 selection.reversed = true;
5851 selection.goal = SelectionGoal::None;
5852 }
5853 })
5854 });
5855 this.insert("", window, cx);
5856 this.refresh_inline_completion(true, false, window, cx);
5857 });
5858 }
5859
5860 pub fn tab_prev(&mut self, _: &TabPrev, window: &mut Window, cx: &mut Context<Self>) {
5861 if self.move_to_prev_snippet_tabstop(window, cx) {
5862 return;
5863 }
5864
5865 self.outdent(&Outdent, window, cx);
5866 }
5867
5868 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
5869 if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) {
5870 return;
5871 }
5872
5873 let mut selections = self.selections.all_adjusted(cx);
5874 let buffer = self.buffer.read(cx);
5875 let snapshot = buffer.snapshot(cx);
5876 let rows_iter = selections.iter().map(|s| s.head().row);
5877 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
5878
5879 let mut edits = Vec::new();
5880 let mut prev_edited_row = 0;
5881 let mut row_delta = 0;
5882 for selection in &mut selections {
5883 if selection.start.row != prev_edited_row {
5884 row_delta = 0;
5885 }
5886 prev_edited_row = selection.end.row;
5887
5888 // If the selection is non-empty, then increase the indentation of the selected lines.
5889 if !selection.is_empty() {
5890 row_delta =
5891 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5892 continue;
5893 }
5894
5895 // If the selection is empty and the cursor is in the leading whitespace before the
5896 // suggested indentation, then auto-indent the line.
5897 let cursor = selection.head();
5898 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
5899 if let Some(suggested_indent) =
5900 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
5901 {
5902 if cursor.column < suggested_indent.len
5903 && cursor.column <= current_indent.len
5904 && current_indent.len <= suggested_indent.len
5905 {
5906 selection.start = Point::new(cursor.row, suggested_indent.len);
5907 selection.end = selection.start;
5908 if row_delta == 0 {
5909 edits.extend(Buffer::edit_for_indent_size_adjustment(
5910 cursor.row,
5911 current_indent,
5912 suggested_indent,
5913 ));
5914 row_delta = suggested_indent.len - current_indent.len;
5915 }
5916 continue;
5917 }
5918 }
5919
5920 // Otherwise, insert a hard or soft tab.
5921 let settings = buffer.settings_at(cursor, cx);
5922 let tab_size = if settings.hard_tabs {
5923 IndentSize::tab()
5924 } else {
5925 let tab_size = settings.tab_size.get();
5926 let char_column = snapshot
5927 .text_for_range(Point::new(cursor.row, 0)..cursor)
5928 .flat_map(str::chars)
5929 .count()
5930 + row_delta as usize;
5931 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
5932 IndentSize::spaces(chars_to_next_tab_stop)
5933 };
5934 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
5935 selection.end = selection.start;
5936 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
5937 row_delta += tab_size.len;
5938 }
5939
5940 self.transact(window, cx, |this, window, cx| {
5941 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5942 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
5943 s.select(selections)
5944 });
5945 this.refresh_inline_completion(true, false, window, cx);
5946 });
5947 }
5948
5949 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
5950 if self.read_only(cx) {
5951 return;
5952 }
5953 let mut selections = self.selections.all::<Point>(cx);
5954 let mut prev_edited_row = 0;
5955 let mut row_delta = 0;
5956 let mut edits = Vec::new();
5957 let buffer = self.buffer.read(cx);
5958 let snapshot = buffer.snapshot(cx);
5959 for selection in &mut selections {
5960 if selection.start.row != prev_edited_row {
5961 row_delta = 0;
5962 }
5963 prev_edited_row = selection.end.row;
5964
5965 row_delta =
5966 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5967 }
5968
5969 self.transact(window, cx, |this, window, cx| {
5970 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5971 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
5972 s.select(selections)
5973 });
5974 });
5975 }
5976
5977 fn indent_selection(
5978 buffer: &MultiBuffer,
5979 snapshot: &MultiBufferSnapshot,
5980 selection: &mut Selection<Point>,
5981 edits: &mut Vec<(Range<Point>, String)>,
5982 delta_for_start_row: u32,
5983 cx: &App,
5984 ) -> u32 {
5985 let settings = buffer.settings_at(selection.start, cx);
5986 let tab_size = settings.tab_size.get();
5987 let indent_kind = if settings.hard_tabs {
5988 IndentKind::Tab
5989 } else {
5990 IndentKind::Space
5991 };
5992 let mut start_row = selection.start.row;
5993 let mut end_row = selection.end.row + 1;
5994
5995 // If a selection ends at the beginning of a line, don't indent
5996 // that last line.
5997 if selection.end.column == 0 && selection.end.row > selection.start.row {
5998 end_row -= 1;
5999 }
6000
6001 // Avoid re-indenting a row that has already been indented by a
6002 // previous selection, but still update this selection's column
6003 // to reflect that indentation.
6004 if delta_for_start_row > 0 {
6005 start_row += 1;
6006 selection.start.column += delta_for_start_row;
6007 if selection.end.row == selection.start.row {
6008 selection.end.column += delta_for_start_row;
6009 }
6010 }
6011
6012 let mut delta_for_end_row = 0;
6013 let has_multiple_rows = start_row + 1 != end_row;
6014 for row in start_row..end_row {
6015 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
6016 let indent_delta = match (current_indent.kind, indent_kind) {
6017 (IndentKind::Space, IndentKind::Space) => {
6018 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
6019 IndentSize::spaces(columns_to_next_tab_stop)
6020 }
6021 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
6022 (_, IndentKind::Tab) => IndentSize::tab(),
6023 };
6024
6025 let start = if has_multiple_rows || current_indent.len < selection.start.column {
6026 0
6027 } else {
6028 selection.start.column
6029 };
6030 let row_start = Point::new(row, start);
6031 edits.push((
6032 row_start..row_start,
6033 indent_delta.chars().collect::<String>(),
6034 ));
6035
6036 // Update this selection's endpoints to reflect the indentation.
6037 if row == selection.start.row {
6038 selection.start.column += indent_delta.len;
6039 }
6040 if row == selection.end.row {
6041 selection.end.column += indent_delta.len;
6042 delta_for_end_row = indent_delta.len;
6043 }
6044 }
6045
6046 if selection.start.row == selection.end.row {
6047 delta_for_start_row + delta_for_end_row
6048 } else {
6049 delta_for_end_row
6050 }
6051 }
6052
6053 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
6054 if self.read_only(cx) {
6055 return;
6056 }
6057 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6058 let selections = self.selections.all::<Point>(cx);
6059 let mut deletion_ranges = Vec::new();
6060 let mut last_outdent = None;
6061 {
6062 let buffer = self.buffer.read(cx);
6063 let snapshot = buffer.snapshot(cx);
6064 for selection in &selections {
6065 let settings = buffer.settings_at(selection.start, cx);
6066 let tab_size = settings.tab_size.get();
6067 let mut rows = selection.spanned_rows(false, &display_map);
6068
6069 // Avoid re-outdenting a row that has already been outdented by a
6070 // previous selection.
6071 if let Some(last_row) = last_outdent {
6072 if last_row == rows.start {
6073 rows.start = rows.start.next_row();
6074 }
6075 }
6076 let has_multiple_rows = rows.len() > 1;
6077 for row in rows.iter_rows() {
6078 let indent_size = snapshot.indent_size_for_line(row);
6079 if indent_size.len > 0 {
6080 let deletion_len = match indent_size.kind {
6081 IndentKind::Space => {
6082 let columns_to_prev_tab_stop = indent_size.len % tab_size;
6083 if columns_to_prev_tab_stop == 0 {
6084 tab_size
6085 } else {
6086 columns_to_prev_tab_stop
6087 }
6088 }
6089 IndentKind::Tab => 1,
6090 };
6091 let start = if has_multiple_rows
6092 || deletion_len > selection.start.column
6093 || indent_size.len < selection.start.column
6094 {
6095 0
6096 } else {
6097 selection.start.column - deletion_len
6098 };
6099 deletion_ranges.push(
6100 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
6101 );
6102 last_outdent = Some(row);
6103 }
6104 }
6105 }
6106 }
6107
6108 self.transact(window, cx, |this, window, cx| {
6109 this.buffer.update(cx, |buffer, cx| {
6110 let empty_str: Arc<str> = Arc::default();
6111 buffer.edit(
6112 deletion_ranges
6113 .into_iter()
6114 .map(|range| (range, empty_str.clone())),
6115 None,
6116 cx,
6117 );
6118 });
6119 let selections = this.selections.all::<usize>(cx);
6120 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6121 s.select(selections)
6122 });
6123 });
6124 }
6125
6126 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
6127 if self.read_only(cx) {
6128 return;
6129 }
6130 let selections = self
6131 .selections
6132 .all::<usize>(cx)
6133 .into_iter()
6134 .map(|s| s.range());
6135
6136 self.transact(window, cx, |this, window, cx| {
6137 this.buffer.update(cx, |buffer, cx| {
6138 buffer.autoindent_ranges(selections, cx);
6139 });
6140 let selections = this.selections.all::<usize>(cx);
6141 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6142 s.select(selections)
6143 });
6144 });
6145 }
6146
6147 pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
6148 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6149 let selections = self.selections.all::<Point>(cx);
6150
6151 let mut new_cursors = Vec::new();
6152 let mut edit_ranges = Vec::new();
6153 let mut selections = selections.iter().peekable();
6154 while let Some(selection) = selections.next() {
6155 let mut rows = selection.spanned_rows(false, &display_map);
6156 let goal_display_column = selection.head().to_display_point(&display_map).column();
6157
6158 // Accumulate contiguous regions of rows that we want to delete.
6159 while let Some(next_selection) = selections.peek() {
6160 let next_rows = next_selection.spanned_rows(false, &display_map);
6161 if next_rows.start <= rows.end {
6162 rows.end = next_rows.end;
6163 selections.next().unwrap();
6164 } else {
6165 break;
6166 }
6167 }
6168
6169 let buffer = &display_map.buffer_snapshot;
6170 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
6171 let edit_end;
6172 let cursor_buffer_row;
6173 if buffer.max_point().row >= rows.end.0 {
6174 // If there's a line after the range, delete the \n from the end of the row range
6175 // and position the cursor on the next line.
6176 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
6177 cursor_buffer_row = rows.end;
6178 } else {
6179 // If there isn't a line after the range, delete the \n from the line before the
6180 // start of the row range and position the cursor there.
6181 edit_start = edit_start.saturating_sub(1);
6182 edit_end = buffer.len();
6183 cursor_buffer_row = rows.start.previous_row();
6184 }
6185
6186 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
6187 *cursor.column_mut() =
6188 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
6189
6190 new_cursors.push((
6191 selection.id,
6192 buffer.anchor_after(cursor.to_point(&display_map)),
6193 ));
6194 edit_ranges.push(edit_start..edit_end);
6195 }
6196
6197 self.transact(window, cx, |this, window, cx| {
6198 let buffer = this.buffer.update(cx, |buffer, cx| {
6199 let empty_str: Arc<str> = Arc::default();
6200 buffer.edit(
6201 edit_ranges
6202 .into_iter()
6203 .map(|range| (range, empty_str.clone())),
6204 None,
6205 cx,
6206 );
6207 buffer.snapshot(cx)
6208 });
6209 let new_selections = new_cursors
6210 .into_iter()
6211 .map(|(id, cursor)| {
6212 let cursor = cursor.to_point(&buffer);
6213 Selection {
6214 id,
6215 start: cursor,
6216 end: cursor,
6217 reversed: false,
6218 goal: SelectionGoal::None,
6219 }
6220 })
6221 .collect();
6222
6223 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6224 s.select(new_selections);
6225 });
6226 });
6227 }
6228
6229 pub fn join_lines_impl(
6230 &mut self,
6231 insert_whitespace: bool,
6232 window: &mut Window,
6233 cx: &mut Context<Self>,
6234 ) {
6235 if self.read_only(cx) {
6236 return;
6237 }
6238 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
6239 for selection in self.selections.all::<Point>(cx) {
6240 let start = MultiBufferRow(selection.start.row);
6241 // Treat single line selections as if they include the next line. Otherwise this action
6242 // would do nothing for single line selections individual cursors.
6243 let end = if selection.start.row == selection.end.row {
6244 MultiBufferRow(selection.start.row + 1)
6245 } else {
6246 MultiBufferRow(selection.end.row)
6247 };
6248
6249 if let Some(last_row_range) = row_ranges.last_mut() {
6250 if start <= last_row_range.end {
6251 last_row_range.end = end;
6252 continue;
6253 }
6254 }
6255 row_ranges.push(start..end);
6256 }
6257
6258 let snapshot = self.buffer.read(cx).snapshot(cx);
6259 let mut cursor_positions = Vec::new();
6260 for row_range in &row_ranges {
6261 let anchor = snapshot.anchor_before(Point::new(
6262 row_range.end.previous_row().0,
6263 snapshot.line_len(row_range.end.previous_row()),
6264 ));
6265 cursor_positions.push(anchor..anchor);
6266 }
6267
6268 self.transact(window, cx, |this, window, cx| {
6269 for row_range in row_ranges.into_iter().rev() {
6270 for row in row_range.iter_rows().rev() {
6271 let end_of_line = Point::new(row.0, snapshot.line_len(row));
6272 let next_line_row = row.next_row();
6273 let indent = snapshot.indent_size_for_line(next_line_row);
6274 let start_of_next_line = Point::new(next_line_row.0, indent.len);
6275
6276 let replace =
6277 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
6278 " "
6279 } else {
6280 ""
6281 };
6282
6283 this.buffer.update(cx, |buffer, cx| {
6284 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
6285 });
6286 }
6287 }
6288
6289 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6290 s.select_anchor_ranges(cursor_positions)
6291 });
6292 });
6293 }
6294
6295 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
6296 self.join_lines_impl(true, window, cx);
6297 }
6298
6299 pub fn sort_lines_case_sensitive(
6300 &mut self,
6301 _: &SortLinesCaseSensitive,
6302 window: &mut Window,
6303 cx: &mut Context<Self>,
6304 ) {
6305 self.manipulate_lines(window, cx, |lines| lines.sort())
6306 }
6307
6308 pub fn sort_lines_case_insensitive(
6309 &mut self,
6310 _: &SortLinesCaseInsensitive,
6311 window: &mut Window,
6312 cx: &mut Context<Self>,
6313 ) {
6314 self.manipulate_lines(window, cx, |lines| {
6315 lines.sort_by_key(|line| line.to_lowercase())
6316 })
6317 }
6318
6319 pub fn unique_lines_case_insensitive(
6320 &mut self,
6321 _: &UniqueLinesCaseInsensitive,
6322 window: &mut Window,
6323 cx: &mut Context<Self>,
6324 ) {
6325 self.manipulate_lines(window, cx, |lines| {
6326 let mut seen = HashSet::default();
6327 lines.retain(|line| seen.insert(line.to_lowercase()));
6328 })
6329 }
6330
6331 pub fn unique_lines_case_sensitive(
6332 &mut self,
6333 _: &UniqueLinesCaseSensitive,
6334 window: &mut Window,
6335 cx: &mut Context<Self>,
6336 ) {
6337 self.manipulate_lines(window, cx, |lines| {
6338 let mut seen = HashSet::default();
6339 lines.retain(|line| seen.insert(*line));
6340 })
6341 }
6342
6343 pub fn revert_file(&mut self, _: &RevertFile, window: &mut Window, cx: &mut Context<Self>) {
6344 let mut revert_changes = HashMap::default();
6345 let snapshot = self.snapshot(window, cx);
6346 for hunk in snapshot
6347 .hunks_for_ranges(Some(Point::zero()..snapshot.buffer_snapshot.max_point()).into_iter())
6348 {
6349 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
6350 }
6351 if !revert_changes.is_empty() {
6352 self.transact(window, cx, |editor, window, cx| {
6353 editor.revert(revert_changes, window, cx);
6354 });
6355 }
6356 }
6357
6358 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
6359 let Some(project) = self.project.clone() else {
6360 return;
6361 };
6362 self.reload(project, window, cx)
6363 .detach_and_notify_err(window, cx);
6364 }
6365
6366 pub fn revert_selected_hunks(
6367 &mut self,
6368 _: &RevertSelectedHunks,
6369 window: &mut Window,
6370 cx: &mut Context<Self>,
6371 ) {
6372 let selections = self.selections.all(cx).into_iter().map(|s| s.range());
6373 self.revert_hunks_in_ranges(selections, window, cx);
6374 }
6375
6376 fn revert_hunks_in_ranges(
6377 &mut self,
6378 ranges: impl Iterator<Item = Range<Point>>,
6379 window: &mut Window,
6380 cx: &mut Context<Editor>,
6381 ) {
6382 let mut revert_changes = HashMap::default();
6383 let snapshot = self.snapshot(window, cx);
6384 for hunk in &snapshot.hunks_for_ranges(ranges) {
6385 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
6386 }
6387 if !revert_changes.is_empty() {
6388 self.transact(window, cx, |editor, window, cx| {
6389 editor.revert(revert_changes, window, cx);
6390 });
6391 }
6392 }
6393
6394 pub fn open_active_item_in_terminal(
6395 &mut self,
6396 _: &OpenInTerminal,
6397 window: &mut Window,
6398 cx: &mut Context<Self>,
6399 ) {
6400 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
6401 let project_path = buffer.read(cx).project_path(cx)?;
6402 let project = self.project.as_ref()?.read(cx);
6403 let entry = project.entry_for_path(&project_path, cx)?;
6404 let parent = match &entry.canonical_path {
6405 Some(canonical_path) => canonical_path.to_path_buf(),
6406 None => project.absolute_path(&project_path, cx)?,
6407 }
6408 .parent()?
6409 .to_path_buf();
6410 Some(parent)
6411 }) {
6412 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
6413 }
6414 }
6415
6416 pub fn prepare_revert_change(
6417 &self,
6418 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
6419 hunk: &MultiBufferDiffHunk,
6420 cx: &mut App,
6421 ) -> Option<()> {
6422 let buffer = self.buffer.read(cx);
6423 let change_set = buffer.change_set_for(hunk.buffer_id)?;
6424 let buffer = buffer.buffer(hunk.buffer_id)?;
6425 let buffer = buffer.read(cx);
6426 let original_text = change_set
6427 .read(cx)
6428 .base_text
6429 .as_ref()?
6430 .as_rope()
6431 .slice(hunk.diff_base_byte_range.clone());
6432 let buffer_snapshot = buffer.snapshot();
6433 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
6434 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
6435 probe
6436 .0
6437 .start
6438 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
6439 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
6440 }) {
6441 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
6442 Some(())
6443 } else {
6444 None
6445 }
6446 }
6447
6448 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
6449 self.manipulate_lines(window, cx, |lines| lines.reverse())
6450 }
6451
6452 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
6453 self.manipulate_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
6454 }
6455
6456 fn manipulate_lines<Fn>(
6457 &mut self,
6458 window: &mut Window,
6459 cx: &mut Context<Self>,
6460 mut callback: Fn,
6461 ) where
6462 Fn: FnMut(&mut Vec<&str>),
6463 {
6464 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6465 let buffer = self.buffer.read(cx).snapshot(cx);
6466
6467 let mut edits = Vec::new();
6468
6469 let selections = self.selections.all::<Point>(cx);
6470 let mut selections = selections.iter().peekable();
6471 let mut contiguous_row_selections = Vec::new();
6472 let mut new_selections = Vec::new();
6473 let mut added_lines = 0;
6474 let mut removed_lines = 0;
6475
6476 while let Some(selection) = selections.next() {
6477 let (start_row, end_row) = consume_contiguous_rows(
6478 &mut contiguous_row_selections,
6479 selection,
6480 &display_map,
6481 &mut selections,
6482 );
6483
6484 let start_point = Point::new(start_row.0, 0);
6485 let end_point = Point::new(
6486 end_row.previous_row().0,
6487 buffer.line_len(end_row.previous_row()),
6488 );
6489 let text = buffer
6490 .text_for_range(start_point..end_point)
6491 .collect::<String>();
6492
6493 let mut lines = text.split('\n').collect_vec();
6494
6495 let lines_before = lines.len();
6496 callback(&mut lines);
6497 let lines_after = lines.len();
6498
6499 edits.push((start_point..end_point, lines.join("\n")));
6500
6501 // Selections must change based on added and removed line count
6502 let start_row =
6503 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
6504 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
6505 new_selections.push(Selection {
6506 id: selection.id,
6507 start: start_row,
6508 end: end_row,
6509 goal: SelectionGoal::None,
6510 reversed: selection.reversed,
6511 });
6512
6513 if lines_after > lines_before {
6514 added_lines += lines_after - lines_before;
6515 } else if lines_before > lines_after {
6516 removed_lines += lines_before - lines_after;
6517 }
6518 }
6519
6520 self.transact(window, cx, |this, window, cx| {
6521 let buffer = this.buffer.update(cx, |buffer, cx| {
6522 buffer.edit(edits, None, cx);
6523 buffer.snapshot(cx)
6524 });
6525
6526 // Recalculate offsets on newly edited buffer
6527 let new_selections = new_selections
6528 .iter()
6529 .map(|s| {
6530 let start_point = Point::new(s.start.0, 0);
6531 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
6532 Selection {
6533 id: s.id,
6534 start: buffer.point_to_offset(start_point),
6535 end: buffer.point_to_offset(end_point),
6536 goal: s.goal,
6537 reversed: s.reversed,
6538 }
6539 })
6540 .collect();
6541
6542 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6543 s.select(new_selections);
6544 });
6545
6546 this.request_autoscroll(Autoscroll::fit(), cx);
6547 });
6548 }
6549
6550 pub fn convert_to_upper_case(
6551 &mut self,
6552 _: &ConvertToUpperCase,
6553 window: &mut Window,
6554 cx: &mut Context<Self>,
6555 ) {
6556 self.manipulate_text(window, cx, |text| text.to_uppercase())
6557 }
6558
6559 pub fn convert_to_lower_case(
6560 &mut self,
6561 _: &ConvertToLowerCase,
6562 window: &mut Window,
6563 cx: &mut Context<Self>,
6564 ) {
6565 self.manipulate_text(window, cx, |text| text.to_lowercase())
6566 }
6567
6568 pub fn convert_to_title_case(
6569 &mut self,
6570 _: &ConvertToTitleCase,
6571 window: &mut Window,
6572 cx: &mut Context<Self>,
6573 ) {
6574 self.manipulate_text(window, cx, |text| {
6575 text.split('\n')
6576 .map(|line| line.to_case(Case::Title))
6577 .join("\n")
6578 })
6579 }
6580
6581 pub fn convert_to_snake_case(
6582 &mut self,
6583 _: &ConvertToSnakeCase,
6584 window: &mut Window,
6585 cx: &mut Context<Self>,
6586 ) {
6587 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
6588 }
6589
6590 pub fn convert_to_kebab_case(
6591 &mut self,
6592 _: &ConvertToKebabCase,
6593 window: &mut Window,
6594 cx: &mut Context<Self>,
6595 ) {
6596 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
6597 }
6598
6599 pub fn convert_to_upper_camel_case(
6600 &mut self,
6601 _: &ConvertToUpperCamelCase,
6602 window: &mut Window,
6603 cx: &mut Context<Self>,
6604 ) {
6605 self.manipulate_text(window, cx, |text| {
6606 text.split('\n')
6607 .map(|line| line.to_case(Case::UpperCamel))
6608 .join("\n")
6609 })
6610 }
6611
6612 pub fn convert_to_lower_camel_case(
6613 &mut self,
6614 _: &ConvertToLowerCamelCase,
6615 window: &mut Window,
6616 cx: &mut Context<Self>,
6617 ) {
6618 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
6619 }
6620
6621 pub fn convert_to_opposite_case(
6622 &mut self,
6623 _: &ConvertToOppositeCase,
6624 window: &mut Window,
6625 cx: &mut Context<Self>,
6626 ) {
6627 self.manipulate_text(window, cx, |text| {
6628 text.chars()
6629 .fold(String::with_capacity(text.len()), |mut t, c| {
6630 if c.is_uppercase() {
6631 t.extend(c.to_lowercase());
6632 } else {
6633 t.extend(c.to_uppercase());
6634 }
6635 t
6636 })
6637 })
6638 }
6639
6640 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
6641 where
6642 Fn: FnMut(&str) -> String,
6643 {
6644 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6645 let buffer = self.buffer.read(cx).snapshot(cx);
6646
6647 let mut new_selections = Vec::new();
6648 let mut edits = Vec::new();
6649 let mut selection_adjustment = 0i32;
6650
6651 for selection in self.selections.all::<usize>(cx) {
6652 let selection_is_empty = selection.is_empty();
6653
6654 let (start, end) = if selection_is_empty {
6655 let word_range = movement::surrounding_word(
6656 &display_map,
6657 selection.start.to_display_point(&display_map),
6658 );
6659 let start = word_range.start.to_offset(&display_map, Bias::Left);
6660 let end = word_range.end.to_offset(&display_map, Bias::Left);
6661 (start, end)
6662 } else {
6663 (selection.start, selection.end)
6664 };
6665
6666 let text = buffer.text_for_range(start..end).collect::<String>();
6667 let old_length = text.len() as i32;
6668 let text = callback(&text);
6669
6670 new_selections.push(Selection {
6671 start: (start as i32 - selection_adjustment) as usize,
6672 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
6673 goal: SelectionGoal::None,
6674 ..selection
6675 });
6676
6677 selection_adjustment += old_length - text.len() as i32;
6678
6679 edits.push((start..end, text));
6680 }
6681
6682 self.transact(window, cx, |this, window, cx| {
6683 this.buffer.update(cx, |buffer, cx| {
6684 buffer.edit(edits, None, cx);
6685 });
6686
6687 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6688 s.select(new_selections);
6689 });
6690
6691 this.request_autoscroll(Autoscroll::fit(), cx);
6692 });
6693 }
6694
6695 pub fn duplicate(
6696 &mut self,
6697 upwards: bool,
6698 whole_lines: bool,
6699 window: &mut Window,
6700 cx: &mut Context<Self>,
6701 ) {
6702 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6703 let buffer = &display_map.buffer_snapshot;
6704 let selections = self.selections.all::<Point>(cx);
6705
6706 let mut edits = Vec::new();
6707 let mut selections_iter = selections.iter().peekable();
6708 while let Some(selection) = selections_iter.next() {
6709 let mut rows = selection.spanned_rows(false, &display_map);
6710 // duplicate line-wise
6711 if whole_lines || selection.start == selection.end {
6712 // Avoid duplicating the same lines twice.
6713 while let Some(next_selection) = selections_iter.peek() {
6714 let next_rows = next_selection.spanned_rows(false, &display_map);
6715 if next_rows.start < rows.end {
6716 rows.end = next_rows.end;
6717 selections_iter.next().unwrap();
6718 } else {
6719 break;
6720 }
6721 }
6722
6723 // Copy the text from the selected row region and splice it either at the start
6724 // or end of the region.
6725 let start = Point::new(rows.start.0, 0);
6726 let end = Point::new(
6727 rows.end.previous_row().0,
6728 buffer.line_len(rows.end.previous_row()),
6729 );
6730 let text = buffer
6731 .text_for_range(start..end)
6732 .chain(Some("\n"))
6733 .collect::<String>();
6734 let insert_location = if upwards {
6735 Point::new(rows.end.0, 0)
6736 } else {
6737 start
6738 };
6739 edits.push((insert_location..insert_location, text));
6740 } else {
6741 // duplicate character-wise
6742 let start = selection.start;
6743 let end = selection.end;
6744 let text = buffer.text_for_range(start..end).collect::<String>();
6745 edits.push((selection.end..selection.end, text));
6746 }
6747 }
6748
6749 self.transact(window, cx, |this, _, cx| {
6750 this.buffer.update(cx, |buffer, cx| {
6751 buffer.edit(edits, None, cx);
6752 });
6753
6754 this.request_autoscroll(Autoscroll::fit(), cx);
6755 });
6756 }
6757
6758 pub fn duplicate_line_up(
6759 &mut self,
6760 _: &DuplicateLineUp,
6761 window: &mut Window,
6762 cx: &mut Context<Self>,
6763 ) {
6764 self.duplicate(true, true, window, cx);
6765 }
6766
6767 pub fn duplicate_line_down(
6768 &mut self,
6769 _: &DuplicateLineDown,
6770 window: &mut Window,
6771 cx: &mut Context<Self>,
6772 ) {
6773 self.duplicate(false, true, window, cx);
6774 }
6775
6776 pub fn duplicate_selection(
6777 &mut self,
6778 _: &DuplicateSelection,
6779 window: &mut Window,
6780 cx: &mut Context<Self>,
6781 ) {
6782 self.duplicate(false, false, window, cx);
6783 }
6784
6785 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
6786 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6787 let buffer = self.buffer.read(cx).snapshot(cx);
6788
6789 let mut edits = Vec::new();
6790 let mut unfold_ranges = Vec::new();
6791 let mut refold_creases = Vec::new();
6792
6793 let selections = self.selections.all::<Point>(cx);
6794 let mut selections = selections.iter().peekable();
6795 let mut contiguous_row_selections = Vec::new();
6796 let mut new_selections = Vec::new();
6797
6798 while let Some(selection) = selections.next() {
6799 // Find all the selections that span a contiguous row range
6800 let (start_row, end_row) = consume_contiguous_rows(
6801 &mut contiguous_row_selections,
6802 selection,
6803 &display_map,
6804 &mut selections,
6805 );
6806
6807 // Move the text spanned by the row range to be before the line preceding the row range
6808 if start_row.0 > 0 {
6809 let range_to_move = Point::new(
6810 start_row.previous_row().0,
6811 buffer.line_len(start_row.previous_row()),
6812 )
6813 ..Point::new(
6814 end_row.previous_row().0,
6815 buffer.line_len(end_row.previous_row()),
6816 );
6817 let insertion_point = display_map
6818 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
6819 .0;
6820
6821 // Don't move lines across excerpts
6822 if buffer
6823 .excerpt_containing(insertion_point..range_to_move.end)
6824 .is_some()
6825 {
6826 let text = buffer
6827 .text_for_range(range_to_move.clone())
6828 .flat_map(|s| s.chars())
6829 .skip(1)
6830 .chain(['\n'])
6831 .collect::<String>();
6832
6833 edits.push((
6834 buffer.anchor_after(range_to_move.start)
6835 ..buffer.anchor_before(range_to_move.end),
6836 String::new(),
6837 ));
6838 let insertion_anchor = buffer.anchor_after(insertion_point);
6839 edits.push((insertion_anchor..insertion_anchor, text));
6840
6841 let row_delta = range_to_move.start.row - insertion_point.row + 1;
6842
6843 // Move selections up
6844 new_selections.extend(contiguous_row_selections.drain(..).map(
6845 |mut selection| {
6846 selection.start.row -= row_delta;
6847 selection.end.row -= row_delta;
6848 selection
6849 },
6850 ));
6851
6852 // Move folds up
6853 unfold_ranges.push(range_to_move.clone());
6854 for fold in display_map.folds_in_range(
6855 buffer.anchor_before(range_to_move.start)
6856 ..buffer.anchor_after(range_to_move.end),
6857 ) {
6858 let mut start = fold.range.start.to_point(&buffer);
6859 let mut end = fold.range.end.to_point(&buffer);
6860 start.row -= row_delta;
6861 end.row -= row_delta;
6862 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
6863 }
6864 }
6865 }
6866
6867 // If we didn't move line(s), preserve the existing selections
6868 new_selections.append(&mut contiguous_row_selections);
6869 }
6870
6871 self.transact(window, cx, |this, window, cx| {
6872 this.unfold_ranges(&unfold_ranges, true, true, cx);
6873 this.buffer.update(cx, |buffer, cx| {
6874 for (range, text) in edits {
6875 buffer.edit([(range, text)], None, cx);
6876 }
6877 });
6878 this.fold_creases(refold_creases, true, window, cx);
6879 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6880 s.select(new_selections);
6881 })
6882 });
6883 }
6884
6885 pub fn move_line_down(
6886 &mut self,
6887 _: &MoveLineDown,
6888 window: &mut Window,
6889 cx: &mut Context<Self>,
6890 ) {
6891 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6892 let buffer = self.buffer.read(cx).snapshot(cx);
6893
6894 let mut edits = Vec::new();
6895 let mut unfold_ranges = Vec::new();
6896 let mut refold_creases = Vec::new();
6897
6898 let selections = self.selections.all::<Point>(cx);
6899 let mut selections = selections.iter().peekable();
6900 let mut contiguous_row_selections = Vec::new();
6901 let mut new_selections = Vec::new();
6902
6903 while let Some(selection) = selections.next() {
6904 // Find all the selections that span a contiguous row range
6905 let (start_row, end_row) = consume_contiguous_rows(
6906 &mut contiguous_row_selections,
6907 selection,
6908 &display_map,
6909 &mut selections,
6910 );
6911
6912 // Move the text spanned by the row range to be after the last line of the row range
6913 if end_row.0 <= buffer.max_point().row {
6914 let range_to_move =
6915 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
6916 let insertion_point = display_map
6917 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
6918 .0;
6919
6920 // Don't move lines across excerpt boundaries
6921 if buffer
6922 .excerpt_containing(range_to_move.start..insertion_point)
6923 .is_some()
6924 {
6925 let mut text = String::from("\n");
6926 text.extend(buffer.text_for_range(range_to_move.clone()));
6927 text.pop(); // Drop trailing newline
6928 edits.push((
6929 buffer.anchor_after(range_to_move.start)
6930 ..buffer.anchor_before(range_to_move.end),
6931 String::new(),
6932 ));
6933 let insertion_anchor = buffer.anchor_after(insertion_point);
6934 edits.push((insertion_anchor..insertion_anchor, text));
6935
6936 let row_delta = insertion_point.row - range_to_move.end.row + 1;
6937
6938 // Move selections down
6939 new_selections.extend(contiguous_row_selections.drain(..).map(
6940 |mut selection| {
6941 selection.start.row += row_delta;
6942 selection.end.row += row_delta;
6943 selection
6944 },
6945 ));
6946
6947 // Move folds down
6948 unfold_ranges.push(range_to_move.clone());
6949 for fold in display_map.folds_in_range(
6950 buffer.anchor_before(range_to_move.start)
6951 ..buffer.anchor_after(range_to_move.end),
6952 ) {
6953 let mut start = fold.range.start.to_point(&buffer);
6954 let mut end = fold.range.end.to_point(&buffer);
6955 start.row += row_delta;
6956 end.row += row_delta;
6957 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
6958 }
6959 }
6960 }
6961
6962 // If we didn't move line(s), preserve the existing selections
6963 new_selections.append(&mut contiguous_row_selections);
6964 }
6965
6966 self.transact(window, cx, |this, window, cx| {
6967 this.unfold_ranges(&unfold_ranges, true, true, cx);
6968 this.buffer.update(cx, |buffer, cx| {
6969 for (range, text) in edits {
6970 buffer.edit([(range, text)], None, cx);
6971 }
6972 });
6973 this.fold_creases(refold_creases, true, window, cx);
6974 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6975 s.select(new_selections)
6976 });
6977 });
6978 }
6979
6980 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
6981 let text_layout_details = &self.text_layout_details(window);
6982 self.transact(window, cx, |this, window, cx| {
6983 let edits = this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6984 let mut edits: Vec<(Range<usize>, String)> = Default::default();
6985 let line_mode = s.line_mode;
6986 s.move_with(|display_map, selection| {
6987 if !selection.is_empty() || line_mode {
6988 return;
6989 }
6990
6991 let mut head = selection.head();
6992 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
6993 if head.column() == display_map.line_len(head.row()) {
6994 transpose_offset = display_map
6995 .buffer_snapshot
6996 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6997 }
6998
6999 if transpose_offset == 0 {
7000 return;
7001 }
7002
7003 *head.column_mut() += 1;
7004 head = display_map.clip_point(head, Bias::Right);
7005 let goal = SelectionGoal::HorizontalPosition(
7006 display_map
7007 .x_for_display_point(head, text_layout_details)
7008 .into(),
7009 );
7010 selection.collapse_to(head, goal);
7011
7012 let transpose_start = display_map
7013 .buffer_snapshot
7014 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
7015 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
7016 let transpose_end = display_map
7017 .buffer_snapshot
7018 .clip_offset(transpose_offset + 1, Bias::Right);
7019 if let Some(ch) =
7020 display_map.buffer_snapshot.chars_at(transpose_start).next()
7021 {
7022 edits.push((transpose_start..transpose_offset, String::new()));
7023 edits.push((transpose_end..transpose_end, ch.to_string()));
7024 }
7025 }
7026 });
7027 edits
7028 });
7029 this.buffer
7030 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
7031 let selections = this.selections.all::<usize>(cx);
7032 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7033 s.select(selections);
7034 });
7035 });
7036 }
7037
7038 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
7039 self.rewrap_impl(IsVimMode::No, cx)
7040 }
7041
7042 pub fn rewrap_impl(&mut self, is_vim_mode: IsVimMode, cx: &mut Context<Self>) {
7043 let buffer = self.buffer.read(cx).snapshot(cx);
7044 let selections = self.selections.all::<Point>(cx);
7045 let mut selections = selections.iter().peekable();
7046
7047 let mut edits = Vec::new();
7048 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
7049
7050 while let Some(selection) = selections.next() {
7051 let mut start_row = selection.start.row;
7052 let mut end_row = selection.end.row;
7053
7054 // Skip selections that overlap with a range that has already been rewrapped.
7055 let selection_range = start_row..end_row;
7056 if rewrapped_row_ranges
7057 .iter()
7058 .any(|range| range.overlaps(&selection_range))
7059 {
7060 continue;
7061 }
7062
7063 let mut should_rewrap = is_vim_mode == IsVimMode::Yes;
7064
7065 if let Some(language_scope) = buffer.language_scope_at(selection.head()) {
7066 match language_scope.language_name().as_ref() {
7067 "Markdown" | "Plain Text" => {
7068 should_rewrap = true;
7069 }
7070 _ => {}
7071 }
7072 }
7073
7074 let tab_size = buffer.settings_at(selection.head(), cx).tab_size;
7075
7076 // Since not all lines in the selection may be at the same indent
7077 // level, choose the indent size that is the most common between all
7078 // of the lines.
7079 //
7080 // If there is a tie, we use the deepest indent.
7081 let (indent_size, indent_end) = {
7082 let mut indent_size_occurrences = HashMap::default();
7083 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
7084
7085 for row in start_row..=end_row {
7086 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
7087 rows_by_indent_size.entry(indent).or_default().push(row);
7088 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
7089 }
7090
7091 let indent_size = indent_size_occurrences
7092 .into_iter()
7093 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
7094 .map(|(indent, _)| indent)
7095 .unwrap_or_default();
7096 let row = rows_by_indent_size[&indent_size][0];
7097 let indent_end = Point::new(row, indent_size.len);
7098
7099 (indent_size, indent_end)
7100 };
7101
7102 let mut line_prefix = indent_size.chars().collect::<String>();
7103
7104 if let Some(comment_prefix) =
7105 buffer
7106 .language_scope_at(selection.head())
7107 .and_then(|language| {
7108 language
7109 .line_comment_prefixes()
7110 .iter()
7111 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
7112 .cloned()
7113 })
7114 {
7115 line_prefix.push_str(&comment_prefix);
7116 should_rewrap = true;
7117 }
7118
7119 if !should_rewrap {
7120 continue;
7121 }
7122
7123 if selection.is_empty() {
7124 'expand_upwards: while start_row > 0 {
7125 let prev_row = start_row - 1;
7126 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
7127 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
7128 {
7129 start_row = prev_row;
7130 } else {
7131 break 'expand_upwards;
7132 }
7133 }
7134
7135 'expand_downwards: while end_row < buffer.max_point().row {
7136 let next_row = end_row + 1;
7137 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
7138 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
7139 {
7140 end_row = next_row;
7141 } else {
7142 break 'expand_downwards;
7143 }
7144 }
7145 }
7146
7147 let start = Point::new(start_row, 0);
7148 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
7149 let selection_text = buffer.text_for_range(start..end).collect::<String>();
7150 let Some(lines_without_prefixes) = selection_text
7151 .lines()
7152 .map(|line| {
7153 line.strip_prefix(&line_prefix)
7154 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
7155 .ok_or_else(|| {
7156 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
7157 })
7158 })
7159 .collect::<Result<Vec<_>, _>>()
7160 .log_err()
7161 else {
7162 continue;
7163 };
7164
7165 let wrap_column = buffer
7166 .settings_at(Point::new(start_row, 0), cx)
7167 .preferred_line_length as usize;
7168 let wrapped_text = wrap_with_prefix(
7169 line_prefix,
7170 lines_without_prefixes.join(" "),
7171 wrap_column,
7172 tab_size,
7173 );
7174
7175 // TODO: should always use char-based diff while still supporting cursor behavior that
7176 // matches vim.
7177 let diff = match is_vim_mode {
7178 IsVimMode::Yes => TextDiff::from_lines(&selection_text, &wrapped_text),
7179 IsVimMode::No => TextDiff::from_chars(&selection_text, &wrapped_text),
7180 };
7181 let mut offset = start.to_offset(&buffer);
7182 let mut moved_since_edit = true;
7183
7184 for change in diff.iter_all_changes() {
7185 let value = change.value();
7186 match change.tag() {
7187 ChangeTag::Equal => {
7188 offset += value.len();
7189 moved_since_edit = true;
7190 }
7191 ChangeTag::Delete => {
7192 let start = buffer.anchor_after(offset);
7193 let end = buffer.anchor_before(offset + value.len());
7194
7195 if moved_since_edit {
7196 edits.push((start..end, String::new()));
7197 } else {
7198 edits.last_mut().unwrap().0.end = end;
7199 }
7200
7201 offset += value.len();
7202 moved_since_edit = false;
7203 }
7204 ChangeTag::Insert => {
7205 if moved_since_edit {
7206 let anchor = buffer.anchor_after(offset);
7207 edits.push((anchor..anchor, value.to_string()));
7208 } else {
7209 edits.last_mut().unwrap().1.push_str(value);
7210 }
7211
7212 moved_since_edit = false;
7213 }
7214 }
7215 }
7216
7217 rewrapped_row_ranges.push(start_row..=end_row);
7218 }
7219
7220 self.buffer
7221 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
7222 }
7223
7224 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
7225 let mut text = String::new();
7226 let buffer = self.buffer.read(cx).snapshot(cx);
7227 let mut selections = self.selections.all::<Point>(cx);
7228 let mut clipboard_selections = Vec::with_capacity(selections.len());
7229 {
7230 let max_point = buffer.max_point();
7231 let mut is_first = true;
7232 for selection in &mut selections {
7233 let is_entire_line = selection.is_empty() || self.selections.line_mode;
7234 if is_entire_line {
7235 selection.start = Point::new(selection.start.row, 0);
7236 if !selection.is_empty() && selection.end.column == 0 {
7237 selection.end = cmp::min(max_point, selection.end);
7238 } else {
7239 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
7240 }
7241 selection.goal = SelectionGoal::None;
7242 }
7243 if is_first {
7244 is_first = false;
7245 } else {
7246 text += "\n";
7247 }
7248 let mut len = 0;
7249 for chunk in buffer.text_for_range(selection.start..selection.end) {
7250 text.push_str(chunk);
7251 len += chunk.len();
7252 }
7253 clipboard_selections.push(ClipboardSelection {
7254 len,
7255 is_entire_line,
7256 first_line_indent: buffer
7257 .indent_size_for_line(MultiBufferRow(selection.start.row))
7258 .len,
7259 });
7260 }
7261 }
7262
7263 self.transact(window, cx, |this, window, cx| {
7264 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7265 s.select(selections);
7266 });
7267 this.insert("", window, cx);
7268 });
7269 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
7270 }
7271
7272 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
7273 let item = self.cut_common(window, cx);
7274 cx.write_to_clipboard(item);
7275 }
7276
7277 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
7278 self.change_selections(None, window, cx, |s| {
7279 s.move_with(|snapshot, sel| {
7280 if sel.is_empty() {
7281 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
7282 }
7283 });
7284 });
7285 let item = self.cut_common(window, cx);
7286 cx.set_global(KillRing(item))
7287 }
7288
7289 pub fn kill_ring_yank(
7290 &mut self,
7291 _: &KillRingYank,
7292 window: &mut Window,
7293 cx: &mut Context<Self>,
7294 ) {
7295 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
7296 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
7297 (kill_ring.text().to_string(), kill_ring.metadata_json())
7298 } else {
7299 return;
7300 }
7301 } else {
7302 return;
7303 };
7304 self.do_paste(&text, metadata, false, window, cx);
7305 }
7306
7307 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
7308 let selections = self.selections.all::<Point>(cx);
7309 let buffer = self.buffer.read(cx).read(cx);
7310 let mut text = String::new();
7311
7312 let mut clipboard_selections = Vec::with_capacity(selections.len());
7313 {
7314 let max_point = buffer.max_point();
7315 let mut is_first = true;
7316 for selection in selections.iter() {
7317 let mut start = selection.start;
7318 let mut end = selection.end;
7319 let is_entire_line = selection.is_empty() || self.selections.line_mode;
7320 if is_entire_line {
7321 start = Point::new(start.row, 0);
7322 end = cmp::min(max_point, Point::new(end.row + 1, 0));
7323 }
7324 if is_first {
7325 is_first = false;
7326 } else {
7327 text += "\n";
7328 }
7329 let mut len = 0;
7330 for chunk in buffer.text_for_range(start..end) {
7331 text.push_str(chunk);
7332 len += chunk.len();
7333 }
7334 clipboard_selections.push(ClipboardSelection {
7335 len,
7336 is_entire_line,
7337 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
7338 });
7339 }
7340 }
7341
7342 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
7343 text,
7344 clipboard_selections,
7345 ));
7346 }
7347
7348 pub fn do_paste(
7349 &mut self,
7350 text: &String,
7351 clipboard_selections: Option<Vec<ClipboardSelection>>,
7352 handle_entire_lines: bool,
7353 window: &mut Window,
7354 cx: &mut Context<Self>,
7355 ) {
7356 if self.read_only(cx) {
7357 return;
7358 }
7359
7360 let clipboard_text = Cow::Borrowed(text);
7361
7362 self.transact(window, cx, |this, window, cx| {
7363 if let Some(mut clipboard_selections) = clipboard_selections {
7364 let old_selections = this.selections.all::<usize>(cx);
7365 let all_selections_were_entire_line =
7366 clipboard_selections.iter().all(|s| s.is_entire_line);
7367 let first_selection_indent_column =
7368 clipboard_selections.first().map(|s| s.first_line_indent);
7369 if clipboard_selections.len() != old_selections.len() {
7370 clipboard_selections.drain(..);
7371 }
7372 let cursor_offset = this.selections.last::<usize>(cx).head();
7373 let mut auto_indent_on_paste = true;
7374
7375 this.buffer.update(cx, |buffer, cx| {
7376 let snapshot = buffer.read(cx);
7377 auto_indent_on_paste =
7378 snapshot.settings_at(cursor_offset, cx).auto_indent_on_paste;
7379
7380 let mut start_offset = 0;
7381 let mut edits = Vec::new();
7382 let mut original_indent_columns = Vec::new();
7383 for (ix, selection) in old_selections.iter().enumerate() {
7384 let to_insert;
7385 let entire_line;
7386 let original_indent_column;
7387 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
7388 let end_offset = start_offset + clipboard_selection.len;
7389 to_insert = &clipboard_text[start_offset..end_offset];
7390 entire_line = clipboard_selection.is_entire_line;
7391 start_offset = end_offset + 1;
7392 original_indent_column = Some(clipboard_selection.first_line_indent);
7393 } else {
7394 to_insert = clipboard_text.as_str();
7395 entire_line = all_selections_were_entire_line;
7396 original_indent_column = first_selection_indent_column
7397 }
7398
7399 // If the corresponding selection was empty when this slice of the
7400 // clipboard text was written, then the entire line containing the
7401 // selection was copied. If this selection is also currently empty,
7402 // then paste the line before the current line of the buffer.
7403 let range = if selection.is_empty() && handle_entire_lines && entire_line {
7404 let column = selection.start.to_point(&snapshot).column as usize;
7405 let line_start = selection.start - column;
7406 line_start..line_start
7407 } else {
7408 selection.range()
7409 };
7410
7411 edits.push((range, to_insert));
7412 original_indent_columns.extend(original_indent_column);
7413 }
7414 drop(snapshot);
7415
7416 buffer.edit(
7417 edits,
7418 if auto_indent_on_paste {
7419 Some(AutoindentMode::Block {
7420 original_indent_columns,
7421 })
7422 } else {
7423 None
7424 },
7425 cx,
7426 );
7427 });
7428
7429 let selections = this.selections.all::<usize>(cx);
7430 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7431 s.select(selections)
7432 });
7433 } else {
7434 this.insert(&clipboard_text, window, cx);
7435 }
7436 });
7437 }
7438
7439 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
7440 if let Some(item) = cx.read_from_clipboard() {
7441 let entries = item.entries();
7442
7443 match entries.first() {
7444 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
7445 // of all the pasted entries.
7446 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
7447 .do_paste(
7448 clipboard_string.text(),
7449 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
7450 true,
7451 window,
7452 cx,
7453 ),
7454 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
7455 }
7456 }
7457 }
7458
7459 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
7460 if self.read_only(cx) {
7461 return;
7462 }
7463
7464 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
7465 if let Some((selections, _)) =
7466 self.selection_history.transaction(transaction_id).cloned()
7467 {
7468 self.change_selections(None, window, cx, |s| {
7469 s.select_anchors(selections.to_vec());
7470 });
7471 }
7472 self.request_autoscroll(Autoscroll::fit(), cx);
7473 self.unmark_text(window, cx);
7474 self.refresh_inline_completion(true, false, window, cx);
7475 cx.emit(EditorEvent::Edited { transaction_id });
7476 cx.emit(EditorEvent::TransactionUndone { transaction_id });
7477 }
7478 }
7479
7480 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
7481 if self.read_only(cx) {
7482 return;
7483 }
7484
7485 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
7486 if let Some((_, Some(selections))) =
7487 self.selection_history.transaction(transaction_id).cloned()
7488 {
7489 self.change_selections(None, window, cx, |s| {
7490 s.select_anchors(selections.to_vec());
7491 });
7492 }
7493 self.request_autoscroll(Autoscroll::fit(), cx);
7494 self.unmark_text(window, cx);
7495 self.refresh_inline_completion(true, false, window, cx);
7496 cx.emit(EditorEvent::Edited { transaction_id });
7497 }
7498 }
7499
7500 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
7501 self.buffer
7502 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
7503 }
7504
7505 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
7506 self.buffer
7507 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
7508 }
7509
7510 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
7511 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7512 let line_mode = s.line_mode;
7513 s.move_with(|map, selection| {
7514 let cursor = if selection.is_empty() && !line_mode {
7515 movement::left(map, selection.start)
7516 } else {
7517 selection.start
7518 };
7519 selection.collapse_to(cursor, SelectionGoal::None);
7520 });
7521 })
7522 }
7523
7524 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
7525 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7526 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
7527 })
7528 }
7529
7530 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
7531 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7532 let line_mode = s.line_mode;
7533 s.move_with(|map, selection| {
7534 let cursor = if selection.is_empty() && !line_mode {
7535 movement::right(map, selection.end)
7536 } else {
7537 selection.end
7538 };
7539 selection.collapse_to(cursor, SelectionGoal::None)
7540 });
7541 })
7542 }
7543
7544 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
7545 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7546 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
7547 })
7548 }
7549
7550 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
7551 if self.take_rename(true, window, cx).is_some() {
7552 return;
7553 }
7554
7555 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7556 cx.propagate();
7557 return;
7558 }
7559
7560 let text_layout_details = &self.text_layout_details(window);
7561 let selection_count = self.selections.count();
7562 let first_selection = self.selections.first_anchor();
7563
7564 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7565 let line_mode = s.line_mode;
7566 s.move_with(|map, selection| {
7567 if !selection.is_empty() && !line_mode {
7568 selection.goal = SelectionGoal::None;
7569 }
7570 let (cursor, goal) = movement::up(
7571 map,
7572 selection.start,
7573 selection.goal,
7574 false,
7575 text_layout_details,
7576 );
7577 selection.collapse_to(cursor, goal);
7578 });
7579 });
7580
7581 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7582 {
7583 cx.propagate();
7584 }
7585 }
7586
7587 pub fn move_up_by_lines(
7588 &mut self,
7589 action: &MoveUpByLines,
7590 window: &mut Window,
7591 cx: &mut Context<Self>,
7592 ) {
7593 if self.take_rename(true, window, cx).is_some() {
7594 return;
7595 }
7596
7597 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7598 cx.propagate();
7599 return;
7600 }
7601
7602 let text_layout_details = &self.text_layout_details(window);
7603
7604 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7605 let line_mode = s.line_mode;
7606 s.move_with(|map, selection| {
7607 if !selection.is_empty() && !line_mode {
7608 selection.goal = SelectionGoal::None;
7609 }
7610 let (cursor, goal) = movement::up_by_rows(
7611 map,
7612 selection.start,
7613 action.lines,
7614 selection.goal,
7615 false,
7616 text_layout_details,
7617 );
7618 selection.collapse_to(cursor, goal);
7619 });
7620 })
7621 }
7622
7623 pub fn move_down_by_lines(
7624 &mut self,
7625 action: &MoveDownByLines,
7626 window: &mut Window,
7627 cx: &mut Context<Self>,
7628 ) {
7629 if self.take_rename(true, window, cx).is_some() {
7630 return;
7631 }
7632
7633 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7634 cx.propagate();
7635 return;
7636 }
7637
7638 let text_layout_details = &self.text_layout_details(window);
7639
7640 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7641 let line_mode = s.line_mode;
7642 s.move_with(|map, selection| {
7643 if !selection.is_empty() && !line_mode {
7644 selection.goal = SelectionGoal::None;
7645 }
7646 let (cursor, goal) = movement::down_by_rows(
7647 map,
7648 selection.start,
7649 action.lines,
7650 selection.goal,
7651 false,
7652 text_layout_details,
7653 );
7654 selection.collapse_to(cursor, goal);
7655 });
7656 })
7657 }
7658
7659 pub fn select_down_by_lines(
7660 &mut self,
7661 action: &SelectDownByLines,
7662 window: &mut Window,
7663 cx: &mut Context<Self>,
7664 ) {
7665 let text_layout_details = &self.text_layout_details(window);
7666 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7667 s.move_heads_with(|map, head, goal| {
7668 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
7669 })
7670 })
7671 }
7672
7673 pub fn select_up_by_lines(
7674 &mut self,
7675 action: &SelectUpByLines,
7676 window: &mut Window,
7677 cx: &mut Context<Self>,
7678 ) {
7679 let text_layout_details = &self.text_layout_details(window);
7680 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7681 s.move_heads_with(|map, head, goal| {
7682 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
7683 })
7684 })
7685 }
7686
7687 pub fn select_page_up(
7688 &mut self,
7689 _: &SelectPageUp,
7690 window: &mut Window,
7691 cx: &mut Context<Self>,
7692 ) {
7693 let Some(row_count) = self.visible_row_count() else {
7694 return;
7695 };
7696
7697 let text_layout_details = &self.text_layout_details(window);
7698
7699 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7700 s.move_heads_with(|map, head, goal| {
7701 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
7702 })
7703 })
7704 }
7705
7706 pub fn move_page_up(
7707 &mut self,
7708 action: &MovePageUp,
7709 window: &mut Window,
7710 cx: &mut Context<Self>,
7711 ) {
7712 if self.take_rename(true, window, cx).is_some() {
7713 return;
7714 }
7715
7716 if self
7717 .context_menu
7718 .borrow_mut()
7719 .as_mut()
7720 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
7721 .unwrap_or(false)
7722 {
7723 return;
7724 }
7725
7726 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7727 cx.propagate();
7728 return;
7729 }
7730
7731 let Some(row_count) = self.visible_row_count() else {
7732 return;
7733 };
7734
7735 let autoscroll = if action.center_cursor {
7736 Autoscroll::center()
7737 } else {
7738 Autoscroll::fit()
7739 };
7740
7741 let text_layout_details = &self.text_layout_details(window);
7742
7743 self.change_selections(Some(autoscroll), window, cx, |s| {
7744 let line_mode = s.line_mode;
7745 s.move_with(|map, selection| {
7746 if !selection.is_empty() && !line_mode {
7747 selection.goal = SelectionGoal::None;
7748 }
7749 let (cursor, goal) = movement::up_by_rows(
7750 map,
7751 selection.end,
7752 row_count,
7753 selection.goal,
7754 false,
7755 text_layout_details,
7756 );
7757 selection.collapse_to(cursor, goal);
7758 });
7759 });
7760 }
7761
7762 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
7763 let text_layout_details = &self.text_layout_details(window);
7764 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7765 s.move_heads_with(|map, head, goal| {
7766 movement::up(map, head, goal, false, text_layout_details)
7767 })
7768 })
7769 }
7770
7771 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
7772 self.take_rename(true, window, cx);
7773
7774 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7775 cx.propagate();
7776 return;
7777 }
7778
7779 let text_layout_details = &self.text_layout_details(window);
7780 let selection_count = self.selections.count();
7781 let first_selection = self.selections.first_anchor();
7782
7783 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7784 let line_mode = s.line_mode;
7785 s.move_with(|map, selection| {
7786 if !selection.is_empty() && !line_mode {
7787 selection.goal = SelectionGoal::None;
7788 }
7789 let (cursor, goal) = movement::down(
7790 map,
7791 selection.end,
7792 selection.goal,
7793 false,
7794 text_layout_details,
7795 );
7796 selection.collapse_to(cursor, goal);
7797 });
7798 });
7799
7800 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7801 {
7802 cx.propagate();
7803 }
7804 }
7805
7806 pub fn select_page_down(
7807 &mut self,
7808 _: &SelectPageDown,
7809 window: &mut Window,
7810 cx: &mut Context<Self>,
7811 ) {
7812 let Some(row_count) = self.visible_row_count() else {
7813 return;
7814 };
7815
7816 let text_layout_details = &self.text_layout_details(window);
7817
7818 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7819 s.move_heads_with(|map, head, goal| {
7820 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
7821 })
7822 })
7823 }
7824
7825 pub fn move_page_down(
7826 &mut self,
7827 action: &MovePageDown,
7828 window: &mut Window,
7829 cx: &mut Context<Self>,
7830 ) {
7831 if self.take_rename(true, window, cx).is_some() {
7832 return;
7833 }
7834
7835 if self
7836 .context_menu
7837 .borrow_mut()
7838 .as_mut()
7839 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
7840 .unwrap_or(false)
7841 {
7842 return;
7843 }
7844
7845 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7846 cx.propagate();
7847 return;
7848 }
7849
7850 let Some(row_count) = self.visible_row_count() else {
7851 return;
7852 };
7853
7854 let autoscroll = if action.center_cursor {
7855 Autoscroll::center()
7856 } else {
7857 Autoscroll::fit()
7858 };
7859
7860 let text_layout_details = &self.text_layout_details(window);
7861 self.change_selections(Some(autoscroll), window, cx, |s| {
7862 let line_mode = s.line_mode;
7863 s.move_with(|map, selection| {
7864 if !selection.is_empty() && !line_mode {
7865 selection.goal = SelectionGoal::None;
7866 }
7867 let (cursor, goal) = movement::down_by_rows(
7868 map,
7869 selection.end,
7870 row_count,
7871 selection.goal,
7872 false,
7873 text_layout_details,
7874 );
7875 selection.collapse_to(cursor, goal);
7876 });
7877 });
7878 }
7879
7880 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
7881 let text_layout_details = &self.text_layout_details(window);
7882 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7883 s.move_heads_with(|map, head, goal| {
7884 movement::down(map, head, goal, false, text_layout_details)
7885 })
7886 });
7887 }
7888
7889 pub fn context_menu_first(
7890 &mut self,
7891 _: &ContextMenuFirst,
7892 _window: &mut Window,
7893 cx: &mut Context<Self>,
7894 ) {
7895 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
7896 context_menu.select_first(self.completion_provider.as_deref(), cx);
7897 }
7898 }
7899
7900 pub fn context_menu_prev(
7901 &mut self,
7902 _: &ContextMenuPrev,
7903 _window: &mut Window,
7904 cx: &mut Context<Self>,
7905 ) {
7906 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
7907 context_menu.select_prev(self.completion_provider.as_deref(), cx);
7908 }
7909 }
7910
7911 pub fn context_menu_next(
7912 &mut self,
7913 _: &ContextMenuNext,
7914 _window: &mut Window,
7915 cx: &mut Context<Self>,
7916 ) {
7917 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
7918 context_menu.select_next(self.completion_provider.as_deref(), cx);
7919 }
7920 }
7921
7922 pub fn context_menu_last(
7923 &mut self,
7924 _: &ContextMenuLast,
7925 _window: &mut Window,
7926 cx: &mut Context<Self>,
7927 ) {
7928 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
7929 context_menu.select_last(self.completion_provider.as_deref(), cx);
7930 }
7931 }
7932
7933 pub fn move_to_previous_word_start(
7934 &mut self,
7935 _: &MoveToPreviousWordStart,
7936 window: &mut Window,
7937 cx: &mut Context<Self>,
7938 ) {
7939 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7940 s.move_cursors_with(|map, head, _| {
7941 (
7942 movement::previous_word_start(map, head),
7943 SelectionGoal::None,
7944 )
7945 });
7946 })
7947 }
7948
7949 pub fn move_to_previous_subword_start(
7950 &mut self,
7951 _: &MoveToPreviousSubwordStart,
7952 window: &mut Window,
7953 cx: &mut Context<Self>,
7954 ) {
7955 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7956 s.move_cursors_with(|map, head, _| {
7957 (
7958 movement::previous_subword_start(map, head),
7959 SelectionGoal::None,
7960 )
7961 });
7962 })
7963 }
7964
7965 pub fn select_to_previous_word_start(
7966 &mut self,
7967 _: &SelectToPreviousWordStart,
7968 window: &mut Window,
7969 cx: &mut Context<Self>,
7970 ) {
7971 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7972 s.move_heads_with(|map, head, _| {
7973 (
7974 movement::previous_word_start(map, head),
7975 SelectionGoal::None,
7976 )
7977 });
7978 })
7979 }
7980
7981 pub fn select_to_previous_subword_start(
7982 &mut self,
7983 _: &SelectToPreviousSubwordStart,
7984 window: &mut Window,
7985 cx: &mut Context<Self>,
7986 ) {
7987 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7988 s.move_heads_with(|map, head, _| {
7989 (
7990 movement::previous_subword_start(map, head),
7991 SelectionGoal::None,
7992 )
7993 });
7994 })
7995 }
7996
7997 pub fn delete_to_previous_word_start(
7998 &mut self,
7999 action: &DeleteToPreviousWordStart,
8000 window: &mut Window,
8001 cx: &mut Context<Self>,
8002 ) {
8003 self.transact(window, cx, |this, window, cx| {
8004 this.select_autoclose_pair(window, cx);
8005 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8006 let line_mode = s.line_mode;
8007 s.move_with(|map, selection| {
8008 if selection.is_empty() && !line_mode {
8009 let cursor = if action.ignore_newlines {
8010 movement::previous_word_start(map, selection.head())
8011 } else {
8012 movement::previous_word_start_or_newline(map, selection.head())
8013 };
8014 selection.set_head(cursor, SelectionGoal::None);
8015 }
8016 });
8017 });
8018 this.insert("", window, cx);
8019 });
8020 }
8021
8022 pub fn delete_to_previous_subword_start(
8023 &mut self,
8024 _: &DeleteToPreviousSubwordStart,
8025 window: &mut Window,
8026 cx: &mut Context<Self>,
8027 ) {
8028 self.transact(window, cx, |this, window, cx| {
8029 this.select_autoclose_pair(window, cx);
8030 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8031 let line_mode = s.line_mode;
8032 s.move_with(|map, selection| {
8033 if selection.is_empty() && !line_mode {
8034 let cursor = movement::previous_subword_start(map, selection.head());
8035 selection.set_head(cursor, SelectionGoal::None);
8036 }
8037 });
8038 });
8039 this.insert("", window, cx);
8040 });
8041 }
8042
8043 pub fn move_to_next_word_end(
8044 &mut self,
8045 _: &MoveToNextWordEnd,
8046 window: &mut Window,
8047 cx: &mut Context<Self>,
8048 ) {
8049 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8050 s.move_cursors_with(|map, head, _| {
8051 (movement::next_word_end(map, head), SelectionGoal::None)
8052 });
8053 })
8054 }
8055
8056 pub fn move_to_next_subword_end(
8057 &mut self,
8058 _: &MoveToNextSubwordEnd,
8059 window: &mut Window,
8060 cx: &mut Context<Self>,
8061 ) {
8062 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8063 s.move_cursors_with(|map, head, _| {
8064 (movement::next_subword_end(map, head), SelectionGoal::None)
8065 });
8066 })
8067 }
8068
8069 pub fn select_to_next_word_end(
8070 &mut self,
8071 _: &SelectToNextWordEnd,
8072 window: &mut Window,
8073 cx: &mut Context<Self>,
8074 ) {
8075 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8076 s.move_heads_with(|map, head, _| {
8077 (movement::next_word_end(map, head), SelectionGoal::None)
8078 });
8079 })
8080 }
8081
8082 pub fn select_to_next_subword_end(
8083 &mut self,
8084 _: &SelectToNextSubwordEnd,
8085 window: &mut Window,
8086 cx: &mut Context<Self>,
8087 ) {
8088 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8089 s.move_heads_with(|map, head, _| {
8090 (movement::next_subword_end(map, head), SelectionGoal::None)
8091 });
8092 })
8093 }
8094
8095 pub fn delete_to_next_word_end(
8096 &mut self,
8097 action: &DeleteToNextWordEnd,
8098 window: &mut Window,
8099 cx: &mut Context<Self>,
8100 ) {
8101 self.transact(window, cx, |this, window, cx| {
8102 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8103 let line_mode = s.line_mode;
8104 s.move_with(|map, selection| {
8105 if selection.is_empty() && !line_mode {
8106 let cursor = if action.ignore_newlines {
8107 movement::next_word_end(map, selection.head())
8108 } else {
8109 movement::next_word_end_or_newline(map, selection.head())
8110 };
8111 selection.set_head(cursor, SelectionGoal::None);
8112 }
8113 });
8114 });
8115 this.insert("", window, cx);
8116 });
8117 }
8118
8119 pub fn delete_to_next_subword_end(
8120 &mut self,
8121 _: &DeleteToNextSubwordEnd,
8122 window: &mut Window,
8123 cx: &mut Context<Self>,
8124 ) {
8125 self.transact(window, cx, |this, window, cx| {
8126 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8127 s.move_with(|map, selection| {
8128 if selection.is_empty() {
8129 let cursor = movement::next_subword_end(map, selection.head());
8130 selection.set_head(cursor, SelectionGoal::None);
8131 }
8132 });
8133 });
8134 this.insert("", window, cx);
8135 });
8136 }
8137
8138 pub fn move_to_beginning_of_line(
8139 &mut self,
8140 action: &MoveToBeginningOfLine,
8141 window: &mut Window,
8142 cx: &mut Context<Self>,
8143 ) {
8144 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8145 s.move_cursors_with(|map, head, _| {
8146 (
8147 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
8148 SelectionGoal::None,
8149 )
8150 });
8151 })
8152 }
8153
8154 pub fn select_to_beginning_of_line(
8155 &mut self,
8156 action: &SelectToBeginningOfLine,
8157 window: &mut Window,
8158 cx: &mut Context<Self>,
8159 ) {
8160 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8161 s.move_heads_with(|map, head, _| {
8162 (
8163 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
8164 SelectionGoal::None,
8165 )
8166 });
8167 });
8168 }
8169
8170 pub fn delete_to_beginning_of_line(
8171 &mut self,
8172 _: &DeleteToBeginningOfLine,
8173 window: &mut Window,
8174 cx: &mut Context<Self>,
8175 ) {
8176 self.transact(window, cx, |this, window, cx| {
8177 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8178 s.move_with(|_, selection| {
8179 selection.reversed = true;
8180 });
8181 });
8182
8183 this.select_to_beginning_of_line(
8184 &SelectToBeginningOfLine {
8185 stop_at_soft_wraps: false,
8186 },
8187 window,
8188 cx,
8189 );
8190 this.backspace(&Backspace, window, cx);
8191 });
8192 }
8193
8194 pub fn move_to_end_of_line(
8195 &mut self,
8196 action: &MoveToEndOfLine,
8197 window: &mut Window,
8198 cx: &mut Context<Self>,
8199 ) {
8200 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8201 s.move_cursors_with(|map, head, _| {
8202 (
8203 movement::line_end(map, head, action.stop_at_soft_wraps),
8204 SelectionGoal::None,
8205 )
8206 });
8207 })
8208 }
8209
8210 pub fn select_to_end_of_line(
8211 &mut self,
8212 action: &SelectToEndOfLine,
8213 window: &mut Window,
8214 cx: &mut Context<Self>,
8215 ) {
8216 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8217 s.move_heads_with(|map, head, _| {
8218 (
8219 movement::line_end(map, head, action.stop_at_soft_wraps),
8220 SelectionGoal::None,
8221 )
8222 });
8223 })
8224 }
8225
8226 pub fn delete_to_end_of_line(
8227 &mut self,
8228 _: &DeleteToEndOfLine,
8229 window: &mut Window,
8230 cx: &mut Context<Self>,
8231 ) {
8232 self.transact(window, cx, |this, window, cx| {
8233 this.select_to_end_of_line(
8234 &SelectToEndOfLine {
8235 stop_at_soft_wraps: false,
8236 },
8237 window,
8238 cx,
8239 );
8240 this.delete(&Delete, window, cx);
8241 });
8242 }
8243
8244 pub fn cut_to_end_of_line(
8245 &mut self,
8246 _: &CutToEndOfLine,
8247 window: &mut Window,
8248 cx: &mut Context<Self>,
8249 ) {
8250 self.transact(window, cx, |this, window, cx| {
8251 this.select_to_end_of_line(
8252 &SelectToEndOfLine {
8253 stop_at_soft_wraps: false,
8254 },
8255 window,
8256 cx,
8257 );
8258 this.cut(&Cut, window, cx);
8259 });
8260 }
8261
8262 pub fn move_to_start_of_paragraph(
8263 &mut self,
8264 _: &MoveToStartOfParagraph,
8265 window: &mut Window,
8266 cx: &mut Context<Self>,
8267 ) {
8268 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8269 cx.propagate();
8270 return;
8271 }
8272
8273 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8274 s.move_with(|map, selection| {
8275 selection.collapse_to(
8276 movement::start_of_paragraph(map, selection.head(), 1),
8277 SelectionGoal::None,
8278 )
8279 });
8280 })
8281 }
8282
8283 pub fn move_to_end_of_paragraph(
8284 &mut self,
8285 _: &MoveToEndOfParagraph,
8286 window: &mut Window,
8287 cx: &mut Context<Self>,
8288 ) {
8289 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8290 cx.propagate();
8291 return;
8292 }
8293
8294 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8295 s.move_with(|map, selection| {
8296 selection.collapse_to(
8297 movement::end_of_paragraph(map, selection.head(), 1),
8298 SelectionGoal::None,
8299 )
8300 });
8301 })
8302 }
8303
8304 pub fn select_to_start_of_paragraph(
8305 &mut self,
8306 _: &SelectToStartOfParagraph,
8307 window: &mut Window,
8308 cx: &mut Context<Self>,
8309 ) {
8310 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8311 cx.propagate();
8312 return;
8313 }
8314
8315 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8316 s.move_heads_with(|map, head, _| {
8317 (
8318 movement::start_of_paragraph(map, head, 1),
8319 SelectionGoal::None,
8320 )
8321 });
8322 })
8323 }
8324
8325 pub fn select_to_end_of_paragraph(
8326 &mut self,
8327 _: &SelectToEndOfParagraph,
8328 window: &mut Window,
8329 cx: &mut Context<Self>,
8330 ) {
8331 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8332 cx.propagate();
8333 return;
8334 }
8335
8336 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8337 s.move_heads_with(|map, head, _| {
8338 (
8339 movement::end_of_paragraph(map, head, 1),
8340 SelectionGoal::None,
8341 )
8342 });
8343 })
8344 }
8345
8346 pub fn move_to_beginning(
8347 &mut self,
8348 _: &MoveToBeginning,
8349 window: &mut Window,
8350 cx: &mut Context<Self>,
8351 ) {
8352 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8353 cx.propagate();
8354 return;
8355 }
8356
8357 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8358 s.select_ranges(vec![0..0]);
8359 });
8360 }
8361
8362 pub fn select_to_beginning(
8363 &mut self,
8364 _: &SelectToBeginning,
8365 window: &mut Window,
8366 cx: &mut Context<Self>,
8367 ) {
8368 let mut selection = self.selections.last::<Point>(cx);
8369 selection.set_head(Point::zero(), SelectionGoal::None);
8370
8371 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8372 s.select(vec![selection]);
8373 });
8374 }
8375
8376 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
8377 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8378 cx.propagate();
8379 return;
8380 }
8381
8382 let cursor = self.buffer.read(cx).read(cx).len();
8383 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8384 s.select_ranges(vec![cursor..cursor])
8385 });
8386 }
8387
8388 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
8389 self.nav_history = nav_history;
8390 }
8391
8392 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
8393 self.nav_history.as_ref()
8394 }
8395
8396 fn push_to_nav_history(
8397 &mut self,
8398 cursor_anchor: Anchor,
8399 new_position: Option<Point>,
8400 cx: &mut Context<Self>,
8401 ) {
8402 if let Some(nav_history) = self.nav_history.as_mut() {
8403 let buffer = self.buffer.read(cx).read(cx);
8404 let cursor_position = cursor_anchor.to_point(&buffer);
8405 let scroll_state = self.scroll_manager.anchor();
8406 let scroll_top_row = scroll_state.top_row(&buffer);
8407 drop(buffer);
8408
8409 if let Some(new_position) = new_position {
8410 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
8411 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
8412 return;
8413 }
8414 }
8415
8416 nav_history.push(
8417 Some(NavigationData {
8418 cursor_anchor,
8419 cursor_position,
8420 scroll_anchor: scroll_state,
8421 scroll_top_row,
8422 }),
8423 cx,
8424 );
8425 }
8426 }
8427
8428 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
8429 let buffer = self.buffer.read(cx).snapshot(cx);
8430 let mut selection = self.selections.first::<usize>(cx);
8431 selection.set_head(buffer.len(), SelectionGoal::None);
8432 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8433 s.select(vec![selection]);
8434 });
8435 }
8436
8437 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
8438 let end = self.buffer.read(cx).read(cx).len();
8439 self.change_selections(None, window, cx, |s| {
8440 s.select_ranges(vec![0..end]);
8441 });
8442 }
8443
8444 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
8445 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8446 let mut selections = self.selections.all::<Point>(cx);
8447 let max_point = display_map.buffer_snapshot.max_point();
8448 for selection in &mut selections {
8449 let rows = selection.spanned_rows(true, &display_map);
8450 selection.start = Point::new(rows.start.0, 0);
8451 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
8452 selection.reversed = false;
8453 }
8454 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8455 s.select(selections);
8456 });
8457 }
8458
8459 pub fn split_selection_into_lines(
8460 &mut self,
8461 _: &SplitSelectionIntoLines,
8462 window: &mut Window,
8463 cx: &mut Context<Self>,
8464 ) {
8465 let mut to_unfold = Vec::new();
8466 let mut new_selection_ranges = Vec::new();
8467 {
8468 let selections = self.selections.all::<Point>(cx);
8469 let buffer = self.buffer.read(cx).read(cx);
8470 for selection in selections {
8471 for row in selection.start.row..selection.end.row {
8472 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
8473 new_selection_ranges.push(cursor..cursor);
8474 }
8475 new_selection_ranges.push(selection.end..selection.end);
8476 to_unfold.push(selection.start..selection.end);
8477 }
8478 }
8479 self.unfold_ranges(&to_unfold, true, true, cx);
8480 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8481 s.select_ranges(new_selection_ranges);
8482 });
8483 }
8484
8485 pub fn add_selection_above(
8486 &mut self,
8487 _: &AddSelectionAbove,
8488 window: &mut Window,
8489 cx: &mut Context<Self>,
8490 ) {
8491 self.add_selection(true, window, cx);
8492 }
8493
8494 pub fn add_selection_below(
8495 &mut self,
8496 _: &AddSelectionBelow,
8497 window: &mut Window,
8498 cx: &mut Context<Self>,
8499 ) {
8500 self.add_selection(false, window, cx);
8501 }
8502
8503 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
8504 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8505 let mut selections = self.selections.all::<Point>(cx);
8506 let text_layout_details = self.text_layout_details(window);
8507 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
8508 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
8509 let range = oldest_selection.display_range(&display_map).sorted();
8510
8511 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
8512 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
8513 let positions = start_x.min(end_x)..start_x.max(end_x);
8514
8515 selections.clear();
8516 let mut stack = Vec::new();
8517 for row in range.start.row().0..=range.end.row().0 {
8518 if let Some(selection) = self.selections.build_columnar_selection(
8519 &display_map,
8520 DisplayRow(row),
8521 &positions,
8522 oldest_selection.reversed,
8523 &text_layout_details,
8524 ) {
8525 stack.push(selection.id);
8526 selections.push(selection);
8527 }
8528 }
8529
8530 if above {
8531 stack.reverse();
8532 }
8533
8534 AddSelectionsState { above, stack }
8535 });
8536
8537 let last_added_selection = *state.stack.last().unwrap();
8538 let mut new_selections = Vec::new();
8539 if above == state.above {
8540 let end_row = if above {
8541 DisplayRow(0)
8542 } else {
8543 display_map.max_point().row()
8544 };
8545
8546 'outer: for selection in selections {
8547 if selection.id == last_added_selection {
8548 let range = selection.display_range(&display_map).sorted();
8549 debug_assert_eq!(range.start.row(), range.end.row());
8550 let mut row = range.start.row();
8551 let positions =
8552 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
8553 px(start)..px(end)
8554 } else {
8555 let start_x =
8556 display_map.x_for_display_point(range.start, &text_layout_details);
8557 let end_x =
8558 display_map.x_for_display_point(range.end, &text_layout_details);
8559 start_x.min(end_x)..start_x.max(end_x)
8560 };
8561
8562 while row != end_row {
8563 if above {
8564 row.0 -= 1;
8565 } else {
8566 row.0 += 1;
8567 }
8568
8569 if let Some(new_selection) = self.selections.build_columnar_selection(
8570 &display_map,
8571 row,
8572 &positions,
8573 selection.reversed,
8574 &text_layout_details,
8575 ) {
8576 state.stack.push(new_selection.id);
8577 if above {
8578 new_selections.push(new_selection);
8579 new_selections.push(selection);
8580 } else {
8581 new_selections.push(selection);
8582 new_selections.push(new_selection);
8583 }
8584
8585 continue 'outer;
8586 }
8587 }
8588 }
8589
8590 new_selections.push(selection);
8591 }
8592 } else {
8593 new_selections = selections;
8594 new_selections.retain(|s| s.id != last_added_selection);
8595 state.stack.pop();
8596 }
8597
8598 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8599 s.select(new_selections);
8600 });
8601 if state.stack.len() > 1 {
8602 self.add_selections_state = Some(state);
8603 }
8604 }
8605
8606 pub fn select_next_match_internal(
8607 &mut self,
8608 display_map: &DisplaySnapshot,
8609 replace_newest: bool,
8610 autoscroll: Option<Autoscroll>,
8611 window: &mut Window,
8612 cx: &mut Context<Self>,
8613 ) -> Result<()> {
8614 fn select_next_match_ranges(
8615 this: &mut Editor,
8616 range: Range<usize>,
8617 replace_newest: bool,
8618 auto_scroll: Option<Autoscroll>,
8619 window: &mut Window,
8620 cx: &mut Context<Editor>,
8621 ) {
8622 this.unfold_ranges(&[range.clone()], false, true, cx);
8623 this.change_selections(auto_scroll, window, cx, |s| {
8624 if replace_newest {
8625 s.delete(s.newest_anchor().id);
8626 }
8627 s.insert_range(range.clone());
8628 });
8629 }
8630
8631 let buffer = &display_map.buffer_snapshot;
8632 let mut selections = self.selections.all::<usize>(cx);
8633 if let Some(mut select_next_state) = self.select_next_state.take() {
8634 let query = &select_next_state.query;
8635 if !select_next_state.done {
8636 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
8637 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
8638 let mut next_selected_range = None;
8639
8640 let bytes_after_last_selection =
8641 buffer.bytes_in_range(last_selection.end..buffer.len());
8642 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
8643 let query_matches = query
8644 .stream_find_iter(bytes_after_last_selection)
8645 .map(|result| (last_selection.end, result))
8646 .chain(
8647 query
8648 .stream_find_iter(bytes_before_first_selection)
8649 .map(|result| (0, result)),
8650 );
8651
8652 for (start_offset, query_match) in query_matches {
8653 let query_match = query_match.unwrap(); // can only fail due to I/O
8654 let offset_range =
8655 start_offset + query_match.start()..start_offset + query_match.end();
8656 let display_range = offset_range.start.to_display_point(display_map)
8657 ..offset_range.end.to_display_point(display_map);
8658
8659 if !select_next_state.wordwise
8660 || (!movement::is_inside_word(display_map, display_range.start)
8661 && !movement::is_inside_word(display_map, display_range.end))
8662 {
8663 // TODO: This is n^2, because we might check all the selections
8664 if !selections
8665 .iter()
8666 .any(|selection| selection.range().overlaps(&offset_range))
8667 {
8668 next_selected_range = Some(offset_range);
8669 break;
8670 }
8671 }
8672 }
8673
8674 if let Some(next_selected_range) = next_selected_range {
8675 select_next_match_ranges(
8676 self,
8677 next_selected_range,
8678 replace_newest,
8679 autoscroll,
8680 window,
8681 cx,
8682 );
8683 } else {
8684 select_next_state.done = true;
8685 }
8686 }
8687
8688 self.select_next_state = Some(select_next_state);
8689 } else {
8690 let mut only_carets = true;
8691 let mut same_text_selected = true;
8692 let mut selected_text = None;
8693
8694 let mut selections_iter = selections.iter().peekable();
8695 while let Some(selection) = selections_iter.next() {
8696 if selection.start != selection.end {
8697 only_carets = false;
8698 }
8699
8700 if same_text_selected {
8701 if selected_text.is_none() {
8702 selected_text =
8703 Some(buffer.text_for_range(selection.range()).collect::<String>());
8704 }
8705
8706 if let Some(next_selection) = selections_iter.peek() {
8707 if next_selection.range().len() == selection.range().len() {
8708 let next_selected_text = buffer
8709 .text_for_range(next_selection.range())
8710 .collect::<String>();
8711 if Some(next_selected_text) != selected_text {
8712 same_text_selected = false;
8713 selected_text = None;
8714 }
8715 } else {
8716 same_text_selected = false;
8717 selected_text = None;
8718 }
8719 }
8720 }
8721 }
8722
8723 if only_carets {
8724 for selection in &mut selections {
8725 let word_range = movement::surrounding_word(
8726 display_map,
8727 selection.start.to_display_point(display_map),
8728 );
8729 selection.start = word_range.start.to_offset(display_map, Bias::Left);
8730 selection.end = word_range.end.to_offset(display_map, Bias::Left);
8731 selection.goal = SelectionGoal::None;
8732 selection.reversed = false;
8733 select_next_match_ranges(
8734 self,
8735 selection.start..selection.end,
8736 replace_newest,
8737 autoscroll,
8738 window,
8739 cx,
8740 );
8741 }
8742
8743 if selections.len() == 1 {
8744 let selection = selections
8745 .last()
8746 .expect("ensured that there's only one selection");
8747 let query = buffer
8748 .text_for_range(selection.start..selection.end)
8749 .collect::<String>();
8750 let is_empty = query.is_empty();
8751 let select_state = SelectNextState {
8752 query: AhoCorasick::new(&[query])?,
8753 wordwise: true,
8754 done: is_empty,
8755 };
8756 self.select_next_state = Some(select_state);
8757 } else {
8758 self.select_next_state = None;
8759 }
8760 } else if let Some(selected_text) = selected_text {
8761 self.select_next_state = Some(SelectNextState {
8762 query: AhoCorasick::new(&[selected_text])?,
8763 wordwise: false,
8764 done: false,
8765 });
8766 self.select_next_match_internal(
8767 display_map,
8768 replace_newest,
8769 autoscroll,
8770 window,
8771 cx,
8772 )?;
8773 }
8774 }
8775 Ok(())
8776 }
8777
8778 pub fn select_all_matches(
8779 &mut self,
8780 _action: &SelectAllMatches,
8781 window: &mut Window,
8782 cx: &mut Context<Self>,
8783 ) -> Result<()> {
8784 self.push_to_selection_history();
8785 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8786
8787 self.select_next_match_internal(&display_map, false, None, window, cx)?;
8788 let Some(select_next_state) = self.select_next_state.as_mut() else {
8789 return Ok(());
8790 };
8791 if select_next_state.done {
8792 return Ok(());
8793 }
8794
8795 let mut new_selections = self.selections.all::<usize>(cx);
8796
8797 let buffer = &display_map.buffer_snapshot;
8798 let query_matches = select_next_state
8799 .query
8800 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
8801
8802 for query_match in query_matches {
8803 let query_match = query_match.unwrap(); // can only fail due to I/O
8804 let offset_range = query_match.start()..query_match.end();
8805 let display_range = offset_range.start.to_display_point(&display_map)
8806 ..offset_range.end.to_display_point(&display_map);
8807
8808 if !select_next_state.wordwise
8809 || (!movement::is_inside_word(&display_map, display_range.start)
8810 && !movement::is_inside_word(&display_map, display_range.end))
8811 {
8812 self.selections.change_with(cx, |selections| {
8813 new_selections.push(Selection {
8814 id: selections.new_selection_id(),
8815 start: offset_range.start,
8816 end: offset_range.end,
8817 reversed: false,
8818 goal: SelectionGoal::None,
8819 });
8820 });
8821 }
8822 }
8823
8824 new_selections.sort_by_key(|selection| selection.start);
8825 let mut ix = 0;
8826 while ix + 1 < new_selections.len() {
8827 let current_selection = &new_selections[ix];
8828 let next_selection = &new_selections[ix + 1];
8829 if current_selection.range().overlaps(&next_selection.range()) {
8830 if current_selection.id < next_selection.id {
8831 new_selections.remove(ix + 1);
8832 } else {
8833 new_selections.remove(ix);
8834 }
8835 } else {
8836 ix += 1;
8837 }
8838 }
8839
8840 let reversed = self.selections.oldest::<usize>(cx).reversed;
8841
8842 for selection in new_selections.iter_mut() {
8843 selection.reversed = reversed;
8844 }
8845
8846 select_next_state.done = true;
8847 self.unfold_ranges(
8848 &new_selections
8849 .iter()
8850 .map(|selection| selection.range())
8851 .collect::<Vec<_>>(),
8852 false,
8853 false,
8854 cx,
8855 );
8856 self.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
8857 selections.select(new_selections)
8858 });
8859
8860 Ok(())
8861 }
8862
8863 pub fn select_next(
8864 &mut self,
8865 action: &SelectNext,
8866 window: &mut Window,
8867 cx: &mut Context<Self>,
8868 ) -> Result<()> {
8869 self.push_to_selection_history();
8870 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8871 self.select_next_match_internal(
8872 &display_map,
8873 action.replace_newest,
8874 Some(Autoscroll::newest()),
8875 window,
8876 cx,
8877 )?;
8878 Ok(())
8879 }
8880
8881 pub fn select_previous(
8882 &mut self,
8883 action: &SelectPrevious,
8884 window: &mut Window,
8885 cx: &mut Context<Self>,
8886 ) -> Result<()> {
8887 self.push_to_selection_history();
8888 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8889 let buffer = &display_map.buffer_snapshot;
8890 let mut selections = self.selections.all::<usize>(cx);
8891 if let Some(mut select_prev_state) = self.select_prev_state.take() {
8892 let query = &select_prev_state.query;
8893 if !select_prev_state.done {
8894 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
8895 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
8896 let mut next_selected_range = None;
8897 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
8898 let bytes_before_last_selection =
8899 buffer.reversed_bytes_in_range(0..last_selection.start);
8900 let bytes_after_first_selection =
8901 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
8902 let query_matches = query
8903 .stream_find_iter(bytes_before_last_selection)
8904 .map(|result| (last_selection.start, result))
8905 .chain(
8906 query
8907 .stream_find_iter(bytes_after_first_selection)
8908 .map(|result| (buffer.len(), result)),
8909 );
8910 for (end_offset, query_match) in query_matches {
8911 let query_match = query_match.unwrap(); // can only fail due to I/O
8912 let offset_range =
8913 end_offset - query_match.end()..end_offset - query_match.start();
8914 let display_range = offset_range.start.to_display_point(&display_map)
8915 ..offset_range.end.to_display_point(&display_map);
8916
8917 if !select_prev_state.wordwise
8918 || (!movement::is_inside_word(&display_map, display_range.start)
8919 && !movement::is_inside_word(&display_map, display_range.end))
8920 {
8921 next_selected_range = Some(offset_range);
8922 break;
8923 }
8924 }
8925
8926 if let Some(next_selected_range) = next_selected_range {
8927 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
8928 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
8929 if action.replace_newest {
8930 s.delete(s.newest_anchor().id);
8931 }
8932 s.insert_range(next_selected_range);
8933 });
8934 } else {
8935 select_prev_state.done = true;
8936 }
8937 }
8938
8939 self.select_prev_state = Some(select_prev_state);
8940 } else {
8941 let mut only_carets = true;
8942 let mut same_text_selected = true;
8943 let mut selected_text = None;
8944
8945 let mut selections_iter = selections.iter().peekable();
8946 while let Some(selection) = selections_iter.next() {
8947 if selection.start != selection.end {
8948 only_carets = false;
8949 }
8950
8951 if same_text_selected {
8952 if selected_text.is_none() {
8953 selected_text =
8954 Some(buffer.text_for_range(selection.range()).collect::<String>());
8955 }
8956
8957 if let Some(next_selection) = selections_iter.peek() {
8958 if next_selection.range().len() == selection.range().len() {
8959 let next_selected_text = buffer
8960 .text_for_range(next_selection.range())
8961 .collect::<String>();
8962 if Some(next_selected_text) != selected_text {
8963 same_text_selected = false;
8964 selected_text = None;
8965 }
8966 } else {
8967 same_text_selected = false;
8968 selected_text = None;
8969 }
8970 }
8971 }
8972 }
8973
8974 if only_carets {
8975 for selection in &mut selections {
8976 let word_range = movement::surrounding_word(
8977 &display_map,
8978 selection.start.to_display_point(&display_map),
8979 );
8980 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
8981 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
8982 selection.goal = SelectionGoal::None;
8983 selection.reversed = false;
8984 }
8985 if selections.len() == 1 {
8986 let selection = selections
8987 .last()
8988 .expect("ensured that there's only one selection");
8989 let query = buffer
8990 .text_for_range(selection.start..selection.end)
8991 .collect::<String>();
8992 let is_empty = query.is_empty();
8993 let select_state = SelectNextState {
8994 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
8995 wordwise: true,
8996 done: is_empty,
8997 };
8998 self.select_prev_state = Some(select_state);
8999 } else {
9000 self.select_prev_state = None;
9001 }
9002
9003 self.unfold_ranges(
9004 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
9005 false,
9006 true,
9007 cx,
9008 );
9009 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
9010 s.select(selections);
9011 });
9012 } else if let Some(selected_text) = selected_text {
9013 self.select_prev_state = Some(SelectNextState {
9014 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
9015 wordwise: false,
9016 done: false,
9017 });
9018 self.select_previous(action, window, cx)?;
9019 }
9020 }
9021 Ok(())
9022 }
9023
9024 pub fn toggle_comments(
9025 &mut self,
9026 action: &ToggleComments,
9027 window: &mut Window,
9028 cx: &mut Context<Self>,
9029 ) {
9030 if self.read_only(cx) {
9031 return;
9032 }
9033 let text_layout_details = &self.text_layout_details(window);
9034 self.transact(window, cx, |this, window, cx| {
9035 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
9036 let mut edits = Vec::new();
9037 let mut selection_edit_ranges = Vec::new();
9038 let mut last_toggled_row = None;
9039 let snapshot = this.buffer.read(cx).read(cx);
9040 let empty_str: Arc<str> = Arc::default();
9041 let mut suffixes_inserted = Vec::new();
9042 let ignore_indent = action.ignore_indent;
9043
9044 fn comment_prefix_range(
9045 snapshot: &MultiBufferSnapshot,
9046 row: MultiBufferRow,
9047 comment_prefix: &str,
9048 comment_prefix_whitespace: &str,
9049 ignore_indent: bool,
9050 ) -> Range<Point> {
9051 let indent_size = if ignore_indent {
9052 0
9053 } else {
9054 snapshot.indent_size_for_line(row).len
9055 };
9056
9057 let start = Point::new(row.0, indent_size);
9058
9059 let mut line_bytes = snapshot
9060 .bytes_in_range(start..snapshot.max_point())
9061 .flatten()
9062 .copied();
9063
9064 // If this line currently begins with the line comment prefix, then record
9065 // the range containing the prefix.
9066 if line_bytes
9067 .by_ref()
9068 .take(comment_prefix.len())
9069 .eq(comment_prefix.bytes())
9070 {
9071 // Include any whitespace that matches the comment prefix.
9072 let matching_whitespace_len = line_bytes
9073 .zip(comment_prefix_whitespace.bytes())
9074 .take_while(|(a, b)| a == b)
9075 .count() as u32;
9076 let end = Point::new(
9077 start.row,
9078 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
9079 );
9080 start..end
9081 } else {
9082 start..start
9083 }
9084 }
9085
9086 fn comment_suffix_range(
9087 snapshot: &MultiBufferSnapshot,
9088 row: MultiBufferRow,
9089 comment_suffix: &str,
9090 comment_suffix_has_leading_space: bool,
9091 ) -> Range<Point> {
9092 let end = Point::new(row.0, snapshot.line_len(row));
9093 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
9094
9095 let mut line_end_bytes = snapshot
9096 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
9097 .flatten()
9098 .copied();
9099
9100 let leading_space_len = if suffix_start_column > 0
9101 && line_end_bytes.next() == Some(b' ')
9102 && comment_suffix_has_leading_space
9103 {
9104 1
9105 } else {
9106 0
9107 };
9108
9109 // If this line currently begins with the line comment prefix, then record
9110 // the range containing the prefix.
9111 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
9112 let start = Point::new(end.row, suffix_start_column - leading_space_len);
9113 start..end
9114 } else {
9115 end..end
9116 }
9117 }
9118
9119 // TODO: Handle selections that cross excerpts
9120 for selection in &mut selections {
9121 let start_column = snapshot
9122 .indent_size_for_line(MultiBufferRow(selection.start.row))
9123 .len;
9124 let language = if let Some(language) =
9125 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
9126 {
9127 language
9128 } else {
9129 continue;
9130 };
9131
9132 selection_edit_ranges.clear();
9133
9134 // If multiple selections contain a given row, avoid processing that
9135 // row more than once.
9136 let mut start_row = MultiBufferRow(selection.start.row);
9137 if last_toggled_row == Some(start_row) {
9138 start_row = start_row.next_row();
9139 }
9140 let end_row =
9141 if selection.end.row > selection.start.row && selection.end.column == 0 {
9142 MultiBufferRow(selection.end.row - 1)
9143 } else {
9144 MultiBufferRow(selection.end.row)
9145 };
9146 last_toggled_row = Some(end_row);
9147
9148 if start_row > end_row {
9149 continue;
9150 }
9151
9152 // If the language has line comments, toggle those.
9153 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
9154
9155 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
9156 if ignore_indent {
9157 full_comment_prefixes = full_comment_prefixes
9158 .into_iter()
9159 .map(|s| Arc::from(s.trim_end()))
9160 .collect();
9161 }
9162
9163 if !full_comment_prefixes.is_empty() {
9164 let first_prefix = full_comment_prefixes
9165 .first()
9166 .expect("prefixes is non-empty");
9167 let prefix_trimmed_lengths = full_comment_prefixes
9168 .iter()
9169 .map(|p| p.trim_end_matches(' ').len())
9170 .collect::<SmallVec<[usize; 4]>>();
9171
9172 let mut all_selection_lines_are_comments = true;
9173
9174 for row in start_row.0..=end_row.0 {
9175 let row = MultiBufferRow(row);
9176 if start_row < end_row && snapshot.is_line_blank(row) {
9177 continue;
9178 }
9179
9180 let prefix_range = full_comment_prefixes
9181 .iter()
9182 .zip(prefix_trimmed_lengths.iter().copied())
9183 .map(|(prefix, trimmed_prefix_len)| {
9184 comment_prefix_range(
9185 snapshot.deref(),
9186 row,
9187 &prefix[..trimmed_prefix_len],
9188 &prefix[trimmed_prefix_len..],
9189 ignore_indent,
9190 )
9191 })
9192 .max_by_key(|range| range.end.column - range.start.column)
9193 .expect("prefixes is non-empty");
9194
9195 if prefix_range.is_empty() {
9196 all_selection_lines_are_comments = false;
9197 }
9198
9199 selection_edit_ranges.push(prefix_range);
9200 }
9201
9202 if all_selection_lines_are_comments {
9203 edits.extend(
9204 selection_edit_ranges
9205 .iter()
9206 .cloned()
9207 .map(|range| (range, empty_str.clone())),
9208 );
9209 } else {
9210 let min_column = selection_edit_ranges
9211 .iter()
9212 .map(|range| range.start.column)
9213 .min()
9214 .unwrap_or(0);
9215 edits.extend(selection_edit_ranges.iter().map(|range| {
9216 let position = Point::new(range.start.row, min_column);
9217 (position..position, first_prefix.clone())
9218 }));
9219 }
9220 } else if let Some((full_comment_prefix, comment_suffix)) =
9221 language.block_comment_delimiters()
9222 {
9223 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
9224 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
9225 let prefix_range = comment_prefix_range(
9226 snapshot.deref(),
9227 start_row,
9228 comment_prefix,
9229 comment_prefix_whitespace,
9230 ignore_indent,
9231 );
9232 let suffix_range = comment_suffix_range(
9233 snapshot.deref(),
9234 end_row,
9235 comment_suffix.trim_start_matches(' '),
9236 comment_suffix.starts_with(' '),
9237 );
9238
9239 if prefix_range.is_empty() || suffix_range.is_empty() {
9240 edits.push((
9241 prefix_range.start..prefix_range.start,
9242 full_comment_prefix.clone(),
9243 ));
9244 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
9245 suffixes_inserted.push((end_row, comment_suffix.len()));
9246 } else {
9247 edits.push((prefix_range, empty_str.clone()));
9248 edits.push((suffix_range, empty_str.clone()));
9249 }
9250 } else {
9251 continue;
9252 }
9253 }
9254
9255 drop(snapshot);
9256 this.buffer.update(cx, |buffer, cx| {
9257 buffer.edit(edits, None, cx);
9258 });
9259
9260 // Adjust selections so that they end before any comment suffixes that
9261 // were inserted.
9262 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
9263 let mut selections = this.selections.all::<Point>(cx);
9264 let snapshot = this.buffer.read(cx).read(cx);
9265 for selection in &mut selections {
9266 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
9267 match row.cmp(&MultiBufferRow(selection.end.row)) {
9268 Ordering::Less => {
9269 suffixes_inserted.next();
9270 continue;
9271 }
9272 Ordering::Greater => break,
9273 Ordering::Equal => {
9274 if selection.end.column == snapshot.line_len(row) {
9275 if selection.is_empty() {
9276 selection.start.column -= suffix_len as u32;
9277 }
9278 selection.end.column -= suffix_len as u32;
9279 }
9280 break;
9281 }
9282 }
9283 }
9284 }
9285
9286 drop(snapshot);
9287 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9288 s.select(selections)
9289 });
9290
9291 let selections = this.selections.all::<Point>(cx);
9292 let selections_on_single_row = selections.windows(2).all(|selections| {
9293 selections[0].start.row == selections[1].start.row
9294 && selections[0].end.row == selections[1].end.row
9295 && selections[0].start.row == selections[0].end.row
9296 });
9297 let selections_selecting = selections
9298 .iter()
9299 .any(|selection| selection.start != selection.end);
9300 let advance_downwards = action.advance_downwards
9301 && selections_on_single_row
9302 && !selections_selecting
9303 && !matches!(this.mode, EditorMode::SingleLine { .. });
9304
9305 if advance_downwards {
9306 let snapshot = this.buffer.read(cx).snapshot(cx);
9307
9308 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9309 s.move_cursors_with(|display_snapshot, display_point, _| {
9310 let mut point = display_point.to_point(display_snapshot);
9311 point.row += 1;
9312 point = snapshot.clip_point(point, Bias::Left);
9313 let display_point = point.to_display_point(display_snapshot);
9314 let goal = SelectionGoal::HorizontalPosition(
9315 display_snapshot
9316 .x_for_display_point(display_point, text_layout_details)
9317 .into(),
9318 );
9319 (display_point, goal)
9320 })
9321 });
9322 }
9323 });
9324 }
9325
9326 pub fn select_enclosing_symbol(
9327 &mut self,
9328 _: &SelectEnclosingSymbol,
9329 window: &mut Window,
9330 cx: &mut Context<Self>,
9331 ) {
9332 let buffer = self.buffer.read(cx).snapshot(cx);
9333 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
9334
9335 fn update_selection(
9336 selection: &Selection<usize>,
9337 buffer_snap: &MultiBufferSnapshot,
9338 ) -> Option<Selection<usize>> {
9339 let cursor = selection.head();
9340 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
9341 for symbol in symbols.iter().rev() {
9342 let start = symbol.range.start.to_offset(buffer_snap);
9343 let end = symbol.range.end.to_offset(buffer_snap);
9344 let new_range = start..end;
9345 if start < selection.start || end > selection.end {
9346 return Some(Selection {
9347 id: selection.id,
9348 start: new_range.start,
9349 end: new_range.end,
9350 goal: SelectionGoal::None,
9351 reversed: selection.reversed,
9352 });
9353 }
9354 }
9355 None
9356 }
9357
9358 let mut selected_larger_symbol = false;
9359 let new_selections = old_selections
9360 .iter()
9361 .map(|selection| match update_selection(selection, &buffer) {
9362 Some(new_selection) => {
9363 if new_selection.range() != selection.range() {
9364 selected_larger_symbol = true;
9365 }
9366 new_selection
9367 }
9368 None => selection.clone(),
9369 })
9370 .collect::<Vec<_>>();
9371
9372 if selected_larger_symbol {
9373 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9374 s.select(new_selections);
9375 });
9376 }
9377 }
9378
9379 pub fn select_larger_syntax_node(
9380 &mut self,
9381 _: &SelectLargerSyntaxNode,
9382 window: &mut Window,
9383 cx: &mut Context<Self>,
9384 ) {
9385 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9386 let buffer = self.buffer.read(cx).snapshot(cx);
9387 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
9388
9389 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
9390 let mut selected_larger_node = false;
9391 let new_selections = old_selections
9392 .iter()
9393 .map(|selection| {
9394 let old_range = selection.start..selection.end;
9395 let mut new_range = old_range.clone();
9396 let mut new_node = None;
9397 while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone())
9398 {
9399 new_node = Some(node);
9400 new_range = containing_range;
9401 if !display_map.intersects_fold(new_range.start)
9402 && !display_map.intersects_fold(new_range.end)
9403 {
9404 break;
9405 }
9406 }
9407
9408 if let Some(node) = new_node {
9409 // Log the ancestor, to support using this action as a way to explore TreeSitter
9410 // nodes. Parent and grandparent are also logged because this operation will not
9411 // visit nodes that have the same range as their parent.
9412 log::info!("Node: {node:?}");
9413 let parent = node.parent();
9414 log::info!("Parent: {parent:?}");
9415 let grandparent = parent.and_then(|x| x.parent());
9416 log::info!("Grandparent: {grandparent:?}");
9417 }
9418
9419 selected_larger_node |= new_range != old_range;
9420 Selection {
9421 id: selection.id,
9422 start: new_range.start,
9423 end: new_range.end,
9424 goal: SelectionGoal::None,
9425 reversed: selection.reversed,
9426 }
9427 })
9428 .collect::<Vec<_>>();
9429
9430 if selected_larger_node {
9431 stack.push(old_selections);
9432 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9433 s.select(new_selections);
9434 });
9435 }
9436 self.select_larger_syntax_node_stack = stack;
9437 }
9438
9439 pub fn select_smaller_syntax_node(
9440 &mut self,
9441 _: &SelectSmallerSyntaxNode,
9442 window: &mut Window,
9443 cx: &mut Context<Self>,
9444 ) {
9445 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
9446 if let Some(selections) = stack.pop() {
9447 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9448 s.select(selections.to_vec());
9449 });
9450 }
9451 self.select_larger_syntax_node_stack = stack;
9452 }
9453
9454 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
9455 if !EditorSettings::get_global(cx).gutter.runnables {
9456 self.clear_tasks();
9457 return Task::ready(());
9458 }
9459 let project = self.project.as_ref().map(Entity::downgrade);
9460 cx.spawn_in(window, |this, mut cx| async move {
9461 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
9462 let Some(project) = project.and_then(|p| p.upgrade()) else {
9463 return;
9464 };
9465 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
9466 this.display_map.update(cx, |map, cx| map.snapshot(cx))
9467 }) else {
9468 return;
9469 };
9470
9471 let hide_runnables = project
9472 .update(&mut cx, |project, cx| {
9473 // Do not display any test indicators in non-dev server remote projects.
9474 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
9475 })
9476 .unwrap_or(true);
9477 if hide_runnables {
9478 return;
9479 }
9480 let new_rows =
9481 cx.background_executor()
9482 .spawn({
9483 let snapshot = display_snapshot.clone();
9484 async move {
9485 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
9486 }
9487 })
9488 .await;
9489
9490 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
9491 this.update(&mut cx, |this, _| {
9492 this.clear_tasks();
9493 for (key, value) in rows {
9494 this.insert_tasks(key, value);
9495 }
9496 })
9497 .ok();
9498 })
9499 }
9500 fn fetch_runnable_ranges(
9501 snapshot: &DisplaySnapshot,
9502 range: Range<Anchor>,
9503 ) -> Vec<language::RunnableRange> {
9504 snapshot.buffer_snapshot.runnable_ranges(range).collect()
9505 }
9506
9507 fn runnable_rows(
9508 project: Entity<Project>,
9509 snapshot: DisplaySnapshot,
9510 runnable_ranges: Vec<RunnableRange>,
9511 mut cx: AsyncWindowContext,
9512 ) -> Vec<((BufferId, u32), RunnableTasks)> {
9513 runnable_ranges
9514 .into_iter()
9515 .filter_map(|mut runnable| {
9516 let tasks = cx
9517 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
9518 .ok()?;
9519 if tasks.is_empty() {
9520 return None;
9521 }
9522
9523 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
9524
9525 let row = snapshot
9526 .buffer_snapshot
9527 .buffer_line_for_row(MultiBufferRow(point.row))?
9528 .1
9529 .start
9530 .row;
9531
9532 let context_range =
9533 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
9534 Some((
9535 (runnable.buffer_id, row),
9536 RunnableTasks {
9537 templates: tasks,
9538 offset: MultiBufferOffset(runnable.run_range.start),
9539 context_range,
9540 column: point.column,
9541 extra_variables: runnable.extra_captures,
9542 },
9543 ))
9544 })
9545 .collect()
9546 }
9547
9548 fn templates_with_tags(
9549 project: &Entity<Project>,
9550 runnable: &mut Runnable,
9551 cx: &mut App,
9552 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
9553 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
9554 let (worktree_id, file) = project
9555 .buffer_for_id(runnable.buffer, cx)
9556 .and_then(|buffer| buffer.read(cx).file())
9557 .map(|file| (file.worktree_id(cx), file.clone()))
9558 .unzip();
9559
9560 (
9561 project.task_store().read(cx).task_inventory().cloned(),
9562 worktree_id,
9563 file,
9564 )
9565 });
9566
9567 let tags = mem::take(&mut runnable.tags);
9568 let mut tags: Vec<_> = tags
9569 .into_iter()
9570 .flat_map(|tag| {
9571 let tag = tag.0.clone();
9572 inventory
9573 .as_ref()
9574 .into_iter()
9575 .flat_map(|inventory| {
9576 inventory.read(cx).list_tasks(
9577 file.clone(),
9578 Some(runnable.language.clone()),
9579 worktree_id,
9580 cx,
9581 )
9582 })
9583 .filter(move |(_, template)| {
9584 template.tags.iter().any(|source_tag| source_tag == &tag)
9585 })
9586 })
9587 .sorted_by_key(|(kind, _)| kind.to_owned())
9588 .collect();
9589 if let Some((leading_tag_source, _)) = tags.first() {
9590 // Strongest source wins; if we have worktree tag binding, prefer that to
9591 // global and language bindings;
9592 // if we have a global binding, prefer that to language binding.
9593 let first_mismatch = tags
9594 .iter()
9595 .position(|(tag_source, _)| tag_source != leading_tag_source);
9596 if let Some(index) = first_mismatch {
9597 tags.truncate(index);
9598 }
9599 }
9600
9601 tags
9602 }
9603
9604 pub fn move_to_enclosing_bracket(
9605 &mut self,
9606 _: &MoveToEnclosingBracket,
9607 window: &mut Window,
9608 cx: &mut Context<Self>,
9609 ) {
9610 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9611 s.move_offsets_with(|snapshot, selection| {
9612 let Some(enclosing_bracket_ranges) =
9613 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
9614 else {
9615 return;
9616 };
9617
9618 let mut best_length = usize::MAX;
9619 let mut best_inside = false;
9620 let mut best_in_bracket_range = false;
9621 let mut best_destination = None;
9622 for (open, close) in enclosing_bracket_ranges {
9623 let close = close.to_inclusive();
9624 let length = close.end() - open.start;
9625 let inside = selection.start >= open.end && selection.end <= *close.start();
9626 let in_bracket_range = open.to_inclusive().contains(&selection.head())
9627 || close.contains(&selection.head());
9628
9629 // If best is next to a bracket and current isn't, skip
9630 if !in_bracket_range && best_in_bracket_range {
9631 continue;
9632 }
9633
9634 // Prefer smaller lengths unless best is inside and current isn't
9635 if length > best_length && (best_inside || !inside) {
9636 continue;
9637 }
9638
9639 best_length = length;
9640 best_inside = inside;
9641 best_in_bracket_range = in_bracket_range;
9642 best_destination = Some(
9643 if close.contains(&selection.start) && close.contains(&selection.end) {
9644 if inside {
9645 open.end
9646 } else {
9647 open.start
9648 }
9649 } else if inside {
9650 *close.start()
9651 } else {
9652 *close.end()
9653 },
9654 );
9655 }
9656
9657 if let Some(destination) = best_destination {
9658 selection.collapse_to(destination, SelectionGoal::None);
9659 }
9660 })
9661 });
9662 }
9663
9664 pub fn undo_selection(
9665 &mut self,
9666 _: &UndoSelection,
9667 window: &mut Window,
9668 cx: &mut Context<Self>,
9669 ) {
9670 self.end_selection(window, cx);
9671 self.selection_history.mode = SelectionHistoryMode::Undoing;
9672 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
9673 self.change_selections(None, window, cx, |s| {
9674 s.select_anchors(entry.selections.to_vec())
9675 });
9676 self.select_next_state = entry.select_next_state;
9677 self.select_prev_state = entry.select_prev_state;
9678 self.add_selections_state = entry.add_selections_state;
9679 self.request_autoscroll(Autoscroll::newest(), cx);
9680 }
9681 self.selection_history.mode = SelectionHistoryMode::Normal;
9682 }
9683
9684 pub fn redo_selection(
9685 &mut self,
9686 _: &RedoSelection,
9687 window: &mut Window,
9688 cx: &mut Context<Self>,
9689 ) {
9690 self.end_selection(window, cx);
9691 self.selection_history.mode = SelectionHistoryMode::Redoing;
9692 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
9693 self.change_selections(None, window, cx, |s| {
9694 s.select_anchors(entry.selections.to_vec())
9695 });
9696 self.select_next_state = entry.select_next_state;
9697 self.select_prev_state = entry.select_prev_state;
9698 self.add_selections_state = entry.add_selections_state;
9699 self.request_autoscroll(Autoscroll::newest(), cx);
9700 }
9701 self.selection_history.mode = SelectionHistoryMode::Normal;
9702 }
9703
9704 pub fn expand_excerpts(
9705 &mut self,
9706 action: &ExpandExcerpts,
9707 _: &mut Window,
9708 cx: &mut Context<Self>,
9709 ) {
9710 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
9711 }
9712
9713 pub fn expand_excerpts_down(
9714 &mut self,
9715 action: &ExpandExcerptsDown,
9716 _: &mut Window,
9717 cx: &mut Context<Self>,
9718 ) {
9719 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
9720 }
9721
9722 pub fn expand_excerpts_up(
9723 &mut self,
9724 action: &ExpandExcerptsUp,
9725 _: &mut Window,
9726 cx: &mut Context<Self>,
9727 ) {
9728 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
9729 }
9730
9731 pub fn expand_excerpts_for_direction(
9732 &mut self,
9733 lines: u32,
9734 direction: ExpandExcerptDirection,
9735
9736 cx: &mut Context<Self>,
9737 ) {
9738 let selections = self.selections.disjoint_anchors();
9739
9740 let lines = if lines == 0 {
9741 EditorSettings::get_global(cx).expand_excerpt_lines
9742 } else {
9743 lines
9744 };
9745
9746 self.buffer.update(cx, |buffer, cx| {
9747 let snapshot = buffer.snapshot(cx);
9748 let mut excerpt_ids = selections
9749 .iter()
9750 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
9751 .collect::<Vec<_>>();
9752 excerpt_ids.sort();
9753 excerpt_ids.dedup();
9754 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
9755 })
9756 }
9757
9758 pub fn expand_excerpt(
9759 &mut self,
9760 excerpt: ExcerptId,
9761 direction: ExpandExcerptDirection,
9762 cx: &mut Context<Self>,
9763 ) {
9764 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
9765 self.buffer.update(cx, |buffer, cx| {
9766 buffer.expand_excerpts([excerpt], lines, direction, cx)
9767 })
9768 }
9769
9770 pub fn go_to_singleton_buffer_point(
9771 &mut self,
9772 point: Point,
9773 window: &mut Window,
9774 cx: &mut Context<Self>,
9775 ) {
9776 self.go_to_singleton_buffer_range(point..point, window, cx);
9777 }
9778
9779 pub fn go_to_singleton_buffer_range(
9780 &mut self,
9781 range: Range<Point>,
9782 window: &mut Window,
9783 cx: &mut Context<Self>,
9784 ) {
9785 let multibuffer = self.buffer().read(cx);
9786 let Some(buffer) = multibuffer.as_singleton() else {
9787 return;
9788 };
9789 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
9790 return;
9791 };
9792 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
9793 return;
9794 };
9795 self.change_selections(Some(Autoscroll::center()), window, cx, |s| {
9796 s.select_anchor_ranges([start..end])
9797 });
9798 }
9799
9800 fn go_to_diagnostic(
9801 &mut self,
9802 _: &GoToDiagnostic,
9803 window: &mut Window,
9804 cx: &mut Context<Self>,
9805 ) {
9806 self.go_to_diagnostic_impl(Direction::Next, window, cx)
9807 }
9808
9809 fn go_to_prev_diagnostic(
9810 &mut self,
9811 _: &GoToPrevDiagnostic,
9812 window: &mut Window,
9813 cx: &mut Context<Self>,
9814 ) {
9815 self.go_to_diagnostic_impl(Direction::Prev, window, cx)
9816 }
9817
9818 pub fn go_to_diagnostic_impl(
9819 &mut self,
9820 direction: Direction,
9821 window: &mut Window,
9822 cx: &mut Context<Self>,
9823 ) {
9824 let buffer = self.buffer.read(cx).snapshot(cx);
9825 let selection = self.selections.newest::<usize>(cx);
9826
9827 // If there is an active Diagnostic Popover jump to its diagnostic instead.
9828 if direction == Direction::Next {
9829 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
9830 let Some(buffer_id) = popover.local_diagnostic.range.start.buffer_id else {
9831 return;
9832 };
9833 self.activate_diagnostics(
9834 buffer_id,
9835 popover.local_diagnostic.diagnostic.group_id,
9836 window,
9837 cx,
9838 );
9839 if let Some(active_diagnostics) = self.active_diagnostics.as_ref() {
9840 let primary_range_start = active_diagnostics.primary_range.start;
9841 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9842 let mut new_selection = s.newest_anchor().clone();
9843 new_selection.collapse_to(primary_range_start, SelectionGoal::None);
9844 s.select_anchors(vec![new_selection.clone()]);
9845 });
9846 self.refresh_inline_completion(false, true, window, cx);
9847 }
9848 return;
9849 }
9850 }
9851
9852 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
9853 active_diagnostics
9854 .primary_range
9855 .to_offset(&buffer)
9856 .to_inclusive()
9857 });
9858 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
9859 if active_primary_range.contains(&selection.head()) {
9860 *active_primary_range.start()
9861 } else {
9862 selection.head()
9863 }
9864 } else {
9865 selection.head()
9866 };
9867 let snapshot = self.snapshot(window, cx);
9868 loop {
9869 let mut diagnostics;
9870 if direction == Direction::Prev {
9871 diagnostics = buffer
9872 .diagnostics_in_range::<_, usize>(0..search_start)
9873 .collect::<Vec<_>>();
9874 diagnostics.reverse();
9875 } else {
9876 diagnostics = buffer
9877 .diagnostics_in_range::<_, usize>(search_start..buffer.len())
9878 .collect::<Vec<_>>();
9879 };
9880 let group = diagnostics
9881 .into_iter()
9882 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
9883 // relies on diagnostics_in_range to return diagnostics with the same starting range to
9884 // be sorted in a stable way
9885 // skip until we are at current active diagnostic, if it exists
9886 .skip_while(|entry| {
9887 let is_in_range = match direction {
9888 Direction::Prev => entry.range.end > search_start,
9889 Direction::Next => entry.range.start < search_start,
9890 };
9891 is_in_range
9892 && self
9893 .active_diagnostics
9894 .as_ref()
9895 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
9896 })
9897 .find_map(|entry| {
9898 if entry.diagnostic.is_primary
9899 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
9900 && entry.range.start != entry.range.end
9901 // if we match with the active diagnostic, skip it
9902 && Some(entry.diagnostic.group_id)
9903 != self.active_diagnostics.as_ref().map(|d| d.group_id)
9904 {
9905 Some((entry.range, entry.diagnostic.group_id))
9906 } else {
9907 None
9908 }
9909 });
9910
9911 if let Some((primary_range, group_id)) = group {
9912 let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
9913 return;
9914 };
9915 self.activate_diagnostics(buffer_id, group_id, window, cx);
9916 if self.active_diagnostics.is_some() {
9917 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9918 s.select(vec![Selection {
9919 id: selection.id,
9920 start: primary_range.start,
9921 end: primary_range.start,
9922 reversed: false,
9923 goal: SelectionGoal::None,
9924 }]);
9925 });
9926 self.refresh_inline_completion(false, true, window, cx);
9927 }
9928 break;
9929 } else {
9930 // Cycle around to the start of the buffer, potentially moving back to the start of
9931 // the currently active diagnostic.
9932 active_primary_range.take();
9933 if direction == Direction::Prev {
9934 if search_start == buffer.len() {
9935 break;
9936 } else {
9937 search_start = buffer.len();
9938 }
9939 } else if search_start == 0 {
9940 break;
9941 } else {
9942 search_start = 0;
9943 }
9944 }
9945 }
9946 }
9947
9948 fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
9949 let snapshot = self.snapshot(window, cx);
9950 let selection = self.selections.newest::<Point>(cx);
9951 self.go_to_hunk_after_position(&snapshot, selection.head(), window, cx);
9952 }
9953
9954 fn go_to_hunk_after_position(
9955 &mut self,
9956 snapshot: &EditorSnapshot,
9957 position: Point,
9958 window: &mut Window,
9959 cx: &mut Context<Editor>,
9960 ) -> Option<MultiBufferDiffHunk> {
9961 let mut hunk = snapshot
9962 .buffer_snapshot
9963 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
9964 .find(|hunk| hunk.row_range.start.0 > position.row);
9965 if hunk.is_none() {
9966 hunk = snapshot
9967 .buffer_snapshot
9968 .diff_hunks_in_range(Point::zero()..position)
9969 .find(|hunk| hunk.row_range.end.0 < position.row)
9970 }
9971 if let Some(hunk) = &hunk {
9972 let destination = Point::new(hunk.row_range.start.0, 0);
9973 self.unfold_ranges(&[destination..destination], false, false, cx);
9974 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9975 s.select_ranges(vec![destination..destination]);
9976 });
9977 }
9978
9979 hunk
9980 }
9981
9982 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, window: &mut Window, cx: &mut Context<Self>) {
9983 let snapshot = self.snapshot(window, cx);
9984 let selection = self.selections.newest::<Point>(cx);
9985 self.go_to_hunk_before_position(&snapshot, selection.head(), window, cx);
9986 }
9987
9988 fn go_to_hunk_before_position(
9989 &mut self,
9990 snapshot: &EditorSnapshot,
9991 position: Point,
9992 window: &mut Window,
9993 cx: &mut Context<Editor>,
9994 ) -> Option<MultiBufferDiffHunk> {
9995 let mut hunk = snapshot.buffer_snapshot.diff_hunk_before(position);
9996 if hunk.is_none() {
9997 hunk = snapshot.buffer_snapshot.diff_hunk_before(Point::MAX);
9998 }
9999 if let Some(hunk) = &hunk {
10000 let destination = Point::new(hunk.row_range.start.0, 0);
10001 self.unfold_ranges(&[destination..destination], false, false, cx);
10002 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10003 s.select_ranges(vec![destination..destination]);
10004 });
10005 }
10006
10007 hunk
10008 }
10009
10010 pub fn go_to_definition(
10011 &mut self,
10012 _: &GoToDefinition,
10013 window: &mut Window,
10014 cx: &mut Context<Self>,
10015 ) -> Task<Result<Navigated>> {
10016 let definition =
10017 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
10018 cx.spawn_in(window, |editor, mut cx| async move {
10019 if definition.await? == Navigated::Yes {
10020 return Ok(Navigated::Yes);
10021 }
10022 match editor.update_in(&mut cx, |editor, window, cx| {
10023 editor.find_all_references(&FindAllReferences, window, cx)
10024 })? {
10025 Some(references) => references.await,
10026 None => Ok(Navigated::No),
10027 }
10028 })
10029 }
10030
10031 pub fn go_to_declaration(
10032 &mut self,
10033 _: &GoToDeclaration,
10034 window: &mut Window,
10035 cx: &mut Context<Self>,
10036 ) -> Task<Result<Navigated>> {
10037 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
10038 }
10039
10040 pub fn go_to_declaration_split(
10041 &mut self,
10042 _: &GoToDeclaration,
10043 window: &mut Window,
10044 cx: &mut Context<Self>,
10045 ) -> Task<Result<Navigated>> {
10046 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
10047 }
10048
10049 pub fn go_to_implementation(
10050 &mut self,
10051 _: &GoToImplementation,
10052 window: &mut Window,
10053 cx: &mut Context<Self>,
10054 ) -> Task<Result<Navigated>> {
10055 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
10056 }
10057
10058 pub fn go_to_implementation_split(
10059 &mut self,
10060 _: &GoToImplementationSplit,
10061 window: &mut Window,
10062 cx: &mut Context<Self>,
10063 ) -> Task<Result<Navigated>> {
10064 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
10065 }
10066
10067 pub fn go_to_type_definition(
10068 &mut self,
10069 _: &GoToTypeDefinition,
10070 window: &mut Window,
10071 cx: &mut Context<Self>,
10072 ) -> Task<Result<Navigated>> {
10073 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
10074 }
10075
10076 pub fn go_to_definition_split(
10077 &mut self,
10078 _: &GoToDefinitionSplit,
10079 window: &mut Window,
10080 cx: &mut Context<Self>,
10081 ) -> Task<Result<Navigated>> {
10082 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
10083 }
10084
10085 pub fn go_to_type_definition_split(
10086 &mut self,
10087 _: &GoToTypeDefinitionSplit,
10088 window: &mut Window,
10089 cx: &mut Context<Self>,
10090 ) -> Task<Result<Navigated>> {
10091 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
10092 }
10093
10094 fn go_to_definition_of_kind(
10095 &mut self,
10096 kind: GotoDefinitionKind,
10097 split: bool,
10098 window: &mut Window,
10099 cx: &mut Context<Self>,
10100 ) -> Task<Result<Navigated>> {
10101 let Some(provider) = self.semantics_provider.clone() else {
10102 return Task::ready(Ok(Navigated::No));
10103 };
10104 let head = self.selections.newest::<usize>(cx).head();
10105 let buffer = self.buffer.read(cx);
10106 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
10107 text_anchor
10108 } else {
10109 return Task::ready(Ok(Navigated::No));
10110 };
10111
10112 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
10113 return Task::ready(Ok(Navigated::No));
10114 };
10115
10116 cx.spawn_in(window, |editor, mut cx| async move {
10117 let definitions = definitions.await?;
10118 let navigated = editor
10119 .update_in(&mut cx, |editor, window, cx| {
10120 editor.navigate_to_hover_links(
10121 Some(kind),
10122 definitions
10123 .into_iter()
10124 .filter(|location| {
10125 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
10126 })
10127 .map(HoverLink::Text)
10128 .collect::<Vec<_>>(),
10129 split,
10130 window,
10131 cx,
10132 )
10133 })?
10134 .await?;
10135 anyhow::Ok(navigated)
10136 })
10137 }
10138
10139 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
10140 let selection = self.selections.newest_anchor();
10141 let head = selection.head();
10142 let tail = selection.tail();
10143
10144 let Some((buffer, start_position)) =
10145 self.buffer.read(cx).text_anchor_for_position(head, cx)
10146 else {
10147 return;
10148 };
10149
10150 let end_position = if head != tail {
10151 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
10152 return;
10153 };
10154 Some(pos)
10155 } else {
10156 None
10157 };
10158
10159 let url_finder = cx.spawn_in(window, |editor, mut cx| async move {
10160 let url = if let Some(end_pos) = end_position {
10161 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
10162 } else {
10163 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
10164 };
10165
10166 if let Some(url) = url {
10167 editor.update(&mut cx, |_, cx| {
10168 cx.open_url(&url);
10169 })
10170 } else {
10171 Ok(())
10172 }
10173 });
10174
10175 url_finder.detach();
10176 }
10177
10178 pub fn open_selected_filename(
10179 &mut self,
10180 _: &OpenSelectedFilename,
10181 window: &mut Window,
10182 cx: &mut Context<Self>,
10183 ) {
10184 let Some(workspace) = self.workspace() else {
10185 return;
10186 };
10187
10188 let position = self.selections.newest_anchor().head();
10189
10190 let Some((buffer, buffer_position)) =
10191 self.buffer.read(cx).text_anchor_for_position(position, cx)
10192 else {
10193 return;
10194 };
10195
10196 let project = self.project.clone();
10197
10198 cx.spawn_in(window, |_, mut cx| async move {
10199 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
10200
10201 if let Some((_, path)) = result {
10202 workspace
10203 .update_in(&mut cx, |workspace, window, cx| {
10204 workspace.open_resolved_path(path, window, cx)
10205 })?
10206 .await?;
10207 }
10208 anyhow::Ok(())
10209 })
10210 .detach();
10211 }
10212
10213 pub(crate) fn navigate_to_hover_links(
10214 &mut self,
10215 kind: Option<GotoDefinitionKind>,
10216 mut definitions: Vec<HoverLink>,
10217 split: bool,
10218 window: &mut Window,
10219 cx: &mut Context<Editor>,
10220 ) -> Task<Result<Navigated>> {
10221 // If there is one definition, just open it directly
10222 if definitions.len() == 1 {
10223 let definition = definitions.pop().unwrap();
10224
10225 enum TargetTaskResult {
10226 Location(Option<Location>),
10227 AlreadyNavigated,
10228 }
10229
10230 let target_task = match definition {
10231 HoverLink::Text(link) => {
10232 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
10233 }
10234 HoverLink::InlayHint(lsp_location, server_id) => {
10235 let computation =
10236 self.compute_target_location(lsp_location, server_id, window, cx);
10237 cx.background_executor().spawn(async move {
10238 let location = computation.await?;
10239 Ok(TargetTaskResult::Location(location))
10240 })
10241 }
10242 HoverLink::Url(url) => {
10243 cx.open_url(&url);
10244 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
10245 }
10246 HoverLink::File(path) => {
10247 if let Some(workspace) = self.workspace() {
10248 cx.spawn_in(window, |_, mut cx| async move {
10249 workspace
10250 .update_in(&mut cx, |workspace, window, cx| {
10251 workspace.open_resolved_path(path, window, cx)
10252 })?
10253 .await
10254 .map(|_| TargetTaskResult::AlreadyNavigated)
10255 })
10256 } else {
10257 Task::ready(Ok(TargetTaskResult::Location(None)))
10258 }
10259 }
10260 };
10261 cx.spawn_in(window, |editor, mut cx| async move {
10262 let target = match target_task.await.context("target resolution task")? {
10263 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
10264 TargetTaskResult::Location(None) => return Ok(Navigated::No),
10265 TargetTaskResult::Location(Some(target)) => target,
10266 };
10267
10268 editor.update_in(&mut cx, |editor, window, cx| {
10269 let Some(workspace) = editor.workspace() else {
10270 return Navigated::No;
10271 };
10272 let pane = workspace.read(cx).active_pane().clone();
10273
10274 let range = target.range.to_point(target.buffer.read(cx));
10275 let range = editor.range_for_match(&range);
10276 let range = collapse_multiline_range(range);
10277
10278 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
10279 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
10280 } else {
10281 window.defer(cx, move |window, cx| {
10282 let target_editor: Entity<Self> =
10283 workspace.update(cx, |workspace, cx| {
10284 let pane = if split {
10285 workspace.adjacent_pane(window, cx)
10286 } else {
10287 workspace.active_pane().clone()
10288 };
10289
10290 workspace.open_project_item(
10291 pane,
10292 target.buffer.clone(),
10293 true,
10294 true,
10295 window,
10296 cx,
10297 )
10298 });
10299 target_editor.update(cx, |target_editor, cx| {
10300 // When selecting a definition in a different buffer, disable the nav history
10301 // to avoid creating a history entry at the previous cursor location.
10302 pane.update(cx, |pane, _| pane.disable_history());
10303 target_editor.go_to_singleton_buffer_range(range, window, cx);
10304 pane.update(cx, |pane, _| pane.enable_history());
10305 });
10306 });
10307 }
10308 Navigated::Yes
10309 })
10310 })
10311 } else if !definitions.is_empty() {
10312 cx.spawn_in(window, |editor, mut cx| async move {
10313 let (title, location_tasks, workspace) = editor
10314 .update_in(&mut cx, |editor, window, cx| {
10315 let tab_kind = match kind {
10316 Some(GotoDefinitionKind::Implementation) => "Implementations",
10317 _ => "Definitions",
10318 };
10319 let title = definitions
10320 .iter()
10321 .find_map(|definition| match definition {
10322 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
10323 let buffer = origin.buffer.read(cx);
10324 format!(
10325 "{} for {}",
10326 tab_kind,
10327 buffer
10328 .text_for_range(origin.range.clone())
10329 .collect::<String>()
10330 )
10331 }),
10332 HoverLink::InlayHint(_, _) => None,
10333 HoverLink::Url(_) => None,
10334 HoverLink::File(_) => None,
10335 })
10336 .unwrap_or(tab_kind.to_string());
10337 let location_tasks = definitions
10338 .into_iter()
10339 .map(|definition| match definition {
10340 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
10341 HoverLink::InlayHint(lsp_location, server_id) => editor
10342 .compute_target_location(lsp_location, server_id, window, cx),
10343 HoverLink::Url(_) => Task::ready(Ok(None)),
10344 HoverLink::File(_) => Task::ready(Ok(None)),
10345 })
10346 .collect::<Vec<_>>();
10347 (title, location_tasks, editor.workspace().clone())
10348 })
10349 .context("location tasks preparation")?;
10350
10351 let locations = future::join_all(location_tasks)
10352 .await
10353 .into_iter()
10354 .filter_map(|location| location.transpose())
10355 .collect::<Result<_>>()
10356 .context("location tasks")?;
10357
10358 let Some(workspace) = workspace else {
10359 return Ok(Navigated::No);
10360 };
10361 let opened = workspace
10362 .update_in(&mut cx, |workspace, window, cx| {
10363 Self::open_locations_in_multibuffer(
10364 workspace,
10365 locations,
10366 title,
10367 split,
10368 MultibufferSelectionMode::First,
10369 window,
10370 cx,
10371 )
10372 })
10373 .ok();
10374
10375 anyhow::Ok(Navigated::from_bool(opened.is_some()))
10376 })
10377 } else {
10378 Task::ready(Ok(Navigated::No))
10379 }
10380 }
10381
10382 fn compute_target_location(
10383 &self,
10384 lsp_location: lsp::Location,
10385 server_id: LanguageServerId,
10386 window: &mut Window,
10387 cx: &mut Context<Self>,
10388 ) -> Task<anyhow::Result<Option<Location>>> {
10389 let Some(project) = self.project.clone() else {
10390 return Task::ready(Ok(None));
10391 };
10392
10393 cx.spawn_in(window, move |editor, mut cx| async move {
10394 let location_task = editor.update(&mut cx, |_, cx| {
10395 project.update(cx, |project, cx| {
10396 let language_server_name = project
10397 .language_server_statuses(cx)
10398 .find(|(id, _)| server_id == *id)
10399 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
10400 language_server_name.map(|language_server_name| {
10401 project.open_local_buffer_via_lsp(
10402 lsp_location.uri.clone(),
10403 server_id,
10404 language_server_name,
10405 cx,
10406 )
10407 })
10408 })
10409 })?;
10410 let location = match location_task {
10411 Some(task) => Some({
10412 let target_buffer_handle = task.await.context("open local buffer")?;
10413 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
10414 let target_start = target_buffer
10415 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
10416 let target_end = target_buffer
10417 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
10418 target_buffer.anchor_after(target_start)
10419 ..target_buffer.anchor_before(target_end)
10420 })?;
10421 Location {
10422 buffer: target_buffer_handle,
10423 range,
10424 }
10425 }),
10426 None => None,
10427 };
10428 Ok(location)
10429 })
10430 }
10431
10432 pub fn find_all_references(
10433 &mut self,
10434 _: &FindAllReferences,
10435 window: &mut Window,
10436 cx: &mut Context<Self>,
10437 ) -> Option<Task<Result<Navigated>>> {
10438 let selection = self.selections.newest::<usize>(cx);
10439 let multi_buffer = self.buffer.read(cx);
10440 let head = selection.head();
10441
10442 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
10443 let head_anchor = multi_buffer_snapshot.anchor_at(
10444 head,
10445 if head < selection.tail() {
10446 Bias::Right
10447 } else {
10448 Bias::Left
10449 },
10450 );
10451
10452 match self
10453 .find_all_references_task_sources
10454 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
10455 {
10456 Ok(_) => {
10457 log::info!(
10458 "Ignoring repeated FindAllReferences invocation with the position of already running task"
10459 );
10460 return None;
10461 }
10462 Err(i) => {
10463 self.find_all_references_task_sources.insert(i, head_anchor);
10464 }
10465 }
10466
10467 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
10468 let workspace = self.workspace()?;
10469 let project = workspace.read(cx).project().clone();
10470 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
10471 Some(cx.spawn_in(window, |editor, mut cx| async move {
10472 let _cleanup = defer({
10473 let mut cx = cx.clone();
10474 move || {
10475 let _ = editor.update(&mut cx, |editor, _| {
10476 if let Ok(i) =
10477 editor
10478 .find_all_references_task_sources
10479 .binary_search_by(|anchor| {
10480 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
10481 })
10482 {
10483 editor.find_all_references_task_sources.remove(i);
10484 }
10485 });
10486 }
10487 });
10488
10489 let locations = references.await?;
10490 if locations.is_empty() {
10491 return anyhow::Ok(Navigated::No);
10492 }
10493
10494 workspace.update_in(&mut cx, |workspace, window, cx| {
10495 let title = locations
10496 .first()
10497 .as_ref()
10498 .map(|location| {
10499 let buffer = location.buffer.read(cx);
10500 format!(
10501 "References to `{}`",
10502 buffer
10503 .text_for_range(location.range.clone())
10504 .collect::<String>()
10505 )
10506 })
10507 .unwrap();
10508 Self::open_locations_in_multibuffer(
10509 workspace,
10510 locations,
10511 title,
10512 false,
10513 MultibufferSelectionMode::First,
10514 window,
10515 cx,
10516 );
10517 Navigated::Yes
10518 })
10519 }))
10520 }
10521
10522 /// Opens a multibuffer with the given project locations in it
10523 pub fn open_locations_in_multibuffer(
10524 workspace: &mut Workspace,
10525 mut locations: Vec<Location>,
10526 title: String,
10527 split: bool,
10528 multibuffer_selection_mode: MultibufferSelectionMode,
10529 window: &mut Window,
10530 cx: &mut Context<Workspace>,
10531 ) {
10532 // If there are multiple definitions, open them in a multibuffer
10533 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
10534 let mut locations = locations.into_iter().peekable();
10535 let mut ranges = Vec::new();
10536 let capability = workspace.project().read(cx).capability();
10537
10538 let excerpt_buffer = cx.new(|cx| {
10539 let mut multibuffer = MultiBuffer::new(capability);
10540 while let Some(location) = locations.next() {
10541 let buffer = location.buffer.read(cx);
10542 let mut ranges_for_buffer = Vec::new();
10543 let range = location.range.to_offset(buffer);
10544 ranges_for_buffer.push(range.clone());
10545
10546 while let Some(next_location) = locations.peek() {
10547 if next_location.buffer == location.buffer {
10548 ranges_for_buffer.push(next_location.range.to_offset(buffer));
10549 locations.next();
10550 } else {
10551 break;
10552 }
10553 }
10554
10555 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
10556 ranges.extend(multibuffer.push_excerpts_with_context_lines(
10557 location.buffer.clone(),
10558 ranges_for_buffer,
10559 DEFAULT_MULTIBUFFER_CONTEXT,
10560 cx,
10561 ))
10562 }
10563
10564 multibuffer.with_title(title)
10565 });
10566
10567 let editor = cx.new(|cx| {
10568 Editor::for_multibuffer(
10569 excerpt_buffer,
10570 Some(workspace.project().clone()),
10571 true,
10572 window,
10573 cx,
10574 )
10575 });
10576 editor.update(cx, |editor, cx| {
10577 match multibuffer_selection_mode {
10578 MultibufferSelectionMode::First => {
10579 if let Some(first_range) = ranges.first() {
10580 editor.change_selections(None, window, cx, |selections| {
10581 selections.clear_disjoint();
10582 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
10583 });
10584 }
10585 editor.highlight_background::<Self>(
10586 &ranges,
10587 |theme| theme.editor_highlighted_line_background,
10588 cx,
10589 );
10590 }
10591 MultibufferSelectionMode::All => {
10592 editor.change_selections(None, window, cx, |selections| {
10593 selections.clear_disjoint();
10594 selections.select_anchor_ranges(ranges);
10595 });
10596 }
10597 }
10598 editor.register_buffers_with_language_servers(cx);
10599 });
10600
10601 let item = Box::new(editor);
10602 let item_id = item.item_id();
10603
10604 if split {
10605 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
10606 } else {
10607 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
10608 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
10609 pane.close_current_preview_item(window, cx)
10610 } else {
10611 None
10612 }
10613 });
10614 workspace.add_item_to_active_pane(item.clone(), destination_index, true, window, cx);
10615 }
10616 workspace.active_pane().update(cx, |pane, cx| {
10617 pane.set_preview_item_id(Some(item_id), cx);
10618 });
10619 }
10620
10621 pub fn rename(
10622 &mut self,
10623 _: &Rename,
10624 window: &mut Window,
10625 cx: &mut Context<Self>,
10626 ) -> Option<Task<Result<()>>> {
10627 use language::ToOffset as _;
10628
10629 let provider = self.semantics_provider.clone()?;
10630 let selection = self.selections.newest_anchor().clone();
10631 let (cursor_buffer, cursor_buffer_position) = self
10632 .buffer
10633 .read(cx)
10634 .text_anchor_for_position(selection.head(), cx)?;
10635 let (tail_buffer, cursor_buffer_position_end) = self
10636 .buffer
10637 .read(cx)
10638 .text_anchor_for_position(selection.tail(), cx)?;
10639 if tail_buffer != cursor_buffer {
10640 return None;
10641 }
10642
10643 let snapshot = cursor_buffer.read(cx).snapshot();
10644 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
10645 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
10646 let prepare_rename = provider
10647 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
10648 .unwrap_or_else(|| Task::ready(Ok(None)));
10649 drop(snapshot);
10650
10651 Some(cx.spawn_in(window, |this, mut cx| async move {
10652 let rename_range = if let Some(range) = prepare_rename.await? {
10653 Some(range)
10654 } else {
10655 this.update(&mut cx, |this, cx| {
10656 let buffer = this.buffer.read(cx).snapshot(cx);
10657 let mut buffer_highlights = this
10658 .document_highlights_for_position(selection.head(), &buffer)
10659 .filter(|highlight| {
10660 highlight.start.excerpt_id == selection.head().excerpt_id
10661 && highlight.end.excerpt_id == selection.head().excerpt_id
10662 });
10663 buffer_highlights
10664 .next()
10665 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
10666 })?
10667 };
10668 if let Some(rename_range) = rename_range {
10669 this.update_in(&mut cx, |this, window, cx| {
10670 let snapshot = cursor_buffer.read(cx).snapshot();
10671 let rename_buffer_range = rename_range.to_offset(&snapshot);
10672 let cursor_offset_in_rename_range =
10673 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
10674 let cursor_offset_in_rename_range_end =
10675 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
10676
10677 this.take_rename(false, window, cx);
10678 let buffer = this.buffer.read(cx).read(cx);
10679 let cursor_offset = selection.head().to_offset(&buffer);
10680 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
10681 let rename_end = rename_start + rename_buffer_range.len();
10682 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
10683 let mut old_highlight_id = None;
10684 let old_name: Arc<str> = buffer
10685 .chunks(rename_start..rename_end, true)
10686 .map(|chunk| {
10687 if old_highlight_id.is_none() {
10688 old_highlight_id = chunk.syntax_highlight_id;
10689 }
10690 chunk.text
10691 })
10692 .collect::<String>()
10693 .into();
10694
10695 drop(buffer);
10696
10697 // Position the selection in the rename editor so that it matches the current selection.
10698 this.show_local_selections = false;
10699 let rename_editor = cx.new(|cx| {
10700 let mut editor = Editor::single_line(window, cx);
10701 editor.buffer.update(cx, |buffer, cx| {
10702 buffer.edit([(0..0, old_name.clone())], None, cx)
10703 });
10704 let rename_selection_range = match cursor_offset_in_rename_range
10705 .cmp(&cursor_offset_in_rename_range_end)
10706 {
10707 Ordering::Equal => {
10708 editor.select_all(&SelectAll, window, cx);
10709 return editor;
10710 }
10711 Ordering::Less => {
10712 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
10713 }
10714 Ordering::Greater => {
10715 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
10716 }
10717 };
10718 if rename_selection_range.end > old_name.len() {
10719 editor.select_all(&SelectAll, window, cx);
10720 } else {
10721 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10722 s.select_ranges([rename_selection_range]);
10723 });
10724 }
10725 editor
10726 });
10727 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
10728 if e == &EditorEvent::Focused {
10729 cx.emit(EditorEvent::FocusedIn)
10730 }
10731 })
10732 .detach();
10733
10734 let write_highlights =
10735 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
10736 let read_highlights =
10737 this.clear_background_highlights::<DocumentHighlightRead>(cx);
10738 let ranges = write_highlights
10739 .iter()
10740 .flat_map(|(_, ranges)| ranges.iter())
10741 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
10742 .cloned()
10743 .collect();
10744
10745 this.highlight_text::<Rename>(
10746 ranges,
10747 HighlightStyle {
10748 fade_out: Some(0.6),
10749 ..Default::default()
10750 },
10751 cx,
10752 );
10753 let rename_focus_handle = rename_editor.focus_handle(cx);
10754 window.focus(&rename_focus_handle);
10755 let block_id = this.insert_blocks(
10756 [BlockProperties {
10757 style: BlockStyle::Flex,
10758 placement: BlockPlacement::Below(range.start),
10759 height: 1,
10760 render: Arc::new({
10761 let rename_editor = rename_editor.clone();
10762 move |cx: &mut BlockContext| {
10763 let mut text_style = cx.editor_style.text.clone();
10764 if let Some(highlight_style) = old_highlight_id
10765 .and_then(|h| h.style(&cx.editor_style.syntax))
10766 {
10767 text_style = text_style.highlight(highlight_style);
10768 }
10769 div()
10770 .block_mouse_down()
10771 .pl(cx.anchor_x)
10772 .child(EditorElement::new(
10773 &rename_editor,
10774 EditorStyle {
10775 background: cx.theme().system().transparent,
10776 local_player: cx.editor_style.local_player,
10777 text: text_style,
10778 scrollbar_width: cx.editor_style.scrollbar_width,
10779 syntax: cx.editor_style.syntax.clone(),
10780 status: cx.editor_style.status.clone(),
10781 inlay_hints_style: HighlightStyle {
10782 font_weight: Some(FontWeight::BOLD),
10783 ..make_inlay_hints_style(cx.app)
10784 },
10785 inline_completion_styles: make_suggestion_styles(
10786 cx.app,
10787 ),
10788 ..EditorStyle::default()
10789 },
10790 ))
10791 .into_any_element()
10792 }
10793 }),
10794 priority: 0,
10795 }],
10796 Some(Autoscroll::fit()),
10797 cx,
10798 )[0];
10799 this.pending_rename = Some(RenameState {
10800 range,
10801 old_name,
10802 editor: rename_editor,
10803 block_id,
10804 });
10805 })?;
10806 }
10807
10808 Ok(())
10809 }))
10810 }
10811
10812 pub fn confirm_rename(
10813 &mut self,
10814 _: &ConfirmRename,
10815 window: &mut Window,
10816 cx: &mut Context<Self>,
10817 ) -> Option<Task<Result<()>>> {
10818 let rename = self.take_rename(false, window, cx)?;
10819 let workspace = self.workspace()?.downgrade();
10820 let (buffer, start) = self
10821 .buffer
10822 .read(cx)
10823 .text_anchor_for_position(rename.range.start, cx)?;
10824 let (end_buffer, _) = self
10825 .buffer
10826 .read(cx)
10827 .text_anchor_for_position(rename.range.end, cx)?;
10828 if buffer != end_buffer {
10829 return None;
10830 }
10831
10832 let old_name = rename.old_name;
10833 let new_name = rename.editor.read(cx).text(cx);
10834
10835 let rename = self.semantics_provider.as_ref()?.perform_rename(
10836 &buffer,
10837 start,
10838 new_name.clone(),
10839 cx,
10840 )?;
10841
10842 Some(cx.spawn_in(window, |editor, mut cx| async move {
10843 let project_transaction = rename.await?;
10844 Self::open_project_transaction(
10845 &editor,
10846 workspace,
10847 project_transaction,
10848 format!("Rename: {} → {}", old_name, new_name),
10849 cx.clone(),
10850 )
10851 .await?;
10852
10853 editor.update(&mut cx, |editor, cx| {
10854 editor.refresh_document_highlights(cx);
10855 })?;
10856 Ok(())
10857 }))
10858 }
10859
10860 fn take_rename(
10861 &mut self,
10862 moving_cursor: bool,
10863 window: &mut Window,
10864 cx: &mut Context<Self>,
10865 ) -> Option<RenameState> {
10866 let rename = self.pending_rename.take()?;
10867 if rename.editor.focus_handle(cx).is_focused(window) {
10868 window.focus(&self.focus_handle);
10869 }
10870
10871 self.remove_blocks(
10872 [rename.block_id].into_iter().collect(),
10873 Some(Autoscroll::fit()),
10874 cx,
10875 );
10876 self.clear_highlights::<Rename>(cx);
10877 self.show_local_selections = true;
10878
10879 if moving_cursor {
10880 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
10881 editor.selections.newest::<usize>(cx).head()
10882 });
10883
10884 // Update the selection to match the position of the selection inside
10885 // the rename editor.
10886 let snapshot = self.buffer.read(cx).read(cx);
10887 let rename_range = rename.range.to_offset(&snapshot);
10888 let cursor_in_editor = snapshot
10889 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
10890 .min(rename_range.end);
10891 drop(snapshot);
10892
10893 self.change_selections(None, window, cx, |s| {
10894 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
10895 });
10896 } else {
10897 self.refresh_document_highlights(cx);
10898 }
10899
10900 Some(rename)
10901 }
10902
10903 pub fn pending_rename(&self) -> Option<&RenameState> {
10904 self.pending_rename.as_ref()
10905 }
10906
10907 fn format(
10908 &mut self,
10909 _: &Format,
10910 window: &mut Window,
10911 cx: &mut Context<Self>,
10912 ) -> Option<Task<Result<()>>> {
10913 let project = match &self.project {
10914 Some(project) => project.clone(),
10915 None => return None,
10916 };
10917
10918 Some(self.perform_format(
10919 project,
10920 FormatTrigger::Manual,
10921 FormatTarget::Buffers,
10922 window,
10923 cx,
10924 ))
10925 }
10926
10927 fn format_selections(
10928 &mut self,
10929 _: &FormatSelections,
10930 window: &mut Window,
10931 cx: &mut Context<Self>,
10932 ) -> Option<Task<Result<()>>> {
10933 let project = match &self.project {
10934 Some(project) => project.clone(),
10935 None => return None,
10936 };
10937
10938 let ranges = self
10939 .selections
10940 .all_adjusted(cx)
10941 .into_iter()
10942 .map(|selection| selection.range())
10943 .collect_vec();
10944
10945 Some(self.perform_format(
10946 project,
10947 FormatTrigger::Manual,
10948 FormatTarget::Ranges(ranges),
10949 window,
10950 cx,
10951 ))
10952 }
10953
10954 fn perform_format(
10955 &mut self,
10956 project: Entity<Project>,
10957 trigger: FormatTrigger,
10958 target: FormatTarget,
10959 window: &mut Window,
10960 cx: &mut Context<Self>,
10961 ) -> Task<Result<()>> {
10962 let buffer = self.buffer.clone();
10963 let (buffers, target) = match target {
10964 FormatTarget::Buffers => {
10965 let mut buffers = buffer.read(cx).all_buffers();
10966 if trigger == FormatTrigger::Save {
10967 buffers.retain(|buffer| buffer.read(cx).is_dirty());
10968 }
10969 (buffers, LspFormatTarget::Buffers)
10970 }
10971 FormatTarget::Ranges(selection_ranges) => {
10972 let multi_buffer = buffer.read(cx);
10973 let snapshot = multi_buffer.read(cx);
10974 let mut buffers = HashSet::default();
10975 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
10976 BTreeMap::new();
10977 for selection_range in selection_ranges {
10978 for (buffer, buffer_range, _) in
10979 snapshot.range_to_buffer_ranges(selection_range)
10980 {
10981 let buffer_id = buffer.remote_id();
10982 let start = buffer.anchor_before(buffer_range.start);
10983 let end = buffer.anchor_after(buffer_range.end);
10984 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
10985 buffer_id_to_ranges
10986 .entry(buffer_id)
10987 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
10988 .or_insert_with(|| vec![start..end]);
10989 }
10990 }
10991 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
10992 }
10993 };
10994
10995 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
10996 let format = project.update(cx, |project, cx| {
10997 project.format(buffers, target, true, trigger, cx)
10998 });
10999
11000 cx.spawn_in(window, |_, mut cx| async move {
11001 let transaction = futures::select_biased! {
11002 () = timeout => {
11003 log::warn!("timed out waiting for formatting");
11004 None
11005 }
11006 transaction = format.log_err().fuse() => transaction,
11007 };
11008
11009 buffer
11010 .update(&mut cx, |buffer, cx| {
11011 if let Some(transaction) = transaction {
11012 if !buffer.is_singleton() {
11013 buffer.push_transaction(&transaction.0, cx);
11014 }
11015 }
11016
11017 cx.notify();
11018 })
11019 .ok();
11020
11021 Ok(())
11022 })
11023 }
11024
11025 fn restart_language_server(
11026 &mut self,
11027 _: &RestartLanguageServer,
11028 _: &mut Window,
11029 cx: &mut Context<Self>,
11030 ) {
11031 if let Some(project) = self.project.clone() {
11032 self.buffer.update(cx, |multi_buffer, cx| {
11033 project.update(cx, |project, cx| {
11034 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
11035 });
11036 })
11037 }
11038 }
11039
11040 fn cancel_language_server_work(
11041 &mut self,
11042 _: &actions::CancelLanguageServerWork,
11043 _: &mut Window,
11044 cx: &mut Context<Self>,
11045 ) {
11046 if let Some(project) = self.project.clone() {
11047 self.buffer.update(cx, |multi_buffer, cx| {
11048 project.update(cx, |project, cx| {
11049 project.cancel_language_server_work_for_buffers(multi_buffer.all_buffers(), cx);
11050 });
11051 })
11052 }
11053 }
11054
11055 fn show_character_palette(
11056 &mut self,
11057 _: &ShowCharacterPalette,
11058 window: &mut Window,
11059 _: &mut Context<Self>,
11060 ) {
11061 window.show_character_palette();
11062 }
11063
11064 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
11065 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
11066 let buffer = self.buffer.read(cx).snapshot(cx);
11067 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
11068 let is_valid = buffer
11069 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone())
11070 .any(|entry| {
11071 entry.diagnostic.is_primary
11072 && !entry.range.is_empty()
11073 && entry.range.start == primary_range_start
11074 && entry.diagnostic.message == active_diagnostics.primary_message
11075 });
11076
11077 if is_valid != active_diagnostics.is_valid {
11078 active_diagnostics.is_valid = is_valid;
11079 let mut new_styles = HashMap::default();
11080 for (block_id, diagnostic) in &active_diagnostics.blocks {
11081 new_styles.insert(
11082 *block_id,
11083 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
11084 );
11085 }
11086 self.display_map.update(cx, |display_map, _cx| {
11087 display_map.replace_blocks(new_styles)
11088 });
11089 }
11090 }
11091 }
11092
11093 fn activate_diagnostics(
11094 &mut self,
11095 buffer_id: BufferId,
11096 group_id: usize,
11097 window: &mut Window,
11098 cx: &mut Context<Self>,
11099 ) {
11100 self.dismiss_diagnostics(cx);
11101 let snapshot = self.snapshot(window, cx);
11102 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
11103 let buffer = self.buffer.read(cx).snapshot(cx);
11104
11105 let mut primary_range = None;
11106 let mut primary_message = None;
11107 let diagnostic_group = buffer
11108 .diagnostic_group(buffer_id, group_id)
11109 .filter_map(|entry| {
11110 let start = entry.range.start;
11111 let end = entry.range.end;
11112 if snapshot.is_line_folded(MultiBufferRow(start.row))
11113 && (start.row == end.row
11114 || snapshot.is_line_folded(MultiBufferRow(end.row)))
11115 {
11116 return None;
11117 }
11118 if entry.diagnostic.is_primary {
11119 primary_range = Some(entry.range.clone());
11120 primary_message = Some(entry.diagnostic.message.clone());
11121 }
11122 Some(entry)
11123 })
11124 .collect::<Vec<_>>();
11125 let primary_range = primary_range?;
11126 let primary_message = primary_message?;
11127
11128 let blocks = display_map
11129 .insert_blocks(
11130 diagnostic_group.iter().map(|entry| {
11131 let diagnostic = entry.diagnostic.clone();
11132 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
11133 BlockProperties {
11134 style: BlockStyle::Fixed,
11135 placement: BlockPlacement::Below(
11136 buffer.anchor_after(entry.range.start),
11137 ),
11138 height: message_height,
11139 render: diagnostic_block_renderer(diagnostic, None, true, true),
11140 priority: 0,
11141 }
11142 }),
11143 cx,
11144 )
11145 .into_iter()
11146 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
11147 .collect();
11148
11149 Some(ActiveDiagnosticGroup {
11150 primary_range: buffer.anchor_before(primary_range.start)
11151 ..buffer.anchor_after(primary_range.end),
11152 primary_message,
11153 group_id,
11154 blocks,
11155 is_valid: true,
11156 })
11157 });
11158 }
11159
11160 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
11161 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
11162 self.display_map.update(cx, |display_map, cx| {
11163 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
11164 });
11165 cx.notify();
11166 }
11167 }
11168
11169 pub fn set_selections_from_remote(
11170 &mut self,
11171 selections: Vec<Selection<Anchor>>,
11172 pending_selection: Option<Selection<Anchor>>,
11173 window: &mut Window,
11174 cx: &mut Context<Self>,
11175 ) {
11176 let old_cursor_position = self.selections.newest_anchor().head();
11177 self.selections.change_with(cx, |s| {
11178 s.select_anchors(selections);
11179 if let Some(pending_selection) = pending_selection {
11180 s.set_pending(pending_selection, SelectMode::Character);
11181 } else {
11182 s.clear_pending();
11183 }
11184 });
11185 self.selections_did_change(false, &old_cursor_position, true, window, cx);
11186 }
11187
11188 fn push_to_selection_history(&mut self) {
11189 self.selection_history.push(SelectionHistoryEntry {
11190 selections: self.selections.disjoint_anchors(),
11191 select_next_state: self.select_next_state.clone(),
11192 select_prev_state: self.select_prev_state.clone(),
11193 add_selections_state: self.add_selections_state.clone(),
11194 });
11195 }
11196
11197 pub fn transact(
11198 &mut self,
11199 window: &mut Window,
11200 cx: &mut Context<Self>,
11201 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
11202 ) -> Option<TransactionId> {
11203 self.start_transaction_at(Instant::now(), window, cx);
11204 update(self, window, cx);
11205 self.end_transaction_at(Instant::now(), cx)
11206 }
11207
11208 pub fn start_transaction_at(
11209 &mut self,
11210 now: Instant,
11211 window: &mut Window,
11212 cx: &mut Context<Self>,
11213 ) {
11214 self.end_selection(window, cx);
11215 if let Some(tx_id) = self
11216 .buffer
11217 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
11218 {
11219 self.selection_history
11220 .insert_transaction(tx_id, self.selections.disjoint_anchors());
11221 cx.emit(EditorEvent::TransactionBegun {
11222 transaction_id: tx_id,
11223 })
11224 }
11225 }
11226
11227 pub fn end_transaction_at(
11228 &mut self,
11229 now: Instant,
11230 cx: &mut Context<Self>,
11231 ) -> Option<TransactionId> {
11232 if let Some(transaction_id) = self
11233 .buffer
11234 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
11235 {
11236 if let Some((_, end_selections)) =
11237 self.selection_history.transaction_mut(transaction_id)
11238 {
11239 *end_selections = Some(self.selections.disjoint_anchors());
11240 } else {
11241 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
11242 }
11243
11244 cx.emit(EditorEvent::Edited { transaction_id });
11245 Some(transaction_id)
11246 } else {
11247 None
11248 }
11249 }
11250
11251 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
11252 if self.selection_mark_mode {
11253 self.change_selections(None, window, cx, |s| {
11254 s.move_with(|_, sel| {
11255 sel.collapse_to(sel.head(), SelectionGoal::None);
11256 });
11257 })
11258 }
11259 self.selection_mark_mode = true;
11260 cx.notify();
11261 }
11262
11263 pub fn swap_selection_ends(
11264 &mut self,
11265 _: &actions::SwapSelectionEnds,
11266 window: &mut Window,
11267 cx: &mut Context<Self>,
11268 ) {
11269 self.change_selections(None, window, cx, |s| {
11270 s.move_with(|_, sel| {
11271 if sel.start != sel.end {
11272 sel.reversed = !sel.reversed
11273 }
11274 });
11275 });
11276 self.request_autoscroll(Autoscroll::newest(), cx);
11277 cx.notify();
11278 }
11279
11280 pub fn toggle_fold(
11281 &mut self,
11282 _: &actions::ToggleFold,
11283 window: &mut Window,
11284 cx: &mut Context<Self>,
11285 ) {
11286 if self.is_singleton(cx) {
11287 let selection = self.selections.newest::<Point>(cx);
11288
11289 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11290 let range = if selection.is_empty() {
11291 let point = selection.head().to_display_point(&display_map);
11292 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
11293 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
11294 .to_point(&display_map);
11295 start..end
11296 } else {
11297 selection.range()
11298 };
11299 if display_map.folds_in_range(range).next().is_some() {
11300 self.unfold_lines(&Default::default(), window, cx)
11301 } else {
11302 self.fold(&Default::default(), window, cx)
11303 }
11304 } else {
11305 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
11306 let buffer_ids: HashSet<_> = multi_buffer_snapshot
11307 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
11308 .map(|(snapshot, _, _)| snapshot.remote_id())
11309 .collect();
11310
11311 for buffer_id in buffer_ids {
11312 if self.is_buffer_folded(buffer_id, cx) {
11313 self.unfold_buffer(buffer_id, cx);
11314 } else {
11315 self.fold_buffer(buffer_id, cx);
11316 }
11317 }
11318 }
11319 }
11320
11321 pub fn toggle_fold_recursive(
11322 &mut self,
11323 _: &actions::ToggleFoldRecursive,
11324 window: &mut Window,
11325 cx: &mut Context<Self>,
11326 ) {
11327 let selection = self.selections.newest::<Point>(cx);
11328
11329 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11330 let range = if selection.is_empty() {
11331 let point = selection.head().to_display_point(&display_map);
11332 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
11333 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
11334 .to_point(&display_map);
11335 start..end
11336 } else {
11337 selection.range()
11338 };
11339 if display_map.folds_in_range(range).next().is_some() {
11340 self.unfold_recursive(&Default::default(), window, cx)
11341 } else {
11342 self.fold_recursive(&Default::default(), window, cx)
11343 }
11344 }
11345
11346 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
11347 if self.is_singleton(cx) {
11348 let mut to_fold = Vec::new();
11349 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11350 let selections = self.selections.all_adjusted(cx);
11351
11352 for selection in selections {
11353 let range = selection.range().sorted();
11354 let buffer_start_row = range.start.row;
11355
11356 if range.start.row != range.end.row {
11357 let mut found = false;
11358 let mut row = range.start.row;
11359 while row <= range.end.row {
11360 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
11361 {
11362 found = true;
11363 row = crease.range().end.row + 1;
11364 to_fold.push(crease);
11365 } else {
11366 row += 1
11367 }
11368 }
11369 if found {
11370 continue;
11371 }
11372 }
11373
11374 for row in (0..=range.start.row).rev() {
11375 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
11376 if crease.range().end.row >= buffer_start_row {
11377 to_fold.push(crease);
11378 if row <= range.start.row {
11379 break;
11380 }
11381 }
11382 }
11383 }
11384 }
11385
11386 self.fold_creases(to_fold, true, window, cx);
11387 } else {
11388 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
11389
11390 let buffer_ids: HashSet<_> = multi_buffer_snapshot
11391 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
11392 .map(|(snapshot, _, _)| snapshot.remote_id())
11393 .collect();
11394 for buffer_id in buffer_ids {
11395 self.fold_buffer(buffer_id, cx);
11396 }
11397 }
11398 }
11399
11400 fn fold_at_level(
11401 &mut self,
11402 fold_at: &FoldAtLevel,
11403 window: &mut Window,
11404 cx: &mut Context<Self>,
11405 ) {
11406 if !self.buffer.read(cx).is_singleton() {
11407 return;
11408 }
11409
11410 let fold_at_level = fold_at.level;
11411 let snapshot = self.buffer.read(cx).snapshot(cx);
11412 let mut to_fold = Vec::new();
11413 let mut stack = vec![(0, snapshot.max_row().0, 1)];
11414
11415 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
11416 while start_row < end_row {
11417 match self
11418 .snapshot(window, cx)
11419 .crease_for_buffer_row(MultiBufferRow(start_row))
11420 {
11421 Some(crease) => {
11422 let nested_start_row = crease.range().start.row + 1;
11423 let nested_end_row = crease.range().end.row;
11424
11425 if current_level < fold_at_level {
11426 stack.push((nested_start_row, nested_end_row, current_level + 1));
11427 } else if current_level == fold_at_level {
11428 to_fold.push(crease);
11429 }
11430
11431 start_row = nested_end_row + 1;
11432 }
11433 None => start_row += 1,
11434 }
11435 }
11436 }
11437
11438 self.fold_creases(to_fold, true, window, cx);
11439 }
11440
11441 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
11442 if self.buffer.read(cx).is_singleton() {
11443 let mut fold_ranges = Vec::new();
11444 let snapshot = self.buffer.read(cx).snapshot(cx);
11445
11446 for row in 0..snapshot.max_row().0 {
11447 if let Some(foldable_range) = self
11448 .snapshot(window, cx)
11449 .crease_for_buffer_row(MultiBufferRow(row))
11450 {
11451 fold_ranges.push(foldable_range);
11452 }
11453 }
11454
11455 self.fold_creases(fold_ranges, true, window, cx);
11456 } else {
11457 self.toggle_fold_multiple_buffers = cx.spawn_in(window, |editor, mut cx| async move {
11458 editor
11459 .update_in(&mut cx, |editor, _, cx| {
11460 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
11461 editor.fold_buffer(buffer_id, cx);
11462 }
11463 })
11464 .ok();
11465 });
11466 }
11467 }
11468
11469 pub fn fold_function_bodies(
11470 &mut self,
11471 _: &actions::FoldFunctionBodies,
11472 window: &mut Window,
11473 cx: &mut Context<Self>,
11474 ) {
11475 let snapshot = self.buffer.read(cx).snapshot(cx);
11476
11477 let ranges = snapshot
11478 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
11479 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
11480 .collect::<Vec<_>>();
11481
11482 let creases = ranges
11483 .into_iter()
11484 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
11485 .collect();
11486
11487 self.fold_creases(creases, true, window, cx);
11488 }
11489
11490 pub fn fold_recursive(
11491 &mut self,
11492 _: &actions::FoldRecursive,
11493 window: &mut Window,
11494 cx: &mut Context<Self>,
11495 ) {
11496 let mut to_fold = Vec::new();
11497 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11498 let selections = self.selections.all_adjusted(cx);
11499
11500 for selection in selections {
11501 let range = selection.range().sorted();
11502 let buffer_start_row = range.start.row;
11503
11504 if range.start.row != range.end.row {
11505 let mut found = false;
11506 for row in range.start.row..=range.end.row {
11507 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
11508 found = true;
11509 to_fold.push(crease);
11510 }
11511 }
11512 if found {
11513 continue;
11514 }
11515 }
11516
11517 for row in (0..=range.start.row).rev() {
11518 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
11519 if crease.range().end.row >= buffer_start_row {
11520 to_fold.push(crease);
11521 } else {
11522 break;
11523 }
11524 }
11525 }
11526 }
11527
11528 self.fold_creases(to_fold, true, window, cx);
11529 }
11530
11531 pub fn fold_at(&mut self, fold_at: &FoldAt, window: &mut Window, cx: &mut Context<Self>) {
11532 let buffer_row = fold_at.buffer_row;
11533 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11534
11535 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
11536 let autoscroll = self
11537 .selections
11538 .all::<Point>(cx)
11539 .iter()
11540 .any(|selection| crease.range().overlaps(&selection.range()));
11541
11542 self.fold_creases(vec![crease], autoscroll, window, cx);
11543 }
11544 }
11545
11546 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
11547 if self.is_singleton(cx) {
11548 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11549 let buffer = &display_map.buffer_snapshot;
11550 let selections = self.selections.all::<Point>(cx);
11551 let ranges = selections
11552 .iter()
11553 .map(|s| {
11554 let range = s.display_range(&display_map).sorted();
11555 let mut start = range.start.to_point(&display_map);
11556 let mut end = range.end.to_point(&display_map);
11557 start.column = 0;
11558 end.column = buffer.line_len(MultiBufferRow(end.row));
11559 start..end
11560 })
11561 .collect::<Vec<_>>();
11562
11563 self.unfold_ranges(&ranges, true, true, cx);
11564 } else {
11565 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
11566 let buffer_ids: HashSet<_> = multi_buffer_snapshot
11567 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
11568 .map(|(snapshot, _, _)| snapshot.remote_id())
11569 .collect();
11570 for buffer_id in buffer_ids {
11571 self.unfold_buffer(buffer_id, cx);
11572 }
11573 }
11574 }
11575
11576 pub fn unfold_recursive(
11577 &mut self,
11578 _: &UnfoldRecursive,
11579 _window: &mut Window,
11580 cx: &mut Context<Self>,
11581 ) {
11582 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11583 let selections = self.selections.all::<Point>(cx);
11584 let ranges = selections
11585 .iter()
11586 .map(|s| {
11587 let mut range = s.display_range(&display_map).sorted();
11588 *range.start.column_mut() = 0;
11589 *range.end.column_mut() = display_map.line_len(range.end.row());
11590 let start = range.start.to_point(&display_map);
11591 let end = range.end.to_point(&display_map);
11592 start..end
11593 })
11594 .collect::<Vec<_>>();
11595
11596 self.unfold_ranges(&ranges, true, true, cx);
11597 }
11598
11599 pub fn unfold_at(
11600 &mut self,
11601 unfold_at: &UnfoldAt,
11602 _window: &mut Window,
11603 cx: &mut Context<Self>,
11604 ) {
11605 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11606
11607 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
11608 ..Point::new(
11609 unfold_at.buffer_row.0,
11610 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
11611 );
11612
11613 let autoscroll = self
11614 .selections
11615 .all::<Point>(cx)
11616 .iter()
11617 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
11618
11619 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
11620 }
11621
11622 pub fn unfold_all(
11623 &mut self,
11624 _: &actions::UnfoldAll,
11625 _window: &mut Window,
11626 cx: &mut Context<Self>,
11627 ) {
11628 if self.buffer.read(cx).is_singleton() {
11629 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11630 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
11631 } else {
11632 self.toggle_fold_multiple_buffers = cx.spawn(|editor, mut cx| async move {
11633 editor
11634 .update(&mut cx, |editor, cx| {
11635 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
11636 editor.unfold_buffer(buffer_id, cx);
11637 }
11638 })
11639 .ok();
11640 });
11641 }
11642 }
11643
11644 pub fn fold_selected_ranges(
11645 &mut self,
11646 _: &FoldSelectedRanges,
11647 window: &mut Window,
11648 cx: &mut Context<Self>,
11649 ) {
11650 let selections = self.selections.all::<Point>(cx);
11651 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11652 let line_mode = self.selections.line_mode;
11653 let ranges = selections
11654 .into_iter()
11655 .map(|s| {
11656 if line_mode {
11657 let start = Point::new(s.start.row, 0);
11658 let end = Point::new(
11659 s.end.row,
11660 display_map
11661 .buffer_snapshot
11662 .line_len(MultiBufferRow(s.end.row)),
11663 );
11664 Crease::simple(start..end, display_map.fold_placeholder.clone())
11665 } else {
11666 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
11667 }
11668 })
11669 .collect::<Vec<_>>();
11670 self.fold_creases(ranges, true, window, cx);
11671 }
11672
11673 pub fn fold_ranges<T: ToOffset + Clone>(
11674 &mut self,
11675 ranges: Vec<Range<T>>,
11676 auto_scroll: bool,
11677 window: &mut Window,
11678 cx: &mut Context<Self>,
11679 ) {
11680 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11681 let ranges = ranges
11682 .into_iter()
11683 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
11684 .collect::<Vec<_>>();
11685 self.fold_creases(ranges, auto_scroll, window, cx);
11686 }
11687
11688 pub fn fold_creases<T: ToOffset + Clone>(
11689 &mut self,
11690 creases: Vec<Crease<T>>,
11691 auto_scroll: bool,
11692 window: &mut Window,
11693 cx: &mut Context<Self>,
11694 ) {
11695 if creases.is_empty() {
11696 return;
11697 }
11698
11699 let mut buffers_affected = HashSet::default();
11700 let multi_buffer = self.buffer().read(cx);
11701 for crease in &creases {
11702 if let Some((_, buffer, _)) =
11703 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
11704 {
11705 buffers_affected.insert(buffer.read(cx).remote_id());
11706 };
11707 }
11708
11709 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
11710
11711 if auto_scroll {
11712 self.request_autoscroll(Autoscroll::fit(), cx);
11713 }
11714
11715 cx.notify();
11716
11717 if let Some(active_diagnostics) = self.active_diagnostics.take() {
11718 // Clear diagnostics block when folding a range that contains it.
11719 let snapshot = self.snapshot(window, cx);
11720 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
11721 drop(snapshot);
11722 self.active_diagnostics = Some(active_diagnostics);
11723 self.dismiss_diagnostics(cx);
11724 } else {
11725 self.active_diagnostics = Some(active_diagnostics);
11726 }
11727 }
11728
11729 self.scrollbar_marker_state.dirty = true;
11730 }
11731
11732 /// Removes any folds whose ranges intersect any of the given ranges.
11733 pub fn unfold_ranges<T: ToOffset + Clone>(
11734 &mut self,
11735 ranges: &[Range<T>],
11736 inclusive: bool,
11737 auto_scroll: bool,
11738 cx: &mut Context<Self>,
11739 ) {
11740 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
11741 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
11742 });
11743 }
11744
11745 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
11746 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
11747 return;
11748 }
11749 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
11750 self.display_map
11751 .update(cx, |display_map, cx| display_map.fold_buffer(buffer_id, cx));
11752 cx.emit(EditorEvent::BufferFoldToggled {
11753 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
11754 folded: true,
11755 });
11756 cx.notify();
11757 }
11758
11759 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
11760 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
11761 return;
11762 }
11763 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
11764 self.display_map.update(cx, |display_map, cx| {
11765 display_map.unfold_buffer(buffer_id, cx);
11766 });
11767 cx.emit(EditorEvent::BufferFoldToggled {
11768 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
11769 folded: false,
11770 });
11771 cx.notify();
11772 }
11773
11774 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
11775 self.display_map.read(cx).is_buffer_folded(buffer)
11776 }
11777
11778 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
11779 self.display_map.read(cx).folded_buffers()
11780 }
11781
11782 /// Removes any folds with the given ranges.
11783 pub fn remove_folds_with_type<T: ToOffset + Clone>(
11784 &mut self,
11785 ranges: &[Range<T>],
11786 type_id: TypeId,
11787 auto_scroll: bool,
11788 cx: &mut Context<Self>,
11789 ) {
11790 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
11791 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
11792 });
11793 }
11794
11795 fn remove_folds_with<T: ToOffset + Clone>(
11796 &mut self,
11797 ranges: &[Range<T>],
11798 auto_scroll: bool,
11799 cx: &mut Context<Self>,
11800 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
11801 ) {
11802 if ranges.is_empty() {
11803 return;
11804 }
11805
11806 let mut buffers_affected = HashSet::default();
11807 let multi_buffer = self.buffer().read(cx);
11808 for range in ranges {
11809 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
11810 buffers_affected.insert(buffer.read(cx).remote_id());
11811 };
11812 }
11813
11814 self.display_map.update(cx, update);
11815
11816 if auto_scroll {
11817 self.request_autoscroll(Autoscroll::fit(), cx);
11818 }
11819
11820 cx.notify();
11821 self.scrollbar_marker_state.dirty = true;
11822 self.active_indent_guides_state.dirty = true;
11823 }
11824
11825 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
11826 self.display_map.read(cx).fold_placeholder.clone()
11827 }
11828
11829 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
11830 self.buffer.update(cx, |buffer, cx| {
11831 buffer.set_all_diff_hunks_expanded(cx);
11832 });
11833 }
11834
11835 pub fn expand_all_diff_hunks(
11836 &mut self,
11837 _: &ExpandAllHunkDiffs,
11838 _window: &mut Window,
11839 cx: &mut Context<Self>,
11840 ) {
11841 self.buffer.update(cx, |buffer, cx| {
11842 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
11843 });
11844 }
11845
11846 pub fn toggle_selected_diff_hunks(
11847 &mut self,
11848 _: &ToggleSelectedDiffHunks,
11849 _window: &mut Window,
11850 cx: &mut Context<Self>,
11851 ) {
11852 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
11853 self.toggle_diff_hunks_in_ranges(ranges, cx);
11854 }
11855
11856 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
11857 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
11858 self.buffer
11859 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
11860 }
11861
11862 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
11863 self.buffer.update(cx, |buffer, cx| {
11864 let ranges = vec![Anchor::min()..Anchor::max()];
11865 if !buffer.all_diff_hunks_expanded()
11866 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
11867 {
11868 buffer.collapse_diff_hunks(ranges, cx);
11869 true
11870 } else {
11871 false
11872 }
11873 })
11874 }
11875
11876 fn toggle_diff_hunks_in_ranges(
11877 &mut self,
11878 ranges: Vec<Range<Anchor>>,
11879 cx: &mut Context<'_, Editor>,
11880 ) {
11881 self.buffer.update(cx, |buffer, cx| {
11882 if buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx) {
11883 buffer.collapse_diff_hunks(ranges, cx)
11884 } else {
11885 buffer.expand_diff_hunks(ranges, cx)
11886 }
11887 })
11888 }
11889
11890 pub(crate) fn apply_all_diff_hunks(
11891 &mut self,
11892 _: &ApplyAllDiffHunks,
11893 window: &mut Window,
11894 cx: &mut Context<Self>,
11895 ) {
11896 let buffers = self.buffer.read(cx).all_buffers();
11897 for branch_buffer in buffers {
11898 branch_buffer.update(cx, |branch_buffer, cx| {
11899 branch_buffer.merge_into_base(Vec::new(), cx);
11900 });
11901 }
11902
11903 if let Some(project) = self.project.clone() {
11904 self.save(true, project, window, cx).detach_and_log_err(cx);
11905 }
11906 }
11907
11908 pub(crate) fn apply_selected_diff_hunks(
11909 &mut self,
11910 _: &ApplyDiffHunk,
11911 window: &mut Window,
11912 cx: &mut Context<Self>,
11913 ) {
11914 let snapshot = self.snapshot(window, cx);
11915 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx).into_iter());
11916 let mut ranges_by_buffer = HashMap::default();
11917 self.transact(window, cx, |editor, _window, cx| {
11918 for hunk in hunks {
11919 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
11920 ranges_by_buffer
11921 .entry(buffer.clone())
11922 .or_insert_with(Vec::new)
11923 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
11924 }
11925 }
11926
11927 for (buffer, ranges) in ranges_by_buffer {
11928 buffer.update(cx, |buffer, cx| {
11929 buffer.merge_into_base(ranges, cx);
11930 });
11931 }
11932 });
11933
11934 if let Some(project) = self.project.clone() {
11935 self.save(true, project, window, cx).detach_and_log_err(cx);
11936 }
11937 }
11938
11939 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
11940 if hovered != self.gutter_hovered {
11941 self.gutter_hovered = hovered;
11942 cx.notify();
11943 }
11944 }
11945
11946 pub fn insert_blocks(
11947 &mut self,
11948 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
11949 autoscroll: Option<Autoscroll>,
11950 cx: &mut Context<Self>,
11951 ) -> Vec<CustomBlockId> {
11952 let blocks = self
11953 .display_map
11954 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
11955 if let Some(autoscroll) = autoscroll {
11956 self.request_autoscroll(autoscroll, cx);
11957 }
11958 cx.notify();
11959 blocks
11960 }
11961
11962 pub fn resize_blocks(
11963 &mut self,
11964 heights: HashMap<CustomBlockId, u32>,
11965 autoscroll: Option<Autoscroll>,
11966 cx: &mut Context<Self>,
11967 ) {
11968 self.display_map
11969 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
11970 if let Some(autoscroll) = autoscroll {
11971 self.request_autoscroll(autoscroll, cx);
11972 }
11973 cx.notify();
11974 }
11975
11976 pub fn replace_blocks(
11977 &mut self,
11978 renderers: HashMap<CustomBlockId, RenderBlock>,
11979 autoscroll: Option<Autoscroll>,
11980 cx: &mut Context<Self>,
11981 ) {
11982 self.display_map
11983 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
11984 if let Some(autoscroll) = autoscroll {
11985 self.request_autoscroll(autoscroll, cx);
11986 }
11987 cx.notify();
11988 }
11989
11990 pub fn remove_blocks(
11991 &mut self,
11992 block_ids: HashSet<CustomBlockId>,
11993 autoscroll: Option<Autoscroll>,
11994 cx: &mut Context<Self>,
11995 ) {
11996 self.display_map.update(cx, |display_map, cx| {
11997 display_map.remove_blocks(block_ids, cx)
11998 });
11999 if let Some(autoscroll) = autoscroll {
12000 self.request_autoscroll(autoscroll, cx);
12001 }
12002 cx.notify();
12003 }
12004
12005 pub fn row_for_block(
12006 &self,
12007 block_id: CustomBlockId,
12008 cx: &mut Context<Self>,
12009 ) -> Option<DisplayRow> {
12010 self.display_map
12011 .update(cx, |map, cx| map.row_for_block(block_id, cx))
12012 }
12013
12014 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
12015 self.focused_block = Some(focused_block);
12016 }
12017
12018 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
12019 self.focused_block.take()
12020 }
12021
12022 pub fn insert_creases(
12023 &mut self,
12024 creases: impl IntoIterator<Item = Crease<Anchor>>,
12025 cx: &mut Context<Self>,
12026 ) -> Vec<CreaseId> {
12027 self.display_map
12028 .update(cx, |map, cx| map.insert_creases(creases, cx))
12029 }
12030
12031 pub fn remove_creases(
12032 &mut self,
12033 ids: impl IntoIterator<Item = CreaseId>,
12034 cx: &mut Context<Self>,
12035 ) {
12036 self.display_map
12037 .update(cx, |map, cx| map.remove_creases(ids, cx));
12038 }
12039
12040 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
12041 self.display_map
12042 .update(cx, |map, cx| map.snapshot(cx))
12043 .longest_row()
12044 }
12045
12046 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
12047 self.display_map
12048 .update(cx, |map, cx| map.snapshot(cx))
12049 .max_point()
12050 }
12051
12052 pub fn text(&self, cx: &App) -> String {
12053 self.buffer.read(cx).read(cx).text()
12054 }
12055
12056 pub fn is_empty(&self, cx: &App) -> bool {
12057 self.buffer.read(cx).read(cx).is_empty()
12058 }
12059
12060 pub fn text_option(&self, cx: &App) -> Option<String> {
12061 let text = self.text(cx);
12062 let text = text.trim();
12063
12064 if text.is_empty() {
12065 return None;
12066 }
12067
12068 Some(text.to_string())
12069 }
12070
12071 pub fn set_text(
12072 &mut self,
12073 text: impl Into<Arc<str>>,
12074 window: &mut Window,
12075 cx: &mut Context<Self>,
12076 ) {
12077 self.transact(window, cx, |this, _, cx| {
12078 this.buffer
12079 .read(cx)
12080 .as_singleton()
12081 .expect("you can only call set_text on editors for singleton buffers")
12082 .update(cx, |buffer, cx| buffer.set_text(text, cx));
12083 });
12084 }
12085
12086 pub fn display_text(&self, cx: &mut App) -> String {
12087 self.display_map
12088 .update(cx, |map, cx| map.snapshot(cx))
12089 .text()
12090 }
12091
12092 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
12093 let mut wrap_guides = smallvec::smallvec![];
12094
12095 if self.show_wrap_guides == Some(false) {
12096 return wrap_guides;
12097 }
12098
12099 let settings = self.buffer.read(cx).settings_at(0, cx);
12100 if settings.show_wrap_guides {
12101 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
12102 wrap_guides.push((soft_wrap as usize, true));
12103 } else if let SoftWrap::Bounded(soft_wrap) = self.soft_wrap_mode(cx) {
12104 wrap_guides.push((soft_wrap as usize, true));
12105 }
12106 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
12107 }
12108
12109 wrap_guides
12110 }
12111
12112 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
12113 let settings = self.buffer.read(cx).settings_at(0, cx);
12114 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
12115 match mode {
12116 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
12117 SoftWrap::None
12118 }
12119 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
12120 language_settings::SoftWrap::PreferredLineLength => {
12121 SoftWrap::Column(settings.preferred_line_length)
12122 }
12123 language_settings::SoftWrap::Bounded => {
12124 SoftWrap::Bounded(settings.preferred_line_length)
12125 }
12126 }
12127 }
12128
12129 pub fn set_soft_wrap_mode(
12130 &mut self,
12131 mode: language_settings::SoftWrap,
12132
12133 cx: &mut Context<Self>,
12134 ) {
12135 self.soft_wrap_mode_override = Some(mode);
12136 cx.notify();
12137 }
12138
12139 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
12140 self.text_style_refinement = Some(style);
12141 }
12142
12143 /// called by the Element so we know what style we were most recently rendered with.
12144 pub(crate) fn set_style(
12145 &mut self,
12146 style: EditorStyle,
12147 window: &mut Window,
12148 cx: &mut Context<Self>,
12149 ) {
12150 let rem_size = window.rem_size();
12151 self.display_map.update(cx, |map, cx| {
12152 map.set_font(
12153 style.text.font(),
12154 style.text.font_size.to_pixels(rem_size),
12155 cx,
12156 )
12157 });
12158 self.style = Some(style);
12159 }
12160
12161 pub fn style(&self) -> Option<&EditorStyle> {
12162 self.style.as_ref()
12163 }
12164
12165 // Called by the element. This method is not designed to be called outside of the editor
12166 // element's layout code because it does not notify when rewrapping is computed synchronously.
12167 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
12168 self.display_map
12169 .update(cx, |map, cx| map.set_wrap_width(width, cx))
12170 }
12171
12172 pub fn set_soft_wrap(&mut self) {
12173 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
12174 }
12175
12176 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
12177 if self.soft_wrap_mode_override.is_some() {
12178 self.soft_wrap_mode_override.take();
12179 } else {
12180 let soft_wrap = match self.soft_wrap_mode(cx) {
12181 SoftWrap::GitDiff => return,
12182 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
12183 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
12184 language_settings::SoftWrap::None
12185 }
12186 };
12187 self.soft_wrap_mode_override = Some(soft_wrap);
12188 }
12189 cx.notify();
12190 }
12191
12192 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
12193 let Some(workspace) = self.workspace() else {
12194 return;
12195 };
12196 let fs = workspace.read(cx).app_state().fs.clone();
12197 let current_show = TabBarSettings::get_global(cx).show;
12198 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
12199 setting.show = Some(!current_show);
12200 });
12201 }
12202
12203 pub fn toggle_indent_guides(
12204 &mut self,
12205 _: &ToggleIndentGuides,
12206 _: &mut Window,
12207 cx: &mut Context<Self>,
12208 ) {
12209 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
12210 self.buffer
12211 .read(cx)
12212 .settings_at(0, cx)
12213 .indent_guides
12214 .enabled
12215 });
12216 self.show_indent_guides = Some(!currently_enabled);
12217 cx.notify();
12218 }
12219
12220 fn should_show_indent_guides(&self) -> Option<bool> {
12221 self.show_indent_guides
12222 }
12223
12224 pub fn toggle_line_numbers(
12225 &mut self,
12226 _: &ToggleLineNumbers,
12227 _: &mut Window,
12228 cx: &mut Context<Self>,
12229 ) {
12230 let mut editor_settings = EditorSettings::get_global(cx).clone();
12231 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
12232 EditorSettings::override_global(editor_settings, cx);
12233 }
12234
12235 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
12236 self.use_relative_line_numbers
12237 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
12238 }
12239
12240 pub fn toggle_relative_line_numbers(
12241 &mut self,
12242 _: &ToggleRelativeLineNumbers,
12243 _: &mut Window,
12244 cx: &mut Context<Self>,
12245 ) {
12246 let is_relative = self.should_use_relative_line_numbers(cx);
12247 self.set_relative_line_number(Some(!is_relative), cx)
12248 }
12249
12250 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
12251 self.use_relative_line_numbers = is_relative;
12252 cx.notify();
12253 }
12254
12255 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
12256 self.show_gutter = show_gutter;
12257 cx.notify();
12258 }
12259
12260 pub fn set_show_scrollbars(&mut self, show_scrollbars: bool, cx: &mut Context<Self>) {
12261 self.show_scrollbars = show_scrollbars;
12262 cx.notify();
12263 }
12264
12265 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
12266 self.show_line_numbers = Some(show_line_numbers);
12267 cx.notify();
12268 }
12269
12270 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
12271 self.show_git_diff_gutter = Some(show_git_diff_gutter);
12272 cx.notify();
12273 }
12274
12275 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
12276 self.show_code_actions = Some(show_code_actions);
12277 cx.notify();
12278 }
12279
12280 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
12281 self.show_runnables = Some(show_runnables);
12282 cx.notify();
12283 }
12284
12285 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
12286 if self.display_map.read(cx).masked != masked {
12287 self.display_map.update(cx, |map, _| map.masked = masked);
12288 }
12289 cx.notify()
12290 }
12291
12292 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
12293 self.show_wrap_guides = Some(show_wrap_guides);
12294 cx.notify();
12295 }
12296
12297 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
12298 self.show_indent_guides = Some(show_indent_guides);
12299 cx.notify();
12300 }
12301
12302 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
12303 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
12304 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
12305 if let Some(dir) = file.abs_path(cx).parent() {
12306 return Some(dir.to_owned());
12307 }
12308 }
12309
12310 if let Some(project_path) = buffer.read(cx).project_path(cx) {
12311 return Some(project_path.path.to_path_buf());
12312 }
12313 }
12314
12315 None
12316 }
12317
12318 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
12319 self.active_excerpt(cx)?
12320 .1
12321 .read(cx)
12322 .file()
12323 .and_then(|f| f.as_local())
12324 }
12325
12326 fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
12327 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
12328 let project_path = buffer.read(cx).project_path(cx)?;
12329 let project = self.project.as_ref()?.read(cx);
12330 project.absolute_path(&project_path, cx)
12331 })
12332 }
12333
12334 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
12335 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
12336 let project_path = buffer.read(cx).project_path(cx)?;
12337 let project = self.project.as_ref()?.read(cx);
12338 let entry = project.entry_for_path(&project_path, cx)?;
12339 let path = entry.path.to_path_buf();
12340 Some(path)
12341 })
12342 }
12343
12344 pub fn reveal_in_finder(
12345 &mut self,
12346 _: &RevealInFileManager,
12347 _window: &mut Window,
12348 cx: &mut Context<Self>,
12349 ) {
12350 if let Some(target) = self.target_file(cx) {
12351 cx.reveal_path(&target.abs_path(cx));
12352 }
12353 }
12354
12355 pub fn copy_path(&mut self, _: &CopyPath, _window: &mut Window, cx: &mut Context<Self>) {
12356 if let Some(path) = self.target_file_abs_path(cx) {
12357 if let Some(path) = path.to_str() {
12358 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
12359 }
12360 }
12361 }
12362
12363 pub fn copy_relative_path(
12364 &mut self,
12365 _: &CopyRelativePath,
12366 _window: &mut Window,
12367 cx: &mut Context<Self>,
12368 ) {
12369 if let Some(path) = self.target_file_path(cx) {
12370 if let Some(path) = path.to_str() {
12371 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
12372 }
12373 }
12374 }
12375
12376 pub fn toggle_git_blame(
12377 &mut self,
12378 _: &ToggleGitBlame,
12379 window: &mut Window,
12380 cx: &mut Context<Self>,
12381 ) {
12382 self.show_git_blame_gutter = !self.show_git_blame_gutter;
12383
12384 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
12385 self.start_git_blame(true, window, cx);
12386 }
12387
12388 cx.notify();
12389 }
12390
12391 pub fn toggle_git_blame_inline(
12392 &mut self,
12393 _: &ToggleGitBlameInline,
12394 window: &mut Window,
12395 cx: &mut Context<Self>,
12396 ) {
12397 self.toggle_git_blame_inline_internal(true, window, cx);
12398 cx.notify();
12399 }
12400
12401 pub fn git_blame_inline_enabled(&self) -> bool {
12402 self.git_blame_inline_enabled
12403 }
12404
12405 pub fn toggle_selection_menu(
12406 &mut self,
12407 _: &ToggleSelectionMenu,
12408 _: &mut Window,
12409 cx: &mut Context<Self>,
12410 ) {
12411 self.show_selection_menu = self
12412 .show_selection_menu
12413 .map(|show_selections_menu| !show_selections_menu)
12414 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
12415
12416 cx.notify();
12417 }
12418
12419 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
12420 self.show_selection_menu
12421 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
12422 }
12423
12424 fn start_git_blame(
12425 &mut self,
12426 user_triggered: bool,
12427 window: &mut Window,
12428 cx: &mut Context<Self>,
12429 ) {
12430 if let Some(project) = self.project.as_ref() {
12431 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
12432 return;
12433 };
12434
12435 if buffer.read(cx).file().is_none() {
12436 return;
12437 }
12438
12439 let focused = self.focus_handle(cx).contains_focused(window, cx);
12440
12441 let project = project.clone();
12442 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
12443 self.blame_subscription =
12444 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
12445 self.blame = Some(blame);
12446 }
12447 }
12448
12449 fn toggle_git_blame_inline_internal(
12450 &mut self,
12451 user_triggered: bool,
12452 window: &mut Window,
12453 cx: &mut Context<Self>,
12454 ) {
12455 if self.git_blame_inline_enabled {
12456 self.git_blame_inline_enabled = false;
12457 self.show_git_blame_inline = false;
12458 self.show_git_blame_inline_delay_task.take();
12459 } else {
12460 self.git_blame_inline_enabled = true;
12461 self.start_git_blame_inline(user_triggered, window, cx);
12462 }
12463
12464 cx.notify();
12465 }
12466
12467 fn start_git_blame_inline(
12468 &mut self,
12469 user_triggered: bool,
12470 window: &mut Window,
12471 cx: &mut Context<Self>,
12472 ) {
12473 self.start_git_blame(user_triggered, window, cx);
12474
12475 if ProjectSettings::get_global(cx)
12476 .git
12477 .inline_blame_delay()
12478 .is_some()
12479 {
12480 self.start_inline_blame_timer(window, cx);
12481 } else {
12482 self.show_git_blame_inline = true
12483 }
12484 }
12485
12486 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
12487 self.blame.as_ref()
12488 }
12489
12490 pub fn show_git_blame_gutter(&self) -> bool {
12491 self.show_git_blame_gutter
12492 }
12493
12494 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
12495 self.show_git_blame_gutter && self.has_blame_entries(cx)
12496 }
12497
12498 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
12499 self.show_git_blame_inline
12500 && self.focus_handle.is_focused(window)
12501 && !self.newest_selection_head_on_empty_line(cx)
12502 && self.has_blame_entries(cx)
12503 }
12504
12505 fn has_blame_entries(&self, cx: &App) -> bool {
12506 self.blame()
12507 .map_or(false, |blame| blame.read(cx).has_generated_entries())
12508 }
12509
12510 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
12511 let cursor_anchor = self.selections.newest_anchor().head();
12512
12513 let snapshot = self.buffer.read(cx).snapshot(cx);
12514 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
12515
12516 snapshot.line_len(buffer_row) == 0
12517 }
12518
12519 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
12520 let buffer_and_selection = maybe!({
12521 let selection = self.selections.newest::<Point>(cx);
12522 let selection_range = selection.range();
12523
12524 let multi_buffer = self.buffer().read(cx);
12525 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
12526 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
12527
12528 let (buffer, range, _) = if selection.reversed {
12529 buffer_ranges.first()
12530 } else {
12531 buffer_ranges.last()
12532 }?;
12533
12534 let selection = text::ToPoint::to_point(&range.start, &buffer).row
12535 ..text::ToPoint::to_point(&range.end, &buffer).row;
12536 Some((
12537 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
12538 selection,
12539 ))
12540 });
12541
12542 let Some((buffer, selection)) = buffer_and_selection else {
12543 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
12544 };
12545
12546 let Some(project) = self.project.as_ref() else {
12547 return Task::ready(Err(anyhow!("editor does not have project")));
12548 };
12549
12550 project.update(cx, |project, cx| {
12551 project.get_permalink_to_line(&buffer, selection, cx)
12552 })
12553 }
12554
12555 pub fn copy_permalink_to_line(
12556 &mut self,
12557 _: &CopyPermalinkToLine,
12558 window: &mut Window,
12559 cx: &mut Context<Self>,
12560 ) {
12561 let permalink_task = self.get_permalink_to_line(cx);
12562 let workspace = self.workspace();
12563
12564 cx.spawn_in(window, |_, mut cx| async move {
12565 match permalink_task.await {
12566 Ok(permalink) => {
12567 cx.update(|_, cx| {
12568 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
12569 })
12570 .ok();
12571 }
12572 Err(err) => {
12573 let message = format!("Failed to copy permalink: {err}");
12574
12575 Err::<(), anyhow::Error>(err).log_err();
12576
12577 if let Some(workspace) = workspace {
12578 workspace
12579 .update_in(&mut cx, |workspace, _, cx| {
12580 struct CopyPermalinkToLine;
12581
12582 workspace.show_toast(
12583 Toast::new(
12584 NotificationId::unique::<CopyPermalinkToLine>(),
12585 message,
12586 ),
12587 cx,
12588 )
12589 })
12590 .ok();
12591 }
12592 }
12593 }
12594 })
12595 .detach();
12596 }
12597
12598 pub fn copy_file_location(
12599 &mut self,
12600 _: &CopyFileLocation,
12601 _: &mut Window,
12602 cx: &mut Context<Self>,
12603 ) {
12604 let selection = self.selections.newest::<Point>(cx).start.row + 1;
12605 if let Some(file) = self.target_file(cx) {
12606 if let Some(path) = file.path().to_str() {
12607 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
12608 }
12609 }
12610 }
12611
12612 pub fn open_permalink_to_line(
12613 &mut self,
12614 _: &OpenPermalinkToLine,
12615 window: &mut Window,
12616 cx: &mut Context<Self>,
12617 ) {
12618 let permalink_task = self.get_permalink_to_line(cx);
12619 let workspace = self.workspace();
12620
12621 cx.spawn_in(window, |_, mut cx| async move {
12622 match permalink_task.await {
12623 Ok(permalink) => {
12624 cx.update(|_, cx| {
12625 cx.open_url(permalink.as_ref());
12626 })
12627 .ok();
12628 }
12629 Err(err) => {
12630 let message = format!("Failed to open permalink: {err}");
12631
12632 Err::<(), anyhow::Error>(err).log_err();
12633
12634 if let Some(workspace) = workspace {
12635 workspace
12636 .update(&mut cx, |workspace, cx| {
12637 struct OpenPermalinkToLine;
12638
12639 workspace.show_toast(
12640 Toast::new(
12641 NotificationId::unique::<OpenPermalinkToLine>(),
12642 message,
12643 ),
12644 cx,
12645 )
12646 })
12647 .ok();
12648 }
12649 }
12650 }
12651 })
12652 .detach();
12653 }
12654
12655 pub fn insert_uuid_v4(
12656 &mut self,
12657 _: &InsertUuidV4,
12658 window: &mut Window,
12659 cx: &mut Context<Self>,
12660 ) {
12661 self.insert_uuid(UuidVersion::V4, window, cx);
12662 }
12663
12664 pub fn insert_uuid_v7(
12665 &mut self,
12666 _: &InsertUuidV7,
12667 window: &mut Window,
12668 cx: &mut Context<Self>,
12669 ) {
12670 self.insert_uuid(UuidVersion::V7, window, cx);
12671 }
12672
12673 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
12674 self.transact(window, cx, |this, window, cx| {
12675 let edits = this
12676 .selections
12677 .all::<Point>(cx)
12678 .into_iter()
12679 .map(|selection| {
12680 let uuid = match version {
12681 UuidVersion::V4 => uuid::Uuid::new_v4(),
12682 UuidVersion::V7 => uuid::Uuid::now_v7(),
12683 };
12684
12685 (selection.range(), uuid.to_string())
12686 });
12687 this.edit(edits, cx);
12688 this.refresh_inline_completion(true, false, window, cx);
12689 });
12690 }
12691
12692 pub fn open_selections_in_multibuffer(
12693 &mut self,
12694 _: &OpenSelectionsInMultibuffer,
12695 window: &mut Window,
12696 cx: &mut Context<Self>,
12697 ) {
12698 let multibuffer = self.buffer.read(cx);
12699
12700 let Some(buffer) = multibuffer.as_singleton() else {
12701 return;
12702 };
12703
12704 let Some(workspace) = self.workspace() else {
12705 return;
12706 };
12707
12708 let locations = self
12709 .selections
12710 .disjoint_anchors()
12711 .iter()
12712 .map(|range| Location {
12713 buffer: buffer.clone(),
12714 range: range.start.text_anchor..range.end.text_anchor,
12715 })
12716 .collect::<Vec<_>>();
12717
12718 let title = multibuffer.title(cx).to_string();
12719
12720 cx.spawn_in(window, |_, mut cx| async move {
12721 workspace.update_in(&mut cx, |workspace, window, cx| {
12722 Self::open_locations_in_multibuffer(
12723 workspace,
12724 locations,
12725 format!("Selections for '{title}'"),
12726 false,
12727 MultibufferSelectionMode::All,
12728 window,
12729 cx,
12730 );
12731 })
12732 })
12733 .detach();
12734 }
12735
12736 /// Adds a row highlight for the given range. If a row has multiple highlights, the
12737 /// last highlight added will be used.
12738 ///
12739 /// If the range ends at the beginning of a line, then that line will not be highlighted.
12740 pub fn highlight_rows<T: 'static>(
12741 &mut self,
12742 range: Range<Anchor>,
12743 color: Hsla,
12744 should_autoscroll: bool,
12745 cx: &mut Context<Self>,
12746 ) {
12747 let snapshot = self.buffer().read(cx).snapshot(cx);
12748 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
12749 let ix = row_highlights.binary_search_by(|highlight| {
12750 Ordering::Equal
12751 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
12752 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
12753 });
12754
12755 if let Err(mut ix) = ix {
12756 let index = post_inc(&mut self.highlight_order);
12757
12758 // If this range intersects with the preceding highlight, then merge it with
12759 // the preceding highlight. Otherwise insert a new highlight.
12760 let mut merged = false;
12761 if ix > 0 {
12762 let prev_highlight = &mut row_highlights[ix - 1];
12763 if prev_highlight
12764 .range
12765 .end
12766 .cmp(&range.start, &snapshot)
12767 .is_ge()
12768 {
12769 ix -= 1;
12770 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
12771 prev_highlight.range.end = range.end;
12772 }
12773 merged = true;
12774 prev_highlight.index = index;
12775 prev_highlight.color = color;
12776 prev_highlight.should_autoscroll = should_autoscroll;
12777 }
12778 }
12779
12780 if !merged {
12781 row_highlights.insert(
12782 ix,
12783 RowHighlight {
12784 range: range.clone(),
12785 index,
12786 color,
12787 should_autoscroll,
12788 },
12789 );
12790 }
12791
12792 // If any of the following highlights intersect with this one, merge them.
12793 while let Some(next_highlight) = row_highlights.get(ix + 1) {
12794 let highlight = &row_highlights[ix];
12795 if next_highlight
12796 .range
12797 .start
12798 .cmp(&highlight.range.end, &snapshot)
12799 .is_le()
12800 {
12801 if next_highlight
12802 .range
12803 .end
12804 .cmp(&highlight.range.end, &snapshot)
12805 .is_gt()
12806 {
12807 row_highlights[ix].range.end = next_highlight.range.end;
12808 }
12809 row_highlights.remove(ix + 1);
12810 } else {
12811 break;
12812 }
12813 }
12814 }
12815 }
12816
12817 /// Remove any highlighted row ranges of the given type that intersect the
12818 /// given ranges.
12819 pub fn remove_highlighted_rows<T: 'static>(
12820 &mut self,
12821 ranges_to_remove: Vec<Range<Anchor>>,
12822 cx: &mut Context<Self>,
12823 ) {
12824 let snapshot = self.buffer().read(cx).snapshot(cx);
12825 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
12826 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
12827 row_highlights.retain(|highlight| {
12828 while let Some(range_to_remove) = ranges_to_remove.peek() {
12829 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
12830 Ordering::Less | Ordering::Equal => {
12831 ranges_to_remove.next();
12832 }
12833 Ordering::Greater => {
12834 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
12835 Ordering::Less | Ordering::Equal => {
12836 return false;
12837 }
12838 Ordering::Greater => break,
12839 }
12840 }
12841 }
12842 }
12843
12844 true
12845 })
12846 }
12847
12848 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
12849 pub fn clear_row_highlights<T: 'static>(&mut self) {
12850 self.highlighted_rows.remove(&TypeId::of::<T>());
12851 }
12852
12853 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
12854 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
12855 self.highlighted_rows
12856 .get(&TypeId::of::<T>())
12857 .map_or(&[] as &[_], |vec| vec.as_slice())
12858 .iter()
12859 .map(|highlight| (highlight.range.clone(), highlight.color))
12860 }
12861
12862 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
12863 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
12864 /// Allows to ignore certain kinds of highlights.
12865 pub fn highlighted_display_rows(
12866 &self,
12867 window: &mut Window,
12868 cx: &mut App,
12869 ) -> BTreeMap<DisplayRow, Hsla> {
12870 let snapshot = self.snapshot(window, cx);
12871 let mut used_highlight_orders = HashMap::default();
12872 self.highlighted_rows
12873 .iter()
12874 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
12875 .fold(
12876 BTreeMap::<DisplayRow, Hsla>::new(),
12877 |mut unique_rows, highlight| {
12878 let start = highlight.range.start.to_display_point(&snapshot);
12879 let end = highlight.range.end.to_display_point(&snapshot);
12880 let start_row = start.row().0;
12881 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
12882 && end.column() == 0
12883 {
12884 end.row().0.saturating_sub(1)
12885 } else {
12886 end.row().0
12887 };
12888 for row in start_row..=end_row {
12889 let used_index =
12890 used_highlight_orders.entry(row).or_insert(highlight.index);
12891 if highlight.index >= *used_index {
12892 *used_index = highlight.index;
12893 unique_rows.insert(DisplayRow(row), highlight.color);
12894 }
12895 }
12896 unique_rows
12897 },
12898 )
12899 }
12900
12901 pub fn highlighted_display_row_for_autoscroll(
12902 &self,
12903 snapshot: &DisplaySnapshot,
12904 ) -> Option<DisplayRow> {
12905 self.highlighted_rows
12906 .values()
12907 .flat_map(|highlighted_rows| highlighted_rows.iter())
12908 .filter_map(|highlight| {
12909 if highlight.should_autoscroll {
12910 Some(highlight.range.start.to_display_point(snapshot).row())
12911 } else {
12912 None
12913 }
12914 })
12915 .min()
12916 }
12917
12918 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
12919 self.highlight_background::<SearchWithinRange>(
12920 ranges,
12921 |colors| colors.editor_document_highlight_read_background,
12922 cx,
12923 )
12924 }
12925
12926 pub fn set_breadcrumb_header(&mut self, new_header: String) {
12927 self.breadcrumb_header = Some(new_header);
12928 }
12929
12930 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
12931 self.clear_background_highlights::<SearchWithinRange>(cx);
12932 }
12933
12934 pub fn highlight_background<T: 'static>(
12935 &mut self,
12936 ranges: &[Range<Anchor>],
12937 color_fetcher: fn(&ThemeColors) -> Hsla,
12938 cx: &mut Context<Self>,
12939 ) {
12940 self.background_highlights
12941 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
12942 self.scrollbar_marker_state.dirty = true;
12943 cx.notify();
12944 }
12945
12946 pub fn clear_background_highlights<T: 'static>(
12947 &mut self,
12948 cx: &mut Context<Self>,
12949 ) -> Option<BackgroundHighlight> {
12950 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
12951 if !text_highlights.1.is_empty() {
12952 self.scrollbar_marker_state.dirty = true;
12953 cx.notify();
12954 }
12955 Some(text_highlights)
12956 }
12957
12958 pub fn highlight_gutter<T: 'static>(
12959 &mut self,
12960 ranges: &[Range<Anchor>],
12961 color_fetcher: fn(&App) -> Hsla,
12962 cx: &mut Context<Self>,
12963 ) {
12964 self.gutter_highlights
12965 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
12966 cx.notify();
12967 }
12968
12969 pub fn clear_gutter_highlights<T: 'static>(
12970 &mut self,
12971 cx: &mut Context<Self>,
12972 ) -> Option<GutterHighlight> {
12973 cx.notify();
12974 self.gutter_highlights.remove(&TypeId::of::<T>())
12975 }
12976
12977 #[cfg(feature = "test-support")]
12978 pub fn all_text_background_highlights(
12979 &self,
12980 window: &mut Window,
12981 cx: &mut Context<Self>,
12982 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
12983 let snapshot = self.snapshot(window, cx);
12984 let buffer = &snapshot.buffer_snapshot;
12985 let start = buffer.anchor_before(0);
12986 let end = buffer.anchor_after(buffer.len());
12987 let theme = cx.theme().colors();
12988 self.background_highlights_in_range(start..end, &snapshot, theme)
12989 }
12990
12991 #[cfg(feature = "test-support")]
12992 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
12993 let snapshot = self.buffer().read(cx).snapshot(cx);
12994
12995 let highlights = self
12996 .background_highlights
12997 .get(&TypeId::of::<items::BufferSearchHighlights>());
12998
12999 if let Some((_color, ranges)) = highlights {
13000 ranges
13001 .iter()
13002 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
13003 .collect_vec()
13004 } else {
13005 vec![]
13006 }
13007 }
13008
13009 fn document_highlights_for_position<'a>(
13010 &'a self,
13011 position: Anchor,
13012 buffer: &'a MultiBufferSnapshot,
13013 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
13014 let read_highlights = self
13015 .background_highlights
13016 .get(&TypeId::of::<DocumentHighlightRead>())
13017 .map(|h| &h.1);
13018 let write_highlights = self
13019 .background_highlights
13020 .get(&TypeId::of::<DocumentHighlightWrite>())
13021 .map(|h| &h.1);
13022 let left_position = position.bias_left(buffer);
13023 let right_position = position.bias_right(buffer);
13024 read_highlights
13025 .into_iter()
13026 .chain(write_highlights)
13027 .flat_map(move |ranges| {
13028 let start_ix = match ranges.binary_search_by(|probe| {
13029 let cmp = probe.end.cmp(&left_position, buffer);
13030 if cmp.is_ge() {
13031 Ordering::Greater
13032 } else {
13033 Ordering::Less
13034 }
13035 }) {
13036 Ok(i) | Err(i) => i,
13037 };
13038
13039 ranges[start_ix..]
13040 .iter()
13041 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
13042 })
13043 }
13044
13045 pub fn has_background_highlights<T: 'static>(&self) -> bool {
13046 self.background_highlights
13047 .get(&TypeId::of::<T>())
13048 .map_or(false, |(_, highlights)| !highlights.is_empty())
13049 }
13050
13051 pub fn background_highlights_in_range(
13052 &self,
13053 search_range: Range<Anchor>,
13054 display_snapshot: &DisplaySnapshot,
13055 theme: &ThemeColors,
13056 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
13057 let mut results = Vec::new();
13058 for (color_fetcher, ranges) in self.background_highlights.values() {
13059 let color = color_fetcher(theme);
13060 let start_ix = match ranges.binary_search_by(|probe| {
13061 let cmp = probe
13062 .end
13063 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
13064 if cmp.is_gt() {
13065 Ordering::Greater
13066 } else {
13067 Ordering::Less
13068 }
13069 }) {
13070 Ok(i) | Err(i) => i,
13071 };
13072 for range in &ranges[start_ix..] {
13073 if range
13074 .start
13075 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
13076 .is_ge()
13077 {
13078 break;
13079 }
13080
13081 let start = range.start.to_display_point(display_snapshot);
13082 let end = range.end.to_display_point(display_snapshot);
13083 results.push((start..end, color))
13084 }
13085 }
13086 results
13087 }
13088
13089 pub fn background_highlight_row_ranges<T: 'static>(
13090 &self,
13091 search_range: Range<Anchor>,
13092 display_snapshot: &DisplaySnapshot,
13093 count: usize,
13094 ) -> Vec<RangeInclusive<DisplayPoint>> {
13095 let mut results = Vec::new();
13096 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
13097 return vec![];
13098 };
13099
13100 let start_ix = match ranges.binary_search_by(|probe| {
13101 let cmp = probe
13102 .end
13103 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
13104 if cmp.is_gt() {
13105 Ordering::Greater
13106 } else {
13107 Ordering::Less
13108 }
13109 }) {
13110 Ok(i) | Err(i) => i,
13111 };
13112 let mut push_region = |start: Option<Point>, end: Option<Point>| {
13113 if let (Some(start_display), Some(end_display)) = (start, end) {
13114 results.push(
13115 start_display.to_display_point(display_snapshot)
13116 ..=end_display.to_display_point(display_snapshot),
13117 );
13118 }
13119 };
13120 let mut start_row: Option<Point> = None;
13121 let mut end_row: Option<Point> = None;
13122 if ranges.len() > count {
13123 return Vec::new();
13124 }
13125 for range in &ranges[start_ix..] {
13126 if range
13127 .start
13128 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
13129 .is_ge()
13130 {
13131 break;
13132 }
13133 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
13134 if let Some(current_row) = &end_row {
13135 if end.row == current_row.row {
13136 continue;
13137 }
13138 }
13139 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
13140 if start_row.is_none() {
13141 assert_eq!(end_row, None);
13142 start_row = Some(start);
13143 end_row = Some(end);
13144 continue;
13145 }
13146 if let Some(current_end) = end_row.as_mut() {
13147 if start.row > current_end.row + 1 {
13148 push_region(start_row, end_row);
13149 start_row = Some(start);
13150 end_row = Some(end);
13151 } else {
13152 // Merge two hunks.
13153 *current_end = end;
13154 }
13155 } else {
13156 unreachable!();
13157 }
13158 }
13159 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
13160 push_region(start_row, end_row);
13161 results
13162 }
13163
13164 pub fn gutter_highlights_in_range(
13165 &self,
13166 search_range: Range<Anchor>,
13167 display_snapshot: &DisplaySnapshot,
13168 cx: &App,
13169 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
13170 let mut results = Vec::new();
13171 for (color_fetcher, ranges) in self.gutter_highlights.values() {
13172 let color = color_fetcher(cx);
13173 let start_ix = match ranges.binary_search_by(|probe| {
13174 let cmp = probe
13175 .end
13176 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
13177 if cmp.is_gt() {
13178 Ordering::Greater
13179 } else {
13180 Ordering::Less
13181 }
13182 }) {
13183 Ok(i) | Err(i) => i,
13184 };
13185 for range in &ranges[start_ix..] {
13186 if range
13187 .start
13188 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
13189 .is_ge()
13190 {
13191 break;
13192 }
13193
13194 let start = range.start.to_display_point(display_snapshot);
13195 let end = range.end.to_display_point(display_snapshot);
13196 results.push((start..end, color))
13197 }
13198 }
13199 results
13200 }
13201
13202 /// Get the text ranges corresponding to the redaction query
13203 pub fn redacted_ranges(
13204 &self,
13205 search_range: Range<Anchor>,
13206 display_snapshot: &DisplaySnapshot,
13207 cx: &App,
13208 ) -> Vec<Range<DisplayPoint>> {
13209 display_snapshot
13210 .buffer_snapshot
13211 .redacted_ranges(search_range, |file| {
13212 if let Some(file) = file {
13213 file.is_private()
13214 && EditorSettings::get(
13215 Some(SettingsLocation {
13216 worktree_id: file.worktree_id(cx),
13217 path: file.path().as_ref(),
13218 }),
13219 cx,
13220 )
13221 .redact_private_values
13222 } else {
13223 false
13224 }
13225 })
13226 .map(|range| {
13227 range.start.to_display_point(display_snapshot)
13228 ..range.end.to_display_point(display_snapshot)
13229 })
13230 .collect()
13231 }
13232
13233 pub fn highlight_text<T: 'static>(
13234 &mut self,
13235 ranges: Vec<Range<Anchor>>,
13236 style: HighlightStyle,
13237 cx: &mut Context<Self>,
13238 ) {
13239 self.display_map.update(cx, |map, _| {
13240 map.highlight_text(TypeId::of::<T>(), ranges, style)
13241 });
13242 cx.notify();
13243 }
13244
13245 pub(crate) fn highlight_inlays<T: 'static>(
13246 &mut self,
13247 highlights: Vec<InlayHighlight>,
13248 style: HighlightStyle,
13249 cx: &mut Context<Self>,
13250 ) {
13251 self.display_map.update(cx, |map, _| {
13252 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
13253 });
13254 cx.notify();
13255 }
13256
13257 pub fn text_highlights<'a, T: 'static>(
13258 &'a self,
13259 cx: &'a App,
13260 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
13261 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
13262 }
13263
13264 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
13265 let cleared = self
13266 .display_map
13267 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
13268 if cleared {
13269 cx.notify();
13270 }
13271 }
13272
13273 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
13274 (self.read_only(cx) || self.blink_manager.read(cx).visible())
13275 && self.focus_handle.is_focused(window)
13276 }
13277
13278 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
13279 self.show_cursor_when_unfocused = is_enabled;
13280 cx.notify();
13281 }
13282
13283 pub fn lsp_store(&self, cx: &App) -> Option<Entity<LspStore>> {
13284 self.project
13285 .as_ref()
13286 .map(|project| project.read(cx).lsp_store())
13287 }
13288
13289 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
13290 cx.notify();
13291 }
13292
13293 fn on_buffer_event(
13294 &mut self,
13295 multibuffer: &Entity<MultiBuffer>,
13296 event: &multi_buffer::Event,
13297 window: &mut Window,
13298 cx: &mut Context<Self>,
13299 ) {
13300 match event {
13301 multi_buffer::Event::Edited {
13302 singleton_buffer_edited,
13303 edited_buffer: buffer_edited,
13304 } => {
13305 self.scrollbar_marker_state.dirty = true;
13306 self.active_indent_guides_state.dirty = true;
13307 self.refresh_active_diagnostics(cx);
13308 self.refresh_code_actions(window, cx);
13309 if self.has_active_inline_completion() {
13310 self.update_visible_inline_completion(window, cx);
13311 }
13312 if let Some(buffer) = buffer_edited {
13313 let buffer_id = buffer.read(cx).remote_id();
13314 if !self.registered_buffers.contains_key(&buffer_id) {
13315 if let Some(lsp_store) = self.lsp_store(cx) {
13316 lsp_store.update(cx, |lsp_store, cx| {
13317 self.registered_buffers.insert(
13318 buffer_id,
13319 lsp_store.register_buffer_with_language_servers(&buffer, cx),
13320 );
13321 })
13322 }
13323 }
13324 }
13325 cx.emit(EditorEvent::BufferEdited);
13326 cx.emit(SearchEvent::MatchesInvalidated);
13327 if *singleton_buffer_edited {
13328 if let Some(project) = &self.project {
13329 let project = project.read(cx);
13330 #[allow(clippy::mutable_key_type)]
13331 let languages_affected = multibuffer
13332 .read(cx)
13333 .all_buffers()
13334 .into_iter()
13335 .filter_map(|buffer| {
13336 let buffer = buffer.read(cx);
13337 let language = buffer.language()?;
13338 if project.is_local()
13339 && project
13340 .language_servers_for_local_buffer(buffer, cx)
13341 .count()
13342 == 0
13343 {
13344 None
13345 } else {
13346 Some(language)
13347 }
13348 })
13349 .cloned()
13350 .collect::<HashSet<_>>();
13351 if !languages_affected.is_empty() {
13352 self.refresh_inlay_hints(
13353 InlayHintRefreshReason::BufferEdited(languages_affected),
13354 cx,
13355 );
13356 }
13357 }
13358 }
13359
13360 let Some(project) = &self.project else { return };
13361 let (telemetry, is_via_ssh) = {
13362 let project = project.read(cx);
13363 let telemetry = project.client().telemetry().clone();
13364 let is_via_ssh = project.is_via_ssh();
13365 (telemetry, is_via_ssh)
13366 };
13367 refresh_linked_ranges(self, window, cx);
13368 telemetry.log_edit_event("editor", is_via_ssh);
13369 }
13370 multi_buffer::Event::ExcerptsAdded {
13371 buffer,
13372 predecessor,
13373 excerpts,
13374 } => {
13375 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
13376 let buffer_id = buffer.read(cx).remote_id();
13377 if self.buffer.read(cx).change_set_for(buffer_id).is_none() {
13378 if let Some(project) = &self.project {
13379 get_unstaged_changes_for_buffers(
13380 project,
13381 [buffer.clone()],
13382 self.buffer.clone(),
13383 cx,
13384 );
13385 }
13386 }
13387 cx.emit(EditorEvent::ExcerptsAdded {
13388 buffer: buffer.clone(),
13389 predecessor: *predecessor,
13390 excerpts: excerpts.clone(),
13391 });
13392 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
13393 }
13394 multi_buffer::Event::ExcerptsRemoved { ids } => {
13395 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
13396 let buffer = self.buffer.read(cx);
13397 self.registered_buffers
13398 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
13399 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
13400 }
13401 multi_buffer::Event::ExcerptsEdited { ids } => {
13402 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
13403 }
13404 multi_buffer::Event::ExcerptsExpanded { ids } => {
13405 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
13406 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
13407 }
13408 multi_buffer::Event::Reparsed(buffer_id) => {
13409 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
13410
13411 cx.emit(EditorEvent::Reparsed(*buffer_id));
13412 }
13413 multi_buffer::Event::DiffHunksToggled => {
13414 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
13415 }
13416 multi_buffer::Event::LanguageChanged(buffer_id) => {
13417 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
13418 cx.emit(EditorEvent::Reparsed(*buffer_id));
13419 cx.notify();
13420 }
13421 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
13422 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
13423 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
13424 cx.emit(EditorEvent::TitleChanged)
13425 }
13426 // multi_buffer::Event::DiffBaseChanged => {
13427 // self.scrollbar_marker_state.dirty = true;
13428 // cx.emit(EditorEvent::DiffBaseChanged);
13429 // cx.notify();
13430 // }
13431 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
13432 multi_buffer::Event::DiagnosticsUpdated => {
13433 self.refresh_active_diagnostics(cx);
13434 self.scrollbar_marker_state.dirty = true;
13435 cx.notify();
13436 }
13437 _ => {}
13438 };
13439 }
13440
13441 fn on_display_map_changed(
13442 &mut self,
13443 _: Entity<DisplayMap>,
13444 _: &mut Window,
13445 cx: &mut Context<Self>,
13446 ) {
13447 cx.notify();
13448 }
13449
13450 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
13451 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
13452 self.refresh_inline_completion(true, false, window, cx);
13453 self.refresh_inlay_hints(
13454 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
13455 self.selections.newest_anchor().head(),
13456 &self.buffer.read(cx).snapshot(cx),
13457 cx,
13458 )),
13459 cx,
13460 );
13461
13462 let old_cursor_shape = self.cursor_shape;
13463
13464 {
13465 let editor_settings = EditorSettings::get_global(cx);
13466 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
13467 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
13468 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
13469 }
13470
13471 if old_cursor_shape != self.cursor_shape {
13472 cx.emit(EditorEvent::CursorShapeChanged);
13473 }
13474
13475 let project_settings = ProjectSettings::get_global(cx);
13476 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
13477
13478 if self.mode == EditorMode::Full {
13479 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
13480 if self.git_blame_inline_enabled != inline_blame_enabled {
13481 self.toggle_git_blame_inline_internal(false, window, cx);
13482 }
13483 }
13484
13485 cx.notify();
13486 }
13487
13488 pub fn set_searchable(&mut self, searchable: bool) {
13489 self.searchable = searchable;
13490 }
13491
13492 pub fn searchable(&self) -> bool {
13493 self.searchable
13494 }
13495
13496 fn open_proposed_changes_editor(
13497 &mut self,
13498 _: &OpenProposedChangesEditor,
13499 window: &mut Window,
13500 cx: &mut Context<Self>,
13501 ) {
13502 let Some(workspace) = self.workspace() else {
13503 cx.propagate();
13504 return;
13505 };
13506
13507 let selections = self.selections.all::<usize>(cx);
13508 let multi_buffer = self.buffer.read(cx);
13509 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
13510 let mut new_selections_by_buffer = HashMap::default();
13511 for selection in selections {
13512 for (buffer, range, _) in
13513 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
13514 {
13515 let mut range = range.to_point(buffer);
13516 range.start.column = 0;
13517 range.end.column = buffer.line_len(range.end.row);
13518 new_selections_by_buffer
13519 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
13520 .or_insert(Vec::new())
13521 .push(range)
13522 }
13523 }
13524
13525 let proposed_changes_buffers = new_selections_by_buffer
13526 .into_iter()
13527 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
13528 .collect::<Vec<_>>();
13529 let proposed_changes_editor = cx.new(|cx| {
13530 ProposedChangesEditor::new(
13531 "Proposed changes",
13532 proposed_changes_buffers,
13533 self.project.clone(),
13534 window,
13535 cx,
13536 )
13537 });
13538
13539 window.defer(cx, move |window, cx| {
13540 workspace.update(cx, |workspace, cx| {
13541 workspace.active_pane().update(cx, |pane, cx| {
13542 pane.add_item(
13543 Box::new(proposed_changes_editor),
13544 true,
13545 true,
13546 None,
13547 window,
13548 cx,
13549 );
13550 });
13551 });
13552 });
13553 }
13554
13555 pub fn open_excerpts_in_split(
13556 &mut self,
13557 _: &OpenExcerptsSplit,
13558 window: &mut Window,
13559 cx: &mut Context<Self>,
13560 ) {
13561 self.open_excerpts_common(None, true, window, cx)
13562 }
13563
13564 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
13565 self.open_excerpts_common(None, false, window, cx)
13566 }
13567
13568 fn open_excerpts_common(
13569 &mut self,
13570 jump_data: Option<JumpData>,
13571 split: bool,
13572 window: &mut Window,
13573 cx: &mut Context<Self>,
13574 ) {
13575 let Some(workspace) = self.workspace() else {
13576 cx.propagate();
13577 return;
13578 };
13579
13580 if self.buffer.read(cx).is_singleton() {
13581 cx.propagate();
13582 return;
13583 }
13584
13585 let mut new_selections_by_buffer = HashMap::default();
13586 match &jump_data {
13587 Some(JumpData::MultiBufferPoint {
13588 excerpt_id,
13589 position,
13590 anchor,
13591 line_offset_from_top,
13592 }) => {
13593 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
13594 if let Some(buffer) = multi_buffer_snapshot
13595 .buffer_id_for_excerpt(*excerpt_id)
13596 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
13597 {
13598 let buffer_snapshot = buffer.read(cx).snapshot();
13599 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
13600 language::ToPoint::to_point(anchor, &buffer_snapshot)
13601 } else {
13602 buffer_snapshot.clip_point(*position, Bias::Left)
13603 };
13604 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
13605 new_selections_by_buffer.insert(
13606 buffer,
13607 (
13608 vec![jump_to_offset..jump_to_offset],
13609 Some(*line_offset_from_top),
13610 ),
13611 );
13612 }
13613 }
13614 Some(JumpData::MultiBufferRow {
13615 row,
13616 line_offset_from_top,
13617 }) => {
13618 let point = MultiBufferPoint::new(row.0, 0);
13619 if let Some((buffer, buffer_point, _)) =
13620 self.buffer.read(cx).point_to_buffer_point(point, cx)
13621 {
13622 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
13623 new_selections_by_buffer
13624 .entry(buffer)
13625 .or_insert((Vec::new(), Some(*line_offset_from_top)))
13626 .0
13627 .push(buffer_offset..buffer_offset)
13628 }
13629 }
13630 None => {
13631 let selections = self.selections.all::<usize>(cx);
13632 let multi_buffer = self.buffer.read(cx);
13633 for selection in selections {
13634 for (buffer, mut range, _) in multi_buffer
13635 .snapshot(cx)
13636 .range_to_buffer_ranges(selection.range())
13637 {
13638 // When editing branch buffers, jump to the corresponding location
13639 // in their base buffer.
13640 let mut buffer_handle = multi_buffer.buffer(buffer.remote_id()).unwrap();
13641 let buffer = buffer_handle.read(cx);
13642 if let Some(base_buffer) = buffer.base_buffer() {
13643 range = buffer.range_to_version(range, &base_buffer.read(cx).version());
13644 buffer_handle = base_buffer;
13645 }
13646
13647 if selection.reversed {
13648 mem::swap(&mut range.start, &mut range.end);
13649 }
13650 new_selections_by_buffer
13651 .entry(buffer_handle)
13652 .or_insert((Vec::new(), None))
13653 .0
13654 .push(range)
13655 }
13656 }
13657 }
13658 }
13659
13660 if new_selections_by_buffer.is_empty() {
13661 return;
13662 }
13663
13664 // We defer the pane interaction because we ourselves are a workspace item
13665 // and activating a new item causes the pane to call a method on us reentrantly,
13666 // which panics if we're on the stack.
13667 window.defer(cx, move |window, cx| {
13668 workspace.update(cx, |workspace, cx| {
13669 let pane = if split {
13670 workspace.adjacent_pane(window, cx)
13671 } else {
13672 workspace.active_pane().clone()
13673 };
13674
13675 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
13676 let editor = buffer
13677 .read(cx)
13678 .file()
13679 .is_none()
13680 .then(|| {
13681 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
13682 // so `workspace.open_project_item` will never find them, always opening a new editor.
13683 // Instead, we try to activate the existing editor in the pane first.
13684 let (editor, pane_item_index) =
13685 pane.read(cx).items().enumerate().find_map(|(i, item)| {
13686 let editor = item.downcast::<Editor>()?;
13687 let singleton_buffer =
13688 editor.read(cx).buffer().read(cx).as_singleton()?;
13689 if singleton_buffer == buffer {
13690 Some((editor, i))
13691 } else {
13692 None
13693 }
13694 })?;
13695 pane.update(cx, |pane, cx| {
13696 pane.activate_item(pane_item_index, true, true, window, cx)
13697 });
13698 Some(editor)
13699 })
13700 .flatten()
13701 .unwrap_or_else(|| {
13702 workspace.open_project_item::<Self>(
13703 pane.clone(),
13704 buffer,
13705 true,
13706 true,
13707 window,
13708 cx,
13709 )
13710 });
13711
13712 editor.update(cx, |editor, cx| {
13713 let autoscroll = match scroll_offset {
13714 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
13715 None => Autoscroll::newest(),
13716 };
13717 let nav_history = editor.nav_history.take();
13718 editor.change_selections(Some(autoscroll), window, cx, |s| {
13719 s.select_ranges(ranges);
13720 });
13721 editor.nav_history = nav_history;
13722 });
13723 }
13724 })
13725 });
13726 }
13727
13728 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
13729 let snapshot = self.buffer.read(cx).read(cx);
13730 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
13731 Some(
13732 ranges
13733 .iter()
13734 .map(move |range| {
13735 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
13736 })
13737 .collect(),
13738 )
13739 }
13740
13741 fn selection_replacement_ranges(
13742 &self,
13743 range: Range<OffsetUtf16>,
13744 cx: &mut App,
13745 ) -> Vec<Range<OffsetUtf16>> {
13746 let selections = self.selections.all::<OffsetUtf16>(cx);
13747 let newest_selection = selections
13748 .iter()
13749 .max_by_key(|selection| selection.id)
13750 .unwrap();
13751 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
13752 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
13753 let snapshot = self.buffer.read(cx).read(cx);
13754 selections
13755 .into_iter()
13756 .map(|mut selection| {
13757 selection.start.0 =
13758 (selection.start.0 as isize).saturating_add(start_delta) as usize;
13759 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
13760 snapshot.clip_offset_utf16(selection.start, Bias::Left)
13761 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
13762 })
13763 .collect()
13764 }
13765
13766 fn report_editor_event(
13767 &self,
13768 event_type: &'static str,
13769 file_extension: Option<String>,
13770 cx: &App,
13771 ) {
13772 if cfg!(any(test, feature = "test-support")) {
13773 return;
13774 }
13775
13776 let Some(project) = &self.project else { return };
13777
13778 // If None, we are in a file without an extension
13779 let file = self
13780 .buffer
13781 .read(cx)
13782 .as_singleton()
13783 .and_then(|b| b.read(cx).file());
13784 let file_extension = file_extension.or(file
13785 .as_ref()
13786 .and_then(|file| Path::new(file.file_name(cx)).extension())
13787 .and_then(|e| e.to_str())
13788 .map(|a| a.to_string()));
13789
13790 let vim_mode = cx
13791 .global::<SettingsStore>()
13792 .raw_user_settings()
13793 .get("vim_mode")
13794 == Some(&serde_json::Value::Bool(true));
13795
13796 let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
13797 == language::language_settings::InlineCompletionProvider::Copilot;
13798 let copilot_enabled_for_language = self
13799 .buffer
13800 .read(cx)
13801 .settings_at(0, cx)
13802 .show_inline_completions;
13803
13804 let project = project.read(cx);
13805 telemetry::event!(
13806 event_type,
13807 file_extension,
13808 vim_mode,
13809 copilot_enabled,
13810 copilot_enabled_for_language,
13811 is_via_ssh = project.is_via_ssh(),
13812 );
13813 }
13814
13815 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
13816 /// with each line being an array of {text, highlight} objects.
13817 fn copy_highlight_json(
13818 &mut self,
13819 _: &CopyHighlightJson,
13820 window: &mut Window,
13821 cx: &mut Context<Self>,
13822 ) {
13823 #[derive(Serialize)]
13824 struct Chunk<'a> {
13825 text: String,
13826 highlight: Option<&'a str>,
13827 }
13828
13829 let snapshot = self.buffer.read(cx).snapshot(cx);
13830 let range = self
13831 .selected_text_range(false, window, cx)
13832 .and_then(|selection| {
13833 if selection.range.is_empty() {
13834 None
13835 } else {
13836 Some(selection.range)
13837 }
13838 })
13839 .unwrap_or_else(|| 0..snapshot.len());
13840
13841 let chunks = snapshot.chunks(range, true);
13842 let mut lines = Vec::new();
13843 let mut line: VecDeque<Chunk> = VecDeque::new();
13844
13845 let Some(style) = self.style.as_ref() else {
13846 return;
13847 };
13848
13849 for chunk in chunks {
13850 let highlight = chunk
13851 .syntax_highlight_id
13852 .and_then(|id| id.name(&style.syntax));
13853 let mut chunk_lines = chunk.text.split('\n').peekable();
13854 while let Some(text) = chunk_lines.next() {
13855 let mut merged_with_last_token = false;
13856 if let Some(last_token) = line.back_mut() {
13857 if last_token.highlight == highlight {
13858 last_token.text.push_str(text);
13859 merged_with_last_token = true;
13860 }
13861 }
13862
13863 if !merged_with_last_token {
13864 line.push_back(Chunk {
13865 text: text.into(),
13866 highlight,
13867 });
13868 }
13869
13870 if chunk_lines.peek().is_some() {
13871 if line.len() > 1 && line.front().unwrap().text.is_empty() {
13872 line.pop_front();
13873 }
13874 if line.len() > 1 && line.back().unwrap().text.is_empty() {
13875 line.pop_back();
13876 }
13877
13878 lines.push(mem::take(&mut line));
13879 }
13880 }
13881 }
13882
13883 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
13884 return;
13885 };
13886 cx.write_to_clipboard(ClipboardItem::new_string(lines));
13887 }
13888
13889 pub fn open_context_menu(
13890 &mut self,
13891 _: &OpenContextMenu,
13892 window: &mut Window,
13893 cx: &mut Context<Self>,
13894 ) {
13895 self.request_autoscroll(Autoscroll::newest(), cx);
13896 let position = self.selections.newest_display(cx).start;
13897 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
13898 }
13899
13900 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
13901 &self.inlay_hint_cache
13902 }
13903
13904 pub fn replay_insert_event(
13905 &mut self,
13906 text: &str,
13907 relative_utf16_range: Option<Range<isize>>,
13908 window: &mut Window,
13909 cx: &mut Context<Self>,
13910 ) {
13911 if !self.input_enabled {
13912 cx.emit(EditorEvent::InputIgnored { text: text.into() });
13913 return;
13914 }
13915 if let Some(relative_utf16_range) = relative_utf16_range {
13916 let selections = self.selections.all::<OffsetUtf16>(cx);
13917 self.change_selections(None, window, cx, |s| {
13918 let new_ranges = selections.into_iter().map(|range| {
13919 let start = OffsetUtf16(
13920 range
13921 .head()
13922 .0
13923 .saturating_add_signed(relative_utf16_range.start),
13924 );
13925 let end = OffsetUtf16(
13926 range
13927 .head()
13928 .0
13929 .saturating_add_signed(relative_utf16_range.end),
13930 );
13931 start..end
13932 });
13933 s.select_ranges(new_ranges);
13934 });
13935 }
13936
13937 self.handle_input(text, window, cx);
13938 }
13939
13940 pub fn supports_inlay_hints(&self, cx: &App) -> bool {
13941 let Some(provider) = self.semantics_provider.as_ref() else {
13942 return false;
13943 };
13944
13945 let mut supports = false;
13946 self.buffer().read(cx).for_each_buffer(|buffer| {
13947 supports |= provider.supports_inlay_hints(buffer, cx);
13948 });
13949 supports
13950 }
13951 pub fn is_focused(&self, window: &mut Window) -> bool {
13952 self.focus_handle.is_focused(window)
13953 }
13954
13955 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
13956 cx.emit(EditorEvent::Focused);
13957
13958 if let Some(descendant) = self
13959 .last_focused_descendant
13960 .take()
13961 .and_then(|descendant| descendant.upgrade())
13962 {
13963 window.focus(&descendant);
13964 } else {
13965 if let Some(blame) = self.blame.as_ref() {
13966 blame.update(cx, GitBlame::focus)
13967 }
13968
13969 self.blink_manager.update(cx, BlinkManager::enable);
13970 self.show_cursor_names(window, cx);
13971 self.buffer.update(cx, |buffer, cx| {
13972 buffer.finalize_last_transaction(cx);
13973 if self.leader_peer_id.is_none() {
13974 buffer.set_active_selections(
13975 &self.selections.disjoint_anchors(),
13976 self.selections.line_mode,
13977 self.cursor_shape,
13978 cx,
13979 );
13980 }
13981 });
13982 }
13983 }
13984
13985 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
13986 cx.emit(EditorEvent::FocusedIn)
13987 }
13988
13989 fn handle_focus_out(
13990 &mut self,
13991 event: FocusOutEvent,
13992 _window: &mut Window,
13993 _cx: &mut Context<Self>,
13994 ) {
13995 if event.blurred != self.focus_handle {
13996 self.last_focused_descendant = Some(event.blurred);
13997 }
13998 }
13999
14000 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
14001 self.blink_manager.update(cx, BlinkManager::disable);
14002 self.buffer
14003 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
14004
14005 if let Some(blame) = self.blame.as_ref() {
14006 blame.update(cx, GitBlame::blur)
14007 }
14008 if !self.hover_state.focused(window, cx) {
14009 hide_hover(self, cx);
14010 }
14011
14012 self.hide_context_menu(window, cx);
14013 cx.emit(EditorEvent::Blurred);
14014 cx.notify();
14015 }
14016
14017 pub fn register_action<A: Action>(
14018 &mut self,
14019 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
14020 ) -> Subscription {
14021 let id = self.next_editor_action_id.post_inc();
14022 let listener = Arc::new(listener);
14023 self.editor_actions.borrow_mut().insert(
14024 id,
14025 Box::new(move |window, _| {
14026 let listener = listener.clone();
14027 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
14028 let action = action.downcast_ref().unwrap();
14029 if phase == DispatchPhase::Bubble {
14030 listener(action, window, cx)
14031 }
14032 })
14033 }),
14034 );
14035
14036 let editor_actions = self.editor_actions.clone();
14037 Subscription::new(move || {
14038 editor_actions.borrow_mut().remove(&id);
14039 })
14040 }
14041
14042 pub fn file_header_size(&self) -> u32 {
14043 FILE_HEADER_HEIGHT
14044 }
14045
14046 pub fn revert(
14047 &mut self,
14048 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
14049 window: &mut Window,
14050 cx: &mut Context<Self>,
14051 ) {
14052 self.buffer().update(cx, |multi_buffer, cx| {
14053 for (buffer_id, changes) in revert_changes {
14054 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
14055 buffer.update(cx, |buffer, cx| {
14056 buffer.edit(
14057 changes.into_iter().map(|(range, text)| {
14058 (range, text.to_string().map(Arc::<str>::from))
14059 }),
14060 None,
14061 cx,
14062 );
14063 });
14064 }
14065 }
14066 });
14067 self.change_selections(None, window, cx, |selections| selections.refresh());
14068 }
14069
14070 pub fn to_pixel_point(
14071 &self,
14072 source: multi_buffer::Anchor,
14073 editor_snapshot: &EditorSnapshot,
14074 window: &mut Window,
14075 ) -> Option<gpui::Point<Pixels>> {
14076 let source_point = source.to_display_point(editor_snapshot);
14077 self.display_to_pixel_point(source_point, editor_snapshot, window)
14078 }
14079
14080 pub fn display_to_pixel_point(
14081 &self,
14082 source: DisplayPoint,
14083 editor_snapshot: &EditorSnapshot,
14084 window: &mut Window,
14085 ) -> Option<gpui::Point<Pixels>> {
14086 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
14087 let text_layout_details = self.text_layout_details(window);
14088 let scroll_top = text_layout_details
14089 .scroll_anchor
14090 .scroll_position(editor_snapshot)
14091 .y;
14092
14093 if source.row().as_f32() < scroll_top.floor() {
14094 return None;
14095 }
14096 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
14097 let source_y = line_height * (source.row().as_f32() - scroll_top);
14098 Some(gpui::Point::new(source_x, source_y))
14099 }
14100
14101 pub fn has_active_completions_menu(&self) -> bool {
14102 self.context_menu.borrow().as_ref().map_or(false, |menu| {
14103 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
14104 })
14105 }
14106
14107 pub fn register_addon<T: Addon>(&mut self, instance: T) {
14108 self.addons
14109 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
14110 }
14111
14112 pub fn unregister_addon<T: Addon>(&mut self) {
14113 self.addons.remove(&std::any::TypeId::of::<T>());
14114 }
14115
14116 pub fn addon<T: Addon>(&self) -> Option<&T> {
14117 let type_id = std::any::TypeId::of::<T>();
14118 self.addons
14119 .get(&type_id)
14120 .and_then(|item| item.to_any().downcast_ref::<T>())
14121 }
14122
14123 fn character_size(&self, window: &mut Window) -> gpui::Point<Pixels> {
14124 let text_layout_details = self.text_layout_details(window);
14125 let style = &text_layout_details.editor_style;
14126 let font_id = window.text_system().resolve_font(&style.text.font());
14127 let font_size = style.text.font_size.to_pixels(window.rem_size());
14128 let line_height = style.text.line_height_in_pixels(window.rem_size());
14129 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
14130
14131 gpui::Point::new(em_width, line_height)
14132 }
14133}
14134
14135fn get_unstaged_changes_for_buffers(
14136 project: &Entity<Project>,
14137 buffers: impl IntoIterator<Item = Entity<Buffer>>,
14138 buffer: Entity<MultiBuffer>,
14139 cx: &mut App,
14140) {
14141 let mut tasks = Vec::new();
14142 project.update(cx, |project, cx| {
14143 for buffer in buffers {
14144 tasks.push(project.open_unstaged_changes(buffer.clone(), cx))
14145 }
14146 });
14147 cx.spawn(|mut cx| async move {
14148 let change_sets = futures::future::join_all(tasks).await;
14149 buffer
14150 .update(&mut cx, |buffer, cx| {
14151 for change_set in change_sets {
14152 if let Some(change_set) = change_set.log_err() {
14153 buffer.add_change_set(change_set, cx);
14154 }
14155 }
14156 })
14157 .ok();
14158 })
14159 .detach();
14160}
14161
14162fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
14163 let tab_size = tab_size.get() as usize;
14164 let mut width = offset;
14165
14166 for ch in text.chars() {
14167 width += if ch == '\t' {
14168 tab_size - (width % tab_size)
14169 } else {
14170 1
14171 };
14172 }
14173
14174 width - offset
14175}
14176
14177#[cfg(test)]
14178mod tests {
14179 use super::*;
14180
14181 #[test]
14182 fn test_string_size_with_expanded_tabs() {
14183 let nz = |val| NonZeroU32::new(val).unwrap();
14184 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
14185 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
14186 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
14187 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
14188 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
14189 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
14190 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
14191 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
14192 }
14193}
14194
14195/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
14196struct WordBreakingTokenizer<'a> {
14197 input: &'a str,
14198}
14199
14200impl<'a> WordBreakingTokenizer<'a> {
14201 fn new(input: &'a str) -> Self {
14202 Self { input }
14203 }
14204}
14205
14206fn is_char_ideographic(ch: char) -> bool {
14207 use unicode_script::Script::*;
14208 use unicode_script::UnicodeScript;
14209 matches!(ch.script(), Han | Tangut | Yi)
14210}
14211
14212fn is_grapheme_ideographic(text: &str) -> bool {
14213 text.chars().any(is_char_ideographic)
14214}
14215
14216fn is_grapheme_whitespace(text: &str) -> bool {
14217 text.chars().any(|x| x.is_whitespace())
14218}
14219
14220fn should_stay_with_preceding_ideograph(text: &str) -> bool {
14221 text.chars().next().map_or(false, |ch| {
14222 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
14223 })
14224}
14225
14226#[derive(PartialEq, Eq, Debug, Clone, Copy)]
14227struct WordBreakToken<'a> {
14228 token: &'a str,
14229 grapheme_len: usize,
14230 is_whitespace: bool,
14231}
14232
14233impl<'a> Iterator for WordBreakingTokenizer<'a> {
14234 /// Yields a span, the count of graphemes in the token, and whether it was
14235 /// whitespace. Note that it also breaks at word boundaries.
14236 type Item = WordBreakToken<'a>;
14237
14238 fn next(&mut self) -> Option<Self::Item> {
14239 use unicode_segmentation::UnicodeSegmentation;
14240 if self.input.is_empty() {
14241 return None;
14242 }
14243
14244 let mut iter = self.input.graphemes(true).peekable();
14245 let mut offset = 0;
14246 let mut graphemes = 0;
14247 if let Some(first_grapheme) = iter.next() {
14248 let is_whitespace = is_grapheme_whitespace(first_grapheme);
14249 offset += first_grapheme.len();
14250 graphemes += 1;
14251 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
14252 if let Some(grapheme) = iter.peek().copied() {
14253 if should_stay_with_preceding_ideograph(grapheme) {
14254 offset += grapheme.len();
14255 graphemes += 1;
14256 }
14257 }
14258 } else {
14259 let mut words = self.input[offset..].split_word_bound_indices().peekable();
14260 let mut next_word_bound = words.peek().copied();
14261 if next_word_bound.map_or(false, |(i, _)| i == 0) {
14262 next_word_bound = words.next();
14263 }
14264 while let Some(grapheme) = iter.peek().copied() {
14265 if next_word_bound.map_or(false, |(i, _)| i == offset) {
14266 break;
14267 };
14268 if is_grapheme_whitespace(grapheme) != is_whitespace {
14269 break;
14270 };
14271 offset += grapheme.len();
14272 graphemes += 1;
14273 iter.next();
14274 }
14275 }
14276 let token = &self.input[..offset];
14277 self.input = &self.input[offset..];
14278 if is_whitespace {
14279 Some(WordBreakToken {
14280 token: " ",
14281 grapheme_len: 1,
14282 is_whitespace: true,
14283 })
14284 } else {
14285 Some(WordBreakToken {
14286 token,
14287 grapheme_len: graphemes,
14288 is_whitespace: false,
14289 })
14290 }
14291 } else {
14292 None
14293 }
14294 }
14295}
14296
14297#[test]
14298fn test_word_breaking_tokenizer() {
14299 let tests: &[(&str, &[(&str, usize, bool)])] = &[
14300 ("", &[]),
14301 (" ", &[(" ", 1, true)]),
14302 ("Ʒ", &[("Ʒ", 1, false)]),
14303 ("Ǽ", &[("Ǽ", 1, false)]),
14304 ("⋑", &[("⋑", 1, false)]),
14305 ("⋑⋑", &[("⋑⋑", 2, false)]),
14306 (
14307 "原理,进而",
14308 &[
14309 ("原", 1, false),
14310 ("理,", 2, false),
14311 ("进", 1, false),
14312 ("而", 1, false),
14313 ],
14314 ),
14315 (
14316 "hello world",
14317 &[("hello", 5, false), (" ", 1, true), ("world", 5, false)],
14318 ),
14319 (
14320 "hello, world",
14321 &[("hello,", 6, false), (" ", 1, true), ("world", 5, false)],
14322 ),
14323 (
14324 " hello world",
14325 &[
14326 (" ", 1, true),
14327 ("hello", 5, false),
14328 (" ", 1, true),
14329 ("world", 5, false),
14330 ],
14331 ),
14332 (
14333 "这是什么 \n 钢笔",
14334 &[
14335 ("这", 1, false),
14336 ("是", 1, false),
14337 ("什", 1, false),
14338 ("么", 1, false),
14339 (" ", 1, true),
14340 ("钢", 1, false),
14341 ("笔", 1, false),
14342 ],
14343 ),
14344 (" mutton", &[(" ", 1, true), ("mutton", 6, false)]),
14345 ];
14346
14347 for (input, result) in tests {
14348 assert_eq!(
14349 WordBreakingTokenizer::new(input).collect::<Vec<_>>(),
14350 result
14351 .iter()
14352 .copied()
14353 .map(|(token, grapheme_len, is_whitespace)| WordBreakToken {
14354 token,
14355 grapheme_len,
14356 is_whitespace,
14357 })
14358 .collect::<Vec<_>>()
14359 );
14360 }
14361}
14362
14363fn wrap_with_prefix(
14364 line_prefix: String,
14365 unwrapped_text: String,
14366 wrap_column: usize,
14367 tab_size: NonZeroU32,
14368) -> String {
14369 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
14370 let mut wrapped_text = String::new();
14371 let mut current_line = line_prefix.clone();
14372
14373 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
14374 let mut current_line_len = line_prefix_len;
14375 for WordBreakToken {
14376 token,
14377 grapheme_len,
14378 is_whitespace,
14379 } in tokenizer
14380 {
14381 if current_line_len + grapheme_len > wrap_column && current_line_len != line_prefix_len {
14382 wrapped_text.push_str(current_line.trim_end());
14383 wrapped_text.push('\n');
14384 current_line.truncate(line_prefix.len());
14385 current_line_len = line_prefix_len;
14386 if !is_whitespace {
14387 current_line.push_str(token);
14388 current_line_len += grapheme_len;
14389 }
14390 } else if !is_whitespace {
14391 current_line.push_str(token);
14392 current_line_len += grapheme_len;
14393 } else if current_line_len != line_prefix_len {
14394 current_line.push(' ');
14395 current_line_len += 1;
14396 }
14397 }
14398
14399 if !current_line.is_empty() {
14400 wrapped_text.push_str(¤t_line);
14401 }
14402 wrapped_text
14403}
14404
14405#[test]
14406fn test_wrap_with_prefix() {
14407 assert_eq!(
14408 wrap_with_prefix(
14409 "# ".to_string(),
14410 "abcdefg".to_string(),
14411 4,
14412 NonZeroU32::new(4).unwrap()
14413 ),
14414 "# abcdefg"
14415 );
14416 assert_eq!(
14417 wrap_with_prefix(
14418 "".to_string(),
14419 "\thello world".to_string(),
14420 8,
14421 NonZeroU32::new(4).unwrap()
14422 ),
14423 "hello\nworld"
14424 );
14425 assert_eq!(
14426 wrap_with_prefix(
14427 "// ".to_string(),
14428 "xx \nyy zz aa bb cc".to_string(),
14429 12,
14430 NonZeroU32::new(4).unwrap()
14431 ),
14432 "// xx yy zz\n// aa bb cc"
14433 );
14434 assert_eq!(
14435 wrap_with_prefix(
14436 String::new(),
14437 "这是什么 \n 钢笔".to_string(),
14438 3,
14439 NonZeroU32::new(4).unwrap()
14440 ),
14441 "这是什\n么 钢\n笔"
14442 );
14443}
14444
14445pub trait CollaborationHub {
14446 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
14447 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
14448 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
14449}
14450
14451impl CollaborationHub for Entity<Project> {
14452 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
14453 self.read(cx).collaborators()
14454 }
14455
14456 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
14457 self.read(cx).user_store().read(cx).participant_indices()
14458 }
14459
14460 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
14461 let this = self.read(cx);
14462 let user_ids = this.collaborators().values().map(|c| c.user_id);
14463 this.user_store().read_with(cx, |user_store, cx| {
14464 user_store.participant_names(user_ids, cx)
14465 })
14466 }
14467}
14468
14469pub trait SemanticsProvider {
14470 fn hover(
14471 &self,
14472 buffer: &Entity<Buffer>,
14473 position: text::Anchor,
14474 cx: &mut App,
14475 ) -> Option<Task<Vec<project::Hover>>>;
14476
14477 fn inlay_hints(
14478 &self,
14479 buffer_handle: Entity<Buffer>,
14480 range: Range<text::Anchor>,
14481 cx: &mut App,
14482 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
14483
14484 fn resolve_inlay_hint(
14485 &self,
14486 hint: InlayHint,
14487 buffer_handle: Entity<Buffer>,
14488 server_id: LanguageServerId,
14489 cx: &mut App,
14490 ) -> Option<Task<anyhow::Result<InlayHint>>>;
14491
14492 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &App) -> bool;
14493
14494 fn document_highlights(
14495 &self,
14496 buffer: &Entity<Buffer>,
14497 position: text::Anchor,
14498 cx: &mut App,
14499 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
14500
14501 fn definitions(
14502 &self,
14503 buffer: &Entity<Buffer>,
14504 position: text::Anchor,
14505 kind: GotoDefinitionKind,
14506 cx: &mut App,
14507 ) -> Option<Task<Result<Vec<LocationLink>>>>;
14508
14509 fn range_for_rename(
14510 &self,
14511 buffer: &Entity<Buffer>,
14512 position: text::Anchor,
14513 cx: &mut App,
14514 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
14515
14516 fn perform_rename(
14517 &self,
14518 buffer: &Entity<Buffer>,
14519 position: text::Anchor,
14520 new_name: String,
14521 cx: &mut App,
14522 ) -> Option<Task<Result<ProjectTransaction>>>;
14523}
14524
14525pub trait CompletionProvider {
14526 fn completions(
14527 &self,
14528 buffer: &Entity<Buffer>,
14529 buffer_position: text::Anchor,
14530 trigger: CompletionContext,
14531 window: &mut Window,
14532 cx: &mut Context<Editor>,
14533 ) -> Task<Result<Vec<Completion>>>;
14534
14535 fn resolve_completions(
14536 &self,
14537 buffer: Entity<Buffer>,
14538 completion_indices: Vec<usize>,
14539 completions: Rc<RefCell<Box<[Completion]>>>,
14540 cx: &mut Context<Editor>,
14541 ) -> Task<Result<bool>>;
14542
14543 fn apply_additional_edits_for_completion(
14544 &self,
14545 _buffer: Entity<Buffer>,
14546 _completions: Rc<RefCell<Box<[Completion]>>>,
14547 _completion_index: usize,
14548 _push_to_history: bool,
14549 _cx: &mut Context<Editor>,
14550 ) -> Task<Result<Option<language::Transaction>>> {
14551 Task::ready(Ok(None))
14552 }
14553
14554 fn is_completion_trigger(
14555 &self,
14556 buffer: &Entity<Buffer>,
14557 position: language::Anchor,
14558 text: &str,
14559 trigger_in_words: bool,
14560 cx: &mut Context<Editor>,
14561 ) -> bool;
14562
14563 fn sort_completions(&self) -> bool {
14564 true
14565 }
14566}
14567
14568pub trait CodeActionProvider {
14569 fn id(&self) -> Arc<str>;
14570
14571 fn code_actions(
14572 &self,
14573 buffer: &Entity<Buffer>,
14574 range: Range<text::Anchor>,
14575 window: &mut Window,
14576 cx: &mut App,
14577 ) -> Task<Result<Vec<CodeAction>>>;
14578
14579 fn apply_code_action(
14580 &self,
14581 buffer_handle: Entity<Buffer>,
14582 action: CodeAction,
14583 excerpt_id: ExcerptId,
14584 push_to_history: bool,
14585 window: &mut Window,
14586 cx: &mut App,
14587 ) -> Task<Result<ProjectTransaction>>;
14588}
14589
14590impl CodeActionProvider for Entity<Project> {
14591 fn id(&self) -> Arc<str> {
14592 "project".into()
14593 }
14594
14595 fn code_actions(
14596 &self,
14597 buffer: &Entity<Buffer>,
14598 range: Range<text::Anchor>,
14599 _window: &mut Window,
14600 cx: &mut App,
14601 ) -> Task<Result<Vec<CodeAction>>> {
14602 self.update(cx, |project, cx| {
14603 project.code_actions(buffer, range, None, cx)
14604 })
14605 }
14606
14607 fn apply_code_action(
14608 &self,
14609 buffer_handle: Entity<Buffer>,
14610 action: CodeAction,
14611 _excerpt_id: ExcerptId,
14612 push_to_history: bool,
14613 _window: &mut Window,
14614 cx: &mut App,
14615 ) -> Task<Result<ProjectTransaction>> {
14616 self.update(cx, |project, cx| {
14617 project.apply_code_action(buffer_handle, action, push_to_history, cx)
14618 })
14619 }
14620}
14621
14622fn snippet_completions(
14623 project: &Project,
14624 buffer: &Entity<Buffer>,
14625 buffer_position: text::Anchor,
14626 cx: &mut App,
14627) -> Task<Result<Vec<Completion>>> {
14628 let language = buffer.read(cx).language_at(buffer_position);
14629 let language_name = language.as_ref().map(|language| language.lsp_id());
14630 let snippet_store = project.snippets().read(cx);
14631 let snippets = snippet_store.snippets_for(language_name, cx);
14632
14633 if snippets.is_empty() {
14634 return Task::ready(Ok(vec![]));
14635 }
14636 let snapshot = buffer.read(cx).text_snapshot();
14637 let chars: String = snapshot
14638 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
14639 .collect();
14640
14641 let scope = language.map(|language| language.default_scope());
14642 let executor = cx.background_executor().clone();
14643
14644 cx.background_executor().spawn(async move {
14645 let classifier = CharClassifier::new(scope).for_completion(true);
14646 let mut last_word = chars
14647 .chars()
14648 .take_while(|c| classifier.is_word(*c))
14649 .collect::<String>();
14650 last_word = last_word.chars().rev().collect();
14651
14652 if last_word.is_empty() {
14653 return Ok(vec![]);
14654 }
14655
14656 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
14657 let to_lsp = |point: &text::Anchor| {
14658 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
14659 point_to_lsp(end)
14660 };
14661 let lsp_end = to_lsp(&buffer_position);
14662
14663 let candidates = snippets
14664 .iter()
14665 .enumerate()
14666 .flat_map(|(ix, snippet)| {
14667 snippet
14668 .prefix
14669 .iter()
14670 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
14671 })
14672 .collect::<Vec<StringMatchCandidate>>();
14673
14674 let mut matches = fuzzy::match_strings(
14675 &candidates,
14676 &last_word,
14677 last_word.chars().any(|c| c.is_uppercase()),
14678 100,
14679 &Default::default(),
14680 executor,
14681 )
14682 .await;
14683
14684 // Remove all candidates where the query's start does not match the start of any word in the candidate
14685 if let Some(query_start) = last_word.chars().next() {
14686 matches.retain(|string_match| {
14687 split_words(&string_match.string).any(|word| {
14688 // Check that the first codepoint of the word as lowercase matches the first
14689 // codepoint of the query as lowercase
14690 word.chars()
14691 .flat_map(|codepoint| codepoint.to_lowercase())
14692 .zip(query_start.to_lowercase())
14693 .all(|(word_cp, query_cp)| word_cp == query_cp)
14694 })
14695 });
14696 }
14697
14698 let matched_strings = matches
14699 .into_iter()
14700 .map(|m| m.string)
14701 .collect::<HashSet<_>>();
14702
14703 let result: Vec<Completion> = snippets
14704 .into_iter()
14705 .filter_map(|snippet| {
14706 let matching_prefix = snippet
14707 .prefix
14708 .iter()
14709 .find(|prefix| matched_strings.contains(*prefix))?;
14710 let start = as_offset - last_word.len();
14711 let start = snapshot.anchor_before(start);
14712 let range = start..buffer_position;
14713 let lsp_start = to_lsp(&start);
14714 let lsp_range = lsp::Range {
14715 start: lsp_start,
14716 end: lsp_end,
14717 };
14718 Some(Completion {
14719 old_range: range,
14720 new_text: snippet.body.clone(),
14721 resolved: false,
14722 label: CodeLabel {
14723 text: matching_prefix.clone(),
14724 runs: vec![],
14725 filter_range: 0..matching_prefix.len(),
14726 },
14727 server_id: LanguageServerId(usize::MAX),
14728 documentation: snippet
14729 .description
14730 .clone()
14731 .map(CompletionDocumentation::SingleLine),
14732 lsp_completion: lsp::CompletionItem {
14733 label: snippet.prefix.first().unwrap().clone(),
14734 kind: Some(CompletionItemKind::SNIPPET),
14735 label_details: snippet.description.as_ref().map(|description| {
14736 lsp::CompletionItemLabelDetails {
14737 detail: Some(description.clone()),
14738 description: None,
14739 }
14740 }),
14741 insert_text_format: Some(InsertTextFormat::SNIPPET),
14742 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
14743 lsp::InsertReplaceEdit {
14744 new_text: snippet.body.clone(),
14745 insert: lsp_range,
14746 replace: lsp_range,
14747 },
14748 )),
14749 filter_text: Some(snippet.body.clone()),
14750 sort_text: Some(char::MAX.to_string()),
14751 ..Default::default()
14752 },
14753 confirm: None,
14754 })
14755 })
14756 .collect();
14757
14758 Ok(result)
14759 })
14760}
14761
14762impl CompletionProvider for Entity<Project> {
14763 fn completions(
14764 &self,
14765 buffer: &Entity<Buffer>,
14766 buffer_position: text::Anchor,
14767 options: CompletionContext,
14768 _window: &mut Window,
14769 cx: &mut Context<Editor>,
14770 ) -> Task<Result<Vec<Completion>>> {
14771 self.update(cx, |project, cx| {
14772 let snippets = snippet_completions(project, buffer, buffer_position, cx);
14773 let project_completions = project.completions(buffer, buffer_position, options, cx);
14774 cx.background_executor().spawn(async move {
14775 let mut completions = project_completions.await?;
14776 let snippets_completions = snippets.await?;
14777 completions.extend(snippets_completions);
14778 Ok(completions)
14779 })
14780 })
14781 }
14782
14783 fn resolve_completions(
14784 &self,
14785 buffer: Entity<Buffer>,
14786 completion_indices: Vec<usize>,
14787 completions: Rc<RefCell<Box<[Completion]>>>,
14788 cx: &mut Context<Editor>,
14789 ) -> Task<Result<bool>> {
14790 self.update(cx, |project, cx| {
14791 project.lsp_store().update(cx, |lsp_store, cx| {
14792 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
14793 })
14794 })
14795 }
14796
14797 fn apply_additional_edits_for_completion(
14798 &self,
14799 buffer: Entity<Buffer>,
14800 completions: Rc<RefCell<Box<[Completion]>>>,
14801 completion_index: usize,
14802 push_to_history: bool,
14803 cx: &mut Context<Editor>,
14804 ) -> Task<Result<Option<language::Transaction>>> {
14805 self.update(cx, |project, cx| {
14806 project.lsp_store().update(cx, |lsp_store, cx| {
14807 lsp_store.apply_additional_edits_for_completion(
14808 buffer,
14809 completions,
14810 completion_index,
14811 push_to_history,
14812 cx,
14813 )
14814 })
14815 })
14816 }
14817
14818 fn is_completion_trigger(
14819 &self,
14820 buffer: &Entity<Buffer>,
14821 position: language::Anchor,
14822 text: &str,
14823 trigger_in_words: bool,
14824 cx: &mut Context<Editor>,
14825 ) -> bool {
14826 let mut chars = text.chars();
14827 let char = if let Some(char) = chars.next() {
14828 char
14829 } else {
14830 return false;
14831 };
14832 if chars.next().is_some() {
14833 return false;
14834 }
14835
14836 let buffer = buffer.read(cx);
14837 let snapshot = buffer.snapshot();
14838 if !snapshot.settings_at(position, cx).show_completions_on_input {
14839 return false;
14840 }
14841 let classifier = snapshot.char_classifier_at(position).for_completion(true);
14842 if trigger_in_words && classifier.is_word(char) {
14843 return true;
14844 }
14845
14846 buffer.completion_triggers().contains(text)
14847 }
14848}
14849
14850impl SemanticsProvider for Entity<Project> {
14851 fn hover(
14852 &self,
14853 buffer: &Entity<Buffer>,
14854 position: text::Anchor,
14855 cx: &mut App,
14856 ) -> Option<Task<Vec<project::Hover>>> {
14857 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
14858 }
14859
14860 fn document_highlights(
14861 &self,
14862 buffer: &Entity<Buffer>,
14863 position: text::Anchor,
14864 cx: &mut App,
14865 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
14866 Some(self.update(cx, |project, cx| {
14867 project.document_highlights(buffer, position, cx)
14868 }))
14869 }
14870
14871 fn definitions(
14872 &self,
14873 buffer: &Entity<Buffer>,
14874 position: text::Anchor,
14875 kind: GotoDefinitionKind,
14876 cx: &mut App,
14877 ) -> Option<Task<Result<Vec<LocationLink>>>> {
14878 Some(self.update(cx, |project, cx| match kind {
14879 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
14880 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
14881 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
14882 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
14883 }))
14884 }
14885
14886 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &App) -> bool {
14887 // TODO: make this work for remote projects
14888 self.read(cx)
14889 .language_servers_for_local_buffer(buffer.read(cx), cx)
14890 .any(
14891 |(_, server)| match server.capabilities().inlay_hint_provider {
14892 Some(lsp::OneOf::Left(enabled)) => enabled,
14893 Some(lsp::OneOf::Right(_)) => true,
14894 None => false,
14895 },
14896 )
14897 }
14898
14899 fn inlay_hints(
14900 &self,
14901 buffer_handle: Entity<Buffer>,
14902 range: Range<text::Anchor>,
14903 cx: &mut App,
14904 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
14905 Some(self.update(cx, |project, cx| {
14906 project.inlay_hints(buffer_handle, range, cx)
14907 }))
14908 }
14909
14910 fn resolve_inlay_hint(
14911 &self,
14912 hint: InlayHint,
14913 buffer_handle: Entity<Buffer>,
14914 server_id: LanguageServerId,
14915 cx: &mut App,
14916 ) -> Option<Task<anyhow::Result<InlayHint>>> {
14917 Some(self.update(cx, |project, cx| {
14918 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
14919 }))
14920 }
14921
14922 fn range_for_rename(
14923 &self,
14924 buffer: &Entity<Buffer>,
14925 position: text::Anchor,
14926 cx: &mut App,
14927 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
14928 Some(self.update(cx, |project, cx| {
14929 let buffer = buffer.clone();
14930 let task = project.prepare_rename(buffer.clone(), position, cx);
14931 cx.spawn(|_, mut cx| async move {
14932 Ok(match task.await? {
14933 PrepareRenameResponse::Success(range) => Some(range),
14934 PrepareRenameResponse::InvalidPosition => None,
14935 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
14936 // Fallback on using TreeSitter info to determine identifier range
14937 buffer.update(&mut cx, |buffer, _| {
14938 let snapshot = buffer.snapshot();
14939 let (range, kind) = snapshot.surrounding_word(position);
14940 if kind != Some(CharKind::Word) {
14941 return None;
14942 }
14943 Some(
14944 snapshot.anchor_before(range.start)
14945 ..snapshot.anchor_after(range.end),
14946 )
14947 })?
14948 }
14949 })
14950 })
14951 }))
14952 }
14953
14954 fn perform_rename(
14955 &self,
14956 buffer: &Entity<Buffer>,
14957 position: text::Anchor,
14958 new_name: String,
14959 cx: &mut App,
14960 ) -> Option<Task<Result<ProjectTransaction>>> {
14961 Some(self.update(cx, |project, cx| {
14962 project.perform_rename(buffer.clone(), position, new_name, cx)
14963 }))
14964 }
14965}
14966
14967fn inlay_hint_settings(
14968 location: Anchor,
14969 snapshot: &MultiBufferSnapshot,
14970 cx: &mut Context<Editor>,
14971) -> InlayHintSettings {
14972 let file = snapshot.file_at(location);
14973 let language = snapshot.language_at(location).map(|l| l.name());
14974 language_settings(language, file, cx).inlay_hints
14975}
14976
14977fn consume_contiguous_rows(
14978 contiguous_row_selections: &mut Vec<Selection<Point>>,
14979 selection: &Selection<Point>,
14980 display_map: &DisplaySnapshot,
14981 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
14982) -> (MultiBufferRow, MultiBufferRow) {
14983 contiguous_row_selections.push(selection.clone());
14984 let start_row = MultiBufferRow(selection.start.row);
14985 let mut end_row = ending_row(selection, display_map);
14986
14987 while let Some(next_selection) = selections.peek() {
14988 if next_selection.start.row <= end_row.0 {
14989 end_row = ending_row(next_selection, display_map);
14990 contiguous_row_selections.push(selections.next().unwrap().clone());
14991 } else {
14992 break;
14993 }
14994 }
14995 (start_row, end_row)
14996}
14997
14998fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
14999 if next_selection.end.column > 0 || next_selection.is_empty() {
15000 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
15001 } else {
15002 MultiBufferRow(next_selection.end.row)
15003 }
15004}
15005
15006impl EditorSnapshot {
15007 pub fn remote_selections_in_range<'a>(
15008 &'a self,
15009 range: &'a Range<Anchor>,
15010 collaboration_hub: &dyn CollaborationHub,
15011 cx: &'a App,
15012 ) -> impl 'a + Iterator<Item = RemoteSelection> {
15013 let participant_names = collaboration_hub.user_names(cx);
15014 let participant_indices = collaboration_hub.user_participant_indices(cx);
15015 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
15016 let collaborators_by_replica_id = collaborators_by_peer_id
15017 .iter()
15018 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
15019 .collect::<HashMap<_, _>>();
15020 self.buffer_snapshot
15021 .selections_in_range(range, false)
15022 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
15023 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
15024 let participant_index = participant_indices.get(&collaborator.user_id).copied();
15025 let user_name = participant_names.get(&collaborator.user_id).cloned();
15026 Some(RemoteSelection {
15027 replica_id,
15028 selection,
15029 cursor_shape,
15030 line_mode,
15031 participant_index,
15032 peer_id: collaborator.peer_id,
15033 user_name,
15034 })
15035 })
15036 }
15037
15038 pub fn hunks_for_ranges(
15039 &self,
15040 ranges: impl Iterator<Item = Range<Point>>,
15041 ) -> Vec<MultiBufferDiffHunk> {
15042 let mut hunks = Vec::new();
15043 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
15044 HashMap::default();
15045 for query_range in ranges {
15046 let query_rows =
15047 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
15048 for hunk in self.buffer_snapshot.diff_hunks_in_range(
15049 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
15050 ) {
15051 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
15052 // when the caret is just above or just below the deleted hunk.
15053 let allow_adjacent = hunk.status() == DiffHunkStatus::Removed;
15054 let related_to_selection = if allow_adjacent {
15055 hunk.row_range.overlaps(&query_rows)
15056 || hunk.row_range.start == query_rows.end
15057 || hunk.row_range.end == query_rows.start
15058 } else {
15059 hunk.row_range.overlaps(&query_rows)
15060 };
15061 if related_to_selection {
15062 if !processed_buffer_rows
15063 .entry(hunk.buffer_id)
15064 .or_default()
15065 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
15066 {
15067 continue;
15068 }
15069 hunks.push(hunk);
15070 }
15071 }
15072 }
15073
15074 hunks
15075 }
15076
15077 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
15078 self.display_snapshot.buffer_snapshot.language_at(position)
15079 }
15080
15081 pub fn is_focused(&self) -> bool {
15082 self.is_focused
15083 }
15084
15085 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
15086 self.placeholder_text.as_ref()
15087 }
15088
15089 pub fn scroll_position(&self) -> gpui::Point<f32> {
15090 self.scroll_anchor.scroll_position(&self.display_snapshot)
15091 }
15092
15093 fn gutter_dimensions(
15094 &self,
15095 font_id: FontId,
15096 font_size: Pixels,
15097 max_line_number_width: Pixels,
15098 cx: &App,
15099 ) -> Option<GutterDimensions> {
15100 if !self.show_gutter {
15101 return None;
15102 }
15103
15104 let descent = cx.text_system().descent(font_id, font_size);
15105 let em_width = cx.text_system().em_width(font_id, font_size).log_err()?;
15106 let em_advance = cx.text_system().em_advance(font_id, font_size).log_err()?;
15107
15108 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
15109 matches!(
15110 ProjectSettings::get_global(cx).git.git_gutter,
15111 Some(GitGutterSetting::TrackedFiles)
15112 )
15113 });
15114 let gutter_settings = EditorSettings::get_global(cx).gutter;
15115 let show_line_numbers = self
15116 .show_line_numbers
15117 .unwrap_or(gutter_settings.line_numbers);
15118 let line_gutter_width = if show_line_numbers {
15119 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
15120 let min_width_for_number_on_gutter = em_advance * 4.0;
15121 max_line_number_width.max(min_width_for_number_on_gutter)
15122 } else {
15123 0.0.into()
15124 };
15125
15126 let show_code_actions = self
15127 .show_code_actions
15128 .unwrap_or(gutter_settings.code_actions);
15129
15130 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
15131
15132 let git_blame_entries_width =
15133 self.git_blame_gutter_max_author_length
15134 .map(|max_author_length| {
15135 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
15136
15137 /// The number of characters to dedicate to gaps and margins.
15138 const SPACING_WIDTH: usize = 4;
15139
15140 let max_char_count = max_author_length
15141 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
15142 + ::git::SHORT_SHA_LENGTH
15143 + MAX_RELATIVE_TIMESTAMP.len()
15144 + SPACING_WIDTH;
15145
15146 em_advance * max_char_count
15147 });
15148
15149 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
15150 left_padding += if show_code_actions || show_runnables {
15151 em_width * 3.0
15152 } else if show_git_gutter && show_line_numbers {
15153 em_width * 2.0
15154 } else if show_git_gutter || show_line_numbers {
15155 em_width
15156 } else {
15157 px(0.)
15158 };
15159
15160 let right_padding = if gutter_settings.folds && show_line_numbers {
15161 em_width * 4.0
15162 } else if gutter_settings.folds {
15163 em_width * 3.0
15164 } else if show_line_numbers {
15165 em_width
15166 } else {
15167 px(0.)
15168 };
15169
15170 Some(GutterDimensions {
15171 left_padding,
15172 right_padding,
15173 width: line_gutter_width + left_padding + right_padding,
15174 margin: -descent,
15175 git_blame_entries_width,
15176 })
15177 }
15178
15179 pub fn render_crease_toggle(
15180 &self,
15181 buffer_row: MultiBufferRow,
15182 row_contains_cursor: bool,
15183 editor: Entity<Editor>,
15184 window: &mut Window,
15185 cx: &mut App,
15186 ) -> Option<AnyElement> {
15187 let folded = self.is_line_folded(buffer_row);
15188 let mut is_foldable = false;
15189
15190 if let Some(crease) = self
15191 .crease_snapshot
15192 .query_row(buffer_row, &self.buffer_snapshot)
15193 {
15194 is_foldable = true;
15195 match crease {
15196 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
15197 if let Some(render_toggle) = render_toggle {
15198 let toggle_callback =
15199 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
15200 if folded {
15201 editor.update(cx, |editor, cx| {
15202 editor.fold_at(&crate::FoldAt { buffer_row }, window, cx)
15203 });
15204 } else {
15205 editor.update(cx, |editor, cx| {
15206 editor.unfold_at(
15207 &crate::UnfoldAt { buffer_row },
15208 window,
15209 cx,
15210 )
15211 });
15212 }
15213 });
15214 return Some((render_toggle)(
15215 buffer_row,
15216 folded,
15217 toggle_callback,
15218 window,
15219 cx,
15220 ));
15221 }
15222 }
15223 }
15224 }
15225
15226 is_foldable |= self.starts_indent(buffer_row);
15227
15228 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
15229 Some(
15230 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
15231 .toggle_state(folded)
15232 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
15233 if folded {
15234 this.unfold_at(&UnfoldAt { buffer_row }, window, cx);
15235 } else {
15236 this.fold_at(&FoldAt { buffer_row }, window, cx);
15237 }
15238 }))
15239 .into_any_element(),
15240 )
15241 } else {
15242 None
15243 }
15244 }
15245
15246 pub fn render_crease_trailer(
15247 &self,
15248 buffer_row: MultiBufferRow,
15249 window: &mut Window,
15250 cx: &mut App,
15251 ) -> Option<AnyElement> {
15252 let folded = self.is_line_folded(buffer_row);
15253 if let Crease::Inline { render_trailer, .. } = self
15254 .crease_snapshot
15255 .query_row(buffer_row, &self.buffer_snapshot)?
15256 {
15257 let render_trailer = render_trailer.as_ref()?;
15258 Some(render_trailer(buffer_row, folded, window, cx))
15259 } else {
15260 None
15261 }
15262 }
15263}
15264
15265impl Deref for EditorSnapshot {
15266 type Target = DisplaySnapshot;
15267
15268 fn deref(&self) -> &Self::Target {
15269 &self.display_snapshot
15270 }
15271}
15272
15273#[derive(Clone, Debug, PartialEq, Eq)]
15274pub enum EditorEvent {
15275 InputIgnored {
15276 text: Arc<str>,
15277 },
15278 InputHandled {
15279 utf16_range_to_replace: Option<Range<isize>>,
15280 text: Arc<str>,
15281 },
15282 ExcerptsAdded {
15283 buffer: Entity<Buffer>,
15284 predecessor: ExcerptId,
15285 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
15286 },
15287 ExcerptsRemoved {
15288 ids: Vec<ExcerptId>,
15289 },
15290 BufferFoldToggled {
15291 ids: Vec<ExcerptId>,
15292 folded: bool,
15293 },
15294 ExcerptsEdited {
15295 ids: Vec<ExcerptId>,
15296 },
15297 ExcerptsExpanded {
15298 ids: Vec<ExcerptId>,
15299 },
15300 BufferEdited,
15301 Edited {
15302 transaction_id: clock::Lamport,
15303 },
15304 Reparsed(BufferId),
15305 Focused,
15306 FocusedIn,
15307 Blurred,
15308 DirtyChanged,
15309 Saved,
15310 TitleChanged,
15311 DiffBaseChanged,
15312 SelectionsChanged {
15313 local: bool,
15314 },
15315 ScrollPositionChanged {
15316 local: bool,
15317 autoscroll: bool,
15318 },
15319 Closed,
15320 TransactionUndone {
15321 transaction_id: clock::Lamport,
15322 },
15323 TransactionBegun {
15324 transaction_id: clock::Lamport,
15325 },
15326 Reloaded,
15327 CursorShapeChanged,
15328}
15329
15330impl EventEmitter<EditorEvent> for Editor {}
15331
15332impl Focusable for Editor {
15333 fn focus_handle(&self, _cx: &App) -> FocusHandle {
15334 self.focus_handle.clone()
15335 }
15336}
15337
15338impl Render for Editor {
15339 fn render<'a>(&mut self, _: &mut Window, cx: &mut Context<'a, Self>) -> impl IntoElement {
15340 let settings = ThemeSettings::get_global(cx);
15341
15342 let mut text_style = match self.mode {
15343 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
15344 color: cx.theme().colors().editor_foreground,
15345 font_family: settings.ui_font.family.clone(),
15346 font_features: settings.ui_font.features.clone(),
15347 font_fallbacks: settings.ui_font.fallbacks.clone(),
15348 font_size: rems(0.875).into(),
15349 font_weight: settings.ui_font.weight,
15350 line_height: relative(settings.buffer_line_height.value()),
15351 ..Default::default()
15352 },
15353 EditorMode::Full => TextStyle {
15354 color: cx.theme().colors().editor_foreground,
15355 font_family: settings.buffer_font.family.clone(),
15356 font_features: settings.buffer_font.features.clone(),
15357 font_fallbacks: settings.buffer_font.fallbacks.clone(),
15358 font_size: settings.buffer_font_size().into(),
15359 font_weight: settings.buffer_font.weight,
15360 line_height: relative(settings.buffer_line_height.value()),
15361 ..Default::default()
15362 },
15363 };
15364 if let Some(text_style_refinement) = &self.text_style_refinement {
15365 text_style.refine(text_style_refinement)
15366 }
15367
15368 let background = match self.mode {
15369 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
15370 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
15371 EditorMode::Full => cx.theme().colors().editor_background,
15372 };
15373
15374 EditorElement::new(
15375 &cx.entity(),
15376 EditorStyle {
15377 background,
15378 local_player: cx.theme().players().local(),
15379 text: text_style,
15380 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
15381 syntax: cx.theme().syntax().clone(),
15382 status: cx.theme().status().clone(),
15383 inlay_hints_style: make_inlay_hints_style(cx),
15384 inline_completion_styles: make_suggestion_styles(cx),
15385 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
15386 },
15387 )
15388 }
15389}
15390
15391impl EntityInputHandler for Editor {
15392 fn text_for_range(
15393 &mut self,
15394 range_utf16: Range<usize>,
15395 adjusted_range: &mut Option<Range<usize>>,
15396 _: &mut Window,
15397 cx: &mut Context<Self>,
15398 ) -> Option<String> {
15399 let snapshot = self.buffer.read(cx).read(cx);
15400 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
15401 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
15402 if (start.0..end.0) != range_utf16 {
15403 adjusted_range.replace(start.0..end.0);
15404 }
15405 Some(snapshot.text_for_range(start..end).collect())
15406 }
15407
15408 fn selected_text_range(
15409 &mut self,
15410 ignore_disabled_input: bool,
15411 _: &mut Window,
15412 cx: &mut Context<Self>,
15413 ) -> Option<UTF16Selection> {
15414 // Prevent the IME menu from appearing when holding down an alphabetic key
15415 // while input is disabled.
15416 if !ignore_disabled_input && !self.input_enabled {
15417 return None;
15418 }
15419
15420 let selection = self.selections.newest::<OffsetUtf16>(cx);
15421 let range = selection.range();
15422
15423 Some(UTF16Selection {
15424 range: range.start.0..range.end.0,
15425 reversed: selection.reversed,
15426 })
15427 }
15428
15429 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
15430 let snapshot = self.buffer.read(cx).read(cx);
15431 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
15432 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
15433 }
15434
15435 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
15436 self.clear_highlights::<InputComposition>(cx);
15437 self.ime_transaction.take();
15438 }
15439
15440 fn replace_text_in_range(
15441 &mut self,
15442 range_utf16: Option<Range<usize>>,
15443 text: &str,
15444 window: &mut Window,
15445 cx: &mut Context<Self>,
15446 ) {
15447 if !self.input_enabled {
15448 cx.emit(EditorEvent::InputIgnored { text: text.into() });
15449 return;
15450 }
15451
15452 self.transact(window, cx, |this, window, cx| {
15453 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
15454 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
15455 Some(this.selection_replacement_ranges(range_utf16, cx))
15456 } else {
15457 this.marked_text_ranges(cx)
15458 };
15459
15460 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
15461 let newest_selection_id = this.selections.newest_anchor().id;
15462 this.selections
15463 .all::<OffsetUtf16>(cx)
15464 .iter()
15465 .zip(ranges_to_replace.iter())
15466 .find_map(|(selection, range)| {
15467 if selection.id == newest_selection_id {
15468 Some(
15469 (range.start.0 as isize - selection.head().0 as isize)
15470 ..(range.end.0 as isize - selection.head().0 as isize),
15471 )
15472 } else {
15473 None
15474 }
15475 })
15476 });
15477
15478 cx.emit(EditorEvent::InputHandled {
15479 utf16_range_to_replace: range_to_replace,
15480 text: text.into(),
15481 });
15482
15483 if let Some(new_selected_ranges) = new_selected_ranges {
15484 this.change_selections(None, window, cx, |selections| {
15485 selections.select_ranges(new_selected_ranges)
15486 });
15487 this.backspace(&Default::default(), window, cx);
15488 }
15489
15490 this.handle_input(text, window, cx);
15491 });
15492
15493 if let Some(transaction) = self.ime_transaction {
15494 self.buffer.update(cx, |buffer, cx| {
15495 buffer.group_until_transaction(transaction, cx);
15496 });
15497 }
15498
15499 self.unmark_text(window, cx);
15500 }
15501
15502 fn replace_and_mark_text_in_range(
15503 &mut self,
15504 range_utf16: Option<Range<usize>>,
15505 text: &str,
15506 new_selected_range_utf16: Option<Range<usize>>,
15507 window: &mut Window,
15508 cx: &mut Context<Self>,
15509 ) {
15510 if !self.input_enabled {
15511 return;
15512 }
15513
15514 let transaction = self.transact(window, cx, |this, window, cx| {
15515 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
15516 let snapshot = this.buffer.read(cx).read(cx);
15517 if let Some(relative_range_utf16) = range_utf16.as_ref() {
15518 for marked_range in &mut marked_ranges {
15519 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
15520 marked_range.start.0 += relative_range_utf16.start;
15521 marked_range.start =
15522 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
15523 marked_range.end =
15524 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
15525 }
15526 }
15527 Some(marked_ranges)
15528 } else if let Some(range_utf16) = range_utf16 {
15529 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
15530 Some(this.selection_replacement_ranges(range_utf16, cx))
15531 } else {
15532 None
15533 };
15534
15535 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
15536 let newest_selection_id = this.selections.newest_anchor().id;
15537 this.selections
15538 .all::<OffsetUtf16>(cx)
15539 .iter()
15540 .zip(ranges_to_replace.iter())
15541 .find_map(|(selection, range)| {
15542 if selection.id == newest_selection_id {
15543 Some(
15544 (range.start.0 as isize - selection.head().0 as isize)
15545 ..(range.end.0 as isize - selection.head().0 as isize),
15546 )
15547 } else {
15548 None
15549 }
15550 })
15551 });
15552
15553 cx.emit(EditorEvent::InputHandled {
15554 utf16_range_to_replace: range_to_replace,
15555 text: text.into(),
15556 });
15557
15558 if let Some(ranges) = ranges_to_replace {
15559 this.change_selections(None, window, cx, |s| s.select_ranges(ranges));
15560 }
15561
15562 let marked_ranges = {
15563 let snapshot = this.buffer.read(cx).read(cx);
15564 this.selections
15565 .disjoint_anchors()
15566 .iter()
15567 .map(|selection| {
15568 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
15569 })
15570 .collect::<Vec<_>>()
15571 };
15572
15573 if text.is_empty() {
15574 this.unmark_text(window, cx);
15575 } else {
15576 this.highlight_text::<InputComposition>(
15577 marked_ranges.clone(),
15578 HighlightStyle {
15579 underline: Some(UnderlineStyle {
15580 thickness: px(1.),
15581 color: None,
15582 wavy: false,
15583 }),
15584 ..Default::default()
15585 },
15586 cx,
15587 );
15588 }
15589
15590 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
15591 let use_autoclose = this.use_autoclose;
15592 let use_auto_surround = this.use_auto_surround;
15593 this.set_use_autoclose(false);
15594 this.set_use_auto_surround(false);
15595 this.handle_input(text, window, cx);
15596 this.set_use_autoclose(use_autoclose);
15597 this.set_use_auto_surround(use_auto_surround);
15598
15599 if let Some(new_selected_range) = new_selected_range_utf16 {
15600 let snapshot = this.buffer.read(cx).read(cx);
15601 let new_selected_ranges = marked_ranges
15602 .into_iter()
15603 .map(|marked_range| {
15604 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
15605 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
15606 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
15607 snapshot.clip_offset_utf16(new_start, Bias::Left)
15608 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
15609 })
15610 .collect::<Vec<_>>();
15611
15612 drop(snapshot);
15613 this.change_selections(None, window, cx, |selections| {
15614 selections.select_ranges(new_selected_ranges)
15615 });
15616 }
15617 });
15618
15619 self.ime_transaction = self.ime_transaction.or(transaction);
15620 if let Some(transaction) = self.ime_transaction {
15621 self.buffer.update(cx, |buffer, cx| {
15622 buffer.group_until_transaction(transaction, cx);
15623 });
15624 }
15625
15626 if self.text_highlights::<InputComposition>(cx).is_none() {
15627 self.ime_transaction.take();
15628 }
15629 }
15630
15631 fn bounds_for_range(
15632 &mut self,
15633 range_utf16: Range<usize>,
15634 element_bounds: gpui::Bounds<Pixels>,
15635 window: &mut Window,
15636 cx: &mut Context<Self>,
15637 ) -> Option<gpui::Bounds<Pixels>> {
15638 let text_layout_details = self.text_layout_details(window);
15639 let gpui::Point {
15640 x: em_width,
15641 y: line_height,
15642 } = self.character_size(window);
15643
15644 let snapshot = self.snapshot(window, cx);
15645 let scroll_position = snapshot.scroll_position();
15646 let scroll_left = scroll_position.x * em_width;
15647
15648 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
15649 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
15650 + self.gutter_dimensions.width
15651 + self.gutter_dimensions.margin;
15652 let y = line_height * (start.row().as_f32() - scroll_position.y);
15653
15654 Some(Bounds {
15655 origin: element_bounds.origin + point(x, y),
15656 size: size(em_width, line_height),
15657 })
15658 }
15659}
15660
15661trait SelectionExt {
15662 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
15663 fn spanned_rows(
15664 &self,
15665 include_end_if_at_line_start: bool,
15666 map: &DisplaySnapshot,
15667 ) -> Range<MultiBufferRow>;
15668}
15669
15670impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
15671 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
15672 let start = self
15673 .start
15674 .to_point(&map.buffer_snapshot)
15675 .to_display_point(map);
15676 let end = self
15677 .end
15678 .to_point(&map.buffer_snapshot)
15679 .to_display_point(map);
15680 if self.reversed {
15681 end..start
15682 } else {
15683 start..end
15684 }
15685 }
15686
15687 fn spanned_rows(
15688 &self,
15689 include_end_if_at_line_start: bool,
15690 map: &DisplaySnapshot,
15691 ) -> Range<MultiBufferRow> {
15692 let start = self.start.to_point(&map.buffer_snapshot);
15693 let mut end = self.end.to_point(&map.buffer_snapshot);
15694 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
15695 end.row -= 1;
15696 }
15697
15698 let buffer_start = map.prev_line_boundary(start).0;
15699 let buffer_end = map.next_line_boundary(end).0;
15700 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
15701 }
15702}
15703
15704impl<T: InvalidationRegion> InvalidationStack<T> {
15705 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
15706 where
15707 S: Clone + ToOffset,
15708 {
15709 while let Some(region) = self.last() {
15710 let all_selections_inside_invalidation_ranges =
15711 if selections.len() == region.ranges().len() {
15712 selections
15713 .iter()
15714 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
15715 .all(|(selection, invalidation_range)| {
15716 let head = selection.head().to_offset(buffer);
15717 invalidation_range.start <= head && invalidation_range.end >= head
15718 })
15719 } else {
15720 false
15721 };
15722
15723 if all_selections_inside_invalidation_ranges {
15724 break;
15725 } else {
15726 self.pop();
15727 }
15728 }
15729 }
15730}
15731
15732impl<T> Default for InvalidationStack<T> {
15733 fn default() -> Self {
15734 Self(Default::default())
15735 }
15736}
15737
15738impl<T> Deref for InvalidationStack<T> {
15739 type Target = Vec<T>;
15740
15741 fn deref(&self) -> &Self::Target {
15742 &self.0
15743 }
15744}
15745
15746impl<T> DerefMut for InvalidationStack<T> {
15747 fn deref_mut(&mut self) -> &mut Self::Target {
15748 &mut self.0
15749 }
15750}
15751
15752impl InvalidationRegion for SnippetState {
15753 fn ranges(&self) -> &[Range<Anchor>] {
15754 &self.ranges[self.active_index]
15755 }
15756}
15757
15758pub fn diagnostic_block_renderer(
15759 diagnostic: Diagnostic,
15760 max_message_rows: Option<u8>,
15761 allow_closing: bool,
15762 _is_valid: bool,
15763) -> RenderBlock {
15764 let (text_without_backticks, code_ranges) =
15765 highlight_diagnostic_message(&diagnostic, max_message_rows);
15766
15767 Arc::new(move |cx: &mut BlockContext| {
15768 let group_id: SharedString = cx.block_id.to_string().into();
15769
15770 let mut text_style = cx.window.text_style().clone();
15771 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
15772 let theme_settings = ThemeSettings::get_global(cx);
15773 text_style.font_family = theme_settings.buffer_font.family.clone();
15774 text_style.font_style = theme_settings.buffer_font.style;
15775 text_style.font_features = theme_settings.buffer_font.features.clone();
15776 text_style.font_weight = theme_settings.buffer_font.weight;
15777
15778 let multi_line_diagnostic = diagnostic.message.contains('\n');
15779
15780 let buttons = |diagnostic: &Diagnostic| {
15781 if multi_line_diagnostic {
15782 v_flex()
15783 } else {
15784 h_flex()
15785 }
15786 .when(allow_closing, |div| {
15787 div.children(diagnostic.is_primary.then(|| {
15788 IconButton::new("close-block", IconName::XCircle)
15789 .icon_color(Color::Muted)
15790 .size(ButtonSize::Compact)
15791 .style(ButtonStyle::Transparent)
15792 .visible_on_hover(group_id.clone())
15793 .on_click(move |_click, window, cx| {
15794 window.dispatch_action(Box::new(Cancel), cx)
15795 })
15796 .tooltip(|window, cx| {
15797 Tooltip::for_action("Close Diagnostics", &Cancel, window, cx)
15798 })
15799 }))
15800 })
15801 .child(
15802 IconButton::new("copy-block", IconName::Copy)
15803 .icon_color(Color::Muted)
15804 .size(ButtonSize::Compact)
15805 .style(ButtonStyle::Transparent)
15806 .visible_on_hover(group_id.clone())
15807 .on_click({
15808 let message = diagnostic.message.clone();
15809 move |_click, _, cx| {
15810 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
15811 }
15812 })
15813 .tooltip(Tooltip::text("Copy diagnostic message")),
15814 )
15815 };
15816
15817 let icon_size = buttons(&diagnostic).into_any_element().layout_as_root(
15818 AvailableSpace::min_size(),
15819 cx.window,
15820 cx.app,
15821 );
15822
15823 h_flex()
15824 .id(cx.block_id)
15825 .group(group_id.clone())
15826 .relative()
15827 .size_full()
15828 .block_mouse_down()
15829 .pl(cx.gutter_dimensions.width)
15830 .w(cx.max_width - cx.gutter_dimensions.full_width())
15831 .child(
15832 div()
15833 .flex()
15834 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
15835 .flex_shrink(),
15836 )
15837 .child(buttons(&diagnostic))
15838 .child(div().flex().flex_shrink_0().child(
15839 StyledText::new(text_without_backticks.clone()).with_highlights(
15840 &text_style,
15841 code_ranges.iter().map(|range| {
15842 (
15843 range.clone(),
15844 HighlightStyle {
15845 font_weight: Some(FontWeight::BOLD),
15846 ..Default::default()
15847 },
15848 )
15849 }),
15850 ),
15851 ))
15852 .into_any_element()
15853 })
15854}
15855
15856fn inline_completion_edit_text(
15857 current_snapshot: &BufferSnapshot,
15858 edits: &[(Range<Anchor>, String)],
15859 edit_preview: &EditPreview,
15860 include_deletions: bool,
15861 cx: &App,
15862) -> Option<HighlightedText> {
15863 let edits = edits
15864 .iter()
15865 .map(|(anchor, text)| {
15866 (
15867 anchor.start.text_anchor..anchor.end.text_anchor,
15868 text.clone(),
15869 )
15870 })
15871 .collect::<Vec<_>>();
15872
15873 Some(edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx))
15874}
15875
15876pub fn highlight_diagnostic_message(
15877 diagnostic: &Diagnostic,
15878 mut max_message_rows: Option<u8>,
15879) -> (SharedString, Vec<Range<usize>>) {
15880 let mut text_without_backticks = String::new();
15881 let mut code_ranges = Vec::new();
15882
15883 if let Some(source) = &diagnostic.source {
15884 text_without_backticks.push_str(source);
15885 code_ranges.push(0..source.len());
15886 text_without_backticks.push_str(": ");
15887 }
15888
15889 let mut prev_offset = 0;
15890 let mut in_code_block = false;
15891 let has_row_limit = max_message_rows.is_some();
15892 let mut newline_indices = diagnostic
15893 .message
15894 .match_indices('\n')
15895 .filter(|_| has_row_limit)
15896 .map(|(ix, _)| ix)
15897 .fuse()
15898 .peekable();
15899
15900 for (quote_ix, _) in diagnostic
15901 .message
15902 .match_indices('`')
15903 .chain([(diagnostic.message.len(), "")])
15904 {
15905 let mut first_newline_ix = None;
15906 let mut last_newline_ix = None;
15907 while let Some(newline_ix) = newline_indices.peek() {
15908 if *newline_ix < quote_ix {
15909 if first_newline_ix.is_none() {
15910 first_newline_ix = Some(*newline_ix);
15911 }
15912 last_newline_ix = Some(*newline_ix);
15913
15914 if let Some(rows_left) = &mut max_message_rows {
15915 if *rows_left == 0 {
15916 break;
15917 } else {
15918 *rows_left -= 1;
15919 }
15920 }
15921 let _ = newline_indices.next();
15922 } else {
15923 break;
15924 }
15925 }
15926 let prev_len = text_without_backticks.len();
15927 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
15928 text_without_backticks.push_str(new_text);
15929 if in_code_block {
15930 code_ranges.push(prev_len..text_without_backticks.len());
15931 }
15932 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
15933 in_code_block = !in_code_block;
15934 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
15935 text_without_backticks.push_str("...");
15936 break;
15937 }
15938 }
15939
15940 (text_without_backticks.into(), code_ranges)
15941}
15942
15943fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
15944 match severity {
15945 DiagnosticSeverity::ERROR => colors.error,
15946 DiagnosticSeverity::WARNING => colors.warning,
15947 DiagnosticSeverity::INFORMATION => colors.info,
15948 DiagnosticSeverity::HINT => colors.info,
15949 _ => colors.ignored,
15950 }
15951}
15952
15953pub fn styled_runs_for_code_label<'a>(
15954 label: &'a CodeLabel,
15955 syntax_theme: &'a theme::SyntaxTheme,
15956) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
15957 let fade_out = HighlightStyle {
15958 fade_out: Some(0.35),
15959 ..Default::default()
15960 };
15961
15962 let mut prev_end = label.filter_range.end;
15963 label
15964 .runs
15965 .iter()
15966 .enumerate()
15967 .flat_map(move |(ix, (range, highlight_id))| {
15968 let style = if let Some(style) = highlight_id.style(syntax_theme) {
15969 style
15970 } else {
15971 return Default::default();
15972 };
15973 let mut muted_style = style;
15974 muted_style.highlight(fade_out);
15975
15976 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
15977 if range.start >= label.filter_range.end {
15978 if range.start > prev_end {
15979 runs.push((prev_end..range.start, fade_out));
15980 }
15981 runs.push((range.clone(), muted_style));
15982 } else if range.end <= label.filter_range.end {
15983 runs.push((range.clone(), style));
15984 } else {
15985 runs.push((range.start..label.filter_range.end, style));
15986 runs.push((label.filter_range.end..range.end, muted_style));
15987 }
15988 prev_end = cmp::max(prev_end, range.end);
15989
15990 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
15991 runs.push((prev_end..label.text.len(), fade_out));
15992 }
15993
15994 runs
15995 })
15996}
15997
15998pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
15999 let mut prev_index = 0;
16000 let mut prev_codepoint: Option<char> = None;
16001 text.char_indices()
16002 .chain([(text.len(), '\0')])
16003 .filter_map(move |(index, codepoint)| {
16004 let prev_codepoint = prev_codepoint.replace(codepoint)?;
16005 let is_boundary = index == text.len()
16006 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
16007 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
16008 if is_boundary {
16009 let chunk = &text[prev_index..index];
16010 prev_index = index;
16011 Some(chunk)
16012 } else {
16013 None
16014 }
16015 })
16016}
16017
16018pub trait RangeToAnchorExt: Sized {
16019 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
16020
16021 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
16022 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
16023 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
16024 }
16025}
16026
16027impl<T: ToOffset> RangeToAnchorExt for Range<T> {
16028 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
16029 let start_offset = self.start.to_offset(snapshot);
16030 let end_offset = self.end.to_offset(snapshot);
16031 if start_offset == end_offset {
16032 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
16033 } else {
16034 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
16035 }
16036 }
16037}
16038
16039pub trait RowExt {
16040 fn as_f32(&self) -> f32;
16041
16042 fn next_row(&self) -> Self;
16043
16044 fn previous_row(&self) -> Self;
16045
16046 fn minus(&self, other: Self) -> u32;
16047}
16048
16049impl RowExt for DisplayRow {
16050 fn as_f32(&self) -> f32 {
16051 self.0 as f32
16052 }
16053
16054 fn next_row(&self) -> Self {
16055 Self(self.0 + 1)
16056 }
16057
16058 fn previous_row(&self) -> Self {
16059 Self(self.0.saturating_sub(1))
16060 }
16061
16062 fn minus(&self, other: Self) -> u32 {
16063 self.0 - other.0
16064 }
16065}
16066
16067impl RowExt for MultiBufferRow {
16068 fn as_f32(&self) -> f32 {
16069 self.0 as f32
16070 }
16071
16072 fn next_row(&self) -> Self {
16073 Self(self.0 + 1)
16074 }
16075
16076 fn previous_row(&self) -> Self {
16077 Self(self.0.saturating_sub(1))
16078 }
16079
16080 fn minus(&self, other: Self) -> u32 {
16081 self.0 - other.0
16082 }
16083}
16084
16085trait RowRangeExt {
16086 type Row;
16087
16088 fn len(&self) -> usize;
16089
16090 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
16091}
16092
16093impl RowRangeExt for Range<MultiBufferRow> {
16094 type Row = MultiBufferRow;
16095
16096 fn len(&self) -> usize {
16097 (self.end.0 - self.start.0) as usize
16098 }
16099
16100 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
16101 (self.start.0..self.end.0).map(MultiBufferRow)
16102 }
16103}
16104
16105impl RowRangeExt for Range<DisplayRow> {
16106 type Row = DisplayRow;
16107
16108 fn len(&self) -> usize {
16109 (self.end.0 - self.start.0) as usize
16110 }
16111
16112 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
16113 (self.start.0..self.end.0).map(DisplayRow)
16114 }
16115}
16116
16117/// If select range has more than one line, we
16118/// just point the cursor to range.start.
16119fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
16120 if range.start.row == range.end.row {
16121 range
16122 } else {
16123 range.start..range.start
16124 }
16125}
16126pub struct KillRing(ClipboardItem);
16127impl Global for KillRing {}
16128
16129const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
16130
16131fn all_edits_insertions_or_deletions(
16132 edits: &Vec<(Range<Anchor>, String)>,
16133 snapshot: &MultiBufferSnapshot,
16134) -> bool {
16135 let mut all_insertions = true;
16136 let mut all_deletions = true;
16137
16138 for (range, new_text) in edits.iter() {
16139 let range_is_empty = range.to_offset(&snapshot).is_empty();
16140 let text_is_empty = new_text.is_empty();
16141
16142 if range_is_empty != text_is_empty {
16143 if range_is_empty {
16144 all_deletions = false;
16145 } else {
16146 all_insertions = false;
16147 }
16148 } else {
16149 return false;
16150 }
16151
16152 if !all_insertions && !all_deletions {
16153 return false;
16154 }
16155 }
16156 all_insertions || all_deletions
16157}