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
50pub(crate) use actions::*;
51pub use actions::{AcceptEditPrediction, OpenExcerpts, OpenExcerptsSplit};
52use aho_corasick::AhoCorasick;
53use anyhow::{anyhow, Context as _, Result};
54use blink_manager::BlinkManager;
55use client::{Collaborator, ParticipantIndex};
56use clock::ReplicaId;
57use collections::{BTreeMap, HashMap, HashSet, VecDeque};
58use convert_case::{Case, Casing};
59use display_map::*;
60pub use display_map::{DisplayPoint, FoldPlaceholder};
61pub use editor_settings::{
62 CurrentLineHighlight, EditorSettings, ScrollBeyondLastLine, SearchSettings, ShowScrollbar,
63};
64pub use editor_settings_controls::*;
65use element::{AcceptEditPredictionBinding, LineWithInvisibles, PositionMap};
66pub use element::{
67 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
68};
69use futures::{future, FutureExt};
70use fuzzy::StringMatchCandidate;
71
72use code_context_menus::{
73 AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
74 CompletionsMenu, ContextMenuOrigin,
75};
76use git::blame::GitBlame;
77use gpui::{
78 div, impl_actions, point, prelude::*, pulsating_between, px, relative, size, Action, Animation,
79 AnimationExt, AnyElement, App, AsyncWindowContext, AvailableSpace, Background, Bounds,
80 ClipboardEntry, ClipboardItem, Context, DispatchPhase, ElementId, Entity, EntityInputHandler,
81 EventEmitter, FocusHandle, FocusOutEvent, Focusable, FontId, FontWeight, Global,
82 HighlightStyle, Hsla, InteractiveText, KeyContext, Modifiers, MouseButton, MouseDownEvent,
83 PaintQuad, ParentElement, Pixels, Render, SharedString, Size, Styled, StyledText, Subscription,
84 Task, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle, UniformListScrollHandle,
85 WeakEntity, WeakFocusHandle, Window,
86};
87use highlight_matching_bracket::refresh_matching_bracket_highlights;
88use hover_popover::{hide_hover, HoverState};
89use indent_guides::ActiveIndentGuidesState;
90use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
91pub use inline_completion::Direction;
92use inline_completion::{EditPredictionProvider, InlineCompletionProviderHandle};
93pub use items::MAX_TAB_TITLE_LEN;
94use itertools::Itertools;
95use language::{
96 language_settings::{self, all_language_settings, language_settings, InlayHintSettings},
97 markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
98 CompletionDocumentation, CursorShape, Diagnostic, EditPreview, HighlightedText, IndentKind,
99 IndentSize, InlineCompletionPreviewMode, Language, OffsetRangeExt, Point, Selection,
100 SelectionGoal, TextObject, TransactionId, TreeSitterOptions,
101};
102use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
103use linked_editing_ranges::refresh_linked_ranges;
104use mouse_context_menu::MouseContextMenu;
105pub use proposed_changes_editor::{
106 ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar,
107};
108use similar::{ChangeTag, TextDiff};
109use std::iter::Peekable;
110use task::{ResolvedTask, TaskTemplate, TaskVariables};
111
112use hover_links::{find_file, HoverLink, HoveredLinkState, InlayHighlight};
113pub use lsp::CompletionContext;
114use lsp::{
115 CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity, InsertTextFormat,
116 LanguageServerId, LanguageServerName,
117};
118
119use language::BufferSnapshot;
120use movement::TextLayoutDetails;
121pub use multi_buffer::{
122 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, RowInfo,
123 ToOffset, ToPoint,
124};
125use multi_buffer::{
126 ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
127 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, TakeUntilExt, 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(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
193pub(crate) const EDIT_PREDICTION_REQUIRES_MODIFIER_KEY_CONTEXT: &str =
194 "edit_prediction_requires_modifier";
195
196pub fn render_parsed_markdown(
197 element_id: impl Into<ElementId>,
198 parsed: &language::ParsedMarkdown,
199 editor_style: &EditorStyle,
200 workspace: Option<WeakEntity<Workspace>>,
201 cx: &mut App,
202) -> InteractiveText {
203 let code_span_background_color = cx
204 .theme()
205 .colors()
206 .editor_document_highlight_read_background;
207
208 let highlights = gpui::combine_highlights(
209 parsed.highlights.iter().filter_map(|(range, highlight)| {
210 let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
211 Some((range.clone(), highlight))
212 }),
213 parsed
214 .regions
215 .iter()
216 .zip(&parsed.region_ranges)
217 .filter_map(|(region, range)| {
218 if region.code {
219 Some((
220 range.clone(),
221 HighlightStyle {
222 background_color: Some(code_span_background_color),
223 ..Default::default()
224 },
225 ))
226 } else {
227 None
228 }
229 }),
230 );
231
232 let mut links = Vec::new();
233 let mut link_ranges = Vec::new();
234 for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
235 if let Some(link) = region.link.clone() {
236 links.push(link);
237 link_ranges.push(range.clone());
238 }
239 }
240
241 InteractiveText::new(
242 element_id,
243 StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
244 )
245 .on_click(
246 link_ranges,
247 move |clicked_range_ix, window, cx| match &links[clicked_range_ix] {
248 markdown::Link::Web { url } => cx.open_url(url),
249 markdown::Link::Path { path } => {
250 if let Some(workspace) = &workspace {
251 _ = workspace.update(cx, |workspace, cx| {
252 workspace
253 .open_abs_path(path.clone(), false, window, cx)
254 .detach();
255 });
256 }
257 }
258 },
259 )
260}
261
262#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
263pub enum InlayId {
264 InlineCompletion(usize),
265 Hint(usize),
266}
267
268impl InlayId {
269 fn id(&self) -> usize {
270 match self {
271 Self::InlineCompletion(id) => *id,
272 Self::Hint(id) => *id,
273 }
274 }
275}
276
277enum DocumentHighlightRead {}
278enum DocumentHighlightWrite {}
279enum InputComposition {}
280
281#[derive(Debug, Copy, Clone, PartialEq, Eq)]
282pub enum Navigated {
283 Yes,
284 No,
285}
286
287impl Navigated {
288 pub fn from_bool(yes: bool) -> Navigated {
289 if yes {
290 Navigated::Yes
291 } else {
292 Navigated::No
293 }
294 }
295}
296
297pub fn init_settings(cx: &mut App) {
298 EditorSettings::register(cx);
299}
300
301pub fn init(cx: &mut App) {
302 init_settings(cx);
303
304 workspace::register_project_item::<Editor>(cx);
305 workspace::FollowableViewRegistry::register::<Editor>(cx);
306 workspace::register_serializable_item::<Editor>(cx);
307
308 cx.observe_new(
309 |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
310 workspace.register_action(Editor::new_file);
311 workspace.register_action(Editor::new_file_vertical);
312 workspace.register_action(Editor::new_file_horizontal);
313 workspace.register_action(Editor::cancel_language_server_work);
314 },
315 )
316 .detach();
317
318 cx.on_action(move |_: &workspace::NewFile, cx| {
319 let app_state = workspace::AppState::global(cx);
320 if let Some(app_state) = app_state.upgrade() {
321 workspace::open_new(
322 Default::default(),
323 app_state,
324 cx,
325 |workspace, window, cx| {
326 Editor::new_file(workspace, &Default::default(), window, cx)
327 },
328 )
329 .detach();
330 }
331 });
332 cx.on_action(move |_: &workspace::NewWindow, cx| {
333 let app_state = workspace::AppState::global(cx);
334 if let Some(app_state) = app_state.upgrade() {
335 workspace::open_new(
336 Default::default(),
337 app_state,
338 cx,
339 |workspace, window, cx| {
340 cx.activate(true);
341 Editor::new_file(workspace, &Default::default(), window, cx)
342 },
343 )
344 .detach();
345 }
346 });
347}
348
349pub struct SearchWithinRange;
350
351trait InvalidationRegion {
352 fn ranges(&self) -> &[Range<Anchor>];
353}
354
355#[derive(Clone, Debug, PartialEq)]
356pub enum SelectPhase {
357 Begin {
358 position: DisplayPoint,
359 add: bool,
360 click_count: usize,
361 },
362 BeginColumnar {
363 position: DisplayPoint,
364 reset: bool,
365 goal_column: u32,
366 },
367 Extend {
368 position: DisplayPoint,
369 click_count: usize,
370 },
371 Update {
372 position: DisplayPoint,
373 goal_column: u32,
374 scroll_delta: gpui::Point<f32>,
375 },
376 End,
377}
378
379#[derive(Clone, Debug)]
380pub enum SelectMode {
381 Character,
382 Word(Range<Anchor>),
383 Line(Range<Anchor>),
384 All,
385}
386
387#[derive(Copy, Clone, PartialEq, Eq, Debug)]
388pub enum EditorMode {
389 SingleLine { auto_width: bool },
390 AutoHeight { max_lines: usize },
391 Full,
392}
393
394#[derive(Copy, Clone, Debug)]
395pub enum SoftWrap {
396 /// Prefer not to wrap at all.
397 ///
398 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
399 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
400 GitDiff,
401 /// Prefer a single line generally, unless an overly long line is encountered.
402 None,
403 /// Soft wrap lines that exceed the editor width.
404 EditorWidth,
405 /// Soft wrap lines at the preferred line length.
406 Column(u32),
407 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
408 Bounded(u32),
409}
410
411#[derive(Clone)]
412pub struct EditorStyle {
413 pub background: Hsla,
414 pub local_player: PlayerColor,
415 pub text: TextStyle,
416 pub scrollbar_width: Pixels,
417 pub syntax: Arc<SyntaxTheme>,
418 pub status: StatusColors,
419 pub inlay_hints_style: HighlightStyle,
420 pub inline_completion_styles: InlineCompletionStyles,
421 pub unnecessary_code_fade: f32,
422}
423
424impl Default for EditorStyle {
425 fn default() -> Self {
426 Self {
427 background: Hsla::default(),
428 local_player: PlayerColor::default(),
429 text: TextStyle::default(),
430 scrollbar_width: Pixels::default(),
431 syntax: Default::default(),
432 // HACK: Status colors don't have a real default.
433 // We should look into removing the status colors from the editor
434 // style and retrieve them directly from the theme.
435 status: StatusColors::dark(),
436 inlay_hints_style: HighlightStyle::default(),
437 inline_completion_styles: InlineCompletionStyles {
438 insertion: HighlightStyle::default(),
439 whitespace: HighlightStyle::default(),
440 },
441 unnecessary_code_fade: Default::default(),
442 }
443 }
444}
445
446pub fn make_inlay_hints_style(cx: &mut App) -> HighlightStyle {
447 let show_background = language_settings::language_settings(None, None, cx)
448 .inlay_hints
449 .show_background;
450
451 HighlightStyle {
452 color: Some(cx.theme().status().hint),
453 background_color: show_background.then(|| cx.theme().status().hint_background),
454 ..HighlightStyle::default()
455 }
456}
457
458pub fn make_suggestion_styles(cx: &mut App) -> InlineCompletionStyles {
459 InlineCompletionStyles {
460 insertion: HighlightStyle {
461 color: Some(cx.theme().status().predictive),
462 ..HighlightStyle::default()
463 },
464 whitespace: HighlightStyle {
465 background_color: Some(cx.theme().status().created_background),
466 ..HighlightStyle::default()
467 },
468 }
469}
470
471type CompletionId = usize;
472
473pub(crate) enum EditDisplayMode {
474 TabAccept,
475 DiffPopover,
476 Inline,
477}
478
479enum InlineCompletion {
480 Edit {
481 edits: Vec<(Range<Anchor>, String)>,
482 edit_preview: Option<EditPreview>,
483 display_mode: EditDisplayMode,
484 snapshot: BufferSnapshot,
485 },
486 Move {
487 target: Anchor,
488 snapshot: BufferSnapshot,
489 },
490}
491
492struct InlineCompletionState {
493 inlay_ids: Vec<InlayId>,
494 completion: InlineCompletion,
495 completion_id: Option<SharedString>,
496 invalidation_range: Range<Anchor>,
497}
498
499enum EditPredictionSettings {
500 Disabled,
501 Enabled {
502 show_in_menu: bool,
503 preview_requires_modifier: bool,
504 },
505}
506
507impl EditPredictionSettings {
508 pub fn is_enabled(&self) -> bool {
509 match self {
510 EditPredictionSettings::Disabled => false,
511 EditPredictionSettings::Enabled { .. } => true,
512 }
513 }
514}
515
516enum InlineCompletionHighlight {}
517
518pub enum MenuInlineCompletionsPolicy {
519 Never,
520 ByProvider,
521}
522
523// TODO az do we need this?
524#[derive(Clone)]
525pub enum EditPredictionPreview {
526 /// Modifier is not pressed
527 Inactive,
528 /// Modifier pressed, animating to active
529 MovingTo {
530 animation: Range<Instant>,
531 scroll_position_at_start: Option<gpui::Point<f32>>,
532 target_point: DisplayPoint,
533 },
534 Arrived {
535 scroll_position_at_start: Option<gpui::Point<f32>>,
536 scroll_position_at_arrival: Option<gpui::Point<f32>>,
537 target_point: Option<DisplayPoint>,
538 },
539 /// Modifier released, animating from active
540 MovingFrom {
541 animation: Range<Instant>,
542 target_point: DisplayPoint,
543 },
544}
545
546impl EditPredictionPreview {
547 fn start(
548 &mut self,
549 completion: &InlineCompletion,
550 snapshot: &EditorSnapshot,
551 cursor: DisplayPoint,
552 ) -> bool {
553 if matches!(self, Self::MovingTo { .. } | Self::Arrived { .. }) {
554 return false;
555 }
556 (*self, _) = Self::start_now(completion, snapshot, cursor);
557 true
558 }
559
560 fn restart(
561 &mut self,
562 completion: &InlineCompletion,
563 snapshot: &EditorSnapshot,
564 cursor: DisplayPoint,
565 ) -> bool {
566 match self {
567 Self::Inactive => false,
568 Self::MovingTo { target_point, .. }
569 | Self::Arrived {
570 target_point: Some(target_point),
571 ..
572 } => {
573 let (new_preview, new_target_point) = Self::start_now(completion, snapshot, cursor);
574
575 if new_target_point != Some(*target_point) {
576 *self = new_preview;
577 return true;
578 }
579
580 false
581 }
582 Self::Arrived {
583 target_point: None, ..
584 } => {
585 let (new_preview, _) = Self::start_now(completion, snapshot, cursor);
586
587 *self = new_preview;
588 true
589 }
590 Self::MovingFrom { .. } => false,
591 }
592 }
593
594 fn start_now(
595 completion: &InlineCompletion,
596 snapshot: &EditorSnapshot,
597 cursor: DisplayPoint,
598 ) -> (Self, Option<DisplayPoint>) {
599 let now = Instant::now();
600 match completion {
601 InlineCompletion::Edit { .. } => (
602 Self::Arrived {
603 target_point: None,
604 scroll_position_at_start: None,
605 scroll_position_at_arrival: None,
606 },
607 None,
608 ),
609 InlineCompletion::Move { target, .. } => {
610 let target_point = target.to_display_point(&snapshot.display_snapshot);
611 let duration = Self::animation_duration(cursor, target_point);
612
613 (
614 Self::MovingTo {
615 animation: now..now + duration,
616 scroll_position_at_start: Some(snapshot.scroll_position()),
617 target_point,
618 },
619 Some(target_point),
620 )
621 }
622 }
623 }
624
625 fn animation_duration(a: DisplayPoint, b: DisplayPoint) -> Duration {
626 const SPEED: f32 = 8.0;
627
628 let row_diff = b.row().0.abs_diff(a.row().0);
629 let column_diff = b.column().abs_diff(a.column());
630 let distance = ((row_diff.pow(2) + column_diff.pow(2)) as f32).sqrt();
631 Duration::from_millis((distance * SPEED) as u64)
632 }
633
634 fn end(
635 &mut self,
636 cursor: DisplayPoint,
637 scroll_pixel_position: gpui::Point<Pixels>,
638 window: &mut Window,
639 cx: &mut Context<Editor>,
640 ) -> bool {
641 let (scroll_position, target_point) = match self {
642 Self::MovingTo {
643 scroll_position_at_start,
644 target_point,
645 ..
646 }
647 | Self::Arrived {
648 scroll_position_at_start,
649 scroll_position_at_arrival: None,
650 target_point: Some(target_point),
651 ..
652 } => (*scroll_position_at_start, target_point),
653 Self::Arrived {
654 scroll_position_at_start,
655 scroll_position_at_arrival: Some(scroll_at_arrival),
656 target_point: Some(target_point),
657 } => {
658 const TOLERANCE: f32 = 4.0;
659
660 let diff = *scroll_at_arrival - scroll_pixel_position.map(|p| p.0);
661
662 if diff.x.abs() < TOLERANCE && diff.y.abs() < TOLERANCE {
663 (*scroll_position_at_start, target_point)
664 } else {
665 (None, target_point)
666 }
667 }
668 Self::Arrived {
669 target_point: None, ..
670 } => {
671 *self = Self::Inactive;
672 return true;
673 }
674 Self::MovingFrom { .. } | Self::Inactive => return false,
675 };
676
677 let now = Instant::now();
678 let duration = Self::animation_duration(cursor, *target_point);
679 let target_point = *target_point;
680
681 *self = Self::MovingFrom {
682 animation: now..now + duration,
683 target_point,
684 };
685
686 if let Some(scroll_position) = scroll_position {
687 cx.spawn_in(window, |editor, mut cx| async move {
688 smol::Timer::after(duration).await;
689 editor
690 .update_in(&mut cx, |editor, window, cx| {
691 if let Self::MovingFrom { .. } | Self::Inactive =
692 editor.edit_prediction_preview
693 {
694 editor.set_scroll_position(scroll_position, window, cx)
695 }
696 })
697 .log_err();
698 })
699 .detach();
700 }
701
702 true
703 }
704
705 /// Whether the preview is active or we are animating to or from it.
706 fn is_active(&self) -> bool {
707 matches!(
708 self,
709 Self::MovingTo { .. } | Self::Arrived { .. } | Self::MovingFrom { .. }
710 )
711 }
712
713 /// Returns true if the preview is active, not cancelled, and the animation is settled.
714 fn is_active_settled(&self) -> bool {
715 matches!(self, Self::Arrived { .. })
716 }
717
718 #[allow(clippy::too_many_arguments)]
719 fn move_state(
720 &mut self,
721 snapshot: &EditorSnapshot,
722 visible_row_range: Range<DisplayRow>,
723 line_layouts: &[LineWithInvisibles],
724 scroll_pixel_position: gpui::Point<Pixels>,
725 line_height: Pixels,
726 target: Anchor,
727 cursor: Option<DisplayPoint>,
728 ) -> Option<EditPredictionMoveState> {
729 let delta = match self {
730 Self::Inactive => return None,
731 Self::Arrived { .. } => 1.,
732 Self::MovingTo {
733 animation,
734 scroll_position_at_start: original_scroll_position,
735 target_point,
736 } => {
737 let now = Instant::now();
738 if animation.end < now {
739 *self = Self::Arrived {
740 scroll_position_at_start: *original_scroll_position,
741 scroll_position_at_arrival: Some(scroll_pixel_position.map(|p| p.0)),
742 target_point: Some(*target_point),
743 };
744 1.0
745 } else {
746 (now - animation.start).as_secs_f32()
747 / (animation.end - animation.start).as_secs_f32()
748 }
749 }
750 Self::MovingFrom { animation, .. } => {
751 let now = Instant::now();
752 if animation.end < now {
753 *self = Self::Inactive;
754 return None;
755 } else {
756 let delta = (now - animation.start).as_secs_f32()
757 / (animation.end - animation.start).as_secs_f32();
758 1.0 - delta
759 }
760 }
761 };
762
763 let cursor = cursor?;
764
765 if !visible_row_range.contains(&cursor.row()) {
766 return None;
767 }
768
769 let target_position = target.to_display_point(&snapshot.display_snapshot);
770
771 if !visible_row_range.contains(&target_position.row()) {
772 return None;
773 }
774
775 let target_row_layout =
776 &line_layouts[target_position.row().minus(visible_row_range.start) as usize];
777 let target_column = target_position.column() as usize;
778
779 let target_character_x = target_row_layout.x_for_index(target_column);
780
781 let target_x = target_character_x - scroll_pixel_position.x;
782 let target_y =
783 (target_position.row().as_f32() - scroll_pixel_position.y / line_height) * line_height;
784
785 let origin_x = line_layouts[cursor.row().minus(visible_row_range.start) as usize]
786 .x_for_index(cursor.column() as usize);
787 let origin_y =
788 (cursor.row().as_f32() - scroll_pixel_position.y / line_height) * line_height;
789
790 let delta = 1.0 - (-10.0 * delta).exp2();
791
792 let x = origin_x + (target_x - origin_x) * delta;
793 let y = origin_y + (target_y - origin_y) * delta;
794
795 Some(EditPredictionMoveState {
796 delta,
797 position: point(x, y),
798 })
799 }
800}
801
802pub(crate) struct EditPredictionMoveState {
803 delta: f32,
804 position: gpui::Point<Pixels>,
805}
806
807impl EditPredictionMoveState {
808 pub fn is_animation_completed(&self) -> bool {
809 self.delta >= 1.
810 }
811}
812
813#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
814struct EditorActionId(usize);
815
816impl EditorActionId {
817 pub fn post_inc(&mut self) -> Self {
818 let answer = self.0;
819
820 *self = Self(answer + 1);
821
822 Self(answer)
823 }
824}
825
826// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
827// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
828
829type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
830type GutterHighlight = (fn(&App) -> Hsla, Arc<[Range<Anchor>]>);
831
832#[derive(Default)]
833struct ScrollbarMarkerState {
834 scrollbar_size: Size<Pixels>,
835 dirty: bool,
836 markers: Arc<[PaintQuad]>,
837 pending_refresh: Option<Task<Result<()>>>,
838}
839
840impl ScrollbarMarkerState {
841 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
842 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
843 }
844}
845
846#[derive(Clone, Debug)]
847struct RunnableTasks {
848 templates: Vec<(TaskSourceKind, TaskTemplate)>,
849 offset: MultiBufferOffset,
850 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
851 column: u32,
852 // Values of all named captures, including those starting with '_'
853 extra_variables: HashMap<String, String>,
854 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
855 context_range: Range<BufferOffset>,
856}
857
858impl RunnableTasks {
859 fn resolve<'a>(
860 &'a self,
861 cx: &'a task::TaskContext,
862 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
863 self.templates.iter().filter_map(|(kind, template)| {
864 template
865 .resolve_task(&kind.to_id_base(), cx)
866 .map(|task| (kind.clone(), task))
867 })
868 }
869}
870
871#[derive(Clone)]
872struct ResolvedTasks {
873 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
874 position: Anchor,
875}
876#[derive(Copy, Clone, Debug)]
877struct MultiBufferOffset(usize);
878#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
879struct BufferOffset(usize);
880
881// Addons allow storing per-editor state in other crates (e.g. Vim)
882pub trait Addon: 'static {
883 fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
884
885 fn render_buffer_header_controls(
886 &self,
887 _: &ExcerptInfo,
888 _: &Window,
889 _: &App,
890 ) -> Option<AnyElement> {
891 None
892 }
893
894 fn to_any(&self) -> &dyn std::any::Any;
895}
896
897#[derive(Debug, Copy, Clone, PartialEq, Eq)]
898pub enum IsVimMode {
899 Yes,
900 No,
901}
902
903/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
904///
905/// See the [module level documentation](self) for more information.
906pub struct Editor {
907 focus_handle: FocusHandle,
908 last_focused_descendant: Option<WeakFocusHandle>,
909 /// The text buffer being edited
910 buffer: Entity<MultiBuffer>,
911 /// Map of how text in the buffer should be displayed.
912 /// Handles soft wraps, folds, fake inlay text insertions, etc.
913 pub display_map: Entity<DisplayMap>,
914 pub selections: SelectionsCollection,
915 pub scroll_manager: ScrollManager,
916 /// When inline assist editors are linked, they all render cursors because
917 /// typing enters text into each of them, even the ones that aren't focused.
918 pub(crate) show_cursor_when_unfocused: bool,
919 columnar_selection_tail: Option<Anchor>,
920 add_selections_state: Option<AddSelectionsState>,
921 select_next_state: Option<SelectNextState>,
922 select_prev_state: Option<SelectNextState>,
923 selection_history: SelectionHistory,
924 autoclose_regions: Vec<AutocloseRegion>,
925 snippet_stack: InvalidationStack<SnippetState>,
926 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
927 ime_transaction: Option<TransactionId>,
928 active_diagnostics: Option<ActiveDiagnosticGroup>,
929 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
930
931 // TODO: make this a access method
932 pub project: Option<Entity<Project>>,
933 semantics_provider: Option<Rc<dyn SemanticsProvider>>,
934 completion_provider: Option<Box<dyn CompletionProvider>>,
935 collaboration_hub: Option<Box<dyn CollaborationHub>>,
936 blink_manager: Entity<BlinkManager>,
937 show_cursor_names: bool,
938 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
939 pub show_local_selections: bool,
940 mode: EditorMode,
941 show_breadcrumbs: bool,
942 show_gutter: bool,
943 show_scrollbars: bool,
944 show_line_numbers: Option<bool>,
945 use_relative_line_numbers: Option<bool>,
946 show_git_diff_gutter: Option<bool>,
947 show_code_actions: Option<bool>,
948 show_runnables: Option<bool>,
949 show_wrap_guides: Option<bool>,
950 show_indent_guides: Option<bool>,
951 placeholder_text: Option<Arc<str>>,
952 highlight_order: usize,
953 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
954 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
955 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
956 scrollbar_marker_state: ScrollbarMarkerState,
957 active_indent_guides_state: ActiveIndentGuidesState,
958 nav_history: Option<ItemNavHistory>,
959 context_menu: RefCell<Option<CodeContextMenu>>,
960 mouse_context_menu: Option<MouseContextMenu>,
961 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
962 signature_help_state: SignatureHelpState,
963 auto_signature_help: Option<bool>,
964 find_all_references_task_sources: Vec<Anchor>,
965 next_completion_id: CompletionId,
966 available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
967 code_actions_task: Option<Task<Result<()>>>,
968 document_highlights_task: Option<Task<()>>,
969 linked_editing_range_task: Option<Task<Option<()>>>,
970 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
971 pending_rename: Option<RenameState>,
972 searchable: bool,
973 cursor_shape: CursorShape,
974 current_line_highlight: Option<CurrentLineHighlight>,
975 collapse_matches: bool,
976 autoindent_mode: Option<AutoindentMode>,
977 workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
978 input_enabled: bool,
979 use_modal_editing: bool,
980 read_only: bool,
981 leader_peer_id: Option<PeerId>,
982 remote_id: Option<ViewId>,
983 hover_state: HoverState,
984 pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
985 gutter_hovered: bool,
986 hovered_link_state: Option<HoveredLinkState>,
987 edit_prediction_provider: Option<RegisteredInlineCompletionProvider>,
988 code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
989 active_inline_completion: Option<InlineCompletionState>,
990 /// Used to prevent flickering as the user types while the menu is open
991 stale_inline_completion_in_menu: Option<InlineCompletionState>,
992 edit_prediction_settings: EditPredictionSettings,
993 inline_completions_hidden_for_vim_mode: bool,
994 show_inline_completions_override: Option<bool>,
995 menu_inline_completions_policy: MenuInlineCompletionsPolicy,
996 edit_prediction_preview: EditPredictionPreview,
997 inlay_hint_cache: InlayHintCache,
998 next_inlay_id: usize,
999 _subscriptions: Vec<Subscription>,
1000 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
1001 gutter_dimensions: GutterDimensions,
1002 style: Option<EditorStyle>,
1003 text_style_refinement: Option<TextStyleRefinement>,
1004 next_editor_action_id: EditorActionId,
1005 editor_actions:
1006 Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut Window, &mut Context<Self>)>>>>,
1007 use_autoclose: bool,
1008 use_auto_surround: bool,
1009 auto_replace_emoji_shortcode: bool,
1010 show_git_blame_gutter: bool,
1011 show_git_blame_inline: bool,
1012 show_git_blame_inline_delay_task: Option<Task<()>>,
1013 distinguish_unstaged_diff_hunks: bool,
1014 git_blame_inline_enabled: bool,
1015 serialize_dirty_buffers: bool,
1016 show_selection_menu: Option<bool>,
1017 blame: Option<Entity<GitBlame>>,
1018 blame_subscription: Option<Subscription>,
1019 custom_context_menu: Option<
1020 Box<
1021 dyn 'static
1022 + Fn(
1023 &mut Self,
1024 DisplayPoint,
1025 &mut Window,
1026 &mut Context<Self>,
1027 ) -> Option<Entity<ui::ContextMenu>>,
1028 >,
1029 >,
1030 last_bounds: Option<Bounds<Pixels>>,
1031 last_position_map: Option<Rc<PositionMap>>,
1032 expect_bounds_change: Option<Bounds<Pixels>>,
1033 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
1034 tasks_update_task: Option<Task<()>>,
1035 in_project_search: bool,
1036 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
1037 breadcrumb_header: Option<String>,
1038 focused_block: Option<FocusedBlock>,
1039 next_scroll_position: NextScrollCursorCenterTopBottom,
1040 addons: HashMap<TypeId, Box<dyn Addon>>,
1041 registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
1042 selection_mark_mode: bool,
1043 toggle_fold_multiple_buffers: Task<()>,
1044 _scroll_cursor_center_top_bottom_task: Task<()>,
1045}
1046
1047#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
1048enum NextScrollCursorCenterTopBottom {
1049 #[default]
1050 Center,
1051 Top,
1052 Bottom,
1053}
1054
1055impl NextScrollCursorCenterTopBottom {
1056 fn next(&self) -> Self {
1057 match self {
1058 Self::Center => Self::Top,
1059 Self::Top => Self::Bottom,
1060 Self::Bottom => Self::Center,
1061 }
1062 }
1063}
1064
1065#[derive(Clone)]
1066pub struct EditorSnapshot {
1067 pub mode: EditorMode,
1068 show_gutter: bool,
1069 show_line_numbers: Option<bool>,
1070 show_git_diff_gutter: Option<bool>,
1071 show_code_actions: Option<bool>,
1072 show_runnables: Option<bool>,
1073 git_blame_gutter_max_author_length: Option<usize>,
1074 pub display_snapshot: DisplaySnapshot,
1075 pub placeholder_text: Option<Arc<str>>,
1076 is_focused: bool,
1077 scroll_anchor: ScrollAnchor,
1078 ongoing_scroll: OngoingScroll,
1079 current_line_highlight: CurrentLineHighlight,
1080 gutter_hovered: bool,
1081}
1082
1083const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
1084
1085#[derive(Default, Debug, Clone, Copy)]
1086pub struct GutterDimensions {
1087 pub left_padding: Pixels,
1088 pub right_padding: Pixels,
1089 pub width: Pixels,
1090 pub margin: Pixels,
1091 pub git_blame_entries_width: Option<Pixels>,
1092}
1093
1094impl GutterDimensions {
1095 /// The full width of the space taken up by the gutter.
1096 pub fn full_width(&self) -> Pixels {
1097 self.margin + self.width
1098 }
1099
1100 /// The width of the space reserved for the fold indicators,
1101 /// use alongside 'justify_end' and `gutter_width` to
1102 /// right align content with the line numbers
1103 pub fn fold_area_width(&self) -> Pixels {
1104 self.margin + self.right_padding
1105 }
1106}
1107
1108#[derive(Debug)]
1109pub struct RemoteSelection {
1110 pub replica_id: ReplicaId,
1111 pub selection: Selection<Anchor>,
1112 pub cursor_shape: CursorShape,
1113 pub peer_id: PeerId,
1114 pub line_mode: bool,
1115 pub participant_index: Option<ParticipantIndex>,
1116 pub user_name: Option<SharedString>,
1117}
1118
1119#[derive(Clone, Debug)]
1120struct SelectionHistoryEntry {
1121 selections: Arc<[Selection<Anchor>]>,
1122 select_next_state: Option<SelectNextState>,
1123 select_prev_state: Option<SelectNextState>,
1124 add_selections_state: Option<AddSelectionsState>,
1125}
1126
1127enum SelectionHistoryMode {
1128 Normal,
1129 Undoing,
1130 Redoing,
1131}
1132
1133#[derive(Clone, PartialEq, Eq, Hash)]
1134struct HoveredCursor {
1135 replica_id: u16,
1136 selection_id: usize,
1137}
1138
1139impl Default for SelectionHistoryMode {
1140 fn default() -> Self {
1141 Self::Normal
1142 }
1143}
1144
1145#[derive(Default)]
1146struct SelectionHistory {
1147 #[allow(clippy::type_complexity)]
1148 selections_by_transaction:
1149 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
1150 mode: SelectionHistoryMode,
1151 undo_stack: VecDeque<SelectionHistoryEntry>,
1152 redo_stack: VecDeque<SelectionHistoryEntry>,
1153}
1154
1155impl SelectionHistory {
1156 fn insert_transaction(
1157 &mut self,
1158 transaction_id: TransactionId,
1159 selections: Arc<[Selection<Anchor>]>,
1160 ) {
1161 self.selections_by_transaction
1162 .insert(transaction_id, (selections, None));
1163 }
1164
1165 #[allow(clippy::type_complexity)]
1166 fn transaction(
1167 &self,
1168 transaction_id: TransactionId,
1169 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
1170 self.selections_by_transaction.get(&transaction_id)
1171 }
1172
1173 #[allow(clippy::type_complexity)]
1174 fn transaction_mut(
1175 &mut self,
1176 transaction_id: TransactionId,
1177 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
1178 self.selections_by_transaction.get_mut(&transaction_id)
1179 }
1180
1181 fn push(&mut self, entry: SelectionHistoryEntry) {
1182 if !entry.selections.is_empty() {
1183 match self.mode {
1184 SelectionHistoryMode::Normal => {
1185 self.push_undo(entry);
1186 self.redo_stack.clear();
1187 }
1188 SelectionHistoryMode::Undoing => self.push_redo(entry),
1189 SelectionHistoryMode::Redoing => self.push_undo(entry),
1190 }
1191 }
1192 }
1193
1194 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
1195 if self
1196 .undo_stack
1197 .back()
1198 .map_or(true, |e| e.selections != entry.selections)
1199 {
1200 self.undo_stack.push_back(entry);
1201 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
1202 self.undo_stack.pop_front();
1203 }
1204 }
1205 }
1206
1207 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
1208 if self
1209 .redo_stack
1210 .back()
1211 .map_or(true, |e| e.selections != entry.selections)
1212 {
1213 self.redo_stack.push_back(entry);
1214 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
1215 self.redo_stack.pop_front();
1216 }
1217 }
1218 }
1219}
1220
1221struct RowHighlight {
1222 index: usize,
1223 range: Range<Anchor>,
1224 color: Hsla,
1225 should_autoscroll: bool,
1226}
1227
1228#[derive(Clone, Debug)]
1229struct AddSelectionsState {
1230 above: bool,
1231 stack: Vec<usize>,
1232}
1233
1234#[derive(Clone)]
1235struct SelectNextState {
1236 query: AhoCorasick,
1237 wordwise: bool,
1238 done: bool,
1239}
1240
1241impl std::fmt::Debug for SelectNextState {
1242 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1243 f.debug_struct(std::any::type_name::<Self>())
1244 .field("wordwise", &self.wordwise)
1245 .field("done", &self.done)
1246 .finish()
1247 }
1248}
1249
1250#[derive(Debug)]
1251struct AutocloseRegion {
1252 selection_id: usize,
1253 range: Range<Anchor>,
1254 pair: BracketPair,
1255}
1256
1257#[derive(Debug)]
1258struct SnippetState {
1259 ranges: Vec<Vec<Range<Anchor>>>,
1260 active_index: usize,
1261 choices: Vec<Option<Vec<String>>>,
1262}
1263
1264#[doc(hidden)]
1265pub struct RenameState {
1266 pub range: Range<Anchor>,
1267 pub old_name: Arc<str>,
1268 pub editor: Entity<Editor>,
1269 block_id: CustomBlockId,
1270}
1271
1272struct InvalidationStack<T>(Vec<T>);
1273
1274struct RegisteredInlineCompletionProvider {
1275 provider: Arc<dyn InlineCompletionProviderHandle>,
1276 _subscription: Subscription,
1277}
1278
1279#[derive(Debug)]
1280struct ActiveDiagnosticGroup {
1281 primary_range: Range<Anchor>,
1282 primary_message: String,
1283 group_id: usize,
1284 blocks: HashMap<CustomBlockId, Diagnostic>,
1285 is_valid: bool,
1286}
1287
1288#[derive(Serialize, Deserialize, Clone, Debug)]
1289pub struct ClipboardSelection {
1290 pub len: usize,
1291 pub is_entire_line: bool,
1292 pub first_line_indent: u32,
1293}
1294
1295#[derive(Debug)]
1296pub(crate) struct NavigationData {
1297 cursor_anchor: Anchor,
1298 cursor_position: Point,
1299 scroll_anchor: ScrollAnchor,
1300 scroll_top_row: u32,
1301}
1302
1303#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1304pub enum GotoDefinitionKind {
1305 Symbol,
1306 Declaration,
1307 Type,
1308 Implementation,
1309}
1310
1311#[derive(Debug, Clone)]
1312enum InlayHintRefreshReason {
1313 Toggle(bool),
1314 SettingsChange(InlayHintSettings),
1315 NewLinesShown,
1316 BufferEdited(HashSet<Arc<Language>>),
1317 RefreshRequested,
1318 ExcerptsRemoved(Vec<ExcerptId>),
1319}
1320
1321impl InlayHintRefreshReason {
1322 fn description(&self) -> &'static str {
1323 match self {
1324 Self::Toggle(_) => "toggle",
1325 Self::SettingsChange(_) => "settings change",
1326 Self::NewLinesShown => "new lines shown",
1327 Self::BufferEdited(_) => "buffer edited",
1328 Self::RefreshRequested => "refresh requested",
1329 Self::ExcerptsRemoved(_) => "excerpts removed",
1330 }
1331 }
1332}
1333
1334pub enum FormatTarget {
1335 Buffers,
1336 Ranges(Vec<Range<MultiBufferPoint>>),
1337}
1338
1339pub(crate) struct FocusedBlock {
1340 id: BlockId,
1341 focus_handle: WeakFocusHandle,
1342}
1343
1344#[derive(Clone)]
1345enum JumpData {
1346 MultiBufferRow {
1347 row: MultiBufferRow,
1348 line_offset_from_top: u32,
1349 },
1350 MultiBufferPoint {
1351 excerpt_id: ExcerptId,
1352 position: Point,
1353 anchor: text::Anchor,
1354 line_offset_from_top: u32,
1355 },
1356}
1357
1358pub enum MultibufferSelectionMode {
1359 First,
1360 All,
1361}
1362
1363impl Editor {
1364 pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1365 let buffer = cx.new(|cx| Buffer::local("", cx));
1366 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1367 Self::new(
1368 EditorMode::SingleLine { auto_width: false },
1369 buffer,
1370 None,
1371 false,
1372 window,
1373 cx,
1374 )
1375 }
1376
1377 pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1378 let buffer = cx.new(|cx| Buffer::local("", cx));
1379 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1380 Self::new(EditorMode::Full, buffer, None, false, window, cx)
1381 }
1382
1383 pub fn auto_width(window: &mut Window, cx: &mut Context<Self>) -> Self {
1384 let buffer = cx.new(|cx| Buffer::local("", cx));
1385 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1386 Self::new(
1387 EditorMode::SingleLine { auto_width: true },
1388 buffer,
1389 None,
1390 false,
1391 window,
1392 cx,
1393 )
1394 }
1395
1396 pub fn auto_height(max_lines: usize, window: &mut Window, cx: &mut Context<Self>) -> Self {
1397 let buffer = cx.new(|cx| Buffer::local("", cx));
1398 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1399 Self::new(
1400 EditorMode::AutoHeight { max_lines },
1401 buffer,
1402 None,
1403 false,
1404 window,
1405 cx,
1406 )
1407 }
1408
1409 pub fn for_buffer(
1410 buffer: Entity<Buffer>,
1411 project: Option<Entity<Project>>,
1412 window: &mut Window,
1413 cx: &mut Context<Self>,
1414 ) -> Self {
1415 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1416 Self::new(EditorMode::Full, buffer, project, false, window, cx)
1417 }
1418
1419 pub fn for_multibuffer(
1420 buffer: Entity<MultiBuffer>,
1421 project: Option<Entity<Project>>,
1422 show_excerpt_controls: bool,
1423 window: &mut Window,
1424 cx: &mut Context<Self>,
1425 ) -> Self {
1426 Self::new(
1427 EditorMode::Full,
1428 buffer,
1429 project,
1430 show_excerpt_controls,
1431 window,
1432 cx,
1433 )
1434 }
1435
1436 pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
1437 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1438 let mut clone = Self::new(
1439 self.mode,
1440 self.buffer.clone(),
1441 self.project.clone(),
1442 show_excerpt_controls,
1443 window,
1444 cx,
1445 );
1446 self.display_map.update(cx, |display_map, cx| {
1447 let snapshot = display_map.snapshot(cx);
1448 clone.display_map.update(cx, |display_map, cx| {
1449 display_map.set_state(&snapshot, cx);
1450 });
1451 });
1452 clone.selections.clone_state(&self.selections);
1453 clone.scroll_manager.clone_state(&self.scroll_manager);
1454 clone.searchable = self.searchable;
1455 clone
1456 }
1457
1458 pub fn new(
1459 mode: EditorMode,
1460 buffer: Entity<MultiBuffer>,
1461 project: Option<Entity<Project>>,
1462 show_excerpt_controls: bool,
1463 window: &mut Window,
1464 cx: &mut Context<Self>,
1465 ) -> Self {
1466 let style = window.text_style();
1467 let font_size = style.font_size.to_pixels(window.rem_size());
1468 let editor = cx.entity().downgrade();
1469 let fold_placeholder = FoldPlaceholder {
1470 constrain_width: true,
1471 render: Arc::new(move |fold_id, fold_range, _, cx| {
1472 let editor = editor.clone();
1473 div()
1474 .id(fold_id)
1475 .bg(cx.theme().colors().ghost_element_background)
1476 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1477 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1478 .rounded_sm()
1479 .size_full()
1480 .cursor_pointer()
1481 .child("⋯")
1482 .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
1483 .on_click(move |_, _window, cx| {
1484 editor
1485 .update(cx, |editor, cx| {
1486 editor.unfold_ranges(
1487 &[fold_range.start..fold_range.end],
1488 true,
1489 false,
1490 cx,
1491 );
1492 cx.stop_propagation();
1493 })
1494 .ok();
1495 })
1496 .into_any()
1497 }),
1498 merge_adjacent: true,
1499 ..Default::default()
1500 };
1501 let display_map = cx.new(|cx| {
1502 DisplayMap::new(
1503 buffer.clone(),
1504 style.font(),
1505 font_size,
1506 None,
1507 show_excerpt_controls,
1508 FILE_HEADER_HEIGHT,
1509 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1510 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1511 fold_placeholder,
1512 cx,
1513 )
1514 });
1515
1516 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1517
1518 let blink_manager = cx.new(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1519
1520 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1521 .then(|| language_settings::SoftWrap::None);
1522
1523 let mut project_subscriptions = Vec::new();
1524 if mode == EditorMode::Full {
1525 if let Some(project) = project.as_ref() {
1526 if buffer.read(cx).is_singleton() {
1527 project_subscriptions.push(cx.observe_in(project, window, |_, _, _, cx| {
1528 cx.emit(EditorEvent::TitleChanged);
1529 }));
1530 }
1531 project_subscriptions.push(cx.subscribe_in(
1532 project,
1533 window,
1534 |editor, _, event, window, cx| {
1535 if let project::Event::RefreshInlayHints = event {
1536 editor
1537 .refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1538 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1539 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1540 let focus_handle = editor.focus_handle(cx);
1541 if focus_handle.is_focused(window) {
1542 let snapshot = buffer.read(cx).snapshot();
1543 for (range, snippet) in snippet_edits {
1544 let editor_range =
1545 language::range_from_lsp(*range).to_offset(&snapshot);
1546 editor
1547 .insert_snippet(
1548 &[editor_range],
1549 snippet.clone(),
1550 window,
1551 cx,
1552 )
1553 .ok();
1554 }
1555 }
1556 }
1557 }
1558 },
1559 ));
1560 if let Some(task_inventory) = project
1561 .read(cx)
1562 .task_store()
1563 .read(cx)
1564 .task_inventory()
1565 .cloned()
1566 {
1567 project_subscriptions.push(cx.observe_in(
1568 &task_inventory,
1569 window,
1570 |editor, _, window, cx| {
1571 editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
1572 },
1573 ));
1574 }
1575 }
1576 }
1577
1578 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1579
1580 let inlay_hint_settings =
1581 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1582 let focus_handle = cx.focus_handle();
1583 cx.on_focus(&focus_handle, window, Self::handle_focus)
1584 .detach();
1585 cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
1586 .detach();
1587 cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
1588 .detach();
1589 cx.on_blur(&focus_handle, window, Self::handle_blur)
1590 .detach();
1591
1592 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1593 Some(false)
1594 } else {
1595 None
1596 };
1597
1598 let mut code_action_providers = Vec::new();
1599 if let Some(project) = project.clone() {
1600 get_uncommitted_diff_for_buffer(
1601 &project,
1602 buffer.read(cx).all_buffers(),
1603 buffer.clone(),
1604 cx,
1605 );
1606 code_action_providers.push(Rc::new(project) as Rc<_>);
1607 }
1608
1609 let mut this = Self {
1610 focus_handle,
1611 show_cursor_when_unfocused: false,
1612 last_focused_descendant: None,
1613 buffer: buffer.clone(),
1614 display_map: display_map.clone(),
1615 selections,
1616 scroll_manager: ScrollManager::new(cx),
1617 columnar_selection_tail: None,
1618 add_selections_state: None,
1619 select_next_state: None,
1620 select_prev_state: None,
1621 selection_history: Default::default(),
1622 autoclose_regions: Default::default(),
1623 snippet_stack: Default::default(),
1624 select_larger_syntax_node_stack: Vec::new(),
1625 ime_transaction: Default::default(),
1626 active_diagnostics: None,
1627 soft_wrap_mode_override,
1628 completion_provider: project.clone().map(|project| Box::new(project) as _),
1629 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
1630 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1631 project,
1632 blink_manager: blink_manager.clone(),
1633 show_local_selections: true,
1634 show_scrollbars: true,
1635 mode,
1636 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1637 show_gutter: mode == EditorMode::Full,
1638 show_line_numbers: None,
1639 use_relative_line_numbers: None,
1640 show_git_diff_gutter: None,
1641 show_code_actions: None,
1642 show_runnables: None,
1643 show_wrap_guides: None,
1644 show_indent_guides,
1645 placeholder_text: None,
1646 highlight_order: 0,
1647 highlighted_rows: HashMap::default(),
1648 background_highlights: Default::default(),
1649 gutter_highlights: TreeMap::default(),
1650 scrollbar_marker_state: ScrollbarMarkerState::default(),
1651 active_indent_guides_state: ActiveIndentGuidesState::default(),
1652 nav_history: None,
1653 context_menu: RefCell::new(None),
1654 mouse_context_menu: None,
1655 completion_tasks: Default::default(),
1656 signature_help_state: SignatureHelpState::default(),
1657 auto_signature_help: None,
1658 find_all_references_task_sources: Vec::new(),
1659 next_completion_id: 0,
1660 next_inlay_id: 0,
1661 code_action_providers,
1662 available_code_actions: Default::default(),
1663 code_actions_task: Default::default(),
1664 document_highlights_task: Default::default(),
1665 linked_editing_range_task: Default::default(),
1666 pending_rename: Default::default(),
1667 searchable: true,
1668 cursor_shape: EditorSettings::get_global(cx)
1669 .cursor_shape
1670 .unwrap_or_default(),
1671 current_line_highlight: None,
1672 autoindent_mode: Some(AutoindentMode::EachLine),
1673 collapse_matches: false,
1674 workspace: None,
1675 input_enabled: true,
1676 use_modal_editing: mode == EditorMode::Full,
1677 read_only: false,
1678 use_autoclose: true,
1679 use_auto_surround: true,
1680 auto_replace_emoji_shortcode: false,
1681 leader_peer_id: None,
1682 remote_id: None,
1683 hover_state: Default::default(),
1684 pending_mouse_down: None,
1685 hovered_link_state: Default::default(),
1686 edit_prediction_provider: None,
1687 active_inline_completion: None,
1688 stale_inline_completion_in_menu: None,
1689 edit_prediction_preview: EditPredictionPreview::Inactive,
1690 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1691
1692 gutter_hovered: false,
1693 pixel_position_of_newest_cursor: None,
1694 last_bounds: None,
1695 last_position_map: None,
1696 expect_bounds_change: None,
1697 gutter_dimensions: GutterDimensions::default(),
1698 style: None,
1699 show_cursor_names: false,
1700 hovered_cursors: Default::default(),
1701 next_editor_action_id: EditorActionId::default(),
1702 editor_actions: Rc::default(),
1703 inline_completions_hidden_for_vim_mode: false,
1704 show_inline_completions_override: None,
1705 menu_inline_completions_policy: MenuInlineCompletionsPolicy::ByProvider,
1706 edit_prediction_settings: EditPredictionSettings::Disabled,
1707 custom_context_menu: None,
1708 show_git_blame_gutter: false,
1709 show_git_blame_inline: false,
1710 distinguish_unstaged_diff_hunks: false,
1711 show_selection_menu: None,
1712 show_git_blame_inline_delay_task: None,
1713 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1714 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1715 .session
1716 .restore_unsaved_buffers,
1717 blame: None,
1718 blame_subscription: None,
1719 tasks: Default::default(),
1720 _subscriptions: vec![
1721 cx.observe(&buffer, Self::on_buffer_changed),
1722 cx.subscribe_in(&buffer, window, Self::on_buffer_event),
1723 cx.observe_in(&display_map, window, Self::on_display_map_changed),
1724 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1725 cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
1726 cx.observe_window_activation(window, |editor, window, cx| {
1727 let active = window.is_window_active();
1728 editor.blink_manager.update(cx, |blink_manager, cx| {
1729 if active {
1730 blink_manager.enable(cx);
1731 } else {
1732 blink_manager.disable(cx);
1733 }
1734 });
1735 }),
1736 ],
1737 tasks_update_task: None,
1738 linked_edit_ranges: Default::default(),
1739 in_project_search: false,
1740 previous_search_ranges: None,
1741 breadcrumb_header: None,
1742 focused_block: None,
1743 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1744 addons: HashMap::default(),
1745 registered_buffers: HashMap::default(),
1746 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1747 selection_mark_mode: false,
1748 toggle_fold_multiple_buffers: Task::ready(()),
1749 text_style_refinement: None,
1750 };
1751 this.tasks_update_task = Some(this.refresh_runnables(window, cx));
1752 this._subscriptions.extend(project_subscriptions);
1753
1754 this.end_selection(window, cx);
1755 this.scroll_manager.show_scrollbar(window, cx);
1756
1757 if mode == EditorMode::Full {
1758 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1759 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1760
1761 if this.git_blame_inline_enabled {
1762 this.git_blame_inline_enabled = true;
1763 this.start_git_blame_inline(false, window, cx);
1764 }
1765
1766 if let Some(buffer) = buffer.read(cx).as_singleton() {
1767 if let Some(project) = this.project.as_ref() {
1768 let lsp_store = project.read(cx).lsp_store();
1769 let handle = lsp_store.update(cx, |lsp_store, cx| {
1770 lsp_store.register_buffer_with_language_servers(&buffer, cx)
1771 });
1772 this.registered_buffers
1773 .insert(buffer.read(cx).remote_id(), handle);
1774 }
1775 }
1776 }
1777
1778 this.report_editor_event("Editor Opened", None, cx);
1779 this
1780 }
1781
1782 pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
1783 self.mouse_context_menu
1784 .as_ref()
1785 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
1786 }
1787
1788 fn key_context(&self, window: &Window, cx: &App) -> KeyContext {
1789 let mut key_context = KeyContext::new_with_defaults();
1790 key_context.add("Editor");
1791 let mode = match self.mode {
1792 EditorMode::SingleLine { .. } => "single_line",
1793 EditorMode::AutoHeight { .. } => "auto_height",
1794 EditorMode::Full => "full",
1795 };
1796
1797 if EditorSettings::jupyter_enabled(cx) {
1798 key_context.add("jupyter");
1799 }
1800
1801 key_context.set("mode", mode);
1802 if self.pending_rename.is_some() {
1803 key_context.add("renaming");
1804 }
1805
1806 let mut showing_completions = false;
1807
1808 match self.context_menu.borrow().as_ref() {
1809 Some(CodeContextMenu::Completions(_)) => {
1810 key_context.add("menu");
1811 key_context.add("showing_completions");
1812 showing_completions = true;
1813 }
1814 Some(CodeContextMenu::CodeActions(_)) => {
1815 key_context.add("menu");
1816 key_context.add("showing_code_actions")
1817 }
1818 None => {}
1819 }
1820
1821 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
1822 if !self.focus_handle(cx).contains_focused(window, cx)
1823 || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
1824 {
1825 for addon in self.addons.values() {
1826 addon.extend_key_context(&mut key_context, cx)
1827 }
1828 }
1829
1830 if let Some(extension) = self
1831 .buffer
1832 .read(cx)
1833 .as_singleton()
1834 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
1835 {
1836 key_context.set("extension", extension.to_string());
1837 }
1838
1839 if self.has_active_inline_completion() {
1840 key_context.add("copilot_suggestion");
1841 key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
1842
1843 if showing_completions || self.edit_prediction_requires_modifier() {
1844 key_context.add(EDIT_PREDICTION_REQUIRES_MODIFIER_KEY_CONTEXT);
1845 }
1846 }
1847
1848 if self.selection_mark_mode {
1849 key_context.add("selection_mode");
1850 }
1851
1852 key_context
1853 }
1854
1855 pub fn accept_edit_prediction_keybind(
1856 &self,
1857 window: &Window,
1858 cx: &App,
1859 ) -> AcceptEditPredictionBinding {
1860 let mut context = self.key_context(window, cx);
1861 context.add(EDIT_PREDICTION_KEY_CONTEXT);
1862
1863 AcceptEditPredictionBinding(
1864 window
1865 .bindings_for_action_in_context(&AcceptEditPrediction, context)
1866 .into_iter()
1867 .rev()
1868 .next(),
1869 )
1870 }
1871
1872 pub fn new_file(
1873 workspace: &mut Workspace,
1874 _: &workspace::NewFile,
1875 window: &mut Window,
1876 cx: &mut Context<Workspace>,
1877 ) {
1878 Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
1879 "Failed to create buffer",
1880 window,
1881 cx,
1882 |e, _, _| match e.error_code() {
1883 ErrorCode::RemoteUpgradeRequired => Some(format!(
1884 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1885 e.error_tag("required").unwrap_or("the latest version")
1886 )),
1887 _ => None,
1888 },
1889 );
1890 }
1891
1892 pub fn new_in_workspace(
1893 workspace: &mut Workspace,
1894 window: &mut Window,
1895 cx: &mut Context<Workspace>,
1896 ) -> Task<Result<Entity<Editor>>> {
1897 let project = workspace.project().clone();
1898 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1899
1900 cx.spawn_in(window, |workspace, mut cx| async move {
1901 let buffer = create.await?;
1902 workspace.update_in(&mut cx, |workspace, window, cx| {
1903 let editor =
1904 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
1905 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
1906 editor
1907 })
1908 })
1909 }
1910
1911 fn new_file_vertical(
1912 workspace: &mut Workspace,
1913 _: &workspace::NewFileSplitVertical,
1914 window: &mut Window,
1915 cx: &mut Context<Workspace>,
1916 ) {
1917 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
1918 }
1919
1920 fn new_file_horizontal(
1921 workspace: &mut Workspace,
1922 _: &workspace::NewFileSplitHorizontal,
1923 window: &mut Window,
1924 cx: &mut Context<Workspace>,
1925 ) {
1926 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
1927 }
1928
1929 fn new_file_in_direction(
1930 workspace: &mut Workspace,
1931 direction: SplitDirection,
1932 window: &mut Window,
1933 cx: &mut Context<Workspace>,
1934 ) {
1935 let project = workspace.project().clone();
1936 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1937
1938 cx.spawn_in(window, |workspace, mut cx| async move {
1939 let buffer = create.await?;
1940 workspace.update_in(&mut cx, move |workspace, window, cx| {
1941 workspace.split_item(
1942 direction,
1943 Box::new(
1944 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
1945 ),
1946 window,
1947 cx,
1948 )
1949 })?;
1950 anyhow::Ok(())
1951 })
1952 .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
1953 match e.error_code() {
1954 ErrorCode::RemoteUpgradeRequired => Some(format!(
1955 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1956 e.error_tag("required").unwrap_or("the latest version")
1957 )),
1958 _ => None,
1959 }
1960 });
1961 }
1962
1963 pub fn leader_peer_id(&self) -> Option<PeerId> {
1964 self.leader_peer_id
1965 }
1966
1967 pub fn buffer(&self) -> &Entity<MultiBuffer> {
1968 &self.buffer
1969 }
1970
1971 pub fn workspace(&self) -> Option<Entity<Workspace>> {
1972 self.workspace.as_ref()?.0.upgrade()
1973 }
1974
1975 pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
1976 self.buffer().read(cx).title(cx)
1977 }
1978
1979 pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
1980 let git_blame_gutter_max_author_length = self
1981 .render_git_blame_gutter(cx)
1982 .then(|| {
1983 if let Some(blame) = self.blame.as_ref() {
1984 let max_author_length =
1985 blame.update(cx, |blame, cx| blame.max_author_length(cx));
1986 Some(max_author_length)
1987 } else {
1988 None
1989 }
1990 })
1991 .flatten();
1992
1993 EditorSnapshot {
1994 mode: self.mode,
1995 show_gutter: self.show_gutter,
1996 show_line_numbers: self.show_line_numbers,
1997 show_git_diff_gutter: self.show_git_diff_gutter,
1998 show_code_actions: self.show_code_actions,
1999 show_runnables: self.show_runnables,
2000 git_blame_gutter_max_author_length,
2001 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
2002 scroll_anchor: self.scroll_manager.anchor(),
2003 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
2004 placeholder_text: self.placeholder_text.clone(),
2005 is_focused: self.focus_handle.is_focused(window),
2006 current_line_highlight: self
2007 .current_line_highlight
2008 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
2009 gutter_hovered: self.gutter_hovered,
2010 }
2011 }
2012
2013 pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
2014 self.buffer.read(cx).language_at(point, cx)
2015 }
2016
2017 pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
2018 self.buffer.read(cx).read(cx).file_at(point).cloned()
2019 }
2020
2021 pub fn active_excerpt(
2022 &self,
2023 cx: &App,
2024 ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
2025 self.buffer
2026 .read(cx)
2027 .excerpt_containing(self.selections.newest_anchor().head(), cx)
2028 }
2029
2030 pub fn mode(&self) -> EditorMode {
2031 self.mode
2032 }
2033
2034 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
2035 self.collaboration_hub.as_deref()
2036 }
2037
2038 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
2039 self.collaboration_hub = Some(hub);
2040 }
2041
2042 pub fn set_in_project_search(&mut self, in_project_search: bool) {
2043 self.in_project_search = in_project_search;
2044 }
2045
2046 pub fn set_custom_context_menu(
2047 &mut self,
2048 f: impl 'static
2049 + Fn(
2050 &mut Self,
2051 DisplayPoint,
2052 &mut Window,
2053 &mut Context<Self>,
2054 ) -> Option<Entity<ui::ContextMenu>>,
2055 ) {
2056 self.custom_context_menu = Some(Box::new(f))
2057 }
2058
2059 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
2060 self.completion_provider = provider;
2061 }
2062
2063 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
2064 self.semantics_provider.clone()
2065 }
2066
2067 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
2068 self.semantics_provider = provider;
2069 }
2070
2071 pub fn set_edit_prediction_provider<T>(
2072 &mut self,
2073 provider: Option<Entity<T>>,
2074 window: &mut Window,
2075 cx: &mut Context<Self>,
2076 ) where
2077 T: EditPredictionProvider,
2078 {
2079 self.edit_prediction_provider =
2080 provider.map(|provider| RegisteredInlineCompletionProvider {
2081 _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
2082 if this.focus_handle.is_focused(window) {
2083 this.update_visible_inline_completion(window, cx);
2084 }
2085 }),
2086 provider: Arc::new(provider),
2087 });
2088 self.refresh_inline_completion(false, false, window, cx);
2089 }
2090
2091 pub fn placeholder_text(&self) -> Option<&str> {
2092 self.placeholder_text.as_deref()
2093 }
2094
2095 pub fn set_placeholder_text(
2096 &mut self,
2097 placeholder_text: impl Into<Arc<str>>,
2098 cx: &mut Context<Self>,
2099 ) {
2100 let placeholder_text = Some(placeholder_text.into());
2101 if self.placeholder_text != placeholder_text {
2102 self.placeholder_text = placeholder_text;
2103 cx.notify();
2104 }
2105 }
2106
2107 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
2108 self.cursor_shape = cursor_shape;
2109
2110 // Disrupt blink for immediate user feedback that the cursor shape has changed
2111 self.blink_manager.update(cx, BlinkManager::show_cursor);
2112
2113 cx.notify();
2114 }
2115
2116 pub fn set_current_line_highlight(
2117 &mut self,
2118 current_line_highlight: Option<CurrentLineHighlight>,
2119 ) {
2120 self.current_line_highlight = current_line_highlight;
2121 }
2122
2123 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
2124 self.collapse_matches = collapse_matches;
2125 }
2126
2127 fn register_buffers_with_language_servers(&mut self, cx: &mut Context<Self>) {
2128 let buffers = self.buffer.read(cx).all_buffers();
2129 let Some(lsp_store) = self.lsp_store(cx) else {
2130 return;
2131 };
2132 lsp_store.update(cx, |lsp_store, cx| {
2133 for buffer in buffers {
2134 self.registered_buffers
2135 .entry(buffer.read(cx).remote_id())
2136 .or_insert_with(|| {
2137 lsp_store.register_buffer_with_language_servers(&buffer, cx)
2138 });
2139 }
2140 })
2141 }
2142
2143 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
2144 if self.collapse_matches {
2145 return range.start..range.start;
2146 }
2147 range.clone()
2148 }
2149
2150 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
2151 if self.display_map.read(cx).clip_at_line_ends != clip {
2152 self.display_map
2153 .update(cx, |map, _| map.clip_at_line_ends = clip);
2154 }
2155 }
2156
2157 pub fn set_input_enabled(&mut self, input_enabled: bool) {
2158 self.input_enabled = input_enabled;
2159 }
2160
2161 pub fn set_inline_completions_hidden_for_vim_mode(
2162 &mut self,
2163 hidden: bool,
2164 window: &mut Window,
2165 cx: &mut Context<Self>,
2166 ) {
2167 if hidden != self.inline_completions_hidden_for_vim_mode {
2168 self.inline_completions_hidden_for_vim_mode = hidden;
2169 if hidden {
2170 self.update_visible_inline_completion(window, cx);
2171 } else {
2172 self.refresh_inline_completion(true, false, window, cx);
2173 }
2174 }
2175 }
2176
2177 pub fn set_menu_inline_completions_policy(&mut self, value: MenuInlineCompletionsPolicy) {
2178 self.menu_inline_completions_policy = value;
2179 }
2180
2181 pub fn set_autoindent(&mut self, autoindent: bool) {
2182 if autoindent {
2183 self.autoindent_mode = Some(AutoindentMode::EachLine);
2184 } else {
2185 self.autoindent_mode = None;
2186 }
2187 }
2188
2189 pub fn read_only(&self, cx: &App) -> bool {
2190 self.read_only || self.buffer.read(cx).read_only()
2191 }
2192
2193 pub fn set_read_only(&mut self, read_only: bool) {
2194 self.read_only = read_only;
2195 }
2196
2197 pub fn set_use_autoclose(&mut self, autoclose: bool) {
2198 self.use_autoclose = autoclose;
2199 }
2200
2201 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
2202 self.use_auto_surround = auto_surround;
2203 }
2204
2205 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
2206 self.auto_replace_emoji_shortcode = auto_replace;
2207 }
2208
2209 pub fn toggle_inline_completions(
2210 &mut self,
2211 _: &ToggleEditPrediction,
2212 window: &mut Window,
2213 cx: &mut Context<Self>,
2214 ) {
2215 if self.show_inline_completions_override.is_some() {
2216 self.set_show_edit_predictions(None, window, cx);
2217 } else {
2218 let show_edit_predictions = !self.edit_predictions_enabled();
2219 self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
2220 }
2221 }
2222
2223 pub fn set_show_edit_predictions(
2224 &mut self,
2225 show_edit_predictions: Option<bool>,
2226 window: &mut Window,
2227 cx: &mut Context<Self>,
2228 ) {
2229 self.show_inline_completions_override = show_edit_predictions;
2230 self.refresh_inline_completion(false, true, window, cx);
2231 }
2232
2233 fn inline_completions_disabled_in_scope(
2234 &self,
2235 buffer: &Entity<Buffer>,
2236 buffer_position: language::Anchor,
2237 cx: &App,
2238 ) -> bool {
2239 let snapshot = buffer.read(cx).snapshot();
2240 let settings = snapshot.settings_at(buffer_position, cx);
2241
2242 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
2243 return false;
2244 };
2245
2246 scope.override_name().map_or(false, |scope_name| {
2247 settings
2248 .edit_predictions_disabled_in
2249 .iter()
2250 .any(|s| s == scope_name)
2251 })
2252 }
2253
2254 pub fn set_use_modal_editing(&mut self, to: bool) {
2255 self.use_modal_editing = to;
2256 }
2257
2258 pub fn use_modal_editing(&self) -> bool {
2259 self.use_modal_editing
2260 }
2261
2262 fn selections_did_change(
2263 &mut self,
2264 local: bool,
2265 old_cursor_position: &Anchor,
2266 show_completions: bool,
2267 window: &mut Window,
2268 cx: &mut Context<Self>,
2269 ) {
2270 window.invalidate_character_coordinates();
2271
2272 // Copy selections to primary selection buffer
2273 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
2274 if local {
2275 let selections = self.selections.all::<usize>(cx);
2276 let buffer_handle = self.buffer.read(cx).read(cx);
2277
2278 let mut text = String::new();
2279 for (index, selection) in selections.iter().enumerate() {
2280 let text_for_selection = buffer_handle
2281 .text_for_range(selection.start..selection.end)
2282 .collect::<String>();
2283
2284 text.push_str(&text_for_selection);
2285 if index != selections.len() - 1 {
2286 text.push('\n');
2287 }
2288 }
2289
2290 if !text.is_empty() {
2291 cx.write_to_primary(ClipboardItem::new_string(text));
2292 }
2293 }
2294
2295 if self.focus_handle.is_focused(window) && self.leader_peer_id.is_none() {
2296 self.buffer.update(cx, |buffer, cx| {
2297 buffer.set_active_selections(
2298 &self.selections.disjoint_anchors(),
2299 self.selections.line_mode,
2300 self.cursor_shape,
2301 cx,
2302 )
2303 });
2304 }
2305 let display_map = self
2306 .display_map
2307 .update(cx, |display_map, cx| display_map.snapshot(cx));
2308 let buffer = &display_map.buffer_snapshot;
2309 self.add_selections_state = None;
2310 self.select_next_state = None;
2311 self.select_prev_state = None;
2312 self.select_larger_syntax_node_stack.clear();
2313 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2314 self.snippet_stack
2315 .invalidate(&self.selections.disjoint_anchors(), buffer);
2316 self.take_rename(false, window, cx);
2317
2318 let new_cursor_position = self.selections.newest_anchor().head();
2319
2320 self.push_to_nav_history(
2321 *old_cursor_position,
2322 Some(new_cursor_position.to_point(buffer)),
2323 cx,
2324 );
2325
2326 if local {
2327 let new_cursor_position = self.selections.newest_anchor().head();
2328 let mut context_menu = self.context_menu.borrow_mut();
2329 let completion_menu = match context_menu.as_ref() {
2330 Some(CodeContextMenu::Completions(menu)) => Some(menu),
2331 _ => {
2332 *context_menu = None;
2333 None
2334 }
2335 };
2336 if let Some(buffer_id) = new_cursor_position.buffer_id {
2337 if !self.registered_buffers.contains_key(&buffer_id) {
2338 if let Some(lsp_store) = self.lsp_store(cx) {
2339 lsp_store.update(cx, |lsp_store, cx| {
2340 let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) else {
2341 return;
2342 };
2343 self.registered_buffers.insert(
2344 buffer_id,
2345 lsp_store.register_buffer_with_language_servers(&buffer, cx),
2346 );
2347 })
2348 }
2349 }
2350 }
2351
2352 if let Some(completion_menu) = completion_menu {
2353 let cursor_position = new_cursor_position.to_offset(buffer);
2354 let (word_range, kind) =
2355 buffer.surrounding_word(completion_menu.initial_position, true);
2356 if kind == Some(CharKind::Word)
2357 && word_range.to_inclusive().contains(&cursor_position)
2358 {
2359 let mut completion_menu = completion_menu.clone();
2360 drop(context_menu);
2361
2362 let query = Self::completion_query(buffer, cursor_position);
2363 cx.spawn(move |this, mut cx| async move {
2364 completion_menu
2365 .filter(query.as_deref(), cx.background_executor().clone())
2366 .await;
2367
2368 this.update(&mut cx, |this, cx| {
2369 let mut context_menu = this.context_menu.borrow_mut();
2370 let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
2371 else {
2372 return;
2373 };
2374
2375 if menu.id > completion_menu.id {
2376 return;
2377 }
2378
2379 *context_menu = Some(CodeContextMenu::Completions(completion_menu));
2380 drop(context_menu);
2381 cx.notify();
2382 })
2383 })
2384 .detach();
2385
2386 if show_completions {
2387 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
2388 }
2389 } else {
2390 drop(context_menu);
2391 self.hide_context_menu(window, cx);
2392 }
2393 } else {
2394 drop(context_menu);
2395 }
2396
2397 hide_hover(self, cx);
2398
2399 if old_cursor_position.to_display_point(&display_map).row()
2400 != new_cursor_position.to_display_point(&display_map).row()
2401 {
2402 self.available_code_actions.take();
2403 }
2404 self.refresh_code_actions(window, cx);
2405 self.refresh_document_highlights(cx);
2406 refresh_matching_bracket_highlights(self, window, cx);
2407 self.update_visible_inline_completion(window, cx);
2408 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
2409 if self.git_blame_inline_enabled {
2410 self.start_inline_blame_timer(window, cx);
2411 }
2412 }
2413
2414 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2415 cx.emit(EditorEvent::SelectionsChanged { local });
2416
2417 if self.selections.disjoint_anchors().len() == 1 {
2418 cx.emit(SearchEvent::ActiveMatchChanged)
2419 }
2420 cx.notify();
2421 }
2422
2423 pub fn change_selections<R>(
2424 &mut self,
2425 autoscroll: Option<Autoscroll>,
2426 window: &mut Window,
2427 cx: &mut Context<Self>,
2428 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2429 ) -> R {
2430 self.change_selections_inner(autoscroll, true, window, cx, change)
2431 }
2432
2433 pub fn change_selections_inner<R>(
2434 &mut self,
2435 autoscroll: Option<Autoscroll>,
2436 request_completions: bool,
2437 window: &mut Window,
2438 cx: &mut Context<Self>,
2439 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2440 ) -> R {
2441 let old_cursor_position = self.selections.newest_anchor().head();
2442 self.push_to_selection_history();
2443
2444 let (changed, result) = self.selections.change_with(cx, change);
2445
2446 if changed {
2447 if let Some(autoscroll) = autoscroll {
2448 self.request_autoscroll(autoscroll, cx);
2449 }
2450 self.selections_did_change(true, &old_cursor_position, request_completions, window, cx);
2451
2452 if self.should_open_signature_help_automatically(
2453 &old_cursor_position,
2454 self.signature_help_state.backspace_pressed(),
2455 cx,
2456 ) {
2457 self.show_signature_help(&ShowSignatureHelp, window, cx);
2458 }
2459 self.signature_help_state.set_backspace_pressed(false);
2460 }
2461
2462 result
2463 }
2464
2465 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2466 where
2467 I: IntoIterator<Item = (Range<S>, T)>,
2468 S: ToOffset,
2469 T: Into<Arc<str>>,
2470 {
2471 if self.read_only(cx) {
2472 return;
2473 }
2474
2475 self.buffer
2476 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2477 }
2478
2479 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2480 where
2481 I: IntoIterator<Item = (Range<S>, T)>,
2482 S: ToOffset,
2483 T: Into<Arc<str>>,
2484 {
2485 if self.read_only(cx) {
2486 return;
2487 }
2488
2489 self.buffer.update(cx, |buffer, cx| {
2490 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2491 });
2492 }
2493
2494 pub fn edit_with_block_indent<I, S, T>(
2495 &mut self,
2496 edits: I,
2497 original_indent_columns: Vec<u32>,
2498 cx: &mut Context<Self>,
2499 ) where
2500 I: IntoIterator<Item = (Range<S>, T)>,
2501 S: ToOffset,
2502 T: Into<Arc<str>>,
2503 {
2504 if self.read_only(cx) {
2505 return;
2506 }
2507
2508 self.buffer.update(cx, |buffer, cx| {
2509 buffer.edit(
2510 edits,
2511 Some(AutoindentMode::Block {
2512 original_indent_columns,
2513 }),
2514 cx,
2515 )
2516 });
2517 }
2518
2519 fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
2520 self.hide_context_menu(window, cx);
2521
2522 match phase {
2523 SelectPhase::Begin {
2524 position,
2525 add,
2526 click_count,
2527 } => self.begin_selection(position, add, click_count, window, cx),
2528 SelectPhase::BeginColumnar {
2529 position,
2530 goal_column,
2531 reset,
2532 } => self.begin_columnar_selection(position, goal_column, reset, window, cx),
2533 SelectPhase::Extend {
2534 position,
2535 click_count,
2536 } => self.extend_selection(position, click_count, window, cx),
2537 SelectPhase::Update {
2538 position,
2539 goal_column,
2540 scroll_delta,
2541 } => self.update_selection(position, goal_column, scroll_delta, window, cx),
2542 SelectPhase::End => self.end_selection(window, cx),
2543 }
2544 }
2545
2546 fn extend_selection(
2547 &mut self,
2548 position: DisplayPoint,
2549 click_count: usize,
2550 window: &mut Window,
2551 cx: &mut Context<Self>,
2552 ) {
2553 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2554 let tail = self.selections.newest::<usize>(cx).tail();
2555 self.begin_selection(position, false, click_count, window, cx);
2556
2557 let position = position.to_offset(&display_map, Bias::Left);
2558 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2559
2560 let mut pending_selection = self
2561 .selections
2562 .pending_anchor()
2563 .expect("extend_selection not called with pending selection");
2564 if position >= tail {
2565 pending_selection.start = tail_anchor;
2566 } else {
2567 pending_selection.end = tail_anchor;
2568 pending_selection.reversed = true;
2569 }
2570
2571 let mut pending_mode = self.selections.pending_mode().unwrap();
2572 match &mut pending_mode {
2573 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2574 _ => {}
2575 }
2576
2577 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
2578 s.set_pending(pending_selection, pending_mode)
2579 });
2580 }
2581
2582 fn begin_selection(
2583 &mut self,
2584 position: DisplayPoint,
2585 add: bool,
2586 click_count: usize,
2587 window: &mut Window,
2588 cx: &mut Context<Self>,
2589 ) {
2590 if !self.focus_handle.is_focused(window) {
2591 self.last_focused_descendant = None;
2592 window.focus(&self.focus_handle);
2593 }
2594
2595 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2596 let buffer = &display_map.buffer_snapshot;
2597 let newest_selection = self.selections.newest_anchor().clone();
2598 let position = display_map.clip_point(position, Bias::Left);
2599
2600 let start;
2601 let end;
2602 let mode;
2603 let mut auto_scroll;
2604 match click_count {
2605 1 => {
2606 start = buffer.anchor_before(position.to_point(&display_map));
2607 end = start;
2608 mode = SelectMode::Character;
2609 auto_scroll = true;
2610 }
2611 2 => {
2612 let range = movement::surrounding_word(&display_map, position);
2613 start = buffer.anchor_before(range.start.to_point(&display_map));
2614 end = buffer.anchor_before(range.end.to_point(&display_map));
2615 mode = SelectMode::Word(start..end);
2616 auto_scroll = true;
2617 }
2618 3 => {
2619 let position = display_map
2620 .clip_point(position, Bias::Left)
2621 .to_point(&display_map);
2622 let line_start = display_map.prev_line_boundary(position).0;
2623 let next_line_start = buffer.clip_point(
2624 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2625 Bias::Left,
2626 );
2627 start = buffer.anchor_before(line_start);
2628 end = buffer.anchor_before(next_line_start);
2629 mode = SelectMode::Line(start..end);
2630 auto_scroll = true;
2631 }
2632 _ => {
2633 start = buffer.anchor_before(0);
2634 end = buffer.anchor_before(buffer.len());
2635 mode = SelectMode::All;
2636 auto_scroll = false;
2637 }
2638 }
2639 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2640
2641 let point_to_delete: Option<usize> = {
2642 let selected_points: Vec<Selection<Point>> =
2643 self.selections.disjoint_in_range(start..end, cx);
2644
2645 if !add || click_count > 1 {
2646 None
2647 } else if !selected_points.is_empty() {
2648 Some(selected_points[0].id)
2649 } else {
2650 let clicked_point_already_selected =
2651 self.selections.disjoint.iter().find(|selection| {
2652 selection.start.to_point(buffer) == start.to_point(buffer)
2653 || selection.end.to_point(buffer) == end.to_point(buffer)
2654 });
2655
2656 clicked_point_already_selected.map(|selection| selection.id)
2657 }
2658 };
2659
2660 let selections_count = self.selections.count();
2661
2662 self.change_selections(auto_scroll.then(Autoscroll::newest), window, cx, |s| {
2663 if let Some(point_to_delete) = point_to_delete {
2664 s.delete(point_to_delete);
2665
2666 if selections_count == 1 {
2667 s.set_pending_anchor_range(start..end, mode);
2668 }
2669 } else {
2670 if !add {
2671 s.clear_disjoint();
2672 } else if click_count > 1 {
2673 s.delete(newest_selection.id)
2674 }
2675
2676 s.set_pending_anchor_range(start..end, mode);
2677 }
2678 });
2679 }
2680
2681 fn begin_columnar_selection(
2682 &mut self,
2683 position: DisplayPoint,
2684 goal_column: u32,
2685 reset: bool,
2686 window: &mut Window,
2687 cx: &mut Context<Self>,
2688 ) {
2689 if !self.focus_handle.is_focused(window) {
2690 self.last_focused_descendant = None;
2691 window.focus(&self.focus_handle);
2692 }
2693
2694 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2695
2696 if reset {
2697 let pointer_position = display_map
2698 .buffer_snapshot
2699 .anchor_before(position.to_point(&display_map));
2700
2701 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
2702 s.clear_disjoint();
2703 s.set_pending_anchor_range(
2704 pointer_position..pointer_position,
2705 SelectMode::Character,
2706 );
2707 });
2708 }
2709
2710 let tail = self.selections.newest::<Point>(cx).tail();
2711 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2712
2713 if !reset {
2714 self.select_columns(
2715 tail.to_display_point(&display_map),
2716 position,
2717 goal_column,
2718 &display_map,
2719 window,
2720 cx,
2721 );
2722 }
2723 }
2724
2725 fn update_selection(
2726 &mut self,
2727 position: DisplayPoint,
2728 goal_column: u32,
2729 scroll_delta: gpui::Point<f32>,
2730 window: &mut Window,
2731 cx: &mut Context<Self>,
2732 ) {
2733 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2734
2735 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2736 let tail = tail.to_display_point(&display_map);
2737 self.select_columns(tail, position, goal_column, &display_map, window, cx);
2738 } else if let Some(mut pending) = self.selections.pending_anchor() {
2739 let buffer = self.buffer.read(cx).snapshot(cx);
2740 let head;
2741 let tail;
2742 let mode = self.selections.pending_mode().unwrap();
2743 match &mode {
2744 SelectMode::Character => {
2745 head = position.to_point(&display_map);
2746 tail = pending.tail().to_point(&buffer);
2747 }
2748 SelectMode::Word(original_range) => {
2749 let original_display_range = original_range.start.to_display_point(&display_map)
2750 ..original_range.end.to_display_point(&display_map);
2751 let original_buffer_range = original_display_range.start.to_point(&display_map)
2752 ..original_display_range.end.to_point(&display_map);
2753 if movement::is_inside_word(&display_map, position)
2754 || original_display_range.contains(&position)
2755 {
2756 let word_range = movement::surrounding_word(&display_map, position);
2757 if word_range.start < original_display_range.start {
2758 head = word_range.start.to_point(&display_map);
2759 } else {
2760 head = word_range.end.to_point(&display_map);
2761 }
2762 } else {
2763 head = position.to_point(&display_map);
2764 }
2765
2766 if head <= original_buffer_range.start {
2767 tail = original_buffer_range.end;
2768 } else {
2769 tail = original_buffer_range.start;
2770 }
2771 }
2772 SelectMode::Line(original_range) => {
2773 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2774
2775 let position = display_map
2776 .clip_point(position, Bias::Left)
2777 .to_point(&display_map);
2778 let line_start = display_map.prev_line_boundary(position).0;
2779 let next_line_start = buffer.clip_point(
2780 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2781 Bias::Left,
2782 );
2783
2784 if line_start < original_range.start {
2785 head = line_start
2786 } else {
2787 head = next_line_start
2788 }
2789
2790 if head <= original_range.start {
2791 tail = original_range.end;
2792 } else {
2793 tail = original_range.start;
2794 }
2795 }
2796 SelectMode::All => {
2797 return;
2798 }
2799 };
2800
2801 if head < tail {
2802 pending.start = buffer.anchor_before(head);
2803 pending.end = buffer.anchor_before(tail);
2804 pending.reversed = true;
2805 } else {
2806 pending.start = buffer.anchor_before(tail);
2807 pending.end = buffer.anchor_before(head);
2808 pending.reversed = false;
2809 }
2810
2811 self.change_selections(None, window, cx, |s| {
2812 s.set_pending(pending, mode);
2813 });
2814 } else {
2815 log::error!("update_selection dispatched with no pending selection");
2816 return;
2817 }
2818
2819 self.apply_scroll_delta(scroll_delta, window, cx);
2820 cx.notify();
2821 }
2822
2823 fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
2824 self.columnar_selection_tail.take();
2825 if self.selections.pending_anchor().is_some() {
2826 let selections = self.selections.all::<usize>(cx);
2827 self.change_selections(None, window, cx, |s| {
2828 s.select(selections);
2829 s.clear_pending();
2830 });
2831 }
2832 }
2833
2834 fn select_columns(
2835 &mut self,
2836 tail: DisplayPoint,
2837 head: DisplayPoint,
2838 goal_column: u32,
2839 display_map: &DisplaySnapshot,
2840 window: &mut Window,
2841 cx: &mut Context<Self>,
2842 ) {
2843 let start_row = cmp::min(tail.row(), head.row());
2844 let end_row = cmp::max(tail.row(), head.row());
2845 let start_column = cmp::min(tail.column(), goal_column);
2846 let end_column = cmp::max(tail.column(), goal_column);
2847 let reversed = start_column < tail.column();
2848
2849 let selection_ranges = (start_row.0..=end_row.0)
2850 .map(DisplayRow)
2851 .filter_map(|row| {
2852 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2853 let start = display_map
2854 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2855 .to_point(display_map);
2856 let end = display_map
2857 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2858 .to_point(display_map);
2859 if reversed {
2860 Some(end..start)
2861 } else {
2862 Some(start..end)
2863 }
2864 } else {
2865 None
2866 }
2867 })
2868 .collect::<Vec<_>>();
2869
2870 self.change_selections(None, window, cx, |s| {
2871 s.select_ranges(selection_ranges);
2872 });
2873 cx.notify();
2874 }
2875
2876 pub fn has_pending_nonempty_selection(&self) -> bool {
2877 let pending_nonempty_selection = match self.selections.pending_anchor() {
2878 Some(Selection { start, end, .. }) => start != end,
2879 None => false,
2880 };
2881
2882 pending_nonempty_selection
2883 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2884 }
2885
2886 pub fn has_pending_selection(&self) -> bool {
2887 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2888 }
2889
2890 pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
2891 self.selection_mark_mode = false;
2892
2893 if self.clear_expanded_diff_hunks(cx) {
2894 cx.notify();
2895 return;
2896 }
2897 if self.dismiss_menus_and_popups(true, window, cx) {
2898 return;
2899 }
2900
2901 if self.mode == EditorMode::Full
2902 && self.change_selections(Some(Autoscroll::fit()), window, cx, |s| s.try_cancel())
2903 {
2904 return;
2905 }
2906
2907 cx.propagate();
2908 }
2909
2910 pub fn dismiss_menus_and_popups(
2911 &mut self,
2912 is_user_requested: bool,
2913 window: &mut Window,
2914 cx: &mut Context<Self>,
2915 ) -> bool {
2916 if self.take_rename(false, window, cx).is_some() {
2917 return true;
2918 }
2919
2920 if hide_hover(self, cx) {
2921 return true;
2922 }
2923
2924 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2925 return true;
2926 }
2927
2928 if self.hide_context_menu(window, cx).is_some() {
2929 return true;
2930 }
2931
2932 if self.mouse_context_menu.take().is_some() {
2933 return true;
2934 }
2935
2936 if is_user_requested && self.discard_inline_completion(true, cx) {
2937 return true;
2938 }
2939
2940 if self.snippet_stack.pop().is_some() {
2941 return true;
2942 }
2943
2944 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
2945 self.dismiss_diagnostics(cx);
2946 return true;
2947 }
2948
2949 false
2950 }
2951
2952 fn linked_editing_ranges_for(
2953 &self,
2954 selection: Range<text::Anchor>,
2955 cx: &App,
2956 ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
2957 if self.linked_edit_ranges.is_empty() {
2958 return None;
2959 }
2960 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2961 selection.end.buffer_id.and_then(|end_buffer_id| {
2962 if selection.start.buffer_id != Some(end_buffer_id) {
2963 return None;
2964 }
2965 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2966 let snapshot = buffer.read(cx).snapshot();
2967 self.linked_edit_ranges
2968 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2969 .map(|ranges| (ranges, snapshot, buffer))
2970 })?;
2971 use text::ToOffset as TO;
2972 // find offset from the start of current range to current cursor position
2973 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2974
2975 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2976 let start_difference = start_offset - start_byte_offset;
2977 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2978 let end_difference = end_offset - start_byte_offset;
2979 // Current range has associated linked ranges.
2980 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2981 for range in linked_ranges.iter() {
2982 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2983 let end_offset = start_offset + end_difference;
2984 let start_offset = start_offset + start_difference;
2985 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2986 continue;
2987 }
2988 if self.selections.disjoint_anchor_ranges().any(|s| {
2989 if s.start.buffer_id != selection.start.buffer_id
2990 || s.end.buffer_id != selection.end.buffer_id
2991 {
2992 return false;
2993 }
2994 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
2995 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
2996 }) {
2997 continue;
2998 }
2999 let start = buffer_snapshot.anchor_after(start_offset);
3000 let end = buffer_snapshot.anchor_after(end_offset);
3001 linked_edits
3002 .entry(buffer.clone())
3003 .or_default()
3004 .push(start..end);
3005 }
3006 Some(linked_edits)
3007 }
3008
3009 pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3010 let text: Arc<str> = text.into();
3011
3012 if self.read_only(cx) {
3013 return;
3014 }
3015
3016 let selections = self.selections.all_adjusted(cx);
3017 let mut bracket_inserted = false;
3018 let mut edits = Vec::new();
3019 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3020 let mut new_selections = Vec::with_capacity(selections.len());
3021 let mut new_autoclose_regions = Vec::new();
3022 let snapshot = self.buffer.read(cx).read(cx);
3023
3024 for (selection, autoclose_region) in
3025 self.selections_with_autoclose_regions(selections, &snapshot)
3026 {
3027 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
3028 // Determine if the inserted text matches the opening or closing
3029 // bracket of any of this language's bracket pairs.
3030 let mut bracket_pair = None;
3031 let mut is_bracket_pair_start = false;
3032 let mut is_bracket_pair_end = false;
3033 if !text.is_empty() {
3034 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
3035 // and they are removing the character that triggered IME popup.
3036 for (pair, enabled) in scope.brackets() {
3037 if !pair.close && !pair.surround {
3038 continue;
3039 }
3040
3041 if enabled && pair.start.ends_with(text.as_ref()) {
3042 let prefix_len = pair.start.len() - text.len();
3043 let preceding_text_matches_prefix = prefix_len == 0
3044 || (selection.start.column >= (prefix_len as u32)
3045 && snapshot.contains_str_at(
3046 Point::new(
3047 selection.start.row,
3048 selection.start.column - (prefix_len as u32),
3049 ),
3050 &pair.start[..prefix_len],
3051 ));
3052 if preceding_text_matches_prefix {
3053 bracket_pair = Some(pair.clone());
3054 is_bracket_pair_start = true;
3055 break;
3056 }
3057 }
3058 if pair.end.as_str() == text.as_ref() {
3059 bracket_pair = Some(pair.clone());
3060 is_bracket_pair_end = true;
3061 break;
3062 }
3063 }
3064 }
3065
3066 if let Some(bracket_pair) = bracket_pair {
3067 let snapshot_settings = snapshot.settings_at(selection.start, cx);
3068 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
3069 let auto_surround =
3070 self.use_auto_surround && snapshot_settings.use_auto_surround;
3071 if selection.is_empty() {
3072 if is_bracket_pair_start {
3073 // If the inserted text is a suffix of an opening bracket and the
3074 // selection is preceded by the rest of the opening bracket, then
3075 // insert the closing bracket.
3076 let following_text_allows_autoclose = snapshot
3077 .chars_at(selection.start)
3078 .next()
3079 .map_or(true, |c| scope.should_autoclose_before(c));
3080
3081 let is_closing_quote = if bracket_pair.end == bracket_pair.start
3082 && bracket_pair.start.len() == 1
3083 {
3084 let target = bracket_pair.start.chars().next().unwrap();
3085 let current_line_count = snapshot
3086 .reversed_chars_at(selection.start)
3087 .take_while(|&c| c != '\n')
3088 .filter(|&c| c == target)
3089 .count();
3090 current_line_count % 2 == 1
3091 } else {
3092 false
3093 };
3094
3095 if autoclose
3096 && bracket_pair.close
3097 && following_text_allows_autoclose
3098 && !is_closing_quote
3099 {
3100 let anchor = snapshot.anchor_before(selection.end);
3101 new_selections.push((selection.map(|_| anchor), text.len()));
3102 new_autoclose_regions.push((
3103 anchor,
3104 text.len(),
3105 selection.id,
3106 bracket_pair.clone(),
3107 ));
3108 edits.push((
3109 selection.range(),
3110 format!("{}{}", text, bracket_pair.end).into(),
3111 ));
3112 bracket_inserted = true;
3113 continue;
3114 }
3115 }
3116
3117 if let Some(region) = autoclose_region {
3118 // If the selection is followed by an auto-inserted closing bracket,
3119 // then don't insert that closing bracket again; just move the selection
3120 // past the closing bracket.
3121 let should_skip = selection.end == region.range.end.to_point(&snapshot)
3122 && text.as_ref() == region.pair.end.as_str();
3123 if should_skip {
3124 let anchor = snapshot.anchor_after(selection.end);
3125 new_selections
3126 .push((selection.map(|_| anchor), region.pair.end.len()));
3127 continue;
3128 }
3129 }
3130
3131 let always_treat_brackets_as_autoclosed = snapshot
3132 .settings_at(selection.start, cx)
3133 .always_treat_brackets_as_autoclosed;
3134 if always_treat_brackets_as_autoclosed
3135 && is_bracket_pair_end
3136 && snapshot.contains_str_at(selection.end, text.as_ref())
3137 {
3138 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
3139 // and the inserted text is a closing bracket and the selection is followed
3140 // by the closing bracket then move the selection past the closing bracket.
3141 let anchor = snapshot.anchor_after(selection.end);
3142 new_selections.push((selection.map(|_| anchor), text.len()));
3143 continue;
3144 }
3145 }
3146 // If an opening bracket is 1 character long and is typed while
3147 // text is selected, then surround that text with the bracket pair.
3148 else if auto_surround
3149 && bracket_pair.surround
3150 && is_bracket_pair_start
3151 && bracket_pair.start.chars().count() == 1
3152 {
3153 edits.push((selection.start..selection.start, text.clone()));
3154 edits.push((
3155 selection.end..selection.end,
3156 bracket_pair.end.as_str().into(),
3157 ));
3158 bracket_inserted = true;
3159 new_selections.push((
3160 Selection {
3161 id: selection.id,
3162 start: snapshot.anchor_after(selection.start),
3163 end: snapshot.anchor_before(selection.end),
3164 reversed: selection.reversed,
3165 goal: selection.goal,
3166 },
3167 0,
3168 ));
3169 continue;
3170 }
3171 }
3172 }
3173
3174 if self.auto_replace_emoji_shortcode
3175 && selection.is_empty()
3176 && text.as_ref().ends_with(':')
3177 {
3178 if let Some(possible_emoji_short_code) =
3179 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
3180 {
3181 if !possible_emoji_short_code.is_empty() {
3182 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
3183 let emoji_shortcode_start = Point::new(
3184 selection.start.row,
3185 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
3186 );
3187
3188 // Remove shortcode from buffer
3189 edits.push((
3190 emoji_shortcode_start..selection.start,
3191 "".to_string().into(),
3192 ));
3193 new_selections.push((
3194 Selection {
3195 id: selection.id,
3196 start: snapshot.anchor_after(emoji_shortcode_start),
3197 end: snapshot.anchor_before(selection.start),
3198 reversed: selection.reversed,
3199 goal: selection.goal,
3200 },
3201 0,
3202 ));
3203
3204 // Insert emoji
3205 let selection_start_anchor = snapshot.anchor_after(selection.start);
3206 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3207 edits.push((selection.start..selection.end, emoji.to_string().into()));
3208
3209 continue;
3210 }
3211 }
3212 }
3213 }
3214
3215 // If not handling any auto-close operation, then just replace the selected
3216 // text with the given input and move the selection to the end of the
3217 // newly inserted text.
3218 let anchor = snapshot.anchor_after(selection.end);
3219 if !self.linked_edit_ranges.is_empty() {
3220 let start_anchor = snapshot.anchor_before(selection.start);
3221
3222 let is_word_char = text.chars().next().map_or(true, |char| {
3223 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
3224 classifier.is_word(char)
3225 });
3226
3227 if is_word_char {
3228 if let Some(ranges) = self
3229 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3230 {
3231 for (buffer, edits) in ranges {
3232 linked_edits
3233 .entry(buffer.clone())
3234 .or_default()
3235 .extend(edits.into_iter().map(|range| (range, text.clone())));
3236 }
3237 }
3238 }
3239 }
3240
3241 new_selections.push((selection.map(|_| anchor), 0));
3242 edits.push((selection.start..selection.end, text.clone()));
3243 }
3244
3245 drop(snapshot);
3246
3247 self.transact(window, cx, |this, window, cx| {
3248 this.buffer.update(cx, |buffer, cx| {
3249 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3250 });
3251 for (buffer, edits) in linked_edits {
3252 buffer.update(cx, |buffer, cx| {
3253 let snapshot = buffer.snapshot();
3254 let edits = edits
3255 .into_iter()
3256 .map(|(range, text)| {
3257 use text::ToPoint as TP;
3258 let end_point = TP::to_point(&range.end, &snapshot);
3259 let start_point = TP::to_point(&range.start, &snapshot);
3260 (start_point..end_point, text)
3261 })
3262 .sorted_by_key(|(range, _)| range.start)
3263 .collect::<Vec<_>>();
3264 buffer.edit(edits, None, cx);
3265 })
3266 }
3267 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3268 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3269 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3270 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
3271 .zip(new_selection_deltas)
3272 .map(|(selection, delta)| Selection {
3273 id: selection.id,
3274 start: selection.start + delta,
3275 end: selection.end + delta,
3276 reversed: selection.reversed,
3277 goal: SelectionGoal::None,
3278 })
3279 .collect::<Vec<_>>();
3280
3281 let mut i = 0;
3282 for (position, delta, selection_id, pair) in new_autoclose_regions {
3283 let position = position.to_offset(&map.buffer_snapshot) + delta;
3284 let start = map.buffer_snapshot.anchor_before(position);
3285 let end = map.buffer_snapshot.anchor_after(position);
3286 while let Some(existing_state) = this.autoclose_regions.get(i) {
3287 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
3288 Ordering::Less => i += 1,
3289 Ordering::Greater => break,
3290 Ordering::Equal => {
3291 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
3292 Ordering::Less => i += 1,
3293 Ordering::Equal => break,
3294 Ordering::Greater => break,
3295 }
3296 }
3297 }
3298 }
3299 this.autoclose_regions.insert(
3300 i,
3301 AutocloseRegion {
3302 selection_id,
3303 range: start..end,
3304 pair,
3305 },
3306 );
3307 }
3308
3309 let had_active_inline_completion = this.has_active_inline_completion();
3310 this.change_selections_inner(Some(Autoscroll::fit()), false, window, cx, |s| {
3311 s.select(new_selections)
3312 });
3313
3314 if !bracket_inserted {
3315 if let Some(on_type_format_task) =
3316 this.trigger_on_type_formatting(text.to_string(), window, cx)
3317 {
3318 on_type_format_task.detach_and_log_err(cx);
3319 }
3320 }
3321
3322 let editor_settings = EditorSettings::get_global(cx);
3323 if bracket_inserted
3324 && (editor_settings.auto_signature_help
3325 || editor_settings.show_signature_help_after_edits)
3326 {
3327 this.show_signature_help(&ShowSignatureHelp, window, cx);
3328 }
3329
3330 let trigger_in_words =
3331 this.show_edit_predictions_in_menu() || !had_active_inline_completion;
3332 this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
3333 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
3334 this.refresh_inline_completion(true, false, window, cx);
3335 });
3336 }
3337
3338 fn find_possible_emoji_shortcode_at_position(
3339 snapshot: &MultiBufferSnapshot,
3340 position: Point,
3341 ) -> Option<String> {
3342 let mut chars = Vec::new();
3343 let mut found_colon = false;
3344 for char in snapshot.reversed_chars_at(position).take(100) {
3345 // Found a possible emoji shortcode in the middle of the buffer
3346 if found_colon {
3347 if char.is_whitespace() {
3348 chars.reverse();
3349 return Some(chars.iter().collect());
3350 }
3351 // If the previous character is not a whitespace, we are in the middle of a word
3352 // and we only want to complete the shortcode if the word is made up of other emojis
3353 let mut containing_word = String::new();
3354 for ch in snapshot
3355 .reversed_chars_at(position)
3356 .skip(chars.len() + 1)
3357 .take(100)
3358 {
3359 if ch.is_whitespace() {
3360 break;
3361 }
3362 containing_word.push(ch);
3363 }
3364 let containing_word = containing_word.chars().rev().collect::<String>();
3365 if util::word_consists_of_emojis(containing_word.as_str()) {
3366 chars.reverse();
3367 return Some(chars.iter().collect());
3368 }
3369 }
3370
3371 if char.is_whitespace() || !char.is_ascii() {
3372 return None;
3373 }
3374 if char == ':' {
3375 found_colon = true;
3376 } else {
3377 chars.push(char);
3378 }
3379 }
3380 // Found a possible emoji shortcode at the beginning of the buffer
3381 chars.reverse();
3382 Some(chars.iter().collect())
3383 }
3384
3385 pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
3386 self.transact(window, cx, |this, window, cx| {
3387 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3388 let selections = this.selections.all::<usize>(cx);
3389 let multi_buffer = this.buffer.read(cx);
3390 let buffer = multi_buffer.snapshot(cx);
3391 selections
3392 .iter()
3393 .map(|selection| {
3394 let start_point = selection.start.to_point(&buffer);
3395 let mut indent =
3396 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3397 indent.len = cmp::min(indent.len, start_point.column);
3398 let start = selection.start;
3399 let end = selection.end;
3400 let selection_is_empty = start == end;
3401 let language_scope = buffer.language_scope_at(start);
3402 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3403 &language_scope
3404 {
3405 let leading_whitespace_len = buffer
3406 .reversed_chars_at(start)
3407 .take_while(|c| c.is_whitespace() && *c != '\n')
3408 .map(|c| c.len_utf8())
3409 .sum::<usize>();
3410
3411 let trailing_whitespace_len = buffer
3412 .chars_at(end)
3413 .take_while(|c| c.is_whitespace() && *c != '\n')
3414 .map(|c| c.len_utf8())
3415 .sum::<usize>();
3416
3417 let insert_extra_newline =
3418 language.brackets().any(|(pair, enabled)| {
3419 let pair_start = pair.start.trim_end();
3420 let pair_end = pair.end.trim_start();
3421
3422 enabled
3423 && pair.newline
3424 && buffer.contains_str_at(
3425 end + trailing_whitespace_len,
3426 pair_end,
3427 )
3428 && buffer.contains_str_at(
3429 (start - leading_whitespace_len)
3430 .saturating_sub(pair_start.len()),
3431 pair_start,
3432 )
3433 });
3434
3435 // Comment extension on newline is allowed only for cursor selections
3436 let comment_delimiter = maybe!({
3437 if !selection_is_empty {
3438 return None;
3439 }
3440
3441 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
3442 return None;
3443 }
3444
3445 let delimiters = language.line_comment_prefixes();
3446 let max_len_of_delimiter =
3447 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3448 let (snapshot, range) =
3449 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3450
3451 let mut index_of_first_non_whitespace = 0;
3452 let comment_candidate = snapshot
3453 .chars_for_range(range)
3454 .skip_while(|c| {
3455 let should_skip = c.is_whitespace();
3456 if should_skip {
3457 index_of_first_non_whitespace += 1;
3458 }
3459 should_skip
3460 })
3461 .take(max_len_of_delimiter)
3462 .collect::<String>();
3463 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3464 comment_candidate.starts_with(comment_prefix.as_ref())
3465 })?;
3466 let cursor_is_placed_after_comment_marker =
3467 index_of_first_non_whitespace + comment_prefix.len()
3468 <= start_point.column as usize;
3469 if cursor_is_placed_after_comment_marker {
3470 Some(comment_prefix.clone())
3471 } else {
3472 None
3473 }
3474 });
3475 (comment_delimiter, insert_extra_newline)
3476 } else {
3477 (None, false)
3478 };
3479
3480 let capacity_for_delimiter = comment_delimiter
3481 .as_deref()
3482 .map(str::len)
3483 .unwrap_or_default();
3484 let mut new_text =
3485 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3486 new_text.push('\n');
3487 new_text.extend(indent.chars());
3488 if let Some(delimiter) = &comment_delimiter {
3489 new_text.push_str(delimiter);
3490 }
3491 if insert_extra_newline {
3492 new_text = new_text.repeat(2);
3493 }
3494
3495 let anchor = buffer.anchor_after(end);
3496 let new_selection = selection.map(|_| anchor);
3497 (
3498 (start..end, new_text),
3499 (insert_extra_newline, new_selection),
3500 )
3501 })
3502 .unzip()
3503 };
3504
3505 this.edit_with_autoindent(edits, cx);
3506 let buffer = this.buffer.read(cx).snapshot(cx);
3507 let new_selections = selection_fixup_info
3508 .into_iter()
3509 .map(|(extra_newline_inserted, new_selection)| {
3510 let mut cursor = new_selection.end.to_point(&buffer);
3511 if extra_newline_inserted {
3512 cursor.row -= 1;
3513 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3514 }
3515 new_selection.map(|_| cursor)
3516 })
3517 .collect();
3518
3519 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3520 s.select(new_selections)
3521 });
3522 this.refresh_inline_completion(true, false, window, cx);
3523 });
3524 }
3525
3526 pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
3527 let buffer = self.buffer.read(cx);
3528 let snapshot = buffer.snapshot(cx);
3529
3530 let mut edits = Vec::new();
3531 let mut rows = Vec::new();
3532
3533 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3534 let cursor = selection.head();
3535 let row = cursor.row;
3536
3537 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3538
3539 let newline = "\n".to_string();
3540 edits.push((start_of_line..start_of_line, newline));
3541
3542 rows.push(row + rows_inserted as u32);
3543 }
3544
3545 self.transact(window, cx, |editor, window, cx| {
3546 editor.edit(edits, cx);
3547
3548 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3549 let mut index = 0;
3550 s.move_cursors_with(|map, _, _| {
3551 let row = rows[index];
3552 index += 1;
3553
3554 let point = Point::new(row, 0);
3555 let boundary = map.next_line_boundary(point).1;
3556 let clipped = map.clip_point(boundary, Bias::Left);
3557
3558 (clipped, SelectionGoal::None)
3559 });
3560 });
3561
3562 let mut indent_edits = Vec::new();
3563 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3564 for row in rows {
3565 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3566 for (row, indent) in indents {
3567 if indent.len == 0 {
3568 continue;
3569 }
3570
3571 let text = match indent.kind {
3572 IndentKind::Space => " ".repeat(indent.len as usize),
3573 IndentKind::Tab => "\t".repeat(indent.len as usize),
3574 };
3575 let point = Point::new(row.0, 0);
3576 indent_edits.push((point..point, text));
3577 }
3578 }
3579 editor.edit(indent_edits, cx);
3580 });
3581 }
3582
3583 pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
3584 let buffer = self.buffer.read(cx);
3585 let snapshot = buffer.snapshot(cx);
3586
3587 let mut edits = Vec::new();
3588 let mut rows = Vec::new();
3589 let mut rows_inserted = 0;
3590
3591 for selection in self.selections.all_adjusted(cx) {
3592 let cursor = selection.head();
3593 let row = cursor.row;
3594
3595 let point = Point::new(row + 1, 0);
3596 let start_of_line = snapshot.clip_point(point, Bias::Left);
3597
3598 let newline = "\n".to_string();
3599 edits.push((start_of_line..start_of_line, newline));
3600
3601 rows_inserted += 1;
3602 rows.push(row + rows_inserted);
3603 }
3604
3605 self.transact(window, cx, |editor, window, cx| {
3606 editor.edit(edits, cx);
3607
3608 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3609 let mut index = 0;
3610 s.move_cursors_with(|map, _, _| {
3611 let row = rows[index];
3612 index += 1;
3613
3614 let point = Point::new(row, 0);
3615 let boundary = map.next_line_boundary(point).1;
3616 let clipped = map.clip_point(boundary, Bias::Left);
3617
3618 (clipped, SelectionGoal::None)
3619 });
3620 });
3621
3622 let mut indent_edits = Vec::new();
3623 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3624 for row in rows {
3625 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3626 for (row, indent) in indents {
3627 if indent.len == 0 {
3628 continue;
3629 }
3630
3631 let text = match indent.kind {
3632 IndentKind::Space => " ".repeat(indent.len as usize),
3633 IndentKind::Tab => "\t".repeat(indent.len as usize),
3634 };
3635 let point = Point::new(row.0, 0);
3636 indent_edits.push((point..point, text));
3637 }
3638 }
3639 editor.edit(indent_edits, cx);
3640 });
3641 }
3642
3643 pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3644 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3645 original_indent_columns: Vec::new(),
3646 });
3647 self.insert_with_autoindent_mode(text, autoindent, window, cx);
3648 }
3649
3650 fn insert_with_autoindent_mode(
3651 &mut self,
3652 text: &str,
3653 autoindent_mode: Option<AutoindentMode>,
3654 window: &mut Window,
3655 cx: &mut Context<Self>,
3656 ) {
3657 if self.read_only(cx) {
3658 return;
3659 }
3660
3661 let text: Arc<str> = text.into();
3662 self.transact(window, cx, |this, window, cx| {
3663 let old_selections = this.selections.all_adjusted(cx);
3664 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3665 let anchors = {
3666 let snapshot = buffer.read(cx);
3667 old_selections
3668 .iter()
3669 .map(|s| {
3670 let anchor = snapshot.anchor_after(s.head());
3671 s.map(|_| anchor)
3672 })
3673 .collect::<Vec<_>>()
3674 };
3675 buffer.edit(
3676 old_selections
3677 .iter()
3678 .map(|s| (s.start..s.end, text.clone())),
3679 autoindent_mode,
3680 cx,
3681 );
3682 anchors
3683 });
3684
3685 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3686 s.select_anchors(selection_anchors);
3687 });
3688
3689 cx.notify();
3690 });
3691 }
3692
3693 fn trigger_completion_on_input(
3694 &mut self,
3695 text: &str,
3696 trigger_in_words: bool,
3697 window: &mut Window,
3698 cx: &mut Context<Self>,
3699 ) {
3700 if self.is_completion_trigger(text, trigger_in_words, cx) {
3701 self.show_completions(
3702 &ShowCompletions {
3703 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3704 },
3705 window,
3706 cx,
3707 );
3708 } else {
3709 self.hide_context_menu(window, cx);
3710 }
3711 }
3712
3713 fn is_completion_trigger(
3714 &self,
3715 text: &str,
3716 trigger_in_words: bool,
3717 cx: &mut Context<Self>,
3718 ) -> bool {
3719 let position = self.selections.newest_anchor().head();
3720 let multibuffer = self.buffer.read(cx);
3721 let Some(buffer) = position
3722 .buffer_id
3723 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3724 else {
3725 return false;
3726 };
3727
3728 if let Some(completion_provider) = &self.completion_provider {
3729 completion_provider.is_completion_trigger(
3730 &buffer,
3731 position.text_anchor,
3732 text,
3733 trigger_in_words,
3734 cx,
3735 )
3736 } else {
3737 false
3738 }
3739 }
3740
3741 /// If any empty selections is touching the start of its innermost containing autoclose
3742 /// region, expand it to select the brackets.
3743 fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3744 let selections = self.selections.all::<usize>(cx);
3745 let buffer = self.buffer.read(cx).read(cx);
3746 let new_selections = self
3747 .selections_with_autoclose_regions(selections, &buffer)
3748 .map(|(mut selection, region)| {
3749 if !selection.is_empty() {
3750 return selection;
3751 }
3752
3753 if let Some(region) = region {
3754 let mut range = region.range.to_offset(&buffer);
3755 if selection.start == range.start && range.start >= region.pair.start.len() {
3756 range.start -= region.pair.start.len();
3757 if buffer.contains_str_at(range.start, ®ion.pair.start)
3758 && buffer.contains_str_at(range.end, ®ion.pair.end)
3759 {
3760 range.end += region.pair.end.len();
3761 selection.start = range.start;
3762 selection.end = range.end;
3763
3764 return selection;
3765 }
3766 }
3767 }
3768
3769 let always_treat_brackets_as_autoclosed = buffer
3770 .settings_at(selection.start, cx)
3771 .always_treat_brackets_as_autoclosed;
3772
3773 if !always_treat_brackets_as_autoclosed {
3774 return selection;
3775 }
3776
3777 if let Some(scope) = buffer.language_scope_at(selection.start) {
3778 for (pair, enabled) in scope.brackets() {
3779 if !enabled || !pair.close {
3780 continue;
3781 }
3782
3783 if buffer.contains_str_at(selection.start, &pair.end) {
3784 let pair_start_len = pair.start.len();
3785 if buffer.contains_str_at(
3786 selection.start.saturating_sub(pair_start_len),
3787 &pair.start,
3788 ) {
3789 selection.start -= pair_start_len;
3790 selection.end += pair.end.len();
3791
3792 return selection;
3793 }
3794 }
3795 }
3796 }
3797
3798 selection
3799 })
3800 .collect();
3801
3802 drop(buffer);
3803 self.change_selections(None, window, cx, |selections| {
3804 selections.select(new_selections)
3805 });
3806 }
3807
3808 /// Iterate the given selections, and for each one, find the smallest surrounding
3809 /// autoclose region. This uses the ordering of the selections and the autoclose
3810 /// regions to avoid repeated comparisons.
3811 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3812 &'a self,
3813 selections: impl IntoIterator<Item = Selection<D>>,
3814 buffer: &'a MultiBufferSnapshot,
3815 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3816 let mut i = 0;
3817 let mut regions = self.autoclose_regions.as_slice();
3818 selections.into_iter().map(move |selection| {
3819 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3820
3821 let mut enclosing = None;
3822 while let Some(pair_state) = regions.get(i) {
3823 if pair_state.range.end.to_offset(buffer) < range.start {
3824 regions = ®ions[i + 1..];
3825 i = 0;
3826 } else if pair_state.range.start.to_offset(buffer) > range.end {
3827 break;
3828 } else {
3829 if pair_state.selection_id == selection.id {
3830 enclosing = Some(pair_state);
3831 }
3832 i += 1;
3833 }
3834 }
3835
3836 (selection, enclosing)
3837 })
3838 }
3839
3840 /// Remove any autoclose regions that no longer contain their selection.
3841 fn invalidate_autoclose_regions(
3842 &mut self,
3843 mut selections: &[Selection<Anchor>],
3844 buffer: &MultiBufferSnapshot,
3845 ) {
3846 self.autoclose_regions.retain(|state| {
3847 let mut i = 0;
3848 while let Some(selection) = selections.get(i) {
3849 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3850 selections = &selections[1..];
3851 continue;
3852 }
3853 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3854 break;
3855 }
3856 if selection.id == state.selection_id {
3857 return true;
3858 } else {
3859 i += 1;
3860 }
3861 }
3862 false
3863 });
3864 }
3865
3866 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3867 let offset = position.to_offset(buffer);
3868 let (word_range, kind) = buffer.surrounding_word(offset, true);
3869 if offset > word_range.start && kind == Some(CharKind::Word) {
3870 Some(
3871 buffer
3872 .text_for_range(word_range.start..offset)
3873 .collect::<String>(),
3874 )
3875 } else {
3876 None
3877 }
3878 }
3879
3880 pub fn toggle_inlay_hints(
3881 &mut self,
3882 _: &ToggleInlayHints,
3883 _: &mut Window,
3884 cx: &mut Context<Self>,
3885 ) {
3886 self.refresh_inlay_hints(
3887 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
3888 cx,
3889 );
3890 }
3891
3892 pub fn inlay_hints_enabled(&self) -> bool {
3893 self.inlay_hint_cache.enabled
3894 }
3895
3896 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut Context<Self>) {
3897 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
3898 return;
3899 }
3900
3901 let reason_description = reason.description();
3902 let ignore_debounce = matches!(
3903 reason,
3904 InlayHintRefreshReason::SettingsChange(_)
3905 | InlayHintRefreshReason::Toggle(_)
3906 | InlayHintRefreshReason::ExcerptsRemoved(_)
3907 );
3908 let (invalidate_cache, required_languages) = match reason {
3909 InlayHintRefreshReason::Toggle(enabled) => {
3910 self.inlay_hint_cache.enabled = enabled;
3911 if enabled {
3912 (InvalidationStrategy::RefreshRequested, None)
3913 } else {
3914 self.inlay_hint_cache.clear();
3915 self.splice_inlays(
3916 &self
3917 .visible_inlay_hints(cx)
3918 .iter()
3919 .map(|inlay| inlay.id)
3920 .collect::<Vec<InlayId>>(),
3921 Vec::new(),
3922 cx,
3923 );
3924 return;
3925 }
3926 }
3927 InlayHintRefreshReason::SettingsChange(new_settings) => {
3928 match self.inlay_hint_cache.update_settings(
3929 &self.buffer,
3930 new_settings,
3931 self.visible_inlay_hints(cx),
3932 cx,
3933 ) {
3934 ControlFlow::Break(Some(InlaySplice {
3935 to_remove,
3936 to_insert,
3937 })) => {
3938 self.splice_inlays(&to_remove, to_insert, cx);
3939 return;
3940 }
3941 ControlFlow::Break(None) => return,
3942 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3943 }
3944 }
3945 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3946 if let Some(InlaySplice {
3947 to_remove,
3948 to_insert,
3949 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3950 {
3951 self.splice_inlays(&to_remove, to_insert, cx);
3952 }
3953 return;
3954 }
3955 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3956 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3957 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3958 }
3959 InlayHintRefreshReason::RefreshRequested => {
3960 (InvalidationStrategy::RefreshRequested, None)
3961 }
3962 };
3963
3964 if let Some(InlaySplice {
3965 to_remove,
3966 to_insert,
3967 }) = self.inlay_hint_cache.spawn_hint_refresh(
3968 reason_description,
3969 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3970 invalidate_cache,
3971 ignore_debounce,
3972 cx,
3973 ) {
3974 self.splice_inlays(&to_remove, to_insert, cx);
3975 }
3976 }
3977
3978 fn visible_inlay_hints(&self, cx: &Context<Editor>) -> Vec<Inlay> {
3979 self.display_map
3980 .read(cx)
3981 .current_inlays()
3982 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3983 .cloned()
3984 .collect()
3985 }
3986
3987 pub fn excerpts_for_inlay_hints_query(
3988 &self,
3989 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3990 cx: &mut Context<Editor>,
3991 ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
3992 let Some(project) = self.project.as_ref() else {
3993 return HashMap::default();
3994 };
3995 let project = project.read(cx);
3996 let multi_buffer = self.buffer().read(cx);
3997 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3998 let multi_buffer_visible_start = self
3999 .scroll_manager
4000 .anchor()
4001 .anchor
4002 .to_point(&multi_buffer_snapshot);
4003 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
4004 multi_buffer_visible_start
4005 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
4006 Bias::Left,
4007 );
4008 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
4009 multi_buffer_snapshot
4010 .range_to_buffer_ranges(multi_buffer_visible_range)
4011 .into_iter()
4012 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
4013 .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
4014 let buffer_file = project::File::from_dyn(buffer.file())?;
4015 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
4016 let worktree_entry = buffer_worktree
4017 .read(cx)
4018 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
4019 if worktree_entry.is_ignored {
4020 return None;
4021 }
4022
4023 let language = buffer.language()?;
4024 if let Some(restrict_to_languages) = restrict_to_languages {
4025 if !restrict_to_languages.contains(language) {
4026 return None;
4027 }
4028 }
4029 Some((
4030 excerpt_id,
4031 (
4032 multi_buffer.buffer(buffer.remote_id()).unwrap(),
4033 buffer.version().clone(),
4034 excerpt_visible_range,
4035 ),
4036 ))
4037 })
4038 .collect()
4039 }
4040
4041 pub fn text_layout_details(&self, window: &mut Window) -> TextLayoutDetails {
4042 TextLayoutDetails {
4043 text_system: window.text_system().clone(),
4044 editor_style: self.style.clone().unwrap(),
4045 rem_size: window.rem_size(),
4046 scroll_anchor: self.scroll_manager.anchor(),
4047 visible_rows: self.visible_line_count(),
4048 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
4049 }
4050 }
4051
4052 pub fn splice_inlays(
4053 &self,
4054 to_remove: &[InlayId],
4055 to_insert: Vec<Inlay>,
4056 cx: &mut Context<Self>,
4057 ) {
4058 self.display_map.update(cx, |display_map, cx| {
4059 display_map.splice_inlays(to_remove, to_insert, cx)
4060 });
4061 cx.notify();
4062 }
4063
4064 fn trigger_on_type_formatting(
4065 &self,
4066 input: String,
4067 window: &mut Window,
4068 cx: &mut Context<Self>,
4069 ) -> Option<Task<Result<()>>> {
4070 if input.len() != 1 {
4071 return None;
4072 }
4073
4074 let project = self.project.as_ref()?;
4075 let position = self.selections.newest_anchor().head();
4076 let (buffer, buffer_position) = self
4077 .buffer
4078 .read(cx)
4079 .text_anchor_for_position(position, cx)?;
4080
4081 let settings = language_settings::language_settings(
4082 buffer
4083 .read(cx)
4084 .language_at(buffer_position)
4085 .map(|l| l.name()),
4086 buffer.read(cx).file(),
4087 cx,
4088 );
4089 if !settings.use_on_type_format {
4090 return None;
4091 }
4092
4093 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
4094 // hence we do LSP request & edit on host side only — add formats to host's history.
4095 let push_to_lsp_host_history = true;
4096 // If this is not the host, append its history with new edits.
4097 let push_to_client_history = project.read(cx).is_via_collab();
4098
4099 let on_type_formatting = project.update(cx, |project, cx| {
4100 project.on_type_format(
4101 buffer.clone(),
4102 buffer_position,
4103 input,
4104 push_to_lsp_host_history,
4105 cx,
4106 )
4107 });
4108 Some(cx.spawn_in(window, |editor, mut cx| async move {
4109 if let Some(transaction) = on_type_formatting.await? {
4110 if push_to_client_history {
4111 buffer
4112 .update(&mut cx, |buffer, _| {
4113 buffer.push_transaction(transaction, Instant::now());
4114 })
4115 .ok();
4116 }
4117 editor.update(&mut cx, |editor, cx| {
4118 editor.refresh_document_highlights(cx);
4119 })?;
4120 }
4121 Ok(())
4122 }))
4123 }
4124
4125 pub fn show_completions(
4126 &mut self,
4127 options: &ShowCompletions,
4128 window: &mut Window,
4129 cx: &mut Context<Self>,
4130 ) {
4131 if self.pending_rename.is_some() {
4132 return;
4133 }
4134
4135 let Some(provider) = self.completion_provider.as_ref() else {
4136 return;
4137 };
4138
4139 if !self.snippet_stack.is_empty() && self.context_menu.borrow().as_ref().is_some() {
4140 return;
4141 }
4142
4143 let position = self.selections.newest_anchor().head();
4144 if position.diff_base_anchor.is_some() {
4145 return;
4146 }
4147 let (buffer, buffer_position) =
4148 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
4149 output
4150 } else {
4151 return;
4152 };
4153 let show_completion_documentation = buffer
4154 .read(cx)
4155 .snapshot()
4156 .settings_at(buffer_position, cx)
4157 .show_completion_documentation;
4158
4159 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
4160
4161 let trigger_kind = match &options.trigger {
4162 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
4163 CompletionTriggerKind::TRIGGER_CHARACTER
4164 }
4165 _ => CompletionTriggerKind::INVOKED,
4166 };
4167 let completion_context = CompletionContext {
4168 trigger_character: options.trigger.as_ref().and_then(|trigger| {
4169 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
4170 Some(String::from(trigger))
4171 } else {
4172 None
4173 }
4174 }),
4175 trigger_kind,
4176 };
4177 let completions =
4178 provider.completions(&buffer, buffer_position, completion_context, window, cx);
4179 let sort_completions = provider.sort_completions();
4180
4181 let id = post_inc(&mut self.next_completion_id);
4182 let task = cx.spawn_in(window, |editor, mut cx| {
4183 async move {
4184 editor.update(&mut cx, |this, _| {
4185 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
4186 })?;
4187 let completions = completions.await.log_err();
4188 let menu = if let Some(completions) = completions {
4189 let mut menu = CompletionsMenu::new(
4190 id,
4191 sort_completions,
4192 show_completion_documentation,
4193 position,
4194 buffer.clone(),
4195 completions.into(),
4196 );
4197
4198 menu.filter(query.as_deref(), cx.background_executor().clone())
4199 .await;
4200
4201 menu.visible().then_some(menu)
4202 } else {
4203 None
4204 };
4205
4206 editor.update_in(&mut cx, |editor, window, cx| {
4207 match editor.context_menu.borrow().as_ref() {
4208 None => {}
4209 Some(CodeContextMenu::Completions(prev_menu)) => {
4210 if prev_menu.id > id {
4211 return;
4212 }
4213 }
4214 _ => return,
4215 }
4216
4217 if editor.focus_handle.is_focused(window) && menu.is_some() {
4218 let mut menu = menu.unwrap();
4219 menu.resolve_visible_completions(editor.completion_provider.as_deref(), cx);
4220
4221 *editor.context_menu.borrow_mut() =
4222 Some(CodeContextMenu::Completions(menu));
4223
4224 if editor.show_edit_predictions_in_menu() {
4225 editor.update_visible_inline_completion(window, cx);
4226 } else {
4227 editor.discard_inline_completion(false, cx);
4228 }
4229
4230 cx.notify();
4231 } else if editor.completion_tasks.len() <= 1 {
4232 // If there are no more completion tasks and the last menu was
4233 // empty, we should hide it.
4234 let was_hidden = editor.hide_context_menu(window, cx).is_none();
4235 // If it was already hidden and we don't show inline
4236 // completions in the menu, we should also show the
4237 // inline-completion when available.
4238 if was_hidden && editor.show_edit_predictions_in_menu() {
4239 editor.update_visible_inline_completion(window, cx);
4240 }
4241 }
4242 })?;
4243
4244 Ok::<_, anyhow::Error>(())
4245 }
4246 .log_err()
4247 });
4248
4249 self.completion_tasks.push((id, task));
4250 }
4251
4252 pub fn confirm_completion(
4253 &mut self,
4254 action: &ConfirmCompletion,
4255 window: &mut Window,
4256 cx: &mut Context<Self>,
4257 ) -> Option<Task<Result<()>>> {
4258 self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
4259 }
4260
4261 pub fn compose_completion(
4262 &mut self,
4263 action: &ComposeCompletion,
4264 window: &mut Window,
4265 cx: &mut Context<Self>,
4266 ) -> Option<Task<Result<()>>> {
4267 self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
4268 }
4269
4270 fn do_completion(
4271 &mut self,
4272 item_ix: Option<usize>,
4273 intent: CompletionIntent,
4274 window: &mut Window,
4275 cx: &mut Context<Editor>,
4276 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
4277 use language::ToOffset as _;
4278
4279 let completions_menu =
4280 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(window, cx)? {
4281 menu
4282 } else {
4283 return None;
4284 };
4285
4286 let entries = completions_menu.entries.borrow();
4287 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
4288 if self.show_edit_predictions_in_menu() {
4289 self.discard_inline_completion(true, cx);
4290 }
4291 let candidate_id = mat.candidate_id;
4292 drop(entries);
4293
4294 let buffer_handle = completions_menu.buffer;
4295 let completion = completions_menu
4296 .completions
4297 .borrow()
4298 .get(candidate_id)?
4299 .clone();
4300 cx.stop_propagation();
4301
4302 let snippet;
4303 let text;
4304
4305 if completion.is_snippet() {
4306 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4307 text = snippet.as_ref().unwrap().text.clone();
4308 } else {
4309 snippet = None;
4310 text = completion.new_text.clone();
4311 };
4312 let selections = self.selections.all::<usize>(cx);
4313 let buffer = buffer_handle.read(cx);
4314 let old_range = completion.old_range.to_offset(buffer);
4315 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4316
4317 let newest_selection = self.selections.newest_anchor();
4318 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4319 return None;
4320 }
4321
4322 let lookbehind = newest_selection
4323 .start
4324 .text_anchor
4325 .to_offset(buffer)
4326 .saturating_sub(old_range.start);
4327 let lookahead = old_range
4328 .end
4329 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4330 let mut common_prefix_len = old_text
4331 .bytes()
4332 .zip(text.bytes())
4333 .take_while(|(a, b)| a == b)
4334 .count();
4335
4336 let snapshot = self.buffer.read(cx).snapshot(cx);
4337 let mut range_to_replace: Option<Range<isize>> = None;
4338 let mut ranges = Vec::new();
4339 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4340 for selection in &selections {
4341 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4342 let start = selection.start.saturating_sub(lookbehind);
4343 let end = selection.end + lookahead;
4344 if selection.id == newest_selection.id {
4345 range_to_replace = Some(
4346 ((start + common_prefix_len) as isize - selection.start as isize)
4347 ..(end as isize - selection.start as isize),
4348 );
4349 }
4350 ranges.push(start + common_prefix_len..end);
4351 } else {
4352 common_prefix_len = 0;
4353 ranges.clear();
4354 ranges.extend(selections.iter().map(|s| {
4355 if s.id == newest_selection.id {
4356 range_to_replace = Some(
4357 old_range.start.to_offset_utf16(&snapshot).0 as isize
4358 - selection.start as isize
4359 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4360 - selection.start as isize,
4361 );
4362 old_range.clone()
4363 } else {
4364 s.start..s.end
4365 }
4366 }));
4367 break;
4368 }
4369 if !self.linked_edit_ranges.is_empty() {
4370 let start_anchor = snapshot.anchor_before(selection.head());
4371 let end_anchor = snapshot.anchor_after(selection.tail());
4372 if let Some(ranges) = self
4373 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4374 {
4375 for (buffer, edits) in ranges {
4376 linked_edits.entry(buffer.clone()).or_default().extend(
4377 edits
4378 .into_iter()
4379 .map(|range| (range, text[common_prefix_len..].to_owned())),
4380 );
4381 }
4382 }
4383 }
4384 }
4385 let text = &text[common_prefix_len..];
4386
4387 cx.emit(EditorEvent::InputHandled {
4388 utf16_range_to_replace: range_to_replace,
4389 text: text.into(),
4390 });
4391
4392 self.transact(window, cx, |this, window, cx| {
4393 if let Some(mut snippet) = snippet {
4394 snippet.text = text.to_string();
4395 for tabstop in snippet
4396 .tabstops
4397 .iter_mut()
4398 .flat_map(|tabstop| tabstop.ranges.iter_mut())
4399 {
4400 tabstop.start -= common_prefix_len as isize;
4401 tabstop.end -= common_prefix_len as isize;
4402 }
4403
4404 this.insert_snippet(&ranges, snippet, window, cx).log_err();
4405 } else {
4406 this.buffer.update(cx, |buffer, cx| {
4407 buffer.edit(
4408 ranges.iter().map(|range| (range.clone(), text)),
4409 this.autoindent_mode.clone(),
4410 cx,
4411 );
4412 });
4413 }
4414 for (buffer, edits) in linked_edits {
4415 buffer.update(cx, |buffer, cx| {
4416 let snapshot = buffer.snapshot();
4417 let edits = edits
4418 .into_iter()
4419 .map(|(range, text)| {
4420 use text::ToPoint as TP;
4421 let end_point = TP::to_point(&range.end, &snapshot);
4422 let start_point = TP::to_point(&range.start, &snapshot);
4423 (start_point..end_point, text)
4424 })
4425 .sorted_by_key(|(range, _)| range.start)
4426 .collect::<Vec<_>>();
4427 buffer.edit(edits, None, cx);
4428 })
4429 }
4430
4431 this.refresh_inline_completion(true, false, window, cx);
4432 });
4433
4434 let show_new_completions_on_confirm = completion
4435 .confirm
4436 .as_ref()
4437 .map_or(false, |confirm| confirm(intent, window, cx));
4438 if show_new_completions_on_confirm {
4439 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
4440 }
4441
4442 let provider = self.completion_provider.as_ref()?;
4443 drop(completion);
4444 let apply_edits = provider.apply_additional_edits_for_completion(
4445 buffer_handle,
4446 completions_menu.completions.clone(),
4447 candidate_id,
4448 true,
4449 cx,
4450 );
4451
4452 let editor_settings = EditorSettings::get_global(cx);
4453 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4454 // After the code completion is finished, users often want to know what signatures are needed.
4455 // so we should automatically call signature_help
4456 self.show_signature_help(&ShowSignatureHelp, window, cx);
4457 }
4458
4459 Some(cx.foreground_executor().spawn(async move {
4460 apply_edits.await?;
4461 Ok(())
4462 }))
4463 }
4464
4465 pub fn toggle_code_actions(
4466 &mut self,
4467 action: &ToggleCodeActions,
4468 window: &mut Window,
4469 cx: &mut Context<Self>,
4470 ) {
4471 let mut context_menu = self.context_menu.borrow_mut();
4472 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4473 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4474 // Toggle if we're selecting the same one
4475 *context_menu = None;
4476 cx.notify();
4477 return;
4478 } else {
4479 // Otherwise, clear it and start a new one
4480 *context_menu = None;
4481 cx.notify();
4482 }
4483 }
4484 drop(context_menu);
4485 let snapshot = self.snapshot(window, cx);
4486 let deployed_from_indicator = action.deployed_from_indicator;
4487 let mut task = self.code_actions_task.take();
4488 let action = action.clone();
4489 cx.spawn_in(window, |editor, mut cx| async move {
4490 while let Some(prev_task) = task {
4491 prev_task.await.log_err();
4492 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4493 }
4494
4495 let spawned_test_task = editor.update_in(&mut cx, |editor, window, cx| {
4496 if editor.focus_handle.is_focused(window) {
4497 let multibuffer_point = action
4498 .deployed_from_indicator
4499 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4500 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4501 let (buffer, buffer_row) = snapshot
4502 .buffer_snapshot
4503 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4504 .and_then(|(buffer_snapshot, range)| {
4505 editor
4506 .buffer
4507 .read(cx)
4508 .buffer(buffer_snapshot.remote_id())
4509 .map(|buffer| (buffer, range.start.row))
4510 })?;
4511 let (_, code_actions) = editor
4512 .available_code_actions
4513 .clone()
4514 .and_then(|(location, code_actions)| {
4515 let snapshot = location.buffer.read(cx).snapshot();
4516 let point_range = location.range.to_point(&snapshot);
4517 let point_range = point_range.start.row..=point_range.end.row;
4518 if point_range.contains(&buffer_row) {
4519 Some((location, code_actions))
4520 } else {
4521 None
4522 }
4523 })
4524 .unzip();
4525 let buffer_id = buffer.read(cx).remote_id();
4526 let tasks = editor
4527 .tasks
4528 .get(&(buffer_id, buffer_row))
4529 .map(|t| Arc::new(t.to_owned()));
4530 if tasks.is_none() && code_actions.is_none() {
4531 return None;
4532 }
4533
4534 editor.completion_tasks.clear();
4535 editor.discard_inline_completion(false, cx);
4536 let task_context =
4537 tasks
4538 .as_ref()
4539 .zip(editor.project.clone())
4540 .map(|(tasks, project)| {
4541 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4542 });
4543
4544 Some(cx.spawn_in(window, |editor, mut cx| async move {
4545 let task_context = match task_context {
4546 Some(task_context) => task_context.await,
4547 None => None,
4548 };
4549 let resolved_tasks =
4550 tasks.zip(task_context).map(|(tasks, task_context)| {
4551 Rc::new(ResolvedTasks {
4552 templates: tasks.resolve(&task_context).collect(),
4553 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4554 multibuffer_point.row,
4555 tasks.column,
4556 )),
4557 })
4558 });
4559 let spawn_straight_away = resolved_tasks
4560 .as_ref()
4561 .map_or(false, |tasks| tasks.templates.len() == 1)
4562 && code_actions
4563 .as_ref()
4564 .map_or(true, |actions| actions.is_empty());
4565 if let Ok(task) = editor.update_in(&mut cx, |editor, window, cx| {
4566 *editor.context_menu.borrow_mut() =
4567 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4568 buffer,
4569 actions: CodeActionContents {
4570 tasks: resolved_tasks,
4571 actions: code_actions,
4572 },
4573 selected_item: Default::default(),
4574 scroll_handle: UniformListScrollHandle::default(),
4575 deployed_from_indicator,
4576 }));
4577 if spawn_straight_away {
4578 if let Some(task) = editor.confirm_code_action(
4579 &ConfirmCodeAction { item_ix: Some(0) },
4580 window,
4581 cx,
4582 ) {
4583 cx.notify();
4584 return task;
4585 }
4586 }
4587 cx.notify();
4588 Task::ready(Ok(()))
4589 }) {
4590 task.await
4591 } else {
4592 Ok(())
4593 }
4594 }))
4595 } else {
4596 Some(Task::ready(Ok(())))
4597 }
4598 })?;
4599 if let Some(task) = spawned_test_task {
4600 task.await?;
4601 }
4602
4603 Ok::<_, anyhow::Error>(())
4604 })
4605 .detach_and_log_err(cx);
4606 }
4607
4608 pub fn confirm_code_action(
4609 &mut self,
4610 action: &ConfirmCodeAction,
4611 window: &mut Window,
4612 cx: &mut Context<Self>,
4613 ) -> Option<Task<Result<()>>> {
4614 let actions_menu =
4615 if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
4616 menu
4617 } else {
4618 return None;
4619 };
4620 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4621 let action = actions_menu.actions.get(action_ix)?;
4622 let title = action.label();
4623 let buffer = actions_menu.buffer;
4624 let workspace = self.workspace()?;
4625
4626 match action {
4627 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4628 workspace.update(cx, |workspace, cx| {
4629 workspace::tasks::schedule_resolved_task(
4630 workspace,
4631 task_source_kind,
4632 resolved_task,
4633 false,
4634 cx,
4635 );
4636
4637 Some(Task::ready(Ok(())))
4638 })
4639 }
4640 CodeActionsItem::CodeAction {
4641 excerpt_id,
4642 action,
4643 provider,
4644 } => {
4645 let apply_code_action =
4646 provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
4647 let workspace = workspace.downgrade();
4648 Some(cx.spawn_in(window, |editor, cx| async move {
4649 let project_transaction = apply_code_action.await?;
4650 Self::open_project_transaction(
4651 &editor,
4652 workspace,
4653 project_transaction,
4654 title,
4655 cx,
4656 )
4657 .await
4658 }))
4659 }
4660 }
4661 }
4662
4663 pub async fn open_project_transaction(
4664 this: &WeakEntity<Editor>,
4665 workspace: WeakEntity<Workspace>,
4666 transaction: ProjectTransaction,
4667 title: String,
4668 mut cx: AsyncWindowContext,
4669 ) -> Result<()> {
4670 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4671 cx.update(|_, cx| {
4672 entries.sort_unstable_by_key(|(buffer, _)| {
4673 buffer.read(cx).file().map(|f| f.path().clone())
4674 });
4675 })?;
4676
4677 // If the project transaction's edits are all contained within this editor, then
4678 // avoid opening a new editor to display them.
4679
4680 if let Some((buffer, transaction)) = entries.first() {
4681 if entries.len() == 1 {
4682 let excerpt = this.update(&mut cx, |editor, cx| {
4683 editor
4684 .buffer()
4685 .read(cx)
4686 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4687 })?;
4688 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4689 if excerpted_buffer == *buffer {
4690 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4691 let excerpt_range = excerpt_range.to_offset(buffer);
4692 buffer
4693 .edited_ranges_for_transaction::<usize>(transaction)
4694 .all(|range| {
4695 excerpt_range.start <= range.start
4696 && excerpt_range.end >= range.end
4697 })
4698 })?;
4699
4700 if all_edits_within_excerpt {
4701 return Ok(());
4702 }
4703 }
4704 }
4705 }
4706 } else {
4707 return Ok(());
4708 }
4709
4710 let mut ranges_to_highlight = Vec::new();
4711 let excerpt_buffer = cx.new(|cx| {
4712 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4713 for (buffer_handle, transaction) in &entries {
4714 let buffer = buffer_handle.read(cx);
4715 ranges_to_highlight.extend(
4716 multibuffer.push_excerpts_with_context_lines(
4717 buffer_handle.clone(),
4718 buffer
4719 .edited_ranges_for_transaction::<usize>(transaction)
4720 .collect(),
4721 DEFAULT_MULTIBUFFER_CONTEXT,
4722 cx,
4723 ),
4724 );
4725 }
4726 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4727 multibuffer
4728 })?;
4729
4730 workspace.update_in(&mut cx, |workspace, window, cx| {
4731 let project = workspace.project().clone();
4732 let editor = cx
4733 .new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, window, cx));
4734 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
4735 editor.update(cx, |editor, cx| {
4736 editor.highlight_background::<Self>(
4737 &ranges_to_highlight,
4738 |theme| theme.editor_highlighted_line_background,
4739 cx,
4740 );
4741 });
4742 })?;
4743
4744 Ok(())
4745 }
4746
4747 pub fn clear_code_action_providers(&mut self) {
4748 self.code_action_providers.clear();
4749 self.available_code_actions.take();
4750 }
4751
4752 pub fn add_code_action_provider(
4753 &mut self,
4754 provider: Rc<dyn CodeActionProvider>,
4755 window: &mut Window,
4756 cx: &mut Context<Self>,
4757 ) {
4758 if self
4759 .code_action_providers
4760 .iter()
4761 .any(|existing_provider| existing_provider.id() == provider.id())
4762 {
4763 return;
4764 }
4765
4766 self.code_action_providers.push(provider);
4767 self.refresh_code_actions(window, cx);
4768 }
4769
4770 pub fn remove_code_action_provider(
4771 &mut self,
4772 id: Arc<str>,
4773 window: &mut Window,
4774 cx: &mut Context<Self>,
4775 ) {
4776 self.code_action_providers
4777 .retain(|provider| provider.id() != id);
4778 self.refresh_code_actions(window, cx);
4779 }
4780
4781 fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
4782 let buffer = self.buffer.read(cx);
4783 let newest_selection = self.selections.newest_anchor().clone();
4784 if newest_selection.head().diff_base_anchor.is_some() {
4785 return None;
4786 }
4787 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4788 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4789 if start_buffer != end_buffer {
4790 return None;
4791 }
4792
4793 self.code_actions_task = Some(cx.spawn_in(window, |this, mut cx| async move {
4794 cx.background_executor()
4795 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4796 .await;
4797
4798 let (providers, tasks) = this.update_in(&mut cx, |this, window, cx| {
4799 let providers = this.code_action_providers.clone();
4800 let tasks = this
4801 .code_action_providers
4802 .iter()
4803 .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
4804 .collect::<Vec<_>>();
4805 (providers, tasks)
4806 })?;
4807
4808 let mut actions = Vec::new();
4809 for (provider, provider_actions) in
4810 providers.into_iter().zip(future::join_all(tasks).await)
4811 {
4812 if let Some(provider_actions) = provider_actions.log_err() {
4813 actions.extend(provider_actions.into_iter().map(|action| {
4814 AvailableCodeAction {
4815 excerpt_id: newest_selection.start.excerpt_id,
4816 action,
4817 provider: provider.clone(),
4818 }
4819 }));
4820 }
4821 }
4822
4823 this.update(&mut cx, |this, cx| {
4824 this.available_code_actions = if actions.is_empty() {
4825 None
4826 } else {
4827 Some((
4828 Location {
4829 buffer: start_buffer,
4830 range: start..end,
4831 },
4832 actions.into(),
4833 ))
4834 };
4835 cx.notify();
4836 })
4837 }));
4838 None
4839 }
4840
4841 fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
4842 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4843 self.show_git_blame_inline = false;
4844
4845 self.show_git_blame_inline_delay_task =
4846 Some(cx.spawn_in(window, |this, mut cx| async move {
4847 cx.background_executor().timer(delay).await;
4848
4849 this.update(&mut cx, |this, cx| {
4850 this.show_git_blame_inline = true;
4851 cx.notify();
4852 })
4853 .log_err();
4854 }));
4855 }
4856 }
4857
4858 fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
4859 if self.pending_rename.is_some() {
4860 return None;
4861 }
4862
4863 let provider = self.semantics_provider.clone()?;
4864 let buffer = self.buffer.read(cx);
4865 let newest_selection = self.selections.newest_anchor().clone();
4866 let cursor_position = newest_selection.head();
4867 let (cursor_buffer, cursor_buffer_position) =
4868 buffer.text_anchor_for_position(cursor_position, cx)?;
4869 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4870 if cursor_buffer != tail_buffer {
4871 return None;
4872 }
4873 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
4874 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4875 cx.background_executor()
4876 .timer(Duration::from_millis(debounce))
4877 .await;
4878
4879 let highlights = if let Some(highlights) = cx
4880 .update(|cx| {
4881 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4882 })
4883 .ok()
4884 .flatten()
4885 {
4886 highlights.await.log_err()
4887 } else {
4888 None
4889 };
4890
4891 if let Some(highlights) = highlights {
4892 this.update(&mut cx, |this, cx| {
4893 if this.pending_rename.is_some() {
4894 return;
4895 }
4896
4897 let buffer_id = cursor_position.buffer_id;
4898 let buffer = this.buffer.read(cx);
4899 if !buffer
4900 .text_anchor_for_position(cursor_position, cx)
4901 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4902 {
4903 return;
4904 }
4905
4906 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4907 let mut write_ranges = Vec::new();
4908 let mut read_ranges = Vec::new();
4909 for highlight in highlights {
4910 for (excerpt_id, excerpt_range) in
4911 buffer.excerpts_for_buffer(cursor_buffer.read(cx).remote_id(), cx)
4912 {
4913 let start = highlight
4914 .range
4915 .start
4916 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4917 let end = highlight
4918 .range
4919 .end
4920 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4921 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4922 continue;
4923 }
4924
4925 let range = Anchor {
4926 buffer_id,
4927 excerpt_id,
4928 text_anchor: start,
4929 diff_base_anchor: None,
4930 }..Anchor {
4931 buffer_id,
4932 excerpt_id,
4933 text_anchor: end,
4934 diff_base_anchor: None,
4935 };
4936 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4937 write_ranges.push(range);
4938 } else {
4939 read_ranges.push(range);
4940 }
4941 }
4942 }
4943
4944 this.highlight_background::<DocumentHighlightRead>(
4945 &read_ranges,
4946 |theme| theme.editor_document_highlight_read_background,
4947 cx,
4948 );
4949 this.highlight_background::<DocumentHighlightWrite>(
4950 &write_ranges,
4951 |theme| theme.editor_document_highlight_write_background,
4952 cx,
4953 );
4954 cx.notify();
4955 })
4956 .log_err();
4957 }
4958 }));
4959 None
4960 }
4961
4962 pub fn refresh_inline_completion(
4963 &mut self,
4964 debounce: bool,
4965 user_requested: bool,
4966 window: &mut Window,
4967 cx: &mut Context<Self>,
4968 ) -> Option<()> {
4969 let provider = self.edit_prediction_provider()?;
4970 let cursor = self.selections.newest_anchor().head();
4971 let (buffer, cursor_buffer_position) =
4972 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4973
4974 if !self.inline_completions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
4975 self.discard_inline_completion(false, cx);
4976 return None;
4977 }
4978
4979 if !user_requested
4980 && (!self.should_show_edit_predictions()
4981 || !self.is_focused(window)
4982 || buffer.read(cx).is_empty())
4983 {
4984 self.discard_inline_completion(false, cx);
4985 return None;
4986 }
4987
4988 self.update_visible_inline_completion(window, cx);
4989 provider.refresh(
4990 self.project.clone(),
4991 buffer,
4992 cursor_buffer_position,
4993 debounce,
4994 cx,
4995 );
4996 Some(())
4997 }
4998
4999 fn show_edit_predictions_in_menu(&self) -> bool {
5000 match self.edit_prediction_settings {
5001 EditPredictionSettings::Disabled => false,
5002 EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
5003 }
5004 }
5005
5006 pub fn edit_predictions_enabled(&self) -> bool {
5007 match self.edit_prediction_settings {
5008 EditPredictionSettings::Disabled => false,
5009 EditPredictionSettings::Enabled { .. } => true,
5010 }
5011 }
5012
5013 fn edit_prediction_requires_modifier(&self) -> bool {
5014 match self.edit_prediction_settings {
5015 EditPredictionSettings::Disabled => false,
5016 EditPredictionSettings::Enabled {
5017 preview_requires_modifier,
5018 ..
5019 } => preview_requires_modifier,
5020 }
5021 }
5022
5023 fn edit_prediction_settings_at_position(
5024 &self,
5025 buffer: &Entity<Buffer>,
5026 buffer_position: language::Anchor,
5027 cx: &App,
5028 ) -> EditPredictionSettings {
5029 if self.mode != EditorMode::Full
5030 || !self.show_inline_completions_override.unwrap_or(true)
5031 || self.inline_completions_disabled_in_scope(buffer, buffer_position, cx)
5032 {
5033 return EditPredictionSettings::Disabled;
5034 }
5035
5036 let buffer = buffer.read(cx);
5037
5038 let file = buffer.file();
5039
5040 if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
5041 return EditPredictionSettings::Disabled;
5042 };
5043
5044 let by_provider = matches!(
5045 self.menu_inline_completions_policy,
5046 MenuInlineCompletionsPolicy::ByProvider
5047 );
5048
5049 let show_in_menu = by_provider
5050 && EditorSettings::get_global(cx).show_edit_predictions_in_menu
5051 && self
5052 .edit_prediction_provider
5053 .as_ref()
5054 .map_or(false, |provider| {
5055 provider.provider.show_completions_in_menu()
5056 });
5057
5058 let preview_requires_modifier = all_language_settings(file, cx)
5059 .inline_completions_preview_mode()
5060 == InlineCompletionPreviewMode::WhenHoldingModifier;
5061
5062 EditPredictionSettings::Enabled {
5063 show_in_menu,
5064 preview_requires_modifier,
5065 }
5066 }
5067
5068 fn should_show_edit_predictions(&self) -> bool {
5069 self.snippet_stack.is_empty() && self.edit_predictions_enabled()
5070 }
5071
5072 pub fn inline_completions_enabled(&self, cx: &App) -> bool {
5073 let cursor = self.selections.newest_anchor().head();
5074 if let Some((buffer, cursor_position)) =
5075 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5076 {
5077 self.inline_completions_enabled_in_buffer(&buffer, cursor_position, cx)
5078 } else {
5079 false
5080 }
5081 }
5082
5083 fn inline_completions_enabled_in_buffer(
5084 &self,
5085 buffer: &Entity<Buffer>,
5086 buffer_position: language::Anchor,
5087 cx: &App,
5088 ) -> bool {
5089 maybe!({
5090 let provider = self.edit_prediction_provider()?;
5091 if !provider.is_enabled(&buffer, buffer_position, cx) {
5092 return Some(false);
5093 }
5094 let buffer = buffer.read(cx);
5095 let Some(file) = buffer.file() else {
5096 return Some(true);
5097 };
5098 let settings = all_language_settings(Some(file), cx);
5099 Some(settings.inline_completions_enabled_for_path(file.path()))
5100 })
5101 .unwrap_or(false)
5102 }
5103
5104 fn cycle_inline_completion(
5105 &mut self,
5106 direction: Direction,
5107 window: &mut Window,
5108 cx: &mut Context<Self>,
5109 ) -> Option<()> {
5110 let provider = self.edit_prediction_provider()?;
5111 let cursor = self.selections.newest_anchor().head();
5112 let (buffer, cursor_buffer_position) =
5113 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5114 if self.inline_completions_hidden_for_vim_mode || !self.should_show_edit_predictions() {
5115 return None;
5116 }
5117
5118 provider.cycle(buffer, cursor_buffer_position, direction, cx);
5119 self.update_visible_inline_completion(window, cx);
5120
5121 Some(())
5122 }
5123
5124 pub fn show_inline_completion(
5125 &mut self,
5126 _: &ShowEditPrediction,
5127 window: &mut Window,
5128 cx: &mut Context<Self>,
5129 ) {
5130 if !self.has_active_inline_completion() {
5131 self.refresh_inline_completion(false, true, window, cx);
5132 return;
5133 }
5134
5135 self.update_visible_inline_completion(window, cx);
5136 }
5137
5138 pub fn display_cursor_names(
5139 &mut self,
5140 _: &DisplayCursorNames,
5141 window: &mut Window,
5142 cx: &mut Context<Self>,
5143 ) {
5144 self.show_cursor_names(window, cx);
5145 }
5146
5147 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
5148 self.show_cursor_names = true;
5149 cx.notify();
5150 cx.spawn_in(window, |this, mut cx| async move {
5151 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5152 this.update(&mut cx, |this, cx| {
5153 this.show_cursor_names = false;
5154 cx.notify()
5155 })
5156 .ok()
5157 })
5158 .detach();
5159 }
5160
5161 pub fn next_edit_prediction(
5162 &mut self,
5163 _: &NextEditPrediction,
5164 window: &mut Window,
5165 cx: &mut Context<Self>,
5166 ) {
5167 if self.has_active_inline_completion() {
5168 self.cycle_inline_completion(Direction::Next, window, cx);
5169 } else {
5170 let is_copilot_disabled = self
5171 .refresh_inline_completion(false, true, window, cx)
5172 .is_none();
5173 if is_copilot_disabled {
5174 cx.propagate();
5175 }
5176 }
5177 }
5178
5179 pub fn previous_edit_prediction(
5180 &mut self,
5181 _: &PreviousEditPrediction,
5182 window: &mut Window,
5183 cx: &mut Context<Self>,
5184 ) {
5185 if self.has_active_inline_completion() {
5186 self.cycle_inline_completion(Direction::Prev, window, cx);
5187 } else {
5188 let is_copilot_disabled = self
5189 .refresh_inline_completion(false, true, window, cx)
5190 .is_none();
5191 if is_copilot_disabled {
5192 cx.propagate();
5193 }
5194 }
5195 }
5196
5197 pub fn accept_edit_prediction(
5198 &mut self,
5199 _: &AcceptEditPrediction,
5200 window: &mut Window,
5201 cx: &mut Context<Self>,
5202 ) {
5203 let buffer = self.buffer.read(cx);
5204 let snapshot = buffer.snapshot(cx);
5205 let selection = self.selections.newest_adjusted(cx);
5206 let cursor = selection.head();
5207 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
5208 let suggested_indents = snapshot.suggested_indents([cursor.row], cx);
5209 if let Some(suggested_indent) = suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
5210 {
5211 if cursor.column < suggested_indent.len
5212 && cursor.column <= current_indent.len
5213 && current_indent.len <= suggested_indent.len
5214 {
5215 self.tab(&Default::default(), window, cx);
5216 return;
5217 }
5218 }
5219
5220 if self.show_edit_predictions_in_menu() {
5221 self.hide_context_menu(window, cx);
5222 }
5223
5224 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5225 return;
5226 };
5227
5228 self.report_inline_completion_event(
5229 active_inline_completion.completion_id.clone(),
5230 true,
5231 cx,
5232 );
5233
5234 match &active_inline_completion.completion {
5235 InlineCompletion::Move { target, .. } => {
5236 let target = *target;
5237 // Note that this is also done in vim's handler of the Tab action.
5238 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
5239 selections.select_anchor_ranges([target..target]);
5240 });
5241 }
5242 InlineCompletion::Edit { edits, .. } => {
5243 if let Some(provider) = self.edit_prediction_provider() {
5244 provider.accept(cx);
5245 }
5246
5247 let snapshot = self.buffer.read(cx).snapshot(cx);
5248 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
5249
5250 self.buffer.update(cx, |buffer, cx| {
5251 buffer.edit(edits.iter().cloned(), None, cx)
5252 });
5253
5254 self.change_selections(None, window, cx, |s| {
5255 s.select_anchor_ranges([last_edit_end..last_edit_end])
5256 });
5257
5258 self.update_visible_inline_completion(window, cx);
5259 if self.active_inline_completion.is_none() {
5260 self.refresh_inline_completion(true, true, window, cx);
5261 }
5262
5263 cx.notify();
5264 }
5265 }
5266 }
5267
5268 pub fn accept_partial_inline_completion(
5269 &mut self,
5270 _: &AcceptPartialEditPrediction,
5271 window: &mut Window,
5272 cx: &mut Context<Self>,
5273 ) {
5274 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5275 return;
5276 };
5277 if self.selections.count() != 1 {
5278 return;
5279 }
5280
5281 self.report_inline_completion_event(
5282 active_inline_completion.completion_id.clone(),
5283 true,
5284 cx,
5285 );
5286
5287 match &active_inline_completion.completion {
5288 InlineCompletion::Move { target, .. } => {
5289 let target = *target;
5290 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
5291 selections.select_anchor_ranges([target..target]);
5292 });
5293 }
5294 InlineCompletion::Edit { edits, .. } => {
5295 // Find an insertion that starts at the cursor position.
5296 let snapshot = self.buffer.read(cx).snapshot(cx);
5297 let cursor_offset = self.selections.newest::<usize>(cx).head();
5298 let insertion = edits.iter().find_map(|(range, text)| {
5299 let range = range.to_offset(&snapshot);
5300 if range.is_empty() && range.start == cursor_offset {
5301 Some(text)
5302 } else {
5303 None
5304 }
5305 });
5306
5307 if let Some(text) = insertion {
5308 let mut partial_completion = text
5309 .chars()
5310 .by_ref()
5311 .take_while(|c| c.is_alphabetic())
5312 .collect::<String>();
5313 if partial_completion.is_empty() {
5314 partial_completion = text
5315 .chars()
5316 .by_ref()
5317 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5318 .collect::<String>();
5319 }
5320
5321 cx.emit(EditorEvent::InputHandled {
5322 utf16_range_to_replace: None,
5323 text: partial_completion.clone().into(),
5324 });
5325
5326 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
5327
5328 self.refresh_inline_completion(true, true, window, cx);
5329 cx.notify();
5330 } else {
5331 self.accept_edit_prediction(&Default::default(), window, cx);
5332 }
5333 }
5334 }
5335 }
5336
5337 fn discard_inline_completion(
5338 &mut self,
5339 should_report_inline_completion_event: bool,
5340 cx: &mut Context<Self>,
5341 ) -> bool {
5342 if should_report_inline_completion_event {
5343 let completion_id = self
5344 .active_inline_completion
5345 .as_ref()
5346 .and_then(|active_completion| active_completion.completion_id.clone());
5347
5348 self.report_inline_completion_event(completion_id, false, cx);
5349 }
5350
5351 if let Some(provider) = self.edit_prediction_provider() {
5352 provider.discard(cx);
5353 }
5354
5355 self.take_active_inline_completion(cx)
5356 }
5357
5358 fn report_inline_completion_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
5359 let Some(provider) = self.edit_prediction_provider() else {
5360 return;
5361 };
5362
5363 let Some((_, buffer, _)) = self
5364 .buffer
5365 .read(cx)
5366 .excerpt_containing(self.selections.newest_anchor().head(), cx)
5367 else {
5368 return;
5369 };
5370
5371 let extension = buffer
5372 .read(cx)
5373 .file()
5374 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
5375
5376 let event_type = match accepted {
5377 true => "Edit Prediction Accepted",
5378 false => "Edit Prediction Discarded",
5379 };
5380 telemetry::event!(
5381 event_type,
5382 provider = provider.name(),
5383 prediction_id = id,
5384 suggestion_accepted = accepted,
5385 file_extension = extension,
5386 );
5387 }
5388
5389 pub fn has_active_inline_completion(&self) -> bool {
5390 self.active_inline_completion.is_some()
5391 }
5392
5393 fn take_active_inline_completion(&mut self, cx: &mut Context<Self>) -> bool {
5394 let Some(active_inline_completion) = self.active_inline_completion.take() else {
5395 return false;
5396 };
5397
5398 self.splice_inlays(&active_inline_completion.inlay_ids, Default::default(), cx);
5399 self.clear_highlights::<InlineCompletionHighlight>(cx);
5400 self.stale_inline_completion_in_menu = Some(active_inline_completion);
5401 true
5402 }
5403
5404 /// Returns true when we're displaying the edit prediction popover below the cursor
5405 /// like we are not previewing and the LSP autocomplete menu is visible
5406 /// or we are in `when_holding_modifier` mode.
5407 pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
5408 if self.edit_prediction_preview.is_active()
5409 || !self.show_edit_predictions_in_menu()
5410 || !self.edit_predictions_enabled()
5411 {
5412 return false;
5413 }
5414
5415 if self.has_visible_completions_menu() {
5416 return true;
5417 }
5418
5419 has_completion && self.edit_prediction_requires_modifier()
5420 }
5421
5422 fn handle_modifiers_changed(
5423 &mut self,
5424 modifiers: Modifiers,
5425 position_map: &PositionMap,
5426 window: &mut Window,
5427 cx: &mut Context<Self>,
5428 ) {
5429 if self.show_edit_predictions_in_menu() {
5430 self.update_edit_prediction_preview(&modifiers, position_map, window, cx);
5431 }
5432
5433 let mouse_position = window.mouse_position();
5434 if !position_map.text_hitbox.is_hovered(window) {
5435 return;
5436 }
5437
5438 self.update_hovered_link(
5439 position_map.point_for_position(mouse_position),
5440 &position_map.snapshot,
5441 modifiers,
5442 window,
5443 cx,
5444 )
5445 }
5446
5447 fn update_edit_prediction_preview(
5448 &mut self,
5449 modifiers: &Modifiers,
5450 position_map: &PositionMap,
5451 window: &mut Window,
5452 cx: &mut Context<Self>,
5453 ) {
5454 let accept_keybind = self.accept_edit_prediction_keybind(window, cx);
5455 let Some(accept_keystroke) = accept_keybind.keystroke() else {
5456 return;
5457 };
5458
5459 if &accept_keystroke.modifiers == modifiers {
5460 let Some(completion) = self.active_inline_completion.as_ref() else {
5461 return;
5462 };
5463
5464 if !self.edit_prediction_requires_modifier() && !self.has_visible_completions_menu() {
5465 return;
5466 }
5467
5468 let transitioned = self.edit_prediction_preview.start(
5469 &completion.completion,
5470 &position_map.snapshot,
5471 self.selections
5472 .newest_anchor()
5473 .head()
5474 .to_display_point(&position_map.snapshot),
5475 );
5476
5477 if transitioned {
5478 self.request_autoscroll(Autoscroll::fit(), cx);
5479 self.update_visible_inline_completion(window, cx);
5480 cx.notify();
5481 }
5482 } else if self.edit_prediction_preview.end(
5483 self.selections
5484 .newest_anchor()
5485 .head()
5486 .to_display_point(&position_map.snapshot),
5487 position_map.scroll_pixel_position,
5488 window,
5489 cx,
5490 ) {
5491 self.update_visible_inline_completion(window, cx);
5492 cx.notify();
5493 }
5494 }
5495
5496 fn update_visible_inline_completion(
5497 &mut self,
5498 window: &mut Window,
5499 cx: &mut Context<Self>,
5500 ) -> Option<()> {
5501 let selection = self.selections.newest_anchor();
5502 let cursor = selection.head();
5503 let multibuffer = self.buffer.read(cx).snapshot(cx);
5504 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
5505 let excerpt_id = cursor.excerpt_id;
5506
5507 let show_in_menu = self.show_edit_predictions_in_menu();
5508 let completions_menu_has_precedence = !show_in_menu
5509 && (self.context_menu.borrow().is_some()
5510 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
5511
5512 if completions_menu_has_precedence
5513 || !offset_selection.is_empty()
5514 || self
5515 .active_inline_completion
5516 .as_ref()
5517 .map_or(false, |completion| {
5518 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
5519 let invalidation_range = invalidation_range.start..=invalidation_range.end;
5520 !invalidation_range.contains(&offset_selection.head())
5521 })
5522 {
5523 self.discard_inline_completion(false, cx);
5524 return None;
5525 }
5526
5527 self.take_active_inline_completion(cx);
5528 let Some(provider) = self.edit_prediction_provider() else {
5529 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5530 return None;
5531 };
5532
5533 let (buffer, cursor_buffer_position) =
5534 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5535
5536 self.edit_prediction_settings =
5537 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5538
5539 if !self.edit_prediction_settings.is_enabled() {
5540 self.discard_inline_completion(false, cx);
5541 return None;
5542 }
5543
5544 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
5545 let edits = inline_completion
5546 .edits
5547 .into_iter()
5548 .flat_map(|(range, new_text)| {
5549 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
5550 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
5551 Some((start..end, new_text))
5552 })
5553 .collect::<Vec<_>>();
5554 if edits.is_empty() {
5555 return None;
5556 }
5557
5558 let first_edit_start = edits.first().unwrap().0.start;
5559 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
5560 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
5561
5562 let last_edit_end = edits.last().unwrap().0.end;
5563 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
5564 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
5565
5566 let cursor_row = cursor.to_point(&multibuffer).row;
5567
5568 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
5569
5570 let mut inlay_ids = Vec::new();
5571 let invalidation_row_range;
5572 let move_invalidation_row_range = if cursor_row < edit_start_row {
5573 Some(cursor_row..edit_end_row)
5574 } else if cursor_row > edit_end_row {
5575 Some(edit_start_row..cursor_row)
5576 } else {
5577 None
5578 };
5579 let is_move =
5580 move_invalidation_row_range.is_some() || self.inline_completions_hidden_for_vim_mode;
5581 let completion = if is_move {
5582 invalidation_row_range =
5583 move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
5584 let target = first_edit_start;
5585 InlineCompletion::Move { target, snapshot }
5586 } else {
5587 let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
5588 && !self.inline_completions_hidden_for_vim_mode;
5589
5590 if show_completions_in_buffer {
5591 if edits
5592 .iter()
5593 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
5594 {
5595 let mut inlays = Vec::new();
5596 for (range, new_text) in &edits {
5597 let inlay = Inlay::inline_completion(
5598 post_inc(&mut self.next_inlay_id),
5599 range.start,
5600 new_text.as_str(),
5601 );
5602 inlay_ids.push(inlay.id);
5603 inlays.push(inlay);
5604 }
5605
5606 self.splice_inlays(&[], inlays, cx);
5607 } else {
5608 let background_color = cx.theme().status().deleted_background;
5609 self.highlight_text::<InlineCompletionHighlight>(
5610 edits.iter().map(|(range, _)| range.clone()).collect(),
5611 HighlightStyle {
5612 background_color: Some(background_color),
5613 ..Default::default()
5614 },
5615 cx,
5616 );
5617 }
5618 }
5619
5620 invalidation_row_range = edit_start_row..edit_end_row;
5621
5622 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
5623 if provider.show_tab_accept_marker() {
5624 EditDisplayMode::TabAccept
5625 } else {
5626 EditDisplayMode::Inline
5627 }
5628 } else {
5629 EditDisplayMode::DiffPopover
5630 };
5631
5632 InlineCompletion::Edit {
5633 edits,
5634 edit_preview: inline_completion.edit_preview,
5635 display_mode,
5636 snapshot,
5637 }
5638 };
5639
5640 let invalidation_range = multibuffer
5641 .anchor_before(Point::new(invalidation_row_range.start, 0))
5642 ..multibuffer.anchor_after(Point::new(
5643 invalidation_row_range.end,
5644 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
5645 ));
5646
5647 self.stale_inline_completion_in_menu = None;
5648 let editor_snapshot = self.snapshot(window, cx);
5649 if self.edit_prediction_preview.restart(
5650 &completion,
5651 &editor_snapshot,
5652 cursor.to_display_point(&editor_snapshot),
5653 ) {
5654 self.request_autoscroll(Autoscroll::fit(), cx);
5655 }
5656
5657 self.active_inline_completion = Some(InlineCompletionState {
5658 inlay_ids,
5659 completion,
5660 completion_id: inline_completion.id,
5661 invalidation_range,
5662 });
5663
5664 cx.notify();
5665
5666 Some(())
5667 }
5668
5669 pub fn edit_prediction_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5670 Some(self.edit_prediction_provider.as_ref()?.provider.clone())
5671 }
5672
5673 fn render_code_actions_indicator(
5674 &self,
5675 _style: &EditorStyle,
5676 row: DisplayRow,
5677 is_active: bool,
5678 cx: &mut Context<Self>,
5679 ) -> Option<IconButton> {
5680 if self.available_code_actions.is_some() {
5681 Some(
5682 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5683 .shape(ui::IconButtonShape::Square)
5684 .icon_size(IconSize::XSmall)
5685 .icon_color(Color::Muted)
5686 .toggle_state(is_active)
5687 .tooltip({
5688 let focus_handle = self.focus_handle.clone();
5689 move |window, cx| {
5690 Tooltip::for_action_in(
5691 "Toggle Code Actions",
5692 &ToggleCodeActions {
5693 deployed_from_indicator: None,
5694 },
5695 &focus_handle,
5696 window,
5697 cx,
5698 )
5699 }
5700 })
5701 .on_click(cx.listener(move |editor, _e, window, cx| {
5702 window.focus(&editor.focus_handle(cx));
5703 editor.toggle_code_actions(
5704 &ToggleCodeActions {
5705 deployed_from_indicator: Some(row),
5706 },
5707 window,
5708 cx,
5709 );
5710 })),
5711 )
5712 } else {
5713 None
5714 }
5715 }
5716
5717 fn clear_tasks(&mut self) {
5718 self.tasks.clear()
5719 }
5720
5721 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5722 if self.tasks.insert(key, value).is_some() {
5723 // This case should hopefully be rare, but just in case...
5724 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5725 }
5726 }
5727
5728 fn build_tasks_context(
5729 project: &Entity<Project>,
5730 buffer: &Entity<Buffer>,
5731 buffer_row: u32,
5732 tasks: &Arc<RunnableTasks>,
5733 cx: &mut Context<Self>,
5734 ) -> Task<Option<task::TaskContext>> {
5735 let position = Point::new(buffer_row, tasks.column);
5736 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
5737 let location = Location {
5738 buffer: buffer.clone(),
5739 range: range_start..range_start,
5740 };
5741 // Fill in the environmental variables from the tree-sitter captures
5742 let mut captured_task_variables = TaskVariables::default();
5743 for (capture_name, value) in tasks.extra_variables.clone() {
5744 captured_task_variables.insert(
5745 task::VariableName::Custom(capture_name.into()),
5746 value.clone(),
5747 );
5748 }
5749 project.update(cx, |project, cx| {
5750 project.task_store().update(cx, |task_store, cx| {
5751 task_store.task_context_for_location(captured_task_variables, location, cx)
5752 })
5753 })
5754 }
5755
5756 pub fn spawn_nearest_task(
5757 &mut self,
5758 action: &SpawnNearestTask,
5759 window: &mut Window,
5760 cx: &mut Context<Self>,
5761 ) {
5762 let Some((workspace, _)) = self.workspace.clone() else {
5763 return;
5764 };
5765 let Some(project) = self.project.clone() else {
5766 return;
5767 };
5768
5769 // Try to find a closest, enclosing node using tree-sitter that has a
5770 // task
5771 let Some((buffer, buffer_row, tasks)) = self
5772 .find_enclosing_node_task(cx)
5773 // Or find the task that's closest in row-distance.
5774 .or_else(|| self.find_closest_task(cx))
5775 else {
5776 return;
5777 };
5778
5779 let reveal_strategy = action.reveal;
5780 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
5781 cx.spawn_in(window, |_, mut cx| async move {
5782 let context = task_context.await?;
5783 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
5784
5785 let resolved = resolved_task.resolved.as_mut()?;
5786 resolved.reveal = reveal_strategy;
5787
5788 workspace
5789 .update(&mut cx, |workspace, cx| {
5790 workspace::tasks::schedule_resolved_task(
5791 workspace,
5792 task_source_kind,
5793 resolved_task,
5794 false,
5795 cx,
5796 );
5797 })
5798 .ok()
5799 })
5800 .detach();
5801 }
5802
5803 fn find_closest_task(
5804 &mut self,
5805 cx: &mut Context<Self>,
5806 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5807 let cursor_row = self.selections.newest_adjusted(cx).head().row;
5808
5809 let ((buffer_id, row), tasks) = self
5810 .tasks
5811 .iter()
5812 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
5813
5814 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
5815 let tasks = Arc::new(tasks.to_owned());
5816 Some((buffer, *row, tasks))
5817 }
5818
5819 fn find_enclosing_node_task(
5820 &mut self,
5821 cx: &mut Context<Self>,
5822 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5823 let snapshot = self.buffer.read(cx).snapshot(cx);
5824 let offset = self.selections.newest::<usize>(cx).head();
5825 let excerpt = snapshot.excerpt_containing(offset..offset)?;
5826 let buffer_id = excerpt.buffer().remote_id();
5827
5828 let layer = excerpt.buffer().syntax_layer_at(offset)?;
5829 let mut cursor = layer.node().walk();
5830
5831 while cursor.goto_first_child_for_byte(offset).is_some() {
5832 if cursor.node().end_byte() == offset {
5833 cursor.goto_next_sibling();
5834 }
5835 }
5836
5837 // Ascend to the smallest ancestor that contains the range and has a task.
5838 loop {
5839 let node = cursor.node();
5840 let node_range = node.byte_range();
5841 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
5842
5843 // Check if this node contains our offset
5844 if node_range.start <= offset && node_range.end >= offset {
5845 // If it contains offset, check for task
5846 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
5847 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
5848 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
5849 }
5850 }
5851
5852 if !cursor.goto_parent() {
5853 break;
5854 }
5855 }
5856 None
5857 }
5858
5859 fn render_run_indicator(
5860 &self,
5861 _style: &EditorStyle,
5862 is_active: bool,
5863 row: DisplayRow,
5864 cx: &mut Context<Self>,
5865 ) -> IconButton {
5866 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5867 .shape(ui::IconButtonShape::Square)
5868 .icon_size(IconSize::XSmall)
5869 .icon_color(Color::Muted)
5870 .toggle_state(is_active)
5871 .on_click(cx.listener(move |editor, _e, window, cx| {
5872 window.focus(&editor.focus_handle(cx));
5873 editor.toggle_code_actions(
5874 &ToggleCodeActions {
5875 deployed_from_indicator: Some(row),
5876 },
5877 window,
5878 cx,
5879 );
5880 }))
5881 }
5882
5883 pub fn context_menu_visible(&self) -> bool {
5884 !self.edit_prediction_preview.is_active()
5885 && self
5886 .context_menu
5887 .borrow()
5888 .as_ref()
5889 .map_or(false, |menu| menu.visible())
5890 }
5891
5892 fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
5893 self.context_menu
5894 .borrow()
5895 .as_ref()
5896 .map(|menu| menu.origin())
5897 }
5898
5899 fn edit_prediction_cursor_popover_height(&self) -> Pixels {
5900 px(30.)
5901 }
5902
5903 fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
5904 if self.read_only(cx) {
5905 cx.theme().players().read_only()
5906 } else {
5907 self.style.as_ref().unwrap().local_player
5908 }
5909 }
5910
5911 #[allow(clippy::too_many_arguments)]
5912 fn render_edit_prediction_cursor_popover(
5913 &self,
5914 min_width: Pixels,
5915 max_width: Pixels,
5916 cursor_point: Point,
5917 style: &EditorStyle,
5918 accept_keystroke: &gpui::Keystroke,
5919 _window: &Window,
5920 cx: &mut Context<Editor>,
5921 ) -> Option<AnyElement> {
5922 let provider = self.edit_prediction_provider.as_ref()?;
5923
5924 if provider.provider.needs_terms_acceptance(cx) {
5925 return Some(
5926 h_flex()
5927 .min_w(min_width)
5928 .flex_1()
5929 .px_2()
5930 .py_1()
5931 .gap_3()
5932 .elevation_2(cx)
5933 .hover(|style| style.bg(cx.theme().colors().element_hover))
5934 .id("accept-terms")
5935 .cursor_pointer()
5936 .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
5937 .on_click(cx.listener(|this, _event, window, cx| {
5938 cx.stop_propagation();
5939 this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx);
5940 window.dispatch_action(
5941 zed_actions::OpenZedPredictOnboarding.boxed_clone(),
5942 cx,
5943 );
5944 }))
5945 .child(
5946 h_flex()
5947 .flex_1()
5948 .gap_2()
5949 .child(Icon::new(IconName::ZedPredict))
5950 .child(Label::new("Accept Terms of Service"))
5951 .child(div().w_full())
5952 .child(
5953 Icon::new(IconName::ArrowUpRight)
5954 .color(Color::Muted)
5955 .size(IconSize::Small),
5956 )
5957 .into_any_element(),
5958 )
5959 .into_any(),
5960 );
5961 }
5962
5963 let is_refreshing = provider.provider.is_refreshing(cx);
5964
5965 fn pending_completion_container() -> Div {
5966 h_flex()
5967 .h_full()
5968 .flex_1()
5969 .gap_2()
5970 .child(Icon::new(IconName::ZedPredict))
5971 }
5972
5973 let completion = match &self.active_inline_completion {
5974 Some(completion) => match &completion.completion {
5975 InlineCompletion::Move {
5976 target, snapshot, ..
5977 } if !self.has_visible_completions_menu() => {
5978 use text::ToPoint as _;
5979
5980 return Some(
5981 h_flex()
5982 .px_2()
5983 .py_1()
5984 .elevation_2(cx)
5985 .border_color(cx.theme().colors().border)
5986 .rounded_tl(px(0.))
5987 .gap_2()
5988 .child(
5989 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
5990 Icon::new(IconName::ZedPredictDown)
5991 } else {
5992 Icon::new(IconName::ZedPredictUp)
5993 },
5994 )
5995 .child(Label::new("Hold"))
5996 .children(ui::render_modifiers(
5997 &accept_keystroke.modifiers,
5998 PlatformStyle::platform(),
5999 Some(Color::Default),
6000 None,
6001 true,
6002 ))
6003 .into_any(),
6004 );
6005 }
6006 _ => self.render_edit_prediction_cursor_popover_preview(
6007 completion,
6008 cursor_point,
6009 style,
6010 cx,
6011 )?,
6012 },
6013
6014 None if is_refreshing => match &self.stale_inline_completion_in_menu {
6015 Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
6016 stale_completion,
6017 cursor_point,
6018 style,
6019 cx,
6020 )?,
6021
6022 None => {
6023 pending_completion_container().child(Label::new("...").size(LabelSize::Small))
6024 }
6025 },
6026
6027 None => pending_completion_container().child(Label::new("No Prediction")),
6028 };
6029
6030 let completion = if is_refreshing {
6031 completion
6032 .with_animation(
6033 "loading-completion",
6034 Animation::new(Duration::from_secs(2))
6035 .repeat()
6036 .with_easing(pulsating_between(0.4, 0.8)),
6037 |label, delta| label.opacity(delta),
6038 )
6039 .into_any_element()
6040 } else {
6041 completion.into_any_element()
6042 };
6043
6044 let has_completion = self.active_inline_completion.is_some();
6045
6046 Some(
6047 h_flex()
6048 .min_w(min_width)
6049 .max_w(max_width)
6050 .flex_1()
6051 .px_2()
6052 .py_1()
6053 .elevation_2(cx)
6054 .border_color(cx.theme().colors().border)
6055 .child(completion)
6056 .child(ui::Divider::vertical())
6057 .child(
6058 h_flex()
6059 .h_full()
6060 .gap_1()
6061 .pl_2()
6062 .child(
6063 h_flex()
6064 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6065 .gap_1()
6066 .children(ui::render_modifiers(
6067 &accept_keystroke.modifiers,
6068 PlatformStyle::platform(),
6069 Some(if !has_completion {
6070 Color::Muted
6071 } else {
6072 Color::Default
6073 }),
6074 None,
6075 true,
6076 )),
6077 )
6078 .child(Label::new("Preview").into_any_element())
6079 .opacity(if has_completion { 1.0 } else { 0.4 }),
6080 )
6081 .into_any(),
6082 )
6083 }
6084
6085 fn render_edit_prediction_cursor_popover_preview(
6086 &self,
6087 completion: &InlineCompletionState,
6088 cursor_point: Point,
6089 style: &EditorStyle,
6090 cx: &mut Context<Editor>,
6091 ) -> Option<Div> {
6092 use text::ToPoint as _;
6093
6094 fn render_relative_row_jump(
6095 prefix: impl Into<String>,
6096 current_row: u32,
6097 target_row: u32,
6098 ) -> Div {
6099 let (row_diff, arrow) = if target_row < current_row {
6100 (current_row - target_row, IconName::ArrowUp)
6101 } else {
6102 (target_row - current_row, IconName::ArrowDown)
6103 };
6104
6105 h_flex()
6106 .child(
6107 Label::new(format!("{}{}", prefix.into(), row_diff))
6108 .color(Color::Muted)
6109 .size(LabelSize::Small),
6110 )
6111 .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
6112 }
6113
6114 match &completion.completion {
6115 InlineCompletion::Move {
6116 target, snapshot, ..
6117 } => Some(
6118 h_flex()
6119 .px_2()
6120 .gap_2()
6121 .flex_1()
6122 .child(
6123 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
6124 Icon::new(IconName::ZedPredictDown)
6125 } else {
6126 Icon::new(IconName::ZedPredictUp)
6127 },
6128 )
6129 .child(Label::new("Jump to Edit")),
6130 ),
6131
6132 InlineCompletion::Edit {
6133 edits,
6134 edit_preview,
6135 snapshot,
6136 display_mode: _,
6137 } => {
6138 let first_edit_row = edits.first()?.0.start.text_anchor.to_point(&snapshot).row;
6139
6140 let highlighted_edits = crate::inline_completion_edit_text(
6141 &snapshot,
6142 &edits,
6143 edit_preview.as_ref()?,
6144 true,
6145 cx,
6146 );
6147
6148 let len_total = highlighted_edits.text.len();
6149 let first_line = &highlighted_edits.text
6150 [..highlighted_edits.text.find('\n').unwrap_or(len_total)];
6151 let first_line_len = first_line.len();
6152
6153 let first_highlight_start = highlighted_edits
6154 .highlights
6155 .first()
6156 .map_or(0, |(range, _)| range.start);
6157 let drop_prefix_len = first_line
6158 .char_indices()
6159 .find(|(_, c)| !c.is_whitespace())
6160 .map_or(first_highlight_start, |(ix, _)| {
6161 ix.min(first_highlight_start)
6162 });
6163
6164 let preview_text = &first_line[drop_prefix_len..];
6165 let preview_len = preview_text.len();
6166 let highlights = highlighted_edits
6167 .highlights
6168 .into_iter()
6169 .take_until(|(range, _)| range.start > first_line_len)
6170 .map(|(range, style)| {
6171 (
6172 range.start - drop_prefix_len
6173 ..(range.end - drop_prefix_len).min(preview_len),
6174 style,
6175 )
6176 });
6177
6178 let styled_text = gpui::StyledText::new(SharedString::new(preview_text))
6179 .with_highlights(&style.text, highlights);
6180
6181 let preview = h_flex()
6182 .gap_1()
6183 .min_w_16()
6184 .child(styled_text)
6185 .when(len_total > first_line_len, |parent| parent.child("…"));
6186
6187 let left = if first_edit_row != cursor_point.row {
6188 render_relative_row_jump("", cursor_point.row, first_edit_row)
6189 .into_any_element()
6190 } else {
6191 Icon::new(IconName::ZedPredict).into_any_element()
6192 };
6193
6194 Some(
6195 h_flex()
6196 .h_full()
6197 .flex_1()
6198 .gap_2()
6199 .pr_1()
6200 .overflow_x_hidden()
6201 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6202 .child(left)
6203 .child(preview),
6204 )
6205 }
6206 }
6207 }
6208
6209 fn render_context_menu(
6210 &self,
6211 style: &EditorStyle,
6212 max_height_in_lines: u32,
6213 y_flipped: bool,
6214 window: &mut Window,
6215 cx: &mut Context<Editor>,
6216 ) -> Option<AnyElement> {
6217 let menu = self.context_menu.borrow();
6218 let menu = menu.as_ref()?;
6219 if !menu.visible() {
6220 return None;
6221 };
6222 Some(menu.render(style, max_height_in_lines, y_flipped, window, cx))
6223 }
6224
6225 fn render_context_menu_aside(
6226 &self,
6227 style: &EditorStyle,
6228 max_size: Size<Pixels>,
6229 cx: &mut Context<Editor>,
6230 ) -> Option<AnyElement> {
6231 self.context_menu.borrow().as_ref().and_then(|menu| {
6232 if menu.visible() {
6233 menu.render_aside(
6234 style,
6235 max_size,
6236 self.workspace.as_ref().map(|(w, _)| w.clone()),
6237 cx,
6238 )
6239 } else {
6240 None
6241 }
6242 })
6243 }
6244
6245 fn hide_context_menu(
6246 &mut self,
6247 window: &mut Window,
6248 cx: &mut Context<Self>,
6249 ) -> Option<CodeContextMenu> {
6250 cx.notify();
6251 self.completion_tasks.clear();
6252 let context_menu = self.context_menu.borrow_mut().take();
6253 self.stale_inline_completion_in_menu.take();
6254 self.update_visible_inline_completion(window, cx);
6255 context_menu
6256 }
6257
6258 fn show_snippet_choices(
6259 &mut self,
6260 choices: &Vec<String>,
6261 selection: Range<Anchor>,
6262 cx: &mut Context<Self>,
6263 ) {
6264 if selection.start.buffer_id.is_none() {
6265 return;
6266 }
6267 let buffer_id = selection.start.buffer_id.unwrap();
6268 let buffer = self.buffer().read(cx).buffer(buffer_id);
6269 let id = post_inc(&mut self.next_completion_id);
6270
6271 if let Some(buffer) = buffer {
6272 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
6273 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
6274 ));
6275 }
6276 }
6277
6278 pub fn insert_snippet(
6279 &mut self,
6280 insertion_ranges: &[Range<usize>],
6281 snippet: Snippet,
6282 window: &mut Window,
6283 cx: &mut Context<Self>,
6284 ) -> Result<()> {
6285 struct Tabstop<T> {
6286 is_end_tabstop: bool,
6287 ranges: Vec<Range<T>>,
6288 choices: Option<Vec<String>>,
6289 }
6290
6291 let tabstops = self.buffer.update(cx, |buffer, cx| {
6292 let snippet_text: Arc<str> = snippet.text.clone().into();
6293 buffer.edit(
6294 insertion_ranges
6295 .iter()
6296 .cloned()
6297 .map(|range| (range, snippet_text.clone())),
6298 Some(AutoindentMode::EachLine),
6299 cx,
6300 );
6301
6302 let snapshot = &*buffer.read(cx);
6303 let snippet = &snippet;
6304 snippet
6305 .tabstops
6306 .iter()
6307 .map(|tabstop| {
6308 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
6309 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
6310 });
6311 let mut tabstop_ranges = tabstop
6312 .ranges
6313 .iter()
6314 .flat_map(|tabstop_range| {
6315 let mut delta = 0_isize;
6316 insertion_ranges.iter().map(move |insertion_range| {
6317 let insertion_start = insertion_range.start as isize + delta;
6318 delta +=
6319 snippet.text.len() as isize - insertion_range.len() as isize;
6320
6321 let start = ((insertion_start + tabstop_range.start) as usize)
6322 .min(snapshot.len());
6323 let end = ((insertion_start + tabstop_range.end) as usize)
6324 .min(snapshot.len());
6325 snapshot.anchor_before(start)..snapshot.anchor_after(end)
6326 })
6327 })
6328 .collect::<Vec<_>>();
6329 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
6330
6331 Tabstop {
6332 is_end_tabstop,
6333 ranges: tabstop_ranges,
6334 choices: tabstop.choices.clone(),
6335 }
6336 })
6337 .collect::<Vec<_>>()
6338 });
6339 if let Some(tabstop) = tabstops.first() {
6340 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6341 s.select_ranges(tabstop.ranges.iter().cloned());
6342 });
6343
6344 if let Some(choices) = &tabstop.choices {
6345 if let Some(selection) = tabstop.ranges.first() {
6346 self.show_snippet_choices(choices, selection.clone(), cx)
6347 }
6348 }
6349
6350 // If we're already at the last tabstop and it's at the end of the snippet,
6351 // we're done, we don't need to keep the state around.
6352 if !tabstop.is_end_tabstop {
6353 let choices = tabstops
6354 .iter()
6355 .map(|tabstop| tabstop.choices.clone())
6356 .collect();
6357
6358 let ranges = tabstops
6359 .into_iter()
6360 .map(|tabstop| tabstop.ranges)
6361 .collect::<Vec<_>>();
6362
6363 self.snippet_stack.push(SnippetState {
6364 active_index: 0,
6365 ranges,
6366 choices,
6367 });
6368 }
6369
6370 // Check whether the just-entered snippet ends with an auto-closable bracket.
6371 if self.autoclose_regions.is_empty() {
6372 let snapshot = self.buffer.read(cx).snapshot(cx);
6373 for selection in &mut self.selections.all::<Point>(cx) {
6374 let selection_head = selection.head();
6375 let Some(scope) = snapshot.language_scope_at(selection_head) else {
6376 continue;
6377 };
6378
6379 let mut bracket_pair = None;
6380 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
6381 let prev_chars = snapshot
6382 .reversed_chars_at(selection_head)
6383 .collect::<String>();
6384 for (pair, enabled) in scope.brackets() {
6385 if enabled
6386 && pair.close
6387 && prev_chars.starts_with(pair.start.as_str())
6388 && next_chars.starts_with(pair.end.as_str())
6389 {
6390 bracket_pair = Some(pair.clone());
6391 break;
6392 }
6393 }
6394 if let Some(pair) = bracket_pair {
6395 let start = snapshot.anchor_after(selection_head);
6396 let end = snapshot.anchor_after(selection_head);
6397 self.autoclose_regions.push(AutocloseRegion {
6398 selection_id: selection.id,
6399 range: start..end,
6400 pair,
6401 });
6402 }
6403 }
6404 }
6405 }
6406 Ok(())
6407 }
6408
6409 pub fn move_to_next_snippet_tabstop(
6410 &mut self,
6411 window: &mut Window,
6412 cx: &mut Context<Self>,
6413 ) -> bool {
6414 self.move_to_snippet_tabstop(Bias::Right, window, cx)
6415 }
6416
6417 pub fn move_to_prev_snippet_tabstop(
6418 &mut self,
6419 window: &mut Window,
6420 cx: &mut Context<Self>,
6421 ) -> bool {
6422 self.move_to_snippet_tabstop(Bias::Left, window, cx)
6423 }
6424
6425 pub fn move_to_snippet_tabstop(
6426 &mut self,
6427 bias: Bias,
6428 window: &mut Window,
6429 cx: &mut Context<Self>,
6430 ) -> bool {
6431 if let Some(mut snippet) = self.snippet_stack.pop() {
6432 match bias {
6433 Bias::Left => {
6434 if snippet.active_index > 0 {
6435 snippet.active_index -= 1;
6436 } else {
6437 self.snippet_stack.push(snippet);
6438 return false;
6439 }
6440 }
6441 Bias::Right => {
6442 if snippet.active_index + 1 < snippet.ranges.len() {
6443 snippet.active_index += 1;
6444 } else {
6445 self.snippet_stack.push(snippet);
6446 return false;
6447 }
6448 }
6449 }
6450 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
6451 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6452 s.select_anchor_ranges(current_ranges.iter().cloned())
6453 });
6454
6455 if let Some(choices) = &snippet.choices[snippet.active_index] {
6456 if let Some(selection) = current_ranges.first() {
6457 self.show_snippet_choices(&choices, selection.clone(), cx);
6458 }
6459 }
6460
6461 // If snippet state is not at the last tabstop, push it back on the stack
6462 if snippet.active_index + 1 < snippet.ranges.len() {
6463 self.snippet_stack.push(snippet);
6464 }
6465 return true;
6466 }
6467 }
6468
6469 false
6470 }
6471
6472 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
6473 self.transact(window, cx, |this, window, cx| {
6474 this.select_all(&SelectAll, window, cx);
6475 this.insert("", window, cx);
6476 });
6477 }
6478
6479 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
6480 self.transact(window, cx, |this, window, cx| {
6481 this.select_autoclose_pair(window, cx);
6482 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
6483 if !this.linked_edit_ranges.is_empty() {
6484 let selections = this.selections.all::<MultiBufferPoint>(cx);
6485 let snapshot = this.buffer.read(cx).snapshot(cx);
6486
6487 for selection in selections.iter() {
6488 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
6489 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
6490 if selection_start.buffer_id != selection_end.buffer_id {
6491 continue;
6492 }
6493 if let Some(ranges) =
6494 this.linked_editing_ranges_for(selection_start..selection_end, cx)
6495 {
6496 for (buffer, entries) in ranges {
6497 linked_ranges.entry(buffer).or_default().extend(entries);
6498 }
6499 }
6500 }
6501 }
6502
6503 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
6504 if !this.selections.line_mode {
6505 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
6506 for selection in &mut selections {
6507 if selection.is_empty() {
6508 let old_head = selection.head();
6509 let mut new_head =
6510 movement::left(&display_map, old_head.to_display_point(&display_map))
6511 .to_point(&display_map);
6512 if let Some((buffer, line_buffer_range)) = display_map
6513 .buffer_snapshot
6514 .buffer_line_for_row(MultiBufferRow(old_head.row))
6515 {
6516 let indent_size =
6517 buffer.indent_size_for_line(line_buffer_range.start.row);
6518 let indent_len = match indent_size.kind {
6519 IndentKind::Space => {
6520 buffer.settings_at(line_buffer_range.start, cx).tab_size
6521 }
6522 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
6523 };
6524 if old_head.column <= indent_size.len && old_head.column > 0 {
6525 let indent_len = indent_len.get();
6526 new_head = cmp::min(
6527 new_head,
6528 MultiBufferPoint::new(
6529 old_head.row,
6530 ((old_head.column - 1) / indent_len) * indent_len,
6531 ),
6532 );
6533 }
6534 }
6535
6536 selection.set_head(new_head, SelectionGoal::None);
6537 }
6538 }
6539 }
6540
6541 this.signature_help_state.set_backspace_pressed(true);
6542 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6543 s.select(selections)
6544 });
6545 this.insert("", window, cx);
6546 let empty_str: Arc<str> = Arc::from("");
6547 for (buffer, edits) in linked_ranges {
6548 let snapshot = buffer.read(cx).snapshot();
6549 use text::ToPoint as TP;
6550
6551 let edits = edits
6552 .into_iter()
6553 .map(|range| {
6554 let end_point = TP::to_point(&range.end, &snapshot);
6555 let mut start_point = TP::to_point(&range.start, &snapshot);
6556
6557 if end_point == start_point {
6558 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
6559 .saturating_sub(1);
6560 start_point =
6561 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
6562 };
6563
6564 (start_point..end_point, empty_str.clone())
6565 })
6566 .sorted_by_key(|(range, _)| range.start)
6567 .collect::<Vec<_>>();
6568 buffer.update(cx, |this, cx| {
6569 this.edit(edits, None, cx);
6570 })
6571 }
6572 this.refresh_inline_completion(true, false, window, cx);
6573 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
6574 });
6575 }
6576
6577 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
6578 self.transact(window, cx, |this, window, cx| {
6579 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6580 let line_mode = s.line_mode;
6581 s.move_with(|map, selection| {
6582 if selection.is_empty() && !line_mode {
6583 let cursor = movement::right(map, selection.head());
6584 selection.end = cursor;
6585 selection.reversed = true;
6586 selection.goal = SelectionGoal::None;
6587 }
6588 })
6589 });
6590 this.insert("", window, cx);
6591 this.refresh_inline_completion(true, false, window, cx);
6592 });
6593 }
6594
6595 pub fn tab_prev(&mut self, _: &TabPrev, window: &mut Window, cx: &mut Context<Self>) {
6596 if self.move_to_prev_snippet_tabstop(window, cx) {
6597 return;
6598 }
6599
6600 self.outdent(&Outdent, window, cx);
6601 }
6602
6603 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
6604 if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) {
6605 return;
6606 }
6607
6608 let mut selections = self.selections.all_adjusted(cx);
6609 let buffer = self.buffer.read(cx);
6610 let snapshot = buffer.snapshot(cx);
6611 let rows_iter = selections.iter().map(|s| s.head().row);
6612 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
6613
6614 let mut edits = Vec::new();
6615 let mut prev_edited_row = 0;
6616 let mut row_delta = 0;
6617 for selection in &mut selections {
6618 if selection.start.row != prev_edited_row {
6619 row_delta = 0;
6620 }
6621 prev_edited_row = selection.end.row;
6622
6623 // If the selection is non-empty, then increase the indentation of the selected lines.
6624 if !selection.is_empty() {
6625 row_delta =
6626 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
6627 continue;
6628 }
6629
6630 // If the selection is empty and the cursor is in the leading whitespace before the
6631 // suggested indentation, then auto-indent the line.
6632 let cursor = selection.head();
6633 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
6634 if let Some(suggested_indent) =
6635 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
6636 {
6637 if cursor.column < suggested_indent.len
6638 && cursor.column <= current_indent.len
6639 && current_indent.len <= suggested_indent.len
6640 {
6641 selection.start = Point::new(cursor.row, suggested_indent.len);
6642 selection.end = selection.start;
6643 if row_delta == 0 {
6644 edits.extend(Buffer::edit_for_indent_size_adjustment(
6645 cursor.row,
6646 current_indent,
6647 suggested_indent,
6648 ));
6649 row_delta = suggested_indent.len - current_indent.len;
6650 }
6651 continue;
6652 }
6653 }
6654
6655 // Otherwise, insert a hard or soft tab.
6656 let settings = buffer.settings_at(cursor, cx);
6657 let tab_size = if settings.hard_tabs {
6658 IndentSize::tab()
6659 } else {
6660 let tab_size = settings.tab_size.get();
6661 let char_column = snapshot
6662 .text_for_range(Point::new(cursor.row, 0)..cursor)
6663 .flat_map(str::chars)
6664 .count()
6665 + row_delta as usize;
6666 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
6667 IndentSize::spaces(chars_to_next_tab_stop)
6668 };
6669 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
6670 selection.end = selection.start;
6671 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
6672 row_delta += tab_size.len;
6673 }
6674
6675 self.transact(window, cx, |this, window, cx| {
6676 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
6677 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6678 s.select(selections)
6679 });
6680 this.refresh_inline_completion(true, false, window, cx);
6681 });
6682 }
6683
6684 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
6685 if self.read_only(cx) {
6686 return;
6687 }
6688 let mut selections = self.selections.all::<Point>(cx);
6689 let mut prev_edited_row = 0;
6690 let mut row_delta = 0;
6691 let mut edits = Vec::new();
6692 let buffer = self.buffer.read(cx);
6693 let snapshot = buffer.snapshot(cx);
6694 for selection in &mut selections {
6695 if selection.start.row != prev_edited_row {
6696 row_delta = 0;
6697 }
6698 prev_edited_row = selection.end.row;
6699
6700 row_delta =
6701 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
6702 }
6703
6704 self.transact(window, cx, |this, window, cx| {
6705 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
6706 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6707 s.select(selections)
6708 });
6709 });
6710 }
6711
6712 fn indent_selection(
6713 buffer: &MultiBuffer,
6714 snapshot: &MultiBufferSnapshot,
6715 selection: &mut Selection<Point>,
6716 edits: &mut Vec<(Range<Point>, String)>,
6717 delta_for_start_row: u32,
6718 cx: &App,
6719 ) -> u32 {
6720 let settings = buffer.settings_at(selection.start, cx);
6721 let tab_size = settings.tab_size.get();
6722 let indent_kind = if settings.hard_tabs {
6723 IndentKind::Tab
6724 } else {
6725 IndentKind::Space
6726 };
6727 let mut start_row = selection.start.row;
6728 let mut end_row = selection.end.row + 1;
6729
6730 // If a selection ends at the beginning of a line, don't indent
6731 // that last line.
6732 if selection.end.column == 0 && selection.end.row > selection.start.row {
6733 end_row -= 1;
6734 }
6735
6736 // Avoid re-indenting a row that has already been indented by a
6737 // previous selection, but still update this selection's column
6738 // to reflect that indentation.
6739 if delta_for_start_row > 0 {
6740 start_row += 1;
6741 selection.start.column += delta_for_start_row;
6742 if selection.end.row == selection.start.row {
6743 selection.end.column += delta_for_start_row;
6744 }
6745 }
6746
6747 let mut delta_for_end_row = 0;
6748 let has_multiple_rows = start_row + 1 != end_row;
6749 for row in start_row..end_row {
6750 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
6751 let indent_delta = match (current_indent.kind, indent_kind) {
6752 (IndentKind::Space, IndentKind::Space) => {
6753 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
6754 IndentSize::spaces(columns_to_next_tab_stop)
6755 }
6756 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
6757 (_, IndentKind::Tab) => IndentSize::tab(),
6758 };
6759
6760 let start = if has_multiple_rows || current_indent.len < selection.start.column {
6761 0
6762 } else {
6763 selection.start.column
6764 };
6765 let row_start = Point::new(row, start);
6766 edits.push((
6767 row_start..row_start,
6768 indent_delta.chars().collect::<String>(),
6769 ));
6770
6771 // Update this selection's endpoints to reflect the indentation.
6772 if row == selection.start.row {
6773 selection.start.column += indent_delta.len;
6774 }
6775 if row == selection.end.row {
6776 selection.end.column += indent_delta.len;
6777 delta_for_end_row = indent_delta.len;
6778 }
6779 }
6780
6781 if selection.start.row == selection.end.row {
6782 delta_for_start_row + delta_for_end_row
6783 } else {
6784 delta_for_end_row
6785 }
6786 }
6787
6788 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
6789 if self.read_only(cx) {
6790 return;
6791 }
6792 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6793 let selections = self.selections.all::<Point>(cx);
6794 let mut deletion_ranges = Vec::new();
6795 let mut last_outdent = None;
6796 {
6797 let buffer = self.buffer.read(cx);
6798 let snapshot = buffer.snapshot(cx);
6799 for selection in &selections {
6800 let settings = buffer.settings_at(selection.start, cx);
6801 let tab_size = settings.tab_size.get();
6802 let mut rows = selection.spanned_rows(false, &display_map);
6803
6804 // Avoid re-outdenting a row that has already been outdented by a
6805 // previous selection.
6806 if let Some(last_row) = last_outdent {
6807 if last_row == rows.start {
6808 rows.start = rows.start.next_row();
6809 }
6810 }
6811 let has_multiple_rows = rows.len() > 1;
6812 for row in rows.iter_rows() {
6813 let indent_size = snapshot.indent_size_for_line(row);
6814 if indent_size.len > 0 {
6815 let deletion_len = match indent_size.kind {
6816 IndentKind::Space => {
6817 let columns_to_prev_tab_stop = indent_size.len % tab_size;
6818 if columns_to_prev_tab_stop == 0 {
6819 tab_size
6820 } else {
6821 columns_to_prev_tab_stop
6822 }
6823 }
6824 IndentKind::Tab => 1,
6825 };
6826 let start = if has_multiple_rows
6827 || deletion_len > selection.start.column
6828 || indent_size.len < selection.start.column
6829 {
6830 0
6831 } else {
6832 selection.start.column - deletion_len
6833 };
6834 deletion_ranges.push(
6835 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
6836 );
6837 last_outdent = Some(row);
6838 }
6839 }
6840 }
6841 }
6842
6843 self.transact(window, cx, |this, window, cx| {
6844 this.buffer.update(cx, |buffer, cx| {
6845 let empty_str: Arc<str> = Arc::default();
6846 buffer.edit(
6847 deletion_ranges
6848 .into_iter()
6849 .map(|range| (range, empty_str.clone())),
6850 None,
6851 cx,
6852 );
6853 });
6854 let selections = this.selections.all::<usize>(cx);
6855 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6856 s.select(selections)
6857 });
6858 });
6859 }
6860
6861 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
6862 if self.read_only(cx) {
6863 return;
6864 }
6865 let selections = self
6866 .selections
6867 .all::<usize>(cx)
6868 .into_iter()
6869 .map(|s| s.range());
6870
6871 self.transact(window, cx, |this, window, cx| {
6872 this.buffer.update(cx, |buffer, cx| {
6873 buffer.autoindent_ranges(selections, cx);
6874 });
6875 let selections = this.selections.all::<usize>(cx);
6876 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6877 s.select(selections)
6878 });
6879 });
6880 }
6881
6882 pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
6883 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6884 let selections = self.selections.all::<Point>(cx);
6885
6886 let mut new_cursors = Vec::new();
6887 let mut edit_ranges = Vec::new();
6888 let mut selections = selections.iter().peekable();
6889 while let Some(selection) = selections.next() {
6890 let mut rows = selection.spanned_rows(false, &display_map);
6891 let goal_display_column = selection.head().to_display_point(&display_map).column();
6892
6893 // Accumulate contiguous regions of rows that we want to delete.
6894 while let Some(next_selection) = selections.peek() {
6895 let next_rows = next_selection.spanned_rows(false, &display_map);
6896 if next_rows.start <= rows.end {
6897 rows.end = next_rows.end;
6898 selections.next().unwrap();
6899 } else {
6900 break;
6901 }
6902 }
6903
6904 let buffer = &display_map.buffer_snapshot;
6905 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
6906 let edit_end;
6907 let cursor_buffer_row;
6908 if buffer.max_point().row >= rows.end.0 {
6909 // If there's a line after the range, delete the \n from the end of the row range
6910 // and position the cursor on the next line.
6911 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
6912 cursor_buffer_row = rows.end;
6913 } else {
6914 // If there isn't a line after the range, delete the \n from the line before the
6915 // start of the row range and position the cursor there.
6916 edit_start = edit_start.saturating_sub(1);
6917 edit_end = buffer.len();
6918 cursor_buffer_row = rows.start.previous_row();
6919 }
6920
6921 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
6922 *cursor.column_mut() =
6923 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
6924
6925 new_cursors.push((
6926 selection.id,
6927 buffer.anchor_after(cursor.to_point(&display_map)),
6928 ));
6929 edit_ranges.push(edit_start..edit_end);
6930 }
6931
6932 self.transact(window, cx, |this, window, cx| {
6933 let buffer = this.buffer.update(cx, |buffer, cx| {
6934 let empty_str: Arc<str> = Arc::default();
6935 buffer.edit(
6936 edit_ranges
6937 .into_iter()
6938 .map(|range| (range, empty_str.clone())),
6939 None,
6940 cx,
6941 );
6942 buffer.snapshot(cx)
6943 });
6944 let new_selections = new_cursors
6945 .into_iter()
6946 .map(|(id, cursor)| {
6947 let cursor = cursor.to_point(&buffer);
6948 Selection {
6949 id,
6950 start: cursor,
6951 end: cursor,
6952 reversed: false,
6953 goal: SelectionGoal::None,
6954 }
6955 })
6956 .collect();
6957
6958 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6959 s.select(new_selections);
6960 });
6961 });
6962 }
6963
6964 pub fn join_lines_impl(
6965 &mut self,
6966 insert_whitespace: bool,
6967 window: &mut Window,
6968 cx: &mut Context<Self>,
6969 ) {
6970 if self.read_only(cx) {
6971 return;
6972 }
6973 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
6974 for selection in self.selections.all::<Point>(cx) {
6975 let start = MultiBufferRow(selection.start.row);
6976 // Treat single line selections as if they include the next line. Otherwise this action
6977 // would do nothing for single line selections individual cursors.
6978 let end = if selection.start.row == selection.end.row {
6979 MultiBufferRow(selection.start.row + 1)
6980 } else {
6981 MultiBufferRow(selection.end.row)
6982 };
6983
6984 if let Some(last_row_range) = row_ranges.last_mut() {
6985 if start <= last_row_range.end {
6986 last_row_range.end = end;
6987 continue;
6988 }
6989 }
6990 row_ranges.push(start..end);
6991 }
6992
6993 let snapshot = self.buffer.read(cx).snapshot(cx);
6994 let mut cursor_positions = Vec::new();
6995 for row_range in &row_ranges {
6996 let anchor = snapshot.anchor_before(Point::new(
6997 row_range.end.previous_row().0,
6998 snapshot.line_len(row_range.end.previous_row()),
6999 ));
7000 cursor_positions.push(anchor..anchor);
7001 }
7002
7003 self.transact(window, cx, |this, window, cx| {
7004 for row_range in row_ranges.into_iter().rev() {
7005 for row in row_range.iter_rows().rev() {
7006 let end_of_line = Point::new(row.0, snapshot.line_len(row));
7007 let next_line_row = row.next_row();
7008 let indent = snapshot.indent_size_for_line(next_line_row);
7009 let start_of_next_line = Point::new(next_line_row.0, indent.len);
7010
7011 let replace =
7012 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
7013 " "
7014 } else {
7015 ""
7016 };
7017
7018 this.buffer.update(cx, |buffer, cx| {
7019 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
7020 });
7021 }
7022 }
7023
7024 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7025 s.select_anchor_ranges(cursor_positions)
7026 });
7027 });
7028 }
7029
7030 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
7031 self.join_lines_impl(true, window, cx);
7032 }
7033
7034 pub fn sort_lines_case_sensitive(
7035 &mut self,
7036 _: &SortLinesCaseSensitive,
7037 window: &mut Window,
7038 cx: &mut Context<Self>,
7039 ) {
7040 self.manipulate_lines(window, cx, |lines| lines.sort())
7041 }
7042
7043 pub fn sort_lines_case_insensitive(
7044 &mut self,
7045 _: &SortLinesCaseInsensitive,
7046 window: &mut Window,
7047 cx: &mut Context<Self>,
7048 ) {
7049 self.manipulate_lines(window, cx, |lines| {
7050 lines.sort_by_key(|line| line.to_lowercase())
7051 })
7052 }
7053
7054 pub fn unique_lines_case_insensitive(
7055 &mut self,
7056 _: &UniqueLinesCaseInsensitive,
7057 window: &mut Window,
7058 cx: &mut Context<Self>,
7059 ) {
7060 self.manipulate_lines(window, cx, |lines| {
7061 let mut seen = HashSet::default();
7062 lines.retain(|line| seen.insert(line.to_lowercase()));
7063 })
7064 }
7065
7066 pub fn unique_lines_case_sensitive(
7067 &mut self,
7068 _: &UniqueLinesCaseSensitive,
7069 window: &mut Window,
7070 cx: &mut Context<Self>,
7071 ) {
7072 self.manipulate_lines(window, cx, |lines| {
7073 let mut seen = HashSet::default();
7074 lines.retain(|line| seen.insert(*line));
7075 })
7076 }
7077
7078 pub fn revert_file(&mut self, _: &RevertFile, window: &mut Window, cx: &mut Context<Self>) {
7079 let mut revert_changes = HashMap::default();
7080 let snapshot = self.snapshot(window, cx);
7081 for hunk in snapshot
7082 .hunks_for_ranges(Some(Point::zero()..snapshot.buffer_snapshot.max_point()).into_iter())
7083 {
7084 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
7085 }
7086 if !revert_changes.is_empty() {
7087 self.transact(window, cx, |editor, window, cx| {
7088 editor.revert(revert_changes, window, cx);
7089 });
7090 }
7091 }
7092
7093 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
7094 let Some(project) = self.project.clone() else {
7095 return;
7096 };
7097 self.reload(project, window, cx)
7098 .detach_and_notify_err(window, cx);
7099 }
7100
7101 pub fn revert_selected_hunks(
7102 &mut self,
7103 _: &RevertSelectedHunks,
7104 window: &mut Window,
7105 cx: &mut Context<Self>,
7106 ) {
7107 let selections = self.selections.all(cx).into_iter().map(|s| s.range());
7108 self.revert_hunks_in_ranges(selections, window, cx);
7109 }
7110
7111 fn revert_hunks_in_ranges(
7112 &mut self,
7113 ranges: impl Iterator<Item = Range<Point>>,
7114 window: &mut Window,
7115 cx: &mut Context<Editor>,
7116 ) {
7117 let mut revert_changes = HashMap::default();
7118 let snapshot = self.snapshot(window, cx);
7119 for hunk in &snapshot.hunks_for_ranges(ranges) {
7120 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
7121 }
7122 if !revert_changes.is_empty() {
7123 self.transact(window, cx, |editor, window, cx| {
7124 editor.revert(revert_changes, window, cx);
7125 });
7126 }
7127 }
7128
7129 pub fn open_active_item_in_terminal(
7130 &mut self,
7131 _: &OpenInTerminal,
7132 window: &mut Window,
7133 cx: &mut Context<Self>,
7134 ) {
7135 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
7136 let project_path = buffer.read(cx).project_path(cx)?;
7137 let project = self.project.as_ref()?.read(cx);
7138 let entry = project.entry_for_path(&project_path, cx)?;
7139 let parent = match &entry.canonical_path {
7140 Some(canonical_path) => canonical_path.to_path_buf(),
7141 None => project.absolute_path(&project_path, cx)?,
7142 }
7143 .parent()?
7144 .to_path_buf();
7145 Some(parent)
7146 }) {
7147 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
7148 }
7149 }
7150
7151 pub fn prepare_revert_change(
7152 &self,
7153 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
7154 hunk: &MultiBufferDiffHunk,
7155 cx: &mut App,
7156 ) -> Option<()> {
7157 let buffer = self.buffer.read(cx);
7158 let diff = buffer.diff_for(hunk.buffer_id)?;
7159 let buffer = buffer.buffer(hunk.buffer_id)?;
7160 let buffer = buffer.read(cx);
7161 let original_text = diff
7162 .read(cx)
7163 .base_text()
7164 .as_ref()?
7165 .as_rope()
7166 .slice(hunk.diff_base_byte_range.clone());
7167 let buffer_snapshot = buffer.snapshot();
7168 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
7169 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
7170 probe
7171 .0
7172 .start
7173 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
7174 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
7175 }) {
7176 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
7177 Some(())
7178 } else {
7179 None
7180 }
7181 }
7182
7183 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
7184 self.manipulate_lines(window, cx, |lines| lines.reverse())
7185 }
7186
7187 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
7188 self.manipulate_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
7189 }
7190
7191 fn manipulate_lines<Fn>(
7192 &mut self,
7193 window: &mut Window,
7194 cx: &mut Context<Self>,
7195 mut callback: Fn,
7196 ) where
7197 Fn: FnMut(&mut Vec<&str>),
7198 {
7199 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7200 let buffer = self.buffer.read(cx).snapshot(cx);
7201
7202 let mut edits = Vec::new();
7203
7204 let selections = self.selections.all::<Point>(cx);
7205 let mut selections = selections.iter().peekable();
7206 let mut contiguous_row_selections = Vec::new();
7207 let mut new_selections = Vec::new();
7208 let mut added_lines = 0;
7209 let mut removed_lines = 0;
7210
7211 while let Some(selection) = selections.next() {
7212 let (start_row, end_row) = consume_contiguous_rows(
7213 &mut contiguous_row_selections,
7214 selection,
7215 &display_map,
7216 &mut selections,
7217 );
7218
7219 let start_point = Point::new(start_row.0, 0);
7220 let end_point = Point::new(
7221 end_row.previous_row().0,
7222 buffer.line_len(end_row.previous_row()),
7223 );
7224 let text = buffer
7225 .text_for_range(start_point..end_point)
7226 .collect::<String>();
7227
7228 let mut lines = text.split('\n').collect_vec();
7229
7230 let lines_before = lines.len();
7231 callback(&mut lines);
7232 let lines_after = lines.len();
7233
7234 edits.push((start_point..end_point, lines.join("\n")));
7235
7236 // Selections must change based on added and removed line count
7237 let start_row =
7238 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
7239 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
7240 new_selections.push(Selection {
7241 id: selection.id,
7242 start: start_row,
7243 end: end_row,
7244 goal: SelectionGoal::None,
7245 reversed: selection.reversed,
7246 });
7247
7248 if lines_after > lines_before {
7249 added_lines += lines_after - lines_before;
7250 } else if lines_before > lines_after {
7251 removed_lines += lines_before - lines_after;
7252 }
7253 }
7254
7255 self.transact(window, cx, |this, window, cx| {
7256 let buffer = this.buffer.update(cx, |buffer, cx| {
7257 buffer.edit(edits, None, cx);
7258 buffer.snapshot(cx)
7259 });
7260
7261 // Recalculate offsets on newly edited buffer
7262 let new_selections = new_selections
7263 .iter()
7264 .map(|s| {
7265 let start_point = Point::new(s.start.0, 0);
7266 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
7267 Selection {
7268 id: s.id,
7269 start: buffer.point_to_offset(start_point),
7270 end: buffer.point_to_offset(end_point),
7271 goal: s.goal,
7272 reversed: s.reversed,
7273 }
7274 })
7275 .collect();
7276
7277 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7278 s.select(new_selections);
7279 });
7280
7281 this.request_autoscroll(Autoscroll::fit(), cx);
7282 });
7283 }
7284
7285 pub fn convert_to_upper_case(
7286 &mut self,
7287 _: &ConvertToUpperCase,
7288 window: &mut Window,
7289 cx: &mut Context<Self>,
7290 ) {
7291 self.manipulate_text(window, cx, |text| text.to_uppercase())
7292 }
7293
7294 pub fn convert_to_lower_case(
7295 &mut self,
7296 _: &ConvertToLowerCase,
7297 window: &mut Window,
7298 cx: &mut Context<Self>,
7299 ) {
7300 self.manipulate_text(window, cx, |text| text.to_lowercase())
7301 }
7302
7303 pub fn convert_to_title_case(
7304 &mut self,
7305 _: &ConvertToTitleCase,
7306 window: &mut Window,
7307 cx: &mut Context<Self>,
7308 ) {
7309 self.manipulate_text(window, cx, |text| {
7310 text.split('\n')
7311 .map(|line| line.to_case(Case::Title))
7312 .join("\n")
7313 })
7314 }
7315
7316 pub fn convert_to_snake_case(
7317 &mut self,
7318 _: &ConvertToSnakeCase,
7319 window: &mut Window,
7320 cx: &mut Context<Self>,
7321 ) {
7322 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
7323 }
7324
7325 pub fn convert_to_kebab_case(
7326 &mut self,
7327 _: &ConvertToKebabCase,
7328 window: &mut Window,
7329 cx: &mut Context<Self>,
7330 ) {
7331 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
7332 }
7333
7334 pub fn convert_to_upper_camel_case(
7335 &mut self,
7336 _: &ConvertToUpperCamelCase,
7337 window: &mut Window,
7338 cx: &mut Context<Self>,
7339 ) {
7340 self.manipulate_text(window, cx, |text| {
7341 text.split('\n')
7342 .map(|line| line.to_case(Case::UpperCamel))
7343 .join("\n")
7344 })
7345 }
7346
7347 pub fn convert_to_lower_camel_case(
7348 &mut self,
7349 _: &ConvertToLowerCamelCase,
7350 window: &mut Window,
7351 cx: &mut Context<Self>,
7352 ) {
7353 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
7354 }
7355
7356 pub fn convert_to_opposite_case(
7357 &mut self,
7358 _: &ConvertToOppositeCase,
7359 window: &mut Window,
7360 cx: &mut Context<Self>,
7361 ) {
7362 self.manipulate_text(window, cx, |text| {
7363 text.chars()
7364 .fold(String::with_capacity(text.len()), |mut t, c| {
7365 if c.is_uppercase() {
7366 t.extend(c.to_lowercase());
7367 } else {
7368 t.extend(c.to_uppercase());
7369 }
7370 t
7371 })
7372 })
7373 }
7374
7375 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
7376 where
7377 Fn: FnMut(&str) -> String,
7378 {
7379 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7380 let buffer = self.buffer.read(cx).snapshot(cx);
7381
7382 let mut new_selections = Vec::new();
7383 let mut edits = Vec::new();
7384 let mut selection_adjustment = 0i32;
7385
7386 for selection in self.selections.all::<usize>(cx) {
7387 let selection_is_empty = selection.is_empty();
7388
7389 let (start, end) = if selection_is_empty {
7390 let word_range = movement::surrounding_word(
7391 &display_map,
7392 selection.start.to_display_point(&display_map),
7393 );
7394 let start = word_range.start.to_offset(&display_map, Bias::Left);
7395 let end = word_range.end.to_offset(&display_map, Bias::Left);
7396 (start, end)
7397 } else {
7398 (selection.start, selection.end)
7399 };
7400
7401 let text = buffer.text_for_range(start..end).collect::<String>();
7402 let old_length = text.len() as i32;
7403 let text = callback(&text);
7404
7405 new_selections.push(Selection {
7406 start: (start as i32 - selection_adjustment) as usize,
7407 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
7408 goal: SelectionGoal::None,
7409 ..selection
7410 });
7411
7412 selection_adjustment += old_length - text.len() as i32;
7413
7414 edits.push((start..end, text));
7415 }
7416
7417 self.transact(window, cx, |this, window, cx| {
7418 this.buffer.update(cx, |buffer, cx| {
7419 buffer.edit(edits, None, cx);
7420 });
7421
7422 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7423 s.select(new_selections);
7424 });
7425
7426 this.request_autoscroll(Autoscroll::fit(), cx);
7427 });
7428 }
7429
7430 pub fn duplicate(
7431 &mut self,
7432 upwards: bool,
7433 whole_lines: bool,
7434 window: &mut Window,
7435 cx: &mut Context<Self>,
7436 ) {
7437 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7438 let buffer = &display_map.buffer_snapshot;
7439 let selections = self.selections.all::<Point>(cx);
7440
7441 let mut edits = Vec::new();
7442 let mut selections_iter = selections.iter().peekable();
7443 while let Some(selection) = selections_iter.next() {
7444 let mut rows = selection.spanned_rows(false, &display_map);
7445 // duplicate line-wise
7446 if whole_lines || selection.start == selection.end {
7447 // Avoid duplicating the same lines twice.
7448 while let Some(next_selection) = selections_iter.peek() {
7449 let next_rows = next_selection.spanned_rows(false, &display_map);
7450 if next_rows.start < rows.end {
7451 rows.end = next_rows.end;
7452 selections_iter.next().unwrap();
7453 } else {
7454 break;
7455 }
7456 }
7457
7458 // Copy the text from the selected row region and splice it either at the start
7459 // or end of the region.
7460 let start = Point::new(rows.start.0, 0);
7461 let end = Point::new(
7462 rows.end.previous_row().0,
7463 buffer.line_len(rows.end.previous_row()),
7464 );
7465 let text = buffer
7466 .text_for_range(start..end)
7467 .chain(Some("\n"))
7468 .collect::<String>();
7469 let insert_location = if upwards {
7470 Point::new(rows.end.0, 0)
7471 } else {
7472 start
7473 };
7474 edits.push((insert_location..insert_location, text));
7475 } else {
7476 // duplicate character-wise
7477 let start = selection.start;
7478 let end = selection.end;
7479 let text = buffer.text_for_range(start..end).collect::<String>();
7480 edits.push((selection.end..selection.end, text));
7481 }
7482 }
7483
7484 self.transact(window, cx, |this, _, cx| {
7485 this.buffer.update(cx, |buffer, cx| {
7486 buffer.edit(edits, None, cx);
7487 });
7488
7489 this.request_autoscroll(Autoscroll::fit(), cx);
7490 });
7491 }
7492
7493 pub fn duplicate_line_up(
7494 &mut self,
7495 _: &DuplicateLineUp,
7496 window: &mut Window,
7497 cx: &mut Context<Self>,
7498 ) {
7499 self.duplicate(true, true, window, cx);
7500 }
7501
7502 pub fn duplicate_line_down(
7503 &mut self,
7504 _: &DuplicateLineDown,
7505 window: &mut Window,
7506 cx: &mut Context<Self>,
7507 ) {
7508 self.duplicate(false, true, window, cx);
7509 }
7510
7511 pub fn duplicate_selection(
7512 &mut self,
7513 _: &DuplicateSelection,
7514 window: &mut Window,
7515 cx: &mut Context<Self>,
7516 ) {
7517 self.duplicate(false, false, window, cx);
7518 }
7519
7520 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
7521 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7522 let buffer = self.buffer.read(cx).snapshot(cx);
7523
7524 let mut edits = Vec::new();
7525 let mut unfold_ranges = Vec::new();
7526 let mut refold_creases = Vec::new();
7527
7528 let selections = self.selections.all::<Point>(cx);
7529 let mut selections = selections.iter().peekable();
7530 let mut contiguous_row_selections = Vec::new();
7531 let mut new_selections = Vec::new();
7532
7533 while let Some(selection) = selections.next() {
7534 // Find all the selections that span a contiguous row range
7535 let (start_row, end_row) = consume_contiguous_rows(
7536 &mut contiguous_row_selections,
7537 selection,
7538 &display_map,
7539 &mut selections,
7540 );
7541
7542 // Move the text spanned by the row range to be before the line preceding the row range
7543 if start_row.0 > 0 {
7544 let range_to_move = Point::new(
7545 start_row.previous_row().0,
7546 buffer.line_len(start_row.previous_row()),
7547 )
7548 ..Point::new(
7549 end_row.previous_row().0,
7550 buffer.line_len(end_row.previous_row()),
7551 );
7552 let insertion_point = display_map
7553 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
7554 .0;
7555
7556 // Don't move lines across excerpts
7557 if buffer
7558 .excerpt_containing(insertion_point..range_to_move.end)
7559 .is_some()
7560 {
7561 let text = buffer
7562 .text_for_range(range_to_move.clone())
7563 .flat_map(|s| s.chars())
7564 .skip(1)
7565 .chain(['\n'])
7566 .collect::<String>();
7567
7568 edits.push((
7569 buffer.anchor_after(range_to_move.start)
7570 ..buffer.anchor_before(range_to_move.end),
7571 String::new(),
7572 ));
7573 let insertion_anchor = buffer.anchor_after(insertion_point);
7574 edits.push((insertion_anchor..insertion_anchor, text));
7575
7576 let row_delta = range_to_move.start.row - insertion_point.row + 1;
7577
7578 // Move selections up
7579 new_selections.extend(contiguous_row_selections.drain(..).map(
7580 |mut selection| {
7581 selection.start.row -= row_delta;
7582 selection.end.row -= row_delta;
7583 selection
7584 },
7585 ));
7586
7587 // Move folds up
7588 unfold_ranges.push(range_to_move.clone());
7589 for fold in display_map.folds_in_range(
7590 buffer.anchor_before(range_to_move.start)
7591 ..buffer.anchor_after(range_to_move.end),
7592 ) {
7593 let mut start = fold.range.start.to_point(&buffer);
7594 let mut end = fold.range.end.to_point(&buffer);
7595 start.row -= row_delta;
7596 end.row -= row_delta;
7597 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
7598 }
7599 }
7600 }
7601
7602 // If we didn't move line(s), preserve the existing selections
7603 new_selections.append(&mut contiguous_row_selections);
7604 }
7605
7606 self.transact(window, cx, |this, window, cx| {
7607 this.unfold_ranges(&unfold_ranges, true, true, cx);
7608 this.buffer.update(cx, |buffer, cx| {
7609 for (range, text) in edits {
7610 buffer.edit([(range, text)], None, cx);
7611 }
7612 });
7613 this.fold_creases(refold_creases, true, window, cx);
7614 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7615 s.select(new_selections);
7616 })
7617 });
7618 }
7619
7620 pub fn move_line_down(
7621 &mut self,
7622 _: &MoveLineDown,
7623 window: &mut Window,
7624 cx: &mut Context<Self>,
7625 ) {
7626 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7627 let buffer = self.buffer.read(cx).snapshot(cx);
7628
7629 let mut edits = Vec::new();
7630 let mut unfold_ranges = Vec::new();
7631 let mut refold_creases = Vec::new();
7632
7633 let selections = self.selections.all::<Point>(cx);
7634 let mut selections = selections.iter().peekable();
7635 let mut contiguous_row_selections = Vec::new();
7636 let mut new_selections = Vec::new();
7637
7638 while let Some(selection) = selections.next() {
7639 // Find all the selections that span a contiguous row range
7640 let (start_row, end_row) = consume_contiguous_rows(
7641 &mut contiguous_row_selections,
7642 selection,
7643 &display_map,
7644 &mut selections,
7645 );
7646
7647 // Move the text spanned by the row range to be after the last line of the row range
7648 if end_row.0 <= buffer.max_point().row {
7649 let range_to_move =
7650 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
7651 let insertion_point = display_map
7652 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
7653 .0;
7654
7655 // Don't move lines across excerpt boundaries
7656 if buffer
7657 .excerpt_containing(range_to_move.start..insertion_point)
7658 .is_some()
7659 {
7660 let mut text = String::from("\n");
7661 text.extend(buffer.text_for_range(range_to_move.clone()));
7662 text.pop(); // Drop trailing newline
7663 edits.push((
7664 buffer.anchor_after(range_to_move.start)
7665 ..buffer.anchor_before(range_to_move.end),
7666 String::new(),
7667 ));
7668 let insertion_anchor = buffer.anchor_after(insertion_point);
7669 edits.push((insertion_anchor..insertion_anchor, text));
7670
7671 let row_delta = insertion_point.row - range_to_move.end.row + 1;
7672
7673 // Move selections down
7674 new_selections.extend(contiguous_row_selections.drain(..).map(
7675 |mut selection| {
7676 selection.start.row += row_delta;
7677 selection.end.row += row_delta;
7678 selection
7679 },
7680 ));
7681
7682 // Move folds down
7683 unfold_ranges.push(range_to_move.clone());
7684 for fold in display_map.folds_in_range(
7685 buffer.anchor_before(range_to_move.start)
7686 ..buffer.anchor_after(range_to_move.end),
7687 ) {
7688 let mut start = fold.range.start.to_point(&buffer);
7689 let mut end = fold.range.end.to_point(&buffer);
7690 start.row += row_delta;
7691 end.row += row_delta;
7692 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
7693 }
7694 }
7695 }
7696
7697 // If we didn't move line(s), preserve the existing selections
7698 new_selections.append(&mut contiguous_row_selections);
7699 }
7700
7701 self.transact(window, cx, |this, window, cx| {
7702 this.unfold_ranges(&unfold_ranges, true, true, cx);
7703 this.buffer.update(cx, |buffer, cx| {
7704 for (range, text) in edits {
7705 buffer.edit([(range, text)], None, cx);
7706 }
7707 });
7708 this.fold_creases(refold_creases, true, window, cx);
7709 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7710 s.select(new_selections)
7711 });
7712 });
7713 }
7714
7715 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
7716 let text_layout_details = &self.text_layout_details(window);
7717 self.transact(window, cx, |this, window, cx| {
7718 let edits = this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7719 let mut edits: Vec<(Range<usize>, String)> = Default::default();
7720 let line_mode = s.line_mode;
7721 s.move_with(|display_map, selection| {
7722 if !selection.is_empty() || line_mode {
7723 return;
7724 }
7725
7726 let mut head = selection.head();
7727 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
7728 if head.column() == display_map.line_len(head.row()) {
7729 transpose_offset = display_map
7730 .buffer_snapshot
7731 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
7732 }
7733
7734 if transpose_offset == 0 {
7735 return;
7736 }
7737
7738 *head.column_mut() += 1;
7739 head = display_map.clip_point(head, Bias::Right);
7740 let goal = SelectionGoal::HorizontalPosition(
7741 display_map
7742 .x_for_display_point(head, text_layout_details)
7743 .into(),
7744 );
7745 selection.collapse_to(head, goal);
7746
7747 let transpose_start = display_map
7748 .buffer_snapshot
7749 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
7750 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
7751 let transpose_end = display_map
7752 .buffer_snapshot
7753 .clip_offset(transpose_offset + 1, Bias::Right);
7754 if let Some(ch) =
7755 display_map.buffer_snapshot.chars_at(transpose_start).next()
7756 {
7757 edits.push((transpose_start..transpose_offset, String::new()));
7758 edits.push((transpose_end..transpose_end, ch.to_string()));
7759 }
7760 }
7761 });
7762 edits
7763 });
7764 this.buffer
7765 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
7766 let selections = this.selections.all::<usize>(cx);
7767 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7768 s.select(selections);
7769 });
7770 });
7771 }
7772
7773 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
7774 self.rewrap_impl(IsVimMode::No, cx)
7775 }
7776
7777 pub fn rewrap_impl(&mut self, is_vim_mode: IsVimMode, cx: &mut Context<Self>) {
7778 let buffer = self.buffer.read(cx).snapshot(cx);
7779 let selections = self.selections.all::<Point>(cx);
7780 let mut selections = selections.iter().peekable();
7781
7782 let mut edits = Vec::new();
7783 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
7784
7785 while let Some(selection) = selections.next() {
7786 let mut start_row = selection.start.row;
7787 let mut end_row = selection.end.row;
7788
7789 // Skip selections that overlap with a range that has already been rewrapped.
7790 let selection_range = start_row..end_row;
7791 if rewrapped_row_ranges
7792 .iter()
7793 .any(|range| range.overlaps(&selection_range))
7794 {
7795 continue;
7796 }
7797
7798 let mut should_rewrap = is_vim_mode == IsVimMode::Yes;
7799
7800 if let Some(language_scope) = buffer.language_scope_at(selection.head()) {
7801 match language_scope.language_name().as_ref() {
7802 "Markdown" | "Plain Text" => {
7803 should_rewrap = true;
7804 }
7805 _ => {}
7806 }
7807 }
7808
7809 let tab_size = buffer.settings_at(selection.head(), cx).tab_size;
7810
7811 // Since not all lines in the selection may be at the same indent
7812 // level, choose the indent size that is the most common between all
7813 // of the lines.
7814 //
7815 // If there is a tie, we use the deepest indent.
7816 let (indent_size, indent_end) = {
7817 let mut indent_size_occurrences = HashMap::default();
7818 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
7819
7820 for row in start_row..=end_row {
7821 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
7822 rows_by_indent_size.entry(indent).or_default().push(row);
7823 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
7824 }
7825
7826 let indent_size = indent_size_occurrences
7827 .into_iter()
7828 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
7829 .map(|(indent, _)| indent)
7830 .unwrap_or_default();
7831 let row = rows_by_indent_size[&indent_size][0];
7832 let indent_end = Point::new(row, indent_size.len);
7833
7834 (indent_size, indent_end)
7835 };
7836
7837 let mut line_prefix = indent_size.chars().collect::<String>();
7838
7839 if let Some(comment_prefix) =
7840 buffer
7841 .language_scope_at(selection.head())
7842 .and_then(|language| {
7843 language
7844 .line_comment_prefixes()
7845 .iter()
7846 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
7847 .cloned()
7848 })
7849 {
7850 line_prefix.push_str(&comment_prefix);
7851 should_rewrap = true;
7852 }
7853
7854 if !should_rewrap {
7855 continue;
7856 }
7857
7858 if selection.is_empty() {
7859 'expand_upwards: while start_row > 0 {
7860 let prev_row = start_row - 1;
7861 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
7862 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
7863 {
7864 start_row = prev_row;
7865 } else {
7866 break 'expand_upwards;
7867 }
7868 }
7869
7870 'expand_downwards: while end_row < buffer.max_point().row {
7871 let next_row = end_row + 1;
7872 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
7873 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
7874 {
7875 end_row = next_row;
7876 } else {
7877 break 'expand_downwards;
7878 }
7879 }
7880 }
7881
7882 let start = Point::new(start_row, 0);
7883 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
7884 let selection_text = buffer.text_for_range(start..end).collect::<String>();
7885 let Some(lines_without_prefixes) = selection_text
7886 .lines()
7887 .map(|line| {
7888 line.strip_prefix(&line_prefix)
7889 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
7890 .ok_or_else(|| {
7891 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
7892 })
7893 })
7894 .collect::<Result<Vec<_>, _>>()
7895 .log_err()
7896 else {
7897 continue;
7898 };
7899
7900 let wrap_column = buffer
7901 .settings_at(Point::new(start_row, 0), cx)
7902 .preferred_line_length as usize;
7903 let wrapped_text = wrap_with_prefix(
7904 line_prefix,
7905 lines_without_prefixes.join(" "),
7906 wrap_column,
7907 tab_size,
7908 );
7909
7910 // TODO: should always use char-based diff while still supporting cursor behavior that
7911 // matches vim.
7912 let diff = match is_vim_mode {
7913 IsVimMode::Yes => TextDiff::from_lines(&selection_text, &wrapped_text),
7914 IsVimMode::No => TextDiff::from_chars(&selection_text, &wrapped_text),
7915 };
7916 let mut offset = start.to_offset(&buffer);
7917 let mut moved_since_edit = true;
7918
7919 for change in diff.iter_all_changes() {
7920 let value = change.value();
7921 match change.tag() {
7922 ChangeTag::Equal => {
7923 offset += value.len();
7924 moved_since_edit = true;
7925 }
7926 ChangeTag::Delete => {
7927 let start = buffer.anchor_after(offset);
7928 let end = buffer.anchor_before(offset + value.len());
7929
7930 if moved_since_edit {
7931 edits.push((start..end, String::new()));
7932 } else {
7933 edits.last_mut().unwrap().0.end = end;
7934 }
7935
7936 offset += value.len();
7937 moved_since_edit = false;
7938 }
7939 ChangeTag::Insert => {
7940 if moved_since_edit {
7941 let anchor = buffer.anchor_after(offset);
7942 edits.push((anchor..anchor, value.to_string()));
7943 } else {
7944 edits.last_mut().unwrap().1.push_str(value);
7945 }
7946
7947 moved_since_edit = false;
7948 }
7949 }
7950 }
7951
7952 rewrapped_row_ranges.push(start_row..=end_row);
7953 }
7954
7955 self.buffer
7956 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
7957 }
7958
7959 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
7960 let mut text = String::new();
7961 let buffer = self.buffer.read(cx).snapshot(cx);
7962 let mut selections = self.selections.all::<Point>(cx);
7963 let mut clipboard_selections = Vec::with_capacity(selections.len());
7964 {
7965 let max_point = buffer.max_point();
7966 let mut is_first = true;
7967 for selection in &mut selections {
7968 let is_entire_line = selection.is_empty() || self.selections.line_mode;
7969 if is_entire_line {
7970 selection.start = Point::new(selection.start.row, 0);
7971 if !selection.is_empty() && selection.end.column == 0 {
7972 selection.end = cmp::min(max_point, selection.end);
7973 } else {
7974 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
7975 }
7976 selection.goal = SelectionGoal::None;
7977 }
7978 if is_first {
7979 is_first = false;
7980 } else {
7981 text += "\n";
7982 }
7983 let mut len = 0;
7984 for chunk in buffer.text_for_range(selection.start..selection.end) {
7985 text.push_str(chunk);
7986 len += chunk.len();
7987 }
7988 clipboard_selections.push(ClipboardSelection {
7989 len,
7990 is_entire_line,
7991 first_line_indent: buffer
7992 .indent_size_for_line(MultiBufferRow(selection.start.row))
7993 .len,
7994 });
7995 }
7996 }
7997
7998 self.transact(window, cx, |this, window, cx| {
7999 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8000 s.select(selections);
8001 });
8002 this.insert("", window, cx);
8003 });
8004 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
8005 }
8006
8007 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
8008 let item = self.cut_common(window, cx);
8009 cx.write_to_clipboard(item);
8010 }
8011
8012 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
8013 self.change_selections(None, window, cx, |s| {
8014 s.move_with(|snapshot, sel| {
8015 if sel.is_empty() {
8016 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
8017 }
8018 });
8019 });
8020 let item = self.cut_common(window, cx);
8021 cx.set_global(KillRing(item))
8022 }
8023
8024 pub fn kill_ring_yank(
8025 &mut self,
8026 _: &KillRingYank,
8027 window: &mut Window,
8028 cx: &mut Context<Self>,
8029 ) {
8030 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
8031 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
8032 (kill_ring.text().to_string(), kill_ring.metadata_json())
8033 } else {
8034 return;
8035 }
8036 } else {
8037 return;
8038 };
8039 self.do_paste(&text, metadata, false, window, cx);
8040 }
8041
8042 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
8043 let selections = self.selections.all::<Point>(cx);
8044 let buffer = self.buffer.read(cx).read(cx);
8045 let mut text = String::new();
8046
8047 let mut clipboard_selections = Vec::with_capacity(selections.len());
8048 {
8049 let max_point = buffer.max_point();
8050 let mut is_first = true;
8051 for selection in selections.iter() {
8052 let mut start = selection.start;
8053 let mut end = selection.end;
8054 let is_entire_line = selection.is_empty() || self.selections.line_mode;
8055 if is_entire_line {
8056 start = Point::new(start.row, 0);
8057 end = cmp::min(max_point, Point::new(end.row + 1, 0));
8058 }
8059 if is_first {
8060 is_first = false;
8061 } else {
8062 text += "\n";
8063 }
8064 let mut len = 0;
8065 for chunk in buffer.text_for_range(start..end) {
8066 text.push_str(chunk);
8067 len += chunk.len();
8068 }
8069 clipboard_selections.push(ClipboardSelection {
8070 len,
8071 is_entire_line,
8072 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
8073 });
8074 }
8075 }
8076
8077 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
8078 text,
8079 clipboard_selections,
8080 ));
8081 }
8082
8083 pub fn do_paste(
8084 &mut self,
8085 text: &String,
8086 clipboard_selections: Option<Vec<ClipboardSelection>>,
8087 handle_entire_lines: bool,
8088 window: &mut Window,
8089 cx: &mut Context<Self>,
8090 ) {
8091 if self.read_only(cx) {
8092 return;
8093 }
8094
8095 let clipboard_text = Cow::Borrowed(text);
8096
8097 self.transact(window, cx, |this, window, cx| {
8098 if let Some(mut clipboard_selections) = clipboard_selections {
8099 let old_selections = this.selections.all::<usize>(cx);
8100 let all_selections_were_entire_line =
8101 clipboard_selections.iter().all(|s| s.is_entire_line);
8102 let first_selection_indent_column =
8103 clipboard_selections.first().map(|s| s.first_line_indent);
8104 if clipboard_selections.len() != old_selections.len() {
8105 clipboard_selections.drain(..);
8106 }
8107 let cursor_offset = this.selections.last::<usize>(cx).head();
8108 let mut auto_indent_on_paste = true;
8109
8110 this.buffer.update(cx, |buffer, cx| {
8111 let snapshot = buffer.read(cx);
8112 auto_indent_on_paste =
8113 snapshot.settings_at(cursor_offset, cx).auto_indent_on_paste;
8114
8115 let mut start_offset = 0;
8116 let mut edits = Vec::new();
8117 let mut original_indent_columns = Vec::new();
8118 for (ix, selection) in old_selections.iter().enumerate() {
8119 let to_insert;
8120 let entire_line;
8121 let original_indent_column;
8122 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
8123 let end_offset = start_offset + clipboard_selection.len;
8124 to_insert = &clipboard_text[start_offset..end_offset];
8125 entire_line = clipboard_selection.is_entire_line;
8126 start_offset = end_offset + 1;
8127 original_indent_column = Some(clipboard_selection.first_line_indent);
8128 } else {
8129 to_insert = clipboard_text.as_str();
8130 entire_line = all_selections_were_entire_line;
8131 original_indent_column = first_selection_indent_column
8132 }
8133
8134 // If the corresponding selection was empty when this slice of the
8135 // clipboard text was written, then the entire line containing the
8136 // selection was copied. If this selection is also currently empty,
8137 // then paste the line before the current line of the buffer.
8138 let range = if selection.is_empty() && handle_entire_lines && entire_line {
8139 let column = selection.start.to_point(&snapshot).column as usize;
8140 let line_start = selection.start - column;
8141 line_start..line_start
8142 } else {
8143 selection.range()
8144 };
8145
8146 edits.push((range, to_insert));
8147 original_indent_columns.extend(original_indent_column);
8148 }
8149 drop(snapshot);
8150
8151 buffer.edit(
8152 edits,
8153 if auto_indent_on_paste {
8154 Some(AutoindentMode::Block {
8155 original_indent_columns,
8156 })
8157 } else {
8158 None
8159 },
8160 cx,
8161 );
8162 });
8163
8164 let selections = this.selections.all::<usize>(cx);
8165 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8166 s.select(selections)
8167 });
8168 } else {
8169 this.insert(&clipboard_text, window, cx);
8170 }
8171 });
8172 }
8173
8174 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
8175 if let Some(item) = cx.read_from_clipboard() {
8176 let entries = item.entries();
8177
8178 match entries.first() {
8179 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
8180 // of all the pasted entries.
8181 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
8182 .do_paste(
8183 clipboard_string.text(),
8184 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
8185 true,
8186 window,
8187 cx,
8188 ),
8189 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
8190 }
8191 }
8192 }
8193
8194 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
8195 if self.read_only(cx) {
8196 return;
8197 }
8198
8199 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
8200 if let Some((selections, _)) =
8201 self.selection_history.transaction(transaction_id).cloned()
8202 {
8203 self.change_selections(None, window, cx, |s| {
8204 s.select_anchors(selections.to_vec());
8205 });
8206 }
8207 self.request_autoscroll(Autoscroll::fit(), cx);
8208 self.unmark_text(window, cx);
8209 self.refresh_inline_completion(true, false, window, cx);
8210 cx.emit(EditorEvent::Edited { transaction_id });
8211 cx.emit(EditorEvent::TransactionUndone { transaction_id });
8212 }
8213 }
8214
8215 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
8216 if self.read_only(cx) {
8217 return;
8218 }
8219
8220 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
8221 if let Some((_, Some(selections))) =
8222 self.selection_history.transaction(transaction_id).cloned()
8223 {
8224 self.change_selections(None, window, cx, |s| {
8225 s.select_anchors(selections.to_vec());
8226 });
8227 }
8228 self.request_autoscroll(Autoscroll::fit(), cx);
8229 self.unmark_text(window, cx);
8230 self.refresh_inline_completion(true, false, window, cx);
8231 cx.emit(EditorEvent::Edited { transaction_id });
8232 }
8233 }
8234
8235 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
8236 self.buffer
8237 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
8238 }
8239
8240 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
8241 self.buffer
8242 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
8243 }
8244
8245 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
8246 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8247 let line_mode = s.line_mode;
8248 s.move_with(|map, selection| {
8249 let cursor = if selection.is_empty() && !line_mode {
8250 movement::left(map, selection.start)
8251 } else {
8252 selection.start
8253 };
8254 selection.collapse_to(cursor, SelectionGoal::None);
8255 });
8256 })
8257 }
8258
8259 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
8260 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8261 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
8262 })
8263 }
8264
8265 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
8266 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8267 let line_mode = s.line_mode;
8268 s.move_with(|map, selection| {
8269 let cursor = if selection.is_empty() && !line_mode {
8270 movement::right(map, selection.end)
8271 } else {
8272 selection.end
8273 };
8274 selection.collapse_to(cursor, SelectionGoal::None)
8275 });
8276 })
8277 }
8278
8279 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
8280 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8281 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
8282 })
8283 }
8284
8285 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
8286 if self.take_rename(true, window, cx).is_some() {
8287 return;
8288 }
8289
8290 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8291 cx.propagate();
8292 return;
8293 }
8294
8295 let text_layout_details = &self.text_layout_details(window);
8296 let selection_count = self.selections.count();
8297 let first_selection = self.selections.first_anchor();
8298
8299 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8300 let line_mode = s.line_mode;
8301 s.move_with(|map, selection| {
8302 if !selection.is_empty() && !line_mode {
8303 selection.goal = SelectionGoal::None;
8304 }
8305 let (cursor, goal) = movement::up(
8306 map,
8307 selection.start,
8308 selection.goal,
8309 false,
8310 text_layout_details,
8311 );
8312 selection.collapse_to(cursor, goal);
8313 });
8314 });
8315
8316 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
8317 {
8318 cx.propagate();
8319 }
8320 }
8321
8322 pub fn move_up_by_lines(
8323 &mut self,
8324 action: &MoveUpByLines,
8325 window: &mut Window,
8326 cx: &mut Context<Self>,
8327 ) {
8328 if self.take_rename(true, window, cx).is_some() {
8329 return;
8330 }
8331
8332 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8333 cx.propagate();
8334 return;
8335 }
8336
8337 let text_layout_details = &self.text_layout_details(window);
8338
8339 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8340 let line_mode = s.line_mode;
8341 s.move_with(|map, selection| {
8342 if !selection.is_empty() && !line_mode {
8343 selection.goal = SelectionGoal::None;
8344 }
8345 let (cursor, goal) = movement::up_by_rows(
8346 map,
8347 selection.start,
8348 action.lines,
8349 selection.goal,
8350 false,
8351 text_layout_details,
8352 );
8353 selection.collapse_to(cursor, goal);
8354 });
8355 })
8356 }
8357
8358 pub fn move_down_by_lines(
8359 &mut self,
8360 action: &MoveDownByLines,
8361 window: &mut Window,
8362 cx: &mut Context<Self>,
8363 ) {
8364 if self.take_rename(true, window, cx).is_some() {
8365 return;
8366 }
8367
8368 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8369 cx.propagate();
8370 return;
8371 }
8372
8373 let text_layout_details = &self.text_layout_details(window);
8374
8375 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8376 let line_mode = s.line_mode;
8377 s.move_with(|map, selection| {
8378 if !selection.is_empty() && !line_mode {
8379 selection.goal = SelectionGoal::None;
8380 }
8381 let (cursor, goal) = movement::down_by_rows(
8382 map,
8383 selection.start,
8384 action.lines,
8385 selection.goal,
8386 false,
8387 text_layout_details,
8388 );
8389 selection.collapse_to(cursor, goal);
8390 });
8391 })
8392 }
8393
8394 pub fn select_down_by_lines(
8395 &mut self,
8396 action: &SelectDownByLines,
8397 window: &mut Window,
8398 cx: &mut Context<Self>,
8399 ) {
8400 let text_layout_details = &self.text_layout_details(window);
8401 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8402 s.move_heads_with(|map, head, goal| {
8403 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
8404 })
8405 })
8406 }
8407
8408 pub fn select_up_by_lines(
8409 &mut self,
8410 action: &SelectUpByLines,
8411 window: &mut Window,
8412 cx: &mut Context<Self>,
8413 ) {
8414 let text_layout_details = &self.text_layout_details(window);
8415 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8416 s.move_heads_with(|map, head, goal| {
8417 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
8418 })
8419 })
8420 }
8421
8422 pub fn select_page_up(
8423 &mut self,
8424 _: &SelectPageUp,
8425 window: &mut Window,
8426 cx: &mut Context<Self>,
8427 ) {
8428 let Some(row_count) = self.visible_row_count() else {
8429 return;
8430 };
8431
8432 let text_layout_details = &self.text_layout_details(window);
8433
8434 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8435 s.move_heads_with(|map, head, goal| {
8436 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
8437 })
8438 })
8439 }
8440
8441 pub fn move_page_up(
8442 &mut self,
8443 action: &MovePageUp,
8444 window: &mut Window,
8445 cx: &mut Context<Self>,
8446 ) {
8447 if self.take_rename(true, window, cx).is_some() {
8448 return;
8449 }
8450
8451 if self
8452 .context_menu
8453 .borrow_mut()
8454 .as_mut()
8455 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
8456 .unwrap_or(false)
8457 {
8458 return;
8459 }
8460
8461 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8462 cx.propagate();
8463 return;
8464 }
8465
8466 let Some(row_count) = self.visible_row_count() else {
8467 return;
8468 };
8469
8470 let autoscroll = if action.center_cursor {
8471 Autoscroll::center()
8472 } else {
8473 Autoscroll::fit()
8474 };
8475
8476 let text_layout_details = &self.text_layout_details(window);
8477
8478 self.change_selections(Some(autoscroll), window, cx, |s| {
8479 let line_mode = s.line_mode;
8480 s.move_with(|map, selection| {
8481 if !selection.is_empty() && !line_mode {
8482 selection.goal = SelectionGoal::None;
8483 }
8484 let (cursor, goal) = movement::up_by_rows(
8485 map,
8486 selection.end,
8487 row_count,
8488 selection.goal,
8489 false,
8490 text_layout_details,
8491 );
8492 selection.collapse_to(cursor, goal);
8493 });
8494 });
8495 }
8496
8497 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
8498 let text_layout_details = &self.text_layout_details(window);
8499 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8500 s.move_heads_with(|map, head, goal| {
8501 movement::up(map, head, goal, false, text_layout_details)
8502 })
8503 })
8504 }
8505
8506 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
8507 self.take_rename(true, window, cx);
8508
8509 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8510 cx.propagate();
8511 return;
8512 }
8513
8514 let text_layout_details = &self.text_layout_details(window);
8515 let selection_count = self.selections.count();
8516 let first_selection = self.selections.first_anchor();
8517
8518 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8519 let line_mode = s.line_mode;
8520 s.move_with(|map, selection| {
8521 if !selection.is_empty() && !line_mode {
8522 selection.goal = SelectionGoal::None;
8523 }
8524 let (cursor, goal) = movement::down(
8525 map,
8526 selection.end,
8527 selection.goal,
8528 false,
8529 text_layout_details,
8530 );
8531 selection.collapse_to(cursor, goal);
8532 });
8533 });
8534
8535 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
8536 {
8537 cx.propagate();
8538 }
8539 }
8540
8541 pub fn select_page_down(
8542 &mut self,
8543 _: &SelectPageDown,
8544 window: &mut Window,
8545 cx: &mut Context<Self>,
8546 ) {
8547 let Some(row_count) = self.visible_row_count() else {
8548 return;
8549 };
8550
8551 let text_layout_details = &self.text_layout_details(window);
8552
8553 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8554 s.move_heads_with(|map, head, goal| {
8555 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
8556 })
8557 })
8558 }
8559
8560 pub fn move_page_down(
8561 &mut self,
8562 action: &MovePageDown,
8563 window: &mut Window,
8564 cx: &mut Context<Self>,
8565 ) {
8566 if self.take_rename(true, window, cx).is_some() {
8567 return;
8568 }
8569
8570 if self
8571 .context_menu
8572 .borrow_mut()
8573 .as_mut()
8574 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
8575 .unwrap_or(false)
8576 {
8577 return;
8578 }
8579
8580 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8581 cx.propagate();
8582 return;
8583 }
8584
8585 let Some(row_count) = self.visible_row_count() else {
8586 return;
8587 };
8588
8589 let autoscroll = if action.center_cursor {
8590 Autoscroll::center()
8591 } else {
8592 Autoscroll::fit()
8593 };
8594
8595 let text_layout_details = &self.text_layout_details(window);
8596 self.change_selections(Some(autoscroll), window, cx, |s| {
8597 let line_mode = s.line_mode;
8598 s.move_with(|map, selection| {
8599 if !selection.is_empty() && !line_mode {
8600 selection.goal = SelectionGoal::None;
8601 }
8602 let (cursor, goal) = movement::down_by_rows(
8603 map,
8604 selection.end,
8605 row_count,
8606 selection.goal,
8607 false,
8608 text_layout_details,
8609 );
8610 selection.collapse_to(cursor, goal);
8611 });
8612 });
8613 }
8614
8615 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
8616 let text_layout_details = &self.text_layout_details(window);
8617 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8618 s.move_heads_with(|map, head, goal| {
8619 movement::down(map, head, goal, false, text_layout_details)
8620 })
8621 });
8622 }
8623
8624 pub fn context_menu_first(
8625 &mut self,
8626 _: &ContextMenuFirst,
8627 _window: &mut Window,
8628 cx: &mut Context<Self>,
8629 ) {
8630 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8631 context_menu.select_first(self.completion_provider.as_deref(), cx);
8632 }
8633 }
8634
8635 pub fn context_menu_prev(
8636 &mut self,
8637 _: &ContextMenuPrev,
8638 _window: &mut Window,
8639 cx: &mut Context<Self>,
8640 ) {
8641 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8642 context_menu.select_prev(self.completion_provider.as_deref(), cx);
8643 }
8644 }
8645
8646 pub fn context_menu_next(
8647 &mut self,
8648 _: &ContextMenuNext,
8649 _window: &mut Window,
8650 cx: &mut Context<Self>,
8651 ) {
8652 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8653 context_menu.select_next(self.completion_provider.as_deref(), cx);
8654 }
8655 }
8656
8657 pub fn context_menu_last(
8658 &mut self,
8659 _: &ContextMenuLast,
8660 _window: &mut Window,
8661 cx: &mut Context<Self>,
8662 ) {
8663 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8664 context_menu.select_last(self.completion_provider.as_deref(), cx);
8665 }
8666 }
8667
8668 pub fn move_to_previous_word_start(
8669 &mut self,
8670 _: &MoveToPreviousWordStart,
8671 window: &mut Window,
8672 cx: &mut Context<Self>,
8673 ) {
8674 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8675 s.move_cursors_with(|map, head, _| {
8676 (
8677 movement::previous_word_start(map, head),
8678 SelectionGoal::None,
8679 )
8680 });
8681 })
8682 }
8683
8684 pub fn move_to_previous_subword_start(
8685 &mut self,
8686 _: &MoveToPreviousSubwordStart,
8687 window: &mut Window,
8688 cx: &mut Context<Self>,
8689 ) {
8690 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8691 s.move_cursors_with(|map, head, _| {
8692 (
8693 movement::previous_subword_start(map, head),
8694 SelectionGoal::None,
8695 )
8696 });
8697 })
8698 }
8699
8700 pub fn select_to_previous_word_start(
8701 &mut self,
8702 _: &SelectToPreviousWordStart,
8703 window: &mut Window,
8704 cx: &mut Context<Self>,
8705 ) {
8706 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8707 s.move_heads_with(|map, head, _| {
8708 (
8709 movement::previous_word_start(map, head),
8710 SelectionGoal::None,
8711 )
8712 });
8713 })
8714 }
8715
8716 pub fn select_to_previous_subword_start(
8717 &mut self,
8718 _: &SelectToPreviousSubwordStart,
8719 window: &mut Window,
8720 cx: &mut Context<Self>,
8721 ) {
8722 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8723 s.move_heads_with(|map, head, _| {
8724 (
8725 movement::previous_subword_start(map, head),
8726 SelectionGoal::None,
8727 )
8728 });
8729 })
8730 }
8731
8732 pub fn delete_to_previous_word_start(
8733 &mut self,
8734 action: &DeleteToPreviousWordStart,
8735 window: &mut Window,
8736 cx: &mut Context<Self>,
8737 ) {
8738 self.transact(window, cx, |this, window, cx| {
8739 this.select_autoclose_pair(window, cx);
8740 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8741 let line_mode = s.line_mode;
8742 s.move_with(|map, selection| {
8743 if selection.is_empty() && !line_mode {
8744 let cursor = if action.ignore_newlines {
8745 movement::previous_word_start(map, selection.head())
8746 } else {
8747 movement::previous_word_start_or_newline(map, selection.head())
8748 };
8749 selection.set_head(cursor, SelectionGoal::None);
8750 }
8751 });
8752 });
8753 this.insert("", window, cx);
8754 });
8755 }
8756
8757 pub fn delete_to_previous_subword_start(
8758 &mut self,
8759 _: &DeleteToPreviousSubwordStart,
8760 window: &mut Window,
8761 cx: &mut Context<Self>,
8762 ) {
8763 self.transact(window, cx, |this, window, cx| {
8764 this.select_autoclose_pair(window, cx);
8765 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8766 let line_mode = s.line_mode;
8767 s.move_with(|map, selection| {
8768 if selection.is_empty() && !line_mode {
8769 let cursor = movement::previous_subword_start(map, selection.head());
8770 selection.set_head(cursor, SelectionGoal::None);
8771 }
8772 });
8773 });
8774 this.insert("", window, cx);
8775 });
8776 }
8777
8778 pub fn move_to_next_word_end(
8779 &mut self,
8780 _: &MoveToNextWordEnd,
8781 window: &mut Window,
8782 cx: &mut Context<Self>,
8783 ) {
8784 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8785 s.move_cursors_with(|map, head, _| {
8786 (movement::next_word_end(map, head), SelectionGoal::None)
8787 });
8788 })
8789 }
8790
8791 pub fn move_to_next_subword_end(
8792 &mut self,
8793 _: &MoveToNextSubwordEnd,
8794 window: &mut Window,
8795 cx: &mut Context<Self>,
8796 ) {
8797 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8798 s.move_cursors_with(|map, head, _| {
8799 (movement::next_subword_end(map, head), SelectionGoal::None)
8800 });
8801 })
8802 }
8803
8804 pub fn select_to_next_word_end(
8805 &mut self,
8806 _: &SelectToNextWordEnd,
8807 window: &mut Window,
8808 cx: &mut Context<Self>,
8809 ) {
8810 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8811 s.move_heads_with(|map, head, _| {
8812 (movement::next_word_end(map, head), SelectionGoal::None)
8813 });
8814 })
8815 }
8816
8817 pub fn select_to_next_subword_end(
8818 &mut self,
8819 _: &SelectToNextSubwordEnd,
8820 window: &mut Window,
8821 cx: &mut Context<Self>,
8822 ) {
8823 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8824 s.move_heads_with(|map, head, _| {
8825 (movement::next_subword_end(map, head), SelectionGoal::None)
8826 });
8827 })
8828 }
8829
8830 pub fn delete_to_next_word_end(
8831 &mut self,
8832 action: &DeleteToNextWordEnd,
8833 window: &mut Window,
8834 cx: &mut Context<Self>,
8835 ) {
8836 self.transact(window, cx, |this, window, cx| {
8837 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8838 let line_mode = s.line_mode;
8839 s.move_with(|map, selection| {
8840 if selection.is_empty() && !line_mode {
8841 let cursor = if action.ignore_newlines {
8842 movement::next_word_end(map, selection.head())
8843 } else {
8844 movement::next_word_end_or_newline(map, selection.head())
8845 };
8846 selection.set_head(cursor, SelectionGoal::None);
8847 }
8848 });
8849 });
8850 this.insert("", window, cx);
8851 });
8852 }
8853
8854 pub fn delete_to_next_subword_end(
8855 &mut self,
8856 _: &DeleteToNextSubwordEnd,
8857 window: &mut Window,
8858 cx: &mut Context<Self>,
8859 ) {
8860 self.transact(window, cx, |this, window, cx| {
8861 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8862 s.move_with(|map, selection| {
8863 if selection.is_empty() {
8864 let cursor = movement::next_subword_end(map, selection.head());
8865 selection.set_head(cursor, SelectionGoal::None);
8866 }
8867 });
8868 });
8869 this.insert("", window, cx);
8870 });
8871 }
8872
8873 pub fn move_to_beginning_of_line(
8874 &mut self,
8875 action: &MoveToBeginningOfLine,
8876 window: &mut Window,
8877 cx: &mut Context<Self>,
8878 ) {
8879 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8880 s.move_cursors_with(|map, head, _| {
8881 (
8882 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
8883 SelectionGoal::None,
8884 )
8885 });
8886 })
8887 }
8888
8889 pub fn select_to_beginning_of_line(
8890 &mut self,
8891 action: &SelectToBeginningOfLine,
8892 window: &mut Window,
8893 cx: &mut Context<Self>,
8894 ) {
8895 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8896 s.move_heads_with(|map, head, _| {
8897 (
8898 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
8899 SelectionGoal::None,
8900 )
8901 });
8902 });
8903 }
8904
8905 pub fn delete_to_beginning_of_line(
8906 &mut self,
8907 _: &DeleteToBeginningOfLine,
8908 window: &mut Window,
8909 cx: &mut Context<Self>,
8910 ) {
8911 self.transact(window, cx, |this, window, cx| {
8912 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8913 s.move_with(|_, selection| {
8914 selection.reversed = true;
8915 });
8916 });
8917
8918 this.select_to_beginning_of_line(
8919 &SelectToBeginningOfLine {
8920 stop_at_soft_wraps: false,
8921 },
8922 window,
8923 cx,
8924 );
8925 this.backspace(&Backspace, window, cx);
8926 });
8927 }
8928
8929 pub fn move_to_end_of_line(
8930 &mut self,
8931 action: &MoveToEndOfLine,
8932 window: &mut Window,
8933 cx: &mut Context<Self>,
8934 ) {
8935 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8936 s.move_cursors_with(|map, head, _| {
8937 (
8938 movement::line_end(map, head, action.stop_at_soft_wraps),
8939 SelectionGoal::None,
8940 )
8941 });
8942 })
8943 }
8944
8945 pub fn select_to_end_of_line(
8946 &mut self,
8947 action: &SelectToEndOfLine,
8948 window: &mut Window,
8949 cx: &mut Context<Self>,
8950 ) {
8951 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8952 s.move_heads_with(|map, head, _| {
8953 (
8954 movement::line_end(map, head, action.stop_at_soft_wraps),
8955 SelectionGoal::None,
8956 )
8957 });
8958 })
8959 }
8960
8961 pub fn delete_to_end_of_line(
8962 &mut self,
8963 _: &DeleteToEndOfLine,
8964 window: &mut Window,
8965 cx: &mut Context<Self>,
8966 ) {
8967 self.transact(window, cx, |this, window, cx| {
8968 this.select_to_end_of_line(
8969 &SelectToEndOfLine {
8970 stop_at_soft_wraps: false,
8971 },
8972 window,
8973 cx,
8974 );
8975 this.delete(&Delete, window, cx);
8976 });
8977 }
8978
8979 pub fn cut_to_end_of_line(
8980 &mut self,
8981 _: &CutToEndOfLine,
8982 window: &mut Window,
8983 cx: &mut Context<Self>,
8984 ) {
8985 self.transact(window, cx, |this, window, cx| {
8986 this.select_to_end_of_line(
8987 &SelectToEndOfLine {
8988 stop_at_soft_wraps: false,
8989 },
8990 window,
8991 cx,
8992 );
8993 this.cut(&Cut, window, cx);
8994 });
8995 }
8996
8997 pub fn move_to_start_of_paragraph(
8998 &mut self,
8999 _: &MoveToStartOfParagraph,
9000 window: &mut Window,
9001 cx: &mut Context<Self>,
9002 ) {
9003 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9004 cx.propagate();
9005 return;
9006 }
9007
9008 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9009 s.move_with(|map, selection| {
9010 selection.collapse_to(
9011 movement::start_of_paragraph(map, selection.head(), 1),
9012 SelectionGoal::None,
9013 )
9014 });
9015 })
9016 }
9017
9018 pub fn move_to_end_of_paragraph(
9019 &mut self,
9020 _: &MoveToEndOfParagraph,
9021 window: &mut Window,
9022 cx: &mut Context<Self>,
9023 ) {
9024 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9025 cx.propagate();
9026 return;
9027 }
9028
9029 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9030 s.move_with(|map, selection| {
9031 selection.collapse_to(
9032 movement::end_of_paragraph(map, selection.head(), 1),
9033 SelectionGoal::None,
9034 )
9035 });
9036 })
9037 }
9038
9039 pub fn select_to_start_of_paragraph(
9040 &mut self,
9041 _: &SelectToStartOfParagraph,
9042 window: &mut Window,
9043 cx: &mut Context<Self>,
9044 ) {
9045 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9046 cx.propagate();
9047 return;
9048 }
9049
9050 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9051 s.move_heads_with(|map, head, _| {
9052 (
9053 movement::start_of_paragraph(map, head, 1),
9054 SelectionGoal::None,
9055 )
9056 });
9057 })
9058 }
9059
9060 pub fn select_to_end_of_paragraph(
9061 &mut self,
9062 _: &SelectToEndOfParagraph,
9063 window: &mut Window,
9064 cx: &mut Context<Self>,
9065 ) {
9066 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9067 cx.propagate();
9068 return;
9069 }
9070
9071 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9072 s.move_heads_with(|map, head, _| {
9073 (
9074 movement::end_of_paragraph(map, head, 1),
9075 SelectionGoal::None,
9076 )
9077 });
9078 })
9079 }
9080
9081 pub fn move_to_beginning(
9082 &mut self,
9083 _: &MoveToBeginning,
9084 window: &mut Window,
9085 cx: &mut Context<Self>,
9086 ) {
9087 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9088 cx.propagate();
9089 return;
9090 }
9091
9092 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9093 s.select_ranges(vec![0..0]);
9094 });
9095 }
9096
9097 pub fn select_to_beginning(
9098 &mut self,
9099 _: &SelectToBeginning,
9100 window: &mut Window,
9101 cx: &mut Context<Self>,
9102 ) {
9103 let mut selection = self.selections.last::<Point>(cx);
9104 selection.set_head(Point::zero(), SelectionGoal::None);
9105
9106 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9107 s.select(vec![selection]);
9108 });
9109 }
9110
9111 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
9112 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9113 cx.propagate();
9114 return;
9115 }
9116
9117 let cursor = self.buffer.read(cx).read(cx).len();
9118 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9119 s.select_ranges(vec![cursor..cursor])
9120 });
9121 }
9122
9123 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
9124 self.nav_history = nav_history;
9125 }
9126
9127 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
9128 self.nav_history.as_ref()
9129 }
9130
9131 fn push_to_nav_history(
9132 &mut self,
9133 cursor_anchor: Anchor,
9134 new_position: Option<Point>,
9135 cx: &mut Context<Self>,
9136 ) {
9137 if let Some(nav_history) = self.nav_history.as_mut() {
9138 let buffer = self.buffer.read(cx).read(cx);
9139 let cursor_position = cursor_anchor.to_point(&buffer);
9140 let scroll_state = self.scroll_manager.anchor();
9141 let scroll_top_row = scroll_state.top_row(&buffer);
9142 drop(buffer);
9143
9144 if let Some(new_position) = new_position {
9145 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
9146 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
9147 return;
9148 }
9149 }
9150
9151 nav_history.push(
9152 Some(NavigationData {
9153 cursor_anchor,
9154 cursor_position,
9155 scroll_anchor: scroll_state,
9156 scroll_top_row,
9157 }),
9158 cx,
9159 );
9160 }
9161 }
9162
9163 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
9164 let buffer = self.buffer.read(cx).snapshot(cx);
9165 let mut selection = self.selections.first::<usize>(cx);
9166 selection.set_head(buffer.len(), SelectionGoal::None);
9167 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9168 s.select(vec![selection]);
9169 });
9170 }
9171
9172 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
9173 let end = self.buffer.read(cx).read(cx).len();
9174 self.change_selections(None, window, cx, |s| {
9175 s.select_ranges(vec![0..end]);
9176 });
9177 }
9178
9179 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
9180 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9181 let mut selections = self.selections.all::<Point>(cx);
9182 let max_point = display_map.buffer_snapshot.max_point();
9183 for selection in &mut selections {
9184 let rows = selection.spanned_rows(true, &display_map);
9185 selection.start = Point::new(rows.start.0, 0);
9186 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
9187 selection.reversed = false;
9188 }
9189 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9190 s.select(selections);
9191 });
9192 }
9193
9194 pub fn split_selection_into_lines(
9195 &mut self,
9196 _: &SplitSelectionIntoLines,
9197 window: &mut Window,
9198 cx: &mut Context<Self>,
9199 ) {
9200 let mut to_unfold = Vec::new();
9201 let mut new_selection_ranges = Vec::new();
9202 {
9203 let selections = self.selections.all::<Point>(cx);
9204 let buffer = self.buffer.read(cx).read(cx);
9205 for selection in selections {
9206 for row in selection.start.row..selection.end.row {
9207 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
9208 new_selection_ranges.push(cursor..cursor);
9209 }
9210 new_selection_ranges.push(selection.end..selection.end);
9211 to_unfold.push(selection.start..selection.end);
9212 }
9213 }
9214 self.unfold_ranges(&to_unfold, true, true, cx);
9215 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9216 s.select_ranges(new_selection_ranges);
9217 });
9218 }
9219
9220 pub fn add_selection_above(
9221 &mut self,
9222 _: &AddSelectionAbove,
9223 window: &mut Window,
9224 cx: &mut Context<Self>,
9225 ) {
9226 self.add_selection(true, window, cx);
9227 }
9228
9229 pub fn add_selection_below(
9230 &mut self,
9231 _: &AddSelectionBelow,
9232 window: &mut Window,
9233 cx: &mut Context<Self>,
9234 ) {
9235 self.add_selection(false, window, cx);
9236 }
9237
9238 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
9239 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9240 let mut selections = self.selections.all::<Point>(cx);
9241 let text_layout_details = self.text_layout_details(window);
9242 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
9243 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
9244 let range = oldest_selection.display_range(&display_map).sorted();
9245
9246 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
9247 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
9248 let positions = start_x.min(end_x)..start_x.max(end_x);
9249
9250 selections.clear();
9251 let mut stack = Vec::new();
9252 for row in range.start.row().0..=range.end.row().0 {
9253 if let Some(selection) = self.selections.build_columnar_selection(
9254 &display_map,
9255 DisplayRow(row),
9256 &positions,
9257 oldest_selection.reversed,
9258 &text_layout_details,
9259 ) {
9260 stack.push(selection.id);
9261 selections.push(selection);
9262 }
9263 }
9264
9265 if above {
9266 stack.reverse();
9267 }
9268
9269 AddSelectionsState { above, stack }
9270 });
9271
9272 let last_added_selection = *state.stack.last().unwrap();
9273 let mut new_selections = Vec::new();
9274 if above == state.above {
9275 let end_row = if above {
9276 DisplayRow(0)
9277 } else {
9278 display_map.max_point().row()
9279 };
9280
9281 'outer: for selection in selections {
9282 if selection.id == last_added_selection {
9283 let range = selection.display_range(&display_map).sorted();
9284 debug_assert_eq!(range.start.row(), range.end.row());
9285 let mut row = range.start.row();
9286 let positions =
9287 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
9288 px(start)..px(end)
9289 } else {
9290 let start_x =
9291 display_map.x_for_display_point(range.start, &text_layout_details);
9292 let end_x =
9293 display_map.x_for_display_point(range.end, &text_layout_details);
9294 start_x.min(end_x)..start_x.max(end_x)
9295 };
9296
9297 while row != end_row {
9298 if above {
9299 row.0 -= 1;
9300 } else {
9301 row.0 += 1;
9302 }
9303
9304 if let Some(new_selection) = self.selections.build_columnar_selection(
9305 &display_map,
9306 row,
9307 &positions,
9308 selection.reversed,
9309 &text_layout_details,
9310 ) {
9311 state.stack.push(new_selection.id);
9312 if above {
9313 new_selections.push(new_selection);
9314 new_selections.push(selection);
9315 } else {
9316 new_selections.push(selection);
9317 new_selections.push(new_selection);
9318 }
9319
9320 continue 'outer;
9321 }
9322 }
9323 }
9324
9325 new_selections.push(selection);
9326 }
9327 } else {
9328 new_selections = selections;
9329 new_selections.retain(|s| s.id != last_added_selection);
9330 state.stack.pop();
9331 }
9332
9333 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9334 s.select(new_selections);
9335 });
9336 if state.stack.len() > 1 {
9337 self.add_selections_state = Some(state);
9338 }
9339 }
9340
9341 pub fn select_next_match_internal(
9342 &mut self,
9343 display_map: &DisplaySnapshot,
9344 replace_newest: bool,
9345 autoscroll: Option<Autoscroll>,
9346 window: &mut Window,
9347 cx: &mut Context<Self>,
9348 ) -> Result<()> {
9349 fn select_next_match_ranges(
9350 this: &mut Editor,
9351 range: Range<usize>,
9352 replace_newest: bool,
9353 auto_scroll: Option<Autoscroll>,
9354 window: &mut Window,
9355 cx: &mut Context<Editor>,
9356 ) {
9357 this.unfold_ranges(&[range.clone()], false, true, cx);
9358 this.change_selections(auto_scroll, window, cx, |s| {
9359 if replace_newest {
9360 s.delete(s.newest_anchor().id);
9361 }
9362 s.insert_range(range.clone());
9363 });
9364 }
9365
9366 let buffer = &display_map.buffer_snapshot;
9367 let mut selections = self.selections.all::<usize>(cx);
9368 if let Some(mut select_next_state) = self.select_next_state.take() {
9369 let query = &select_next_state.query;
9370 if !select_next_state.done {
9371 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
9372 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
9373 let mut next_selected_range = None;
9374
9375 let bytes_after_last_selection =
9376 buffer.bytes_in_range(last_selection.end..buffer.len());
9377 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
9378 let query_matches = query
9379 .stream_find_iter(bytes_after_last_selection)
9380 .map(|result| (last_selection.end, result))
9381 .chain(
9382 query
9383 .stream_find_iter(bytes_before_first_selection)
9384 .map(|result| (0, result)),
9385 );
9386
9387 for (start_offset, query_match) in query_matches {
9388 let query_match = query_match.unwrap(); // can only fail due to I/O
9389 let offset_range =
9390 start_offset + query_match.start()..start_offset + query_match.end();
9391 let display_range = offset_range.start.to_display_point(display_map)
9392 ..offset_range.end.to_display_point(display_map);
9393
9394 if !select_next_state.wordwise
9395 || (!movement::is_inside_word(display_map, display_range.start)
9396 && !movement::is_inside_word(display_map, display_range.end))
9397 {
9398 // TODO: This is n^2, because we might check all the selections
9399 if !selections
9400 .iter()
9401 .any(|selection| selection.range().overlaps(&offset_range))
9402 {
9403 next_selected_range = Some(offset_range);
9404 break;
9405 }
9406 }
9407 }
9408
9409 if let Some(next_selected_range) = next_selected_range {
9410 select_next_match_ranges(
9411 self,
9412 next_selected_range,
9413 replace_newest,
9414 autoscroll,
9415 window,
9416 cx,
9417 );
9418 } else {
9419 select_next_state.done = true;
9420 }
9421 }
9422
9423 self.select_next_state = Some(select_next_state);
9424 } else {
9425 let mut only_carets = true;
9426 let mut same_text_selected = true;
9427 let mut selected_text = None;
9428
9429 let mut selections_iter = selections.iter().peekable();
9430 while let Some(selection) = selections_iter.next() {
9431 if selection.start != selection.end {
9432 only_carets = false;
9433 }
9434
9435 if same_text_selected {
9436 if selected_text.is_none() {
9437 selected_text =
9438 Some(buffer.text_for_range(selection.range()).collect::<String>());
9439 }
9440
9441 if let Some(next_selection) = selections_iter.peek() {
9442 if next_selection.range().len() == selection.range().len() {
9443 let next_selected_text = buffer
9444 .text_for_range(next_selection.range())
9445 .collect::<String>();
9446 if Some(next_selected_text) != selected_text {
9447 same_text_selected = false;
9448 selected_text = None;
9449 }
9450 } else {
9451 same_text_selected = false;
9452 selected_text = None;
9453 }
9454 }
9455 }
9456 }
9457
9458 if only_carets {
9459 for selection in &mut selections {
9460 let word_range = movement::surrounding_word(
9461 display_map,
9462 selection.start.to_display_point(display_map),
9463 );
9464 selection.start = word_range.start.to_offset(display_map, Bias::Left);
9465 selection.end = word_range.end.to_offset(display_map, Bias::Left);
9466 selection.goal = SelectionGoal::None;
9467 selection.reversed = false;
9468 select_next_match_ranges(
9469 self,
9470 selection.start..selection.end,
9471 replace_newest,
9472 autoscroll,
9473 window,
9474 cx,
9475 );
9476 }
9477
9478 if selections.len() == 1 {
9479 let selection = selections
9480 .last()
9481 .expect("ensured that there's only one selection");
9482 let query = buffer
9483 .text_for_range(selection.start..selection.end)
9484 .collect::<String>();
9485 let is_empty = query.is_empty();
9486 let select_state = SelectNextState {
9487 query: AhoCorasick::new(&[query])?,
9488 wordwise: true,
9489 done: is_empty,
9490 };
9491 self.select_next_state = Some(select_state);
9492 } else {
9493 self.select_next_state = None;
9494 }
9495 } else if let Some(selected_text) = selected_text {
9496 self.select_next_state = Some(SelectNextState {
9497 query: AhoCorasick::new(&[selected_text])?,
9498 wordwise: false,
9499 done: false,
9500 });
9501 self.select_next_match_internal(
9502 display_map,
9503 replace_newest,
9504 autoscroll,
9505 window,
9506 cx,
9507 )?;
9508 }
9509 }
9510 Ok(())
9511 }
9512
9513 pub fn select_all_matches(
9514 &mut self,
9515 _action: &SelectAllMatches,
9516 window: &mut Window,
9517 cx: &mut Context<Self>,
9518 ) -> Result<()> {
9519 self.push_to_selection_history();
9520 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9521
9522 self.select_next_match_internal(&display_map, false, None, window, cx)?;
9523 let Some(select_next_state) = self.select_next_state.as_mut() else {
9524 return Ok(());
9525 };
9526 if select_next_state.done {
9527 return Ok(());
9528 }
9529
9530 let mut new_selections = self.selections.all::<usize>(cx);
9531
9532 let buffer = &display_map.buffer_snapshot;
9533 let query_matches = select_next_state
9534 .query
9535 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
9536
9537 for query_match in query_matches {
9538 let query_match = query_match.unwrap(); // can only fail due to I/O
9539 let offset_range = query_match.start()..query_match.end();
9540 let display_range = offset_range.start.to_display_point(&display_map)
9541 ..offset_range.end.to_display_point(&display_map);
9542
9543 if !select_next_state.wordwise
9544 || (!movement::is_inside_word(&display_map, display_range.start)
9545 && !movement::is_inside_word(&display_map, display_range.end))
9546 {
9547 self.selections.change_with(cx, |selections| {
9548 new_selections.push(Selection {
9549 id: selections.new_selection_id(),
9550 start: offset_range.start,
9551 end: offset_range.end,
9552 reversed: false,
9553 goal: SelectionGoal::None,
9554 });
9555 });
9556 }
9557 }
9558
9559 new_selections.sort_by_key(|selection| selection.start);
9560 let mut ix = 0;
9561 while ix + 1 < new_selections.len() {
9562 let current_selection = &new_selections[ix];
9563 let next_selection = &new_selections[ix + 1];
9564 if current_selection.range().overlaps(&next_selection.range()) {
9565 if current_selection.id < next_selection.id {
9566 new_selections.remove(ix + 1);
9567 } else {
9568 new_selections.remove(ix);
9569 }
9570 } else {
9571 ix += 1;
9572 }
9573 }
9574
9575 let reversed = self.selections.oldest::<usize>(cx).reversed;
9576
9577 for selection in new_selections.iter_mut() {
9578 selection.reversed = reversed;
9579 }
9580
9581 select_next_state.done = true;
9582 self.unfold_ranges(
9583 &new_selections
9584 .iter()
9585 .map(|selection| selection.range())
9586 .collect::<Vec<_>>(),
9587 false,
9588 false,
9589 cx,
9590 );
9591 self.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
9592 selections.select(new_selections)
9593 });
9594
9595 Ok(())
9596 }
9597
9598 pub fn select_next(
9599 &mut self,
9600 action: &SelectNext,
9601 window: &mut Window,
9602 cx: &mut Context<Self>,
9603 ) -> Result<()> {
9604 self.push_to_selection_history();
9605 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9606 self.select_next_match_internal(
9607 &display_map,
9608 action.replace_newest,
9609 Some(Autoscroll::newest()),
9610 window,
9611 cx,
9612 )?;
9613 Ok(())
9614 }
9615
9616 pub fn select_previous(
9617 &mut self,
9618 action: &SelectPrevious,
9619 window: &mut Window,
9620 cx: &mut Context<Self>,
9621 ) -> Result<()> {
9622 self.push_to_selection_history();
9623 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9624 let buffer = &display_map.buffer_snapshot;
9625 let mut selections = self.selections.all::<usize>(cx);
9626 if let Some(mut select_prev_state) = self.select_prev_state.take() {
9627 let query = &select_prev_state.query;
9628 if !select_prev_state.done {
9629 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
9630 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
9631 let mut next_selected_range = None;
9632 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
9633 let bytes_before_last_selection =
9634 buffer.reversed_bytes_in_range(0..last_selection.start);
9635 let bytes_after_first_selection =
9636 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
9637 let query_matches = query
9638 .stream_find_iter(bytes_before_last_selection)
9639 .map(|result| (last_selection.start, result))
9640 .chain(
9641 query
9642 .stream_find_iter(bytes_after_first_selection)
9643 .map(|result| (buffer.len(), result)),
9644 );
9645 for (end_offset, query_match) in query_matches {
9646 let query_match = query_match.unwrap(); // can only fail due to I/O
9647 let offset_range =
9648 end_offset - query_match.end()..end_offset - query_match.start();
9649 let display_range = offset_range.start.to_display_point(&display_map)
9650 ..offset_range.end.to_display_point(&display_map);
9651
9652 if !select_prev_state.wordwise
9653 || (!movement::is_inside_word(&display_map, display_range.start)
9654 && !movement::is_inside_word(&display_map, display_range.end))
9655 {
9656 next_selected_range = Some(offset_range);
9657 break;
9658 }
9659 }
9660
9661 if let Some(next_selected_range) = next_selected_range {
9662 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
9663 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
9664 if action.replace_newest {
9665 s.delete(s.newest_anchor().id);
9666 }
9667 s.insert_range(next_selected_range);
9668 });
9669 } else {
9670 select_prev_state.done = true;
9671 }
9672 }
9673
9674 self.select_prev_state = Some(select_prev_state);
9675 } else {
9676 let mut only_carets = true;
9677 let mut same_text_selected = true;
9678 let mut selected_text = None;
9679
9680 let mut selections_iter = selections.iter().peekable();
9681 while let Some(selection) = selections_iter.next() {
9682 if selection.start != selection.end {
9683 only_carets = false;
9684 }
9685
9686 if same_text_selected {
9687 if selected_text.is_none() {
9688 selected_text =
9689 Some(buffer.text_for_range(selection.range()).collect::<String>());
9690 }
9691
9692 if let Some(next_selection) = selections_iter.peek() {
9693 if next_selection.range().len() == selection.range().len() {
9694 let next_selected_text = buffer
9695 .text_for_range(next_selection.range())
9696 .collect::<String>();
9697 if Some(next_selected_text) != selected_text {
9698 same_text_selected = false;
9699 selected_text = None;
9700 }
9701 } else {
9702 same_text_selected = false;
9703 selected_text = None;
9704 }
9705 }
9706 }
9707 }
9708
9709 if only_carets {
9710 for selection in &mut selections {
9711 let word_range = movement::surrounding_word(
9712 &display_map,
9713 selection.start.to_display_point(&display_map),
9714 );
9715 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
9716 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
9717 selection.goal = SelectionGoal::None;
9718 selection.reversed = false;
9719 }
9720 if selections.len() == 1 {
9721 let selection = selections
9722 .last()
9723 .expect("ensured that there's only one selection");
9724 let query = buffer
9725 .text_for_range(selection.start..selection.end)
9726 .collect::<String>();
9727 let is_empty = query.is_empty();
9728 let select_state = SelectNextState {
9729 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
9730 wordwise: true,
9731 done: is_empty,
9732 };
9733 self.select_prev_state = Some(select_state);
9734 } else {
9735 self.select_prev_state = None;
9736 }
9737
9738 self.unfold_ranges(
9739 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
9740 false,
9741 true,
9742 cx,
9743 );
9744 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
9745 s.select(selections);
9746 });
9747 } else if let Some(selected_text) = selected_text {
9748 self.select_prev_state = Some(SelectNextState {
9749 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
9750 wordwise: false,
9751 done: false,
9752 });
9753 self.select_previous(action, window, cx)?;
9754 }
9755 }
9756 Ok(())
9757 }
9758
9759 pub fn toggle_comments(
9760 &mut self,
9761 action: &ToggleComments,
9762 window: &mut Window,
9763 cx: &mut Context<Self>,
9764 ) {
9765 if self.read_only(cx) {
9766 return;
9767 }
9768 let text_layout_details = &self.text_layout_details(window);
9769 self.transact(window, cx, |this, window, cx| {
9770 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
9771 let mut edits = Vec::new();
9772 let mut selection_edit_ranges = Vec::new();
9773 let mut last_toggled_row = None;
9774 let snapshot = this.buffer.read(cx).read(cx);
9775 let empty_str: Arc<str> = Arc::default();
9776 let mut suffixes_inserted = Vec::new();
9777 let ignore_indent = action.ignore_indent;
9778
9779 fn comment_prefix_range(
9780 snapshot: &MultiBufferSnapshot,
9781 row: MultiBufferRow,
9782 comment_prefix: &str,
9783 comment_prefix_whitespace: &str,
9784 ignore_indent: bool,
9785 ) -> Range<Point> {
9786 let indent_size = if ignore_indent {
9787 0
9788 } else {
9789 snapshot.indent_size_for_line(row).len
9790 };
9791
9792 let start = Point::new(row.0, indent_size);
9793
9794 let mut line_bytes = snapshot
9795 .bytes_in_range(start..snapshot.max_point())
9796 .flatten()
9797 .copied();
9798
9799 // If this line currently begins with the line comment prefix, then record
9800 // the range containing the prefix.
9801 if line_bytes
9802 .by_ref()
9803 .take(comment_prefix.len())
9804 .eq(comment_prefix.bytes())
9805 {
9806 // Include any whitespace that matches the comment prefix.
9807 let matching_whitespace_len = line_bytes
9808 .zip(comment_prefix_whitespace.bytes())
9809 .take_while(|(a, b)| a == b)
9810 .count() as u32;
9811 let end = Point::new(
9812 start.row,
9813 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
9814 );
9815 start..end
9816 } else {
9817 start..start
9818 }
9819 }
9820
9821 fn comment_suffix_range(
9822 snapshot: &MultiBufferSnapshot,
9823 row: MultiBufferRow,
9824 comment_suffix: &str,
9825 comment_suffix_has_leading_space: bool,
9826 ) -> Range<Point> {
9827 let end = Point::new(row.0, snapshot.line_len(row));
9828 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
9829
9830 let mut line_end_bytes = snapshot
9831 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
9832 .flatten()
9833 .copied();
9834
9835 let leading_space_len = if suffix_start_column > 0
9836 && line_end_bytes.next() == Some(b' ')
9837 && comment_suffix_has_leading_space
9838 {
9839 1
9840 } else {
9841 0
9842 };
9843
9844 // If this line currently begins with the line comment prefix, then record
9845 // the range containing the prefix.
9846 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
9847 let start = Point::new(end.row, suffix_start_column - leading_space_len);
9848 start..end
9849 } else {
9850 end..end
9851 }
9852 }
9853
9854 // TODO: Handle selections that cross excerpts
9855 for selection in &mut selections {
9856 let start_column = snapshot
9857 .indent_size_for_line(MultiBufferRow(selection.start.row))
9858 .len;
9859 let language = if let Some(language) =
9860 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
9861 {
9862 language
9863 } else {
9864 continue;
9865 };
9866
9867 selection_edit_ranges.clear();
9868
9869 // If multiple selections contain a given row, avoid processing that
9870 // row more than once.
9871 let mut start_row = MultiBufferRow(selection.start.row);
9872 if last_toggled_row == Some(start_row) {
9873 start_row = start_row.next_row();
9874 }
9875 let end_row =
9876 if selection.end.row > selection.start.row && selection.end.column == 0 {
9877 MultiBufferRow(selection.end.row - 1)
9878 } else {
9879 MultiBufferRow(selection.end.row)
9880 };
9881 last_toggled_row = Some(end_row);
9882
9883 if start_row > end_row {
9884 continue;
9885 }
9886
9887 // If the language has line comments, toggle those.
9888 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
9889
9890 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
9891 if ignore_indent {
9892 full_comment_prefixes = full_comment_prefixes
9893 .into_iter()
9894 .map(|s| Arc::from(s.trim_end()))
9895 .collect();
9896 }
9897
9898 if !full_comment_prefixes.is_empty() {
9899 let first_prefix = full_comment_prefixes
9900 .first()
9901 .expect("prefixes is non-empty");
9902 let prefix_trimmed_lengths = full_comment_prefixes
9903 .iter()
9904 .map(|p| p.trim_end_matches(' ').len())
9905 .collect::<SmallVec<[usize; 4]>>();
9906
9907 let mut all_selection_lines_are_comments = true;
9908
9909 for row in start_row.0..=end_row.0 {
9910 let row = MultiBufferRow(row);
9911 if start_row < end_row && snapshot.is_line_blank(row) {
9912 continue;
9913 }
9914
9915 let prefix_range = full_comment_prefixes
9916 .iter()
9917 .zip(prefix_trimmed_lengths.iter().copied())
9918 .map(|(prefix, trimmed_prefix_len)| {
9919 comment_prefix_range(
9920 snapshot.deref(),
9921 row,
9922 &prefix[..trimmed_prefix_len],
9923 &prefix[trimmed_prefix_len..],
9924 ignore_indent,
9925 )
9926 })
9927 .max_by_key(|range| range.end.column - range.start.column)
9928 .expect("prefixes is non-empty");
9929
9930 if prefix_range.is_empty() {
9931 all_selection_lines_are_comments = false;
9932 }
9933
9934 selection_edit_ranges.push(prefix_range);
9935 }
9936
9937 if all_selection_lines_are_comments {
9938 edits.extend(
9939 selection_edit_ranges
9940 .iter()
9941 .cloned()
9942 .map(|range| (range, empty_str.clone())),
9943 );
9944 } else {
9945 let min_column = selection_edit_ranges
9946 .iter()
9947 .map(|range| range.start.column)
9948 .min()
9949 .unwrap_or(0);
9950 edits.extend(selection_edit_ranges.iter().map(|range| {
9951 let position = Point::new(range.start.row, min_column);
9952 (position..position, first_prefix.clone())
9953 }));
9954 }
9955 } else if let Some((full_comment_prefix, comment_suffix)) =
9956 language.block_comment_delimiters()
9957 {
9958 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
9959 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
9960 let prefix_range = comment_prefix_range(
9961 snapshot.deref(),
9962 start_row,
9963 comment_prefix,
9964 comment_prefix_whitespace,
9965 ignore_indent,
9966 );
9967 let suffix_range = comment_suffix_range(
9968 snapshot.deref(),
9969 end_row,
9970 comment_suffix.trim_start_matches(' '),
9971 comment_suffix.starts_with(' '),
9972 );
9973
9974 if prefix_range.is_empty() || suffix_range.is_empty() {
9975 edits.push((
9976 prefix_range.start..prefix_range.start,
9977 full_comment_prefix.clone(),
9978 ));
9979 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
9980 suffixes_inserted.push((end_row, comment_suffix.len()));
9981 } else {
9982 edits.push((prefix_range, empty_str.clone()));
9983 edits.push((suffix_range, empty_str.clone()));
9984 }
9985 } else {
9986 continue;
9987 }
9988 }
9989
9990 drop(snapshot);
9991 this.buffer.update(cx, |buffer, cx| {
9992 buffer.edit(edits, None, cx);
9993 });
9994
9995 // Adjust selections so that they end before any comment suffixes that
9996 // were inserted.
9997 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
9998 let mut selections = this.selections.all::<Point>(cx);
9999 let snapshot = this.buffer.read(cx).read(cx);
10000 for selection in &mut selections {
10001 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
10002 match row.cmp(&MultiBufferRow(selection.end.row)) {
10003 Ordering::Less => {
10004 suffixes_inserted.next();
10005 continue;
10006 }
10007 Ordering::Greater => break,
10008 Ordering::Equal => {
10009 if selection.end.column == snapshot.line_len(row) {
10010 if selection.is_empty() {
10011 selection.start.column -= suffix_len as u32;
10012 }
10013 selection.end.column -= suffix_len as u32;
10014 }
10015 break;
10016 }
10017 }
10018 }
10019 }
10020
10021 drop(snapshot);
10022 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10023 s.select(selections)
10024 });
10025
10026 let selections = this.selections.all::<Point>(cx);
10027 let selections_on_single_row = selections.windows(2).all(|selections| {
10028 selections[0].start.row == selections[1].start.row
10029 && selections[0].end.row == selections[1].end.row
10030 && selections[0].start.row == selections[0].end.row
10031 });
10032 let selections_selecting = selections
10033 .iter()
10034 .any(|selection| selection.start != selection.end);
10035 let advance_downwards = action.advance_downwards
10036 && selections_on_single_row
10037 && !selections_selecting
10038 && !matches!(this.mode, EditorMode::SingleLine { .. });
10039
10040 if advance_downwards {
10041 let snapshot = this.buffer.read(cx).snapshot(cx);
10042
10043 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10044 s.move_cursors_with(|display_snapshot, display_point, _| {
10045 let mut point = display_point.to_point(display_snapshot);
10046 point.row += 1;
10047 point = snapshot.clip_point(point, Bias::Left);
10048 let display_point = point.to_display_point(display_snapshot);
10049 let goal = SelectionGoal::HorizontalPosition(
10050 display_snapshot
10051 .x_for_display_point(display_point, text_layout_details)
10052 .into(),
10053 );
10054 (display_point, goal)
10055 })
10056 });
10057 }
10058 });
10059 }
10060
10061 pub fn select_enclosing_symbol(
10062 &mut self,
10063 _: &SelectEnclosingSymbol,
10064 window: &mut Window,
10065 cx: &mut Context<Self>,
10066 ) {
10067 let buffer = self.buffer.read(cx).snapshot(cx);
10068 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
10069
10070 fn update_selection(
10071 selection: &Selection<usize>,
10072 buffer_snap: &MultiBufferSnapshot,
10073 ) -> Option<Selection<usize>> {
10074 let cursor = selection.head();
10075 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
10076 for symbol in symbols.iter().rev() {
10077 let start = symbol.range.start.to_offset(buffer_snap);
10078 let end = symbol.range.end.to_offset(buffer_snap);
10079 let new_range = start..end;
10080 if start < selection.start || end > selection.end {
10081 return Some(Selection {
10082 id: selection.id,
10083 start: new_range.start,
10084 end: new_range.end,
10085 goal: SelectionGoal::None,
10086 reversed: selection.reversed,
10087 });
10088 }
10089 }
10090 None
10091 }
10092
10093 let mut selected_larger_symbol = false;
10094 let new_selections = old_selections
10095 .iter()
10096 .map(|selection| match update_selection(selection, &buffer) {
10097 Some(new_selection) => {
10098 if new_selection.range() != selection.range() {
10099 selected_larger_symbol = true;
10100 }
10101 new_selection
10102 }
10103 None => selection.clone(),
10104 })
10105 .collect::<Vec<_>>();
10106
10107 if selected_larger_symbol {
10108 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10109 s.select(new_selections);
10110 });
10111 }
10112 }
10113
10114 pub fn select_larger_syntax_node(
10115 &mut self,
10116 _: &SelectLargerSyntaxNode,
10117 window: &mut Window,
10118 cx: &mut Context<Self>,
10119 ) {
10120 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10121 let buffer = self.buffer.read(cx).snapshot(cx);
10122 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
10123
10124 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
10125 let mut selected_larger_node = false;
10126 let new_selections = old_selections
10127 .iter()
10128 .map(|selection| {
10129 let old_range = selection.start..selection.end;
10130 let mut new_range = old_range.clone();
10131 let mut new_node = None;
10132 while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone())
10133 {
10134 new_node = Some(node);
10135 new_range = containing_range;
10136 if !display_map.intersects_fold(new_range.start)
10137 && !display_map.intersects_fold(new_range.end)
10138 {
10139 break;
10140 }
10141 }
10142
10143 if let Some(node) = new_node {
10144 // Log the ancestor, to support using this action as a way to explore TreeSitter
10145 // nodes. Parent and grandparent are also logged because this operation will not
10146 // visit nodes that have the same range as their parent.
10147 log::info!("Node: {node:?}");
10148 let parent = node.parent();
10149 log::info!("Parent: {parent:?}");
10150 let grandparent = parent.and_then(|x| x.parent());
10151 log::info!("Grandparent: {grandparent:?}");
10152 }
10153
10154 selected_larger_node |= new_range != old_range;
10155 Selection {
10156 id: selection.id,
10157 start: new_range.start,
10158 end: new_range.end,
10159 goal: SelectionGoal::None,
10160 reversed: selection.reversed,
10161 }
10162 })
10163 .collect::<Vec<_>>();
10164
10165 if selected_larger_node {
10166 stack.push(old_selections);
10167 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10168 s.select(new_selections);
10169 });
10170 }
10171 self.select_larger_syntax_node_stack = stack;
10172 }
10173
10174 pub fn select_smaller_syntax_node(
10175 &mut self,
10176 _: &SelectSmallerSyntaxNode,
10177 window: &mut Window,
10178 cx: &mut Context<Self>,
10179 ) {
10180 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
10181 if let Some(selections) = stack.pop() {
10182 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10183 s.select(selections.to_vec());
10184 });
10185 }
10186 self.select_larger_syntax_node_stack = stack;
10187 }
10188
10189 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
10190 if !EditorSettings::get_global(cx).gutter.runnables {
10191 self.clear_tasks();
10192 return Task::ready(());
10193 }
10194 let project = self.project.as_ref().map(Entity::downgrade);
10195 cx.spawn_in(window, |this, mut cx| async move {
10196 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
10197 let Some(project) = project.and_then(|p| p.upgrade()) else {
10198 return;
10199 };
10200 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
10201 this.display_map.update(cx, |map, cx| map.snapshot(cx))
10202 }) else {
10203 return;
10204 };
10205
10206 let hide_runnables = project
10207 .update(&mut cx, |project, cx| {
10208 // Do not display any test indicators in non-dev server remote projects.
10209 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
10210 })
10211 .unwrap_or(true);
10212 if hide_runnables {
10213 return;
10214 }
10215 let new_rows =
10216 cx.background_executor()
10217 .spawn({
10218 let snapshot = display_snapshot.clone();
10219 async move {
10220 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
10221 }
10222 })
10223 .await;
10224
10225 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
10226 this.update(&mut cx, |this, _| {
10227 this.clear_tasks();
10228 for (key, value) in rows {
10229 this.insert_tasks(key, value);
10230 }
10231 })
10232 .ok();
10233 })
10234 }
10235 fn fetch_runnable_ranges(
10236 snapshot: &DisplaySnapshot,
10237 range: Range<Anchor>,
10238 ) -> Vec<language::RunnableRange> {
10239 snapshot.buffer_snapshot.runnable_ranges(range).collect()
10240 }
10241
10242 fn runnable_rows(
10243 project: Entity<Project>,
10244 snapshot: DisplaySnapshot,
10245 runnable_ranges: Vec<RunnableRange>,
10246 mut cx: AsyncWindowContext,
10247 ) -> Vec<((BufferId, u32), RunnableTasks)> {
10248 runnable_ranges
10249 .into_iter()
10250 .filter_map(|mut runnable| {
10251 let tasks = cx
10252 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
10253 .ok()?;
10254 if tasks.is_empty() {
10255 return None;
10256 }
10257
10258 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
10259
10260 let row = snapshot
10261 .buffer_snapshot
10262 .buffer_line_for_row(MultiBufferRow(point.row))?
10263 .1
10264 .start
10265 .row;
10266
10267 let context_range =
10268 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
10269 Some((
10270 (runnable.buffer_id, row),
10271 RunnableTasks {
10272 templates: tasks,
10273 offset: MultiBufferOffset(runnable.run_range.start),
10274 context_range,
10275 column: point.column,
10276 extra_variables: runnable.extra_captures,
10277 },
10278 ))
10279 })
10280 .collect()
10281 }
10282
10283 fn templates_with_tags(
10284 project: &Entity<Project>,
10285 runnable: &mut Runnable,
10286 cx: &mut App,
10287 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
10288 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
10289 let (worktree_id, file) = project
10290 .buffer_for_id(runnable.buffer, cx)
10291 .and_then(|buffer| buffer.read(cx).file())
10292 .map(|file| (file.worktree_id(cx), file.clone()))
10293 .unzip();
10294
10295 (
10296 project.task_store().read(cx).task_inventory().cloned(),
10297 worktree_id,
10298 file,
10299 )
10300 });
10301
10302 let tags = mem::take(&mut runnable.tags);
10303 let mut tags: Vec<_> = tags
10304 .into_iter()
10305 .flat_map(|tag| {
10306 let tag = tag.0.clone();
10307 inventory
10308 .as_ref()
10309 .into_iter()
10310 .flat_map(|inventory| {
10311 inventory.read(cx).list_tasks(
10312 file.clone(),
10313 Some(runnable.language.clone()),
10314 worktree_id,
10315 cx,
10316 )
10317 })
10318 .filter(move |(_, template)| {
10319 template.tags.iter().any(|source_tag| source_tag == &tag)
10320 })
10321 })
10322 .sorted_by_key(|(kind, _)| kind.to_owned())
10323 .collect();
10324 if let Some((leading_tag_source, _)) = tags.first() {
10325 // Strongest source wins; if we have worktree tag binding, prefer that to
10326 // global and language bindings;
10327 // if we have a global binding, prefer that to language binding.
10328 let first_mismatch = tags
10329 .iter()
10330 .position(|(tag_source, _)| tag_source != leading_tag_source);
10331 if let Some(index) = first_mismatch {
10332 tags.truncate(index);
10333 }
10334 }
10335
10336 tags
10337 }
10338
10339 pub fn move_to_enclosing_bracket(
10340 &mut self,
10341 _: &MoveToEnclosingBracket,
10342 window: &mut Window,
10343 cx: &mut Context<Self>,
10344 ) {
10345 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10346 s.move_offsets_with(|snapshot, selection| {
10347 let Some(enclosing_bracket_ranges) =
10348 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
10349 else {
10350 return;
10351 };
10352
10353 let mut best_length = usize::MAX;
10354 let mut best_inside = false;
10355 let mut best_in_bracket_range = false;
10356 let mut best_destination = None;
10357 for (open, close) in enclosing_bracket_ranges {
10358 let close = close.to_inclusive();
10359 let length = close.end() - open.start;
10360 let inside = selection.start >= open.end && selection.end <= *close.start();
10361 let in_bracket_range = open.to_inclusive().contains(&selection.head())
10362 || close.contains(&selection.head());
10363
10364 // If best is next to a bracket and current isn't, skip
10365 if !in_bracket_range && best_in_bracket_range {
10366 continue;
10367 }
10368
10369 // Prefer smaller lengths unless best is inside and current isn't
10370 if length > best_length && (best_inside || !inside) {
10371 continue;
10372 }
10373
10374 best_length = length;
10375 best_inside = inside;
10376 best_in_bracket_range = in_bracket_range;
10377 best_destination = Some(
10378 if close.contains(&selection.start) && close.contains(&selection.end) {
10379 if inside {
10380 open.end
10381 } else {
10382 open.start
10383 }
10384 } else if inside {
10385 *close.start()
10386 } else {
10387 *close.end()
10388 },
10389 );
10390 }
10391
10392 if let Some(destination) = best_destination {
10393 selection.collapse_to(destination, SelectionGoal::None);
10394 }
10395 })
10396 });
10397 }
10398
10399 pub fn undo_selection(
10400 &mut self,
10401 _: &UndoSelection,
10402 window: &mut Window,
10403 cx: &mut Context<Self>,
10404 ) {
10405 self.end_selection(window, cx);
10406 self.selection_history.mode = SelectionHistoryMode::Undoing;
10407 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
10408 self.change_selections(None, window, cx, |s| {
10409 s.select_anchors(entry.selections.to_vec())
10410 });
10411 self.select_next_state = entry.select_next_state;
10412 self.select_prev_state = entry.select_prev_state;
10413 self.add_selections_state = entry.add_selections_state;
10414 self.request_autoscroll(Autoscroll::newest(), cx);
10415 }
10416 self.selection_history.mode = SelectionHistoryMode::Normal;
10417 }
10418
10419 pub fn redo_selection(
10420 &mut self,
10421 _: &RedoSelection,
10422 window: &mut Window,
10423 cx: &mut Context<Self>,
10424 ) {
10425 self.end_selection(window, cx);
10426 self.selection_history.mode = SelectionHistoryMode::Redoing;
10427 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
10428 self.change_selections(None, window, cx, |s| {
10429 s.select_anchors(entry.selections.to_vec())
10430 });
10431 self.select_next_state = entry.select_next_state;
10432 self.select_prev_state = entry.select_prev_state;
10433 self.add_selections_state = entry.add_selections_state;
10434 self.request_autoscroll(Autoscroll::newest(), cx);
10435 }
10436 self.selection_history.mode = SelectionHistoryMode::Normal;
10437 }
10438
10439 pub fn expand_excerpts(
10440 &mut self,
10441 action: &ExpandExcerpts,
10442 _: &mut Window,
10443 cx: &mut Context<Self>,
10444 ) {
10445 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
10446 }
10447
10448 pub fn expand_excerpts_down(
10449 &mut self,
10450 action: &ExpandExcerptsDown,
10451 _: &mut Window,
10452 cx: &mut Context<Self>,
10453 ) {
10454 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
10455 }
10456
10457 pub fn expand_excerpts_up(
10458 &mut self,
10459 action: &ExpandExcerptsUp,
10460 _: &mut Window,
10461 cx: &mut Context<Self>,
10462 ) {
10463 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
10464 }
10465
10466 pub fn expand_excerpts_for_direction(
10467 &mut self,
10468 lines: u32,
10469 direction: ExpandExcerptDirection,
10470
10471 cx: &mut Context<Self>,
10472 ) {
10473 let selections = self.selections.disjoint_anchors();
10474
10475 let lines = if lines == 0 {
10476 EditorSettings::get_global(cx).expand_excerpt_lines
10477 } else {
10478 lines
10479 };
10480
10481 self.buffer.update(cx, |buffer, cx| {
10482 let snapshot = buffer.snapshot(cx);
10483 let mut excerpt_ids = selections
10484 .iter()
10485 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
10486 .collect::<Vec<_>>();
10487 excerpt_ids.sort();
10488 excerpt_ids.dedup();
10489 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
10490 })
10491 }
10492
10493 pub fn expand_excerpt(
10494 &mut self,
10495 excerpt: ExcerptId,
10496 direction: ExpandExcerptDirection,
10497 cx: &mut Context<Self>,
10498 ) {
10499 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
10500 self.buffer.update(cx, |buffer, cx| {
10501 buffer.expand_excerpts([excerpt], lines, direction, cx)
10502 })
10503 }
10504
10505 pub fn go_to_singleton_buffer_point(
10506 &mut self,
10507 point: Point,
10508 window: &mut Window,
10509 cx: &mut Context<Self>,
10510 ) {
10511 self.go_to_singleton_buffer_range(point..point, window, cx);
10512 }
10513
10514 pub fn go_to_singleton_buffer_range(
10515 &mut self,
10516 range: Range<Point>,
10517 window: &mut Window,
10518 cx: &mut Context<Self>,
10519 ) {
10520 let multibuffer = self.buffer().read(cx);
10521 let Some(buffer) = multibuffer.as_singleton() else {
10522 return;
10523 };
10524 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
10525 return;
10526 };
10527 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
10528 return;
10529 };
10530 self.change_selections(Some(Autoscroll::center()), window, cx, |s| {
10531 s.select_anchor_ranges([start..end])
10532 });
10533 }
10534
10535 fn go_to_diagnostic(
10536 &mut self,
10537 _: &GoToDiagnostic,
10538 window: &mut Window,
10539 cx: &mut Context<Self>,
10540 ) {
10541 self.go_to_diagnostic_impl(Direction::Next, window, cx)
10542 }
10543
10544 fn go_to_prev_diagnostic(
10545 &mut self,
10546 _: &GoToPrevDiagnostic,
10547 window: &mut Window,
10548 cx: &mut Context<Self>,
10549 ) {
10550 self.go_to_diagnostic_impl(Direction::Prev, window, cx)
10551 }
10552
10553 pub fn go_to_diagnostic_impl(
10554 &mut self,
10555 direction: Direction,
10556 window: &mut Window,
10557 cx: &mut Context<Self>,
10558 ) {
10559 let buffer = self.buffer.read(cx).snapshot(cx);
10560 let selection = self.selections.newest::<usize>(cx);
10561
10562 // If there is an active Diagnostic Popover jump to its diagnostic instead.
10563 if direction == Direction::Next {
10564 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
10565 let Some(buffer_id) = popover.local_diagnostic.range.start.buffer_id else {
10566 return;
10567 };
10568 self.activate_diagnostics(
10569 buffer_id,
10570 popover.local_diagnostic.diagnostic.group_id,
10571 window,
10572 cx,
10573 );
10574 if let Some(active_diagnostics) = self.active_diagnostics.as_ref() {
10575 let primary_range_start = active_diagnostics.primary_range.start;
10576 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10577 let mut new_selection = s.newest_anchor().clone();
10578 new_selection.collapse_to(primary_range_start, SelectionGoal::None);
10579 s.select_anchors(vec![new_selection.clone()]);
10580 });
10581 self.refresh_inline_completion(false, true, window, cx);
10582 }
10583 return;
10584 }
10585 }
10586
10587 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
10588 active_diagnostics
10589 .primary_range
10590 .to_offset(&buffer)
10591 .to_inclusive()
10592 });
10593 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
10594 if active_primary_range.contains(&selection.head()) {
10595 *active_primary_range.start()
10596 } else {
10597 selection.head()
10598 }
10599 } else {
10600 selection.head()
10601 };
10602 let snapshot = self.snapshot(window, cx);
10603 loop {
10604 let mut diagnostics;
10605 if direction == Direction::Prev {
10606 diagnostics = buffer
10607 .diagnostics_in_range::<usize>(0..search_start)
10608 .collect::<Vec<_>>();
10609 diagnostics.reverse();
10610 } else {
10611 diagnostics = buffer
10612 .diagnostics_in_range::<usize>(search_start..buffer.len())
10613 .collect::<Vec<_>>();
10614 };
10615 let group = diagnostics
10616 .into_iter()
10617 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
10618 // relies on diagnostics_in_range to return diagnostics with the same starting range to
10619 // be sorted in a stable way
10620 // skip until we are at current active diagnostic, if it exists
10621 .skip_while(|entry| {
10622 let is_in_range = match direction {
10623 Direction::Prev => entry.range.end > search_start,
10624 Direction::Next => entry.range.start < search_start,
10625 };
10626 is_in_range
10627 && self
10628 .active_diagnostics
10629 .as_ref()
10630 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
10631 })
10632 .find_map(|entry| {
10633 if entry.diagnostic.is_primary
10634 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
10635 && entry.range.start != entry.range.end
10636 // if we match with the active diagnostic, skip it
10637 && Some(entry.diagnostic.group_id)
10638 != self.active_diagnostics.as_ref().map(|d| d.group_id)
10639 {
10640 Some((entry.range, entry.diagnostic.group_id))
10641 } else {
10642 None
10643 }
10644 });
10645
10646 if let Some((primary_range, group_id)) = group {
10647 let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
10648 return;
10649 };
10650 self.activate_diagnostics(buffer_id, group_id, window, cx);
10651 if self.active_diagnostics.is_some() {
10652 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10653 s.select(vec![Selection {
10654 id: selection.id,
10655 start: primary_range.start,
10656 end: primary_range.start,
10657 reversed: false,
10658 goal: SelectionGoal::None,
10659 }]);
10660 });
10661 self.refresh_inline_completion(false, true, window, cx);
10662 }
10663 break;
10664 } else {
10665 // Cycle around to the start of the buffer, potentially moving back to the start of
10666 // the currently active diagnostic.
10667 active_primary_range.take();
10668 if direction == Direction::Prev {
10669 if search_start == buffer.len() {
10670 break;
10671 } else {
10672 search_start = buffer.len();
10673 }
10674 } else if search_start == 0 {
10675 break;
10676 } else {
10677 search_start = 0;
10678 }
10679 }
10680 }
10681 }
10682
10683 fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
10684 let snapshot = self.snapshot(window, cx);
10685 let selection = self.selections.newest::<Point>(cx);
10686 self.go_to_hunk_after_position(&snapshot, selection.head(), window, cx);
10687 }
10688
10689 fn go_to_hunk_after_position(
10690 &mut self,
10691 snapshot: &EditorSnapshot,
10692 position: Point,
10693 window: &mut Window,
10694 cx: &mut Context<Editor>,
10695 ) -> Option<MultiBufferDiffHunk> {
10696 let mut hunk = snapshot
10697 .buffer_snapshot
10698 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
10699 .find(|hunk| hunk.row_range.start.0 > position.row);
10700 if hunk.is_none() {
10701 hunk = snapshot
10702 .buffer_snapshot
10703 .diff_hunks_in_range(Point::zero()..position)
10704 .find(|hunk| hunk.row_range.end.0 < position.row)
10705 }
10706 if let Some(hunk) = &hunk {
10707 let destination = Point::new(hunk.row_range.start.0, 0);
10708 self.unfold_ranges(&[destination..destination], false, false, cx);
10709 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10710 s.select_ranges(vec![destination..destination]);
10711 });
10712 }
10713
10714 hunk
10715 }
10716
10717 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, window: &mut Window, cx: &mut Context<Self>) {
10718 let snapshot = self.snapshot(window, cx);
10719 let selection = self.selections.newest::<Point>(cx);
10720 self.go_to_hunk_before_position(&snapshot, selection.head(), window, cx);
10721 }
10722
10723 fn go_to_hunk_before_position(
10724 &mut self,
10725 snapshot: &EditorSnapshot,
10726 position: Point,
10727 window: &mut Window,
10728 cx: &mut Context<Editor>,
10729 ) -> Option<MultiBufferDiffHunk> {
10730 let mut hunk = snapshot.buffer_snapshot.diff_hunk_before(position);
10731 if hunk.is_none() {
10732 hunk = snapshot.buffer_snapshot.diff_hunk_before(Point::MAX);
10733 }
10734 if let Some(hunk) = &hunk {
10735 let destination = Point::new(hunk.row_range.start.0, 0);
10736 self.unfold_ranges(&[destination..destination], false, false, cx);
10737 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10738 s.select_ranges(vec![destination..destination]);
10739 });
10740 }
10741
10742 hunk
10743 }
10744
10745 pub fn go_to_definition(
10746 &mut self,
10747 _: &GoToDefinition,
10748 window: &mut Window,
10749 cx: &mut Context<Self>,
10750 ) -> Task<Result<Navigated>> {
10751 let definition =
10752 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
10753 cx.spawn_in(window, |editor, mut cx| async move {
10754 if definition.await? == Navigated::Yes {
10755 return Ok(Navigated::Yes);
10756 }
10757 match editor.update_in(&mut cx, |editor, window, cx| {
10758 editor.find_all_references(&FindAllReferences, window, cx)
10759 })? {
10760 Some(references) => references.await,
10761 None => Ok(Navigated::No),
10762 }
10763 })
10764 }
10765
10766 pub fn go_to_declaration(
10767 &mut self,
10768 _: &GoToDeclaration,
10769 window: &mut Window,
10770 cx: &mut Context<Self>,
10771 ) -> Task<Result<Navigated>> {
10772 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
10773 }
10774
10775 pub fn go_to_declaration_split(
10776 &mut self,
10777 _: &GoToDeclaration,
10778 window: &mut Window,
10779 cx: &mut Context<Self>,
10780 ) -> Task<Result<Navigated>> {
10781 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
10782 }
10783
10784 pub fn go_to_implementation(
10785 &mut self,
10786 _: &GoToImplementation,
10787 window: &mut Window,
10788 cx: &mut Context<Self>,
10789 ) -> Task<Result<Navigated>> {
10790 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
10791 }
10792
10793 pub fn go_to_implementation_split(
10794 &mut self,
10795 _: &GoToImplementationSplit,
10796 window: &mut Window,
10797 cx: &mut Context<Self>,
10798 ) -> Task<Result<Navigated>> {
10799 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
10800 }
10801
10802 pub fn go_to_type_definition(
10803 &mut self,
10804 _: &GoToTypeDefinition,
10805 window: &mut Window,
10806 cx: &mut Context<Self>,
10807 ) -> Task<Result<Navigated>> {
10808 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
10809 }
10810
10811 pub fn go_to_definition_split(
10812 &mut self,
10813 _: &GoToDefinitionSplit,
10814 window: &mut Window,
10815 cx: &mut Context<Self>,
10816 ) -> Task<Result<Navigated>> {
10817 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
10818 }
10819
10820 pub fn go_to_type_definition_split(
10821 &mut self,
10822 _: &GoToTypeDefinitionSplit,
10823 window: &mut Window,
10824 cx: &mut Context<Self>,
10825 ) -> Task<Result<Navigated>> {
10826 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
10827 }
10828
10829 fn go_to_definition_of_kind(
10830 &mut self,
10831 kind: GotoDefinitionKind,
10832 split: bool,
10833 window: &mut Window,
10834 cx: &mut Context<Self>,
10835 ) -> Task<Result<Navigated>> {
10836 let Some(provider) = self.semantics_provider.clone() else {
10837 return Task::ready(Ok(Navigated::No));
10838 };
10839 let head = self.selections.newest::<usize>(cx).head();
10840 let buffer = self.buffer.read(cx);
10841 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
10842 text_anchor
10843 } else {
10844 return Task::ready(Ok(Navigated::No));
10845 };
10846
10847 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
10848 return Task::ready(Ok(Navigated::No));
10849 };
10850
10851 cx.spawn_in(window, |editor, mut cx| async move {
10852 let definitions = definitions.await?;
10853 let navigated = editor
10854 .update_in(&mut cx, |editor, window, cx| {
10855 editor.navigate_to_hover_links(
10856 Some(kind),
10857 definitions
10858 .into_iter()
10859 .filter(|location| {
10860 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
10861 })
10862 .map(HoverLink::Text)
10863 .collect::<Vec<_>>(),
10864 split,
10865 window,
10866 cx,
10867 )
10868 })?
10869 .await?;
10870 anyhow::Ok(navigated)
10871 })
10872 }
10873
10874 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
10875 let selection = self.selections.newest_anchor();
10876 let head = selection.head();
10877 let tail = selection.tail();
10878
10879 let Some((buffer, start_position)) =
10880 self.buffer.read(cx).text_anchor_for_position(head, cx)
10881 else {
10882 return;
10883 };
10884
10885 let end_position = if head != tail {
10886 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
10887 return;
10888 };
10889 Some(pos)
10890 } else {
10891 None
10892 };
10893
10894 let url_finder = cx.spawn_in(window, |editor, mut cx| async move {
10895 let url = if let Some(end_pos) = end_position {
10896 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
10897 } else {
10898 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
10899 };
10900
10901 if let Some(url) = url {
10902 editor.update(&mut cx, |_, cx| {
10903 cx.open_url(&url);
10904 })
10905 } else {
10906 Ok(())
10907 }
10908 });
10909
10910 url_finder.detach();
10911 }
10912
10913 pub fn open_selected_filename(
10914 &mut self,
10915 _: &OpenSelectedFilename,
10916 window: &mut Window,
10917 cx: &mut Context<Self>,
10918 ) {
10919 let Some(workspace) = self.workspace() else {
10920 return;
10921 };
10922
10923 let position = self.selections.newest_anchor().head();
10924
10925 let Some((buffer, buffer_position)) =
10926 self.buffer.read(cx).text_anchor_for_position(position, cx)
10927 else {
10928 return;
10929 };
10930
10931 let project = self.project.clone();
10932
10933 cx.spawn_in(window, |_, mut cx| async move {
10934 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
10935
10936 if let Some((_, path)) = result {
10937 workspace
10938 .update_in(&mut cx, |workspace, window, cx| {
10939 workspace.open_resolved_path(path, window, cx)
10940 })?
10941 .await?;
10942 }
10943 anyhow::Ok(())
10944 })
10945 .detach();
10946 }
10947
10948 pub(crate) fn navigate_to_hover_links(
10949 &mut self,
10950 kind: Option<GotoDefinitionKind>,
10951 mut definitions: Vec<HoverLink>,
10952 split: bool,
10953 window: &mut Window,
10954 cx: &mut Context<Editor>,
10955 ) -> Task<Result<Navigated>> {
10956 // If there is one definition, just open it directly
10957 if definitions.len() == 1 {
10958 let definition = definitions.pop().unwrap();
10959
10960 enum TargetTaskResult {
10961 Location(Option<Location>),
10962 AlreadyNavigated,
10963 }
10964
10965 let target_task = match definition {
10966 HoverLink::Text(link) => {
10967 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
10968 }
10969 HoverLink::InlayHint(lsp_location, server_id) => {
10970 let computation =
10971 self.compute_target_location(lsp_location, server_id, window, cx);
10972 cx.background_executor().spawn(async move {
10973 let location = computation.await?;
10974 Ok(TargetTaskResult::Location(location))
10975 })
10976 }
10977 HoverLink::Url(url) => {
10978 cx.open_url(&url);
10979 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
10980 }
10981 HoverLink::File(path) => {
10982 if let Some(workspace) = self.workspace() {
10983 cx.spawn_in(window, |_, mut cx| async move {
10984 workspace
10985 .update_in(&mut cx, |workspace, window, cx| {
10986 workspace.open_resolved_path(path, window, cx)
10987 })?
10988 .await
10989 .map(|_| TargetTaskResult::AlreadyNavigated)
10990 })
10991 } else {
10992 Task::ready(Ok(TargetTaskResult::Location(None)))
10993 }
10994 }
10995 };
10996 cx.spawn_in(window, |editor, mut cx| async move {
10997 let target = match target_task.await.context("target resolution task")? {
10998 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
10999 TargetTaskResult::Location(None) => return Ok(Navigated::No),
11000 TargetTaskResult::Location(Some(target)) => target,
11001 };
11002
11003 editor.update_in(&mut cx, |editor, window, cx| {
11004 let Some(workspace) = editor.workspace() else {
11005 return Navigated::No;
11006 };
11007 let pane = workspace.read(cx).active_pane().clone();
11008
11009 let range = target.range.to_point(target.buffer.read(cx));
11010 let range = editor.range_for_match(&range);
11011 let range = collapse_multiline_range(range);
11012
11013 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
11014 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
11015 } else {
11016 window.defer(cx, move |window, cx| {
11017 let target_editor: Entity<Self> =
11018 workspace.update(cx, |workspace, cx| {
11019 let pane = if split {
11020 workspace.adjacent_pane(window, cx)
11021 } else {
11022 workspace.active_pane().clone()
11023 };
11024
11025 workspace.open_project_item(
11026 pane,
11027 target.buffer.clone(),
11028 true,
11029 true,
11030 window,
11031 cx,
11032 )
11033 });
11034 target_editor.update(cx, |target_editor, cx| {
11035 // When selecting a definition in a different buffer, disable the nav history
11036 // to avoid creating a history entry at the previous cursor location.
11037 pane.update(cx, |pane, _| pane.disable_history());
11038 target_editor.go_to_singleton_buffer_range(range, window, cx);
11039 pane.update(cx, |pane, _| pane.enable_history());
11040 });
11041 });
11042 }
11043 Navigated::Yes
11044 })
11045 })
11046 } else if !definitions.is_empty() {
11047 cx.spawn_in(window, |editor, mut cx| async move {
11048 let (title, location_tasks, workspace) = editor
11049 .update_in(&mut cx, |editor, window, cx| {
11050 let tab_kind = match kind {
11051 Some(GotoDefinitionKind::Implementation) => "Implementations",
11052 _ => "Definitions",
11053 };
11054 let title = definitions
11055 .iter()
11056 .find_map(|definition| match definition {
11057 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
11058 let buffer = origin.buffer.read(cx);
11059 format!(
11060 "{} for {}",
11061 tab_kind,
11062 buffer
11063 .text_for_range(origin.range.clone())
11064 .collect::<String>()
11065 )
11066 }),
11067 HoverLink::InlayHint(_, _) => None,
11068 HoverLink::Url(_) => None,
11069 HoverLink::File(_) => None,
11070 })
11071 .unwrap_or(tab_kind.to_string());
11072 let location_tasks = definitions
11073 .into_iter()
11074 .map(|definition| match definition {
11075 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
11076 HoverLink::InlayHint(lsp_location, server_id) => editor
11077 .compute_target_location(lsp_location, server_id, window, cx),
11078 HoverLink::Url(_) => Task::ready(Ok(None)),
11079 HoverLink::File(_) => Task::ready(Ok(None)),
11080 })
11081 .collect::<Vec<_>>();
11082 (title, location_tasks, editor.workspace().clone())
11083 })
11084 .context("location tasks preparation")?;
11085
11086 let locations = future::join_all(location_tasks)
11087 .await
11088 .into_iter()
11089 .filter_map(|location| location.transpose())
11090 .collect::<Result<_>>()
11091 .context("location tasks")?;
11092
11093 let Some(workspace) = workspace else {
11094 return Ok(Navigated::No);
11095 };
11096 let opened = workspace
11097 .update_in(&mut cx, |workspace, window, cx| {
11098 Self::open_locations_in_multibuffer(
11099 workspace,
11100 locations,
11101 title,
11102 split,
11103 MultibufferSelectionMode::First,
11104 window,
11105 cx,
11106 )
11107 })
11108 .ok();
11109
11110 anyhow::Ok(Navigated::from_bool(opened.is_some()))
11111 })
11112 } else {
11113 Task::ready(Ok(Navigated::No))
11114 }
11115 }
11116
11117 fn compute_target_location(
11118 &self,
11119 lsp_location: lsp::Location,
11120 server_id: LanguageServerId,
11121 window: &mut Window,
11122 cx: &mut Context<Self>,
11123 ) -> Task<anyhow::Result<Option<Location>>> {
11124 let Some(project) = self.project.clone() else {
11125 return Task::ready(Ok(None));
11126 };
11127
11128 cx.spawn_in(window, move |editor, mut cx| async move {
11129 let location_task = editor.update(&mut cx, |_, cx| {
11130 project.update(cx, |project, cx| {
11131 let language_server_name = project
11132 .language_server_statuses(cx)
11133 .find(|(id, _)| server_id == *id)
11134 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
11135 language_server_name.map(|language_server_name| {
11136 project.open_local_buffer_via_lsp(
11137 lsp_location.uri.clone(),
11138 server_id,
11139 language_server_name,
11140 cx,
11141 )
11142 })
11143 })
11144 })?;
11145 let location = match location_task {
11146 Some(task) => Some({
11147 let target_buffer_handle = task.await.context("open local buffer")?;
11148 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
11149 let target_start = target_buffer
11150 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
11151 let target_end = target_buffer
11152 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
11153 target_buffer.anchor_after(target_start)
11154 ..target_buffer.anchor_before(target_end)
11155 })?;
11156 Location {
11157 buffer: target_buffer_handle,
11158 range,
11159 }
11160 }),
11161 None => None,
11162 };
11163 Ok(location)
11164 })
11165 }
11166
11167 pub fn find_all_references(
11168 &mut self,
11169 _: &FindAllReferences,
11170 window: &mut Window,
11171 cx: &mut Context<Self>,
11172 ) -> Option<Task<Result<Navigated>>> {
11173 let selection = self.selections.newest::<usize>(cx);
11174 let multi_buffer = self.buffer.read(cx);
11175 let head = selection.head();
11176
11177 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
11178 let head_anchor = multi_buffer_snapshot.anchor_at(
11179 head,
11180 if head < selection.tail() {
11181 Bias::Right
11182 } else {
11183 Bias::Left
11184 },
11185 );
11186
11187 match self
11188 .find_all_references_task_sources
11189 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
11190 {
11191 Ok(_) => {
11192 log::info!(
11193 "Ignoring repeated FindAllReferences invocation with the position of already running task"
11194 );
11195 return None;
11196 }
11197 Err(i) => {
11198 self.find_all_references_task_sources.insert(i, head_anchor);
11199 }
11200 }
11201
11202 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
11203 let workspace = self.workspace()?;
11204 let project = workspace.read(cx).project().clone();
11205 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
11206 Some(cx.spawn_in(window, |editor, mut cx| async move {
11207 let _cleanup = defer({
11208 let mut cx = cx.clone();
11209 move || {
11210 let _ = editor.update(&mut cx, |editor, _| {
11211 if let Ok(i) =
11212 editor
11213 .find_all_references_task_sources
11214 .binary_search_by(|anchor| {
11215 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
11216 })
11217 {
11218 editor.find_all_references_task_sources.remove(i);
11219 }
11220 });
11221 }
11222 });
11223
11224 let locations = references.await?;
11225 if locations.is_empty() {
11226 return anyhow::Ok(Navigated::No);
11227 }
11228
11229 workspace.update_in(&mut cx, |workspace, window, cx| {
11230 let title = locations
11231 .first()
11232 .as_ref()
11233 .map(|location| {
11234 let buffer = location.buffer.read(cx);
11235 format!(
11236 "References to `{}`",
11237 buffer
11238 .text_for_range(location.range.clone())
11239 .collect::<String>()
11240 )
11241 })
11242 .unwrap();
11243 Self::open_locations_in_multibuffer(
11244 workspace,
11245 locations,
11246 title,
11247 false,
11248 MultibufferSelectionMode::First,
11249 window,
11250 cx,
11251 );
11252 Navigated::Yes
11253 })
11254 }))
11255 }
11256
11257 /// Opens a multibuffer with the given project locations in it
11258 pub fn open_locations_in_multibuffer(
11259 workspace: &mut Workspace,
11260 mut locations: Vec<Location>,
11261 title: String,
11262 split: bool,
11263 multibuffer_selection_mode: MultibufferSelectionMode,
11264 window: &mut Window,
11265 cx: &mut Context<Workspace>,
11266 ) {
11267 // If there are multiple definitions, open them in a multibuffer
11268 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
11269 let mut locations = locations.into_iter().peekable();
11270 let mut ranges = Vec::new();
11271 let capability = workspace.project().read(cx).capability();
11272
11273 let excerpt_buffer = cx.new(|cx| {
11274 let mut multibuffer = MultiBuffer::new(capability);
11275 while let Some(location) = locations.next() {
11276 let buffer = location.buffer.read(cx);
11277 let mut ranges_for_buffer = Vec::new();
11278 let range = location.range.to_offset(buffer);
11279 ranges_for_buffer.push(range.clone());
11280
11281 while let Some(next_location) = locations.peek() {
11282 if next_location.buffer == location.buffer {
11283 ranges_for_buffer.push(next_location.range.to_offset(buffer));
11284 locations.next();
11285 } else {
11286 break;
11287 }
11288 }
11289
11290 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
11291 ranges.extend(multibuffer.push_excerpts_with_context_lines(
11292 location.buffer.clone(),
11293 ranges_for_buffer,
11294 DEFAULT_MULTIBUFFER_CONTEXT,
11295 cx,
11296 ))
11297 }
11298
11299 multibuffer.with_title(title)
11300 });
11301
11302 let editor = cx.new(|cx| {
11303 Editor::for_multibuffer(
11304 excerpt_buffer,
11305 Some(workspace.project().clone()),
11306 true,
11307 window,
11308 cx,
11309 )
11310 });
11311 editor.update(cx, |editor, cx| {
11312 match multibuffer_selection_mode {
11313 MultibufferSelectionMode::First => {
11314 if let Some(first_range) = ranges.first() {
11315 editor.change_selections(None, window, cx, |selections| {
11316 selections.clear_disjoint();
11317 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
11318 });
11319 }
11320 editor.highlight_background::<Self>(
11321 &ranges,
11322 |theme| theme.editor_highlighted_line_background,
11323 cx,
11324 );
11325 }
11326 MultibufferSelectionMode::All => {
11327 editor.change_selections(None, window, cx, |selections| {
11328 selections.clear_disjoint();
11329 selections.select_anchor_ranges(ranges);
11330 });
11331 }
11332 }
11333 editor.register_buffers_with_language_servers(cx);
11334 });
11335
11336 let item = Box::new(editor);
11337 let item_id = item.item_id();
11338
11339 if split {
11340 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
11341 } else {
11342 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
11343 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
11344 pane.close_current_preview_item(window, cx)
11345 } else {
11346 None
11347 }
11348 });
11349 workspace.add_item_to_active_pane(item.clone(), destination_index, true, window, cx);
11350 }
11351 workspace.active_pane().update(cx, |pane, cx| {
11352 pane.set_preview_item_id(Some(item_id), cx);
11353 });
11354 }
11355
11356 pub fn rename(
11357 &mut self,
11358 _: &Rename,
11359 window: &mut Window,
11360 cx: &mut Context<Self>,
11361 ) -> Option<Task<Result<()>>> {
11362 use language::ToOffset as _;
11363
11364 let provider = self.semantics_provider.clone()?;
11365 let selection = self.selections.newest_anchor().clone();
11366 let (cursor_buffer, cursor_buffer_position) = self
11367 .buffer
11368 .read(cx)
11369 .text_anchor_for_position(selection.head(), cx)?;
11370 let (tail_buffer, cursor_buffer_position_end) = self
11371 .buffer
11372 .read(cx)
11373 .text_anchor_for_position(selection.tail(), cx)?;
11374 if tail_buffer != cursor_buffer {
11375 return None;
11376 }
11377
11378 let snapshot = cursor_buffer.read(cx).snapshot();
11379 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
11380 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
11381 let prepare_rename = provider
11382 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
11383 .unwrap_or_else(|| Task::ready(Ok(None)));
11384 drop(snapshot);
11385
11386 Some(cx.spawn_in(window, |this, mut cx| async move {
11387 let rename_range = if let Some(range) = prepare_rename.await? {
11388 Some(range)
11389 } else {
11390 this.update(&mut cx, |this, cx| {
11391 let buffer = this.buffer.read(cx).snapshot(cx);
11392 let mut buffer_highlights = this
11393 .document_highlights_for_position(selection.head(), &buffer)
11394 .filter(|highlight| {
11395 highlight.start.excerpt_id == selection.head().excerpt_id
11396 && highlight.end.excerpt_id == selection.head().excerpt_id
11397 });
11398 buffer_highlights
11399 .next()
11400 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
11401 })?
11402 };
11403 if let Some(rename_range) = rename_range {
11404 this.update_in(&mut cx, |this, window, cx| {
11405 let snapshot = cursor_buffer.read(cx).snapshot();
11406 let rename_buffer_range = rename_range.to_offset(&snapshot);
11407 let cursor_offset_in_rename_range =
11408 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
11409 let cursor_offset_in_rename_range_end =
11410 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
11411
11412 this.take_rename(false, window, cx);
11413 let buffer = this.buffer.read(cx).read(cx);
11414 let cursor_offset = selection.head().to_offset(&buffer);
11415 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
11416 let rename_end = rename_start + rename_buffer_range.len();
11417 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
11418 let mut old_highlight_id = None;
11419 let old_name: Arc<str> = buffer
11420 .chunks(rename_start..rename_end, true)
11421 .map(|chunk| {
11422 if old_highlight_id.is_none() {
11423 old_highlight_id = chunk.syntax_highlight_id;
11424 }
11425 chunk.text
11426 })
11427 .collect::<String>()
11428 .into();
11429
11430 drop(buffer);
11431
11432 // Position the selection in the rename editor so that it matches the current selection.
11433 this.show_local_selections = false;
11434 let rename_editor = cx.new(|cx| {
11435 let mut editor = Editor::single_line(window, cx);
11436 editor.buffer.update(cx, |buffer, cx| {
11437 buffer.edit([(0..0, old_name.clone())], None, cx)
11438 });
11439 let rename_selection_range = match cursor_offset_in_rename_range
11440 .cmp(&cursor_offset_in_rename_range_end)
11441 {
11442 Ordering::Equal => {
11443 editor.select_all(&SelectAll, window, cx);
11444 return editor;
11445 }
11446 Ordering::Less => {
11447 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
11448 }
11449 Ordering::Greater => {
11450 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
11451 }
11452 };
11453 if rename_selection_range.end > old_name.len() {
11454 editor.select_all(&SelectAll, window, cx);
11455 } else {
11456 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11457 s.select_ranges([rename_selection_range]);
11458 });
11459 }
11460 editor
11461 });
11462 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
11463 if e == &EditorEvent::Focused {
11464 cx.emit(EditorEvent::FocusedIn)
11465 }
11466 })
11467 .detach();
11468
11469 let write_highlights =
11470 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
11471 let read_highlights =
11472 this.clear_background_highlights::<DocumentHighlightRead>(cx);
11473 let ranges = write_highlights
11474 .iter()
11475 .flat_map(|(_, ranges)| ranges.iter())
11476 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
11477 .cloned()
11478 .collect();
11479
11480 this.highlight_text::<Rename>(
11481 ranges,
11482 HighlightStyle {
11483 fade_out: Some(0.6),
11484 ..Default::default()
11485 },
11486 cx,
11487 );
11488 let rename_focus_handle = rename_editor.focus_handle(cx);
11489 window.focus(&rename_focus_handle);
11490 let block_id = this.insert_blocks(
11491 [BlockProperties {
11492 style: BlockStyle::Flex,
11493 placement: BlockPlacement::Below(range.start),
11494 height: 1,
11495 render: Arc::new({
11496 let rename_editor = rename_editor.clone();
11497 move |cx: &mut BlockContext| {
11498 let mut text_style = cx.editor_style.text.clone();
11499 if let Some(highlight_style) = old_highlight_id
11500 .and_then(|h| h.style(&cx.editor_style.syntax))
11501 {
11502 text_style = text_style.highlight(highlight_style);
11503 }
11504 div()
11505 .block_mouse_down()
11506 .pl(cx.anchor_x)
11507 .child(EditorElement::new(
11508 &rename_editor,
11509 EditorStyle {
11510 background: cx.theme().system().transparent,
11511 local_player: cx.editor_style.local_player,
11512 text: text_style,
11513 scrollbar_width: cx.editor_style.scrollbar_width,
11514 syntax: cx.editor_style.syntax.clone(),
11515 status: cx.editor_style.status.clone(),
11516 inlay_hints_style: HighlightStyle {
11517 font_weight: Some(FontWeight::BOLD),
11518 ..make_inlay_hints_style(cx.app)
11519 },
11520 inline_completion_styles: make_suggestion_styles(
11521 cx.app,
11522 ),
11523 ..EditorStyle::default()
11524 },
11525 ))
11526 .into_any_element()
11527 }
11528 }),
11529 priority: 0,
11530 }],
11531 Some(Autoscroll::fit()),
11532 cx,
11533 )[0];
11534 this.pending_rename = Some(RenameState {
11535 range,
11536 old_name,
11537 editor: rename_editor,
11538 block_id,
11539 });
11540 })?;
11541 }
11542
11543 Ok(())
11544 }))
11545 }
11546
11547 pub fn confirm_rename(
11548 &mut self,
11549 _: &ConfirmRename,
11550 window: &mut Window,
11551 cx: &mut Context<Self>,
11552 ) -> Option<Task<Result<()>>> {
11553 let rename = self.take_rename(false, window, cx)?;
11554 let workspace = self.workspace()?.downgrade();
11555 let (buffer, start) = self
11556 .buffer
11557 .read(cx)
11558 .text_anchor_for_position(rename.range.start, cx)?;
11559 let (end_buffer, _) = self
11560 .buffer
11561 .read(cx)
11562 .text_anchor_for_position(rename.range.end, cx)?;
11563 if buffer != end_buffer {
11564 return None;
11565 }
11566
11567 let old_name = rename.old_name;
11568 let new_name = rename.editor.read(cx).text(cx);
11569
11570 let rename = self.semantics_provider.as_ref()?.perform_rename(
11571 &buffer,
11572 start,
11573 new_name.clone(),
11574 cx,
11575 )?;
11576
11577 Some(cx.spawn_in(window, |editor, mut cx| async move {
11578 let project_transaction = rename.await?;
11579 Self::open_project_transaction(
11580 &editor,
11581 workspace,
11582 project_transaction,
11583 format!("Rename: {} → {}", old_name, new_name),
11584 cx.clone(),
11585 )
11586 .await?;
11587
11588 editor.update(&mut cx, |editor, cx| {
11589 editor.refresh_document_highlights(cx);
11590 })?;
11591 Ok(())
11592 }))
11593 }
11594
11595 fn take_rename(
11596 &mut self,
11597 moving_cursor: bool,
11598 window: &mut Window,
11599 cx: &mut Context<Self>,
11600 ) -> Option<RenameState> {
11601 let rename = self.pending_rename.take()?;
11602 if rename.editor.focus_handle(cx).is_focused(window) {
11603 window.focus(&self.focus_handle);
11604 }
11605
11606 self.remove_blocks(
11607 [rename.block_id].into_iter().collect(),
11608 Some(Autoscroll::fit()),
11609 cx,
11610 );
11611 self.clear_highlights::<Rename>(cx);
11612 self.show_local_selections = true;
11613
11614 if moving_cursor {
11615 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
11616 editor.selections.newest::<usize>(cx).head()
11617 });
11618
11619 // Update the selection to match the position of the selection inside
11620 // the rename editor.
11621 let snapshot = self.buffer.read(cx).read(cx);
11622 let rename_range = rename.range.to_offset(&snapshot);
11623 let cursor_in_editor = snapshot
11624 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
11625 .min(rename_range.end);
11626 drop(snapshot);
11627
11628 self.change_selections(None, window, cx, |s| {
11629 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
11630 });
11631 } else {
11632 self.refresh_document_highlights(cx);
11633 }
11634
11635 Some(rename)
11636 }
11637
11638 pub fn pending_rename(&self) -> Option<&RenameState> {
11639 self.pending_rename.as_ref()
11640 }
11641
11642 fn format(
11643 &mut self,
11644 _: &Format,
11645 window: &mut Window,
11646 cx: &mut Context<Self>,
11647 ) -> Option<Task<Result<()>>> {
11648 let project = match &self.project {
11649 Some(project) => project.clone(),
11650 None => return None,
11651 };
11652
11653 Some(self.perform_format(
11654 project,
11655 FormatTrigger::Manual,
11656 FormatTarget::Buffers,
11657 window,
11658 cx,
11659 ))
11660 }
11661
11662 fn format_selections(
11663 &mut self,
11664 _: &FormatSelections,
11665 window: &mut Window,
11666 cx: &mut Context<Self>,
11667 ) -> Option<Task<Result<()>>> {
11668 let project = match &self.project {
11669 Some(project) => project.clone(),
11670 None => return None,
11671 };
11672
11673 let ranges = self
11674 .selections
11675 .all_adjusted(cx)
11676 .into_iter()
11677 .map(|selection| selection.range())
11678 .collect_vec();
11679
11680 Some(self.perform_format(
11681 project,
11682 FormatTrigger::Manual,
11683 FormatTarget::Ranges(ranges),
11684 window,
11685 cx,
11686 ))
11687 }
11688
11689 fn perform_format(
11690 &mut self,
11691 project: Entity<Project>,
11692 trigger: FormatTrigger,
11693 target: FormatTarget,
11694 window: &mut Window,
11695 cx: &mut Context<Self>,
11696 ) -> Task<Result<()>> {
11697 let buffer = self.buffer.clone();
11698 let (buffers, target) = match target {
11699 FormatTarget::Buffers => {
11700 let mut buffers = buffer.read(cx).all_buffers();
11701 if trigger == FormatTrigger::Save {
11702 buffers.retain(|buffer| buffer.read(cx).is_dirty());
11703 }
11704 (buffers, LspFormatTarget::Buffers)
11705 }
11706 FormatTarget::Ranges(selection_ranges) => {
11707 let multi_buffer = buffer.read(cx);
11708 let snapshot = multi_buffer.read(cx);
11709 let mut buffers = HashSet::default();
11710 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
11711 BTreeMap::new();
11712 for selection_range in selection_ranges {
11713 for (buffer, buffer_range, _) in
11714 snapshot.range_to_buffer_ranges(selection_range)
11715 {
11716 let buffer_id = buffer.remote_id();
11717 let start = buffer.anchor_before(buffer_range.start);
11718 let end = buffer.anchor_after(buffer_range.end);
11719 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
11720 buffer_id_to_ranges
11721 .entry(buffer_id)
11722 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
11723 .or_insert_with(|| vec![start..end]);
11724 }
11725 }
11726 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
11727 }
11728 };
11729
11730 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
11731 let format = project.update(cx, |project, cx| {
11732 project.format(buffers, target, true, trigger, cx)
11733 });
11734
11735 cx.spawn_in(window, |_, mut cx| async move {
11736 let transaction = futures::select_biased! {
11737 () = timeout => {
11738 log::warn!("timed out waiting for formatting");
11739 None
11740 }
11741 transaction = format.log_err().fuse() => transaction,
11742 };
11743
11744 buffer
11745 .update(&mut cx, |buffer, cx| {
11746 if let Some(transaction) = transaction {
11747 if !buffer.is_singleton() {
11748 buffer.push_transaction(&transaction.0, cx);
11749 }
11750 }
11751
11752 cx.notify();
11753 })
11754 .ok();
11755
11756 Ok(())
11757 })
11758 }
11759
11760 fn restart_language_server(
11761 &mut self,
11762 _: &RestartLanguageServer,
11763 _: &mut Window,
11764 cx: &mut Context<Self>,
11765 ) {
11766 if let Some(project) = self.project.clone() {
11767 self.buffer.update(cx, |multi_buffer, cx| {
11768 project.update(cx, |project, cx| {
11769 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
11770 });
11771 })
11772 }
11773 }
11774
11775 fn cancel_language_server_work(
11776 workspace: &mut Workspace,
11777 _: &actions::CancelLanguageServerWork,
11778 _: &mut Window,
11779 cx: &mut Context<Workspace>,
11780 ) {
11781 let project = workspace.project();
11782 let buffers = workspace
11783 .active_item(cx)
11784 .and_then(|item| item.act_as::<Editor>(cx))
11785 .map_or(HashSet::default(), |editor| {
11786 editor.read(cx).buffer.read(cx).all_buffers()
11787 });
11788 project.update(cx, |project, cx| {
11789 project.cancel_language_server_work_for_buffers(buffers, cx);
11790 });
11791 }
11792
11793 fn show_character_palette(
11794 &mut self,
11795 _: &ShowCharacterPalette,
11796 window: &mut Window,
11797 _: &mut Context<Self>,
11798 ) {
11799 window.show_character_palette();
11800 }
11801
11802 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
11803 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
11804 let buffer = self.buffer.read(cx).snapshot(cx);
11805 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
11806 let primary_range_end = active_diagnostics.primary_range.end.to_offset(&buffer);
11807 let is_valid = buffer
11808 .diagnostics_in_range::<usize>(primary_range_start..primary_range_end)
11809 .any(|entry| {
11810 entry.diagnostic.is_primary
11811 && !entry.range.is_empty()
11812 && entry.range.start == primary_range_start
11813 && entry.diagnostic.message == active_diagnostics.primary_message
11814 });
11815
11816 if is_valid != active_diagnostics.is_valid {
11817 active_diagnostics.is_valid = is_valid;
11818 let mut new_styles = HashMap::default();
11819 for (block_id, diagnostic) in &active_diagnostics.blocks {
11820 new_styles.insert(
11821 *block_id,
11822 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
11823 );
11824 }
11825 self.display_map.update(cx, |display_map, _cx| {
11826 display_map.replace_blocks(new_styles)
11827 });
11828 }
11829 }
11830 }
11831
11832 fn activate_diagnostics(
11833 &mut self,
11834 buffer_id: BufferId,
11835 group_id: usize,
11836 window: &mut Window,
11837 cx: &mut Context<Self>,
11838 ) {
11839 self.dismiss_diagnostics(cx);
11840 let snapshot = self.snapshot(window, cx);
11841 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
11842 let buffer = self.buffer.read(cx).snapshot(cx);
11843
11844 let mut primary_range = None;
11845 let mut primary_message = None;
11846 let diagnostic_group = buffer
11847 .diagnostic_group(buffer_id, group_id)
11848 .filter_map(|entry| {
11849 let start = entry.range.start;
11850 let end = entry.range.end;
11851 if snapshot.is_line_folded(MultiBufferRow(start.row))
11852 && (start.row == end.row
11853 || snapshot.is_line_folded(MultiBufferRow(end.row)))
11854 {
11855 return None;
11856 }
11857 if entry.diagnostic.is_primary {
11858 primary_range = Some(entry.range.clone());
11859 primary_message = Some(entry.diagnostic.message.clone());
11860 }
11861 Some(entry)
11862 })
11863 .collect::<Vec<_>>();
11864 let primary_range = primary_range?;
11865 let primary_message = primary_message?;
11866
11867 let blocks = display_map
11868 .insert_blocks(
11869 diagnostic_group.iter().map(|entry| {
11870 let diagnostic = entry.diagnostic.clone();
11871 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
11872 BlockProperties {
11873 style: BlockStyle::Fixed,
11874 placement: BlockPlacement::Below(
11875 buffer.anchor_after(entry.range.start),
11876 ),
11877 height: message_height,
11878 render: diagnostic_block_renderer(diagnostic, None, true, true),
11879 priority: 0,
11880 }
11881 }),
11882 cx,
11883 )
11884 .into_iter()
11885 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
11886 .collect();
11887
11888 Some(ActiveDiagnosticGroup {
11889 primary_range: buffer.anchor_before(primary_range.start)
11890 ..buffer.anchor_after(primary_range.end),
11891 primary_message,
11892 group_id,
11893 blocks,
11894 is_valid: true,
11895 })
11896 });
11897 }
11898
11899 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
11900 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
11901 self.display_map.update(cx, |display_map, cx| {
11902 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
11903 });
11904 cx.notify();
11905 }
11906 }
11907
11908 pub fn set_selections_from_remote(
11909 &mut self,
11910 selections: Vec<Selection<Anchor>>,
11911 pending_selection: Option<Selection<Anchor>>,
11912 window: &mut Window,
11913 cx: &mut Context<Self>,
11914 ) {
11915 let old_cursor_position = self.selections.newest_anchor().head();
11916 self.selections.change_with(cx, |s| {
11917 s.select_anchors(selections);
11918 if let Some(pending_selection) = pending_selection {
11919 s.set_pending(pending_selection, SelectMode::Character);
11920 } else {
11921 s.clear_pending();
11922 }
11923 });
11924 self.selections_did_change(false, &old_cursor_position, true, window, cx);
11925 }
11926
11927 fn push_to_selection_history(&mut self) {
11928 self.selection_history.push(SelectionHistoryEntry {
11929 selections: self.selections.disjoint_anchors(),
11930 select_next_state: self.select_next_state.clone(),
11931 select_prev_state: self.select_prev_state.clone(),
11932 add_selections_state: self.add_selections_state.clone(),
11933 });
11934 }
11935
11936 pub fn transact(
11937 &mut self,
11938 window: &mut Window,
11939 cx: &mut Context<Self>,
11940 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
11941 ) -> Option<TransactionId> {
11942 self.start_transaction_at(Instant::now(), window, cx);
11943 update(self, window, cx);
11944 self.end_transaction_at(Instant::now(), cx)
11945 }
11946
11947 pub fn start_transaction_at(
11948 &mut self,
11949 now: Instant,
11950 window: &mut Window,
11951 cx: &mut Context<Self>,
11952 ) {
11953 self.end_selection(window, cx);
11954 if let Some(tx_id) = self
11955 .buffer
11956 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
11957 {
11958 self.selection_history
11959 .insert_transaction(tx_id, self.selections.disjoint_anchors());
11960 cx.emit(EditorEvent::TransactionBegun {
11961 transaction_id: tx_id,
11962 })
11963 }
11964 }
11965
11966 pub fn end_transaction_at(
11967 &mut self,
11968 now: Instant,
11969 cx: &mut Context<Self>,
11970 ) -> Option<TransactionId> {
11971 if let Some(transaction_id) = self
11972 .buffer
11973 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
11974 {
11975 if let Some((_, end_selections)) =
11976 self.selection_history.transaction_mut(transaction_id)
11977 {
11978 *end_selections = Some(self.selections.disjoint_anchors());
11979 } else {
11980 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
11981 }
11982
11983 cx.emit(EditorEvent::Edited { transaction_id });
11984 Some(transaction_id)
11985 } else {
11986 None
11987 }
11988 }
11989
11990 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
11991 if self.selection_mark_mode {
11992 self.change_selections(None, window, cx, |s| {
11993 s.move_with(|_, sel| {
11994 sel.collapse_to(sel.head(), SelectionGoal::None);
11995 });
11996 })
11997 }
11998 self.selection_mark_mode = true;
11999 cx.notify();
12000 }
12001
12002 pub fn swap_selection_ends(
12003 &mut self,
12004 _: &actions::SwapSelectionEnds,
12005 window: &mut Window,
12006 cx: &mut Context<Self>,
12007 ) {
12008 self.change_selections(None, window, cx, |s| {
12009 s.move_with(|_, sel| {
12010 if sel.start != sel.end {
12011 sel.reversed = !sel.reversed
12012 }
12013 });
12014 });
12015 self.request_autoscroll(Autoscroll::newest(), cx);
12016 cx.notify();
12017 }
12018
12019 pub fn toggle_fold(
12020 &mut self,
12021 _: &actions::ToggleFold,
12022 window: &mut Window,
12023 cx: &mut Context<Self>,
12024 ) {
12025 if self.is_singleton(cx) {
12026 let selection = self.selections.newest::<Point>(cx);
12027
12028 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12029 let range = if selection.is_empty() {
12030 let point = selection.head().to_display_point(&display_map);
12031 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
12032 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
12033 .to_point(&display_map);
12034 start..end
12035 } else {
12036 selection.range()
12037 };
12038 if display_map.folds_in_range(range).next().is_some() {
12039 self.unfold_lines(&Default::default(), window, cx)
12040 } else {
12041 self.fold(&Default::default(), window, cx)
12042 }
12043 } else {
12044 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12045 let buffer_ids: HashSet<_> = multi_buffer_snapshot
12046 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
12047 .map(|(snapshot, _, _)| snapshot.remote_id())
12048 .collect();
12049
12050 for buffer_id in buffer_ids {
12051 if self.is_buffer_folded(buffer_id, cx) {
12052 self.unfold_buffer(buffer_id, cx);
12053 } else {
12054 self.fold_buffer(buffer_id, cx);
12055 }
12056 }
12057 }
12058 }
12059
12060 pub fn toggle_fold_recursive(
12061 &mut self,
12062 _: &actions::ToggleFoldRecursive,
12063 window: &mut Window,
12064 cx: &mut Context<Self>,
12065 ) {
12066 let selection = self.selections.newest::<Point>(cx);
12067
12068 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12069 let range = if selection.is_empty() {
12070 let point = selection.head().to_display_point(&display_map);
12071 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
12072 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
12073 .to_point(&display_map);
12074 start..end
12075 } else {
12076 selection.range()
12077 };
12078 if display_map.folds_in_range(range).next().is_some() {
12079 self.unfold_recursive(&Default::default(), window, cx)
12080 } else {
12081 self.fold_recursive(&Default::default(), window, cx)
12082 }
12083 }
12084
12085 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
12086 if self.is_singleton(cx) {
12087 let mut to_fold = Vec::new();
12088 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12089 let selections = self.selections.all_adjusted(cx);
12090
12091 for selection in selections {
12092 let range = selection.range().sorted();
12093 let buffer_start_row = range.start.row;
12094
12095 if range.start.row != range.end.row {
12096 let mut found = false;
12097 let mut row = range.start.row;
12098 while row <= range.end.row {
12099 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
12100 {
12101 found = true;
12102 row = crease.range().end.row + 1;
12103 to_fold.push(crease);
12104 } else {
12105 row += 1
12106 }
12107 }
12108 if found {
12109 continue;
12110 }
12111 }
12112
12113 for row in (0..=range.start.row).rev() {
12114 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
12115 if crease.range().end.row >= buffer_start_row {
12116 to_fold.push(crease);
12117 if row <= range.start.row {
12118 break;
12119 }
12120 }
12121 }
12122 }
12123 }
12124
12125 self.fold_creases(to_fold, true, window, cx);
12126 } else {
12127 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12128
12129 let buffer_ids: HashSet<_> = multi_buffer_snapshot
12130 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
12131 .map(|(snapshot, _, _)| snapshot.remote_id())
12132 .collect();
12133 for buffer_id in buffer_ids {
12134 self.fold_buffer(buffer_id, cx);
12135 }
12136 }
12137 }
12138
12139 fn fold_at_level(
12140 &mut self,
12141 fold_at: &FoldAtLevel,
12142 window: &mut Window,
12143 cx: &mut Context<Self>,
12144 ) {
12145 if !self.buffer.read(cx).is_singleton() {
12146 return;
12147 }
12148
12149 let fold_at_level = fold_at.0;
12150 let snapshot = self.buffer.read(cx).snapshot(cx);
12151 let mut to_fold = Vec::new();
12152 let mut stack = vec![(0, snapshot.max_row().0, 1)];
12153
12154 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
12155 while start_row < end_row {
12156 match self
12157 .snapshot(window, cx)
12158 .crease_for_buffer_row(MultiBufferRow(start_row))
12159 {
12160 Some(crease) => {
12161 let nested_start_row = crease.range().start.row + 1;
12162 let nested_end_row = crease.range().end.row;
12163
12164 if current_level < fold_at_level {
12165 stack.push((nested_start_row, nested_end_row, current_level + 1));
12166 } else if current_level == fold_at_level {
12167 to_fold.push(crease);
12168 }
12169
12170 start_row = nested_end_row + 1;
12171 }
12172 None => start_row += 1,
12173 }
12174 }
12175 }
12176
12177 self.fold_creases(to_fold, true, window, cx);
12178 }
12179
12180 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
12181 if self.buffer.read(cx).is_singleton() {
12182 let mut fold_ranges = Vec::new();
12183 let snapshot = self.buffer.read(cx).snapshot(cx);
12184
12185 for row in 0..snapshot.max_row().0 {
12186 if let Some(foldable_range) = self
12187 .snapshot(window, cx)
12188 .crease_for_buffer_row(MultiBufferRow(row))
12189 {
12190 fold_ranges.push(foldable_range);
12191 }
12192 }
12193
12194 self.fold_creases(fold_ranges, true, window, cx);
12195 } else {
12196 self.toggle_fold_multiple_buffers = cx.spawn_in(window, |editor, mut cx| async move {
12197 editor
12198 .update_in(&mut cx, |editor, _, cx| {
12199 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
12200 editor.fold_buffer(buffer_id, cx);
12201 }
12202 })
12203 .ok();
12204 });
12205 }
12206 }
12207
12208 pub fn fold_function_bodies(
12209 &mut self,
12210 _: &actions::FoldFunctionBodies,
12211 window: &mut Window,
12212 cx: &mut Context<Self>,
12213 ) {
12214 let snapshot = self.buffer.read(cx).snapshot(cx);
12215
12216 let ranges = snapshot
12217 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
12218 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
12219 .collect::<Vec<_>>();
12220
12221 let creases = ranges
12222 .into_iter()
12223 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
12224 .collect();
12225
12226 self.fold_creases(creases, true, window, cx);
12227 }
12228
12229 pub fn fold_recursive(
12230 &mut self,
12231 _: &actions::FoldRecursive,
12232 window: &mut Window,
12233 cx: &mut Context<Self>,
12234 ) {
12235 let mut to_fold = Vec::new();
12236 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12237 let selections = self.selections.all_adjusted(cx);
12238
12239 for selection in selections {
12240 let range = selection.range().sorted();
12241 let buffer_start_row = range.start.row;
12242
12243 if range.start.row != range.end.row {
12244 let mut found = false;
12245 for row in range.start.row..=range.end.row {
12246 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
12247 found = true;
12248 to_fold.push(crease);
12249 }
12250 }
12251 if found {
12252 continue;
12253 }
12254 }
12255
12256 for row in (0..=range.start.row).rev() {
12257 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
12258 if crease.range().end.row >= buffer_start_row {
12259 to_fold.push(crease);
12260 } else {
12261 break;
12262 }
12263 }
12264 }
12265 }
12266
12267 self.fold_creases(to_fold, true, window, cx);
12268 }
12269
12270 pub fn fold_at(&mut self, fold_at: &FoldAt, window: &mut Window, cx: &mut Context<Self>) {
12271 let buffer_row = fold_at.buffer_row;
12272 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12273
12274 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
12275 let autoscroll = self
12276 .selections
12277 .all::<Point>(cx)
12278 .iter()
12279 .any(|selection| crease.range().overlaps(&selection.range()));
12280
12281 self.fold_creases(vec![crease], autoscroll, window, cx);
12282 }
12283 }
12284
12285 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
12286 if self.is_singleton(cx) {
12287 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12288 let buffer = &display_map.buffer_snapshot;
12289 let selections = self.selections.all::<Point>(cx);
12290 let ranges = selections
12291 .iter()
12292 .map(|s| {
12293 let range = s.display_range(&display_map).sorted();
12294 let mut start = range.start.to_point(&display_map);
12295 let mut end = range.end.to_point(&display_map);
12296 start.column = 0;
12297 end.column = buffer.line_len(MultiBufferRow(end.row));
12298 start..end
12299 })
12300 .collect::<Vec<_>>();
12301
12302 self.unfold_ranges(&ranges, true, true, cx);
12303 } else {
12304 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12305 let buffer_ids: HashSet<_> = multi_buffer_snapshot
12306 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
12307 .map(|(snapshot, _, _)| snapshot.remote_id())
12308 .collect();
12309 for buffer_id in buffer_ids {
12310 self.unfold_buffer(buffer_id, cx);
12311 }
12312 }
12313 }
12314
12315 pub fn unfold_recursive(
12316 &mut self,
12317 _: &UnfoldRecursive,
12318 _window: &mut Window,
12319 cx: &mut Context<Self>,
12320 ) {
12321 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12322 let selections = self.selections.all::<Point>(cx);
12323 let ranges = selections
12324 .iter()
12325 .map(|s| {
12326 let mut range = s.display_range(&display_map).sorted();
12327 *range.start.column_mut() = 0;
12328 *range.end.column_mut() = display_map.line_len(range.end.row());
12329 let start = range.start.to_point(&display_map);
12330 let end = range.end.to_point(&display_map);
12331 start..end
12332 })
12333 .collect::<Vec<_>>();
12334
12335 self.unfold_ranges(&ranges, true, true, cx);
12336 }
12337
12338 pub fn unfold_at(
12339 &mut self,
12340 unfold_at: &UnfoldAt,
12341 _window: &mut Window,
12342 cx: &mut Context<Self>,
12343 ) {
12344 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12345
12346 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
12347 ..Point::new(
12348 unfold_at.buffer_row.0,
12349 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
12350 );
12351
12352 let autoscroll = self
12353 .selections
12354 .all::<Point>(cx)
12355 .iter()
12356 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
12357
12358 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
12359 }
12360
12361 pub fn unfold_all(
12362 &mut self,
12363 _: &actions::UnfoldAll,
12364 _window: &mut Window,
12365 cx: &mut Context<Self>,
12366 ) {
12367 if self.buffer.read(cx).is_singleton() {
12368 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12369 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
12370 } else {
12371 self.toggle_fold_multiple_buffers = cx.spawn(|editor, mut cx| async move {
12372 editor
12373 .update(&mut cx, |editor, cx| {
12374 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
12375 editor.unfold_buffer(buffer_id, cx);
12376 }
12377 })
12378 .ok();
12379 });
12380 }
12381 }
12382
12383 pub fn fold_selected_ranges(
12384 &mut self,
12385 _: &FoldSelectedRanges,
12386 window: &mut Window,
12387 cx: &mut Context<Self>,
12388 ) {
12389 let selections = self.selections.all::<Point>(cx);
12390 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12391 let line_mode = self.selections.line_mode;
12392 let ranges = selections
12393 .into_iter()
12394 .map(|s| {
12395 if line_mode {
12396 let start = Point::new(s.start.row, 0);
12397 let end = Point::new(
12398 s.end.row,
12399 display_map
12400 .buffer_snapshot
12401 .line_len(MultiBufferRow(s.end.row)),
12402 );
12403 Crease::simple(start..end, display_map.fold_placeholder.clone())
12404 } else {
12405 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
12406 }
12407 })
12408 .collect::<Vec<_>>();
12409 self.fold_creases(ranges, true, window, cx);
12410 }
12411
12412 pub fn fold_ranges<T: ToOffset + Clone>(
12413 &mut self,
12414 ranges: Vec<Range<T>>,
12415 auto_scroll: bool,
12416 window: &mut Window,
12417 cx: &mut Context<Self>,
12418 ) {
12419 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12420 let ranges = ranges
12421 .into_iter()
12422 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
12423 .collect::<Vec<_>>();
12424 self.fold_creases(ranges, auto_scroll, window, cx);
12425 }
12426
12427 pub fn fold_creases<T: ToOffset + Clone>(
12428 &mut self,
12429 creases: Vec<Crease<T>>,
12430 auto_scroll: bool,
12431 window: &mut Window,
12432 cx: &mut Context<Self>,
12433 ) {
12434 if creases.is_empty() {
12435 return;
12436 }
12437
12438 let mut buffers_affected = HashSet::default();
12439 let multi_buffer = self.buffer().read(cx);
12440 for crease in &creases {
12441 if let Some((_, buffer, _)) =
12442 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
12443 {
12444 buffers_affected.insert(buffer.read(cx).remote_id());
12445 };
12446 }
12447
12448 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
12449
12450 if auto_scroll {
12451 self.request_autoscroll(Autoscroll::fit(), cx);
12452 }
12453
12454 cx.notify();
12455
12456 if let Some(active_diagnostics) = self.active_diagnostics.take() {
12457 // Clear diagnostics block when folding a range that contains it.
12458 let snapshot = self.snapshot(window, cx);
12459 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
12460 drop(snapshot);
12461 self.active_diagnostics = Some(active_diagnostics);
12462 self.dismiss_diagnostics(cx);
12463 } else {
12464 self.active_diagnostics = Some(active_diagnostics);
12465 }
12466 }
12467
12468 self.scrollbar_marker_state.dirty = true;
12469 }
12470
12471 /// Removes any folds whose ranges intersect any of the given ranges.
12472 pub fn unfold_ranges<T: ToOffset + Clone>(
12473 &mut self,
12474 ranges: &[Range<T>],
12475 inclusive: bool,
12476 auto_scroll: bool,
12477 cx: &mut Context<Self>,
12478 ) {
12479 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
12480 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
12481 });
12482 }
12483
12484 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
12485 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
12486 return;
12487 }
12488 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
12489 self.display_map
12490 .update(cx, |display_map, cx| display_map.fold_buffer(buffer_id, cx));
12491 cx.emit(EditorEvent::BufferFoldToggled {
12492 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
12493 folded: true,
12494 });
12495 cx.notify();
12496 }
12497
12498 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
12499 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
12500 return;
12501 }
12502 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
12503 self.display_map.update(cx, |display_map, cx| {
12504 display_map.unfold_buffer(buffer_id, cx);
12505 });
12506 cx.emit(EditorEvent::BufferFoldToggled {
12507 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
12508 folded: false,
12509 });
12510 cx.notify();
12511 }
12512
12513 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
12514 self.display_map.read(cx).is_buffer_folded(buffer)
12515 }
12516
12517 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
12518 self.display_map.read(cx).folded_buffers()
12519 }
12520
12521 /// Removes any folds with the given ranges.
12522 pub fn remove_folds_with_type<T: ToOffset + Clone>(
12523 &mut self,
12524 ranges: &[Range<T>],
12525 type_id: TypeId,
12526 auto_scroll: bool,
12527 cx: &mut Context<Self>,
12528 ) {
12529 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
12530 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
12531 });
12532 }
12533
12534 fn remove_folds_with<T: ToOffset + Clone>(
12535 &mut self,
12536 ranges: &[Range<T>],
12537 auto_scroll: bool,
12538 cx: &mut Context<Self>,
12539 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
12540 ) {
12541 if ranges.is_empty() {
12542 return;
12543 }
12544
12545 let mut buffers_affected = HashSet::default();
12546 let multi_buffer = self.buffer().read(cx);
12547 for range in ranges {
12548 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
12549 buffers_affected.insert(buffer.read(cx).remote_id());
12550 };
12551 }
12552
12553 self.display_map.update(cx, update);
12554
12555 if auto_scroll {
12556 self.request_autoscroll(Autoscroll::fit(), cx);
12557 }
12558
12559 cx.notify();
12560 self.scrollbar_marker_state.dirty = true;
12561 self.active_indent_guides_state.dirty = true;
12562 }
12563
12564 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
12565 self.display_map.read(cx).fold_placeholder.clone()
12566 }
12567
12568 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
12569 self.buffer.update(cx, |buffer, cx| {
12570 buffer.set_all_diff_hunks_expanded(cx);
12571 });
12572 }
12573
12574 pub fn set_distinguish_unstaged_diff_hunks(&mut self) {
12575 self.distinguish_unstaged_diff_hunks = true;
12576 }
12577
12578 pub fn expand_all_diff_hunks(
12579 &mut self,
12580 _: &ExpandAllHunkDiffs,
12581 _window: &mut Window,
12582 cx: &mut Context<Self>,
12583 ) {
12584 self.buffer.update(cx, |buffer, cx| {
12585 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
12586 });
12587 }
12588
12589 pub fn toggle_selected_diff_hunks(
12590 &mut self,
12591 _: &ToggleSelectedDiffHunks,
12592 _window: &mut Window,
12593 cx: &mut Context<Self>,
12594 ) {
12595 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
12596 self.toggle_diff_hunks_in_ranges(ranges, cx);
12597 }
12598
12599 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
12600 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
12601 self.buffer
12602 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
12603 }
12604
12605 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
12606 self.buffer.update(cx, |buffer, cx| {
12607 let ranges = vec![Anchor::min()..Anchor::max()];
12608 if !buffer.all_diff_hunks_expanded()
12609 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
12610 {
12611 buffer.collapse_diff_hunks(ranges, cx);
12612 true
12613 } else {
12614 false
12615 }
12616 })
12617 }
12618
12619 fn toggle_diff_hunks_in_ranges(
12620 &mut self,
12621 ranges: Vec<Range<Anchor>>,
12622 cx: &mut Context<'_, Editor>,
12623 ) {
12624 self.buffer.update(cx, |buffer, cx| {
12625 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
12626 buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
12627 })
12628 }
12629
12630 fn toggle_diff_hunks_in_ranges_narrow(
12631 &mut self,
12632 ranges: Vec<Range<Anchor>>,
12633 cx: &mut Context<'_, Editor>,
12634 ) {
12635 self.buffer.update(cx, |buffer, cx| {
12636 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
12637 buffer.expand_or_collapse_diff_hunks_narrow(ranges, expand, cx);
12638 })
12639 }
12640
12641 pub(crate) fn apply_all_diff_hunks(
12642 &mut self,
12643 _: &ApplyAllDiffHunks,
12644 window: &mut Window,
12645 cx: &mut Context<Self>,
12646 ) {
12647 let buffers = self.buffer.read(cx).all_buffers();
12648 for branch_buffer in buffers {
12649 branch_buffer.update(cx, |branch_buffer, cx| {
12650 branch_buffer.merge_into_base(Vec::new(), cx);
12651 });
12652 }
12653
12654 if let Some(project) = self.project.clone() {
12655 self.save(true, project, window, cx).detach_and_log_err(cx);
12656 }
12657 }
12658
12659 pub(crate) fn apply_selected_diff_hunks(
12660 &mut self,
12661 _: &ApplyDiffHunk,
12662 window: &mut Window,
12663 cx: &mut Context<Self>,
12664 ) {
12665 let snapshot = self.snapshot(window, cx);
12666 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx).into_iter());
12667 let mut ranges_by_buffer = HashMap::default();
12668 self.transact(window, cx, |editor, _window, cx| {
12669 for hunk in hunks {
12670 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
12671 ranges_by_buffer
12672 .entry(buffer.clone())
12673 .or_insert_with(Vec::new)
12674 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
12675 }
12676 }
12677
12678 for (buffer, ranges) in ranges_by_buffer {
12679 buffer.update(cx, |buffer, cx| {
12680 buffer.merge_into_base(ranges, cx);
12681 });
12682 }
12683 });
12684
12685 if let Some(project) = self.project.clone() {
12686 self.save(true, project, window, cx).detach_and_log_err(cx);
12687 }
12688 }
12689
12690 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
12691 if hovered != self.gutter_hovered {
12692 self.gutter_hovered = hovered;
12693 cx.notify();
12694 }
12695 }
12696
12697 pub fn insert_blocks(
12698 &mut self,
12699 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
12700 autoscroll: Option<Autoscroll>,
12701 cx: &mut Context<Self>,
12702 ) -> Vec<CustomBlockId> {
12703 let blocks = self
12704 .display_map
12705 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
12706 if let Some(autoscroll) = autoscroll {
12707 self.request_autoscroll(autoscroll, cx);
12708 }
12709 cx.notify();
12710 blocks
12711 }
12712
12713 pub fn resize_blocks(
12714 &mut self,
12715 heights: HashMap<CustomBlockId, u32>,
12716 autoscroll: Option<Autoscroll>,
12717 cx: &mut Context<Self>,
12718 ) {
12719 self.display_map
12720 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
12721 if let Some(autoscroll) = autoscroll {
12722 self.request_autoscroll(autoscroll, cx);
12723 }
12724 cx.notify();
12725 }
12726
12727 pub fn replace_blocks(
12728 &mut self,
12729 renderers: HashMap<CustomBlockId, RenderBlock>,
12730 autoscroll: Option<Autoscroll>,
12731 cx: &mut Context<Self>,
12732 ) {
12733 self.display_map
12734 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
12735 if let Some(autoscroll) = autoscroll {
12736 self.request_autoscroll(autoscroll, cx);
12737 }
12738 cx.notify();
12739 }
12740
12741 pub fn remove_blocks(
12742 &mut self,
12743 block_ids: HashSet<CustomBlockId>,
12744 autoscroll: Option<Autoscroll>,
12745 cx: &mut Context<Self>,
12746 ) {
12747 self.display_map.update(cx, |display_map, cx| {
12748 display_map.remove_blocks(block_ids, cx)
12749 });
12750 if let Some(autoscroll) = autoscroll {
12751 self.request_autoscroll(autoscroll, cx);
12752 }
12753 cx.notify();
12754 }
12755
12756 pub fn row_for_block(
12757 &self,
12758 block_id: CustomBlockId,
12759 cx: &mut Context<Self>,
12760 ) -> Option<DisplayRow> {
12761 self.display_map
12762 .update(cx, |map, cx| map.row_for_block(block_id, cx))
12763 }
12764
12765 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
12766 self.focused_block = Some(focused_block);
12767 }
12768
12769 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
12770 self.focused_block.take()
12771 }
12772
12773 pub fn insert_creases(
12774 &mut self,
12775 creases: impl IntoIterator<Item = Crease<Anchor>>,
12776 cx: &mut Context<Self>,
12777 ) -> Vec<CreaseId> {
12778 self.display_map
12779 .update(cx, |map, cx| map.insert_creases(creases, cx))
12780 }
12781
12782 pub fn remove_creases(
12783 &mut self,
12784 ids: impl IntoIterator<Item = CreaseId>,
12785 cx: &mut Context<Self>,
12786 ) {
12787 self.display_map
12788 .update(cx, |map, cx| map.remove_creases(ids, cx));
12789 }
12790
12791 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
12792 self.display_map
12793 .update(cx, |map, cx| map.snapshot(cx))
12794 .longest_row()
12795 }
12796
12797 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
12798 self.display_map
12799 .update(cx, |map, cx| map.snapshot(cx))
12800 .max_point()
12801 }
12802
12803 pub fn text(&self, cx: &App) -> String {
12804 self.buffer.read(cx).read(cx).text()
12805 }
12806
12807 pub fn is_empty(&self, cx: &App) -> bool {
12808 self.buffer.read(cx).read(cx).is_empty()
12809 }
12810
12811 pub fn text_option(&self, cx: &App) -> Option<String> {
12812 let text = self.text(cx);
12813 let text = text.trim();
12814
12815 if text.is_empty() {
12816 return None;
12817 }
12818
12819 Some(text.to_string())
12820 }
12821
12822 pub fn set_text(
12823 &mut self,
12824 text: impl Into<Arc<str>>,
12825 window: &mut Window,
12826 cx: &mut Context<Self>,
12827 ) {
12828 self.transact(window, cx, |this, _, cx| {
12829 this.buffer
12830 .read(cx)
12831 .as_singleton()
12832 .expect("you can only call set_text on editors for singleton buffers")
12833 .update(cx, |buffer, cx| buffer.set_text(text, cx));
12834 });
12835 }
12836
12837 pub fn display_text(&self, cx: &mut App) -> String {
12838 self.display_map
12839 .update(cx, |map, cx| map.snapshot(cx))
12840 .text()
12841 }
12842
12843 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
12844 let mut wrap_guides = smallvec::smallvec![];
12845
12846 if self.show_wrap_guides == Some(false) {
12847 return wrap_guides;
12848 }
12849
12850 let settings = self.buffer.read(cx).settings_at(0, cx);
12851 if settings.show_wrap_guides {
12852 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
12853 wrap_guides.push((soft_wrap as usize, true));
12854 } else if let SoftWrap::Bounded(soft_wrap) = self.soft_wrap_mode(cx) {
12855 wrap_guides.push((soft_wrap as usize, true));
12856 }
12857 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
12858 }
12859
12860 wrap_guides
12861 }
12862
12863 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
12864 let settings = self.buffer.read(cx).settings_at(0, cx);
12865 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
12866 match mode {
12867 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
12868 SoftWrap::None
12869 }
12870 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
12871 language_settings::SoftWrap::PreferredLineLength => {
12872 SoftWrap::Column(settings.preferred_line_length)
12873 }
12874 language_settings::SoftWrap::Bounded => {
12875 SoftWrap::Bounded(settings.preferred_line_length)
12876 }
12877 }
12878 }
12879
12880 pub fn set_soft_wrap_mode(
12881 &mut self,
12882 mode: language_settings::SoftWrap,
12883
12884 cx: &mut Context<Self>,
12885 ) {
12886 self.soft_wrap_mode_override = Some(mode);
12887 cx.notify();
12888 }
12889
12890 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
12891 self.text_style_refinement = Some(style);
12892 }
12893
12894 /// called by the Element so we know what style we were most recently rendered with.
12895 pub(crate) fn set_style(
12896 &mut self,
12897 style: EditorStyle,
12898 window: &mut Window,
12899 cx: &mut Context<Self>,
12900 ) {
12901 let rem_size = window.rem_size();
12902 self.display_map.update(cx, |map, cx| {
12903 map.set_font(
12904 style.text.font(),
12905 style.text.font_size.to_pixels(rem_size),
12906 cx,
12907 )
12908 });
12909 self.style = Some(style);
12910 }
12911
12912 pub fn style(&self) -> Option<&EditorStyle> {
12913 self.style.as_ref()
12914 }
12915
12916 // Called by the element. This method is not designed to be called outside of the editor
12917 // element's layout code because it does not notify when rewrapping is computed synchronously.
12918 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
12919 self.display_map
12920 .update(cx, |map, cx| map.set_wrap_width(width, cx))
12921 }
12922
12923 pub fn set_soft_wrap(&mut self) {
12924 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
12925 }
12926
12927 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
12928 if self.soft_wrap_mode_override.is_some() {
12929 self.soft_wrap_mode_override.take();
12930 } else {
12931 let soft_wrap = match self.soft_wrap_mode(cx) {
12932 SoftWrap::GitDiff => return,
12933 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
12934 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
12935 language_settings::SoftWrap::None
12936 }
12937 };
12938 self.soft_wrap_mode_override = Some(soft_wrap);
12939 }
12940 cx.notify();
12941 }
12942
12943 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
12944 let Some(workspace) = self.workspace() else {
12945 return;
12946 };
12947 let fs = workspace.read(cx).app_state().fs.clone();
12948 let current_show = TabBarSettings::get_global(cx).show;
12949 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
12950 setting.show = Some(!current_show);
12951 });
12952 }
12953
12954 pub fn toggle_indent_guides(
12955 &mut self,
12956 _: &ToggleIndentGuides,
12957 _: &mut Window,
12958 cx: &mut Context<Self>,
12959 ) {
12960 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
12961 self.buffer
12962 .read(cx)
12963 .settings_at(0, cx)
12964 .indent_guides
12965 .enabled
12966 });
12967 self.show_indent_guides = Some(!currently_enabled);
12968 cx.notify();
12969 }
12970
12971 fn should_show_indent_guides(&self) -> Option<bool> {
12972 self.show_indent_guides
12973 }
12974
12975 pub fn toggle_line_numbers(
12976 &mut self,
12977 _: &ToggleLineNumbers,
12978 _: &mut Window,
12979 cx: &mut Context<Self>,
12980 ) {
12981 let mut editor_settings = EditorSettings::get_global(cx).clone();
12982 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
12983 EditorSettings::override_global(editor_settings, cx);
12984 }
12985
12986 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
12987 self.use_relative_line_numbers
12988 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
12989 }
12990
12991 pub fn toggle_relative_line_numbers(
12992 &mut self,
12993 _: &ToggleRelativeLineNumbers,
12994 _: &mut Window,
12995 cx: &mut Context<Self>,
12996 ) {
12997 let is_relative = self.should_use_relative_line_numbers(cx);
12998 self.set_relative_line_number(Some(!is_relative), cx)
12999 }
13000
13001 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
13002 self.use_relative_line_numbers = is_relative;
13003 cx.notify();
13004 }
13005
13006 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
13007 self.show_gutter = show_gutter;
13008 cx.notify();
13009 }
13010
13011 pub fn set_show_scrollbars(&mut self, show_scrollbars: bool, cx: &mut Context<Self>) {
13012 self.show_scrollbars = show_scrollbars;
13013 cx.notify();
13014 }
13015
13016 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
13017 self.show_line_numbers = Some(show_line_numbers);
13018 cx.notify();
13019 }
13020
13021 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
13022 self.show_git_diff_gutter = Some(show_git_diff_gutter);
13023 cx.notify();
13024 }
13025
13026 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
13027 self.show_code_actions = Some(show_code_actions);
13028 cx.notify();
13029 }
13030
13031 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
13032 self.show_runnables = Some(show_runnables);
13033 cx.notify();
13034 }
13035
13036 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
13037 if self.display_map.read(cx).masked != masked {
13038 self.display_map.update(cx, |map, _| map.masked = masked);
13039 }
13040 cx.notify()
13041 }
13042
13043 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
13044 self.show_wrap_guides = Some(show_wrap_guides);
13045 cx.notify();
13046 }
13047
13048 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
13049 self.show_indent_guides = Some(show_indent_guides);
13050 cx.notify();
13051 }
13052
13053 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
13054 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
13055 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
13056 if let Some(dir) = file.abs_path(cx).parent() {
13057 return Some(dir.to_owned());
13058 }
13059 }
13060
13061 if let Some(project_path) = buffer.read(cx).project_path(cx) {
13062 return Some(project_path.path.to_path_buf());
13063 }
13064 }
13065
13066 None
13067 }
13068
13069 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
13070 self.active_excerpt(cx)?
13071 .1
13072 .read(cx)
13073 .file()
13074 .and_then(|f| f.as_local())
13075 }
13076
13077 fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
13078 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
13079 let project_path = buffer.read(cx).project_path(cx)?;
13080 let project = self.project.as_ref()?.read(cx);
13081 project.absolute_path(&project_path, cx)
13082 })
13083 }
13084
13085 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
13086 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
13087 let project_path = buffer.read(cx).project_path(cx)?;
13088 let project = self.project.as_ref()?.read(cx);
13089 let entry = project.entry_for_path(&project_path, cx)?;
13090 let path = entry.path.to_path_buf();
13091 Some(path)
13092 })
13093 }
13094
13095 pub fn reveal_in_finder(
13096 &mut self,
13097 _: &RevealInFileManager,
13098 _window: &mut Window,
13099 cx: &mut Context<Self>,
13100 ) {
13101 if let Some(target) = self.target_file(cx) {
13102 cx.reveal_path(&target.abs_path(cx));
13103 }
13104 }
13105
13106 pub fn copy_path(&mut self, _: &CopyPath, _window: &mut Window, cx: &mut Context<Self>) {
13107 if let Some(path) = self.target_file_abs_path(cx) {
13108 if let Some(path) = path.to_str() {
13109 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
13110 }
13111 }
13112 }
13113
13114 pub fn copy_relative_path(
13115 &mut self,
13116 _: &CopyRelativePath,
13117 _window: &mut Window,
13118 cx: &mut Context<Self>,
13119 ) {
13120 if let Some(path) = self.target_file_path(cx) {
13121 if let Some(path) = path.to_str() {
13122 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
13123 }
13124 }
13125 }
13126
13127 pub fn copy_file_name_without_extension(
13128 &mut self,
13129 _: &CopyFileNameWithoutExtension,
13130 _: &mut Window,
13131 cx: &mut Context<Self>,
13132 ) {
13133 if let Some(file) = self.target_file(cx) {
13134 if let Some(file_stem) = file.path().file_stem() {
13135 if let Some(name) = file_stem.to_str() {
13136 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
13137 }
13138 }
13139 }
13140 }
13141
13142 pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
13143 if let Some(file) = self.target_file(cx) {
13144 if let Some(file_name) = file.path().file_name() {
13145 if let Some(name) = file_name.to_str() {
13146 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
13147 }
13148 }
13149 }
13150 }
13151
13152 pub fn toggle_git_blame(
13153 &mut self,
13154 _: &ToggleGitBlame,
13155 window: &mut Window,
13156 cx: &mut Context<Self>,
13157 ) {
13158 self.show_git_blame_gutter = !self.show_git_blame_gutter;
13159
13160 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
13161 self.start_git_blame(true, window, cx);
13162 }
13163
13164 cx.notify();
13165 }
13166
13167 pub fn toggle_git_blame_inline(
13168 &mut self,
13169 _: &ToggleGitBlameInline,
13170 window: &mut Window,
13171 cx: &mut Context<Self>,
13172 ) {
13173 self.toggle_git_blame_inline_internal(true, window, cx);
13174 cx.notify();
13175 }
13176
13177 pub fn git_blame_inline_enabled(&self) -> bool {
13178 self.git_blame_inline_enabled
13179 }
13180
13181 pub fn toggle_selection_menu(
13182 &mut self,
13183 _: &ToggleSelectionMenu,
13184 _: &mut Window,
13185 cx: &mut Context<Self>,
13186 ) {
13187 self.show_selection_menu = self
13188 .show_selection_menu
13189 .map(|show_selections_menu| !show_selections_menu)
13190 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
13191
13192 cx.notify();
13193 }
13194
13195 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
13196 self.show_selection_menu
13197 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
13198 }
13199
13200 fn start_git_blame(
13201 &mut self,
13202 user_triggered: bool,
13203 window: &mut Window,
13204 cx: &mut Context<Self>,
13205 ) {
13206 if let Some(project) = self.project.as_ref() {
13207 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
13208 return;
13209 };
13210
13211 if buffer.read(cx).file().is_none() {
13212 return;
13213 }
13214
13215 let focused = self.focus_handle(cx).contains_focused(window, cx);
13216
13217 let project = project.clone();
13218 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
13219 self.blame_subscription =
13220 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
13221 self.blame = Some(blame);
13222 }
13223 }
13224
13225 fn toggle_git_blame_inline_internal(
13226 &mut self,
13227 user_triggered: bool,
13228 window: &mut Window,
13229 cx: &mut Context<Self>,
13230 ) {
13231 if self.git_blame_inline_enabled {
13232 self.git_blame_inline_enabled = false;
13233 self.show_git_blame_inline = false;
13234 self.show_git_blame_inline_delay_task.take();
13235 } else {
13236 self.git_blame_inline_enabled = true;
13237 self.start_git_blame_inline(user_triggered, window, cx);
13238 }
13239
13240 cx.notify();
13241 }
13242
13243 fn start_git_blame_inline(
13244 &mut self,
13245 user_triggered: bool,
13246 window: &mut Window,
13247 cx: &mut Context<Self>,
13248 ) {
13249 self.start_git_blame(user_triggered, window, cx);
13250
13251 if ProjectSettings::get_global(cx)
13252 .git
13253 .inline_blame_delay()
13254 .is_some()
13255 {
13256 self.start_inline_blame_timer(window, cx);
13257 } else {
13258 self.show_git_blame_inline = true
13259 }
13260 }
13261
13262 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
13263 self.blame.as_ref()
13264 }
13265
13266 pub fn show_git_blame_gutter(&self) -> bool {
13267 self.show_git_blame_gutter
13268 }
13269
13270 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
13271 self.show_git_blame_gutter && self.has_blame_entries(cx)
13272 }
13273
13274 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
13275 self.show_git_blame_inline
13276 && self.focus_handle.is_focused(window)
13277 && !self.newest_selection_head_on_empty_line(cx)
13278 && self.has_blame_entries(cx)
13279 }
13280
13281 fn has_blame_entries(&self, cx: &App) -> bool {
13282 self.blame()
13283 .map_or(false, |blame| blame.read(cx).has_generated_entries())
13284 }
13285
13286 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
13287 let cursor_anchor = self.selections.newest_anchor().head();
13288
13289 let snapshot = self.buffer.read(cx).snapshot(cx);
13290 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
13291
13292 snapshot.line_len(buffer_row) == 0
13293 }
13294
13295 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
13296 let buffer_and_selection = maybe!({
13297 let selection = self.selections.newest::<Point>(cx);
13298 let selection_range = selection.range();
13299
13300 let multi_buffer = self.buffer().read(cx);
13301 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
13302 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
13303
13304 let (buffer, range, _) = if selection.reversed {
13305 buffer_ranges.first()
13306 } else {
13307 buffer_ranges.last()
13308 }?;
13309
13310 let selection = text::ToPoint::to_point(&range.start, &buffer).row
13311 ..text::ToPoint::to_point(&range.end, &buffer).row;
13312 Some((
13313 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
13314 selection,
13315 ))
13316 });
13317
13318 let Some((buffer, selection)) = buffer_and_selection else {
13319 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
13320 };
13321
13322 let Some(project) = self.project.as_ref() else {
13323 return Task::ready(Err(anyhow!("editor does not have project")));
13324 };
13325
13326 project.update(cx, |project, cx| {
13327 project.get_permalink_to_line(&buffer, selection, cx)
13328 })
13329 }
13330
13331 pub fn copy_permalink_to_line(
13332 &mut self,
13333 _: &CopyPermalinkToLine,
13334 window: &mut Window,
13335 cx: &mut Context<Self>,
13336 ) {
13337 let permalink_task = self.get_permalink_to_line(cx);
13338 let workspace = self.workspace();
13339
13340 cx.spawn_in(window, |_, mut cx| async move {
13341 match permalink_task.await {
13342 Ok(permalink) => {
13343 cx.update(|_, cx| {
13344 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
13345 })
13346 .ok();
13347 }
13348 Err(err) => {
13349 let message = format!("Failed to copy permalink: {err}");
13350
13351 Err::<(), anyhow::Error>(err).log_err();
13352
13353 if let Some(workspace) = workspace {
13354 workspace
13355 .update_in(&mut cx, |workspace, _, cx| {
13356 struct CopyPermalinkToLine;
13357
13358 workspace.show_toast(
13359 Toast::new(
13360 NotificationId::unique::<CopyPermalinkToLine>(),
13361 message,
13362 ),
13363 cx,
13364 )
13365 })
13366 .ok();
13367 }
13368 }
13369 }
13370 })
13371 .detach();
13372 }
13373
13374 pub fn copy_file_location(
13375 &mut self,
13376 _: &CopyFileLocation,
13377 _: &mut Window,
13378 cx: &mut Context<Self>,
13379 ) {
13380 let selection = self.selections.newest::<Point>(cx).start.row + 1;
13381 if let Some(file) = self.target_file(cx) {
13382 if let Some(path) = file.path().to_str() {
13383 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
13384 }
13385 }
13386 }
13387
13388 pub fn open_permalink_to_line(
13389 &mut self,
13390 _: &OpenPermalinkToLine,
13391 window: &mut Window,
13392 cx: &mut Context<Self>,
13393 ) {
13394 let permalink_task = self.get_permalink_to_line(cx);
13395 let workspace = self.workspace();
13396
13397 cx.spawn_in(window, |_, mut cx| async move {
13398 match permalink_task.await {
13399 Ok(permalink) => {
13400 cx.update(|_, cx| {
13401 cx.open_url(permalink.as_ref());
13402 })
13403 .ok();
13404 }
13405 Err(err) => {
13406 let message = format!("Failed to open permalink: {err}");
13407
13408 Err::<(), anyhow::Error>(err).log_err();
13409
13410 if let Some(workspace) = workspace {
13411 workspace
13412 .update(&mut cx, |workspace, cx| {
13413 struct OpenPermalinkToLine;
13414
13415 workspace.show_toast(
13416 Toast::new(
13417 NotificationId::unique::<OpenPermalinkToLine>(),
13418 message,
13419 ),
13420 cx,
13421 )
13422 })
13423 .ok();
13424 }
13425 }
13426 }
13427 })
13428 .detach();
13429 }
13430
13431 pub fn insert_uuid_v4(
13432 &mut self,
13433 _: &InsertUuidV4,
13434 window: &mut Window,
13435 cx: &mut Context<Self>,
13436 ) {
13437 self.insert_uuid(UuidVersion::V4, window, cx);
13438 }
13439
13440 pub fn insert_uuid_v7(
13441 &mut self,
13442 _: &InsertUuidV7,
13443 window: &mut Window,
13444 cx: &mut Context<Self>,
13445 ) {
13446 self.insert_uuid(UuidVersion::V7, window, cx);
13447 }
13448
13449 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
13450 self.transact(window, cx, |this, window, cx| {
13451 let edits = this
13452 .selections
13453 .all::<Point>(cx)
13454 .into_iter()
13455 .map(|selection| {
13456 let uuid = match version {
13457 UuidVersion::V4 => uuid::Uuid::new_v4(),
13458 UuidVersion::V7 => uuid::Uuid::now_v7(),
13459 };
13460
13461 (selection.range(), uuid.to_string())
13462 });
13463 this.edit(edits, cx);
13464 this.refresh_inline_completion(true, false, window, cx);
13465 });
13466 }
13467
13468 pub fn open_selections_in_multibuffer(
13469 &mut self,
13470 _: &OpenSelectionsInMultibuffer,
13471 window: &mut Window,
13472 cx: &mut Context<Self>,
13473 ) {
13474 let multibuffer = self.buffer.read(cx);
13475
13476 let Some(buffer) = multibuffer.as_singleton() else {
13477 return;
13478 };
13479
13480 let Some(workspace) = self.workspace() else {
13481 return;
13482 };
13483
13484 let locations = self
13485 .selections
13486 .disjoint_anchors()
13487 .iter()
13488 .map(|range| Location {
13489 buffer: buffer.clone(),
13490 range: range.start.text_anchor..range.end.text_anchor,
13491 })
13492 .collect::<Vec<_>>();
13493
13494 let title = multibuffer.title(cx).to_string();
13495
13496 cx.spawn_in(window, |_, mut cx| async move {
13497 workspace.update_in(&mut cx, |workspace, window, cx| {
13498 Self::open_locations_in_multibuffer(
13499 workspace,
13500 locations,
13501 format!("Selections for '{title}'"),
13502 false,
13503 MultibufferSelectionMode::All,
13504 window,
13505 cx,
13506 );
13507 })
13508 })
13509 .detach();
13510 }
13511
13512 /// Adds a row highlight for the given range. If a row has multiple highlights, the
13513 /// last highlight added will be used.
13514 ///
13515 /// If the range ends at the beginning of a line, then that line will not be highlighted.
13516 pub fn highlight_rows<T: 'static>(
13517 &mut self,
13518 range: Range<Anchor>,
13519 color: Hsla,
13520 should_autoscroll: bool,
13521 cx: &mut Context<Self>,
13522 ) {
13523 let snapshot = self.buffer().read(cx).snapshot(cx);
13524 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
13525 let ix = row_highlights.binary_search_by(|highlight| {
13526 Ordering::Equal
13527 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
13528 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
13529 });
13530
13531 if let Err(mut ix) = ix {
13532 let index = post_inc(&mut self.highlight_order);
13533
13534 // If this range intersects with the preceding highlight, then merge it with
13535 // the preceding highlight. Otherwise insert a new highlight.
13536 let mut merged = false;
13537 if ix > 0 {
13538 let prev_highlight = &mut row_highlights[ix - 1];
13539 if prev_highlight
13540 .range
13541 .end
13542 .cmp(&range.start, &snapshot)
13543 .is_ge()
13544 {
13545 ix -= 1;
13546 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
13547 prev_highlight.range.end = range.end;
13548 }
13549 merged = true;
13550 prev_highlight.index = index;
13551 prev_highlight.color = color;
13552 prev_highlight.should_autoscroll = should_autoscroll;
13553 }
13554 }
13555
13556 if !merged {
13557 row_highlights.insert(
13558 ix,
13559 RowHighlight {
13560 range: range.clone(),
13561 index,
13562 color,
13563 should_autoscroll,
13564 },
13565 );
13566 }
13567
13568 // If any of the following highlights intersect with this one, merge them.
13569 while let Some(next_highlight) = row_highlights.get(ix + 1) {
13570 let highlight = &row_highlights[ix];
13571 if next_highlight
13572 .range
13573 .start
13574 .cmp(&highlight.range.end, &snapshot)
13575 .is_le()
13576 {
13577 if next_highlight
13578 .range
13579 .end
13580 .cmp(&highlight.range.end, &snapshot)
13581 .is_gt()
13582 {
13583 row_highlights[ix].range.end = next_highlight.range.end;
13584 }
13585 row_highlights.remove(ix + 1);
13586 } else {
13587 break;
13588 }
13589 }
13590 }
13591 }
13592
13593 /// Remove any highlighted row ranges of the given type that intersect the
13594 /// given ranges.
13595 pub fn remove_highlighted_rows<T: 'static>(
13596 &mut self,
13597 ranges_to_remove: Vec<Range<Anchor>>,
13598 cx: &mut Context<Self>,
13599 ) {
13600 let snapshot = self.buffer().read(cx).snapshot(cx);
13601 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
13602 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
13603 row_highlights.retain(|highlight| {
13604 while let Some(range_to_remove) = ranges_to_remove.peek() {
13605 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
13606 Ordering::Less | Ordering::Equal => {
13607 ranges_to_remove.next();
13608 }
13609 Ordering::Greater => {
13610 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
13611 Ordering::Less | Ordering::Equal => {
13612 return false;
13613 }
13614 Ordering::Greater => break,
13615 }
13616 }
13617 }
13618 }
13619
13620 true
13621 })
13622 }
13623
13624 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
13625 pub fn clear_row_highlights<T: 'static>(&mut self) {
13626 self.highlighted_rows.remove(&TypeId::of::<T>());
13627 }
13628
13629 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
13630 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
13631 self.highlighted_rows
13632 .get(&TypeId::of::<T>())
13633 .map_or(&[] as &[_], |vec| vec.as_slice())
13634 .iter()
13635 .map(|highlight| (highlight.range.clone(), highlight.color))
13636 }
13637
13638 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
13639 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
13640 /// Allows to ignore certain kinds of highlights.
13641 pub fn highlighted_display_rows(
13642 &self,
13643 window: &mut Window,
13644 cx: &mut App,
13645 ) -> BTreeMap<DisplayRow, Background> {
13646 let snapshot = self.snapshot(window, cx);
13647 let mut used_highlight_orders = HashMap::default();
13648 self.highlighted_rows
13649 .iter()
13650 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
13651 .fold(
13652 BTreeMap::<DisplayRow, Background>::new(),
13653 |mut unique_rows, highlight| {
13654 let start = highlight.range.start.to_display_point(&snapshot);
13655 let end = highlight.range.end.to_display_point(&snapshot);
13656 let start_row = start.row().0;
13657 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
13658 && end.column() == 0
13659 {
13660 end.row().0.saturating_sub(1)
13661 } else {
13662 end.row().0
13663 };
13664 for row in start_row..=end_row {
13665 let used_index =
13666 used_highlight_orders.entry(row).or_insert(highlight.index);
13667 if highlight.index >= *used_index {
13668 *used_index = highlight.index;
13669 unique_rows.insert(DisplayRow(row), highlight.color.into());
13670 }
13671 }
13672 unique_rows
13673 },
13674 )
13675 }
13676
13677 pub fn highlighted_display_row_for_autoscroll(
13678 &self,
13679 snapshot: &DisplaySnapshot,
13680 ) -> Option<DisplayRow> {
13681 self.highlighted_rows
13682 .values()
13683 .flat_map(|highlighted_rows| highlighted_rows.iter())
13684 .filter_map(|highlight| {
13685 if highlight.should_autoscroll {
13686 Some(highlight.range.start.to_display_point(snapshot).row())
13687 } else {
13688 None
13689 }
13690 })
13691 .min()
13692 }
13693
13694 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
13695 self.highlight_background::<SearchWithinRange>(
13696 ranges,
13697 |colors| colors.editor_document_highlight_read_background,
13698 cx,
13699 )
13700 }
13701
13702 pub fn set_breadcrumb_header(&mut self, new_header: String) {
13703 self.breadcrumb_header = Some(new_header);
13704 }
13705
13706 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
13707 self.clear_background_highlights::<SearchWithinRange>(cx);
13708 }
13709
13710 pub fn highlight_background<T: 'static>(
13711 &mut self,
13712 ranges: &[Range<Anchor>],
13713 color_fetcher: fn(&ThemeColors) -> Hsla,
13714 cx: &mut Context<Self>,
13715 ) {
13716 self.background_highlights
13717 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
13718 self.scrollbar_marker_state.dirty = true;
13719 cx.notify();
13720 }
13721
13722 pub fn clear_background_highlights<T: 'static>(
13723 &mut self,
13724 cx: &mut Context<Self>,
13725 ) -> Option<BackgroundHighlight> {
13726 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
13727 if !text_highlights.1.is_empty() {
13728 self.scrollbar_marker_state.dirty = true;
13729 cx.notify();
13730 }
13731 Some(text_highlights)
13732 }
13733
13734 pub fn highlight_gutter<T: 'static>(
13735 &mut self,
13736 ranges: &[Range<Anchor>],
13737 color_fetcher: fn(&App) -> Hsla,
13738 cx: &mut Context<Self>,
13739 ) {
13740 self.gutter_highlights
13741 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
13742 cx.notify();
13743 }
13744
13745 pub fn clear_gutter_highlights<T: 'static>(
13746 &mut self,
13747 cx: &mut Context<Self>,
13748 ) -> Option<GutterHighlight> {
13749 cx.notify();
13750 self.gutter_highlights.remove(&TypeId::of::<T>())
13751 }
13752
13753 #[cfg(feature = "test-support")]
13754 pub fn all_text_background_highlights(
13755 &self,
13756 window: &mut Window,
13757 cx: &mut Context<Self>,
13758 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
13759 let snapshot = self.snapshot(window, cx);
13760 let buffer = &snapshot.buffer_snapshot;
13761 let start = buffer.anchor_before(0);
13762 let end = buffer.anchor_after(buffer.len());
13763 let theme = cx.theme().colors();
13764 self.background_highlights_in_range(start..end, &snapshot, theme)
13765 }
13766
13767 #[cfg(feature = "test-support")]
13768 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
13769 let snapshot = self.buffer().read(cx).snapshot(cx);
13770
13771 let highlights = self
13772 .background_highlights
13773 .get(&TypeId::of::<items::BufferSearchHighlights>());
13774
13775 if let Some((_color, ranges)) = highlights {
13776 ranges
13777 .iter()
13778 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
13779 .collect_vec()
13780 } else {
13781 vec![]
13782 }
13783 }
13784
13785 fn document_highlights_for_position<'a>(
13786 &'a self,
13787 position: Anchor,
13788 buffer: &'a MultiBufferSnapshot,
13789 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
13790 let read_highlights = self
13791 .background_highlights
13792 .get(&TypeId::of::<DocumentHighlightRead>())
13793 .map(|h| &h.1);
13794 let write_highlights = self
13795 .background_highlights
13796 .get(&TypeId::of::<DocumentHighlightWrite>())
13797 .map(|h| &h.1);
13798 let left_position = position.bias_left(buffer);
13799 let right_position = position.bias_right(buffer);
13800 read_highlights
13801 .into_iter()
13802 .chain(write_highlights)
13803 .flat_map(move |ranges| {
13804 let start_ix = match ranges.binary_search_by(|probe| {
13805 let cmp = probe.end.cmp(&left_position, buffer);
13806 if cmp.is_ge() {
13807 Ordering::Greater
13808 } else {
13809 Ordering::Less
13810 }
13811 }) {
13812 Ok(i) | Err(i) => i,
13813 };
13814
13815 ranges[start_ix..]
13816 .iter()
13817 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
13818 })
13819 }
13820
13821 pub fn has_background_highlights<T: 'static>(&self) -> bool {
13822 self.background_highlights
13823 .get(&TypeId::of::<T>())
13824 .map_or(false, |(_, highlights)| !highlights.is_empty())
13825 }
13826
13827 pub fn background_highlights_in_range(
13828 &self,
13829 search_range: Range<Anchor>,
13830 display_snapshot: &DisplaySnapshot,
13831 theme: &ThemeColors,
13832 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
13833 let mut results = Vec::new();
13834 for (color_fetcher, ranges) in self.background_highlights.values() {
13835 let color = color_fetcher(theme);
13836 let start_ix = match ranges.binary_search_by(|probe| {
13837 let cmp = probe
13838 .end
13839 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
13840 if cmp.is_gt() {
13841 Ordering::Greater
13842 } else {
13843 Ordering::Less
13844 }
13845 }) {
13846 Ok(i) | Err(i) => i,
13847 };
13848 for range in &ranges[start_ix..] {
13849 if range
13850 .start
13851 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
13852 .is_ge()
13853 {
13854 break;
13855 }
13856
13857 let start = range.start.to_display_point(display_snapshot);
13858 let end = range.end.to_display_point(display_snapshot);
13859 results.push((start..end, color))
13860 }
13861 }
13862 results
13863 }
13864
13865 pub fn background_highlight_row_ranges<T: 'static>(
13866 &self,
13867 search_range: Range<Anchor>,
13868 display_snapshot: &DisplaySnapshot,
13869 count: usize,
13870 ) -> Vec<RangeInclusive<DisplayPoint>> {
13871 let mut results = Vec::new();
13872 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
13873 return vec![];
13874 };
13875
13876 let start_ix = match ranges.binary_search_by(|probe| {
13877 let cmp = probe
13878 .end
13879 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
13880 if cmp.is_gt() {
13881 Ordering::Greater
13882 } else {
13883 Ordering::Less
13884 }
13885 }) {
13886 Ok(i) | Err(i) => i,
13887 };
13888 let mut push_region = |start: Option<Point>, end: Option<Point>| {
13889 if let (Some(start_display), Some(end_display)) = (start, end) {
13890 results.push(
13891 start_display.to_display_point(display_snapshot)
13892 ..=end_display.to_display_point(display_snapshot),
13893 );
13894 }
13895 };
13896 let mut start_row: Option<Point> = None;
13897 let mut end_row: Option<Point> = None;
13898 if ranges.len() > count {
13899 return Vec::new();
13900 }
13901 for range in &ranges[start_ix..] {
13902 if range
13903 .start
13904 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
13905 .is_ge()
13906 {
13907 break;
13908 }
13909 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
13910 if let Some(current_row) = &end_row {
13911 if end.row == current_row.row {
13912 continue;
13913 }
13914 }
13915 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
13916 if start_row.is_none() {
13917 assert_eq!(end_row, None);
13918 start_row = Some(start);
13919 end_row = Some(end);
13920 continue;
13921 }
13922 if let Some(current_end) = end_row.as_mut() {
13923 if start.row > current_end.row + 1 {
13924 push_region(start_row, end_row);
13925 start_row = Some(start);
13926 end_row = Some(end);
13927 } else {
13928 // Merge two hunks.
13929 *current_end = end;
13930 }
13931 } else {
13932 unreachable!();
13933 }
13934 }
13935 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
13936 push_region(start_row, end_row);
13937 results
13938 }
13939
13940 pub fn gutter_highlights_in_range(
13941 &self,
13942 search_range: Range<Anchor>,
13943 display_snapshot: &DisplaySnapshot,
13944 cx: &App,
13945 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
13946 let mut results = Vec::new();
13947 for (color_fetcher, ranges) in self.gutter_highlights.values() {
13948 let color = color_fetcher(cx);
13949 let start_ix = match ranges.binary_search_by(|probe| {
13950 let cmp = probe
13951 .end
13952 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
13953 if cmp.is_gt() {
13954 Ordering::Greater
13955 } else {
13956 Ordering::Less
13957 }
13958 }) {
13959 Ok(i) | Err(i) => i,
13960 };
13961 for range in &ranges[start_ix..] {
13962 if range
13963 .start
13964 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
13965 .is_ge()
13966 {
13967 break;
13968 }
13969
13970 let start = range.start.to_display_point(display_snapshot);
13971 let end = range.end.to_display_point(display_snapshot);
13972 results.push((start..end, color))
13973 }
13974 }
13975 results
13976 }
13977
13978 /// Get the text ranges corresponding to the redaction query
13979 pub fn redacted_ranges(
13980 &self,
13981 search_range: Range<Anchor>,
13982 display_snapshot: &DisplaySnapshot,
13983 cx: &App,
13984 ) -> Vec<Range<DisplayPoint>> {
13985 display_snapshot
13986 .buffer_snapshot
13987 .redacted_ranges(search_range, |file| {
13988 if let Some(file) = file {
13989 file.is_private()
13990 && EditorSettings::get(
13991 Some(SettingsLocation {
13992 worktree_id: file.worktree_id(cx),
13993 path: file.path().as_ref(),
13994 }),
13995 cx,
13996 )
13997 .redact_private_values
13998 } else {
13999 false
14000 }
14001 })
14002 .map(|range| {
14003 range.start.to_display_point(display_snapshot)
14004 ..range.end.to_display_point(display_snapshot)
14005 })
14006 .collect()
14007 }
14008
14009 pub fn highlight_text<T: 'static>(
14010 &mut self,
14011 ranges: Vec<Range<Anchor>>,
14012 style: HighlightStyle,
14013 cx: &mut Context<Self>,
14014 ) {
14015 self.display_map.update(cx, |map, _| {
14016 map.highlight_text(TypeId::of::<T>(), ranges, style)
14017 });
14018 cx.notify();
14019 }
14020
14021 pub(crate) fn highlight_inlays<T: 'static>(
14022 &mut self,
14023 highlights: Vec<InlayHighlight>,
14024 style: HighlightStyle,
14025 cx: &mut Context<Self>,
14026 ) {
14027 self.display_map.update(cx, |map, _| {
14028 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
14029 });
14030 cx.notify();
14031 }
14032
14033 pub fn text_highlights<'a, T: 'static>(
14034 &'a self,
14035 cx: &'a App,
14036 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
14037 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
14038 }
14039
14040 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
14041 let cleared = self
14042 .display_map
14043 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
14044 if cleared {
14045 cx.notify();
14046 }
14047 }
14048
14049 pub fn previewing_edit_prediction_move(
14050 &mut self,
14051 ) -> Option<(Anchor, &mut EditPredictionPreview)> {
14052 if !self.edit_prediction_preview.is_active() {
14053 return None;
14054 };
14055
14056 self.active_inline_completion
14057 .as_ref()
14058 .and_then(|completion| match completion.completion {
14059 InlineCompletion::Move { target, .. } => {
14060 Some((target, &mut self.edit_prediction_preview))
14061 }
14062 _ => None,
14063 })
14064 }
14065
14066 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
14067 (self.read_only(cx) || self.blink_manager.read(cx).visible())
14068 && self.focus_handle.is_focused(window)
14069 }
14070
14071 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
14072 self.show_cursor_when_unfocused = is_enabled;
14073 cx.notify();
14074 }
14075
14076 pub fn lsp_store(&self, cx: &App) -> Option<Entity<LspStore>> {
14077 self.project
14078 .as_ref()
14079 .map(|project| project.read(cx).lsp_store())
14080 }
14081
14082 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
14083 cx.notify();
14084 }
14085
14086 fn on_buffer_event(
14087 &mut self,
14088 multibuffer: &Entity<MultiBuffer>,
14089 event: &multi_buffer::Event,
14090 window: &mut Window,
14091 cx: &mut Context<Self>,
14092 ) {
14093 match event {
14094 multi_buffer::Event::Edited {
14095 singleton_buffer_edited,
14096 edited_buffer: buffer_edited,
14097 } => {
14098 self.scrollbar_marker_state.dirty = true;
14099 self.active_indent_guides_state.dirty = true;
14100 self.refresh_active_diagnostics(cx);
14101 self.refresh_code_actions(window, cx);
14102 if self.has_active_inline_completion() {
14103 self.update_visible_inline_completion(window, cx);
14104 }
14105 if let Some(buffer) = buffer_edited {
14106 let buffer_id = buffer.read(cx).remote_id();
14107 if !self.registered_buffers.contains_key(&buffer_id) {
14108 if let Some(lsp_store) = self.lsp_store(cx) {
14109 lsp_store.update(cx, |lsp_store, cx| {
14110 self.registered_buffers.insert(
14111 buffer_id,
14112 lsp_store.register_buffer_with_language_servers(&buffer, cx),
14113 );
14114 })
14115 }
14116 }
14117 }
14118 cx.emit(EditorEvent::BufferEdited);
14119 cx.emit(SearchEvent::MatchesInvalidated);
14120 if *singleton_buffer_edited {
14121 if let Some(project) = &self.project {
14122 let project = project.read(cx);
14123 #[allow(clippy::mutable_key_type)]
14124 let languages_affected = multibuffer
14125 .read(cx)
14126 .all_buffers()
14127 .into_iter()
14128 .filter_map(|buffer| {
14129 let buffer = buffer.read(cx);
14130 let language = buffer.language()?;
14131 if project.is_local()
14132 && project
14133 .language_servers_for_local_buffer(buffer, cx)
14134 .count()
14135 == 0
14136 {
14137 None
14138 } else {
14139 Some(language)
14140 }
14141 })
14142 .cloned()
14143 .collect::<HashSet<_>>();
14144 if !languages_affected.is_empty() {
14145 self.refresh_inlay_hints(
14146 InlayHintRefreshReason::BufferEdited(languages_affected),
14147 cx,
14148 );
14149 }
14150 }
14151 }
14152
14153 let Some(project) = &self.project else { return };
14154 let (telemetry, is_via_ssh) = {
14155 let project = project.read(cx);
14156 let telemetry = project.client().telemetry().clone();
14157 let is_via_ssh = project.is_via_ssh();
14158 (telemetry, is_via_ssh)
14159 };
14160 refresh_linked_ranges(self, window, cx);
14161 telemetry.log_edit_event("editor", is_via_ssh);
14162 }
14163 multi_buffer::Event::ExcerptsAdded {
14164 buffer,
14165 predecessor,
14166 excerpts,
14167 } => {
14168 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
14169 let buffer_id = buffer.read(cx).remote_id();
14170 if self.buffer.read(cx).diff_for(buffer_id).is_none() {
14171 if let Some(project) = &self.project {
14172 get_uncommitted_diff_for_buffer(
14173 project,
14174 [buffer.clone()],
14175 self.buffer.clone(),
14176 cx,
14177 );
14178 }
14179 }
14180 cx.emit(EditorEvent::ExcerptsAdded {
14181 buffer: buffer.clone(),
14182 predecessor: *predecessor,
14183 excerpts: excerpts.clone(),
14184 });
14185 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
14186 }
14187 multi_buffer::Event::ExcerptsRemoved { ids } => {
14188 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
14189 let buffer = self.buffer.read(cx);
14190 self.registered_buffers
14191 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
14192 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
14193 }
14194 multi_buffer::Event::ExcerptsEdited { ids } => {
14195 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
14196 }
14197 multi_buffer::Event::ExcerptsExpanded { ids } => {
14198 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
14199 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
14200 }
14201 multi_buffer::Event::Reparsed(buffer_id) => {
14202 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
14203
14204 cx.emit(EditorEvent::Reparsed(*buffer_id));
14205 }
14206 multi_buffer::Event::DiffHunksToggled => {
14207 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
14208 }
14209 multi_buffer::Event::LanguageChanged(buffer_id) => {
14210 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
14211 cx.emit(EditorEvent::Reparsed(*buffer_id));
14212 cx.notify();
14213 }
14214 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
14215 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
14216 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
14217 cx.emit(EditorEvent::TitleChanged)
14218 }
14219 // multi_buffer::Event::DiffBaseChanged => {
14220 // self.scrollbar_marker_state.dirty = true;
14221 // cx.emit(EditorEvent::DiffBaseChanged);
14222 // cx.notify();
14223 // }
14224 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
14225 multi_buffer::Event::DiagnosticsUpdated => {
14226 self.refresh_active_diagnostics(cx);
14227 self.scrollbar_marker_state.dirty = true;
14228 cx.notify();
14229 }
14230 _ => {}
14231 };
14232 }
14233
14234 fn on_display_map_changed(
14235 &mut self,
14236 _: Entity<DisplayMap>,
14237 _: &mut Window,
14238 cx: &mut Context<Self>,
14239 ) {
14240 cx.notify();
14241 }
14242
14243 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
14244 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
14245 self.refresh_inline_completion(true, false, window, cx);
14246 self.refresh_inlay_hints(
14247 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
14248 self.selections.newest_anchor().head(),
14249 &self.buffer.read(cx).snapshot(cx),
14250 cx,
14251 )),
14252 cx,
14253 );
14254
14255 let old_cursor_shape = self.cursor_shape;
14256
14257 {
14258 let editor_settings = EditorSettings::get_global(cx);
14259 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
14260 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
14261 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
14262 }
14263
14264 if old_cursor_shape != self.cursor_shape {
14265 cx.emit(EditorEvent::CursorShapeChanged);
14266 }
14267
14268 let project_settings = ProjectSettings::get_global(cx);
14269 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
14270
14271 if self.mode == EditorMode::Full {
14272 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
14273 if self.git_blame_inline_enabled != inline_blame_enabled {
14274 self.toggle_git_blame_inline_internal(false, window, cx);
14275 }
14276 }
14277
14278 cx.notify();
14279 }
14280
14281 pub fn set_searchable(&mut self, searchable: bool) {
14282 self.searchable = searchable;
14283 }
14284
14285 pub fn searchable(&self) -> bool {
14286 self.searchable
14287 }
14288
14289 fn open_proposed_changes_editor(
14290 &mut self,
14291 _: &OpenProposedChangesEditor,
14292 window: &mut Window,
14293 cx: &mut Context<Self>,
14294 ) {
14295 let Some(workspace) = self.workspace() else {
14296 cx.propagate();
14297 return;
14298 };
14299
14300 let selections = self.selections.all::<usize>(cx);
14301 let multi_buffer = self.buffer.read(cx);
14302 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
14303 let mut new_selections_by_buffer = HashMap::default();
14304 for selection in selections {
14305 for (buffer, range, _) in
14306 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
14307 {
14308 let mut range = range.to_point(buffer);
14309 range.start.column = 0;
14310 range.end.column = buffer.line_len(range.end.row);
14311 new_selections_by_buffer
14312 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
14313 .or_insert(Vec::new())
14314 .push(range)
14315 }
14316 }
14317
14318 let proposed_changes_buffers = new_selections_by_buffer
14319 .into_iter()
14320 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
14321 .collect::<Vec<_>>();
14322 let proposed_changes_editor = cx.new(|cx| {
14323 ProposedChangesEditor::new(
14324 "Proposed changes",
14325 proposed_changes_buffers,
14326 self.project.clone(),
14327 window,
14328 cx,
14329 )
14330 });
14331
14332 window.defer(cx, move |window, cx| {
14333 workspace.update(cx, |workspace, cx| {
14334 workspace.active_pane().update(cx, |pane, cx| {
14335 pane.add_item(
14336 Box::new(proposed_changes_editor),
14337 true,
14338 true,
14339 None,
14340 window,
14341 cx,
14342 );
14343 });
14344 });
14345 });
14346 }
14347
14348 pub fn open_excerpts_in_split(
14349 &mut self,
14350 _: &OpenExcerptsSplit,
14351 window: &mut Window,
14352 cx: &mut Context<Self>,
14353 ) {
14354 self.open_excerpts_common(None, true, window, cx)
14355 }
14356
14357 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
14358 self.open_excerpts_common(None, false, window, cx)
14359 }
14360
14361 fn open_excerpts_common(
14362 &mut self,
14363 jump_data: Option<JumpData>,
14364 split: bool,
14365 window: &mut Window,
14366 cx: &mut Context<Self>,
14367 ) {
14368 let Some(workspace) = self.workspace() else {
14369 cx.propagate();
14370 return;
14371 };
14372
14373 if self.buffer.read(cx).is_singleton() {
14374 cx.propagate();
14375 return;
14376 }
14377
14378 let mut new_selections_by_buffer = HashMap::default();
14379 match &jump_data {
14380 Some(JumpData::MultiBufferPoint {
14381 excerpt_id,
14382 position,
14383 anchor,
14384 line_offset_from_top,
14385 }) => {
14386 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14387 if let Some(buffer) = multi_buffer_snapshot
14388 .buffer_id_for_excerpt(*excerpt_id)
14389 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
14390 {
14391 let buffer_snapshot = buffer.read(cx).snapshot();
14392 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
14393 language::ToPoint::to_point(anchor, &buffer_snapshot)
14394 } else {
14395 buffer_snapshot.clip_point(*position, Bias::Left)
14396 };
14397 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
14398 new_selections_by_buffer.insert(
14399 buffer,
14400 (
14401 vec![jump_to_offset..jump_to_offset],
14402 Some(*line_offset_from_top),
14403 ),
14404 );
14405 }
14406 }
14407 Some(JumpData::MultiBufferRow {
14408 row,
14409 line_offset_from_top,
14410 }) => {
14411 let point = MultiBufferPoint::new(row.0, 0);
14412 if let Some((buffer, buffer_point, _)) =
14413 self.buffer.read(cx).point_to_buffer_point(point, cx)
14414 {
14415 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
14416 new_selections_by_buffer
14417 .entry(buffer)
14418 .or_insert((Vec::new(), Some(*line_offset_from_top)))
14419 .0
14420 .push(buffer_offset..buffer_offset)
14421 }
14422 }
14423 None => {
14424 let selections = self.selections.all::<usize>(cx);
14425 let multi_buffer = self.buffer.read(cx);
14426 for selection in selections {
14427 for (buffer, mut range, _) in multi_buffer
14428 .snapshot(cx)
14429 .range_to_buffer_ranges(selection.range())
14430 {
14431 // When editing branch buffers, jump to the corresponding location
14432 // in their base buffer.
14433 let mut buffer_handle = multi_buffer.buffer(buffer.remote_id()).unwrap();
14434 let buffer = buffer_handle.read(cx);
14435 if let Some(base_buffer) = buffer.base_buffer() {
14436 range = buffer.range_to_version(range, &base_buffer.read(cx).version());
14437 buffer_handle = base_buffer;
14438 }
14439
14440 if selection.reversed {
14441 mem::swap(&mut range.start, &mut range.end);
14442 }
14443 new_selections_by_buffer
14444 .entry(buffer_handle)
14445 .or_insert((Vec::new(), None))
14446 .0
14447 .push(range)
14448 }
14449 }
14450 }
14451 }
14452
14453 if new_selections_by_buffer.is_empty() {
14454 return;
14455 }
14456
14457 // We defer the pane interaction because we ourselves are a workspace item
14458 // and activating a new item causes the pane to call a method on us reentrantly,
14459 // which panics if we're on the stack.
14460 window.defer(cx, move |window, cx| {
14461 workspace.update(cx, |workspace, cx| {
14462 let pane = if split {
14463 workspace.adjacent_pane(window, cx)
14464 } else {
14465 workspace.active_pane().clone()
14466 };
14467
14468 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
14469 let editor = buffer
14470 .read(cx)
14471 .file()
14472 .is_none()
14473 .then(|| {
14474 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
14475 // so `workspace.open_project_item` will never find them, always opening a new editor.
14476 // Instead, we try to activate the existing editor in the pane first.
14477 let (editor, pane_item_index) =
14478 pane.read(cx).items().enumerate().find_map(|(i, item)| {
14479 let editor = item.downcast::<Editor>()?;
14480 let singleton_buffer =
14481 editor.read(cx).buffer().read(cx).as_singleton()?;
14482 if singleton_buffer == buffer {
14483 Some((editor, i))
14484 } else {
14485 None
14486 }
14487 })?;
14488 pane.update(cx, |pane, cx| {
14489 pane.activate_item(pane_item_index, true, true, window, cx)
14490 });
14491 Some(editor)
14492 })
14493 .flatten()
14494 .unwrap_or_else(|| {
14495 workspace.open_project_item::<Self>(
14496 pane.clone(),
14497 buffer,
14498 true,
14499 true,
14500 window,
14501 cx,
14502 )
14503 });
14504
14505 editor.update(cx, |editor, cx| {
14506 let autoscroll = match scroll_offset {
14507 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
14508 None => Autoscroll::newest(),
14509 };
14510 let nav_history = editor.nav_history.take();
14511 editor.change_selections(Some(autoscroll), window, cx, |s| {
14512 s.select_ranges(ranges);
14513 });
14514 editor.nav_history = nav_history;
14515 });
14516 }
14517 })
14518 });
14519 }
14520
14521 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
14522 let snapshot = self.buffer.read(cx).read(cx);
14523 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
14524 Some(
14525 ranges
14526 .iter()
14527 .map(move |range| {
14528 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
14529 })
14530 .collect(),
14531 )
14532 }
14533
14534 fn selection_replacement_ranges(
14535 &self,
14536 range: Range<OffsetUtf16>,
14537 cx: &mut App,
14538 ) -> Vec<Range<OffsetUtf16>> {
14539 let selections = self.selections.all::<OffsetUtf16>(cx);
14540 let newest_selection = selections
14541 .iter()
14542 .max_by_key(|selection| selection.id)
14543 .unwrap();
14544 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
14545 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
14546 let snapshot = self.buffer.read(cx).read(cx);
14547 selections
14548 .into_iter()
14549 .map(|mut selection| {
14550 selection.start.0 =
14551 (selection.start.0 as isize).saturating_add(start_delta) as usize;
14552 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
14553 snapshot.clip_offset_utf16(selection.start, Bias::Left)
14554 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
14555 })
14556 .collect()
14557 }
14558
14559 fn report_editor_event(
14560 &self,
14561 event_type: &'static str,
14562 file_extension: Option<String>,
14563 cx: &App,
14564 ) {
14565 if cfg!(any(test, feature = "test-support")) {
14566 return;
14567 }
14568
14569 let Some(project) = &self.project else { return };
14570
14571 // If None, we are in a file without an extension
14572 let file = self
14573 .buffer
14574 .read(cx)
14575 .as_singleton()
14576 .and_then(|b| b.read(cx).file());
14577 let file_extension = file_extension.or(file
14578 .as_ref()
14579 .and_then(|file| Path::new(file.file_name(cx)).extension())
14580 .and_then(|e| e.to_str())
14581 .map(|a| a.to_string()));
14582
14583 let vim_mode = cx
14584 .global::<SettingsStore>()
14585 .raw_user_settings()
14586 .get("vim_mode")
14587 == Some(&serde_json::Value::Bool(true));
14588
14589 let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
14590 let copilot_enabled = edit_predictions_provider
14591 == language::language_settings::EditPredictionProvider::Copilot;
14592 let copilot_enabled_for_language = self
14593 .buffer
14594 .read(cx)
14595 .settings_at(0, cx)
14596 .show_edit_predictions;
14597
14598 let project = project.read(cx);
14599 telemetry::event!(
14600 event_type,
14601 file_extension,
14602 vim_mode,
14603 copilot_enabled,
14604 copilot_enabled_for_language,
14605 edit_predictions_provider,
14606 is_via_ssh = project.is_via_ssh(),
14607 );
14608 }
14609
14610 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
14611 /// with each line being an array of {text, highlight} objects.
14612 fn copy_highlight_json(
14613 &mut self,
14614 _: &CopyHighlightJson,
14615 window: &mut Window,
14616 cx: &mut Context<Self>,
14617 ) {
14618 #[derive(Serialize)]
14619 struct Chunk<'a> {
14620 text: String,
14621 highlight: Option<&'a str>,
14622 }
14623
14624 let snapshot = self.buffer.read(cx).snapshot(cx);
14625 let range = self
14626 .selected_text_range(false, window, cx)
14627 .and_then(|selection| {
14628 if selection.range.is_empty() {
14629 None
14630 } else {
14631 Some(selection.range)
14632 }
14633 })
14634 .unwrap_or_else(|| 0..snapshot.len());
14635
14636 let chunks = snapshot.chunks(range, true);
14637 let mut lines = Vec::new();
14638 let mut line: VecDeque<Chunk> = VecDeque::new();
14639
14640 let Some(style) = self.style.as_ref() else {
14641 return;
14642 };
14643
14644 for chunk in chunks {
14645 let highlight = chunk
14646 .syntax_highlight_id
14647 .and_then(|id| id.name(&style.syntax));
14648 let mut chunk_lines = chunk.text.split('\n').peekable();
14649 while let Some(text) = chunk_lines.next() {
14650 let mut merged_with_last_token = false;
14651 if let Some(last_token) = line.back_mut() {
14652 if last_token.highlight == highlight {
14653 last_token.text.push_str(text);
14654 merged_with_last_token = true;
14655 }
14656 }
14657
14658 if !merged_with_last_token {
14659 line.push_back(Chunk {
14660 text: text.into(),
14661 highlight,
14662 });
14663 }
14664
14665 if chunk_lines.peek().is_some() {
14666 if line.len() > 1 && line.front().unwrap().text.is_empty() {
14667 line.pop_front();
14668 }
14669 if line.len() > 1 && line.back().unwrap().text.is_empty() {
14670 line.pop_back();
14671 }
14672
14673 lines.push(mem::take(&mut line));
14674 }
14675 }
14676 }
14677
14678 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
14679 return;
14680 };
14681 cx.write_to_clipboard(ClipboardItem::new_string(lines));
14682 }
14683
14684 pub fn open_context_menu(
14685 &mut self,
14686 _: &OpenContextMenu,
14687 window: &mut Window,
14688 cx: &mut Context<Self>,
14689 ) {
14690 self.request_autoscroll(Autoscroll::newest(), cx);
14691 let position = self.selections.newest_display(cx).start;
14692 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
14693 }
14694
14695 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
14696 &self.inlay_hint_cache
14697 }
14698
14699 pub fn replay_insert_event(
14700 &mut self,
14701 text: &str,
14702 relative_utf16_range: Option<Range<isize>>,
14703 window: &mut Window,
14704 cx: &mut Context<Self>,
14705 ) {
14706 if !self.input_enabled {
14707 cx.emit(EditorEvent::InputIgnored { text: text.into() });
14708 return;
14709 }
14710 if let Some(relative_utf16_range) = relative_utf16_range {
14711 let selections = self.selections.all::<OffsetUtf16>(cx);
14712 self.change_selections(None, window, cx, |s| {
14713 let new_ranges = selections.into_iter().map(|range| {
14714 let start = OffsetUtf16(
14715 range
14716 .head()
14717 .0
14718 .saturating_add_signed(relative_utf16_range.start),
14719 );
14720 let end = OffsetUtf16(
14721 range
14722 .head()
14723 .0
14724 .saturating_add_signed(relative_utf16_range.end),
14725 );
14726 start..end
14727 });
14728 s.select_ranges(new_ranges);
14729 });
14730 }
14731
14732 self.handle_input(text, window, cx);
14733 }
14734
14735 pub fn supports_inlay_hints(&self, cx: &App) -> bool {
14736 let Some(provider) = self.semantics_provider.as_ref() else {
14737 return false;
14738 };
14739
14740 let mut supports = false;
14741 self.buffer().read(cx).for_each_buffer(|buffer| {
14742 supports |= provider.supports_inlay_hints(buffer, cx);
14743 });
14744 supports
14745 }
14746
14747 pub fn is_focused(&self, window: &Window) -> bool {
14748 self.focus_handle.is_focused(window)
14749 }
14750
14751 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
14752 cx.emit(EditorEvent::Focused);
14753
14754 if let Some(descendant) = self
14755 .last_focused_descendant
14756 .take()
14757 .and_then(|descendant| descendant.upgrade())
14758 {
14759 window.focus(&descendant);
14760 } else {
14761 if let Some(blame) = self.blame.as_ref() {
14762 blame.update(cx, GitBlame::focus)
14763 }
14764
14765 self.blink_manager.update(cx, BlinkManager::enable);
14766 self.show_cursor_names(window, cx);
14767 self.buffer.update(cx, |buffer, cx| {
14768 buffer.finalize_last_transaction(cx);
14769 if self.leader_peer_id.is_none() {
14770 buffer.set_active_selections(
14771 &self.selections.disjoint_anchors(),
14772 self.selections.line_mode,
14773 self.cursor_shape,
14774 cx,
14775 );
14776 }
14777 });
14778 }
14779 }
14780
14781 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
14782 cx.emit(EditorEvent::FocusedIn)
14783 }
14784
14785 fn handle_focus_out(
14786 &mut self,
14787 event: FocusOutEvent,
14788 _window: &mut Window,
14789 _cx: &mut Context<Self>,
14790 ) {
14791 if event.blurred != self.focus_handle {
14792 self.last_focused_descendant = Some(event.blurred);
14793 }
14794 }
14795
14796 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
14797 self.blink_manager.update(cx, BlinkManager::disable);
14798 self.buffer
14799 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
14800
14801 if let Some(blame) = self.blame.as_ref() {
14802 blame.update(cx, GitBlame::blur)
14803 }
14804 if !self.hover_state.focused(window, cx) {
14805 hide_hover(self, cx);
14806 }
14807
14808 self.hide_context_menu(window, cx);
14809 cx.emit(EditorEvent::Blurred);
14810 cx.notify();
14811 }
14812
14813 pub fn register_action<A: Action>(
14814 &mut self,
14815 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
14816 ) -> Subscription {
14817 let id = self.next_editor_action_id.post_inc();
14818 let listener = Arc::new(listener);
14819 self.editor_actions.borrow_mut().insert(
14820 id,
14821 Box::new(move |window, _| {
14822 let listener = listener.clone();
14823 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
14824 let action = action.downcast_ref().unwrap();
14825 if phase == DispatchPhase::Bubble {
14826 listener(action, window, cx)
14827 }
14828 })
14829 }),
14830 );
14831
14832 let editor_actions = self.editor_actions.clone();
14833 Subscription::new(move || {
14834 editor_actions.borrow_mut().remove(&id);
14835 })
14836 }
14837
14838 pub fn file_header_size(&self) -> u32 {
14839 FILE_HEADER_HEIGHT
14840 }
14841
14842 pub fn revert(
14843 &mut self,
14844 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
14845 window: &mut Window,
14846 cx: &mut Context<Self>,
14847 ) {
14848 self.buffer().update(cx, |multi_buffer, cx| {
14849 for (buffer_id, changes) in revert_changes {
14850 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
14851 buffer.update(cx, |buffer, cx| {
14852 buffer.edit(
14853 changes.into_iter().map(|(range, text)| {
14854 (range, text.to_string().map(Arc::<str>::from))
14855 }),
14856 None,
14857 cx,
14858 );
14859 });
14860 }
14861 }
14862 });
14863 self.change_selections(None, window, cx, |selections| selections.refresh());
14864 }
14865
14866 pub fn to_pixel_point(
14867 &self,
14868 source: multi_buffer::Anchor,
14869 editor_snapshot: &EditorSnapshot,
14870 window: &mut Window,
14871 ) -> Option<gpui::Point<Pixels>> {
14872 let source_point = source.to_display_point(editor_snapshot);
14873 self.display_to_pixel_point(source_point, editor_snapshot, window)
14874 }
14875
14876 pub fn display_to_pixel_point(
14877 &self,
14878 source: DisplayPoint,
14879 editor_snapshot: &EditorSnapshot,
14880 window: &mut Window,
14881 ) -> Option<gpui::Point<Pixels>> {
14882 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
14883 let text_layout_details = self.text_layout_details(window);
14884 let scroll_top = text_layout_details
14885 .scroll_anchor
14886 .scroll_position(editor_snapshot)
14887 .y;
14888
14889 if source.row().as_f32() < scroll_top.floor() {
14890 return None;
14891 }
14892 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
14893 let source_y = line_height * (source.row().as_f32() - scroll_top);
14894 Some(gpui::Point::new(source_x, source_y))
14895 }
14896
14897 pub fn has_visible_completions_menu(&self) -> bool {
14898 !self.edit_prediction_preview.is_active()
14899 && self.context_menu.borrow().as_ref().map_or(false, |menu| {
14900 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
14901 })
14902 }
14903
14904 pub fn register_addon<T: Addon>(&mut self, instance: T) {
14905 self.addons
14906 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
14907 }
14908
14909 pub fn unregister_addon<T: Addon>(&mut self) {
14910 self.addons.remove(&std::any::TypeId::of::<T>());
14911 }
14912
14913 pub fn addon<T: Addon>(&self) -> Option<&T> {
14914 let type_id = std::any::TypeId::of::<T>();
14915 self.addons
14916 .get(&type_id)
14917 .and_then(|item| item.to_any().downcast_ref::<T>())
14918 }
14919
14920 fn character_size(&self, window: &mut Window) -> gpui::Size<Pixels> {
14921 let text_layout_details = self.text_layout_details(window);
14922 let style = &text_layout_details.editor_style;
14923 let font_id = window.text_system().resolve_font(&style.text.font());
14924 let font_size = style.text.font_size.to_pixels(window.rem_size());
14925 let line_height = style.text.line_height_in_pixels(window.rem_size());
14926 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
14927
14928 gpui::Size::new(em_width, line_height)
14929 }
14930}
14931
14932fn get_uncommitted_diff_for_buffer(
14933 project: &Entity<Project>,
14934 buffers: impl IntoIterator<Item = Entity<Buffer>>,
14935 buffer: Entity<MultiBuffer>,
14936 cx: &mut App,
14937) {
14938 let mut tasks = Vec::new();
14939 project.update(cx, |project, cx| {
14940 for buffer in buffers {
14941 tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
14942 }
14943 });
14944 cx.spawn(|mut cx| async move {
14945 let diffs = futures::future::join_all(tasks).await;
14946 buffer
14947 .update(&mut cx, |buffer, cx| {
14948 for diff in diffs.into_iter().flatten() {
14949 buffer.add_diff(diff, cx);
14950 }
14951 })
14952 .ok();
14953 })
14954 .detach();
14955}
14956
14957fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
14958 let tab_size = tab_size.get() as usize;
14959 let mut width = offset;
14960
14961 for ch in text.chars() {
14962 width += if ch == '\t' {
14963 tab_size - (width % tab_size)
14964 } else {
14965 1
14966 };
14967 }
14968
14969 width - offset
14970}
14971
14972#[cfg(test)]
14973mod tests {
14974 use super::*;
14975
14976 #[test]
14977 fn test_string_size_with_expanded_tabs() {
14978 let nz = |val| NonZeroU32::new(val).unwrap();
14979 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
14980 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
14981 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
14982 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
14983 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
14984 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
14985 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
14986 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
14987 }
14988}
14989
14990/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
14991struct WordBreakingTokenizer<'a> {
14992 input: &'a str,
14993}
14994
14995impl<'a> WordBreakingTokenizer<'a> {
14996 fn new(input: &'a str) -> Self {
14997 Self { input }
14998 }
14999}
15000
15001fn is_char_ideographic(ch: char) -> bool {
15002 use unicode_script::Script::*;
15003 use unicode_script::UnicodeScript;
15004 matches!(ch.script(), Han | Tangut | Yi)
15005}
15006
15007fn is_grapheme_ideographic(text: &str) -> bool {
15008 text.chars().any(is_char_ideographic)
15009}
15010
15011fn is_grapheme_whitespace(text: &str) -> bool {
15012 text.chars().any(|x| x.is_whitespace())
15013}
15014
15015fn should_stay_with_preceding_ideograph(text: &str) -> bool {
15016 text.chars().next().map_or(false, |ch| {
15017 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
15018 })
15019}
15020
15021#[derive(PartialEq, Eq, Debug, Clone, Copy)]
15022struct WordBreakToken<'a> {
15023 token: &'a str,
15024 grapheme_len: usize,
15025 is_whitespace: bool,
15026}
15027
15028impl<'a> Iterator for WordBreakingTokenizer<'a> {
15029 /// Yields a span, the count of graphemes in the token, and whether it was
15030 /// whitespace. Note that it also breaks at word boundaries.
15031 type Item = WordBreakToken<'a>;
15032
15033 fn next(&mut self) -> Option<Self::Item> {
15034 use unicode_segmentation::UnicodeSegmentation;
15035 if self.input.is_empty() {
15036 return None;
15037 }
15038
15039 let mut iter = self.input.graphemes(true).peekable();
15040 let mut offset = 0;
15041 let mut graphemes = 0;
15042 if let Some(first_grapheme) = iter.next() {
15043 let is_whitespace = is_grapheme_whitespace(first_grapheme);
15044 offset += first_grapheme.len();
15045 graphemes += 1;
15046 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
15047 if let Some(grapheme) = iter.peek().copied() {
15048 if should_stay_with_preceding_ideograph(grapheme) {
15049 offset += grapheme.len();
15050 graphemes += 1;
15051 }
15052 }
15053 } else {
15054 let mut words = self.input[offset..].split_word_bound_indices().peekable();
15055 let mut next_word_bound = words.peek().copied();
15056 if next_word_bound.map_or(false, |(i, _)| i == 0) {
15057 next_word_bound = words.next();
15058 }
15059 while let Some(grapheme) = iter.peek().copied() {
15060 if next_word_bound.map_or(false, |(i, _)| i == offset) {
15061 break;
15062 };
15063 if is_grapheme_whitespace(grapheme) != is_whitespace {
15064 break;
15065 };
15066 offset += grapheme.len();
15067 graphemes += 1;
15068 iter.next();
15069 }
15070 }
15071 let token = &self.input[..offset];
15072 self.input = &self.input[offset..];
15073 if is_whitespace {
15074 Some(WordBreakToken {
15075 token: " ",
15076 grapheme_len: 1,
15077 is_whitespace: true,
15078 })
15079 } else {
15080 Some(WordBreakToken {
15081 token,
15082 grapheme_len: graphemes,
15083 is_whitespace: false,
15084 })
15085 }
15086 } else {
15087 None
15088 }
15089 }
15090}
15091
15092#[test]
15093fn test_word_breaking_tokenizer() {
15094 let tests: &[(&str, &[(&str, usize, bool)])] = &[
15095 ("", &[]),
15096 (" ", &[(" ", 1, true)]),
15097 ("Ʒ", &[("Ʒ", 1, false)]),
15098 ("Ǽ", &[("Ǽ", 1, false)]),
15099 ("⋑", &[("⋑", 1, false)]),
15100 ("⋑⋑", &[("⋑⋑", 2, false)]),
15101 (
15102 "原理,进而",
15103 &[
15104 ("原", 1, false),
15105 ("理,", 2, false),
15106 ("进", 1, false),
15107 ("而", 1, false),
15108 ],
15109 ),
15110 (
15111 "hello world",
15112 &[("hello", 5, false), (" ", 1, true), ("world", 5, false)],
15113 ),
15114 (
15115 "hello, world",
15116 &[("hello,", 6, false), (" ", 1, true), ("world", 5, false)],
15117 ),
15118 (
15119 " hello world",
15120 &[
15121 (" ", 1, true),
15122 ("hello", 5, false),
15123 (" ", 1, true),
15124 ("world", 5, false),
15125 ],
15126 ),
15127 (
15128 "这是什么 \n 钢笔",
15129 &[
15130 ("这", 1, false),
15131 ("是", 1, false),
15132 ("什", 1, false),
15133 ("么", 1, false),
15134 (" ", 1, true),
15135 ("钢", 1, false),
15136 ("笔", 1, false),
15137 ],
15138 ),
15139 (" mutton", &[(" ", 1, true), ("mutton", 6, false)]),
15140 ];
15141
15142 for (input, result) in tests {
15143 assert_eq!(
15144 WordBreakingTokenizer::new(input).collect::<Vec<_>>(),
15145 result
15146 .iter()
15147 .copied()
15148 .map(|(token, grapheme_len, is_whitespace)| WordBreakToken {
15149 token,
15150 grapheme_len,
15151 is_whitespace,
15152 })
15153 .collect::<Vec<_>>()
15154 );
15155 }
15156}
15157
15158fn wrap_with_prefix(
15159 line_prefix: String,
15160 unwrapped_text: String,
15161 wrap_column: usize,
15162 tab_size: NonZeroU32,
15163) -> String {
15164 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
15165 let mut wrapped_text = String::new();
15166 let mut current_line = line_prefix.clone();
15167
15168 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
15169 let mut current_line_len = line_prefix_len;
15170 for WordBreakToken {
15171 token,
15172 grapheme_len,
15173 is_whitespace,
15174 } in tokenizer
15175 {
15176 if current_line_len + grapheme_len > wrap_column && current_line_len != line_prefix_len {
15177 wrapped_text.push_str(current_line.trim_end());
15178 wrapped_text.push('\n');
15179 current_line.truncate(line_prefix.len());
15180 current_line_len = line_prefix_len;
15181 if !is_whitespace {
15182 current_line.push_str(token);
15183 current_line_len += grapheme_len;
15184 }
15185 } else if !is_whitespace {
15186 current_line.push_str(token);
15187 current_line_len += grapheme_len;
15188 } else if current_line_len != line_prefix_len {
15189 current_line.push(' ');
15190 current_line_len += 1;
15191 }
15192 }
15193
15194 if !current_line.is_empty() {
15195 wrapped_text.push_str(¤t_line);
15196 }
15197 wrapped_text
15198}
15199
15200#[test]
15201fn test_wrap_with_prefix() {
15202 assert_eq!(
15203 wrap_with_prefix(
15204 "# ".to_string(),
15205 "abcdefg".to_string(),
15206 4,
15207 NonZeroU32::new(4).unwrap()
15208 ),
15209 "# abcdefg"
15210 );
15211 assert_eq!(
15212 wrap_with_prefix(
15213 "".to_string(),
15214 "\thello world".to_string(),
15215 8,
15216 NonZeroU32::new(4).unwrap()
15217 ),
15218 "hello\nworld"
15219 );
15220 assert_eq!(
15221 wrap_with_prefix(
15222 "// ".to_string(),
15223 "xx \nyy zz aa bb cc".to_string(),
15224 12,
15225 NonZeroU32::new(4).unwrap()
15226 ),
15227 "// xx yy zz\n// aa bb cc"
15228 );
15229 assert_eq!(
15230 wrap_with_prefix(
15231 String::new(),
15232 "这是什么 \n 钢笔".to_string(),
15233 3,
15234 NonZeroU32::new(4).unwrap()
15235 ),
15236 "这是什\n么 钢\n笔"
15237 );
15238}
15239
15240pub trait CollaborationHub {
15241 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
15242 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
15243 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
15244}
15245
15246impl CollaborationHub for Entity<Project> {
15247 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
15248 self.read(cx).collaborators()
15249 }
15250
15251 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
15252 self.read(cx).user_store().read(cx).participant_indices()
15253 }
15254
15255 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
15256 let this = self.read(cx);
15257 let user_ids = this.collaborators().values().map(|c| c.user_id);
15258 this.user_store().read_with(cx, |user_store, cx| {
15259 user_store.participant_names(user_ids, cx)
15260 })
15261 }
15262}
15263
15264pub trait SemanticsProvider {
15265 fn hover(
15266 &self,
15267 buffer: &Entity<Buffer>,
15268 position: text::Anchor,
15269 cx: &mut App,
15270 ) -> Option<Task<Vec<project::Hover>>>;
15271
15272 fn inlay_hints(
15273 &self,
15274 buffer_handle: Entity<Buffer>,
15275 range: Range<text::Anchor>,
15276 cx: &mut App,
15277 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
15278
15279 fn resolve_inlay_hint(
15280 &self,
15281 hint: InlayHint,
15282 buffer_handle: Entity<Buffer>,
15283 server_id: LanguageServerId,
15284 cx: &mut App,
15285 ) -> Option<Task<anyhow::Result<InlayHint>>>;
15286
15287 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &App) -> bool;
15288
15289 fn document_highlights(
15290 &self,
15291 buffer: &Entity<Buffer>,
15292 position: text::Anchor,
15293 cx: &mut App,
15294 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
15295
15296 fn definitions(
15297 &self,
15298 buffer: &Entity<Buffer>,
15299 position: text::Anchor,
15300 kind: GotoDefinitionKind,
15301 cx: &mut App,
15302 ) -> Option<Task<Result<Vec<LocationLink>>>>;
15303
15304 fn range_for_rename(
15305 &self,
15306 buffer: &Entity<Buffer>,
15307 position: text::Anchor,
15308 cx: &mut App,
15309 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
15310
15311 fn perform_rename(
15312 &self,
15313 buffer: &Entity<Buffer>,
15314 position: text::Anchor,
15315 new_name: String,
15316 cx: &mut App,
15317 ) -> Option<Task<Result<ProjectTransaction>>>;
15318}
15319
15320pub trait CompletionProvider {
15321 fn completions(
15322 &self,
15323 buffer: &Entity<Buffer>,
15324 buffer_position: text::Anchor,
15325 trigger: CompletionContext,
15326 window: &mut Window,
15327 cx: &mut Context<Editor>,
15328 ) -> Task<Result<Vec<Completion>>>;
15329
15330 fn resolve_completions(
15331 &self,
15332 buffer: Entity<Buffer>,
15333 completion_indices: Vec<usize>,
15334 completions: Rc<RefCell<Box<[Completion]>>>,
15335 cx: &mut Context<Editor>,
15336 ) -> Task<Result<bool>>;
15337
15338 fn apply_additional_edits_for_completion(
15339 &self,
15340 _buffer: Entity<Buffer>,
15341 _completions: Rc<RefCell<Box<[Completion]>>>,
15342 _completion_index: usize,
15343 _push_to_history: bool,
15344 _cx: &mut Context<Editor>,
15345 ) -> Task<Result<Option<language::Transaction>>> {
15346 Task::ready(Ok(None))
15347 }
15348
15349 fn is_completion_trigger(
15350 &self,
15351 buffer: &Entity<Buffer>,
15352 position: language::Anchor,
15353 text: &str,
15354 trigger_in_words: bool,
15355 cx: &mut Context<Editor>,
15356 ) -> bool;
15357
15358 fn sort_completions(&self) -> bool {
15359 true
15360 }
15361}
15362
15363pub trait CodeActionProvider {
15364 fn id(&self) -> Arc<str>;
15365
15366 fn code_actions(
15367 &self,
15368 buffer: &Entity<Buffer>,
15369 range: Range<text::Anchor>,
15370 window: &mut Window,
15371 cx: &mut App,
15372 ) -> Task<Result<Vec<CodeAction>>>;
15373
15374 fn apply_code_action(
15375 &self,
15376 buffer_handle: Entity<Buffer>,
15377 action: CodeAction,
15378 excerpt_id: ExcerptId,
15379 push_to_history: bool,
15380 window: &mut Window,
15381 cx: &mut App,
15382 ) -> Task<Result<ProjectTransaction>>;
15383}
15384
15385impl CodeActionProvider for Entity<Project> {
15386 fn id(&self) -> Arc<str> {
15387 "project".into()
15388 }
15389
15390 fn code_actions(
15391 &self,
15392 buffer: &Entity<Buffer>,
15393 range: Range<text::Anchor>,
15394 _window: &mut Window,
15395 cx: &mut App,
15396 ) -> Task<Result<Vec<CodeAction>>> {
15397 self.update(cx, |project, cx| {
15398 project.code_actions(buffer, range, None, cx)
15399 })
15400 }
15401
15402 fn apply_code_action(
15403 &self,
15404 buffer_handle: Entity<Buffer>,
15405 action: CodeAction,
15406 _excerpt_id: ExcerptId,
15407 push_to_history: bool,
15408 _window: &mut Window,
15409 cx: &mut App,
15410 ) -> Task<Result<ProjectTransaction>> {
15411 self.update(cx, |project, cx| {
15412 project.apply_code_action(buffer_handle, action, push_to_history, cx)
15413 })
15414 }
15415}
15416
15417fn snippet_completions(
15418 project: &Project,
15419 buffer: &Entity<Buffer>,
15420 buffer_position: text::Anchor,
15421 cx: &mut App,
15422) -> Task<Result<Vec<Completion>>> {
15423 let language = buffer.read(cx).language_at(buffer_position);
15424 let language_name = language.as_ref().map(|language| language.lsp_id());
15425 let snippet_store = project.snippets().read(cx);
15426 let snippets = snippet_store.snippets_for(language_name, cx);
15427
15428 if snippets.is_empty() {
15429 return Task::ready(Ok(vec![]));
15430 }
15431 let snapshot = buffer.read(cx).text_snapshot();
15432 let chars: String = snapshot
15433 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
15434 .collect();
15435
15436 let scope = language.map(|language| language.default_scope());
15437 let executor = cx.background_executor().clone();
15438
15439 cx.background_executor().spawn(async move {
15440 let classifier = CharClassifier::new(scope).for_completion(true);
15441 let mut last_word = chars
15442 .chars()
15443 .take_while(|c| classifier.is_word(*c))
15444 .collect::<String>();
15445 last_word = last_word.chars().rev().collect();
15446
15447 if last_word.is_empty() {
15448 return Ok(vec![]);
15449 }
15450
15451 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
15452 let to_lsp = |point: &text::Anchor| {
15453 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
15454 point_to_lsp(end)
15455 };
15456 let lsp_end = to_lsp(&buffer_position);
15457
15458 let candidates = snippets
15459 .iter()
15460 .enumerate()
15461 .flat_map(|(ix, snippet)| {
15462 snippet
15463 .prefix
15464 .iter()
15465 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
15466 })
15467 .collect::<Vec<StringMatchCandidate>>();
15468
15469 let mut matches = fuzzy::match_strings(
15470 &candidates,
15471 &last_word,
15472 last_word.chars().any(|c| c.is_uppercase()),
15473 100,
15474 &Default::default(),
15475 executor,
15476 )
15477 .await;
15478
15479 // Remove all candidates where the query's start does not match the start of any word in the candidate
15480 if let Some(query_start) = last_word.chars().next() {
15481 matches.retain(|string_match| {
15482 split_words(&string_match.string).any(|word| {
15483 // Check that the first codepoint of the word as lowercase matches the first
15484 // codepoint of the query as lowercase
15485 word.chars()
15486 .flat_map(|codepoint| codepoint.to_lowercase())
15487 .zip(query_start.to_lowercase())
15488 .all(|(word_cp, query_cp)| word_cp == query_cp)
15489 })
15490 });
15491 }
15492
15493 let matched_strings = matches
15494 .into_iter()
15495 .map(|m| m.string)
15496 .collect::<HashSet<_>>();
15497
15498 let result: Vec<Completion> = snippets
15499 .into_iter()
15500 .filter_map(|snippet| {
15501 let matching_prefix = snippet
15502 .prefix
15503 .iter()
15504 .find(|prefix| matched_strings.contains(*prefix))?;
15505 let start = as_offset - last_word.len();
15506 let start = snapshot.anchor_before(start);
15507 let range = start..buffer_position;
15508 let lsp_start = to_lsp(&start);
15509 let lsp_range = lsp::Range {
15510 start: lsp_start,
15511 end: lsp_end,
15512 };
15513 Some(Completion {
15514 old_range: range,
15515 new_text: snippet.body.clone(),
15516 resolved: false,
15517 label: CodeLabel {
15518 text: matching_prefix.clone(),
15519 runs: vec![],
15520 filter_range: 0..matching_prefix.len(),
15521 },
15522 server_id: LanguageServerId(usize::MAX),
15523 documentation: snippet
15524 .description
15525 .clone()
15526 .map(CompletionDocumentation::SingleLine),
15527 lsp_completion: lsp::CompletionItem {
15528 label: snippet.prefix.first().unwrap().clone(),
15529 kind: Some(CompletionItemKind::SNIPPET),
15530 label_details: snippet.description.as_ref().map(|description| {
15531 lsp::CompletionItemLabelDetails {
15532 detail: Some(description.clone()),
15533 description: None,
15534 }
15535 }),
15536 insert_text_format: Some(InsertTextFormat::SNIPPET),
15537 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
15538 lsp::InsertReplaceEdit {
15539 new_text: snippet.body.clone(),
15540 insert: lsp_range,
15541 replace: lsp_range,
15542 },
15543 )),
15544 filter_text: Some(snippet.body.clone()),
15545 sort_text: Some(char::MAX.to_string()),
15546 ..Default::default()
15547 },
15548 confirm: None,
15549 })
15550 })
15551 .collect();
15552
15553 Ok(result)
15554 })
15555}
15556
15557impl CompletionProvider for Entity<Project> {
15558 fn completions(
15559 &self,
15560 buffer: &Entity<Buffer>,
15561 buffer_position: text::Anchor,
15562 options: CompletionContext,
15563 _window: &mut Window,
15564 cx: &mut Context<Editor>,
15565 ) -> Task<Result<Vec<Completion>>> {
15566 self.update(cx, |project, cx| {
15567 let snippets = snippet_completions(project, buffer, buffer_position, cx);
15568 let project_completions = project.completions(buffer, buffer_position, options, cx);
15569 cx.background_executor().spawn(async move {
15570 let mut completions = project_completions.await?;
15571 let snippets_completions = snippets.await?;
15572 completions.extend(snippets_completions);
15573 Ok(completions)
15574 })
15575 })
15576 }
15577
15578 fn resolve_completions(
15579 &self,
15580 buffer: Entity<Buffer>,
15581 completion_indices: Vec<usize>,
15582 completions: Rc<RefCell<Box<[Completion]>>>,
15583 cx: &mut Context<Editor>,
15584 ) -> Task<Result<bool>> {
15585 self.update(cx, |project, cx| {
15586 project.lsp_store().update(cx, |lsp_store, cx| {
15587 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
15588 })
15589 })
15590 }
15591
15592 fn apply_additional_edits_for_completion(
15593 &self,
15594 buffer: Entity<Buffer>,
15595 completions: Rc<RefCell<Box<[Completion]>>>,
15596 completion_index: usize,
15597 push_to_history: bool,
15598 cx: &mut Context<Editor>,
15599 ) -> Task<Result<Option<language::Transaction>>> {
15600 self.update(cx, |project, cx| {
15601 project.lsp_store().update(cx, |lsp_store, cx| {
15602 lsp_store.apply_additional_edits_for_completion(
15603 buffer,
15604 completions,
15605 completion_index,
15606 push_to_history,
15607 cx,
15608 )
15609 })
15610 })
15611 }
15612
15613 fn is_completion_trigger(
15614 &self,
15615 buffer: &Entity<Buffer>,
15616 position: language::Anchor,
15617 text: &str,
15618 trigger_in_words: bool,
15619 cx: &mut Context<Editor>,
15620 ) -> bool {
15621 let mut chars = text.chars();
15622 let char = if let Some(char) = chars.next() {
15623 char
15624 } else {
15625 return false;
15626 };
15627 if chars.next().is_some() {
15628 return false;
15629 }
15630
15631 let buffer = buffer.read(cx);
15632 let snapshot = buffer.snapshot();
15633 if !snapshot.settings_at(position, cx).show_completions_on_input {
15634 return false;
15635 }
15636 let classifier = snapshot.char_classifier_at(position).for_completion(true);
15637 if trigger_in_words && classifier.is_word(char) {
15638 return true;
15639 }
15640
15641 buffer.completion_triggers().contains(text)
15642 }
15643}
15644
15645impl SemanticsProvider for Entity<Project> {
15646 fn hover(
15647 &self,
15648 buffer: &Entity<Buffer>,
15649 position: text::Anchor,
15650 cx: &mut App,
15651 ) -> Option<Task<Vec<project::Hover>>> {
15652 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
15653 }
15654
15655 fn document_highlights(
15656 &self,
15657 buffer: &Entity<Buffer>,
15658 position: text::Anchor,
15659 cx: &mut App,
15660 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
15661 Some(self.update(cx, |project, cx| {
15662 project.document_highlights(buffer, position, cx)
15663 }))
15664 }
15665
15666 fn definitions(
15667 &self,
15668 buffer: &Entity<Buffer>,
15669 position: text::Anchor,
15670 kind: GotoDefinitionKind,
15671 cx: &mut App,
15672 ) -> Option<Task<Result<Vec<LocationLink>>>> {
15673 Some(self.update(cx, |project, cx| match kind {
15674 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
15675 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
15676 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
15677 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
15678 }))
15679 }
15680
15681 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &App) -> bool {
15682 // TODO: make this work for remote projects
15683 self.read(cx)
15684 .language_servers_for_local_buffer(buffer.read(cx), cx)
15685 .any(
15686 |(_, server)| match server.capabilities().inlay_hint_provider {
15687 Some(lsp::OneOf::Left(enabled)) => enabled,
15688 Some(lsp::OneOf::Right(_)) => true,
15689 None => false,
15690 },
15691 )
15692 }
15693
15694 fn inlay_hints(
15695 &self,
15696 buffer_handle: Entity<Buffer>,
15697 range: Range<text::Anchor>,
15698 cx: &mut App,
15699 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
15700 Some(self.update(cx, |project, cx| {
15701 project.inlay_hints(buffer_handle, range, cx)
15702 }))
15703 }
15704
15705 fn resolve_inlay_hint(
15706 &self,
15707 hint: InlayHint,
15708 buffer_handle: Entity<Buffer>,
15709 server_id: LanguageServerId,
15710 cx: &mut App,
15711 ) -> Option<Task<anyhow::Result<InlayHint>>> {
15712 Some(self.update(cx, |project, cx| {
15713 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
15714 }))
15715 }
15716
15717 fn range_for_rename(
15718 &self,
15719 buffer: &Entity<Buffer>,
15720 position: text::Anchor,
15721 cx: &mut App,
15722 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
15723 Some(self.update(cx, |project, cx| {
15724 let buffer = buffer.clone();
15725 let task = project.prepare_rename(buffer.clone(), position, cx);
15726 cx.spawn(|_, mut cx| async move {
15727 Ok(match task.await? {
15728 PrepareRenameResponse::Success(range) => Some(range),
15729 PrepareRenameResponse::InvalidPosition => None,
15730 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
15731 // Fallback on using TreeSitter info to determine identifier range
15732 buffer.update(&mut cx, |buffer, _| {
15733 let snapshot = buffer.snapshot();
15734 let (range, kind) = snapshot.surrounding_word(position);
15735 if kind != Some(CharKind::Word) {
15736 return None;
15737 }
15738 Some(
15739 snapshot.anchor_before(range.start)
15740 ..snapshot.anchor_after(range.end),
15741 )
15742 })?
15743 }
15744 })
15745 })
15746 }))
15747 }
15748
15749 fn perform_rename(
15750 &self,
15751 buffer: &Entity<Buffer>,
15752 position: text::Anchor,
15753 new_name: String,
15754 cx: &mut App,
15755 ) -> Option<Task<Result<ProjectTransaction>>> {
15756 Some(self.update(cx, |project, cx| {
15757 project.perform_rename(buffer.clone(), position, new_name, cx)
15758 }))
15759 }
15760}
15761
15762fn inlay_hint_settings(
15763 location: Anchor,
15764 snapshot: &MultiBufferSnapshot,
15765 cx: &mut Context<Editor>,
15766) -> InlayHintSettings {
15767 let file = snapshot.file_at(location);
15768 let language = snapshot.language_at(location).map(|l| l.name());
15769 language_settings(language, file, cx).inlay_hints
15770}
15771
15772fn consume_contiguous_rows(
15773 contiguous_row_selections: &mut Vec<Selection<Point>>,
15774 selection: &Selection<Point>,
15775 display_map: &DisplaySnapshot,
15776 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
15777) -> (MultiBufferRow, MultiBufferRow) {
15778 contiguous_row_selections.push(selection.clone());
15779 let start_row = MultiBufferRow(selection.start.row);
15780 let mut end_row = ending_row(selection, display_map);
15781
15782 while let Some(next_selection) = selections.peek() {
15783 if next_selection.start.row <= end_row.0 {
15784 end_row = ending_row(next_selection, display_map);
15785 contiguous_row_selections.push(selections.next().unwrap().clone());
15786 } else {
15787 break;
15788 }
15789 }
15790 (start_row, end_row)
15791}
15792
15793fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
15794 if next_selection.end.column > 0 || next_selection.is_empty() {
15795 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
15796 } else {
15797 MultiBufferRow(next_selection.end.row)
15798 }
15799}
15800
15801impl EditorSnapshot {
15802 pub fn remote_selections_in_range<'a>(
15803 &'a self,
15804 range: &'a Range<Anchor>,
15805 collaboration_hub: &dyn CollaborationHub,
15806 cx: &'a App,
15807 ) -> impl 'a + Iterator<Item = RemoteSelection> {
15808 let participant_names = collaboration_hub.user_names(cx);
15809 let participant_indices = collaboration_hub.user_participant_indices(cx);
15810 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
15811 let collaborators_by_replica_id = collaborators_by_peer_id
15812 .iter()
15813 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
15814 .collect::<HashMap<_, _>>();
15815 self.buffer_snapshot
15816 .selections_in_range(range, false)
15817 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
15818 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
15819 let participant_index = participant_indices.get(&collaborator.user_id).copied();
15820 let user_name = participant_names.get(&collaborator.user_id).cloned();
15821 Some(RemoteSelection {
15822 replica_id,
15823 selection,
15824 cursor_shape,
15825 line_mode,
15826 participant_index,
15827 peer_id: collaborator.peer_id,
15828 user_name,
15829 })
15830 })
15831 }
15832
15833 pub fn hunks_for_ranges(
15834 &self,
15835 ranges: impl Iterator<Item = Range<Point>>,
15836 ) -> Vec<MultiBufferDiffHunk> {
15837 let mut hunks = Vec::new();
15838 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
15839 HashMap::default();
15840 for query_range in ranges {
15841 let query_rows =
15842 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
15843 for hunk in self.buffer_snapshot.diff_hunks_in_range(
15844 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
15845 ) {
15846 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
15847 // when the caret is just above or just below the deleted hunk.
15848 let allow_adjacent = hunk.status().is_removed();
15849 let related_to_selection = if allow_adjacent {
15850 hunk.row_range.overlaps(&query_rows)
15851 || hunk.row_range.start == query_rows.end
15852 || hunk.row_range.end == query_rows.start
15853 } else {
15854 hunk.row_range.overlaps(&query_rows)
15855 };
15856 if related_to_selection {
15857 if !processed_buffer_rows
15858 .entry(hunk.buffer_id)
15859 .or_default()
15860 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
15861 {
15862 continue;
15863 }
15864 hunks.push(hunk);
15865 }
15866 }
15867 }
15868
15869 hunks
15870 }
15871
15872 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
15873 self.display_snapshot.buffer_snapshot.language_at(position)
15874 }
15875
15876 pub fn is_focused(&self) -> bool {
15877 self.is_focused
15878 }
15879
15880 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
15881 self.placeholder_text.as_ref()
15882 }
15883
15884 pub fn scroll_position(&self) -> gpui::Point<f32> {
15885 self.scroll_anchor.scroll_position(&self.display_snapshot)
15886 }
15887
15888 fn gutter_dimensions(
15889 &self,
15890 font_id: FontId,
15891 font_size: Pixels,
15892 max_line_number_width: Pixels,
15893 cx: &App,
15894 ) -> Option<GutterDimensions> {
15895 if !self.show_gutter {
15896 return None;
15897 }
15898
15899 let descent = cx.text_system().descent(font_id, font_size);
15900 let em_width = cx.text_system().em_width(font_id, font_size).log_err()?;
15901 let em_advance = cx.text_system().em_advance(font_id, font_size).log_err()?;
15902
15903 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
15904 matches!(
15905 ProjectSettings::get_global(cx).git.git_gutter,
15906 Some(GitGutterSetting::TrackedFiles)
15907 )
15908 });
15909 let gutter_settings = EditorSettings::get_global(cx).gutter;
15910 let show_line_numbers = self
15911 .show_line_numbers
15912 .unwrap_or(gutter_settings.line_numbers);
15913 let line_gutter_width = if show_line_numbers {
15914 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
15915 let min_width_for_number_on_gutter = em_advance * 4.0;
15916 max_line_number_width.max(min_width_for_number_on_gutter)
15917 } else {
15918 0.0.into()
15919 };
15920
15921 let show_code_actions = self
15922 .show_code_actions
15923 .unwrap_or(gutter_settings.code_actions);
15924
15925 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
15926
15927 let git_blame_entries_width =
15928 self.git_blame_gutter_max_author_length
15929 .map(|max_author_length| {
15930 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
15931
15932 /// The number of characters to dedicate to gaps and margins.
15933 const SPACING_WIDTH: usize = 4;
15934
15935 let max_char_count = max_author_length
15936 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
15937 + ::git::SHORT_SHA_LENGTH
15938 + MAX_RELATIVE_TIMESTAMP.len()
15939 + SPACING_WIDTH;
15940
15941 em_advance * max_char_count
15942 });
15943
15944 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
15945 left_padding += if show_code_actions || show_runnables {
15946 em_width * 3.0
15947 } else if show_git_gutter && show_line_numbers {
15948 em_width * 2.0
15949 } else if show_git_gutter || show_line_numbers {
15950 em_width
15951 } else {
15952 px(0.)
15953 };
15954
15955 let right_padding = if gutter_settings.folds && show_line_numbers {
15956 em_width * 4.0
15957 } else if gutter_settings.folds {
15958 em_width * 3.0
15959 } else if show_line_numbers {
15960 em_width
15961 } else {
15962 px(0.)
15963 };
15964
15965 Some(GutterDimensions {
15966 left_padding,
15967 right_padding,
15968 width: line_gutter_width + left_padding + right_padding,
15969 margin: -descent,
15970 git_blame_entries_width,
15971 })
15972 }
15973
15974 pub fn render_crease_toggle(
15975 &self,
15976 buffer_row: MultiBufferRow,
15977 row_contains_cursor: bool,
15978 editor: Entity<Editor>,
15979 window: &mut Window,
15980 cx: &mut App,
15981 ) -> Option<AnyElement> {
15982 let folded = self.is_line_folded(buffer_row);
15983 let mut is_foldable = false;
15984
15985 if let Some(crease) = self
15986 .crease_snapshot
15987 .query_row(buffer_row, &self.buffer_snapshot)
15988 {
15989 is_foldable = true;
15990 match crease {
15991 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
15992 if let Some(render_toggle) = render_toggle {
15993 let toggle_callback =
15994 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
15995 if folded {
15996 editor.update(cx, |editor, cx| {
15997 editor.fold_at(&crate::FoldAt { buffer_row }, window, cx)
15998 });
15999 } else {
16000 editor.update(cx, |editor, cx| {
16001 editor.unfold_at(
16002 &crate::UnfoldAt { buffer_row },
16003 window,
16004 cx,
16005 )
16006 });
16007 }
16008 });
16009 return Some((render_toggle)(
16010 buffer_row,
16011 folded,
16012 toggle_callback,
16013 window,
16014 cx,
16015 ));
16016 }
16017 }
16018 }
16019 }
16020
16021 is_foldable |= self.starts_indent(buffer_row);
16022
16023 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
16024 Some(
16025 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
16026 .toggle_state(folded)
16027 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
16028 if folded {
16029 this.unfold_at(&UnfoldAt { buffer_row }, window, cx);
16030 } else {
16031 this.fold_at(&FoldAt { buffer_row }, window, cx);
16032 }
16033 }))
16034 .into_any_element(),
16035 )
16036 } else {
16037 None
16038 }
16039 }
16040
16041 pub fn render_crease_trailer(
16042 &self,
16043 buffer_row: MultiBufferRow,
16044 window: &mut Window,
16045 cx: &mut App,
16046 ) -> Option<AnyElement> {
16047 let folded = self.is_line_folded(buffer_row);
16048 if let Crease::Inline { render_trailer, .. } = self
16049 .crease_snapshot
16050 .query_row(buffer_row, &self.buffer_snapshot)?
16051 {
16052 let render_trailer = render_trailer.as_ref()?;
16053 Some(render_trailer(buffer_row, folded, window, cx))
16054 } else {
16055 None
16056 }
16057 }
16058}
16059
16060impl Deref for EditorSnapshot {
16061 type Target = DisplaySnapshot;
16062
16063 fn deref(&self) -> &Self::Target {
16064 &self.display_snapshot
16065 }
16066}
16067
16068#[derive(Clone, Debug, PartialEq, Eq)]
16069pub enum EditorEvent {
16070 InputIgnored {
16071 text: Arc<str>,
16072 },
16073 InputHandled {
16074 utf16_range_to_replace: Option<Range<isize>>,
16075 text: Arc<str>,
16076 },
16077 ExcerptsAdded {
16078 buffer: Entity<Buffer>,
16079 predecessor: ExcerptId,
16080 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
16081 },
16082 ExcerptsRemoved {
16083 ids: Vec<ExcerptId>,
16084 },
16085 BufferFoldToggled {
16086 ids: Vec<ExcerptId>,
16087 folded: bool,
16088 },
16089 ExcerptsEdited {
16090 ids: Vec<ExcerptId>,
16091 },
16092 ExcerptsExpanded {
16093 ids: Vec<ExcerptId>,
16094 },
16095 BufferEdited,
16096 Edited {
16097 transaction_id: clock::Lamport,
16098 },
16099 Reparsed(BufferId),
16100 Focused,
16101 FocusedIn,
16102 Blurred,
16103 DirtyChanged,
16104 Saved,
16105 TitleChanged,
16106 DiffBaseChanged,
16107 SelectionsChanged {
16108 local: bool,
16109 },
16110 ScrollPositionChanged {
16111 local: bool,
16112 autoscroll: bool,
16113 },
16114 Closed,
16115 TransactionUndone {
16116 transaction_id: clock::Lamport,
16117 },
16118 TransactionBegun {
16119 transaction_id: clock::Lamport,
16120 },
16121 Reloaded,
16122 CursorShapeChanged,
16123}
16124
16125impl EventEmitter<EditorEvent> for Editor {}
16126
16127impl Focusable for Editor {
16128 fn focus_handle(&self, _cx: &App) -> FocusHandle {
16129 self.focus_handle.clone()
16130 }
16131}
16132
16133impl Render for Editor {
16134 fn render<'a>(&mut self, _: &mut Window, cx: &mut Context<'a, Self>) -> impl IntoElement {
16135 let settings = ThemeSettings::get_global(cx);
16136
16137 let mut text_style = match self.mode {
16138 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
16139 color: cx.theme().colors().editor_foreground,
16140 font_family: settings.ui_font.family.clone(),
16141 font_features: settings.ui_font.features.clone(),
16142 font_fallbacks: settings.ui_font.fallbacks.clone(),
16143 font_size: rems(0.875).into(),
16144 font_weight: settings.ui_font.weight,
16145 line_height: relative(settings.buffer_line_height.value()),
16146 ..Default::default()
16147 },
16148 EditorMode::Full => TextStyle {
16149 color: cx.theme().colors().editor_foreground,
16150 font_family: settings.buffer_font.family.clone(),
16151 font_features: settings.buffer_font.features.clone(),
16152 font_fallbacks: settings.buffer_font.fallbacks.clone(),
16153 font_size: settings.buffer_font_size().into(),
16154 font_weight: settings.buffer_font.weight,
16155 line_height: relative(settings.buffer_line_height.value()),
16156 ..Default::default()
16157 },
16158 };
16159 if let Some(text_style_refinement) = &self.text_style_refinement {
16160 text_style.refine(text_style_refinement)
16161 }
16162
16163 let background = match self.mode {
16164 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
16165 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
16166 EditorMode::Full => cx.theme().colors().editor_background,
16167 };
16168
16169 EditorElement::new(
16170 &cx.entity(),
16171 EditorStyle {
16172 background,
16173 local_player: cx.theme().players().local(),
16174 text: text_style,
16175 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
16176 syntax: cx.theme().syntax().clone(),
16177 status: cx.theme().status().clone(),
16178 inlay_hints_style: make_inlay_hints_style(cx),
16179 inline_completion_styles: make_suggestion_styles(cx),
16180 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
16181 },
16182 )
16183 }
16184}
16185
16186impl EntityInputHandler for Editor {
16187 fn text_for_range(
16188 &mut self,
16189 range_utf16: Range<usize>,
16190 adjusted_range: &mut Option<Range<usize>>,
16191 _: &mut Window,
16192 cx: &mut Context<Self>,
16193 ) -> Option<String> {
16194 let snapshot = self.buffer.read(cx).read(cx);
16195 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
16196 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
16197 if (start.0..end.0) != range_utf16 {
16198 adjusted_range.replace(start.0..end.0);
16199 }
16200 Some(snapshot.text_for_range(start..end).collect())
16201 }
16202
16203 fn selected_text_range(
16204 &mut self,
16205 ignore_disabled_input: bool,
16206 _: &mut Window,
16207 cx: &mut Context<Self>,
16208 ) -> Option<UTF16Selection> {
16209 // Prevent the IME menu from appearing when holding down an alphabetic key
16210 // while input is disabled.
16211 if !ignore_disabled_input && !self.input_enabled {
16212 return None;
16213 }
16214
16215 let selection = self.selections.newest::<OffsetUtf16>(cx);
16216 let range = selection.range();
16217
16218 Some(UTF16Selection {
16219 range: range.start.0..range.end.0,
16220 reversed: selection.reversed,
16221 })
16222 }
16223
16224 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
16225 let snapshot = self.buffer.read(cx).read(cx);
16226 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
16227 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
16228 }
16229
16230 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
16231 self.clear_highlights::<InputComposition>(cx);
16232 self.ime_transaction.take();
16233 }
16234
16235 fn replace_text_in_range(
16236 &mut self,
16237 range_utf16: Option<Range<usize>>,
16238 text: &str,
16239 window: &mut Window,
16240 cx: &mut Context<Self>,
16241 ) {
16242 if !self.input_enabled {
16243 cx.emit(EditorEvent::InputIgnored { text: text.into() });
16244 return;
16245 }
16246
16247 self.transact(window, cx, |this, window, cx| {
16248 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
16249 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
16250 Some(this.selection_replacement_ranges(range_utf16, cx))
16251 } else {
16252 this.marked_text_ranges(cx)
16253 };
16254
16255 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
16256 let newest_selection_id = this.selections.newest_anchor().id;
16257 this.selections
16258 .all::<OffsetUtf16>(cx)
16259 .iter()
16260 .zip(ranges_to_replace.iter())
16261 .find_map(|(selection, range)| {
16262 if selection.id == newest_selection_id {
16263 Some(
16264 (range.start.0 as isize - selection.head().0 as isize)
16265 ..(range.end.0 as isize - selection.head().0 as isize),
16266 )
16267 } else {
16268 None
16269 }
16270 })
16271 });
16272
16273 cx.emit(EditorEvent::InputHandled {
16274 utf16_range_to_replace: range_to_replace,
16275 text: text.into(),
16276 });
16277
16278 if let Some(new_selected_ranges) = new_selected_ranges {
16279 this.change_selections(None, window, cx, |selections| {
16280 selections.select_ranges(new_selected_ranges)
16281 });
16282 this.backspace(&Default::default(), window, cx);
16283 }
16284
16285 this.handle_input(text, window, cx);
16286 });
16287
16288 if let Some(transaction) = self.ime_transaction {
16289 self.buffer.update(cx, |buffer, cx| {
16290 buffer.group_until_transaction(transaction, cx);
16291 });
16292 }
16293
16294 self.unmark_text(window, cx);
16295 }
16296
16297 fn replace_and_mark_text_in_range(
16298 &mut self,
16299 range_utf16: Option<Range<usize>>,
16300 text: &str,
16301 new_selected_range_utf16: Option<Range<usize>>,
16302 window: &mut Window,
16303 cx: &mut Context<Self>,
16304 ) {
16305 if !self.input_enabled {
16306 return;
16307 }
16308
16309 let transaction = self.transact(window, cx, |this, window, cx| {
16310 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
16311 let snapshot = this.buffer.read(cx).read(cx);
16312 if let Some(relative_range_utf16) = range_utf16.as_ref() {
16313 for marked_range in &mut marked_ranges {
16314 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
16315 marked_range.start.0 += relative_range_utf16.start;
16316 marked_range.start =
16317 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
16318 marked_range.end =
16319 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
16320 }
16321 }
16322 Some(marked_ranges)
16323 } else if let Some(range_utf16) = range_utf16 {
16324 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
16325 Some(this.selection_replacement_ranges(range_utf16, cx))
16326 } else {
16327 None
16328 };
16329
16330 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
16331 let newest_selection_id = this.selections.newest_anchor().id;
16332 this.selections
16333 .all::<OffsetUtf16>(cx)
16334 .iter()
16335 .zip(ranges_to_replace.iter())
16336 .find_map(|(selection, range)| {
16337 if selection.id == newest_selection_id {
16338 Some(
16339 (range.start.0 as isize - selection.head().0 as isize)
16340 ..(range.end.0 as isize - selection.head().0 as isize),
16341 )
16342 } else {
16343 None
16344 }
16345 })
16346 });
16347
16348 cx.emit(EditorEvent::InputHandled {
16349 utf16_range_to_replace: range_to_replace,
16350 text: text.into(),
16351 });
16352
16353 if let Some(ranges) = ranges_to_replace {
16354 this.change_selections(None, window, cx, |s| s.select_ranges(ranges));
16355 }
16356
16357 let marked_ranges = {
16358 let snapshot = this.buffer.read(cx).read(cx);
16359 this.selections
16360 .disjoint_anchors()
16361 .iter()
16362 .map(|selection| {
16363 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
16364 })
16365 .collect::<Vec<_>>()
16366 };
16367
16368 if text.is_empty() {
16369 this.unmark_text(window, cx);
16370 } else {
16371 this.highlight_text::<InputComposition>(
16372 marked_ranges.clone(),
16373 HighlightStyle {
16374 underline: Some(UnderlineStyle {
16375 thickness: px(1.),
16376 color: None,
16377 wavy: false,
16378 }),
16379 ..Default::default()
16380 },
16381 cx,
16382 );
16383 }
16384
16385 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
16386 let use_autoclose = this.use_autoclose;
16387 let use_auto_surround = this.use_auto_surround;
16388 this.set_use_autoclose(false);
16389 this.set_use_auto_surround(false);
16390 this.handle_input(text, window, cx);
16391 this.set_use_autoclose(use_autoclose);
16392 this.set_use_auto_surround(use_auto_surround);
16393
16394 if let Some(new_selected_range) = new_selected_range_utf16 {
16395 let snapshot = this.buffer.read(cx).read(cx);
16396 let new_selected_ranges = marked_ranges
16397 .into_iter()
16398 .map(|marked_range| {
16399 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
16400 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
16401 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
16402 snapshot.clip_offset_utf16(new_start, Bias::Left)
16403 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
16404 })
16405 .collect::<Vec<_>>();
16406
16407 drop(snapshot);
16408 this.change_selections(None, window, cx, |selections| {
16409 selections.select_ranges(new_selected_ranges)
16410 });
16411 }
16412 });
16413
16414 self.ime_transaction = self.ime_transaction.or(transaction);
16415 if let Some(transaction) = self.ime_transaction {
16416 self.buffer.update(cx, |buffer, cx| {
16417 buffer.group_until_transaction(transaction, cx);
16418 });
16419 }
16420
16421 if self.text_highlights::<InputComposition>(cx).is_none() {
16422 self.ime_transaction.take();
16423 }
16424 }
16425
16426 fn bounds_for_range(
16427 &mut self,
16428 range_utf16: Range<usize>,
16429 element_bounds: gpui::Bounds<Pixels>,
16430 window: &mut Window,
16431 cx: &mut Context<Self>,
16432 ) -> Option<gpui::Bounds<Pixels>> {
16433 let text_layout_details = self.text_layout_details(window);
16434 let gpui::Size {
16435 width: em_width,
16436 height: line_height,
16437 } = self.character_size(window);
16438
16439 let snapshot = self.snapshot(window, cx);
16440 let scroll_position = snapshot.scroll_position();
16441 let scroll_left = scroll_position.x * em_width;
16442
16443 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
16444 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
16445 + self.gutter_dimensions.width
16446 + self.gutter_dimensions.margin;
16447 let y = line_height * (start.row().as_f32() - scroll_position.y);
16448
16449 Some(Bounds {
16450 origin: element_bounds.origin + point(x, y),
16451 size: size(em_width, line_height),
16452 })
16453 }
16454
16455 fn character_index_for_point(
16456 &mut self,
16457 point: gpui::Point<Pixels>,
16458 _window: &mut Window,
16459 _cx: &mut Context<Self>,
16460 ) -> Option<usize> {
16461 let position_map = self.last_position_map.as_ref()?;
16462 if !position_map.text_hitbox.contains(&point) {
16463 return None;
16464 }
16465 let display_point = position_map.point_for_position(point).previous_valid;
16466 let anchor = position_map
16467 .snapshot
16468 .display_point_to_anchor(display_point, Bias::Left);
16469 let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot);
16470 Some(utf16_offset.0)
16471 }
16472}
16473
16474trait SelectionExt {
16475 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
16476 fn spanned_rows(
16477 &self,
16478 include_end_if_at_line_start: bool,
16479 map: &DisplaySnapshot,
16480 ) -> Range<MultiBufferRow>;
16481}
16482
16483impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
16484 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
16485 let start = self
16486 .start
16487 .to_point(&map.buffer_snapshot)
16488 .to_display_point(map);
16489 let end = self
16490 .end
16491 .to_point(&map.buffer_snapshot)
16492 .to_display_point(map);
16493 if self.reversed {
16494 end..start
16495 } else {
16496 start..end
16497 }
16498 }
16499
16500 fn spanned_rows(
16501 &self,
16502 include_end_if_at_line_start: bool,
16503 map: &DisplaySnapshot,
16504 ) -> Range<MultiBufferRow> {
16505 let start = self.start.to_point(&map.buffer_snapshot);
16506 let mut end = self.end.to_point(&map.buffer_snapshot);
16507 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
16508 end.row -= 1;
16509 }
16510
16511 let buffer_start = map.prev_line_boundary(start).0;
16512 let buffer_end = map.next_line_boundary(end).0;
16513 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
16514 }
16515}
16516
16517impl<T: InvalidationRegion> InvalidationStack<T> {
16518 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
16519 where
16520 S: Clone + ToOffset,
16521 {
16522 while let Some(region) = self.last() {
16523 let all_selections_inside_invalidation_ranges =
16524 if selections.len() == region.ranges().len() {
16525 selections
16526 .iter()
16527 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
16528 .all(|(selection, invalidation_range)| {
16529 let head = selection.head().to_offset(buffer);
16530 invalidation_range.start <= head && invalidation_range.end >= head
16531 })
16532 } else {
16533 false
16534 };
16535
16536 if all_selections_inside_invalidation_ranges {
16537 break;
16538 } else {
16539 self.pop();
16540 }
16541 }
16542 }
16543}
16544
16545impl<T> Default for InvalidationStack<T> {
16546 fn default() -> Self {
16547 Self(Default::default())
16548 }
16549}
16550
16551impl<T> Deref for InvalidationStack<T> {
16552 type Target = Vec<T>;
16553
16554 fn deref(&self) -> &Self::Target {
16555 &self.0
16556 }
16557}
16558
16559impl<T> DerefMut for InvalidationStack<T> {
16560 fn deref_mut(&mut self) -> &mut Self::Target {
16561 &mut self.0
16562 }
16563}
16564
16565impl InvalidationRegion for SnippetState {
16566 fn ranges(&self) -> &[Range<Anchor>] {
16567 &self.ranges[self.active_index]
16568 }
16569}
16570
16571pub fn diagnostic_block_renderer(
16572 diagnostic: Diagnostic,
16573 max_message_rows: Option<u8>,
16574 allow_closing: bool,
16575 _is_valid: bool,
16576) -> RenderBlock {
16577 let (text_without_backticks, code_ranges) =
16578 highlight_diagnostic_message(&diagnostic, max_message_rows);
16579
16580 Arc::new(move |cx: &mut BlockContext| {
16581 let group_id: SharedString = cx.block_id.to_string().into();
16582
16583 let mut text_style = cx.window.text_style().clone();
16584 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
16585 let theme_settings = ThemeSettings::get_global(cx);
16586 text_style.font_family = theme_settings.buffer_font.family.clone();
16587 text_style.font_style = theme_settings.buffer_font.style;
16588 text_style.font_features = theme_settings.buffer_font.features.clone();
16589 text_style.font_weight = theme_settings.buffer_font.weight;
16590
16591 let multi_line_diagnostic = diagnostic.message.contains('\n');
16592
16593 let buttons = |diagnostic: &Diagnostic| {
16594 if multi_line_diagnostic {
16595 v_flex()
16596 } else {
16597 h_flex()
16598 }
16599 .when(allow_closing, |div| {
16600 div.children(diagnostic.is_primary.then(|| {
16601 IconButton::new("close-block", IconName::XCircle)
16602 .icon_color(Color::Muted)
16603 .size(ButtonSize::Compact)
16604 .style(ButtonStyle::Transparent)
16605 .visible_on_hover(group_id.clone())
16606 .on_click(move |_click, window, cx| {
16607 window.dispatch_action(Box::new(Cancel), cx)
16608 })
16609 .tooltip(|window, cx| {
16610 Tooltip::for_action("Close Diagnostics", &Cancel, window, cx)
16611 })
16612 }))
16613 })
16614 .child(
16615 IconButton::new("copy-block", IconName::Copy)
16616 .icon_color(Color::Muted)
16617 .size(ButtonSize::Compact)
16618 .style(ButtonStyle::Transparent)
16619 .visible_on_hover(group_id.clone())
16620 .on_click({
16621 let message = diagnostic.message.clone();
16622 move |_click, _, cx| {
16623 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
16624 }
16625 })
16626 .tooltip(Tooltip::text("Copy diagnostic message")),
16627 )
16628 };
16629
16630 let icon_size = buttons(&diagnostic).into_any_element().layout_as_root(
16631 AvailableSpace::min_size(),
16632 cx.window,
16633 cx.app,
16634 );
16635
16636 h_flex()
16637 .id(cx.block_id)
16638 .group(group_id.clone())
16639 .relative()
16640 .size_full()
16641 .block_mouse_down()
16642 .pl(cx.gutter_dimensions.width)
16643 .w(cx.max_width - cx.gutter_dimensions.full_width())
16644 .child(
16645 div()
16646 .flex()
16647 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
16648 .flex_shrink(),
16649 )
16650 .child(buttons(&diagnostic))
16651 .child(div().flex().flex_shrink_0().child(
16652 StyledText::new(text_without_backticks.clone()).with_highlights(
16653 &text_style,
16654 code_ranges.iter().map(|range| {
16655 (
16656 range.clone(),
16657 HighlightStyle {
16658 font_weight: Some(FontWeight::BOLD),
16659 ..Default::default()
16660 },
16661 )
16662 }),
16663 ),
16664 ))
16665 .into_any_element()
16666 })
16667}
16668
16669fn inline_completion_edit_text(
16670 current_snapshot: &BufferSnapshot,
16671 edits: &[(Range<Anchor>, String)],
16672 edit_preview: &EditPreview,
16673 include_deletions: bool,
16674 cx: &App,
16675) -> HighlightedText {
16676 let edits = edits
16677 .iter()
16678 .map(|(anchor, text)| {
16679 (
16680 anchor.start.text_anchor..anchor.end.text_anchor,
16681 text.clone(),
16682 )
16683 })
16684 .collect::<Vec<_>>();
16685
16686 edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
16687}
16688
16689pub fn highlight_diagnostic_message(
16690 diagnostic: &Diagnostic,
16691 mut max_message_rows: Option<u8>,
16692) -> (SharedString, Vec<Range<usize>>) {
16693 let mut text_without_backticks = String::new();
16694 let mut code_ranges = Vec::new();
16695
16696 if let Some(source) = &diagnostic.source {
16697 text_without_backticks.push_str(source);
16698 code_ranges.push(0..source.len());
16699 text_without_backticks.push_str(": ");
16700 }
16701
16702 let mut prev_offset = 0;
16703 let mut in_code_block = false;
16704 let has_row_limit = max_message_rows.is_some();
16705 let mut newline_indices = diagnostic
16706 .message
16707 .match_indices('\n')
16708 .filter(|_| has_row_limit)
16709 .map(|(ix, _)| ix)
16710 .fuse()
16711 .peekable();
16712
16713 for (quote_ix, _) in diagnostic
16714 .message
16715 .match_indices('`')
16716 .chain([(diagnostic.message.len(), "")])
16717 {
16718 let mut first_newline_ix = None;
16719 let mut last_newline_ix = None;
16720 while let Some(newline_ix) = newline_indices.peek() {
16721 if *newline_ix < quote_ix {
16722 if first_newline_ix.is_none() {
16723 first_newline_ix = Some(*newline_ix);
16724 }
16725 last_newline_ix = Some(*newline_ix);
16726
16727 if let Some(rows_left) = &mut max_message_rows {
16728 if *rows_left == 0 {
16729 break;
16730 } else {
16731 *rows_left -= 1;
16732 }
16733 }
16734 let _ = newline_indices.next();
16735 } else {
16736 break;
16737 }
16738 }
16739 let prev_len = text_without_backticks.len();
16740 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
16741 text_without_backticks.push_str(new_text);
16742 if in_code_block {
16743 code_ranges.push(prev_len..text_without_backticks.len());
16744 }
16745 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
16746 in_code_block = !in_code_block;
16747 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
16748 text_without_backticks.push_str("...");
16749 break;
16750 }
16751 }
16752
16753 (text_without_backticks.into(), code_ranges)
16754}
16755
16756fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
16757 match severity {
16758 DiagnosticSeverity::ERROR => colors.error,
16759 DiagnosticSeverity::WARNING => colors.warning,
16760 DiagnosticSeverity::INFORMATION => colors.info,
16761 DiagnosticSeverity::HINT => colors.info,
16762 _ => colors.ignored,
16763 }
16764}
16765
16766pub fn styled_runs_for_code_label<'a>(
16767 label: &'a CodeLabel,
16768 syntax_theme: &'a theme::SyntaxTheme,
16769) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
16770 let fade_out = HighlightStyle {
16771 fade_out: Some(0.35),
16772 ..Default::default()
16773 };
16774
16775 let mut prev_end = label.filter_range.end;
16776 label
16777 .runs
16778 .iter()
16779 .enumerate()
16780 .flat_map(move |(ix, (range, highlight_id))| {
16781 let style = if let Some(style) = highlight_id.style(syntax_theme) {
16782 style
16783 } else {
16784 return Default::default();
16785 };
16786 let mut muted_style = style;
16787 muted_style.highlight(fade_out);
16788
16789 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
16790 if range.start >= label.filter_range.end {
16791 if range.start > prev_end {
16792 runs.push((prev_end..range.start, fade_out));
16793 }
16794 runs.push((range.clone(), muted_style));
16795 } else if range.end <= label.filter_range.end {
16796 runs.push((range.clone(), style));
16797 } else {
16798 runs.push((range.start..label.filter_range.end, style));
16799 runs.push((label.filter_range.end..range.end, muted_style));
16800 }
16801 prev_end = cmp::max(prev_end, range.end);
16802
16803 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
16804 runs.push((prev_end..label.text.len(), fade_out));
16805 }
16806
16807 runs
16808 })
16809}
16810
16811pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
16812 let mut prev_index = 0;
16813 let mut prev_codepoint: Option<char> = None;
16814 text.char_indices()
16815 .chain([(text.len(), '\0')])
16816 .filter_map(move |(index, codepoint)| {
16817 let prev_codepoint = prev_codepoint.replace(codepoint)?;
16818 let is_boundary = index == text.len()
16819 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
16820 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
16821 if is_boundary {
16822 let chunk = &text[prev_index..index];
16823 prev_index = index;
16824 Some(chunk)
16825 } else {
16826 None
16827 }
16828 })
16829}
16830
16831pub trait RangeToAnchorExt: Sized {
16832 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
16833
16834 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
16835 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
16836 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
16837 }
16838}
16839
16840impl<T: ToOffset> RangeToAnchorExt for Range<T> {
16841 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
16842 let start_offset = self.start.to_offset(snapshot);
16843 let end_offset = self.end.to_offset(snapshot);
16844 if start_offset == end_offset {
16845 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
16846 } else {
16847 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
16848 }
16849 }
16850}
16851
16852pub trait RowExt {
16853 fn as_f32(&self) -> f32;
16854
16855 fn next_row(&self) -> Self;
16856
16857 fn previous_row(&self) -> Self;
16858
16859 fn minus(&self, other: Self) -> u32;
16860}
16861
16862impl RowExt for DisplayRow {
16863 fn as_f32(&self) -> f32 {
16864 self.0 as f32
16865 }
16866
16867 fn next_row(&self) -> Self {
16868 Self(self.0 + 1)
16869 }
16870
16871 fn previous_row(&self) -> Self {
16872 Self(self.0.saturating_sub(1))
16873 }
16874
16875 fn minus(&self, other: Self) -> u32 {
16876 self.0 - other.0
16877 }
16878}
16879
16880impl RowExt for MultiBufferRow {
16881 fn as_f32(&self) -> f32 {
16882 self.0 as f32
16883 }
16884
16885 fn next_row(&self) -> Self {
16886 Self(self.0 + 1)
16887 }
16888
16889 fn previous_row(&self) -> Self {
16890 Self(self.0.saturating_sub(1))
16891 }
16892
16893 fn minus(&self, other: Self) -> u32 {
16894 self.0 - other.0
16895 }
16896}
16897
16898trait RowRangeExt {
16899 type Row;
16900
16901 fn len(&self) -> usize;
16902
16903 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
16904}
16905
16906impl RowRangeExt for Range<MultiBufferRow> {
16907 type Row = MultiBufferRow;
16908
16909 fn len(&self) -> usize {
16910 (self.end.0 - self.start.0) as usize
16911 }
16912
16913 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
16914 (self.start.0..self.end.0).map(MultiBufferRow)
16915 }
16916}
16917
16918impl RowRangeExt for Range<DisplayRow> {
16919 type Row = DisplayRow;
16920
16921 fn len(&self) -> usize {
16922 (self.end.0 - self.start.0) as usize
16923 }
16924
16925 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
16926 (self.start.0..self.end.0).map(DisplayRow)
16927 }
16928}
16929
16930/// If select range has more than one line, we
16931/// just point the cursor to range.start.
16932fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
16933 if range.start.row == range.end.row {
16934 range
16935 } else {
16936 range.start..range.start
16937 }
16938}
16939pub struct KillRing(ClipboardItem);
16940impl Global for KillRing {}
16941
16942const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
16943
16944fn all_edits_insertions_or_deletions(
16945 edits: &Vec<(Range<Anchor>, String)>,
16946 snapshot: &MultiBufferSnapshot,
16947) -> bool {
16948 let mut all_insertions = true;
16949 let mut all_deletions = true;
16950
16951 for (range, new_text) in edits.iter() {
16952 let range_is_empty = range.to_offset(&snapshot).is_empty();
16953 let text_is_empty = new_text.is_empty();
16954
16955 if range_is_empty != text_is_empty {
16956 if range_is_empty {
16957 all_deletions = false;
16958 } else {
16959 all_insertions = false;
16960 }
16961 } else {
16962 return false;
16963 }
16964
16965 if !all_insertions && !all_deletions {
16966 return false;
16967 }
16968 }
16969 all_insertions || all_deletions
16970}