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, EditPredictionsMode, EditPreview,
99 HighlightedText, IndentKind, IndentSize, 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 && self
5051 .edit_prediction_provider
5052 .as_ref()
5053 .map_or(false, |provider| {
5054 provider.provider.show_completions_in_menu()
5055 });
5056
5057 let preview_requires_modifier =
5058 all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Auto;
5059
5060 EditPredictionSettings::Enabled {
5061 show_in_menu,
5062 preview_requires_modifier,
5063 }
5064 }
5065
5066 fn should_show_edit_predictions(&self) -> bool {
5067 self.snippet_stack.is_empty() && self.edit_predictions_enabled()
5068 }
5069
5070 pub fn inline_completions_enabled(&self, cx: &App) -> bool {
5071 let cursor = self.selections.newest_anchor().head();
5072 if let Some((buffer, cursor_position)) =
5073 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5074 {
5075 self.inline_completions_enabled_in_buffer(&buffer, cursor_position, cx)
5076 } else {
5077 false
5078 }
5079 }
5080
5081 fn inline_completions_enabled_in_buffer(
5082 &self,
5083 buffer: &Entity<Buffer>,
5084 buffer_position: language::Anchor,
5085 cx: &App,
5086 ) -> bool {
5087 maybe!({
5088 let provider = self.edit_prediction_provider()?;
5089 if !provider.is_enabled(&buffer, buffer_position, cx) {
5090 return Some(false);
5091 }
5092 let buffer = buffer.read(cx);
5093 let Some(file) = buffer.file() else {
5094 return Some(true);
5095 };
5096 let settings = all_language_settings(Some(file), cx);
5097 Some(settings.inline_completions_enabled_for_path(file.path()))
5098 })
5099 .unwrap_or(false)
5100 }
5101
5102 fn cycle_inline_completion(
5103 &mut self,
5104 direction: Direction,
5105 window: &mut Window,
5106 cx: &mut Context<Self>,
5107 ) -> Option<()> {
5108 let provider = self.edit_prediction_provider()?;
5109 let cursor = self.selections.newest_anchor().head();
5110 let (buffer, cursor_buffer_position) =
5111 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5112 if self.inline_completions_hidden_for_vim_mode || !self.should_show_edit_predictions() {
5113 return None;
5114 }
5115
5116 provider.cycle(buffer, cursor_buffer_position, direction, cx);
5117 self.update_visible_inline_completion(window, cx);
5118
5119 Some(())
5120 }
5121
5122 pub fn show_inline_completion(
5123 &mut self,
5124 _: &ShowEditPrediction,
5125 window: &mut Window,
5126 cx: &mut Context<Self>,
5127 ) {
5128 if !self.has_active_inline_completion() {
5129 self.refresh_inline_completion(false, true, window, cx);
5130 return;
5131 }
5132
5133 self.update_visible_inline_completion(window, cx);
5134 }
5135
5136 pub fn display_cursor_names(
5137 &mut self,
5138 _: &DisplayCursorNames,
5139 window: &mut Window,
5140 cx: &mut Context<Self>,
5141 ) {
5142 self.show_cursor_names(window, cx);
5143 }
5144
5145 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
5146 self.show_cursor_names = true;
5147 cx.notify();
5148 cx.spawn_in(window, |this, mut cx| async move {
5149 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5150 this.update(&mut cx, |this, cx| {
5151 this.show_cursor_names = false;
5152 cx.notify()
5153 })
5154 .ok()
5155 })
5156 .detach();
5157 }
5158
5159 pub fn next_edit_prediction(
5160 &mut self,
5161 _: &NextEditPrediction,
5162 window: &mut Window,
5163 cx: &mut Context<Self>,
5164 ) {
5165 if self.has_active_inline_completion() {
5166 self.cycle_inline_completion(Direction::Next, window, cx);
5167 } else {
5168 let is_copilot_disabled = self
5169 .refresh_inline_completion(false, true, window, cx)
5170 .is_none();
5171 if is_copilot_disabled {
5172 cx.propagate();
5173 }
5174 }
5175 }
5176
5177 pub fn previous_edit_prediction(
5178 &mut self,
5179 _: &PreviousEditPrediction,
5180 window: &mut Window,
5181 cx: &mut Context<Self>,
5182 ) {
5183 if self.has_active_inline_completion() {
5184 self.cycle_inline_completion(Direction::Prev, window, cx);
5185 } else {
5186 let is_copilot_disabled = self
5187 .refresh_inline_completion(false, true, window, cx)
5188 .is_none();
5189 if is_copilot_disabled {
5190 cx.propagate();
5191 }
5192 }
5193 }
5194
5195 pub fn accept_edit_prediction(
5196 &mut self,
5197 _: &AcceptEditPrediction,
5198 window: &mut Window,
5199 cx: &mut Context<Self>,
5200 ) {
5201 let buffer = self.buffer.read(cx);
5202 let snapshot = buffer.snapshot(cx);
5203 let selection = self.selections.newest_adjusted(cx);
5204 let cursor = selection.head();
5205 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
5206 let suggested_indents = snapshot.suggested_indents([cursor.row], cx);
5207 if let Some(suggested_indent) = suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
5208 {
5209 if cursor.column < suggested_indent.len
5210 && cursor.column <= current_indent.len
5211 && current_indent.len <= suggested_indent.len
5212 {
5213 self.tab(&Default::default(), window, cx);
5214 return;
5215 }
5216 }
5217
5218 if self.show_edit_predictions_in_menu() {
5219 self.hide_context_menu(window, cx);
5220 }
5221
5222 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5223 return;
5224 };
5225
5226 self.report_inline_completion_event(
5227 active_inline_completion.completion_id.clone(),
5228 true,
5229 cx,
5230 );
5231
5232 match &active_inline_completion.completion {
5233 InlineCompletion::Move { target, .. } => {
5234 let target = *target;
5235 // Note that this is also done in vim's handler of the Tab action.
5236 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
5237 selections.select_anchor_ranges([target..target]);
5238 });
5239 }
5240 InlineCompletion::Edit { edits, .. } => {
5241 if let Some(provider) = self.edit_prediction_provider() {
5242 provider.accept(cx);
5243 }
5244
5245 let snapshot = self.buffer.read(cx).snapshot(cx);
5246 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
5247
5248 self.buffer.update(cx, |buffer, cx| {
5249 buffer.edit(edits.iter().cloned(), None, cx)
5250 });
5251
5252 self.change_selections(None, window, cx, |s| {
5253 s.select_anchor_ranges([last_edit_end..last_edit_end])
5254 });
5255
5256 self.update_visible_inline_completion(window, cx);
5257 if self.active_inline_completion.is_none() {
5258 self.refresh_inline_completion(true, true, window, cx);
5259 }
5260
5261 cx.notify();
5262 }
5263 }
5264 }
5265
5266 pub fn accept_partial_inline_completion(
5267 &mut self,
5268 _: &AcceptPartialEditPrediction,
5269 window: &mut Window,
5270 cx: &mut Context<Self>,
5271 ) {
5272 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5273 return;
5274 };
5275 if self.selections.count() != 1 {
5276 return;
5277 }
5278
5279 self.report_inline_completion_event(
5280 active_inline_completion.completion_id.clone(),
5281 true,
5282 cx,
5283 );
5284
5285 match &active_inline_completion.completion {
5286 InlineCompletion::Move { target, .. } => {
5287 let target = *target;
5288 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
5289 selections.select_anchor_ranges([target..target]);
5290 });
5291 }
5292 InlineCompletion::Edit { edits, .. } => {
5293 // Find an insertion that starts at the cursor position.
5294 let snapshot = self.buffer.read(cx).snapshot(cx);
5295 let cursor_offset = self.selections.newest::<usize>(cx).head();
5296 let insertion = edits.iter().find_map(|(range, text)| {
5297 let range = range.to_offset(&snapshot);
5298 if range.is_empty() && range.start == cursor_offset {
5299 Some(text)
5300 } else {
5301 None
5302 }
5303 });
5304
5305 if let Some(text) = insertion {
5306 let mut partial_completion = text
5307 .chars()
5308 .by_ref()
5309 .take_while(|c| c.is_alphabetic())
5310 .collect::<String>();
5311 if partial_completion.is_empty() {
5312 partial_completion = text
5313 .chars()
5314 .by_ref()
5315 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5316 .collect::<String>();
5317 }
5318
5319 cx.emit(EditorEvent::InputHandled {
5320 utf16_range_to_replace: None,
5321 text: partial_completion.clone().into(),
5322 });
5323
5324 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
5325
5326 self.refresh_inline_completion(true, true, window, cx);
5327 cx.notify();
5328 } else {
5329 self.accept_edit_prediction(&Default::default(), window, cx);
5330 }
5331 }
5332 }
5333 }
5334
5335 fn discard_inline_completion(
5336 &mut self,
5337 should_report_inline_completion_event: bool,
5338 cx: &mut Context<Self>,
5339 ) -> bool {
5340 if should_report_inline_completion_event {
5341 let completion_id = self
5342 .active_inline_completion
5343 .as_ref()
5344 .and_then(|active_completion| active_completion.completion_id.clone());
5345
5346 self.report_inline_completion_event(completion_id, false, cx);
5347 }
5348
5349 if let Some(provider) = self.edit_prediction_provider() {
5350 provider.discard(cx);
5351 }
5352
5353 self.take_active_inline_completion(cx)
5354 }
5355
5356 fn report_inline_completion_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
5357 let Some(provider) = self.edit_prediction_provider() else {
5358 return;
5359 };
5360
5361 let Some((_, buffer, _)) = self
5362 .buffer
5363 .read(cx)
5364 .excerpt_containing(self.selections.newest_anchor().head(), cx)
5365 else {
5366 return;
5367 };
5368
5369 let extension = buffer
5370 .read(cx)
5371 .file()
5372 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
5373
5374 let event_type = match accepted {
5375 true => "Edit Prediction Accepted",
5376 false => "Edit Prediction Discarded",
5377 };
5378 telemetry::event!(
5379 event_type,
5380 provider = provider.name(),
5381 prediction_id = id,
5382 suggestion_accepted = accepted,
5383 file_extension = extension,
5384 );
5385 }
5386
5387 pub fn has_active_inline_completion(&self) -> bool {
5388 self.active_inline_completion.is_some()
5389 }
5390
5391 fn take_active_inline_completion(&mut self, cx: &mut Context<Self>) -> bool {
5392 let Some(active_inline_completion) = self.active_inline_completion.take() else {
5393 return false;
5394 };
5395
5396 self.splice_inlays(&active_inline_completion.inlay_ids, Default::default(), cx);
5397 self.clear_highlights::<InlineCompletionHighlight>(cx);
5398 self.stale_inline_completion_in_menu = Some(active_inline_completion);
5399 true
5400 }
5401
5402 /// Returns true when we're displaying the edit prediction popover below the cursor
5403 /// like we are not previewing and the LSP autocomplete menu is visible
5404 /// or we are in `when_holding_modifier` mode.
5405 pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
5406 if self.edit_prediction_preview.is_active()
5407 || !self.show_edit_predictions_in_menu()
5408 || !self.edit_predictions_enabled()
5409 {
5410 return false;
5411 }
5412
5413 if self.has_visible_completions_menu() {
5414 return true;
5415 }
5416
5417 has_completion && self.edit_prediction_requires_modifier()
5418 }
5419
5420 fn handle_modifiers_changed(
5421 &mut self,
5422 modifiers: Modifiers,
5423 position_map: &PositionMap,
5424 window: &mut Window,
5425 cx: &mut Context<Self>,
5426 ) {
5427 if self.show_edit_predictions_in_menu() {
5428 self.update_edit_prediction_preview(&modifiers, position_map, window, cx);
5429 }
5430
5431 let mouse_position = window.mouse_position();
5432 if !position_map.text_hitbox.is_hovered(window) {
5433 return;
5434 }
5435
5436 self.update_hovered_link(
5437 position_map.point_for_position(mouse_position),
5438 &position_map.snapshot,
5439 modifiers,
5440 window,
5441 cx,
5442 )
5443 }
5444
5445 fn update_edit_prediction_preview(
5446 &mut self,
5447 modifiers: &Modifiers,
5448 position_map: &PositionMap,
5449 window: &mut Window,
5450 cx: &mut Context<Self>,
5451 ) {
5452 let accept_keybind = self.accept_edit_prediction_keybind(window, cx);
5453 let Some(accept_keystroke) = accept_keybind.keystroke() else {
5454 return;
5455 };
5456
5457 if &accept_keystroke.modifiers == modifiers {
5458 let Some(completion) = self.active_inline_completion.as_ref() else {
5459 return;
5460 };
5461
5462 if !self.edit_prediction_requires_modifier() && !self.has_visible_completions_menu() {
5463 return;
5464 }
5465
5466 let transitioned = self.edit_prediction_preview.start(
5467 &completion.completion,
5468 &position_map.snapshot,
5469 self.selections
5470 .newest_anchor()
5471 .head()
5472 .to_display_point(&position_map.snapshot),
5473 );
5474
5475 if transitioned {
5476 self.request_autoscroll(Autoscroll::fit(), cx);
5477 self.update_visible_inline_completion(window, cx);
5478 cx.notify();
5479 }
5480 } else if self.edit_prediction_preview.end(
5481 self.selections
5482 .newest_anchor()
5483 .head()
5484 .to_display_point(&position_map.snapshot),
5485 position_map.scroll_pixel_position,
5486 window,
5487 cx,
5488 ) {
5489 self.update_visible_inline_completion(window, cx);
5490 cx.notify();
5491 }
5492 }
5493
5494 fn update_visible_inline_completion(
5495 &mut self,
5496 window: &mut Window,
5497 cx: &mut Context<Self>,
5498 ) -> Option<()> {
5499 let selection = self.selections.newest_anchor();
5500 let cursor = selection.head();
5501 let multibuffer = self.buffer.read(cx).snapshot(cx);
5502 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
5503 let excerpt_id = cursor.excerpt_id;
5504
5505 let show_in_menu = self.show_edit_predictions_in_menu();
5506 let completions_menu_has_precedence = !show_in_menu
5507 && (self.context_menu.borrow().is_some()
5508 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
5509
5510 if completions_menu_has_precedence
5511 || !offset_selection.is_empty()
5512 || self
5513 .active_inline_completion
5514 .as_ref()
5515 .map_or(false, |completion| {
5516 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
5517 let invalidation_range = invalidation_range.start..=invalidation_range.end;
5518 !invalidation_range.contains(&offset_selection.head())
5519 })
5520 {
5521 self.discard_inline_completion(false, cx);
5522 return None;
5523 }
5524
5525 self.take_active_inline_completion(cx);
5526 let Some(provider) = self.edit_prediction_provider() else {
5527 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5528 return None;
5529 };
5530
5531 let (buffer, cursor_buffer_position) =
5532 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5533
5534 self.edit_prediction_settings =
5535 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5536
5537 if !self.edit_prediction_settings.is_enabled() {
5538 self.discard_inline_completion(false, cx);
5539 return None;
5540 }
5541
5542 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
5543 let edits = inline_completion
5544 .edits
5545 .into_iter()
5546 .flat_map(|(range, new_text)| {
5547 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
5548 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
5549 Some((start..end, new_text))
5550 })
5551 .collect::<Vec<_>>();
5552 if edits.is_empty() {
5553 return None;
5554 }
5555
5556 let first_edit_start = edits.first().unwrap().0.start;
5557 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
5558 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
5559
5560 let last_edit_end = edits.last().unwrap().0.end;
5561 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
5562 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
5563
5564 let cursor_row = cursor.to_point(&multibuffer).row;
5565
5566 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
5567
5568 let mut inlay_ids = Vec::new();
5569 let invalidation_row_range;
5570 let move_invalidation_row_range = if cursor_row < edit_start_row {
5571 Some(cursor_row..edit_end_row)
5572 } else if cursor_row > edit_end_row {
5573 Some(edit_start_row..cursor_row)
5574 } else {
5575 None
5576 };
5577 let is_move =
5578 move_invalidation_row_range.is_some() || self.inline_completions_hidden_for_vim_mode;
5579 let completion = if is_move {
5580 invalidation_row_range =
5581 move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
5582 let target = first_edit_start;
5583 InlineCompletion::Move { target, snapshot }
5584 } else {
5585 let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
5586 && !self.inline_completions_hidden_for_vim_mode;
5587
5588 if show_completions_in_buffer {
5589 if edits
5590 .iter()
5591 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
5592 {
5593 let mut inlays = Vec::new();
5594 for (range, new_text) in &edits {
5595 let inlay = Inlay::inline_completion(
5596 post_inc(&mut self.next_inlay_id),
5597 range.start,
5598 new_text.as_str(),
5599 );
5600 inlay_ids.push(inlay.id);
5601 inlays.push(inlay);
5602 }
5603
5604 self.splice_inlays(&[], inlays, cx);
5605 } else {
5606 let background_color = cx.theme().status().deleted_background;
5607 self.highlight_text::<InlineCompletionHighlight>(
5608 edits.iter().map(|(range, _)| range.clone()).collect(),
5609 HighlightStyle {
5610 background_color: Some(background_color),
5611 ..Default::default()
5612 },
5613 cx,
5614 );
5615 }
5616 }
5617
5618 invalidation_row_range = edit_start_row..edit_end_row;
5619
5620 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
5621 if provider.show_tab_accept_marker() {
5622 EditDisplayMode::TabAccept
5623 } else {
5624 EditDisplayMode::Inline
5625 }
5626 } else {
5627 EditDisplayMode::DiffPopover
5628 };
5629
5630 InlineCompletion::Edit {
5631 edits,
5632 edit_preview: inline_completion.edit_preview,
5633 display_mode,
5634 snapshot,
5635 }
5636 };
5637
5638 let invalidation_range = multibuffer
5639 .anchor_before(Point::new(invalidation_row_range.start, 0))
5640 ..multibuffer.anchor_after(Point::new(
5641 invalidation_row_range.end,
5642 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
5643 ));
5644
5645 self.stale_inline_completion_in_menu = None;
5646 let editor_snapshot = self.snapshot(window, cx);
5647 if self.edit_prediction_preview.restart(
5648 &completion,
5649 &editor_snapshot,
5650 cursor.to_display_point(&editor_snapshot),
5651 ) {
5652 self.request_autoscroll(Autoscroll::fit(), cx);
5653 }
5654
5655 self.active_inline_completion = Some(InlineCompletionState {
5656 inlay_ids,
5657 completion,
5658 completion_id: inline_completion.id,
5659 invalidation_range,
5660 });
5661
5662 cx.notify();
5663
5664 Some(())
5665 }
5666
5667 pub fn edit_prediction_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5668 Some(self.edit_prediction_provider.as_ref()?.provider.clone())
5669 }
5670
5671 fn render_code_actions_indicator(
5672 &self,
5673 _style: &EditorStyle,
5674 row: DisplayRow,
5675 is_active: bool,
5676 cx: &mut Context<Self>,
5677 ) -> Option<IconButton> {
5678 if self.available_code_actions.is_some() {
5679 Some(
5680 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5681 .shape(ui::IconButtonShape::Square)
5682 .icon_size(IconSize::XSmall)
5683 .icon_color(Color::Muted)
5684 .toggle_state(is_active)
5685 .tooltip({
5686 let focus_handle = self.focus_handle.clone();
5687 move |window, cx| {
5688 Tooltip::for_action_in(
5689 "Toggle Code Actions",
5690 &ToggleCodeActions {
5691 deployed_from_indicator: None,
5692 },
5693 &focus_handle,
5694 window,
5695 cx,
5696 )
5697 }
5698 })
5699 .on_click(cx.listener(move |editor, _e, window, cx| {
5700 window.focus(&editor.focus_handle(cx));
5701 editor.toggle_code_actions(
5702 &ToggleCodeActions {
5703 deployed_from_indicator: Some(row),
5704 },
5705 window,
5706 cx,
5707 );
5708 })),
5709 )
5710 } else {
5711 None
5712 }
5713 }
5714
5715 fn clear_tasks(&mut self) {
5716 self.tasks.clear()
5717 }
5718
5719 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5720 if self.tasks.insert(key, value).is_some() {
5721 // This case should hopefully be rare, but just in case...
5722 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5723 }
5724 }
5725
5726 fn build_tasks_context(
5727 project: &Entity<Project>,
5728 buffer: &Entity<Buffer>,
5729 buffer_row: u32,
5730 tasks: &Arc<RunnableTasks>,
5731 cx: &mut Context<Self>,
5732 ) -> Task<Option<task::TaskContext>> {
5733 let position = Point::new(buffer_row, tasks.column);
5734 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
5735 let location = Location {
5736 buffer: buffer.clone(),
5737 range: range_start..range_start,
5738 };
5739 // Fill in the environmental variables from the tree-sitter captures
5740 let mut captured_task_variables = TaskVariables::default();
5741 for (capture_name, value) in tasks.extra_variables.clone() {
5742 captured_task_variables.insert(
5743 task::VariableName::Custom(capture_name.into()),
5744 value.clone(),
5745 );
5746 }
5747 project.update(cx, |project, cx| {
5748 project.task_store().update(cx, |task_store, cx| {
5749 task_store.task_context_for_location(captured_task_variables, location, cx)
5750 })
5751 })
5752 }
5753
5754 pub fn spawn_nearest_task(
5755 &mut self,
5756 action: &SpawnNearestTask,
5757 window: &mut Window,
5758 cx: &mut Context<Self>,
5759 ) {
5760 let Some((workspace, _)) = self.workspace.clone() else {
5761 return;
5762 };
5763 let Some(project) = self.project.clone() else {
5764 return;
5765 };
5766
5767 // Try to find a closest, enclosing node using tree-sitter that has a
5768 // task
5769 let Some((buffer, buffer_row, tasks)) = self
5770 .find_enclosing_node_task(cx)
5771 // Or find the task that's closest in row-distance.
5772 .or_else(|| self.find_closest_task(cx))
5773 else {
5774 return;
5775 };
5776
5777 let reveal_strategy = action.reveal;
5778 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
5779 cx.spawn_in(window, |_, mut cx| async move {
5780 let context = task_context.await?;
5781 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
5782
5783 let resolved = resolved_task.resolved.as_mut()?;
5784 resolved.reveal = reveal_strategy;
5785
5786 workspace
5787 .update(&mut cx, |workspace, cx| {
5788 workspace::tasks::schedule_resolved_task(
5789 workspace,
5790 task_source_kind,
5791 resolved_task,
5792 false,
5793 cx,
5794 );
5795 })
5796 .ok()
5797 })
5798 .detach();
5799 }
5800
5801 fn find_closest_task(
5802 &mut self,
5803 cx: &mut Context<Self>,
5804 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5805 let cursor_row = self.selections.newest_adjusted(cx).head().row;
5806
5807 let ((buffer_id, row), tasks) = self
5808 .tasks
5809 .iter()
5810 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
5811
5812 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
5813 let tasks = Arc::new(tasks.to_owned());
5814 Some((buffer, *row, tasks))
5815 }
5816
5817 fn find_enclosing_node_task(
5818 &mut self,
5819 cx: &mut Context<Self>,
5820 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5821 let snapshot = self.buffer.read(cx).snapshot(cx);
5822 let offset = self.selections.newest::<usize>(cx).head();
5823 let excerpt = snapshot.excerpt_containing(offset..offset)?;
5824 let buffer_id = excerpt.buffer().remote_id();
5825
5826 let layer = excerpt.buffer().syntax_layer_at(offset)?;
5827 let mut cursor = layer.node().walk();
5828
5829 while cursor.goto_first_child_for_byte(offset).is_some() {
5830 if cursor.node().end_byte() == offset {
5831 cursor.goto_next_sibling();
5832 }
5833 }
5834
5835 // Ascend to the smallest ancestor that contains the range and has a task.
5836 loop {
5837 let node = cursor.node();
5838 let node_range = node.byte_range();
5839 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
5840
5841 // Check if this node contains our offset
5842 if node_range.start <= offset && node_range.end >= offset {
5843 // If it contains offset, check for task
5844 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
5845 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
5846 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
5847 }
5848 }
5849
5850 if !cursor.goto_parent() {
5851 break;
5852 }
5853 }
5854 None
5855 }
5856
5857 fn render_run_indicator(
5858 &self,
5859 _style: &EditorStyle,
5860 is_active: bool,
5861 row: DisplayRow,
5862 cx: &mut Context<Self>,
5863 ) -> IconButton {
5864 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5865 .shape(ui::IconButtonShape::Square)
5866 .icon_size(IconSize::XSmall)
5867 .icon_color(Color::Muted)
5868 .toggle_state(is_active)
5869 .on_click(cx.listener(move |editor, _e, window, cx| {
5870 window.focus(&editor.focus_handle(cx));
5871 editor.toggle_code_actions(
5872 &ToggleCodeActions {
5873 deployed_from_indicator: Some(row),
5874 },
5875 window,
5876 cx,
5877 );
5878 }))
5879 }
5880
5881 pub fn context_menu_visible(&self) -> bool {
5882 !self.edit_prediction_preview.is_active()
5883 && self
5884 .context_menu
5885 .borrow()
5886 .as_ref()
5887 .map_or(false, |menu| menu.visible())
5888 }
5889
5890 fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
5891 self.context_menu
5892 .borrow()
5893 .as_ref()
5894 .map(|menu| menu.origin())
5895 }
5896
5897 fn edit_prediction_cursor_popover_height(&self) -> Pixels {
5898 px(30.)
5899 }
5900
5901 fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
5902 if self.read_only(cx) {
5903 cx.theme().players().read_only()
5904 } else {
5905 self.style.as_ref().unwrap().local_player
5906 }
5907 }
5908
5909 #[allow(clippy::too_many_arguments)]
5910 fn render_edit_prediction_cursor_popover(
5911 &self,
5912 min_width: Pixels,
5913 max_width: Pixels,
5914 cursor_point: Point,
5915 style: &EditorStyle,
5916 accept_keystroke: &gpui::Keystroke,
5917 _window: &Window,
5918 cx: &mut Context<Editor>,
5919 ) -> Option<AnyElement> {
5920 let provider = self.edit_prediction_provider.as_ref()?;
5921
5922 if provider.provider.needs_terms_acceptance(cx) {
5923 return Some(
5924 h_flex()
5925 .min_w(min_width)
5926 .flex_1()
5927 .px_2()
5928 .py_1()
5929 .gap_3()
5930 .elevation_2(cx)
5931 .hover(|style| style.bg(cx.theme().colors().element_hover))
5932 .id("accept-terms")
5933 .cursor_pointer()
5934 .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
5935 .on_click(cx.listener(|this, _event, window, cx| {
5936 cx.stop_propagation();
5937 this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx);
5938 window.dispatch_action(
5939 zed_actions::OpenZedPredictOnboarding.boxed_clone(),
5940 cx,
5941 );
5942 }))
5943 .child(
5944 h_flex()
5945 .flex_1()
5946 .gap_2()
5947 .child(Icon::new(IconName::ZedPredict))
5948 .child(Label::new("Accept Terms of Service"))
5949 .child(div().w_full())
5950 .child(
5951 Icon::new(IconName::ArrowUpRight)
5952 .color(Color::Muted)
5953 .size(IconSize::Small),
5954 )
5955 .into_any_element(),
5956 )
5957 .into_any(),
5958 );
5959 }
5960
5961 let is_refreshing = provider.provider.is_refreshing(cx);
5962
5963 fn pending_completion_container() -> Div {
5964 h_flex()
5965 .h_full()
5966 .flex_1()
5967 .gap_2()
5968 .child(Icon::new(IconName::ZedPredict))
5969 }
5970
5971 let completion = match &self.active_inline_completion {
5972 Some(completion) => match &completion.completion {
5973 InlineCompletion::Move {
5974 target, snapshot, ..
5975 } if !self.has_visible_completions_menu() => {
5976 use text::ToPoint as _;
5977
5978 return Some(
5979 h_flex()
5980 .px_2()
5981 .py_1()
5982 .elevation_2(cx)
5983 .border_color(cx.theme().colors().border)
5984 .rounded_tl(px(0.))
5985 .gap_2()
5986 .child(
5987 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
5988 Icon::new(IconName::ZedPredictDown)
5989 } else {
5990 Icon::new(IconName::ZedPredictUp)
5991 },
5992 )
5993 .child(Label::new("Hold"))
5994 .children(ui::render_modifiers(
5995 &accept_keystroke.modifiers,
5996 PlatformStyle::platform(),
5997 Some(Color::Default),
5998 None,
5999 true,
6000 ))
6001 .into_any(),
6002 );
6003 }
6004 _ => self.render_edit_prediction_cursor_popover_preview(
6005 completion,
6006 cursor_point,
6007 style,
6008 cx,
6009 )?,
6010 },
6011
6012 None if is_refreshing => match &self.stale_inline_completion_in_menu {
6013 Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
6014 stale_completion,
6015 cursor_point,
6016 style,
6017 cx,
6018 )?,
6019
6020 None => {
6021 pending_completion_container().child(Label::new("...").size(LabelSize::Small))
6022 }
6023 },
6024
6025 None => pending_completion_container().child(Label::new("No Prediction")),
6026 };
6027
6028 let completion = if is_refreshing {
6029 completion
6030 .with_animation(
6031 "loading-completion",
6032 Animation::new(Duration::from_secs(2))
6033 .repeat()
6034 .with_easing(pulsating_between(0.4, 0.8)),
6035 |label, delta| label.opacity(delta),
6036 )
6037 .into_any_element()
6038 } else {
6039 completion.into_any_element()
6040 };
6041
6042 let has_completion = self.active_inline_completion.is_some();
6043
6044 Some(
6045 h_flex()
6046 .min_w(min_width)
6047 .max_w(max_width)
6048 .flex_1()
6049 .px_2()
6050 .py_1()
6051 .elevation_2(cx)
6052 .border_color(cx.theme().colors().border)
6053 .child(completion)
6054 .child(ui::Divider::vertical())
6055 .child(
6056 h_flex()
6057 .h_full()
6058 .gap_1()
6059 .pl_2()
6060 .child(
6061 h_flex()
6062 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6063 .gap_1()
6064 .children(ui::render_modifiers(
6065 &accept_keystroke.modifiers,
6066 PlatformStyle::platform(),
6067 Some(if !has_completion {
6068 Color::Muted
6069 } else {
6070 Color::Default
6071 }),
6072 None,
6073 true,
6074 )),
6075 )
6076 .child(Label::new("Preview").into_any_element())
6077 .opacity(if has_completion { 1.0 } else { 0.4 }),
6078 )
6079 .into_any(),
6080 )
6081 }
6082
6083 fn render_edit_prediction_cursor_popover_preview(
6084 &self,
6085 completion: &InlineCompletionState,
6086 cursor_point: Point,
6087 style: &EditorStyle,
6088 cx: &mut Context<Editor>,
6089 ) -> Option<Div> {
6090 use text::ToPoint as _;
6091
6092 fn render_relative_row_jump(
6093 prefix: impl Into<String>,
6094 current_row: u32,
6095 target_row: u32,
6096 ) -> Div {
6097 let (row_diff, arrow) = if target_row < current_row {
6098 (current_row - target_row, IconName::ArrowUp)
6099 } else {
6100 (target_row - current_row, IconName::ArrowDown)
6101 };
6102
6103 h_flex()
6104 .child(
6105 Label::new(format!("{}{}", prefix.into(), row_diff))
6106 .color(Color::Muted)
6107 .size(LabelSize::Small),
6108 )
6109 .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
6110 }
6111
6112 match &completion.completion {
6113 InlineCompletion::Move {
6114 target, snapshot, ..
6115 } => Some(
6116 h_flex()
6117 .px_2()
6118 .gap_2()
6119 .flex_1()
6120 .child(
6121 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
6122 Icon::new(IconName::ZedPredictDown)
6123 } else {
6124 Icon::new(IconName::ZedPredictUp)
6125 },
6126 )
6127 .child(Label::new("Jump to Edit")),
6128 ),
6129
6130 InlineCompletion::Edit {
6131 edits,
6132 edit_preview,
6133 snapshot,
6134 display_mode: _,
6135 } => {
6136 let first_edit_row = edits.first()?.0.start.text_anchor.to_point(&snapshot).row;
6137
6138 let highlighted_edits = crate::inline_completion_edit_text(
6139 &snapshot,
6140 &edits,
6141 edit_preview.as_ref()?,
6142 true,
6143 cx,
6144 );
6145
6146 let len_total = highlighted_edits.text.len();
6147 let first_line = &highlighted_edits.text
6148 [..highlighted_edits.text.find('\n').unwrap_or(len_total)];
6149 let first_line_len = first_line.len();
6150
6151 let first_highlight_start = highlighted_edits
6152 .highlights
6153 .first()
6154 .map_or(0, |(range, _)| range.start);
6155 let drop_prefix_len = first_line
6156 .char_indices()
6157 .find(|(_, c)| !c.is_whitespace())
6158 .map_or(first_highlight_start, |(ix, _)| {
6159 ix.min(first_highlight_start)
6160 });
6161
6162 let preview_text = &first_line[drop_prefix_len..];
6163 let preview_len = preview_text.len();
6164 let highlights = highlighted_edits
6165 .highlights
6166 .into_iter()
6167 .take_until(|(range, _)| range.start > first_line_len)
6168 .map(|(range, style)| {
6169 (
6170 range.start - drop_prefix_len
6171 ..(range.end - drop_prefix_len).min(preview_len),
6172 style,
6173 )
6174 });
6175
6176 let styled_text = gpui::StyledText::new(SharedString::new(preview_text))
6177 .with_highlights(&style.text, highlights);
6178
6179 let preview = h_flex()
6180 .gap_1()
6181 .min_w_16()
6182 .child(styled_text)
6183 .when(len_total > first_line_len, |parent| parent.child("…"));
6184
6185 let left = if first_edit_row != cursor_point.row {
6186 render_relative_row_jump("", cursor_point.row, first_edit_row)
6187 .into_any_element()
6188 } else {
6189 Icon::new(IconName::ZedPredict).into_any_element()
6190 };
6191
6192 Some(
6193 h_flex()
6194 .h_full()
6195 .flex_1()
6196 .gap_2()
6197 .pr_1()
6198 .overflow_x_hidden()
6199 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6200 .child(left)
6201 .child(preview),
6202 )
6203 }
6204 }
6205 }
6206
6207 fn render_context_menu(
6208 &self,
6209 style: &EditorStyle,
6210 max_height_in_lines: u32,
6211 y_flipped: bool,
6212 window: &mut Window,
6213 cx: &mut Context<Editor>,
6214 ) -> Option<AnyElement> {
6215 let menu = self.context_menu.borrow();
6216 let menu = menu.as_ref()?;
6217 if !menu.visible() {
6218 return None;
6219 };
6220 Some(menu.render(style, max_height_in_lines, y_flipped, window, cx))
6221 }
6222
6223 fn render_context_menu_aside(
6224 &self,
6225 style: &EditorStyle,
6226 max_size: Size<Pixels>,
6227 cx: &mut Context<Editor>,
6228 ) -> Option<AnyElement> {
6229 self.context_menu.borrow().as_ref().and_then(|menu| {
6230 if menu.visible() {
6231 menu.render_aside(
6232 style,
6233 max_size,
6234 self.workspace.as_ref().map(|(w, _)| w.clone()),
6235 cx,
6236 )
6237 } else {
6238 None
6239 }
6240 })
6241 }
6242
6243 fn hide_context_menu(
6244 &mut self,
6245 window: &mut Window,
6246 cx: &mut Context<Self>,
6247 ) -> Option<CodeContextMenu> {
6248 cx.notify();
6249 self.completion_tasks.clear();
6250 let context_menu = self.context_menu.borrow_mut().take();
6251 self.stale_inline_completion_in_menu.take();
6252 self.update_visible_inline_completion(window, cx);
6253 context_menu
6254 }
6255
6256 fn show_snippet_choices(
6257 &mut self,
6258 choices: &Vec<String>,
6259 selection: Range<Anchor>,
6260 cx: &mut Context<Self>,
6261 ) {
6262 if selection.start.buffer_id.is_none() {
6263 return;
6264 }
6265 let buffer_id = selection.start.buffer_id.unwrap();
6266 let buffer = self.buffer().read(cx).buffer(buffer_id);
6267 let id = post_inc(&mut self.next_completion_id);
6268
6269 if let Some(buffer) = buffer {
6270 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
6271 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
6272 ));
6273 }
6274 }
6275
6276 pub fn insert_snippet(
6277 &mut self,
6278 insertion_ranges: &[Range<usize>],
6279 snippet: Snippet,
6280 window: &mut Window,
6281 cx: &mut Context<Self>,
6282 ) -> Result<()> {
6283 struct Tabstop<T> {
6284 is_end_tabstop: bool,
6285 ranges: Vec<Range<T>>,
6286 choices: Option<Vec<String>>,
6287 }
6288
6289 let tabstops = self.buffer.update(cx, |buffer, cx| {
6290 let snippet_text: Arc<str> = snippet.text.clone().into();
6291 buffer.edit(
6292 insertion_ranges
6293 .iter()
6294 .cloned()
6295 .map(|range| (range, snippet_text.clone())),
6296 Some(AutoindentMode::EachLine),
6297 cx,
6298 );
6299
6300 let snapshot = &*buffer.read(cx);
6301 let snippet = &snippet;
6302 snippet
6303 .tabstops
6304 .iter()
6305 .map(|tabstop| {
6306 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
6307 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
6308 });
6309 let mut tabstop_ranges = tabstop
6310 .ranges
6311 .iter()
6312 .flat_map(|tabstop_range| {
6313 let mut delta = 0_isize;
6314 insertion_ranges.iter().map(move |insertion_range| {
6315 let insertion_start = insertion_range.start as isize + delta;
6316 delta +=
6317 snippet.text.len() as isize - insertion_range.len() as isize;
6318
6319 let start = ((insertion_start + tabstop_range.start) as usize)
6320 .min(snapshot.len());
6321 let end = ((insertion_start + tabstop_range.end) as usize)
6322 .min(snapshot.len());
6323 snapshot.anchor_before(start)..snapshot.anchor_after(end)
6324 })
6325 })
6326 .collect::<Vec<_>>();
6327 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
6328
6329 Tabstop {
6330 is_end_tabstop,
6331 ranges: tabstop_ranges,
6332 choices: tabstop.choices.clone(),
6333 }
6334 })
6335 .collect::<Vec<_>>()
6336 });
6337 if let Some(tabstop) = tabstops.first() {
6338 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6339 s.select_ranges(tabstop.ranges.iter().cloned());
6340 });
6341
6342 if let Some(choices) = &tabstop.choices {
6343 if let Some(selection) = tabstop.ranges.first() {
6344 self.show_snippet_choices(choices, selection.clone(), cx)
6345 }
6346 }
6347
6348 // If we're already at the last tabstop and it's at the end of the snippet,
6349 // we're done, we don't need to keep the state around.
6350 if !tabstop.is_end_tabstop {
6351 let choices = tabstops
6352 .iter()
6353 .map(|tabstop| tabstop.choices.clone())
6354 .collect();
6355
6356 let ranges = tabstops
6357 .into_iter()
6358 .map(|tabstop| tabstop.ranges)
6359 .collect::<Vec<_>>();
6360
6361 self.snippet_stack.push(SnippetState {
6362 active_index: 0,
6363 ranges,
6364 choices,
6365 });
6366 }
6367
6368 // Check whether the just-entered snippet ends with an auto-closable bracket.
6369 if self.autoclose_regions.is_empty() {
6370 let snapshot = self.buffer.read(cx).snapshot(cx);
6371 for selection in &mut self.selections.all::<Point>(cx) {
6372 let selection_head = selection.head();
6373 let Some(scope) = snapshot.language_scope_at(selection_head) else {
6374 continue;
6375 };
6376
6377 let mut bracket_pair = None;
6378 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
6379 let prev_chars = snapshot
6380 .reversed_chars_at(selection_head)
6381 .collect::<String>();
6382 for (pair, enabled) in scope.brackets() {
6383 if enabled
6384 && pair.close
6385 && prev_chars.starts_with(pair.start.as_str())
6386 && next_chars.starts_with(pair.end.as_str())
6387 {
6388 bracket_pair = Some(pair.clone());
6389 break;
6390 }
6391 }
6392 if let Some(pair) = bracket_pair {
6393 let start = snapshot.anchor_after(selection_head);
6394 let end = snapshot.anchor_after(selection_head);
6395 self.autoclose_regions.push(AutocloseRegion {
6396 selection_id: selection.id,
6397 range: start..end,
6398 pair,
6399 });
6400 }
6401 }
6402 }
6403 }
6404 Ok(())
6405 }
6406
6407 pub fn move_to_next_snippet_tabstop(
6408 &mut self,
6409 window: &mut Window,
6410 cx: &mut Context<Self>,
6411 ) -> bool {
6412 self.move_to_snippet_tabstop(Bias::Right, window, cx)
6413 }
6414
6415 pub fn move_to_prev_snippet_tabstop(
6416 &mut self,
6417 window: &mut Window,
6418 cx: &mut Context<Self>,
6419 ) -> bool {
6420 self.move_to_snippet_tabstop(Bias::Left, window, cx)
6421 }
6422
6423 pub fn move_to_snippet_tabstop(
6424 &mut self,
6425 bias: Bias,
6426 window: &mut Window,
6427 cx: &mut Context<Self>,
6428 ) -> bool {
6429 if let Some(mut snippet) = self.snippet_stack.pop() {
6430 match bias {
6431 Bias::Left => {
6432 if snippet.active_index > 0 {
6433 snippet.active_index -= 1;
6434 } else {
6435 self.snippet_stack.push(snippet);
6436 return false;
6437 }
6438 }
6439 Bias::Right => {
6440 if snippet.active_index + 1 < snippet.ranges.len() {
6441 snippet.active_index += 1;
6442 } else {
6443 self.snippet_stack.push(snippet);
6444 return false;
6445 }
6446 }
6447 }
6448 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
6449 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6450 s.select_anchor_ranges(current_ranges.iter().cloned())
6451 });
6452
6453 if let Some(choices) = &snippet.choices[snippet.active_index] {
6454 if let Some(selection) = current_ranges.first() {
6455 self.show_snippet_choices(&choices, selection.clone(), cx);
6456 }
6457 }
6458
6459 // If snippet state is not at the last tabstop, push it back on the stack
6460 if snippet.active_index + 1 < snippet.ranges.len() {
6461 self.snippet_stack.push(snippet);
6462 }
6463 return true;
6464 }
6465 }
6466
6467 false
6468 }
6469
6470 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
6471 self.transact(window, cx, |this, window, cx| {
6472 this.select_all(&SelectAll, window, cx);
6473 this.insert("", window, cx);
6474 });
6475 }
6476
6477 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
6478 self.transact(window, cx, |this, window, cx| {
6479 this.select_autoclose_pair(window, cx);
6480 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
6481 if !this.linked_edit_ranges.is_empty() {
6482 let selections = this.selections.all::<MultiBufferPoint>(cx);
6483 let snapshot = this.buffer.read(cx).snapshot(cx);
6484
6485 for selection in selections.iter() {
6486 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
6487 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
6488 if selection_start.buffer_id != selection_end.buffer_id {
6489 continue;
6490 }
6491 if let Some(ranges) =
6492 this.linked_editing_ranges_for(selection_start..selection_end, cx)
6493 {
6494 for (buffer, entries) in ranges {
6495 linked_ranges.entry(buffer).or_default().extend(entries);
6496 }
6497 }
6498 }
6499 }
6500
6501 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
6502 if !this.selections.line_mode {
6503 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
6504 for selection in &mut selections {
6505 if selection.is_empty() {
6506 let old_head = selection.head();
6507 let mut new_head =
6508 movement::left(&display_map, old_head.to_display_point(&display_map))
6509 .to_point(&display_map);
6510 if let Some((buffer, line_buffer_range)) = display_map
6511 .buffer_snapshot
6512 .buffer_line_for_row(MultiBufferRow(old_head.row))
6513 {
6514 let indent_size =
6515 buffer.indent_size_for_line(line_buffer_range.start.row);
6516 let indent_len = match indent_size.kind {
6517 IndentKind::Space => {
6518 buffer.settings_at(line_buffer_range.start, cx).tab_size
6519 }
6520 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
6521 };
6522 if old_head.column <= indent_size.len && old_head.column > 0 {
6523 let indent_len = indent_len.get();
6524 new_head = cmp::min(
6525 new_head,
6526 MultiBufferPoint::new(
6527 old_head.row,
6528 ((old_head.column - 1) / indent_len) * indent_len,
6529 ),
6530 );
6531 }
6532 }
6533
6534 selection.set_head(new_head, SelectionGoal::None);
6535 }
6536 }
6537 }
6538
6539 this.signature_help_state.set_backspace_pressed(true);
6540 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6541 s.select(selections)
6542 });
6543 this.insert("", window, cx);
6544 let empty_str: Arc<str> = Arc::from("");
6545 for (buffer, edits) in linked_ranges {
6546 let snapshot = buffer.read(cx).snapshot();
6547 use text::ToPoint as TP;
6548
6549 let edits = edits
6550 .into_iter()
6551 .map(|range| {
6552 let end_point = TP::to_point(&range.end, &snapshot);
6553 let mut start_point = TP::to_point(&range.start, &snapshot);
6554
6555 if end_point == start_point {
6556 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
6557 .saturating_sub(1);
6558 start_point =
6559 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
6560 };
6561
6562 (start_point..end_point, empty_str.clone())
6563 })
6564 .sorted_by_key(|(range, _)| range.start)
6565 .collect::<Vec<_>>();
6566 buffer.update(cx, |this, cx| {
6567 this.edit(edits, None, cx);
6568 })
6569 }
6570 this.refresh_inline_completion(true, false, window, cx);
6571 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
6572 });
6573 }
6574
6575 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
6576 self.transact(window, cx, |this, window, cx| {
6577 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6578 let line_mode = s.line_mode;
6579 s.move_with(|map, selection| {
6580 if selection.is_empty() && !line_mode {
6581 let cursor = movement::right(map, selection.head());
6582 selection.end = cursor;
6583 selection.reversed = true;
6584 selection.goal = SelectionGoal::None;
6585 }
6586 })
6587 });
6588 this.insert("", window, cx);
6589 this.refresh_inline_completion(true, false, window, cx);
6590 });
6591 }
6592
6593 pub fn tab_prev(&mut self, _: &TabPrev, window: &mut Window, cx: &mut Context<Self>) {
6594 if self.move_to_prev_snippet_tabstop(window, cx) {
6595 return;
6596 }
6597
6598 self.outdent(&Outdent, window, cx);
6599 }
6600
6601 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
6602 if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) {
6603 return;
6604 }
6605
6606 let mut selections = self.selections.all_adjusted(cx);
6607 let buffer = self.buffer.read(cx);
6608 let snapshot = buffer.snapshot(cx);
6609 let rows_iter = selections.iter().map(|s| s.head().row);
6610 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
6611
6612 let mut edits = Vec::new();
6613 let mut prev_edited_row = 0;
6614 let mut row_delta = 0;
6615 for selection in &mut selections {
6616 if selection.start.row != prev_edited_row {
6617 row_delta = 0;
6618 }
6619 prev_edited_row = selection.end.row;
6620
6621 // If the selection is non-empty, then increase the indentation of the selected lines.
6622 if !selection.is_empty() {
6623 row_delta =
6624 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
6625 continue;
6626 }
6627
6628 // If the selection is empty and the cursor is in the leading whitespace before the
6629 // suggested indentation, then auto-indent the line.
6630 let cursor = selection.head();
6631 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
6632 if let Some(suggested_indent) =
6633 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
6634 {
6635 if cursor.column < suggested_indent.len
6636 && cursor.column <= current_indent.len
6637 && current_indent.len <= suggested_indent.len
6638 {
6639 selection.start = Point::new(cursor.row, suggested_indent.len);
6640 selection.end = selection.start;
6641 if row_delta == 0 {
6642 edits.extend(Buffer::edit_for_indent_size_adjustment(
6643 cursor.row,
6644 current_indent,
6645 suggested_indent,
6646 ));
6647 row_delta = suggested_indent.len - current_indent.len;
6648 }
6649 continue;
6650 }
6651 }
6652
6653 // Otherwise, insert a hard or soft tab.
6654 let settings = buffer.settings_at(cursor, cx);
6655 let tab_size = if settings.hard_tabs {
6656 IndentSize::tab()
6657 } else {
6658 let tab_size = settings.tab_size.get();
6659 let char_column = snapshot
6660 .text_for_range(Point::new(cursor.row, 0)..cursor)
6661 .flat_map(str::chars)
6662 .count()
6663 + row_delta as usize;
6664 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
6665 IndentSize::spaces(chars_to_next_tab_stop)
6666 };
6667 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
6668 selection.end = selection.start;
6669 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
6670 row_delta += tab_size.len;
6671 }
6672
6673 self.transact(window, cx, |this, window, cx| {
6674 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
6675 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6676 s.select(selections)
6677 });
6678 this.refresh_inline_completion(true, false, window, cx);
6679 });
6680 }
6681
6682 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
6683 if self.read_only(cx) {
6684 return;
6685 }
6686 let mut selections = self.selections.all::<Point>(cx);
6687 let mut prev_edited_row = 0;
6688 let mut row_delta = 0;
6689 let mut edits = Vec::new();
6690 let buffer = self.buffer.read(cx);
6691 let snapshot = buffer.snapshot(cx);
6692 for selection in &mut selections {
6693 if selection.start.row != prev_edited_row {
6694 row_delta = 0;
6695 }
6696 prev_edited_row = selection.end.row;
6697
6698 row_delta =
6699 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
6700 }
6701
6702 self.transact(window, cx, |this, window, cx| {
6703 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
6704 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6705 s.select(selections)
6706 });
6707 });
6708 }
6709
6710 fn indent_selection(
6711 buffer: &MultiBuffer,
6712 snapshot: &MultiBufferSnapshot,
6713 selection: &mut Selection<Point>,
6714 edits: &mut Vec<(Range<Point>, String)>,
6715 delta_for_start_row: u32,
6716 cx: &App,
6717 ) -> u32 {
6718 let settings = buffer.settings_at(selection.start, cx);
6719 let tab_size = settings.tab_size.get();
6720 let indent_kind = if settings.hard_tabs {
6721 IndentKind::Tab
6722 } else {
6723 IndentKind::Space
6724 };
6725 let mut start_row = selection.start.row;
6726 let mut end_row = selection.end.row + 1;
6727
6728 // If a selection ends at the beginning of a line, don't indent
6729 // that last line.
6730 if selection.end.column == 0 && selection.end.row > selection.start.row {
6731 end_row -= 1;
6732 }
6733
6734 // Avoid re-indenting a row that has already been indented by a
6735 // previous selection, but still update this selection's column
6736 // to reflect that indentation.
6737 if delta_for_start_row > 0 {
6738 start_row += 1;
6739 selection.start.column += delta_for_start_row;
6740 if selection.end.row == selection.start.row {
6741 selection.end.column += delta_for_start_row;
6742 }
6743 }
6744
6745 let mut delta_for_end_row = 0;
6746 let has_multiple_rows = start_row + 1 != end_row;
6747 for row in start_row..end_row {
6748 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
6749 let indent_delta = match (current_indent.kind, indent_kind) {
6750 (IndentKind::Space, IndentKind::Space) => {
6751 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
6752 IndentSize::spaces(columns_to_next_tab_stop)
6753 }
6754 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
6755 (_, IndentKind::Tab) => IndentSize::tab(),
6756 };
6757
6758 let start = if has_multiple_rows || current_indent.len < selection.start.column {
6759 0
6760 } else {
6761 selection.start.column
6762 };
6763 let row_start = Point::new(row, start);
6764 edits.push((
6765 row_start..row_start,
6766 indent_delta.chars().collect::<String>(),
6767 ));
6768
6769 // Update this selection's endpoints to reflect the indentation.
6770 if row == selection.start.row {
6771 selection.start.column += indent_delta.len;
6772 }
6773 if row == selection.end.row {
6774 selection.end.column += indent_delta.len;
6775 delta_for_end_row = indent_delta.len;
6776 }
6777 }
6778
6779 if selection.start.row == selection.end.row {
6780 delta_for_start_row + delta_for_end_row
6781 } else {
6782 delta_for_end_row
6783 }
6784 }
6785
6786 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
6787 if self.read_only(cx) {
6788 return;
6789 }
6790 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6791 let selections = self.selections.all::<Point>(cx);
6792 let mut deletion_ranges = Vec::new();
6793 let mut last_outdent = None;
6794 {
6795 let buffer = self.buffer.read(cx);
6796 let snapshot = buffer.snapshot(cx);
6797 for selection in &selections {
6798 let settings = buffer.settings_at(selection.start, cx);
6799 let tab_size = settings.tab_size.get();
6800 let mut rows = selection.spanned_rows(false, &display_map);
6801
6802 // Avoid re-outdenting a row that has already been outdented by a
6803 // previous selection.
6804 if let Some(last_row) = last_outdent {
6805 if last_row == rows.start {
6806 rows.start = rows.start.next_row();
6807 }
6808 }
6809 let has_multiple_rows = rows.len() > 1;
6810 for row in rows.iter_rows() {
6811 let indent_size = snapshot.indent_size_for_line(row);
6812 if indent_size.len > 0 {
6813 let deletion_len = match indent_size.kind {
6814 IndentKind::Space => {
6815 let columns_to_prev_tab_stop = indent_size.len % tab_size;
6816 if columns_to_prev_tab_stop == 0 {
6817 tab_size
6818 } else {
6819 columns_to_prev_tab_stop
6820 }
6821 }
6822 IndentKind::Tab => 1,
6823 };
6824 let start = if has_multiple_rows
6825 || deletion_len > selection.start.column
6826 || indent_size.len < selection.start.column
6827 {
6828 0
6829 } else {
6830 selection.start.column - deletion_len
6831 };
6832 deletion_ranges.push(
6833 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
6834 );
6835 last_outdent = Some(row);
6836 }
6837 }
6838 }
6839 }
6840
6841 self.transact(window, cx, |this, window, cx| {
6842 this.buffer.update(cx, |buffer, cx| {
6843 let empty_str: Arc<str> = Arc::default();
6844 buffer.edit(
6845 deletion_ranges
6846 .into_iter()
6847 .map(|range| (range, empty_str.clone())),
6848 None,
6849 cx,
6850 );
6851 });
6852 let selections = this.selections.all::<usize>(cx);
6853 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6854 s.select(selections)
6855 });
6856 });
6857 }
6858
6859 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
6860 if self.read_only(cx) {
6861 return;
6862 }
6863 let selections = self
6864 .selections
6865 .all::<usize>(cx)
6866 .into_iter()
6867 .map(|s| s.range());
6868
6869 self.transact(window, cx, |this, window, cx| {
6870 this.buffer.update(cx, |buffer, cx| {
6871 buffer.autoindent_ranges(selections, cx);
6872 });
6873 let selections = this.selections.all::<usize>(cx);
6874 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6875 s.select(selections)
6876 });
6877 });
6878 }
6879
6880 pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
6881 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6882 let selections = self.selections.all::<Point>(cx);
6883
6884 let mut new_cursors = Vec::new();
6885 let mut edit_ranges = Vec::new();
6886 let mut selections = selections.iter().peekable();
6887 while let Some(selection) = selections.next() {
6888 let mut rows = selection.spanned_rows(false, &display_map);
6889 let goal_display_column = selection.head().to_display_point(&display_map).column();
6890
6891 // Accumulate contiguous regions of rows that we want to delete.
6892 while let Some(next_selection) = selections.peek() {
6893 let next_rows = next_selection.spanned_rows(false, &display_map);
6894 if next_rows.start <= rows.end {
6895 rows.end = next_rows.end;
6896 selections.next().unwrap();
6897 } else {
6898 break;
6899 }
6900 }
6901
6902 let buffer = &display_map.buffer_snapshot;
6903 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
6904 let edit_end;
6905 let cursor_buffer_row;
6906 if buffer.max_point().row >= rows.end.0 {
6907 // If there's a line after the range, delete the \n from the end of the row range
6908 // and position the cursor on the next line.
6909 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
6910 cursor_buffer_row = rows.end;
6911 } else {
6912 // If there isn't a line after the range, delete the \n from the line before the
6913 // start of the row range and position the cursor there.
6914 edit_start = edit_start.saturating_sub(1);
6915 edit_end = buffer.len();
6916 cursor_buffer_row = rows.start.previous_row();
6917 }
6918
6919 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
6920 *cursor.column_mut() =
6921 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
6922
6923 new_cursors.push((
6924 selection.id,
6925 buffer.anchor_after(cursor.to_point(&display_map)),
6926 ));
6927 edit_ranges.push(edit_start..edit_end);
6928 }
6929
6930 self.transact(window, cx, |this, window, cx| {
6931 let buffer = this.buffer.update(cx, |buffer, cx| {
6932 let empty_str: Arc<str> = Arc::default();
6933 buffer.edit(
6934 edit_ranges
6935 .into_iter()
6936 .map(|range| (range, empty_str.clone())),
6937 None,
6938 cx,
6939 );
6940 buffer.snapshot(cx)
6941 });
6942 let new_selections = new_cursors
6943 .into_iter()
6944 .map(|(id, cursor)| {
6945 let cursor = cursor.to_point(&buffer);
6946 Selection {
6947 id,
6948 start: cursor,
6949 end: cursor,
6950 reversed: false,
6951 goal: SelectionGoal::None,
6952 }
6953 })
6954 .collect();
6955
6956 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6957 s.select(new_selections);
6958 });
6959 });
6960 }
6961
6962 pub fn join_lines_impl(
6963 &mut self,
6964 insert_whitespace: bool,
6965 window: &mut Window,
6966 cx: &mut Context<Self>,
6967 ) {
6968 if self.read_only(cx) {
6969 return;
6970 }
6971 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
6972 for selection in self.selections.all::<Point>(cx) {
6973 let start = MultiBufferRow(selection.start.row);
6974 // Treat single line selections as if they include the next line. Otherwise this action
6975 // would do nothing for single line selections individual cursors.
6976 let end = if selection.start.row == selection.end.row {
6977 MultiBufferRow(selection.start.row + 1)
6978 } else {
6979 MultiBufferRow(selection.end.row)
6980 };
6981
6982 if let Some(last_row_range) = row_ranges.last_mut() {
6983 if start <= last_row_range.end {
6984 last_row_range.end = end;
6985 continue;
6986 }
6987 }
6988 row_ranges.push(start..end);
6989 }
6990
6991 let snapshot = self.buffer.read(cx).snapshot(cx);
6992 let mut cursor_positions = Vec::new();
6993 for row_range in &row_ranges {
6994 let anchor = snapshot.anchor_before(Point::new(
6995 row_range.end.previous_row().0,
6996 snapshot.line_len(row_range.end.previous_row()),
6997 ));
6998 cursor_positions.push(anchor..anchor);
6999 }
7000
7001 self.transact(window, cx, |this, window, cx| {
7002 for row_range in row_ranges.into_iter().rev() {
7003 for row in row_range.iter_rows().rev() {
7004 let end_of_line = Point::new(row.0, snapshot.line_len(row));
7005 let next_line_row = row.next_row();
7006 let indent = snapshot.indent_size_for_line(next_line_row);
7007 let start_of_next_line = Point::new(next_line_row.0, indent.len);
7008
7009 let replace =
7010 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
7011 " "
7012 } else {
7013 ""
7014 };
7015
7016 this.buffer.update(cx, |buffer, cx| {
7017 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
7018 });
7019 }
7020 }
7021
7022 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7023 s.select_anchor_ranges(cursor_positions)
7024 });
7025 });
7026 }
7027
7028 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
7029 self.join_lines_impl(true, window, cx);
7030 }
7031
7032 pub fn sort_lines_case_sensitive(
7033 &mut self,
7034 _: &SortLinesCaseSensitive,
7035 window: &mut Window,
7036 cx: &mut Context<Self>,
7037 ) {
7038 self.manipulate_lines(window, cx, |lines| lines.sort())
7039 }
7040
7041 pub fn sort_lines_case_insensitive(
7042 &mut self,
7043 _: &SortLinesCaseInsensitive,
7044 window: &mut Window,
7045 cx: &mut Context<Self>,
7046 ) {
7047 self.manipulate_lines(window, cx, |lines| {
7048 lines.sort_by_key(|line| line.to_lowercase())
7049 })
7050 }
7051
7052 pub fn unique_lines_case_insensitive(
7053 &mut self,
7054 _: &UniqueLinesCaseInsensitive,
7055 window: &mut Window,
7056 cx: &mut Context<Self>,
7057 ) {
7058 self.manipulate_lines(window, cx, |lines| {
7059 let mut seen = HashSet::default();
7060 lines.retain(|line| seen.insert(line.to_lowercase()));
7061 })
7062 }
7063
7064 pub fn unique_lines_case_sensitive(
7065 &mut self,
7066 _: &UniqueLinesCaseSensitive,
7067 window: &mut Window,
7068 cx: &mut Context<Self>,
7069 ) {
7070 self.manipulate_lines(window, cx, |lines| {
7071 let mut seen = HashSet::default();
7072 lines.retain(|line| seen.insert(*line));
7073 })
7074 }
7075
7076 pub fn revert_file(&mut self, _: &RevertFile, window: &mut Window, cx: &mut Context<Self>) {
7077 let mut revert_changes = HashMap::default();
7078 let snapshot = self.snapshot(window, cx);
7079 for hunk in snapshot
7080 .hunks_for_ranges(Some(Point::zero()..snapshot.buffer_snapshot.max_point()).into_iter())
7081 {
7082 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
7083 }
7084 if !revert_changes.is_empty() {
7085 self.transact(window, cx, |editor, window, cx| {
7086 editor.revert(revert_changes, window, cx);
7087 });
7088 }
7089 }
7090
7091 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
7092 let Some(project) = self.project.clone() else {
7093 return;
7094 };
7095 self.reload(project, window, cx)
7096 .detach_and_notify_err(window, cx);
7097 }
7098
7099 pub fn revert_selected_hunks(
7100 &mut self,
7101 _: &RevertSelectedHunks,
7102 window: &mut Window,
7103 cx: &mut Context<Self>,
7104 ) {
7105 let selections = self.selections.all(cx).into_iter().map(|s| s.range());
7106 self.revert_hunks_in_ranges(selections, window, cx);
7107 }
7108
7109 fn revert_hunks_in_ranges(
7110 &mut self,
7111 ranges: impl Iterator<Item = Range<Point>>,
7112 window: &mut Window,
7113 cx: &mut Context<Editor>,
7114 ) {
7115 let mut revert_changes = HashMap::default();
7116 let snapshot = self.snapshot(window, cx);
7117 for hunk in &snapshot.hunks_for_ranges(ranges) {
7118 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
7119 }
7120 if !revert_changes.is_empty() {
7121 self.transact(window, cx, |editor, window, cx| {
7122 editor.revert(revert_changes, window, cx);
7123 });
7124 }
7125 }
7126
7127 pub fn open_active_item_in_terminal(
7128 &mut self,
7129 _: &OpenInTerminal,
7130 window: &mut Window,
7131 cx: &mut Context<Self>,
7132 ) {
7133 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
7134 let project_path = buffer.read(cx).project_path(cx)?;
7135 let project = self.project.as_ref()?.read(cx);
7136 let entry = project.entry_for_path(&project_path, cx)?;
7137 let parent = match &entry.canonical_path {
7138 Some(canonical_path) => canonical_path.to_path_buf(),
7139 None => project.absolute_path(&project_path, cx)?,
7140 }
7141 .parent()?
7142 .to_path_buf();
7143 Some(parent)
7144 }) {
7145 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
7146 }
7147 }
7148
7149 pub fn prepare_revert_change(
7150 &self,
7151 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
7152 hunk: &MultiBufferDiffHunk,
7153 cx: &mut App,
7154 ) -> Option<()> {
7155 let buffer = self.buffer.read(cx);
7156 let diff = buffer.diff_for(hunk.buffer_id)?;
7157 let buffer = buffer.buffer(hunk.buffer_id)?;
7158 let buffer = buffer.read(cx);
7159 let original_text = diff
7160 .read(cx)
7161 .base_text()
7162 .as_ref()?
7163 .as_rope()
7164 .slice(hunk.diff_base_byte_range.clone());
7165 let buffer_snapshot = buffer.snapshot();
7166 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
7167 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
7168 probe
7169 .0
7170 .start
7171 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
7172 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
7173 }) {
7174 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
7175 Some(())
7176 } else {
7177 None
7178 }
7179 }
7180
7181 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
7182 self.manipulate_lines(window, cx, |lines| lines.reverse())
7183 }
7184
7185 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
7186 self.manipulate_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
7187 }
7188
7189 fn manipulate_lines<Fn>(
7190 &mut self,
7191 window: &mut Window,
7192 cx: &mut Context<Self>,
7193 mut callback: Fn,
7194 ) where
7195 Fn: FnMut(&mut Vec<&str>),
7196 {
7197 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7198 let buffer = self.buffer.read(cx).snapshot(cx);
7199
7200 let mut edits = Vec::new();
7201
7202 let selections = self.selections.all::<Point>(cx);
7203 let mut selections = selections.iter().peekable();
7204 let mut contiguous_row_selections = Vec::new();
7205 let mut new_selections = Vec::new();
7206 let mut added_lines = 0;
7207 let mut removed_lines = 0;
7208
7209 while let Some(selection) = selections.next() {
7210 let (start_row, end_row) = consume_contiguous_rows(
7211 &mut contiguous_row_selections,
7212 selection,
7213 &display_map,
7214 &mut selections,
7215 );
7216
7217 let start_point = Point::new(start_row.0, 0);
7218 let end_point = Point::new(
7219 end_row.previous_row().0,
7220 buffer.line_len(end_row.previous_row()),
7221 );
7222 let text = buffer
7223 .text_for_range(start_point..end_point)
7224 .collect::<String>();
7225
7226 let mut lines = text.split('\n').collect_vec();
7227
7228 let lines_before = lines.len();
7229 callback(&mut lines);
7230 let lines_after = lines.len();
7231
7232 edits.push((start_point..end_point, lines.join("\n")));
7233
7234 // Selections must change based on added and removed line count
7235 let start_row =
7236 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
7237 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
7238 new_selections.push(Selection {
7239 id: selection.id,
7240 start: start_row,
7241 end: end_row,
7242 goal: SelectionGoal::None,
7243 reversed: selection.reversed,
7244 });
7245
7246 if lines_after > lines_before {
7247 added_lines += lines_after - lines_before;
7248 } else if lines_before > lines_after {
7249 removed_lines += lines_before - lines_after;
7250 }
7251 }
7252
7253 self.transact(window, cx, |this, window, cx| {
7254 let buffer = this.buffer.update(cx, |buffer, cx| {
7255 buffer.edit(edits, None, cx);
7256 buffer.snapshot(cx)
7257 });
7258
7259 // Recalculate offsets on newly edited buffer
7260 let new_selections = new_selections
7261 .iter()
7262 .map(|s| {
7263 let start_point = Point::new(s.start.0, 0);
7264 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
7265 Selection {
7266 id: s.id,
7267 start: buffer.point_to_offset(start_point),
7268 end: buffer.point_to_offset(end_point),
7269 goal: s.goal,
7270 reversed: s.reversed,
7271 }
7272 })
7273 .collect();
7274
7275 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7276 s.select(new_selections);
7277 });
7278
7279 this.request_autoscroll(Autoscroll::fit(), cx);
7280 });
7281 }
7282
7283 pub fn convert_to_upper_case(
7284 &mut self,
7285 _: &ConvertToUpperCase,
7286 window: &mut Window,
7287 cx: &mut Context<Self>,
7288 ) {
7289 self.manipulate_text(window, cx, |text| text.to_uppercase())
7290 }
7291
7292 pub fn convert_to_lower_case(
7293 &mut self,
7294 _: &ConvertToLowerCase,
7295 window: &mut Window,
7296 cx: &mut Context<Self>,
7297 ) {
7298 self.manipulate_text(window, cx, |text| text.to_lowercase())
7299 }
7300
7301 pub fn convert_to_title_case(
7302 &mut self,
7303 _: &ConvertToTitleCase,
7304 window: &mut Window,
7305 cx: &mut Context<Self>,
7306 ) {
7307 self.manipulate_text(window, cx, |text| {
7308 text.split('\n')
7309 .map(|line| line.to_case(Case::Title))
7310 .join("\n")
7311 })
7312 }
7313
7314 pub fn convert_to_snake_case(
7315 &mut self,
7316 _: &ConvertToSnakeCase,
7317 window: &mut Window,
7318 cx: &mut Context<Self>,
7319 ) {
7320 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
7321 }
7322
7323 pub fn convert_to_kebab_case(
7324 &mut self,
7325 _: &ConvertToKebabCase,
7326 window: &mut Window,
7327 cx: &mut Context<Self>,
7328 ) {
7329 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
7330 }
7331
7332 pub fn convert_to_upper_camel_case(
7333 &mut self,
7334 _: &ConvertToUpperCamelCase,
7335 window: &mut Window,
7336 cx: &mut Context<Self>,
7337 ) {
7338 self.manipulate_text(window, cx, |text| {
7339 text.split('\n')
7340 .map(|line| line.to_case(Case::UpperCamel))
7341 .join("\n")
7342 })
7343 }
7344
7345 pub fn convert_to_lower_camel_case(
7346 &mut self,
7347 _: &ConvertToLowerCamelCase,
7348 window: &mut Window,
7349 cx: &mut Context<Self>,
7350 ) {
7351 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
7352 }
7353
7354 pub fn convert_to_opposite_case(
7355 &mut self,
7356 _: &ConvertToOppositeCase,
7357 window: &mut Window,
7358 cx: &mut Context<Self>,
7359 ) {
7360 self.manipulate_text(window, cx, |text| {
7361 text.chars()
7362 .fold(String::with_capacity(text.len()), |mut t, c| {
7363 if c.is_uppercase() {
7364 t.extend(c.to_lowercase());
7365 } else {
7366 t.extend(c.to_uppercase());
7367 }
7368 t
7369 })
7370 })
7371 }
7372
7373 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
7374 where
7375 Fn: FnMut(&str) -> String,
7376 {
7377 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7378 let buffer = self.buffer.read(cx).snapshot(cx);
7379
7380 let mut new_selections = Vec::new();
7381 let mut edits = Vec::new();
7382 let mut selection_adjustment = 0i32;
7383
7384 for selection in self.selections.all::<usize>(cx) {
7385 let selection_is_empty = selection.is_empty();
7386
7387 let (start, end) = if selection_is_empty {
7388 let word_range = movement::surrounding_word(
7389 &display_map,
7390 selection.start.to_display_point(&display_map),
7391 );
7392 let start = word_range.start.to_offset(&display_map, Bias::Left);
7393 let end = word_range.end.to_offset(&display_map, Bias::Left);
7394 (start, end)
7395 } else {
7396 (selection.start, selection.end)
7397 };
7398
7399 let text = buffer.text_for_range(start..end).collect::<String>();
7400 let old_length = text.len() as i32;
7401 let text = callback(&text);
7402
7403 new_selections.push(Selection {
7404 start: (start as i32 - selection_adjustment) as usize,
7405 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
7406 goal: SelectionGoal::None,
7407 ..selection
7408 });
7409
7410 selection_adjustment += old_length - text.len() as i32;
7411
7412 edits.push((start..end, text));
7413 }
7414
7415 self.transact(window, cx, |this, window, cx| {
7416 this.buffer.update(cx, |buffer, cx| {
7417 buffer.edit(edits, None, cx);
7418 });
7419
7420 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7421 s.select(new_selections);
7422 });
7423
7424 this.request_autoscroll(Autoscroll::fit(), cx);
7425 });
7426 }
7427
7428 pub fn duplicate(
7429 &mut self,
7430 upwards: bool,
7431 whole_lines: bool,
7432 window: &mut Window,
7433 cx: &mut Context<Self>,
7434 ) {
7435 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7436 let buffer = &display_map.buffer_snapshot;
7437 let selections = self.selections.all::<Point>(cx);
7438
7439 let mut edits = Vec::new();
7440 let mut selections_iter = selections.iter().peekable();
7441 while let Some(selection) = selections_iter.next() {
7442 let mut rows = selection.spanned_rows(false, &display_map);
7443 // duplicate line-wise
7444 if whole_lines || selection.start == selection.end {
7445 // Avoid duplicating the same lines twice.
7446 while let Some(next_selection) = selections_iter.peek() {
7447 let next_rows = next_selection.spanned_rows(false, &display_map);
7448 if next_rows.start < rows.end {
7449 rows.end = next_rows.end;
7450 selections_iter.next().unwrap();
7451 } else {
7452 break;
7453 }
7454 }
7455
7456 // Copy the text from the selected row region and splice it either at the start
7457 // or end of the region.
7458 let start = Point::new(rows.start.0, 0);
7459 let end = Point::new(
7460 rows.end.previous_row().0,
7461 buffer.line_len(rows.end.previous_row()),
7462 );
7463 let text = buffer
7464 .text_for_range(start..end)
7465 .chain(Some("\n"))
7466 .collect::<String>();
7467 let insert_location = if upwards {
7468 Point::new(rows.end.0, 0)
7469 } else {
7470 start
7471 };
7472 edits.push((insert_location..insert_location, text));
7473 } else {
7474 // duplicate character-wise
7475 let start = selection.start;
7476 let end = selection.end;
7477 let text = buffer.text_for_range(start..end).collect::<String>();
7478 edits.push((selection.end..selection.end, text));
7479 }
7480 }
7481
7482 self.transact(window, cx, |this, _, cx| {
7483 this.buffer.update(cx, |buffer, cx| {
7484 buffer.edit(edits, None, cx);
7485 });
7486
7487 this.request_autoscroll(Autoscroll::fit(), cx);
7488 });
7489 }
7490
7491 pub fn duplicate_line_up(
7492 &mut self,
7493 _: &DuplicateLineUp,
7494 window: &mut Window,
7495 cx: &mut Context<Self>,
7496 ) {
7497 self.duplicate(true, true, window, cx);
7498 }
7499
7500 pub fn duplicate_line_down(
7501 &mut self,
7502 _: &DuplicateLineDown,
7503 window: &mut Window,
7504 cx: &mut Context<Self>,
7505 ) {
7506 self.duplicate(false, true, window, cx);
7507 }
7508
7509 pub fn duplicate_selection(
7510 &mut self,
7511 _: &DuplicateSelection,
7512 window: &mut Window,
7513 cx: &mut Context<Self>,
7514 ) {
7515 self.duplicate(false, false, window, cx);
7516 }
7517
7518 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
7519 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7520 let buffer = self.buffer.read(cx).snapshot(cx);
7521
7522 let mut edits = Vec::new();
7523 let mut unfold_ranges = Vec::new();
7524 let mut refold_creases = Vec::new();
7525
7526 let selections = self.selections.all::<Point>(cx);
7527 let mut selections = selections.iter().peekable();
7528 let mut contiguous_row_selections = Vec::new();
7529 let mut new_selections = Vec::new();
7530
7531 while let Some(selection) = selections.next() {
7532 // Find all the selections that span a contiguous row range
7533 let (start_row, end_row) = consume_contiguous_rows(
7534 &mut contiguous_row_selections,
7535 selection,
7536 &display_map,
7537 &mut selections,
7538 );
7539
7540 // Move the text spanned by the row range to be before the line preceding the row range
7541 if start_row.0 > 0 {
7542 let range_to_move = Point::new(
7543 start_row.previous_row().0,
7544 buffer.line_len(start_row.previous_row()),
7545 )
7546 ..Point::new(
7547 end_row.previous_row().0,
7548 buffer.line_len(end_row.previous_row()),
7549 );
7550 let insertion_point = display_map
7551 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
7552 .0;
7553
7554 // Don't move lines across excerpts
7555 if buffer
7556 .excerpt_containing(insertion_point..range_to_move.end)
7557 .is_some()
7558 {
7559 let text = buffer
7560 .text_for_range(range_to_move.clone())
7561 .flat_map(|s| s.chars())
7562 .skip(1)
7563 .chain(['\n'])
7564 .collect::<String>();
7565
7566 edits.push((
7567 buffer.anchor_after(range_to_move.start)
7568 ..buffer.anchor_before(range_to_move.end),
7569 String::new(),
7570 ));
7571 let insertion_anchor = buffer.anchor_after(insertion_point);
7572 edits.push((insertion_anchor..insertion_anchor, text));
7573
7574 let row_delta = range_to_move.start.row - insertion_point.row + 1;
7575
7576 // Move selections up
7577 new_selections.extend(contiguous_row_selections.drain(..).map(
7578 |mut selection| {
7579 selection.start.row -= row_delta;
7580 selection.end.row -= row_delta;
7581 selection
7582 },
7583 ));
7584
7585 // Move folds up
7586 unfold_ranges.push(range_to_move.clone());
7587 for fold in display_map.folds_in_range(
7588 buffer.anchor_before(range_to_move.start)
7589 ..buffer.anchor_after(range_to_move.end),
7590 ) {
7591 let mut start = fold.range.start.to_point(&buffer);
7592 let mut end = fold.range.end.to_point(&buffer);
7593 start.row -= row_delta;
7594 end.row -= row_delta;
7595 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
7596 }
7597 }
7598 }
7599
7600 // If we didn't move line(s), preserve the existing selections
7601 new_selections.append(&mut contiguous_row_selections);
7602 }
7603
7604 self.transact(window, cx, |this, window, cx| {
7605 this.unfold_ranges(&unfold_ranges, true, true, cx);
7606 this.buffer.update(cx, |buffer, cx| {
7607 for (range, text) in edits {
7608 buffer.edit([(range, text)], None, cx);
7609 }
7610 });
7611 this.fold_creases(refold_creases, true, window, cx);
7612 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7613 s.select(new_selections);
7614 })
7615 });
7616 }
7617
7618 pub fn move_line_down(
7619 &mut self,
7620 _: &MoveLineDown,
7621 window: &mut Window,
7622 cx: &mut Context<Self>,
7623 ) {
7624 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7625 let buffer = self.buffer.read(cx).snapshot(cx);
7626
7627 let mut edits = Vec::new();
7628 let mut unfold_ranges = Vec::new();
7629 let mut refold_creases = Vec::new();
7630
7631 let selections = self.selections.all::<Point>(cx);
7632 let mut selections = selections.iter().peekable();
7633 let mut contiguous_row_selections = Vec::new();
7634 let mut new_selections = Vec::new();
7635
7636 while let Some(selection) = selections.next() {
7637 // Find all the selections that span a contiguous row range
7638 let (start_row, end_row) = consume_contiguous_rows(
7639 &mut contiguous_row_selections,
7640 selection,
7641 &display_map,
7642 &mut selections,
7643 );
7644
7645 // Move the text spanned by the row range to be after the last line of the row range
7646 if end_row.0 <= buffer.max_point().row {
7647 let range_to_move =
7648 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
7649 let insertion_point = display_map
7650 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
7651 .0;
7652
7653 // Don't move lines across excerpt boundaries
7654 if buffer
7655 .excerpt_containing(range_to_move.start..insertion_point)
7656 .is_some()
7657 {
7658 let mut text = String::from("\n");
7659 text.extend(buffer.text_for_range(range_to_move.clone()));
7660 text.pop(); // Drop trailing newline
7661 edits.push((
7662 buffer.anchor_after(range_to_move.start)
7663 ..buffer.anchor_before(range_to_move.end),
7664 String::new(),
7665 ));
7666 let insertion_anchor = buffer.anchor_after(insertion_point);
7667 edits.push((insertion_anchor..insertion_anchor, text));
7668
7669 let row_delta = insertion_point.row - range_to_move.end.row + 1;
7670
7671 // Move selections down
7672 new_selections.extend(contiguous_row_selections.drain(..).map(
7673 |mut selection| {
7674 selection.start.row += row_delta;
7675 selection.end.row += row_delta;
7676 selection
7677 },
7678 ));
7679
7680 // Move folds down
7681 unfold_ranges.push(range_to_move.clone());
7682 for fold in display_map.folds_in_range(
7683 buffer.anchor_before(range_to_move.start)
7684 ..buffer.anchor_after(range_to_move.end),
7685 ) {
7686 let mut start = fold.range.start.to_point(&buffer);
7687 let mut end = fold.range.end.to_point(&buffer);
7688 start.row += row_delta;
7689 end.row += row_delta;
7690 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
7691 }
7692 }
7693 }
7694
7695 // If we didn't move line(s), preserve the existing selections
7696 new_selections.append(&mut contiguous_row_selections);
7697 }
7698
7699 self.transact(window, cx, |this, window, cx| {
7700 this.unfold_ranges(&unfold_ranges, true, true, cx);
7701 this.buffer.update(cx, |buffer, cx| {
7702 for (range, text) in edits {
7703 buffer.edit([(range, text)], None, cx);
7704 }
7705 });
7706 this.fold_creases(refold_creases, true, window, cx);
7707 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7708 s.select(new_selections)
7709 });
7710 });
7711 }
7712
7713 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
7714 let text_layout_details = &self.text_layout_details(window);
7715 self.transact(window, cx, |this, window, cx| {
7716 let edits = this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7717 let mut edits: Vec<(Range<usize>, String)> = Default::default();
7718 let line_mode = s.line_mode;
7719 s.move_with(|display_map, selection| {
7720 if !selection.is_empty() || line_mode {
7721 return;
7722 }
7723
7724 let mut head = selection.head();
7725 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
7726 if head.column() == display_map.line_len(head.row()) {
7727 transpose_offset = display_map
7728 .buffer_snapshot
7729 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
7730 }
7731
7732 if transpose_offset == 0 {
7733 return;
7734 }
7735
7736 *head.column_mut() += 1;
7737 head = display_map.clip_point(head, Bias::Right);
7738 let goal = SelectionGoal::HorizontalPosition(
7739 display_map
7740 .x_for_display_point(head, text_layout_details)
7741 .into(),
7742 );
7743 selection.collapse_to(head, goal);
7744
7745 let transpose_start = display_map
7746 .buffer_snapshot
7747 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
7748 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
7749 let transpose_end = display_map
7750 .buffer_snapshot
7751 .clip_offset(transpose_offset + 1, Bias::Right);
7752 if let Some(ch) =
7753 display_map.buffer_snapshot.chars_at(transpose_start).next()
7754 {
7755 edits.push((transpose_start..transpose_offset, String::new()));
7756 edits.push((transpose_end..transpose_end, ch.to_string()));
7757 }
7758 }
7759 });
7760 edits
7761 });
7762 this.buffer
7763 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
7764 let selections = this.selections.all::<usize>(cx);
7765 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7766 s.select(selections);
7767 });
7768 });
7769 }
7770
7771 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
7772 self.rewrap_impl(IsVimMode::No, cx)
7773 }
7774
7775 pub fn rewrap_impl(&mut self, is_vim_mode: IsVimMode, cx: &mut Context<Self>) {
7776 let buffer = self.buffer.read(cx).snapshot(cx);
7777 let selections = self.selections.all::<Point>(cx);
7778 let mut selections = selections.iter().peekable();
7779
7780 let mut edits = Vec::new();
7781 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
7782
7783 while let Some(selection) = selections.next() {
7784 let mut start_row = selection.start.row;
7785 let mut end_row = selection.end.row;
7786
7787 // Skip selections that overlap with a range that has already been rewrapped.
7788 let selection_range = start_row..end_row;
7789 if rewrapped_row_ranges
7790 .iter()
7791 .any(|range| range.overlaps(&selection_range))
7792 {
7793 continue;
7794 }
7795
7796 let mut should_rewrap = is_vim_mode == IsVimMode::Yes;
7797
7798 if let Some(language_scope) = buffer.language_scope_at(selection.head()) {
7799 match language_scope.language_name().as_ref() {
7800 "Markdown" | "Plain Text" => {
7801 should_rewrap = true;
7802 }
7803 _ => {}
7804 }
7805 }
7806
7807 let tab_size = buffer.settings_at(selection.head(), cx).tab_size;
7808
7809 // Since not all lines in the selection may be at the same indent
7810 // level, choose the indent size that is the most common between all
7811 // of the lines.
7812 //
7813 // If there is a tie, we use the deepest indent.
7814 let (indent_size, indent_end) = {
7815 let mut indent_size_occurrences = HashMap::default();
7816 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
7817
7818 for row in start_row..=end_row {
7819 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
7820 rows_by_indent_size.entry(indent).or_default().push(row);
7821 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
7822 }
7823
7824 let indent_size = indent_size_occurrences
7825 .into_iter()
7826 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
7827 .map(|(indent, _)| indent)
7828 .unwrap_or_default();
7829 let row = rows_by_indent_size[&indent_size][0];
7830 let indent_end = Point::new(row, indent_size.len);
7831
7832 (indent_size, indent_end)
7833 };
7834
7835 let mut line_prefix = indent_size.chars().collect::<String>();
7836
7837 if let Some(comment_prefix) =
7838 buffer
7839 .language_scope_at(selection.head())
7840 .and_then(|language| {
7841 language
7842 .line_comment_prefixes()
7843 .iter()
7844 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
7845 .cloned()
7846 })
7847 {
7848 line_prefix.push_str(&comment_prefix);
7849 should_rewrap = true;
7850 }
7851
7852 if !should_rewrap {
7853 continue;
7854 }
7855
7856 if selection.is_empty() {
7857 'expand_upwards: while start_row > 0 {
7858 let prev_row = start_row - 1;
7859 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
7860 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
7861 {
7862 start_row = prev_row;
7863 } else {
7864 break 'expand_upwards;
7865 }
7866 }
7867
7868 'expand_downwards: while end_row < buffer.max_point().row {
7869 let next_row = end_row + 1;
7870 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
7871 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
7872 {
7873 end_row = next_row;
7874 } else {
7875 break 'expand_downwards;
7876 }
7877 }
7878 }
7879
7880 let start = Point::new(start_row, 0);
7881 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
7882 let selection_text = buffer.text_for_range(start..end).collect::<String>();
7883 let Some(lines_without_prefixes) = selection_text
7884 .lines()
7885 .map(|line| {
7886 line.strip_prefix(&line_prefix)
7887 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
7888 .ok_or_else(|| {
7889 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
7890 })
7891 })
7892 .collect::<Result<Vec<_>, _>>()
7893 .log_err()
7894 else {
7895 continue;
7896 };
7897
7898 let wrap_column = buffer
7899 .settings_at(Point::new(start_row, 0), cx)
7900 .preferred_line_length as usize;
7901 let wrapped_text = wrap_with_prefix(
7902 line_prefix,
7903 lines_without_prefixes.join(" "),
7904 wrap_column,
7905 tab_size,
7906 );
7907
7908 // TODO: should always use char-based diff while still supporting cursor behavior that
7909 // matches vim.
7910 let diff = match is_vim_mode {
7911 IsVimMode::Yes => TextDiff::from_lines(&selection_text, &wrapped_text),
7912 IsVimMode::No => TextDiff::from_chars(&selection_text, &wrapped_text),
7913 };
7914 let mut offset = start.to_offset(&buffer);
7915 let mut moved_since_edit = true;
7916
7917 for change in diff.iter_all_changes() {
7918 let value = change.value();
7919 match change.tag() {
7920 ChangeTag::Equal => {
7921 offset += value.len();
7922 moved_since_edit = true;
7923 }
7924 ChangeTag::Delete => {
7925 let start = buffer.anchor_after(offset);
7926 let end = buffer.anchor_before(offset + value.len());
7927
7928 if moved_since_edit {
7929 edits.push((start..end, String::new()));
7930 } else {
7931 edits.last_mut().unwrap().0.end = end;
7932 }
7933
7934 offset += value.len();
7935 moved_since_edit = false;
7936 }
7937 ChangeTag::Insert => {
7938 if moved_since_edit {
7939 let anchor = buffer.anchor_after(offset);
7940 edits.push((anchor..anchor, value.to_string()));
7941 } else {
7942 edits.last_mut().unwrap().1.push_str(value);
7943 }
7944
7945 moved_since_edit = false;
7946 }
7947 }
7948 }
7949
7950 rewrapped_row_ranges.push(start_row..=end_row);
7951 }
7952
7953 self.buffer
7954 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
7955 }
7956
7957 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
7958 let mut text = String::new();
7959 let buffer = self.buffer.read(cx).snapshot(cx);
7960 let mut selections = self.selections.all::<Point>(cx);
7961 let mut clipboard_selections = Vec::with_capacity(selections.len());
7962 {
7963 let max_point = buffer.max_point();
7964 let mut is_first = true;
7965 for selection in &mut selections {
7966 let is_entire_line = selection.is_empty() || self.selections.line_mode;
7967 if is_entire_line {
7968 selection.start = Point::new(selection.start.row, 0);
7969 if !selection.is_empty() && selection.end.column == 0 {
7970 selection.end = cmp::min(max_point, selection.end);
7971 } else {
7972 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
7973 }
7974 selection.goal = SelectionGoal::None;
7975 }
7976 if is_first {
7977 is_first = false;
7978 } else {
7979 text += "\n";
7980 }
7981 let mut len = 0;
7982 for chunk in buffer.text_for_range(selection.start..selection.end) {
7983 text.push_str(chunk);
7984 len += chunk.len();
7985 }
7986 clipboard_selections.push(ClipboardSelection {
7987 len,
7988 is_entire_line,
7989 first_line_indent: buffer
7990 .indent_size_for_line(MultiBufferRow(selection.start.row))
7991 .len,
7992 });
7993 }
7994 }
7995
7996 self.transact(window, cx, |this, window, cx| {
7997 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7998 s.select(selections);
7999 });
8000 this.insert("", window, cx);
8001 });
8002 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
8003 }
8004
8005 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
8006 let item = self.cut_common(window, cx);
8007 cx.write_to_clipboard(item);
8008 }
8009
8010 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
8011 self.change_selections(None, window, cx, |s| {
8012 s.move_with(|snapshot, sel| {
8013 if sel.is_empty() {
8014 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
8015 }
8016 });
8017 });
8018 let item = self.cut_common(window, cx);
8019 cx.set_global(KillRing(item))
8020 }
8021
8022 pub fn kill_ring_yank(
8023 &mut self,
8024 _: &KillRingYank,
8025 window: &mut Window,
8026 cx: &mut Context<Self>,
8027 ) {
8028 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
8029 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
8030 (kill_ring.text().to_string(), kill_ring.metadata_json())
8031 } else {
8032 return;
8033 }
8034 } else {
8035 return;
8036 };
8037 self.do_paste(&text, metadata, false, window, cx);
8038 }
8039
8040 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
8041 let selections = self.selections.all::<Point>(cx);
8042 let buffer = self.buffer.read(cx).read(cx);
8043 let mut text = String::new();
8044
8045 let mut clipboard_selections = Vec::with_capacity(selections.len());
8046 {
8047 let max_point = buffer.max_point();
8048 let mut is_first = true;
8049 for selection in selections.iter() {
8050 let mut start = selection.start;
8051 let mut end = selection.end;
8052 let is_entire_line = selection.is_empty() || self.selections.line_mode;
8053 if is_entire_line {
8054 start = Point::new(start.row, 0);
8055 end = cmp::min(max_point, Point::new(end.row + 1, 0));
8056 }
8057 if is_first {
8058 is_first = false;
8059 } else {
8060 text += "\n";
8061 }
8062 let mut len = 0;
8063 for chunk in buffer.text_for_range(start..end) {
8064 text.push_str(chunk);
8065 len += chunk.len();
8066 }
8067 clipboard_selections.push(ClipboardSelection {
8068 len,
8069 is_entire_line,
8070 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
8071 });
8072 }
8073 }
8074
8075 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
8076 text,
8077 clipboard_selections,
8078 ));
8079 }
8080
8081 pub fn do_paste(
8082 &mut self,
8083 text: &String,
8084 clipboard_selections: Option<Vec<ClipboardSelection>>,
8085 handle_entire_lines: bool,
8086 window: &mut Window,
8087 cx: &mut Context<Self>,
8088 ) {
8089 if self.read_only(cx) {
8090 return;
8091 }
8092
8093 let clipboard_text = Cow::Borrowed(text);
8094
8095 self.transact(window, cx, |this, window, cx| {
8096 if let Some(mut clipboard_selections) = clipboard_selections {
8097 let old_selections = this.selections.all::<usize>(cx);
8098 let all_selections_were_entire_line =
8099 clipboard_selections.iter().all(|s| s.is_entire_line);
8100 let first_selection_indent_column =
8101 clipboard_selections.first().map(|s| s.first_line_indent);
8102 if clipboard_selections.len() != old_selections.len() {
8103 clipboard_selections.drain(..);
8104 }
8105 let cursor_offset = this.selections.last::<usize>(cx).head();
8106 let mut auto_indent_on_paste = true;
8107
8108 this.buffer.update(cx, |buffer, cx| {
8109 let snapshot = buffer.read(cx);
8110 auto_indent_on_paste =
8111 snapshot.settings_at(cursor_offset, cx).auto_indent_on_paste;
8112
8113 let mut start_offset = 0;
8114 let mut edits = Vec::new();
8115 let mut original_indent_columns = Vec::new();
8116 for (ix, selection) in old_selections.iter().enumerate() {
8117 let to_insert;
8118 let entire_line;
8119 let original_indent_column;
8120 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
8121 let end_offset = start_offset + clipboard_selection.len;
8122 to_insert = &clipboard_text[start_offset..end_offset];
8123 entire_line = clipboard_selection.is_entire_line;
8124 start_offset = end_offset + 1;
8125 original_indent_column = Some(clipboard_selection.first_line_indent);
8126 } else {
8127 to_insert = clipboard_text.as_str();
8128 entire_line = all_selections_were_entire_line;
8129 original_indent_column = first_selection_indent_column
8130 }
8131
8132 // If the corresponding selection was empty when this slice of the
8133 // clipboard text was written, then the entire line containing the
8134 // selection was copied. If this selection is also currently empty,
8135 // then paste the line before the current line of the buffer.
8136 let range = if selection.is_empty() && handle_entire_lines && entire_line {
8137 let column = selection.start.to_point(&snapshot).column as usize;
8138 let line_start = selection.start - column;
8139 line_start..line_start
8140 } else {
8141 selection.range()
8142 };
8143
8144 edits.push((range, to_insert));
8145 original_indent_columns.extend(original_indent_column);
8146 }
8147 drop(snapshot);
8148
8149 buffer.edit(
8150 edits,
8151 if auto_indent_on_paste {
8152 Some(AutoindentMode::Block {
8153 original_indent_columns,
8154 })
8155 } else {
8156 None
8157 },
8158 cx,
8159 );
8160 });
8161
8162 let selections = this.selections.all::<usize>(cx);
8163 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8164 s.select(selections)
8165 });
8166 } else {
8167 this.insert(&clipboard_text, window, cx);
8168 }
8169 });
8170 }
8171
8172 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
8173 if let Some(item) = cx.read_from_clipboard() {
8174 let entries = item.entries();
8175
8176 match entries.first() {
8177 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
8178 // of all the pasted entries.
8179 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
8180 .do_paste(
8181 clipboard_string.text(),
8182 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
8183 true,
8184 window,
8185 cx,
8186 ),
8187 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
8188 }
8189 }
8190 }
8191
8192 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
8193 if self.read_only(cx) {
8194 return;
8195 }
8196
8197 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
8198 if let Some((selections, _)) =
8199 self.selection_history.transaction(transaction_id).cloned()
8200 {
8201 self.change_selections(None, window, cx, |s| {
8202 s.select_anchors(selections.to_vec());
8203 });
8204 }
8205 self.request_autoscroll(Autoscroll::fit(), cx);
8206 self.unmark_text(window, cx);
8207 self.refresh_inline_completion(true, false, window, cx);
8208 cx.emit(EditorEvent::Edited { transaction_id });
8209 cx.emit(EditorEvent::TransactionUndone { transaction_id });
8210 }
8211 }
8212
8213 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
8214 if self.read_only(cx) {
8215 return;
8216 }
8217
8218 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
8219 if let Some((_, Some(selections))) =
8220 self.selection_history.transaction(transaction_id).cloned()
8221 {
8222 self.change_selections(None, window, cx, |s| {
8223 s.select_anchors(selections.to_vec());
8224 });
8225 }
8226 self.request_autoscroll(Autoscroll::fit(), cx);
8227 self.unmark_text(window, cx);
8228 self.refresh_inline_completion(true, false, window, cx);
8229 cx.emit(EditorEvent::Edited { transaction_id });
8230 }
8231 }
8232
8233 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
8234 self.buffer
8235 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
8236 }
8237
8238 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
8239 self.buffer
8240 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
8241 }
8242
8243 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
8244 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8245 let line_mode = s.line_mode;
8246 s.move_with(|map, selection| {
8247 let cursor = if selection.is_empty() && !line_mode {
8248 movement::left(map, selection.start)
8249 } else {
8250 selection.start
8251 };
8252 selection.collapse_to(cursor, SelectionGoal::None);
8253 });
8254 })
8255 }
8256
8257 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
8258 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8259 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
8260 })
8261 }
8262
8263 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
8264 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8265 let line_mode = s.line_mode;
8266 s.move_with(|map, selection| {
8267 let cursor = if selection.is_empty() && !line_mode {
8268 movement::right(map, selection.end)
8269 } else {
8270 selection.end
8271 };
8272 selection.collapse_to(cursor, SelectionGoal::None)
8273 });
8274 })
8275 }
8276
8277 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
8278 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8279 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
8280 })
8281 }
8282
8283 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
8284 if self.take_rename(true, window, cx).is_some() {
8285 return;
8286 }
8287
8288 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8289 cx.propagate();
8290 return;
8291 }
8292
8293 let text_layout_details = &self.text_layout_details(window);
8294 let selection_count = self.selections.count();
8295 let first_selection = self.selections.first_anchor();
8296
8297 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8298 let line_mode = s.line_mode;
8299 s.move_with(|map, selection| {
8300 if !selection.is_empty() && !line_mode {
8301 selection.goal = SelectionGoal::None;
8302 }
8303 let (cursor, goal) = movement::up(
8304 map,
8305 selection.start,
8306 selection.goal,
8307 false,
8308 text_layout_details,
8309 );
8310 selection.collapse_to(cursor, goal);
8311 });
8312 });
8313
8314 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
8315 {
8316 cx.propagate();
8317 }
8318 }
8319
8320 pub fn move_up_by_lines(
8321 &mut self,
8322 action: &MoveUpByLines,
8323 window: &mut Window,
8324 cx: &mut Context<Self>,
8325 ) {
8326 if self.take_rename(true, window, cx).is_some() {
8327 return;
8328 }
8329
8330 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8331 cx.propagate();
8332 return;
8333 }
8334
8335 let text_layout_details = &self.text_layout_details(window);
8336
8337 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8338 let line_mode = s.line_mode;
8339 s.move_with(|map, selection| {
8340 if !selection.is_empty() && !line_mode {
8341 selection.goal = SelectionGoal::None;
8342 }
8343 let (cursor, goal) = movement::up_by_rows(
8344 map,
8345 selection.start,
8346 action.lines,
8347 selection.goal,
8348 false,
8349 text_layout_details,
8350 );
8351 selection.collapse_to(cursor, goal);
8352 });
8353 })
8354 }
8355
8356 pub fn move_down_by_lines(
8357 &mut self,
8358 action: &MoveDownByLines,
8359 window: &mut Window,
8360 cx: &mut Context<Self>,
8361 ) {
8362 if self.take_rename(true, window, cx).is_some() {
8363 return;
8364 }
8365
8366 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8367 cx.propagate();
8368 return;
8369 }
8370
8371 let text_layout_details = &self.text_layout_details(window);
8372
8373 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8374 let line_mode = s.line_mode;
8375 s.move_with(|map, selection| {
8376 if !selection.is_empty() && !line_mode {
8377 selection.goal = SelectionGoal::None;
8378 }
8379 let (cursor, goal) = movement::down_by_rows(
8380 map,
8381 selection.start,
8382 action.lines,
8383 selection.goal,
8384 false,
8385 text_layout_details,
8386 );
8387 selection.collapse_to(cursor, goal);
8388 });
8389 })
8390 }
8391
8392 pub fn select_down_by_lines(
8393 &mut self,
8394 action: &SelectDownByLines,
8395 window: &mut Window,
8396 cx: &mut Context<Self>,
8397 ) {
8398 let text_layout_details = &self.text_layout_details(window);
8399 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8400 s.move_heads_with(|map, head, goal| {
8401 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
8402 })
8403 })
8404 }
8405
8406 pub fn select_up_by_lines(
8407 &mut self,
8408 action: &SelectUpByLines,
8409 window: &mut Window,
8410 cx: &mut Context<Self>,
8411 ) {
8412 let text_layout_details = &self.text_layout_details(window);
8413 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8414 s.move_heads_with(|map, head, goal| {
8415 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
8416 })
8417 })
8418 }
8419
8420 pub fn select_page_up(
8421 &mut self,
8422 _: &SelectPageUp,
8423 window: &mut Window,
8424 cx: &mut Context<Self>,
8425 ) {
8426 let Some(row_count) = self.visible_row_count() else {
8427 return;
8428 };
8429
8430 let text_layout_details = &self.text_layout_details(window);
8431
8432 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8433 s.move_heads_with(|map, head, goal| {
8434 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
8435 })
8436 })
8437 }
8438
8439 pub fn move_page_up(
8440 &mut self,
8441 action: &MovePageUp,
8442 window: &mut Window,
8443 cx: &mut Context<Self>,
8444 ) {
8445 if self.take_rename(true, window, cx).is_some() {
8446 return;
8447 }
8448
8449 if self
8450 .context_menu
8451 .borrow_mut()
8452 .as_mut()
8453 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
8454 .unwrap_or(false)
8455 {
8456 return;
8457 }
8458
8459 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8460 cx.propagate();
8461 return;
8462 }
8463
8464 let Some(row_count) = self.visible_row_count() else {
8465 return;
8466 };
8467
8468 let autoscroll = if action.center_cursor {
8469 Autoscroll::center()
8470 } else {
8471 Autoscroll::fit()
8472 };
8473
8474 let text_layout_details = &self.text_layout_details(window);
8475
8476 self.change_selections(Some(autoscroll), window, cx, |s| {
8477 let line_mode = s.line_mode;
8478 s.move_with(|map, selection| {
8479 if !selection.is_empty() && !line_mode {
8480 selection.goal = SelectionGoal::None;
8481 }
8482 let (cursor, goal) = movement::up_by_rows(
8483 map,
8484 selection.end,
8485 row_count,
8486 selection.goal,
8487 false,
8488 text_layout_details,
8489 );
8490 selection.collapse_to(cursor, goal);
8491 });
8492 });
8493 }
8494
8495 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
8496 let text_layout_details = &self.text_layout_details(window);
8497 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8498 s.move_heads_with(|map, head, goal| {
8499 movement::up(map, head, goal, false, text_layout_details)
8500 })
8501 })
8502 }
8503
8504 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
8505 self.take_rename(true, window, cx);
8506
8507 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8508 cx.propagate();
8509 return;
8510 }
8511
8512 let text_layout_details = &self.text_layout_details(window);
8513 let selection_count = self.selections.count();
8514 let first_selection = self.selections.first_anchor();
8515
8516 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8517 let line_mode = s.line_mode;
8518 s.move_with(|map, selection| {
8519 if !selection.is_empty() && !line_mode {
8520 selection.goal = SelectionGoal::None;
8521 }
8522 let (cursor, goal) = movement::down(
8523 map,
8524 selection.end,
8525 selection.goal,
8526 false,
8527 text_layout_details,
8528 );
8529 selection.collapse_to(cursor, goal);
8530 });
8531 });
8532
8533 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
8534 {
8535 cx.propagate();
8536 }
8537 }
8538
8539 pub fn select_page_down(
8540 &mut self,
8541 _: &SelectPageDown,
8542 window: &mut Window,
8543 cx: &mut Context<Self>,
8544 ) {
8545 let Some(row_count) = self.visible_row_count() else {
8546 return;
8547 };
8548
8549 let text_layout_details = &self.text_layout_details(window);
8550
8551 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8552 s.move_heads_with(|map, head, goal| {
8553 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
8554 })
8555 })
8556 }
8557
8558 pub fn move_page_down(
8559 &mut self,
8560 action: &MovePageDown,
8561 window: &mut Window,
8562 cx: &mut Context<Self>,
8563 ) {
8564 if self.take_rename(true, window, cx).is_some() {
8565 return;
8566 }
8567
8568 if self
8569 .context_menu
8570 .borrow_mut()
8571 .as_mut()
8572 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
8573 .unwrap_or(false)
8574 {
8575 return;
8576 }
8577
8578 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8579 cx.propagate();
8580 return;
8581 }
8582
8583 let Some(row_count) = self.visible_row_count() else {
8584 return;
8585 };
8586
8587 let autoscroll = if action.center_cursor {
8588 Autoscroll::center()
8589 } else {
8590 Autoscroll::fit()
8591 };
8592
8593 let text_layout_details = &self.text_layout_details(window);
8594 self.change_selections(Some(autoscroll), window, cx, |s| {
8595 let line_mode = s.line_mode;
8596 s.move_with(|map, selection| {
8597 if !selection.is_empty() && !line_mode {
8598 selection.goal = SelectionGoal::None;
8599 }
8600 let (cursor, goal) = movement::down_by_rows(
8601 map,
8602 selection.end,
8603 row_count,
8604 selection.goal,
8605 false,
8606 text_layout_details,
8607 );
8608 selection.collapse_to(cursor, goal);
8609 });
8610 });
8611 }
8612
8613 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
8614 let text_layout_details = &self.text_layout_details(window);
8615 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8616 s.move_heads_with(|map, head, goal| {
8617 movement::down(map, head, goal, false, text_layout_details)
8618 })
8619 });
8620 }
8621
8622 pub fn context_menu_first(
8623 &mut self,
8624 _: &ContextMenuFirst,
8625 _window: &mut Window,
8626 cx: &mut Context<Self>,
8627 ) {
8628 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8629 context_menu.select_first(self.completion_provider.as_deref(), cx);
8630 }
8631 }
8632
8633 pub fn context_menu_prev(
8634 &mut self,
8635 _: &ContextMenuPrev,
8636 _window: &mut Window,
8637 cx: &mut Context<Self>,
8638 ) {
8639 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8640 context_menu.select_prev(self.completion_provider.as_deref(), cx);
8641 }
8642 }
8643
8644 pub fn context_menu_next(
8645 &mut self,
8646 _: &ContextMenuNext,
8647 _window: &mut Window,
8648 cx: &mut Context<Self>,
8649 ) {
8650 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8651 context_menu.select_next(self.completion_provider.as_deref(), cx);
8652 }
8653 }
8654
8655 pub fn context_menu_last(
8656 &mut self,
8657 _: &ContextMenuLast,
8658 _window: &mut Window,
8659 cx: &mut Context<Self>,
8660 ) {
8661 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8662 context_menu.select_last(self.completion_provider.as_deref(), cx);
8663 }
8664 }
8665
8666 pub fn move_to_previous_word_start(
8667 &mut self,
8668 _: &MoveToPreviousWordStart,
8669 window: &mut Window,
8670 cx: &mut Context<Self>,
8671 ) {
8672 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8673 s.move_cursors_with(|map, head, _| {
8674 (
8675 movement::previous_word_start(map, head),
8676 SelectionGoal::None,
8677 )
8678 });
8679 })
8680 }
8681
8682 pub fn move_to_previous_subword_start(
8683 &mut self,
8684 _: &MoveToPreviousSubwordStart,
8685 window: &mut Window,
8686 cx: &mut Context<Self>,
8687 ) {
8688 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8689 s.move_cursors_with(|map, head, _| {
8690 (
8691 movement::previous_subword_start(map, head),
8692 SelectionGoal::None,
8693 )
8694 });
8695 })
8696 }
8697
8698 pub fn select_to_previous_word_start(
8699 &mut self,
8700 _: &SelectToPreviousWordStart,
8701 window: &mut Window,
8702 cx: &mut Context<Self>,
8703 ) {
8704 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8705 s.move_heads_with(|map, head, _| {
8706 (
8707 movement::previous_word_start(map, head),
8708 SelectionGoal::None,
8709 )
8710 });
8711 })
8712 }
8713
8714 pub fn select_to_previous_subword_start(
8715 &mut self,
8716 _: &SelectToPreviousSubwordStart,
8717 window: &mut Window,
8718 cx: &mut Context<Self>,
8719 ) {
8720 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8721 s.move_heads_with(|map, head, _| {
8722 (
8723 movement::previous_subword_start(map, head),
8724 SelectionGoal::None,
8725 )
8726 });
8727 })
8728 }
8729
8730 pub fn delete_to_previous_word_start(
8731 &mut self,
8732 action: &DeleteToPreviousWordStart,
8733 window: &mut Window,
8734 cx: &mut Context<Self>,
8735 ) {
8736 self.transact(window, cx, |this, window, cx| {
8737 this.select_autoclose_pair(window, cx);
8738 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8739 let line_mode = s.line_mode;
8740 s.move_with(|map, selection| {
8741 if selection.is_empty() && !line_mode {
8742 let cursor = if action.ignore_newlines {
8743 movement::previous_word_start(map, selection.head())
8744 } else {
8745 movement::previous_word_start_or_newline(map, selection.head())
8746 };
8747 selection.set_head(cursor, SelectionGoal::None);
8748 }
8749 });
8750 });
8751 this.insert("", window, cx);
8752 });
8753 }
8754
8755 pub fn delete_to_previous_subword_start(
8756 &mut self,
8757 _: &DeleteToPreviousSubwordStart,
8758 window: &mut Window,
8759 cx: &mut Context<Self>,
8760 ) {
8761 self.transact(window, cx, |this, window, cx| {
8762 this.select_autoclose_pair(window, cx);
8763 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8764 let line_mode = s.line_mode;
8765 s.move_with(|map, selection| {
8766 if selection.is_empty() && !line_mode {
8767 let cursor = movement::previous_subword_start(map, selection.head());
8768 selection.set_head(cursor, SelectionGoal::None);
8769 }
8770 });
8771 });
8772 this.insert("", window, cx);
8773 });
8774 }
8775
8776 pub fn move_to_next_word_end(
8777 &mut self,
8778 _: &MoveToNextWordEnd,
8779 window: &mut Window,
8780 cx: &mut Context<Self>,
8781 ) {
8782 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8783 s.move_cursors_with(|map, head, _| {
8784 (movement::next_word_end(map, head), SelectionGoal::None)
8785 });
8786 })
8787 }
8788
8789 pub fn move_to_next_subword_end(
8790 &mut self,
8791 _: &MoveToNextSubwordEnd,
8792 window: &mut Window,
8793 cx: &mut Context<Self>,
8794 ) {
8795 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8796 s.move_cursors_with(|map, head, _| {
8797 (movement::next_subword_end(map, head), SelectionGoal::None)
8798 });
8799 })
8800 }
8801
8802 pub fn select_to_next_word_end(
8803 &mut self,
8804 _: &SelectToNextWordEnd,
8805 window: &mut Window,
8806 cx: &mut Context<Self>,
8807 ) {
8808 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8809 s.move_heads_with(|map, head, _| {
8810 (movement::next_word_end(map, head), SelectionGoal::None)
8811 });
8812 })
8813 }
8814
8815 pub fn select_to_next_subword_end(
8816 &mut self,
8817 _: &SelectToNextSubwordEnd,
8818 window: &mut Window,
8819 cx: &mut Context<Self>,
8820 ) {
8821 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8822 s.move_heads_with(|map, head, _| {
8823 (movement::next_subword_end(map, head), SelectionGoal::None)
8824 });
8825 })
8826 }
8827
8828 pub fn delete_to_next_word_end(
8829 &mut self,
8830 action: &DeleteToNextWordEnd,
8831 window: &mut Window,
8832 cx: &mut Context<Self>,
8833 ) {
8834 self.transact(window, cx, |this, window, cx| {
8835 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8836 let line_mode = s.line_mode;
8837 s.move_with(|map, selection| {
8838 if selection.is_empty() && !line_mode {
8839 let cursor = if action.ignore_newlines {
8840 movement::next_word_end(map, selection.head())
8841 } else {
8842 movement::next_word_end_or_newline(map, selection.head())
8843 };
8844 selection.set_head(cursor, SelectionGoal::None);
8845 }
8846 });
8847 });
8848 this.insert("", window, cx);
8849 });
8850 }
8851
8852 pub fn delete_to_next_subword_end(
8853 &mut self,
8854 _: &DeleteToNextSubwordEnd,
8855 window: &mut Window,
8856 cx: &mut Context<Self>,
8857 ) {
8858 self.transact(window, cx, |this, window, cx| {
8859 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8860 s.move_with(|map, selection| {
8861 if selection.is_empty() {
8862 let cursor = movement::next_subword_end(map, selection.head());
8863 selection.set_head(cursor, SelectionGoal::None);
8864 }
8865 });
8866 });
8867 this.insert("", window, cx);
8868 });
8869 }
8870
8871 pub fn move_to_beginning_of_line(
8872 &mut self,
8873 action: &MoveToBeginningOfLine,
8874 window: &mut Window,
8875 cx: &mut Context<Self>,
8876 ) {
8877 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8878 s.move_cursors_with(|map, head, _| {
8879 (
8880 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
8881 SelectionGoal::None,
8882 )
8883 });
8884 })
8885 }
8886
8887 pub fn select_to_beginning_of_line(
8888 &mut self,
8889 action: &SelectToBeginningOfLine,
8890 window: &mut Window,
8891 cx: &mut Context<Self>,
8892 ) {
8893 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8894 s.move_heads_with(|map, head, _| {
8895 (
8896 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
8897 SelectionGoal::None,
8898 )
8899 });
8900 });
8901 }
8902
8903 pub fn delete_to_beginning_of_line(
8904 &mut self,
8905 _: &DeleteToBeginningOfLine,
8906 window: &mut Window,
8907 cx: &mut Context<Self>,
8908 ) {
8909 self.transact(window, cx, |this, window, cx| {
8910 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8911 s.move_with(|_, selection| {
8912 selection.reversed = true;
8913 });
8914 });
8915
8916 this.select_to_beginning_of_line(
8917 &SelectToBeginningOfLine {
8918 stop_at_soft_wraps: false,
8919 },
8920 window,
8921 cx,
8922 );
8923 this.backspace(&Backspace, window, cx);
8924 });
8925 }
8926
8927 pub fn move_to_end_of_line(
8928 &mut self,
8929 action: &MoveToEndOfLine,
8930 window: &mut Window,
8931 cx: &mut Context<Self>,
8932 ) {
8933 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8934 s.move_cursors_with(|map, head, _| {
8935 (
8936 movement::line_end(map, head, action.stop_at_soft_wraps),
8937 SelectionGoal::None,
8938 )
8939 });
8940 })
8941 }
8942
8943 pub fn select_to_end_of_line(
8944 &mut self,
8945 action: &SelectToEndOfLine,
8946 window: &mut Window,
8947 cx: &mut Context<Self>,
8948 ) {
8949 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8950 s.move_heads_with(|map, head, _| {
8951 (
8952 movement::line_end(map, head, action.stop_at_soft_wraps),
8953 SelectionGoal::None,
8954 )
8955 });
8956 })
8957 }
8958
8959 pub fn delete_to_end_of_line(
8960 &mut self,
8961 _: &DeleteToEndOfLine,
8962 window: &mut Window,
8963 cx: &mut Context<Self>,
8964 ) {
8965 self.transact(window, cx, |this, window, cx| {
8966 this.select_to_end_of_line(
8967 &SelectToEndOfLine {
8968 stop_at_soft_wraps: false,
8969 },
8970 window,
8971 cx,
8972 );
8973 this.delete(&Delete, window, cx);
8974 });
8975 }
8976
8977 pub fn cut_to_end_of_line(
8978 &mut self,
8979 _: &CutToEndOfLine,
8980 window: &mut Window,
8981 cx: &mut Context<Self>,
8982 ) {
8983 self.transact(window, cx, |this, window, cx| {
8984 this.select_to_end_of_line(
8985 &SelectToEndOfLine {
8986 stop_at_soft_wraps: false,
8987 },
8988 window,
8989 cx,
8990 );
8991 this.cut(&Cut, window, cx);
8992 });
8993 }
8994
8995 pub fn move_to_start_of_paragraph(
8996 &mut self,
8997 _: &MoveToStartOfParagraph,
8998 window: &mut Window,
8999 cx: &mut Context<Self>,
9000 ) {
9001 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9002 cx.propagate();
9003 return;
9004 }
9005
9006 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9007 s.move_with(|map, selection| {
9008 selection.collapse_to(
9009 movement::start_of_paragraph(map, selection.head(), 1),
9010 SelectionGoal::None,
9011 )
9012 });
9013 })
9014 }
9015
9016 pub fn move_to_end_of_paragraph(
9017 &mut self,
9018 _: &MoveToEndOfParagraph,
9019 window: &mut Window,
9020 cx: &mut Context<Self>,
9021 ) {
9022 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9023 cx.propagate();
9024 return;
9025 }
9026
9027 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9028 s.move_with(|map, selection| {
9029 selection.collapse_to(
9030 movement::end_of_paragraph(map, selection.head(), 1),
9031 SelectionGoal::None,
9032 )
9033 });
9034 })
9035 }
9036
9037 pub fn select_to_start_of_paragraph(
9038 &mut self,
9039 _: &SelectToStartOfParagraph,
9040 window: &mut Window,
9041 cx: &mut Context<Self>,
9042 ) {
9043 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9044 cx.propagate();
9045 return;
9046 }
9047
9048 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9049 s.move_heads_with(|map, head, _| {
9050 (
9051 movement::start_of_paragraph(map, head, 1),
9052 SelectionGoal::None,
9053 )
9054 });
9055 })
9056 }
9057
9058 pub fn select_to_end_of_paragraph(
9059 &mut self,
9060 _: &SelectToEndOfParagraph,
9061 window: &mut Window,
9062 cx: &mut Context<Self>,
9063 ) {
9064 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9065 cx.propagate();
9066 return;
9067 }
9068
9069 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9070 s.move_heads_with(|map, head, _| {
9071 (
9072 movement::end_of_paragraph(map, head, 1),
9073 SelectionGoal::None,
9074 )
9075 });
9076 })
9077 }
9078
9079 pub fn move_to_beginning(
9080 &mut self,
9081 _: &MoveToBeginning,
9082 window: &mut Window,
9083 cx: &mut Context<Self>,
9084 ) {
9085 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9086 cx.propagate();
9087 return;
9088 }
9089
9090 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9091 s.select_ranges(vec![0..0]);
9092 });
9093 }
9094
9095 pub fn select_to_beginning(
9096 &mut self,
9097 _: &SelectToBeginning,
9098 window: &mut Window,
9099 cx: &mut Context<Self>,
9100 ) {
9101 let mut selection = self.selections.last::<Point>(cx);
9102 selection.set_head(Point::zero(), SelectionGoal::None);
9103
9104 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9105 s.select(vec![selection]);
9106 });
9107 }
9108
9109 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
9110 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9111 cx.propagate();
9112 return;
9113 }
9114
9115 let cursor = self.buffer.read(cx).read(cx).len();
9116 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9117 s.select_ranges(vec![cursor..cursor])
9118 });
9119 }
9120
9121 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
9122 self.nav_history = nav_history;
9123 }
9124
9125 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
9126 self.nav_history.as_ref()
9127 }
9128
9129 fn push_to_nav_history(
9130 &mut self,
9131 cursor_anchor: Anchor,
9132 new_position: Option<Point>,
9133 cx: &mut Context<Self>,
9134 ) {
9135 if let Some(nav_history) = self.nav_history.as_mut() {
9136 let buffer = self.buffer.read(cx).read(cx);
9137 let cursor_position = cursor_anchor.to_point(&buffer);
9138 let scroll_state = self.scroll_manager.anchor();
9139 let scroll_top_row = scroll_state.top_row(&buffer);
9140 drop(buffer);
9141
9142 if let Some(new_position) = new_position {
9143 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
9144 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
9145 return;
9146 }
9147 }
9148
9149 nav_history.push(
9150 Some(NavigationData {
9151 cursor_anchor,
9152 cursor_position,
9153 scroll_anchor: scroll_state,
9154 scroll_top_row,
9155 }),
9156 cx,
9157 );
9158 }
9159 }
9160
9161 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
9162 let buffer = self.buffer.read(cx).snapshot(cx);
9163 let mut selection = self.selections.first::<usize>(cx);
9164 selection.set_head(buffer.len(), SelectionGoal::None);
9165 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9166 s.select(vec![selection]);
9167 });
9168 }
9169
9170 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
9171 let end = self.buffer.read(cx).read(cx).len();
9172 self.change_selections(None, window, cx, |s| {
9173 s.select_ranges(vec![0..end]);
9174 });
9175 }
9176
9177 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
9178 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9179 let mut selections = self.selections.all::<Point>(cx);
9180 let max_point = display_map.buffer_snapshot.max_point();
9181 for selection in &mut selections {
9182 let rows = selection.spanned_rows(true, &display_map);
9183 selection.start = Point::new(rows.start.0, 0);
9184 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
9185 selection.reversed = false;
9186 }
9187 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9188 s.select(selections);
9189 });
9190 }
9191
9192 pub fn split_selection_into_lines(
9193 &mut self,
9194 _: &SplitSelectionIntoLines,
9195 window: &mut Window,
9196 cx: &mut Context<Self>,
9197 ) {
9198 let mut to_unfold = Vec::new();
9199 let mut new_selection_ranges = Vec::new();
9200 {
9201 let selections = self.selections.all::<Point>(cx);
9202 let buffer = self.buffer.read(cx).read(cx);
9203 for selection in selections {
9204 for row in selection.start.row..selection.end.row {
9205 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
9206 new_selection_ranges.push(cursor..cursor);
9207 }
9208 new_selection_ranges.push(selection.end..selection.end);
9209 to_unfold.push(selection.start..selection.end);
9210 }
9211 }
9212 self.unfold_ranges(&to_unfold, true, true, cx);
9213 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9214 s.select_ranges(new_selection_ranges);
9215 });
9216 }
9217
9218 pub fn add_selection_above(
9219 &mut self,
9220 _: &AddSelectionAbove,
9221 window: &mut Window,
9222 cx: &mut Context<Self>,
9223 ) {
9224 self.add_selection(true, window, cx);
9225 }
9226
9227 pub fn add_selection_below(
9228 &mut self,
9229 _: &AddSelectionBelow,
9230 window: &mut Window,
9231 cx: &mut Context<Self>,
9232 ) {
9233 self.add_selection(false, window, cx);
9234 }
9235
9236 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
9237 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9238 let mut selections = self.selections.all::<Point>(cx);
9239 let text_layout_details = self.text_layout_details(window);
9240 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
9241 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
9242 let range = oldest_selection.display_range(&display_map).sorted();
9243
9244 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
9245 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
9246 let positions = start_x.min(end_x)..start_x.max(end_x);
9247
9248 selections.clear();
9249 let mut stack = Vec::new();
9250 for row in range.start.row().0..=range.end.row().0 {
9251 if let Some(selection) = self.selections.build_columnar_selection(
9252 &display_map,
9253 DisplayRow(row),
9254 &positions,
9255 oldest_selection.reversed,
9256 &text_layout_details,
9257 ) {
9258 stack.push(selection.id);
9259 selections.push(selection);
9260 }
9261 }
9262
9263 if above {
9264 stack.reverse();
9265 }
9266
9267 AddSelectionsState { above, stack }
9268 });
9269
9270 let last_added_selection = *state.stack.last().unwrap();
9271 let mut new_selections = Vec::new();
9272 if above == state.above {
9273 let end_row = if above {
9274 DisplayRow(0)
9275 } else {
9276 display_map.max_point().row()
9277 };
9278
9279 'outer: for selection in selections {
9280 if selection.id == last_added_selection {
9281 let range = selection.display_range(&display_map).sorted();
9282 debug_assert_eq!(range.start.row(), range.end.row());
9283 let mut row = range.start.row();
9284 let positions =
9285 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
9286 px(start)..px(end)
9287 } else {
9288 let start_x =
9289 display_map.x_for_display_point(range.start, &text_layout_details);
9290 let end_x =
9291 display_map.x_for_display_point(range.end, &text_layout_details);
9292 start_x.min(end_x)..start_x.max(end_x)
9293 };
9294
9295 while row != end_row {
9296 if above {
9297 row.0 -= 1;
9298 } else {
9299 row.0 += 1;
9300 }
9301
9302 if let Some(new_selection) = self.selections.build_columnar_selection(
9303 &display_map,
9304 row,
9305 &positions,
9306 selection.reversed,
9307 &text_layout_details,
9308 ) {
9309 state.stack.push(new_selection.id);
9310 if above {
9311 new_selections.push(new_selection);
9312 new_selections.push(selection);
9313 } else {
9314 new_selections.push(selection);
9315 new_selections.push(new_selection);
9316 }
9317
9318 continue 'outer;
9319 }
9320 }
9321 }
9322
9323 new_selections.push(selection);
9324 }
9325 } else {
9326 new_selections = selections;
9327 new_selections.retain(|s| s.id != last_added_selection);
9328 state.stack.pop();
9329 }
9330
9331 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9332 s.select(new_selections);
9333 });
9334 if state.stack.len() > 1 {
9335 self.add_selections_state = Some(state);
9336 }
9337 }
9338
9339 pub fn select_next_match_internal(
9340 &mut self,
9341 display_map: &DisplaySnapshot,
9342 replace_newest: bool,
9343 autoscroll: Option<Autoscroll>,
9344 window: &mut Window,
9345 cx: &mut Context<Self>,
9346 ) -> Result<()> {
9347 fn select_next_match_ranges(
9348 this: &mut Editor,
9349 range: Range<usize>,
9350 replace_newest: bool,
9351 auto_scroll: Option<Autoscroll>,
9352 window: &mut Window,
9353 cx: &mut Context<Editor>,
9354 ) {
9355 this.unfold_ranges(&[range.clone()], false, true, cx);
9356 this.change_selections(auto_scroll, window, cx, |s| {
9357 if replace_newest {
9358 s.delete(s.newest_anchor().id);
9359 }
9360 s.insert_range(range.clone());
9361 });
9362 }
9363
9364 let buffer = &display_map.buffer_snapshot;
9365 let mut selections = self.selections.all::<usize>(cx);
9366 if let Some(mut select_next_state) = self.select_next_state.take() {
9367 let query = &select_next_state.query;
9368 if !select_next_state.done {
9369 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
9370 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
9371 let mut next_selected_range = None;
9372
9373 let bytes_after_last_selection =
9374 buffer.bytes_in_range(last_selection.end..buffer.len());
9375 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
9376 let query_matches = query
9377 .stream_find_iter(bytes_after_last_selection)
9378 .map(|result| (last_selection.end, result))
9379 .chain(
9380 query
9381 .stream_find_iter(bytes_before_first_selection)
9382 .map(|result| (0, result)),
9383 );
9384
9385 for (start_offset, query_match) in query_matches {
9386 let query_match = query_match.unwrap(); // can only fail due to I/O
9387 let offset_range =
9388 start_offset + query_match.start()..start_offset + query_match.end();
9389 let display_range = offset_range.start.to_display_point(display_map)
9390 ..offset_range.end.to_display_point(display_map);
9391
9392 if !select_next_state.wordwise
9393 || (!movement::is_inside_word(display_map, display_range.start)
9394 && !movement::is_inside_word(display_map, display_range.end))
9395 {
9396 // TODO: This is n^2, because we might check all the selections
9397 if !selections
9398 .iter()
9399 .any(|selection| selection.range().overlaps(&offset_range))
9400 {
9401 next_selected_range = Some(offset_range);
9402 break;
9403 }
9404 }
9405 }
9406
9407 if let Some(next_selected_range) = next_selected_range {
9408 select_next_match_ranges(
9409 self,
9410 next_selected_range,
9411 replace_newest,
9412 autoscroll,
9413 window,
9414 cx,
9415 );
9416 } else {
9417 select_next_state.done = true;
9418 }
9419 }
9420
9421 self.select_next_state = Some(select_next_state);
9422 } else {
9423 let mut only_carets = true;
9424 let mut same_text_selected = true;
9425 let mut selected_text = None;
9426
9427 let mut selections_iter = selections.iter().peekable();
9428 while let Some(selection) = selections_iter.next() {
9429 if selection.start != selection.end {
9430 only_carets = false;
9431 }
9432
9433 if same_text_selected {
9434 if selected_text.is_none() {
9435 selected_text =
9436 Some(buffer.text_for_range(selection.range()).collect::<String>());
9437 }
9438
9439 if let Some(next_selection) = selections_iter.peek() {
9440 if next_selection.range().len() == selection.range().len() {
9441 let next_selected_text = buffer
9442 .text_for_range(next_selection.range())
9443 .collect::<String>();
9444 if Some(next_selected_text) != selected_text {
9445 same_text_selected = false;
9446 selected_text = None;
9447 }
9448 } else {
9449 same_text_selected = false;
9450 selected_text = None;
9451 }
9452 }
9453 }
9454 }
9455
9456 if only_carets {
9457 for selection in &mut selections {
9458 let word_range = movement::surrounding_word(
9459 display_map,
9460 selection.start.to_display_point(display_map),
9461 );
9462 selection.start = word_range.start.to_offset(display_map, Bias::Left);
9463 selection.end = word_range.end.to_offset(display_map, Bias::Left);
9464 selection.goal = SelectionGoal::None;
9465 selection.reversed = false;
9466 select_next_match_ranges(
9467 self,
9468 selection.start..selection.end,
9469 replace_newest,
9470 autoscroll,
9471 window,
9472 cx,
9473 );
9474 }
9475
9476 if selections.len() == 1 {
9477 let selection = selections
9478 .last()
9479 .expect("ensured that there's only one selection");
9480 let query = buffer
9481 .text_for_range(selection.start..selection.end)
9482 .collect::<String>();
9483 let is_empty = query.is_empty();
9484 let select_state = SelectNextState {
9485 query: AhoCorasick::new(&[query])?,
9486 wordwise: true,
9487 done: is_empty,
9488 };
9489 self.select_next_state = Some(select_state);
9490 } else {
9491 self.select_next_state = None;
9492 }
9493 } else if let Some(selected_text) = selected_text {
9494 self.select_next_state = Some(SelectNextState {
9495 query: AhoCorasick::new(&[selected_text])?,
9496 wordwise: false,
9497 done: false,
9498 });
9499 self.select_next_match_internal(
9500 display_map,
9501 replace_newest,
9502 autoscroll,
9503 window,
9504 cx,
9505 )?;
9506 }
9507 }
9508 Ok(())
9509 }
9510
9511 pub fn select_all_matches(
9512 &mut self,
9513 _action: &SelectAllMatches,
9514 window: &mut Window,
9515 cx: &mut Context<Self>,
9516 ) -> Result<()> {
9517 self.push_to_selection_history();
9518 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9519
9520 self.select_next_match_internal(&display_map, false, None, window, cx)?;
9521 let Some(select_next_state) = self.select_next_state.as_mut() else {
9522 return Ok(());
9523 };
9524 if select_next_state.done {
9525 return Ok(());
9526 }
9527
9528 let mut new_selections = self.selections.all::<usize>(cx);
9529
9530 let buffer = &display_map.buffer_snapshot;
9531 let query_matches = select_next_state
9532 .query
9533 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
9534
9535 for query_match in query_matches {
9536 let query_match = query_match.unwrap(); // can only fail due to I/O
9537 let offset_range = query_match.start()..query_match.end();
9538 let display_range = offset_range.start.to_display_point(&display_map)
9539 ..offset_range.end.to_display_point(&display_map);
9540
9541 if !select_next_state.wordwise
9542 || (!movement::is_inside_word(&display_map, display_range.start)
9543 && !movement::is_inside_word(&display_map, display_range.end))
9544 {
9545 self.selections.change_with(cx, |selections| {
9546 new_selections.push(Selection {
9547 id: selections.new_selection_id(),
9548 start: offset_range.start,
9549 end: offset_range.end,
9550 reversed: false,
9551 goal: SelectionGoal::None,
9552 });
9553 });
9554 }
9555 }
9556
9557 new_selections.sort_by_key(|selection| selection.start);
9558 let mut ix = 0;
9559 while ix + 1 < new_selections.len() {
9560 let current_selection = &new_selections[ix];
9561 let next_selection = &new_selections[ix + 1];
9562 if current_selection.range().overlaps(&next_selection.range()) {
9563 if current_selection.id < next_selection.id {
9564 new_selections.remove(ix + 1);
9565 } else {
9566 new_selections.remove(ix);
9567 }
9568 } else {
9569 ix += 1;
9570 }
9571 }
9572
9573 let reversed = self.selections.oldest::<usize>(cx).reversed;
9574
9575 for selection in new_selections.iter_mut() {
9576 selection.reversed = reversed;
9577 }
9578
9579 select_next_state.done = true;
9580 self.unfold_ranges(
9581 &new_selections
9582 .iter()
9583 .map(|selection| selection.range())
9584 .collect::<Vec<_>>(),
9585 false,
9586 false,
9587 cx,
9588 );
9589 self.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
9590 selections.select(new_selections)
9591 });
9592
9593 Ok(())
9594 }
9595
9596 pub fn select_next(
9597 &mut self,
9598 action: &SelectNext,
9599 window: &mut Window,
9600 cx: &mut Context<Self>,
9601 ) -> Result<()> {
9602 self.push_to_selection_history();
9603 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9604 self.select_next_match_internal(
9605 &display_map,
9606 action.replace_newest,
9607 Some(Autoscroll::newest()),
9608 window,
9609 cx,
9610 )?;
9611 Ok(())
9612 }
9613
9614 pub fn select_previous(
9615 &mut self,
9616 action: &SelectPrevious,
9617 window: &mut Window,
9618 cx: &mut Context<Self>,
9619 ) -> Result<()> {
9620 self.push_to_selection_history();
9621 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9622 let buffer = &display_map.buffer_snapshot;
9623 let mut selections = self.selections.all::<usize>(cx);
9624 if let Some(mut select_prev_state) = self.select_prev_state.take() {
9625 let query = &select_prev_state.query;
9626 if !select_prev_state.done {
9627 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
9628 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
9629 let mut next_selected_range = None;
9630 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
9631 let bytes_before_last_selection =
9632 buffer.reversed_bytes_in_range(0..last_selection.start);
9633 let bytes_after_first_selection =
9634 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
9635 let query_matches = query
9636 .stream_find_iter(bytes_before_last_selection)
9637 .map(|result| (last_selection.start, result))
9638 .chain(
9639 query
9640 .stream_find_iter(bytes_after_first_selection)
9641 .map(|result| (buffer.len(), result)),
9642 );
9643 for (end_offset, query_match) in query_matches {
9644 let query_match = query_match.unwrap(); // can only fail due to I/O
9645 let offset_range =
9646 end_offset - query_match.end()..end_offset - query_match.start();
9647 let display_range = offset_range.start.to_display_point(&display_map)
9648 ..offset_range.end.to_display_point(&display_map);
9649
9650 if !select_prev_state.wordwise
9651 || (!movement::is_inside_word(&display_map, display_range.start)
9652 && !movement::is_inside_word(&display_map, display_range.end))
9653 {
9654 next_selected_range = Some(offset_range);
9655 break;
9656 }
9657 }
9658
9659 if let Some(next_selected_range) = next_selected_range {
9660 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
9661 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
9662 if action.replace_newest {
9663 s.delete(s.newest_anchor().id);
9664 }
9665 s.insert_range(next_selected_range);
9666 });
9667 } else {
9668 select_prev_state.done = true;
9669 }
9670 }
9671
9672 self.select_prev_state = Some(select_prev_state);
9673 } else {
9674 let mut only_carets = true;
9675 let mut same_text_selected = true;
9676 let mut selected_text = None;
9677
9678 let mut selections_iter = selections.iter().peekable();
9679 while let Some(selection) = selections_iter.next() {
9680 if selection.start != selection.end {
9681 only_carets = false;
9682 }
9683
9684 if same_text_selected {
9685 if selected_text.is_none() {
9686 selected_text =
9687 Some(buffer.text_for_range(selection.range()).collect::<String>());
9688 }
9689
9690 if let Some(next_selection) = selections_iter.peek() {
9691 if next_selection.range().len() == selection.range().len() {
9692 let next_selected_text = buffer
9693 .text_for_range(next_selection.range())
9694 .collect::<String>();
9695 if Some(next_selected_text) != selected_text {
9696 same_text_selected = false;
9697 selected_text = None;
9698 }
9699 } else {
9700 same_text_selected = false;
9701 selected_text = None;
9702 }
9703 }
9704 }
9705 }
9706
9707 if only_carets {
9708 for selection in &mut selections {
9709 let word_range = movement::surrounding_word(
9710 &display_map,
9711 selection.start.to_display_point(&display_map),
9712 );
9713 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
9714 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
9715 selection.goal = SelectionGoal::None;
9716 selection.reversed = false;
9717 }
9718 if selections.len() == 1 {
9719 let selection = selections
9720 .last()
9721 .expect("ensured that there's only one selection");
9722 let query = buffer
9723 .text_for_range(selection.start..selection.end)
9724 .collect::<String>();
9725 let is_empty = query.is_empty();
9726 let select_state = SelectNextState {
9727 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
9728 wordwise: true,
9729 done: is_empty,
9730 };
9731 self.select_prev_state = Some(select_state);
9732 } else {
9733 self.select_prev_state = None;
9734 }
9735
9736 self.unfold_ranges(
9737 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
9738 false,
9739 true,
9740 cx,
9741 );
9742 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
9743 s.select(selections);
9744 });
9745 } else if let Some(selected_text) = selected_text {
9746 self.select_prev_state = Some(SelectNextState {
9747 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
9748 wordwise: false,
9749 done: false,
9750 });
9751 self.select_previous(action, window, cx)?;
9752 }
9753 }
9754 Ok(())
9755 }
9756
9757 pub fn toggle_comments(
9758 &mut self,
9759 action: &ToggleComments,
9760 window: &mut Window,
9761 cx: &mut Context<Self>,
9762 ) {
9763 if self.read_only(cx) {
9764 return;
9765 }
9766 let text_layout_details = &self.text_layout_details(window);
9767 self.transact(window, cx, |this, window, cx| {
9768 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
9769 let mut edits = Vec::new();
9770 let mut selection_edit_ranges = Vec::new();
9771 let mut last_toggled_row = None;
9772 let snapshot = this.buffer.read(cx).read(cx);
9773 let empty_str: Arc<str> = Arc::default();
9774 let mut suffixes_inserted = Vec::new();
9775 let ignore_indent = action.ignore_indent;
9776
9777 fn comment_prefix_range(
9778 snapshot: &MultiBufferSnapshot,
9779 row: MultiBufferRow,
9780 comment_prefix: &str,
9781 comment_prefix_whitespace: &str,
9782 ignore_indent: bool,
9783 ) -> Range<Point> {
9784 let indent_size = if ignore_indent {
9785 0
9786 } else {
9787 snapshot.indent_size_for_line(row).len
9788 };
9789
9790 let start = Point::new(row.0, indent_size);
9791
9792 let mut line_bytes = snapshot
9793 .bytes_in_range(start..snapshot.max_point())
9794 .flatten()
9795 .copied();
9796
9797 // If this line currently begins with the line comment prefix, then record
9798 // the range containing the prefix.
9799 if line_bytes
9800 .by_ref()
9801 .take(comment_prefix.len())
9802 .eq(comment_prefix.bytes())
9803 {
9804 // Include any whitespace that matches the comment prefix.
9805 let matching_whitespace_len = line_bytes
9806 .zip(comment_prefix_whitespace.bytes())
9807 .take_while(|(a, b)| a == b)
9808 .count() as u32;
9809 let end = Point::new(
9810 start.row,
9811 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
9812 );
9813 start..end
9814 } else {
9815 start..start
9816 }
9817 }
9818
9819 fn comment_suffix_range(
9820 snapshot: &MultiBufferSnapshot,
9821 row: MultiBufferRow,
9822 comment_suffix: &str,
9823 comment_suffix_has_leading_space: bool,
9824 ) -> Range<Point> {
9825 let end = Point::new(row.0, snapshot.line_len(row));
9826 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
9827
9828 let mut line_end_bytes = snapshot
9829 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
9830 .flatten()
9831 .copied();
9832
9833 let leading_space_len = if suffix_start_column > 0
9834 && line_end_bytes.next() == Some(b' ')
9835 && comment_suffix_has_leading_space
9836 {
9837 1
9838 } else {
9839 0
9840 };
9841
9842 // If this line currently begins with the line comment prefix, then record
9843 // the range containing the prefix.
9844 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
9845 let start = Point::new(end.row, suffix_start_column - leading_space_len);
9846 start..end
9847 } else {
9848 end..end
9849 }
9850 }
9851
9852 // TODO: Handle selections that cross excerpts
9853 for selection in &mut selections {
9854 let start_column = snapshot
9855 .indent_size_for_line(MultiBufferRow(selection.start.row))
9856 .len;
9857 let language = if let Some(language) =
9858 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
9859 {
9860 language
9861 } else {
9862 continue;
9863 };
9864
9865 selection_edit_ranges.clear();
9866
9867 // If multiple selections contain a given row, avoid processing that
9868 // row more than once.
9869 let mut start_row = MultiBufferRow(selection.start.row);
9870 if last_toggled_row == Some(start_row) {
9871 start_row = start_row.next_row();
9872 }
9873 let end_row =
9874 if selection.end.row > selection.start.row && selection.end.column == 0 {
9875 MultiBufferRow(selection.end.row - 1)
9876 } else {
9877 MultiBufferRow(selection.end.row)
9878 };
9879 last_toggled_row = Some(end_row);
9880
9881 if start_row > end_row {
9882 continue;
9883 }
9884
9885 // If the language has line comments, toggle those.
9886 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
9887
9888 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
9889 if ignore_indent {
9890 full_comment_prefixes = full_comment_prefixes
9891 .into_iter()
9892 .map(|s| Arc::from(s.trim_end()))
9893 .collect();
9894 }
9895
9896 if !full_comment_prefixes.is_empty() {
9897 let first_prefix = full_comment_prefixes
9898 .first()
9899 .expect("prefixes is non-empty");
9900 let prefix_trimmed_lengths = full_comment_prefixes
9901 .iter()
9902 .map(|p| p.trim_end_matches(' ').len())
9903 .collect::<SmallVec<[usize; 4]>>();
9904
9905 let mut all_selection_lines_are_comments = true;
9906
9907 for row in start_row.0..=end_row.0 {
9908 let row = MultiBufferRow(row);
9909 if start_row < end_row && snapshot.is_line_blank(row) {
9910 continue;
9911 }
9912
9913 let prefix_range = full_comment_prefixes
9914 .iter()
9915 .zip(prefix_trimmed_lengths.iter().copied())
9916 .map(|(prefix, trimmed_prefix_len)| {
9917 comment_prefix_range(
9918 snapshot.deref(),
9919 row,
9920 &prefix[..trimmed_prefix_len],
9921 &prefix[trimmed_prefix_len..],
9922 ignore_indent,
9923 )
9924 })
9925 .max_by_key(|range| range.end.column - range.start.column)
9926 .expect("prefixes is non-empty");
9927
9928 if prefix_range.is_empty() {
9929 all_selection_lines_are_comments = false;
9930 }
9931
9932 selection_edit_ranges.push(prefix_range);
9933 }
9934
9935 if all_selection_lines_are_comments {
9936 edits.extend(
9937 selection_edit_ranges
9938 .iter()
9939 .cloned()
9940 .map(|range| (range, empty_str.clone())),
9941 );
9942 } else {
9943 let min_column = selection_edit_ranges
9944 .iter()
9945 .map(|range| range.start.column)
9946 .min()
9947 .unwrap_or(0);
9948 edits.extend(selection_edit_ranges.iter().map(|range| {
9949 let position = Point::new(range.start.row, min_column);
9950 (position..position, first_prefix.clone())
9951 }));
9952 }
9953 } else if let Some((full_comment_prefix, comment_suffix)) =
9954 language.block_comment_delimiters()
9955 {
9956 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
9957 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
9958 let prefix_range = comment_prefix_range(
9959 snapshot.deref(),
9960 start_row,
9961 comment_prefix,
9962 comment_prefix_whitespace,
9963 ignore_indent,
9964 );
9965 let suffix_range = comment_suffix_range(
9966 snapshot.deref(),
9967 end_row,
9968 comment_suffix.trim_start_matches(' '),
9969 comment_suffix.starts_with(' '),
9970 );
9971
9972 if prefix_range.is_empty() || suffix_range.is_empty() {
9973 edits.push((
9974 prefix_range.start..prefix_range.start,
9975 full_comment_prefix.clone(),
9976 ));
9977 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
9978 suffixes_inserted.push((end_row, comment_suffix.len()));
9979 } else {
9980 edits.push((prefix_range, empty_str.clone()));
9981 edits.push((suffix_range, empty_str.clone()));
9982 }
9983 } else {
9984 continue;
9985 }
9986 }
9987
9988 drop(snapshot);
9989 this.buffer.update(cx, |buffer, cx| {
9990 buffer.edit(edits, None, cx);
9991 });
9992
9993 // Adjust selections so that they end before any comment suffixes that
9994 // were inserted.
9995 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
9996 let mut selections = this.selections.all::<Point>(cx);
9997 let snapshot = this.buffer.read(cx).read(cx);
9998 for selection in &mut selections {
9999 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
10000 match row.cmp(&MultiBufferRow(selection.end.row)) {
10001 Ordering::Less => {
10002 suffixes_inserted.next();
10003 continue;
10004 }
10005 Ordering::Greater => break,
10006 Ordering::Equal => {
10007 if selection.end.column == snapshot.line_len(row) {
10008 if selection.is_empty() {
10009 selection.start.column -= suffix_len as u32;
10010 }
10011 selection.end.column -= suffix_len as u32;
10012 }
10013 break;
10014 }
10015 }
10016 }
10017 }
10018
10019 drop(snapshot);
10020 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10021 s.select(selections)
10022 });
10023
10024 let selections = this.selections.all::<Point>(cx);
10025 let selections_on_single_row = selections.windows(2).all(|selections| {
10026 selections[0].start.row == selections[1].start.row
10027 && selections[0].end.row == selections[1].end.row
10028 && selections[0].start.row == selections[0].end.row
10029 });
10030 let selections_selecting = selections
10031 .iter()
10032 .any(|selection| selection.start != selection.end);
10033 let advance_downwards = action.advance_downwards
10034 && selections_on_single_row
10035 && !selections_selecting
10036 && !matches!(this.mode, EditorMode::SingleLine { .. });
10037
10038 if advance_downwards {
10039 let snapshot = this.buffer.read(cx).snapshot(cx);
10040
10041 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10042 s.move_cursors_with(|display_snapshot, display_point, _| {
10043 let mut point = display_point.to_point(display_snapshot);
10044 point.row += 1;
10045 point = snapshot.clip_point(point, Bias::Left);
10046 let display_point = point.to_display_point(display_snapshot);
10047 let goal = SelectionGoal::HorizontalPosition(
10048 display_snapshot
10049 .x_for_display_point(display_point, text_layout_details)
10050 .into(),
10051 );
10052 (display_point, goal)
10053 })
10054 });
10055 }
10056 });
10057 }
10058
10059 pub fn select_enclosing_symbol(
10060 &mut self,
10061 _: &SelectEnclosingSymbol,
10062 window: &mut Window,
10063 cx: &mut Context<Self>,
10064 ) {
10065 let buffer = self.buffer.read(cx).snapshot(cx);
10066 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
10067
10068 fn update_selection(
10069 selection: &Selection<usize>,
10070 buffer_snap: &MultiBufferSnapshot,
10071 ) -> Option<Selection<usize>> {
10072 let cursor = selection.head();
10073 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
10074 for symbol in symbols.iter().rev() {
10075 let start = symbol.range.start.to_offset(buffer_snap);
10076 let end = symbol.range.end.to_offset(buffer_snap);
10077 let new_range = start..end;
10078 if start < selection.start || end > selection.end {
10079 return Some(Selection {
10080 id: selection.id,
10081 start: new_range.start,
10082 end: new_range.end,
10083 goal: SelectionGoal::None,
10084 reversed: selection.reversed,
10085 });
10086 }
10087 }
10088 None
10089 }
10090
10091 let mut selected_larger_symbol = false;
10092 let new_selections = old_selections
10093 .iter()
10094 .map(|selection| match update_selection(selection, &buffer) {
10095 Some(new_selection) => {
10096 if new_selection.range() != selection.range() {
10097 selected_larger_symbol = true;
10098 }
10099 new_selection
10100 }
10101 None => selection.clone(),
10102 })
10103 .collect::<Vec<_>>();
10104
10105 if selected_larger_symbol {
10106 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10107 s.select(new_selections);
10108 });
10109 }
10110 }
10111
10112 pub fn select_larger_syntax_node(
10113 &mut self,
10114 _: &SelectLargerSyntaxNode,
10115 window: &mut Window,
10116 cx: &mut Context<Self>,
10117 ) {
10118 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10119 let buffer = self.buffer.read(cx).snapshot(cx);
10120 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
10121
10122 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
10123 let mut selected_larger_node = false;
10124 let new_selections = old_selections
10125 .iter()
10126 .map(|selection| {
10127 let old_range = selection.start..selection.end;
10128 let mut new_range = old_range.clone();
10129 let mut new_node = None;
10130 while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone())
10131 {
10132 new_node = Some(node);
10133 new_range = containing_range;
10134 if !display_map.intersects_fold(new_range.start)
10135 && !display_map.intersects_fold(new_range.end)
10136 {
10137 break;
10138 }
10139 }
10140
10141 if let Some(node) = new_node {
10142 // Log the ancestor, to support using this action as a way to explore TreeSitter
10143 // nodes. Parent and grandparent are also logged because this operation will not
10144 // visit nodes that have the same range as their parent.
10145 log::info!("Node: {node:?}");
10146 let parent = node.parent();
10147 log::info!("Parent: {parent:?}");
10148 let grandparent = parent.and_then(|x| x.parent());
10149 log::info!("Grandparent: {grandparent:?}");
10150 }
10151
10152 selected_larger_node |= new_range != old_range;
10153 Selection {
10154 id: selection.id,
10155 start: new_range.start,
10156 end: new_range.end,
10157 goal: SelectionGoal::None,
10158 reversed: selection.reversed,
10159 }
10160 })
10161 .collect::<Vec<_>>();
10162
10163 if selected_larger_node {
10164 stack.push(old_selections);
10165 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10166 s.select(new_selections);
10167 });
10168 }
10169 self.select_larger_syntax_node_stack = stack;
10170 }
10171
10172 pub fn select_smaller_syntax_node(
10173 &mut self,
10174 _: &SelectSmallerSyntaxNode,
10175 window: &mut Window,
10176 cx: &mut Context<Self>,
10177 ) {
10178 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
10179 if let Some(selections) = stack.pop() {
10180 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10181 s.select(selections.to_vec());
10182 });
10183 }
10184 self.select_larger_syntax_node_stack = stack;
10185 }
10186
10187 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
10188 if !EditorSettings::get_global(cx).gutter.runnables {
10189 self.clear_tasks();
10190 return Task::ready(());
10191 }
10192 let project = self.project.as_ref().map(Entity::downgrade);
10193 cx.spawn_in(window, |this, mut cx| async move {
10194 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
10195 let Some(project) = project.and_then(|p| p.upgrade()) else {
10196 return;
10197 };
10198 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
10199 this.display_map.update(cx, |map, cx| map.snapshot(cx))
10200 }) else {
10201 return;
10202 };
10203
10204 let hide_runnables = project
10205 .update(&mut cx, |project, cx| {
10206 // Do not display any test indicators in non-dev server remote projects.
10207 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
10208 })
10209 .unwrap_or(true);
10210 if hide_runnables {
10211 return;
10212 }
10213 let new_rows =
10214 cx.background_executor()
10215 .spawn({
10216 let snapshot = display_snapshot.clone();
10217 async move {
10218 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
10219 }
10220 })
10221 .await;
10222
10223 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
10224 this.update(&mut cx, |this, _| {
10225 this.clear_tasks();
10226 for (key, value) in rows {
10227 this.insert_tasks(key, value);
10228 }
10229 })
10230 .ok();
10231 })
10232 }
10233 fn fetch_runnable_ranges(
10234 snapshot: &DisplaySnapshot,
10235 range: Range<Anchor>,
10236 ) -> Vec<language::RunnableRange> {
10237 snapshot.buffer_snapshot.runnable_ranges(range).collect()
10238 }
10239
10240 fn runnable_rows(
10241 project: Entity<Project>,
10242 snapshot: DisplaySnapshot,
10243 runnable_ranges: Vec<RunnableRange>,
10244 mut cx: AsyncWindowContext,
10245 ) -> Vec<((BufferId, u32), RunnableTasks)> {
10246 runnable_ranges
10247 .into_iter()
10248 .filter_map(|mut runnable| {
10249 let tasks = cx
10250 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
10251 .ok()?;
10252 if tasks.is_empty() {
10253 return None;
10254 }
10255
10256 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
10257
10258 let row = snapshot
10259 .buffer_snapshot
10260 .buffer_line_for_row(MultiBufferRow(point.row))?
10261 .1
10262 .start
10263 .row;
10264
10265 let context_range =
10266 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
10267 Some((
10268 (runnable.buffer_id, row),
10269 RunnableTasks {
10270 templates: tasks,
10271 offset: MultiBufferOffset(runnable.run_range.start),
10272 context_range,
10273 column: point.column,
10274 extra_variables: runnable.extra_captures,
10275 },
10276 ))
10277 })
10278 .collect()
10279 }
10280
10281 fn templates_with_tags(
10282 project: &Entity<Project>,
10283 runnable: &mut Runnable,
10284 cx: &mut App,
10285 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
10286 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
10287 let (worktree_id, file) = project
10288 .buffer_for_id(runnable.buffer, cx)
10289 .and_then(|buffer| buffer.read(cx).file())
10290 .map(|file| (file.worktree_id(cx), file.clone()))
10291 .unzip();
10292
10293 (
10294 project.task_store().read(cx).task_inventory().cloned(),
10295 worktree_id,
10296 file,
10297 )
10298 });
10299
10300 let tags = mem::take(&mut runnable.tags);
10301 let mut tags: Vec<_> = tags
10302 .into_iter()
10303 .flat_map(|tag| {
10304 let tag = tag.0.clone();
10305 inventory
10306 .as_ref()
10307 .into_iter()
10308 .flat_map(|inventory| {
10309 inventory.read(cx).list_tasks(
10310 file.clone(),
10311 Some(runnable.language.clone()),
10312 worktree_id,
10313 cx,
10314 )
10315 })
10316 .filter(move |(_, template)| {
10317 template.tags.iter().any(|source_tag| source_tag == &tag)
10318 })
10319 })
10320 .sorted_by_key(|(kind, _)| kind.to_owned())
10321 .collect();
10322 if let Some((leading_tag_source, _)) = tags.first() {
10323 // Strongest source wins; if we have worktree tag binding, prefer that to
10324 // global and language bindings;
10325 // if we have a global binding, prefer that to language binding.
10326 let first_mismatch = tags
10327 .iter()
10328 .position(|(tag_source, _)| tag_source != leading_tag_source);
10329 if let Some(index) = first_mismatch {
10330 tags.truncate(index);
10331 }
10332 }
10333
10334 tags
10335 }
10336
10337 pub fn move_to_enclosing_bracket(
10338 &mut self,
10339 _: &MoveToEnclosingBracket,
10340 window: &mut Window,
10341 cx: &mut Context<Self>,
10342 ) {
10343 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10344 s.move_offsets_with(|snapshot, selection| {
10345 let Some(enclosing_bracket_ranges) =
10346 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
10347 else {
10348 return;
10349 };
10350
10351 let mut best_length = usize::MAX;
10352 let mut best_inside = false;
10353 let mut best_in_bracket_range = false;
10354 let mut best_destination = None;
10355 for (open, close) in enclosing_bracket_ranges {
10356 let close = close.to_inclusive();
10357 let length = close.end() - open.start;
10358 let inside = selection.start >= open.end && selection.end <= *close.start();
10359 let in_bracket_range = open.to_inclusive().contains(&selection.head())
10360 || close.contains(&selection.head());
10361
10362 // If best is next to a bracket and current isn't, skip
10363 if !in_bracket_range && best_in_bracket_range {
10364 continue;
10365 }
10366
10367 // Prefer smaller lengths unless best is inside and current isn't
10368 if length > best_length && (best_inside || !inside) {
10369 continue;
10370 }
10371
10372 best_length = length;
10373 best_inside = inside;
10374 best_in_bracket_range = in_bracket_range;
10375 best_destination = Some(
10376 if close.contains(&selection.start) && close.contains(&selection.end) {
10377 if inside {
10378 open.end
10379 } else {
10380 open.start
10381 }
10382 } else if inside {
10383 *close.start()
10384 } else {
10385 *close.end()
10386 },
10387 );
10388 }
10389
10390 if let Some(destination) = best_destination {
10391 selection.collapse_to(destination, SelectionGoal::None);
10392 }
10393 })
10394 });
10395 }
10396
10397 pub fn undo_selection(
10398 &mut self,
10399 _: &UndoSelection,
10400 window: &mut Window,
10401 cx: &mut Context<Self>,
10402 ) {
10403 self.end_selection(window, cx);
10404 self.selection_history.mode = SelectionHistoryMode::Undoing;
10405 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
10406 self.change_selections(None, window, cx, |s| {
10407 s.select_anchors(entry.selections.to_vec())
10408 });
10409 self.select_next_state = entry.select_next_state;
10410 self.select_prev_state = entry.select_prev_state;
10411 self.add_selections_state = entry.add_selections_state;
10412 self.request_autoscroll(Autoscroll::newest(), cx);
10413 }
10414 self.selection_history.mode = SelectionHistoryMode::Normal;
10415 }
10416
10417 pub fn redo_selection(
10418 &mut self,
10419 _: &RedoSelection,
10420 window: &mut Window,
10421 cx: &mut Context<Self>,
10422 ) {
10423 self.end_selection(window, cx);
10424 self.selection_history.mode = SelectionHistoryMode::Redoing;
10425 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
10426 self.change_selections(None, window, cx, |s| {
10427 s.select_anchors(entry.selections.to_vec())
10428 });
10429 self.select_next_state = entry.select_next_state;
10430 self.select_prev_state = entry.select_prev_state;
10431 self.add_selections_state = entry.add_selections_state;
10432 self.request_autoscroll(Autoscroll::newest(), cx);
10433 }
10434 self.selection_history.mode = SelectionHistoryMode::Normal;
10435 }
10436
10437 pub fn expand_excerpts(
10438 &mut self,
10439 action: &ExpandExcerpts,
10440 _: &mut Window,
10441 cx: &mut Context<Self>,
10442 ) {
10443 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
10444 }
10445
10446 pub fn expand_excerpts_down(
10447 &mut self,
10448 action: &ExpandExcerptsDown,
10449 _: &mut Window,
10450 cx: &mut Context<Self>,
10451 ) {
10452 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
10453 }
10454
10455 pub fn expand_excerpts_up(
10456 &mut self,
10457 action: &ExpandExcerptsUp,
10458 _: &mut Window,
10459 cx: &mut Context<Self>,
10460 ) {
10461 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
10462 }
10463
10464 pub fn expand_excerpts_for_direction(
10465 &mut self,
10466 lines: u32,
10467 direction: ExpandExcerptDirection,
10468
10469 cx: &mut Context<Self>,
10470 ) {
10471 let selections = self.selections.disjoint_anchors();
10472
10473 let lines = if lines == 0 {
10474 EditorSettings::get_global(cx).expand_excerpt_lines
10475 } else {
10476 lines
10477 };
10478
10479 self.buffer.update(cx, |buffer, cx| {
10480 let snapshot = buffer.snapshot(cx);
10481 let mut excerpt_ids = selections
10482 .iter()
10483 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
10484 .collect::<Vec<_>>();
10485 excerpt_ids.sort();
10486 excerpt_ids.dedup();
10487 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
10488 })
10489 }
10490
10491 pub fn expand_excerpt(
10492 &mut self,
10493 excerpt: ExcerptId,
10494 direction: ExpandExcerptDirection,
10495 cx: &mut Context<Self>,
10496 ) {
10497 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
10498 self.buffer.update(cx, |buffer, cx| {
10499 buffer.expand_excerpts([excerpt], lines, direction, cx)
10500 })
10501 }
10502
10503 pub fn go_to_singleton_buffer_point(
10504 &mut self,
10505 point: Point,
10506 window: &mut Window,
10507 cx: &mut Context<Self>,
10508 ) {
10509 self.go_to_singleton_buffer_range(point..point, window, cx);
10510 }
10511
10512 pub fn go_to_singleton_buffer_range(
10513 &mut self,
10514 range: Range<Point>,
10515 window: &mut Window,
10516 cx: &mut Context<Self>,
10517 ) {
10518 let multibuffer = self.buffer().read(cx);
10519 let Some(buffer) = multibuffer.as_singleton() else {
10520 return;
10521 };
10522 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
10523 return;
10524 };
10525 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
10526 return;
10527 };
10528 self.change_selections(Some(Autoscroll::center()), window, cx, |s| {
10529 s.select_anchor_ranges([start..end])
10530 });
10531 }
10532
10533 fn go_to_diagnostic(
10534 &mut self,
10535 _: &GoToDiagnostic,
10536 window: &mut Window,
10537 cx: &mut Context<Self>,
10538 ) {
10539 self.go_to_diagnostic_impl(Direction::Next, window, cx)
10540 }
10541
10542 fn go_to_prev_diagnostic(
10543 &mut self,
10544 _: &GoToPrevDiagnostic,
10545 window: &mut Window,
10546 cx: &mut Context<Self>,
10547 ) {
10548 self.go_to_diagnostic_impl(Direction::Prev, window, cx)
10549 }
10550
10551 pub fn go_to_diagnostic_impl(
10552 &mut self,
10553 direction: Direction,
10554 window: &mut Window,
10555 cx: &mut Context<Self>,
10556 ) {
10557 let buffer = self.buffer.read(cx).snapshot(cx);
10558 let selection = self.selections.newest::<usize>(cx);
10559
10560 // If there is an active Diagnostic Popover jump to its diagnostic instead.
10561 if direction == Direction::Next {
10562 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
10563 let Some(buffer_id) = popover.local_diagnostic.range.start.buffer_id else {
10564 return;
10565 };
10566 self.activate_diagnostics(
10567 buffer_id,
10568 popover.local_diagnostic.diagnostic.group_id,
10569 window,
10570 cx,
10571 );
10572 if let Some(active_diagnostics) = self.active_diagnostics.as_ref() {
10573 let primary_range_start = active_diagnostics.primary_range.start;
10574 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10575 let mut new_selection = s.newest_anchor().clone();
10576 new_selection.collapse_to(primary_range_start, SelectionGoal::None);
10577 s.select_anchors(vec![new_selection.clone()]);
10578 });
10579 self.refresh_inline_completion(false, true, window, cx);
10580 }
10581 return;
10582 }
10583 }
10584
10585 let active_group_id = self
10586 .active_diagnostics
10587 .as_ref()
10588 .map(|active_group| active_group.group_id);
10589 let active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
10590 active_diagnostics
10591 .primary_range
10592 .to_offset(&buffer)
10593 .to_inclusive()
10594 });
10595 let search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
10596 if active_primary_range.contains(&selection.head()) {
10597 *active_primary_range.start()
10598 } else {
10599 selection.head()
10600 }
10601 } else {
10602 selection.head()
10603 };
10604
10605 let snapshot = self.snapshot(window, cx);
10606 let primary_diagnostics_before = buffer
10607 .diagnostics_in_range::<usize>(0..search_start)
10608 .filter(|entry| entry.diagnostic.is_primary)
10609 .filter(|entry| entry.range.start != entry.range.end)
10610 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
10611 .filter(|entry| !snapshot.intersects_fold(entry.range.start))
10612 .collect::<Vec<_>>();
10613 let last_same_group_diagnostic_before = active_group_id.and_then(|active_group_id| {
10614 primary_diagnostics_before
10615 .iter()
10616 .position(|entry| entry.diagnostic.group_id == active_group_id)
10617 });
10618
10619 let primary_diagnostics_after = buffer
10620 .diagnostics_in_range::<usize>(search_start..buffer.len())
10621 .filter(|entry| entry.diagnostic.is_primary)
10622 .filter(|entry| entry.range.start != entry.range.end)
10623 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
10624 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
10625 .collect::<Vec<_>>();
10626 let last_same_group_diagnostic_after = active_group_id.and_then(|active_group_id| {
10627 primary_diagnostics_after
10628 .iter()
10629 .enumerate()
10630 .rev()
10631 .find_map(|(i, entry)| {
10632 if entry.diagnostic.group_id == active_group_id {
10633 Some(i)
10634 } else {
10635 None
10636 }
10637 })
10638 });
10639
10640 let next_primary_diagnostic = match direction {
10641 Direction::Prev => primary_diagnostics_before
10642 .iter()
10643 .take(last_same_group_diagnostic_before.unwrap_or(usize::MAX))
10644 .rev()
10645 .next(),
10646 Direction::Next => primary_diagnostics_after
10647 .iter()
10648 .skip(
10649 last_same_group_diagnostic_after
10650 .map(|index| index + 1)
10651 .unwrap_or(0),
10652 )
10653 .next(),
10654 };
10655
10656 // Cycle around to the start of the buffer, potentially moving back to the start of
10657 // the currently active diagnostic.
10658 let cycle_around = || match direction {
10659 Direction::Prev => primary_diagnostics_after
10660 .iter()
10661 .rev()
10662 .chain(primary_diagnostics_before.iter().rev())
10663 .next(),
10664 Direction::Next => primary_diagnostics_before
10665 .iter()
10666 .chain(primary_diagnostics_after.iter())
10667 .next(),
10668 };
10669
10670 if let Some((primary_range, group_id)) = next_primary_diagnostic
10671 .or_else(cycle_around)
10672 .map(|entry| (&entry.range, entry.diagnostic.group_id))
10673 {
10674 let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
10675 return;
10676 };
10677 self.activate_diagnostics(buffer_id, group_id, window, cx);
10678 if self.active_diagnostics.is_some() {
10679 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10680 s.select(vec![Selection {
10681 id: selection.id,
10682 start: primary_range.start,
10683 end: primary_range.start,
10684 reversed: false,
10685 goal: SelectionGoal::None,
10686 }]);
10687 });
10688 self.refresh_inline_completion(false, true, window, cx);
10689 }
10690 }
10691 }
10692
10693 fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
10694 let snapshot = self.snapshot(window, cx);
10695 let selection = self.selections.newest::<Point>(cx);
10696 self.go_to_hunk_after_position(&snapshot, selection.head(), window, cx);
10697 }
10698
10699 fn go_to_hunk_after_position(
10700 &mut self,
10701 snapshot: &EditorSnapshot,
10702 position: Point,
10703 window: &mut Window,
10704 cx: &mut Context<Editor>,
10705 ) -> Option<MultiBufferDiffHunk> {
10706 let mut hunk = snapshot
10707 .buffer_snapshot
10708 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
10709 .find(|hunk| hunk.row_range.start.0 > position.row);
10710 if hunk.is_none() {
10711 hunk = snapshot
10712 .buffer_snapshot
10713 .diff_hunks_in_range(Point::zero()..position)
10714 .find(|hunk| hunk.row_range.end.0 < position.row)
10715 }
10716 if let Some(hunk) = &hunk {
10717 let destination = Point::new(hunk.row_range.start.0, 0);
10718 self.unfold_ranges(&[destination..destination], false, false, cx);
10719 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10720 s.select_ranges(vec![destination..destination]);
10721 });
10722 }
10723
10724 hunk
10725 }
10726
10727 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, window: &mut Window, cx: &mut Context<Self>) {
10728 let snapshot = self.snapshot(window, cx);
10729 let selection = self.selections.newest::<Point>(cx);
10730 self.go_to_hunk_before_position(&snapshot, selection.head(), window, cx);
10731 }
10732
10733 fn go_to_hunk_before_position(
10734 &mut self,
10735 snapshot: &EditorSnapshot,
10736 position: Point,
10737 window: &mut Window,
10738 cx: &mut Context<Editor>,
10739 ) -> Option<MultiBufferDiffHunk> {
10740 let mut hunk = snapshot.buffer_snapshot.diff_hunk_before(position);
10741 if hunk.is_none() {
10742 hunk = snapshot.buffer_snapshot.diff_hunk_before(Point::MAX);
10743 }
10744 if let Some(hunk) = &hunk {
10745 let destination = Point::new(hunk.row_range.start.0, 0);
10746 self.unfold_ranges(&[destination..destination], false, false, cx);
10747 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10748 s.select_ranges(vec![destination..destination]);
10749 });
10750 }
10751
10752 hunk
10753 }
10754
10755 pub fn go_to_definition(
10756 &mut self,
10757 _: &GoToDefinition,
10758 window: &mut Window,
10759 cx: &mut Context<Self>,
10760 ) -> Task<Result<Navigated>> {
10761 let definition =
10762 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
10763 cx.spawn_in(window, |editor, mut cx| async move {
10764 if definition.await? == Navigated::Yes {
10765 return Ok(Navigated::Yes);
10766 }
10767 match editor.update_in(&mut cx, |editor, window, cx| {
10768 editor.find_all_references(&FindAllReferences, window, cx)
10769 })? {
10770 Some(references) => references.await,
10771 None => Ok(Navigated::No),
10772 }
10773 })
10774 }
10775
10776 pub fn go_to_declaration(
10777 &mut self,
10778 _: &GoToDeclaration,
10779 window: &mut Window,
10780 cx: &mut Context<Self>,
10781 ) -> Task<Result<Navigated>> {
10782 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
10783 }
10784
10785 pub fn go_to_declaration_split(
10786 &mut self,
10787 _: &GoToDeclaration,
10788 window: &mut Window,
10789 cx: &mut Context<Self>,
10790 ) -> Task<Result<Navigated>> {
10791 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
10792 }
10793
10794 pub fn go_to_implementation(
10795 &mut self,
10796 _: &GoToImplementation,
10797 window: &mut Window,
10798 cx: &mut Context<Self>,
10799 ) -> Task<Result<Navigated>> {
10800 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
10801 }
10802
10803 pub fn go_to_implementation_split(
10804 &mut self,
10805 _: &GoToImplementationSplit,
10806 window: &mut Window,
10807 cx: &mut Context<Self>,
10808 ) -> Task<Result<Navigated>> {
10809 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
10810 }
10811
10812 pub fn go_to_type_definition(
10813 &mut self,
10814 _: &GoToTypeDefinition,
10815 window: &mut Window,
10816 cx: &mut Context<Self>,
10817 ) -> Task<Result<Navigated>> {
10818 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
10819 }
10820
10821 pub fn go_to_definition_split(
10822 &mut self,
10823 _: &GoToDefinitionSplit,
10824 window: &mut Window,
10825 cx: &mut Context<Self>,
10826 ) -> Task<Result<Navigated>> {
10827 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
10828 }
10829
10830 pub fn go_to_type_definition_split(
10831 &mut self,
10832 _: &GoToTypeDefinitionSplit,
10833 window: &mut Window,
10834 cx: &mut Context<Self>,
10835 ) -> Task<Result<Navigated>> {
10836 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
10837 }
10838
10839 fn go_to_definition_of_kind(
10840 &mut self,
10841 kind: GotoDefinitionKind,
10842 split: bool,
10843 window: &mut Window,
10844 cx: &mut Context<Self>,
10845 ) -> Task<Result<Navigated>> {
10846 let Some(provider) = self.semantics_provider.clone() else {
10847 return Task::ready(Ok(Navigated::No));
10848 };
10849 let head = self.selections.newest::<usize>(cx).head();
10850 let buffer = self.buffer.read(cx);
10851 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
10852 text_anchor
10853 } else {
10854 return Task::ready(Ok(Navigated::No));
10855 };
10856
10857 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
10858 return Task::ready(Ok(Navigated::No));
10859 };
10860
10861 cx.spawn_in(window, |editor, mut cx| async move {
10862 let definitions = definitions.await?;
10863 let navigated = editor
10864 .update_in(&mut cx, |editor, window, cx| {
10865 editor.navigate_to_hover_links(
10866 Some(kind),
10867 definitions
10868 .into_iter()
10869 .filter(|location| {
10870 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
10871 })
10872 .map(HoverLink::Text)
10873 .collect::<Vec<_>>(),
10874 split,
10875 window,
10876 cx,
10877 )
10878 })?
10879 .await?;
10880 anyhow::Ok(navigated)
10881 })
10882 }
10883
10884 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
10885 let selection = self.selections.newest_anchor();
10886 let head = selection.head();
10887 let tail = selection.tail();
10888
10889 let Some((buffer, start_position)) =
10890 self.buffer.read(cx).text_anchor_for_position(head, cx)
10891 else {
10892 return;
10893 };
10894
10895 let end_position = if head != tail {
10896 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
10897 return;
10898 };
10899 Some(pos)
10900 } else {
10901 None
10902 };
10903
10904 let url_finder = cx.spawn_in(window, |editor, mut cx| async move {
10905 let url = if let Some(end_pos) = end_position {
10906 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
10907 } else {
10908 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
10909 };
10910
10911 if let Some(url) = url {
10912 editor.update(&mut cx, |_, cx| {
10913 cx.open_url(&url);
10914 })
10915 } else {
10916 Ok(())
10917 }
10918 });
10919
10920 url_finder.detach();
10921 }
10922
10923 pub fn open_selected_filename(
10924 &mut self,
10925 _: &OpenSelectedFilename,
10926 window: &mut Window,
10927 cx: &mut Context<Self>,
10928 ) {
10929 let Some(workspace) = self.workspace() else {
10930 return;
10931 };
10932
10933 let position = self.selections.newest_anchor().head();
10934
10935 let Some((buffer, buffer_position)) =
10936 self.buffer.read(cx).text_anchor_for_position(position, cx)
10937 else {
10938 return;
10939 };
10940
10941 let project = self.project.clone();
10942
10943 cx.spawn_in(window, |_, mut cx| async move {
10944 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
10945
10946 if let Some((_, path)) = result {
10947 workspace
10948 .update_in(&mut cx, |workspace, window, cx| {
10949 workspace.open_resolved_path(path, window, cx)
10950 })?
10951 .await?;
10952 }
10953 anyhow::Ok(())
10954 })
10955 .detach();
10956 }
10957
10958 pub(crate) fn navigate_to_hover_links(
10959 &mut self,
10960 kind: Option<GotoDefinitionKind>,
10961 mut definitions: Vec<HoverLink>,
10962 split: bool,
10963 window: &mut Window,
10964 cx: &mut Context<Editor>,
10965 ) -> Task<Result<Navigated>> {
10966 // If there is one definition, just open it directly
10967 if definitions.len() == 1 {
10968 let definition = definitions.pop().unwrap();
10969
10970 enum TargetTaskResult {
10971 Location(Option<Location>),
10972 AlreadyNavigated,
10973 }
10974
10975 let target_task = match definition {
10976 HoverLink::Text(link) => {
10977 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
10978 }
10979 HoverLink::InlayHint(lsp_location, server_id) => {
10980 let computation =
10981 self.compute_target_location(lsp_location, server_id, window, cx);
10982 cx.background_executor().spawn(async move {
10983 let location = computation.await?;
10984 Ok(TargetTaskResult::Location(location))
10985 })
10986 }
10987 HoverLink::Url(url) => {
10988 cx.open_url(&url);
10989 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
10990 }
10991 HoverLink::File(path) => {
10992 if let Some(workspace) = self.workspace() {
10993 cx.spawn_in(window, |_, mut cx| async move {
10994 workspace
10995 .update_in(&mut cx, |workspace, window, cx| {
10996 workspace.open_resolved_path(path, window, cx)
10997 })?
10998 .await
10999 .map(|_| TargetTaskResult::AlreadyNavigated)
11000 })
11001 } else {
11002 Task::ready(Ok(TargetTaskResult::Location(None)))
11003 }
11004 }
11005 };
11006 cx.spawn_in(window, |editor, mut cx| async move {
11007 let target = match target_task.await.context("target resolution task")? {
11008 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
11009 TargetTaskResult::Location(None) => return Ok(Navigated::No),
11010 TargetTaskResult::Location(Some(target)) => target,
11011 };
11012
11013 editor.update_in(&mut cx, |editor, window, cx| {
11014 let Some(workspace) = editor.workspace() else {
11015 return Navigated::No;
11016 };
11017 let pane = workspace.read(cx).active_pane().clone();
11018
11019 let range = target.range.to_point(target.buffer.read(cx));
11020 let range = editor.range_for_match(&range);
11021 let range = collapse_multiline_range(range);
11022
11023 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
11024 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
11025 } else {
11026 window.defer(cx, move |window, cx| {
11027 let target_editor: Entity<Self> =
11028 workspace.update(cx, |workspace, cx| {
11029 let pane = if split {
11030 workspace.adjacent_pane(window, cx)
11031 } else {
11032 workspace.active_pane().clone()
11033 };
11034
11035 workspace.open_project_item(
11036 pane,
11037 target.buffer.clone(),
11038 true,
11039 true,
11040 window,
11041 cx,
11042 )
11043 });
11044 target_editor.update(cx, |target_editor, cx| {
11045 // When selecting a definition in a different buffer, disable the nav history
11046 // to avoid creating a history entry at the previous cursor location.
11047 pane.update(cx, |pane, _| pane.disable_history());
11048 target_editor.go_to_singleton_buffer_range(range, window, cx);
11049 pane.update(cx, |pane, _| pane.enable_history());
11050 });
11051 });
11052 }
11053 Navigated::Yes
11054 })
11055 })
11056 } else if !definitions.is_empty() {
11057 cx.spawn_in(window, |editor, mut cx| async move {
11058 let (title, location_tasks, workspace) = editor
11059 .update_in(&mut cx, |editor, window, cx| {
11060 let tab_kind = match kind {
11061 Some(GotoDefinitionKind::Implementation) => "Implementations",
11062 _ => "Definitions",
11063 };
11064 let title = definitions
11065 .iter()
11066 .find_map(|definition| match definition {
11067 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
11068 let buffer = origin.buffer.read(cx);
11069 format!(
11070 "{} for {}",
11071 tab_kind,
11072 buffer
11073 .text_for_range(origin.range.clone())
11074 .collect::<String>()
11075 )
11076 }),
11077 HoverLink::InlayHint(_, _) => None,
11078 HoverLink::Url(_) => None,
11079 HoverLink::File(_) => None,
11080 })
11081 .unwrap_or(tab_kind.to_string());
11082 let location_tasks = definitions
11083 .into_iter()
11084 .map(|definition| match definition {
11085 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
11086 HoverLink::InlayHint(lsp_location, server_id) => editor
11087 .compute_target_location(lsp_location, server_id, window, cx),
11088 HoverLink::Url(_) => Task::ready(Ok(None)),
11089 HoverLink::File(_) => Task::ready(Ok(None)),
11090 })
11091 .collect::<Vec<_>>();
11092 (title, location_tasks, editor.workspace().clone())
11093 })
11094 .context("location tasks preparation")?;
11095
11096 let locations = future::join_all(location_tasks)
11097 .await
11098 .into_iter()
11099 .filter_map(|location| location.transpose())
11100 .collect::<Result<_>>()
11101 .context("location tasks")?;
11102
11103 let Some(workspace) = workspace else {
11104 return Ok(Navigated::No);
11105 };
11106 let opened = workspace
11107 .update_in(&mut cx, |workspace, window, cx| {
11108 Self::open_locations_in_multibuffer(
11109 workspace,
11110 locations,
11111 title,
11112 split,
11113 MultibufferSelectionMode::First,
11114 window,
11115 cx,
11116 )
11117 })
11118 .ok();
11119
11120 anyhow::Ok(Navigated::from_bool(opened.is_some()))
11121 })
11122 } else {
11123 Task::ready(Ok(Navigated::No))
11124 }
11125 }
11126
11127 fn compute_target_location(
11128 &self,
11129 lsp_location: lsp::Location,
11130 server_id: LanguageServerId,
11131 window: &mut Window,
11132 cx: &mut Context<Self>,
11133 ) -> Task<anyhow::Result<Option<Location>>> {
11134 let Some(project) = self.project.clone() else {
11135 return Task::ready(Ok(None));
11136 };
11137
11138 cx.spawn_in(window, move |editor, mut cx| async move {
11139 let location_task = editor.update(&mut cx, |_, cx| {
11140 project.update(cx, |project, cx| {
11141 let language_server_name = project
11142 .language_server_statuses(cx)
11143 .find(|(id, _)| server_id == *id)
11144 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
11145 language_server_name.map(|language_server_name| {
11146 project.open_local_buffer_via_lsp(
11147 lsp_location.uri.clone(),
11148 server_id,
11149 language_server_name,
11150 cx,
11151 )
11152 })
11153 })
11154 })?;
11155 let location = match location_task {
11156 Some(task) => Some({
11157 let target_buffer_handle = task.await.context("open local buffer")?;
11158 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
11159 let target_start = target_buffer
11160 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
11161 let target_end = target_buffer
11162 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
11163 target_buffer.anchor_after(target_start)
11164 ..target_buffer.anchor_before(target_end)
11165 })?;
11166 Location {
11167 buffer: target_buffer_handle,
11168 range,
11169 }
11170 }),
11171 None => None,
11172 };
11173 Ok(location)
11174 })
11175 }
11176
11177 pub fn find_all_references(
11178 &mut self,
11179 _: &FindAllReferences,
11180 window: &mut Window,
11181 cx: &mut Context<Self>,
11182 ) -> Option<Task<Result<Navigated>>> {
11183 let selection = self.selections.newest::<usize>(cx);
11184 let multi_buffer = self.buffer.read(cx);
11185 let head = selection.head();
11186
11187 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
11188 let head_anchor = multi_buffer_snapshot.anchor_at(
11189 head,
11190 if head < selection.tail() {
11191 Bias::Right
11192 } else {
11193 Bias::Left
11194 },
11195 );
11196
11197 match self
11198 .find_all_references_task_sources
11199 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
11200 {
11201 Ok(_) => {
11202 log::info!(
11203 "Ignoring repeated FindAllReferences invocation with the position of already running task"
11204 );
11205 return None;
11206 }
11207 Err(i) => {
11208 self.find_all_references_task_sources.insert(i, head_anchor);
11209 }
11210 }
11211
11212 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
11213 let workspace = self.workspace()?;
11214 let project = workspace.read(cx).project().clone();
11215 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
11216 Some(cx.spawn_in(window, |editor, mut cx| async move {
11217 let _cleanup = defer({
11218 let mut cx = cx.clone();
11219 move || {
11220 let _ = editor.update(&mut cx, |editor, _| {
11221 if let Ok(i) =
11222 editor
11223 .find_all_references_task_sources
11224 .binary_search_by(|anchor| {
11225 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
11226 })
11227 {
11228 editor.find_all_references_task_sources.remove(i);
11229 }
11230 });
11231 }
11232 });
11233
11234 let locations = references.await?;
11235 if locations.is_empty() {
11236 return anyhow::Ok(Navigated::No);
11237 }
11238
11239 workspace.update_in(&mut cx, |workspace, window, cx| {
11240 let title = locations
11241 .first()
11242 .as_ref()
11243 .map(|location| {
11244 let buffer = location.buffer.read(cx);
11245 format!(
11246 "References to `{}`",
11247 buffer
11248 .text_for_range(location.range.clone())
11249 .collect::<String>()
11250 )
11251 })
11252 .unwrap();
11253 Self::open_locations_in_multibuffer(
11254 workspace,
11255 locations,
11256 title,
11257 false,
11258 MultibufferSelectionMode::First,
11259 window,
11260 cx,
11261 );
11262 Navigated::Yes
11263 })
11264 }))
11265 }
11266
11267 /// Opens a multibuffer with the given project locations in it
11268 pub fn open_locations_in_multibuffer(
11269 workspace: &mut Workspace,
11270 mut locations: Vec<Location>,
11271 title: String,
11272 split: bool,
11273 multibuffer_selection_mode: MultibufferSelectionMode,
11274 window: &mut Window,
11275 cx: &mut Context<Workspace>,
11276 ) {
11277 // If there are multiple definitions, open them in a multibuffer
11278 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
11279 let mut locations = locations.into_iter().peekable();
11280 let mut ranges = Vec::new();
11281 let capability = workspace.project().read(cx).capability();
11282
11283 let excerpt_buffer = cx.new(|cx| {
11284 let mut multibuffer = MultiBuffer::new(capability);
11285 while let Some(location) = locations.next() {
11286 let buffer = location.buffer.read(cx);
11287 let mut ranges_for_buffer = Vec::new();
11288 let range = location.range.to_offset(buffer);
11289 ranges_for_buffer.push(range.clone());
11290
11291 while let Some(next_location) = locations.peek() {
11292 if next_location.buffer == location.buffer {
11293 ranges_for_buffer.push(next_location.range.to_offset(buffer));
11294 locations.next();
11295 } else {
11296 break;
11297 }
11298 }
11299
11300 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
11301 ranges.extend(multibuffer.push_excerpts_with_context_lines(
11302 location.buffer.clone(),
11303 ranges_for_buffer,
11304 DEFAULT_MULTIBUFFER_CONTEXT,
11305 cx,
11306 ))
11307 }
11308
11309 multibuffer.with_title(title)
11310 });
11311
11312 let editor = cx.new(|cx| {
11313 Editor::for_multibuffer(
11314 excerpt_buffer,
11315 Some(workspace.project().clone()),
11316 true,
11317 window,
11318 cx,
11319 )
11320 });
11321 editor.update(cx, |editor, cx| {
11322 match multibuffer_selection_mode {
11323 MultibufferSelectionMode::First => {
11324 if let Some(first_range) = ranges.first() {
11325 editor.change_selections(None, window, cx, |selections| {
11326 selections.clear_disjoint();
11327 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
11328 });
11329 }
11330 editor.highlight_background::<Self>(
11331 &ranges,
11332 |theme| theme.editor_highlighted_line_background,
11333 cx,
11334 );
11335 }
11336 MultibufferSelectionMode::All => {
11337 editor.change_selections(None, window, cx, |selections| {
11338 selections.clear_disjoint();
11339 selections.select_anchor_ranges(ranges);
11340 });
11341 }
11342 }
11343 editor.register_buffers_with_language_servers(cx);
11344 });
11345
11346 let item = Box::new(editor);
11347 let item_id = item.item_id();
11348
11349 if split {
11350 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
11351 } else {
11352 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
11353 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
11354 pane.close_current_preview_item(window, cx)
11355 } else {
11356 None
11357 }
11358 });
11359 workspace.add_item_to_active_pane(item.clone(), destination_index, true, window, cx);
11360 }
11361 workspace.active_pane().update(cx, |pane, cx| {
11362 pane.set_preview_item_id(Some(item_id), cx);
11363 });
11364 }
11365
11366 pub fn rename(
11367 &mut self,
11368 _: &Rename,
11369 window: &mut Window,
11370 cx: &mut Context<Self>,
11371 ) -> Option<Task<Result<()>>> {
11372 use language::ToOffset as _;
11373
11374 let provider = self.semantics_provider.clone()?;
11375 let selection = self.selections.newest_anchor().clone();
11376 let (cursor_buffer, cursor_buffer_position) = self
11377 .buffer
11378 .read(cx)
11379 .text_anchor_for_position(selection.head(), cx)?;
11380 let (tail_buffer, cursor_buffer_position_end) = self
11381 .buffer
11382 .read(cx)
11383 .text_anchor_for_position(selection.tail(), cx)?;
11384 if tail_buffer != cursor_buffer {
11385 return None;
11386 }
11387
11388 let snapshot = cursor_buffer.read(cx).snapshot();
11389 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
11390 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
11391 let prepare_rename = provider
11392 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
11393 .unwrap_or_else(|| Task::ready(Ok(None)));
11394 drop(snapshot);
11395
11396 Some(cx.spawn_in(window, |this, mut cx| async move {
11397 let rename_range = if let Some(range) = prepare_rename.await? {
11398 Some(range)
11399 } else {
11400 this.update(&mut cx, |this, cx| {
11401 let buffer = this.buffer.read(cx).snapshot(cx);
11402 let mut buffer_highlights = this
11403 .document_highlights_for_position(selection.head(), &buffer)
11404 .filter(|highlight| {
11405 highlight.start.excerpt_id == selection.head().excerpt_id
11406 && highlight.end.excerpt_id == selection.head().excerpt_id
11407 });
11408 buffer_highlights
11409 .next()
11410 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
11411 })?
11412 };
11413 if let Some(rename_range) = rename_range {
11414 this.update_in(&mut cx, |this, window, cx| {
11415 let snapshot = cursor_buffer.read(cx).snapshot();
11416 let rename_buffer_range = rename_range.to_offset(&snapshot);
11417 let cursor_offset_in_rename_range =
11418 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
11419 let cursor_offset_in_rename_range_end =
11420 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
11421
11422 this.take_rename(false, window, cx);
11423 let buffer = this.buffer.read(cx).read(cx);
11424 let cursor_offset = selection.head().to_offset(&buffer);
11425 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
11426 let rename_end = rename_start + rename_buffer_range.len();
11427 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
11428 let mut old_highlight_id = None;
11429 let old_name: Arc<str> = buffer
11430 .chunks(rename_start..rename_end, true)
11431 .map(|chunk| {
11432 if old_highlight_id.is_none() {
11433 old_highlight_id = chunk.syntax_highlight_id;
11434 }
11435 chunk.text
11436 })
11437 .collect::<String>()
11438 .into();
11439
11440 drop(buffer);
11441
11442 // Position the selection in the rename editor so that it matches the current selection.
11443 this.show_local_selections = false;
11444 let rename_editor = cx.new(|cx| {
11445 let mut editor = Editor::single_line(window, cx);
11446 editor.buffer.update(cx, |buffer, cx| {
11447 buffer.edit([(0..0, old_name.clone())], None, cx)
11448 });
11449 let rename_selection_range = match cursor_offset_in_rename_range
11450 .cmp(&cursor_offset_in_rename_range_end)
11451 {
11452 Ordering::Equal => {
11453 editor.select_all(&SelectAll, window, cx);
11454 return editor;
11455 }
11456 Ordering::Less => {
11457 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
11458 }
11459 Ordering::Greater => {
11460 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
11461 }
11462 };
11463 if rename_selection_range.end > old_name.len() {
11464 editor.select_all(&SelectAll, window, cx);
11465 } else {
11466 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11467 s.select_ranges([rename_selection_range]);
11468 });
11469 }
11470 editor
11471 });
11472 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
11473 if e == &EditorEvent::Focused {
11474 cx.emit(EditorEvent::FocusedIn)
11475 }
11476 })
11477 .detach();
11478
11479 let write_highlights =
11480 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
11481 let read_highlights =
11482 this.clear_background_highlights::<DocumentHighlightRead>(cx);
11483 let ranges = write_highlights
11484 .iter()
11485 .flat_map(|(_, ranges)| ranges.iter())
11486 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
11487 .cloned()
11488 .collect();
11489
11490 this.highlight_text::<Rename>(
11491 ranges,
11492 HighlightStyle {
11493 fade_out: Some(0.6),
11494 ..Default::default()
11495 },
11496 cx,
11497 );
11498 let rename_focus_handle = rename_editor.focus_handle(cx);
11499 window.focus(&rename_focus_handle);
11500 let block_id = this.insert_blocks(
11501 [BlockProperties {
11502 style: BlockStyle::Flex,
11503 placement: BlockPlacement::Below(range.start),
11504 height: 1,
11505 render: Arc::new({
11506 let rename_editor = rename_editor.clone();
11507 move |cx: &mut BlockContext| {
11508 let mut text_style = cx.editor_style.text.clone();
11509 if let Some(highlight_style) = old_highlight_id
11510 .and_then(|h| h.style(&cx.editor_style.syntax))
11511 {
11512 text_style = text_style.highlight(highlight_style);
11513 }
11514 div()
11515 .block_mouse_down()
11516 .pl(cx.anchor_x)
11517 .child(EditorElement::new(
11518 &rename_editor,
11519 EditorStyle {
11520 background: cx.theme().system().transparent,
11521 local_player: cx.editor_style.local_player,
11522 text: text_style,
11523 scrollbar_width: cx.editor_style.scrollbar_width,
11524 syntax: cx.editor_style.syntax.clone(),
11525 status: cx.editor_style.status.clone(),
11526 inlay_hints_style: HighlightStyle {
11527 font_weight: Some(FontWeight::BOLD),
11528 ..make_inlay_hints_style(cx.app)
11529 },
11530 inline_completion_styles: make_suggestion_styles(
11531 cx.app,
11532 ),
11533 ..EditorStyle::default()
11534 },
11535 ))
11536 .into_any_element()
11537 }
11538 }),
11539 priority: 0,
11540 }],
11541 Some(Autoscroll::fit()),
11542 cx,
11543 )[0];
11544 this.pending_rename = Some(RenameState {
11545 range,
11546 old_name,
11547 editor: rename_editor,
11548 block_id,
11549 });
11550 })?;
11551 }
11552
11553 Ok(())
11554 }))
11555 }
11556
11557 pub fn confirm_rename(
11558 &mut self,
11559 _: &ConfirmRename,
11560 window: &mut Window,
11561 cx: &mut Context<Self>,
11562 ) -> Option<Task<Result<()>>> {
11563 let rename = self.take_rename(false, window, cx)?;
11564 let workspace = self.workspace()?.downgrade();
11565 let (buffer, start) = self
11566 .buffer
11567 .read(cx)
11568 .text_anchor_for_position(rename.range.start, cx)?;
11569 let (end_buffer, _) = self
11570 .buffer
11571 .read(cx)
11572 .text_anchor_for_position(rename.range.end, cx)?;
11573 if buffer != end_buffer {
11574 return None;
11575 }
11576
11577 let old_name = rename.old_name;
11578 let new_name = rename.editor.read(cx).text(cx);
11579
11580 let rename = self.semantics_provider.as_ref()?.perform_rename(
11581 &buffer,
11582 start,
11583 new_name.clone(),
11584 cx,
11585 )?;
11586
11587 Some(cx.spawn_in(window, |editor, mut cx| async move {
11588 let project_transaction = rename.await?;
11589 Self::open_project_transaction(
11590 &editor,
11591 workspace,
11592 project_transaction,
11593 format!("Rename: {} → {}", old_name, new_name),
11594 cx.clone(),
11595 )
11596 .await?;
11597
11598 editor.update(&mut cx, |editor, cx| {
11599 editor.refresh_document_highlights(cx);
11600 })?;
11601 Ok(())
11602 }))
11603 }
11604
11605 fn take_rename(
11606 &mut self,
11607 moving_cursor: bool,
11608 window: &mut Window,
11609 cx: &mut Context<Self>,
11610 ) -> Option<RenameState> {
11611 let rename = self.pending_rename.take()?;
11612 if rename.editor.focus_handle(cx).is_focused(window) {
11613 window.focus(&self.focus_handle);
11614 }
11615
11616 self.remove_blocks(
11617 [rename.block_id].into_iter().collect(),
11618 Some(Autoscroll::fit()),
11619 cx,
11620 );
11621 self.clear_highlights::<Rename>(cx);
11622 self.show_local_selections = true;
11623
11624 if moving_cursor {
11625 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
11626 editor.selections.newest::<usize>(cx).head()
11627 });
11628
11629 // Update the selection to match the position of the selection inside
11630 // the rename editor.
11631 let snapshot = self.buffer.read(cx).read(cx);
11632 let rename_range = rename.range.to_offset(&snapshot);
11633 let cursor_in_editor = snapshot
11634 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
11635 .min(rename_range.end);
11636 drop(snapshot);
11637
11638 self.change_selections(None, window, cx, |s| {
11639 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
11640 });
11641 } else {
11642 self.refresh_document_highlights(cx);
11643 }
11644
11645 Some(rename)
11646 }
11647
11648 pub fn pending_rename(&self) -> Option<&RenameState> {
11649 self.pending_rename.as_ref()
11650 }
11651
11652 fn format(
11653 &mut self,
11654 _: &Format,
11655 window: &mut Window,
11656 cx: &mut Context<Self>,
11657 ) -> Option<Task<Result<()>>> {
11658 let project = match &self.project {
11659 Some(project) => project.clone(),
11660 None => return None,
11661 };
11662
11663 Some(self.perform_format(
11664 project,
11665 FormatTrigger::Manual,
11666 FormatTarget::Buffers,
11667 window,
11668 cx,
11669 ))
11670 }
11671
11672 fn format_selections(
11673 &mut self,
11674 _: &FormatSelections,
11675 window: &mut Window,
11676 cx: &mut Context<Self>,
11677 ) -> Option<Task<Result<()>>> {
11678 let project = match &self.project {
11679 Some(project) => project.clone(),
11680 None => return None,
11681 };
11682
11683 let ranges = self
11684 .selections
11685 .all_adjusted(cx)
11686 .into_iter()
11687 .map(|selection| selection.range())
11688 .collect_vec();
11689
11690 Some(self.perform_format(
11691 project,
11692 FormatTrigger::Manual,
11693 FormatTarget::Ranges(ranges),
11694 window,
11695 cx,
11696 ))
11697 }
11698
11699 fn perform_format(
11700 &mut self,
11701 project: Entity<Project>,
11702 trigger: FormatTrigger,
11703 target: FormatTarget,
11704 window: &mut Window,
11705 cx: &mut Context<Self>,
11706 ) -> Task<Result<()>> {
11707 let buffer = self.buffer.clone();
11708 let (buffers, target) = match target {
11709 FormatTarget::Buffers => {
11710 let mut buffers = buffer.read(cx).all_buffers();
11711 if trigger == FormatTrigger::Save {
11712 buffers.retain(|buffer| buffer.read(cx).is_dirty());
11713 }
11714 (buffers, LspFormatTarget::Buffers)
11715 }
11716 FormatTarget::Ranges(selection_ranges) => {
11717 let multi_buffer = buffer.read(cx);
11718 let snapshot = multi_buffer.read(cx);
11719 let mut buffers = HashSet::default();
11720 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
11721 BTreeMap::new();
11722 for selection_range in selection_ranges {
11723 for (buffer, buffer_range, _) in
11724 snapshot.range_to_buffer_ranges(selection_range)
11725 {
11726 let buffer_id = buffer.remote_id();
11727 let start = buffer.anchor_before(buffer_range.start);
11728 let end = buffer.anchor_after(buffer_range.end);
11729 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
11730 buffer_id_to_ranges
11731 .entry(buffer_id)
11732 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
11733 .or_insert_with(|| vec![start..end]);
11734 }
11735 }
11736 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
11737 }
11738 };
11739
11740 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
11741 let format = project.update(cx, |project, cx| {
11742 project.format(buffers, target, true, trigger, cx)
11743 });
11744
11745 cx.spawn_in(window, |_, mut cx| async move {
11746 let transaction = futures::select_biased! {
11747 () = timeout => {
11748 log::warn!("timed out waiting for formatting");
11749 None
11750 }
11751 transaction = format.log_err().fuse() => transaction,
11752 };
11753
11754 buffer
11755 .update(&mut cx, |buffer, cx| {
11756 if let Some(transaction) = transaction {
11757 if !buffer.is_singleton() {
11758 buffer.push_transaction(&transaction.0, cx);
11759 }
11760 }
11761
11762 cx.notify();
11763 })
11764 .ok();
11765
11766 Ok(())
11767 })
11768 }
11769
11770 fn restart_language_server(
11771 &mut self,
11772 _: &RestartLanguageServer,
11773 _: &mut Window,
11774 cx: &mut Context<Self>,
11775 ) {
11776 if let Some(project) = self.project.clone() {
11777 self.buffer.update(cx, |multi_buffer, cx| {
11778 project.update(cx, |project, cx| {
11779 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
11780 });
11781 })
11782 }
11783 }
11784
11785 fn cancel_language_server_work(
11786 workspace: &mut Workspace,
11787 _: &actions::CancelLanguageServerWork,
11788 _: &mut Window,
11789 cx: &mut Context<Workspace>,
11790 ) {
11791 let project = workspace.project();
11792 let buffers = workspace
11793 .active_item(cx)
11794 .and_then(|item| item.act_as::<Editor>(cx))
11795 .map_or(HashSet::default(), |editor| {
11796 editor.read(cx).buffer.read(cx).all_buffers()
11797 });
11798 project.update(cx, |project, cx| {
11799 project.cancel_language_server_work_for_buffers(buffers, cx);
11800 });
11801 }
11802
11803 fn show_character_palette(
11804 &mut self,
11805 _: &ShowCharacterPalette,
11806 window: &mut Window,
11807 _: &mut Context<Self>,
11808 ) {
11809 window.show_character_palette();
11810 }
11811
11812 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
11813 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
11814 let buffer = self.buffer.read(cx).snapshot(cx);
11815 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
11816 let primary_range_end = active_diagnostics.primary_range.end.to_offset(&buffer);
11817 let is_valid = buffer
11818 .diagnostics_in_range::<usize>(primary_range_start..primary_range_end)
11819 .any(|entry| {
11820 entry.diagnostic.is_primary
11821 && !entry.range.is_empty()
11822 && entry.range.start == primary_range_start
11823 && entry.diagnostic.message == active_diagnostics.primary_message
11824 });
11825
11826 if is_valid != active_diagnostics.is_valid {
11827 active_diagnostics.is_valid = is_valid;
11828 let mut new_styles = HashMap::default();
11829 for (block_id, diagnostic) in &active_diagnostics.blocks {
11830 new_styles.insert(
11831 *block_id,
11832 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
11833 );
11834 }
11835 self.display_map.update(cx, |display_map, _cx| {
11836 display_map.replace_blocks(new_styles)
11837 });
11838 }
11839 }
11840 }
11841
11842 fn activate_diagnostics(
11843 &mut self,
11844 buffer_id: BufferId,
11845 group_id: usize,
11846 window: &mut Window,
11847 cx: &mut Context<Self>,
11848 ) {
11849 self.dismiss_diagnostics(cx);
11850 let snapshot = self.snapshot(window, cx);
11851 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
11852 let buffer = self.buffer.read(cx).snapshot(cx);
11853
11854 let mut primary_range = None;
11855 let mut primary_message = None;
11856 let diagnostic_group = buffer
11857 .diagnostic_group(buffer_id, group_id)
11858 .filter_map(|entry| {
11859 let start = entry.range.start;
11860 let end = entry.range.end;
11861 if snapshot.is_line_folded(MultiBufferRow(start.row))
11862 && (start.row == end.row
11863 || snapshot.is_line_folded(MultiBufferRow(end.row)))
11864 {
11865 return None;
11866 }
11867 if entry.diagnostic.is_primary {
11868 primary_range = Some(entry.range.clone());
11869 primary_message = Some(entry.diagnostic.message.clone());
11870 }
11871 Some(entry)
11872 })
11873 .collect::<Vec<_>>();
11874 let primary_range = primary_range?;
11875 let primary_message = primary_message?;
11876
11877 let blocks = display_map
11878 .insert_blocks(
11879 diagnostic_group.iter().map(|entry| {
11880 let diagnostic = entry.diagnostic.clone();
11881 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
11882 BlockProperties {
11883 style: BlockStyle::Fixed,
11884 placement: BlockPlacement::Below(
11885 buffer.anchor_after(entry.range.start),
11886 ),
11887 height: message_height,
11888 render: diagnostic_block_renderer(diagnostic, None, true, true),
11889 priority: 0,
11890 }
11891 }),
11892 cx,
11893 )
11894 .into_iter()
11895 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
11896 .collect();
11897
11898 Some(ActiveDiagnosticGroup {
11899 primary_range: buffer.anchor_before(primary_range.start)
11900 ..buffer.anchor_after(primary_range.end),
11901 primary_message,
11902 group_id,
11903 blocks,
11904 is_valid: true,
11905 })
11906 });
11907 }
11908
11909 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
11910 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
11911 self.display_map.update(cx, |display_map, cx| {
11912 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
11913 });
11914 cx.notify();
11915 }
11916 }
11917
11918 pub fn set_selections_from_remote(
11919 &mut self,
11920 selections: Vec<Selection<Anchor>>,
11921 pending_selection: Option<Selection<Anchor>>,
11922 window: &mut Window,
11923 cx: &mut Context<Self>,
11924 ) {
11925 let old_cursor_position = self.selections.newest_anchor().head();
11926 self.selections.change_with(cx, |s| {
11927 s.select_anchors(selections);
11928 if let Some(pending_selection) = pending_selection {
11929 s.set_pending(pending_selection, SelectMode::Character);
11930 } else {
11931 s.clear_pending();
11932 }
11933 });
11934 self.selections_did_change(false, &old_cursor_position, true, window, cx);
11935 }
11936
11937 fn push_to_selection_history(&mut self) {
11938 self.selection_history.push(SelectionHistoryEntry {
11939 selections: self.selections.disjoint_anchors(),
11940 select_next_state: self.select_next_state.clone(),
11941 select_prev_state: self.select_prev_state.clone(),
11942 add_selections_state: self.add_selections_state.clone(),
11943 });
11944 }
11945
11946 pub fn transact(
11947 &mut self,
11948 window: &mut Window,
11949 cx: &mut Context<Self>,
11950 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
11951 ) -> Option<TransactionId> {
11952 self.start_transaction_at(Instant::now(), window, cx);
11953 update(self, window, cx);
11954 self.end_transaction_at(Instant::now(), cx)
11955 }
11956
11957 pub fn start_transaction_at(
11958 &mut self,
11959 now: Instant,
11960 window: &mut Window,
11961 cx: &mut Context<Self>,
11962 ) {
11963 self.end_selection(window, cx);
11964 if let Some(tx_id) = self
11965 .buffer
11966 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
11967 {
11968 self.selection_history
11969 .insert_transaction(tx_id, self.selections.disjoint_anchors());
11970 cx.emit(EditorEvent::TransactionBegun {
11971 transaction_id: tx_id,
11972 })
11973 }
11974 }
11975
11976 pub fn end_transaction_at(
11977 &mut self,
11978 now: Instant,
11979 cx: &mut Context<Self>,
11980 ) -> Option<TransactionId> {
11981 if let Some(transaction_id) = self
11982 .buffer
11983 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
11984 {
11985 if let Some((_, end_selections)) =
11986 self.selection_history.transaction_mut(transaction_id)
11987 {
11988 *end_selections = Some(self.selections.disjoint_anchors());
11989 } else {
11990 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
11991 }
11992
11993 cx.emit(EditorEvent::Edited { transaction_id });
11994 Some(transaction_id)
11995 } else {
11996 None
11997 }
11998 }
11999
12000 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
12001 if self.selection_mark_mode {
12002 self.change_selections(None, window, cx, |s| {
12003 s.move_with(|_, sel| {
12004 sel.collapse_to(sel.head(), SelectionGoal::None);
12005 });
12006 })
12007 }
12008 self.selection_mark_mode = true;
12009 cx.notify();
12010 }
12011
12012 pub fn swap_selection_ends(
12013 &mut self,
12014 _: &actions::SwapSelectionEnds,
12015 window: &mut Window,
12016 cx: &mut Context<Self>,
12017 ) {
12018 self.change_selections(None, window, cx, |s| {
12019 s.move_with(|_, sel| {
12020 if sel.start != sel.end {
12021 sel.reversed = !sel.reversed
12022 }
12023 });
12024 });
12025 self.request_autoscroll(Autoscroll::newest(), cx);
12026 cx.notify();
12027 }
12028
12029 pub fn toggle_fold(
12030 &mut self,
12031 _: &actions::ToggleFold,
12032 window: &mut Window,
12033 cx: &mut Context<Self>,
12034 ) {
12035 if self.is_singleton(cx) {
12036 let selection = self.selections.newest::<Point>(cx);
12037
12038 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12039 let range = if selection.is_empty() {
12040 let point = selection.head().to_display_point(&display_map);
12041 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
12042 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
12043 .to_point(&display_map);
12044 start..end
12045 } else {
12046 selection.range()
12047 };
12048 if display_map.folds_in_range(range).next().is_some() {
12049 self.unfold_lines(&Default::default(), window, cx)
12050 } else {
12051 self.fold(&Default::default(), window, cx)
12052 }
12053 } else {
12054 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12055 let buffer_ids: HashSet<_> = multi_buffer_snapshot
12056 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
12057 .map(|(snapshot, _, _)| snapshot.remote_id())
12058 .collect();
12059
12060 for buffer_id in buffer_ids {
12061 if self.is_buffer_folded(buffer_id, cx) {
12062 self.unfold_buffer(buffer_id, cx);
12063 } else {
12064 self.fold_buffer(buffer_id, cx);
12065 }
12066 }
12067 }
12068 }
12069
12070 pub fn toggle_fold_recursive(
12071 &mut self,
12072 _: &actions::ToggleFoldRecursive,
12073 window: &mut Window,
12074 cx: &mut Context<Self>,
12075 ) {
12076 let selection = self.selections.newest::<Point>(cx);
12077
12078 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12079 let range = if selection.is_empty() {
12080 let point = selection.head().to_display_point(&display_map);
12081 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
12082 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
12083 .to_point(&display_map);
12084 start..end
12085 } else {
12086 selection.range()
12087 };
12088 if display_map.folds_in_range(range).next().is_some() {
12089 self.unfold_recursive(&Default::default(), window, cx)
12090 } else {
12091 self.fold_recursive(&Default::default(), window, cx)
12092 }
12093 }
12094
12095 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
12096 if self.is_singleton(cx) {
12097 let mut to_fold = Vec::new();
12098 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12099 let selections = self.selections.all_adjusted(cx);
12100
12101 for selection in selections {
12102 let range = selection.range().sorted();
12103 let buffer_start_row = range.start.row;
12104
12105 if range.start.row != range.end.row {
12106 let mut found = false;
12107 let mut row = range.start.row;
12108 while row <= range.end.row {
12109 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
12110 {
12111 found = true;
12112 row = crease.range().end.row + 1;
12113 to_fold.push(crease);
12114 } else {
12115 row += 1
12116 }
12117 }
12118 if found {
12119 continue;
12120 }
12121 }
12122
12123 for row in (0..=range.start.row).rev() {
12124 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
12125 if crease.range().end.row >= buffer_start_row {
12126 to_fold.push(crease);
12127 if row <= range.start.row {
12128 break;
12129 }
12130 }
12131 }
12132 }
12133 }
12134
12135 self.fold_creases(to_fold, true, window, cx);
12136 } else {
12137 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12138
12139 let buffer_ids: HashSet<_> = multi_buffer_snapshot
12140 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
12141 .map(|(snapshot, _, _)| snapshot.remote_id())
12142 .collect();
12143 for buffer_id in buffer_ids {
12144 self.fold_buffer(buffer_id, cx);
12145 }
12146 }
12147 }
12148
12149 fn fold_at_level(
12150 &mut self,
12151 fold_at: &FoldAtLevel,
12152 window: &mut Window,
12153 cx: &mut Context<Self>,
12154 ) {
12155 if !self.buffer.read(cx).is_singleton() {
12156 return;
12157 }
12158
12159 let fold_at_level = fold_at.0;
12160 let snapshot = self.buffer.read(cx).snapshot(cx);
12161 let mut to_fold = Vec::new();
12162 let mut stack = vec![(0, snapshot.max_row().0, 1)];
12163
12164 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
12165 while start_row < end_row {
12166 match self
12167 .snapshot(window, cx)
12168 .crease_for_buffer_row(MultiBufferRow(start_row))
12169 {
12170 Some(crease) => {
12171 let nested_start_row = crease.range().start.row + 1;
12172 let nested_end_row = crease.range().end.row;
12173
12174 if current_level < fold_at_level {
12175 stack.push((nested_start_row, nested_end_row, current_level + 1));
12176 } else if current_level == fold_at_level {
12177 to_fold.push(crease);
12178 }
12179
12180 start_row = nested_end_row + 1;
12181 }
12182 None => start_row += 1,
12183 }
12184 }
12185 }
12186
12187 self.fold_creases(to_fold, true, window, cx);
12188 }
12189
12190 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
12191 if self.buffer.read(cx).is_singleton() {
12192 let mut fold_ranges = Vec::new();
12193 let snapshot = self.buffer.read(cx).snapshot(cx);
12194
12195 for row in 0..snapshot.max_row().0 {
12196 if let Some(foldable_range) = self
12197 .snapshot(window, cx)
12198 .crease_for_buffer_row(MultiBufferRow(row))
12199 {
12200 fold_ranges.push(foldable_range);
12201 }
12202 }
12203
12204 self.fold_creases(fold_ranges, true, window, cx);
12205 } else {
12206 self.toggle_fold_multiple_buffers = cx.spawn_in(window, |editor, mut cx| async move {
12207 editor
12208 .update_in(&mut cx, |editor, _, cx| {
12209 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
12210 editor.fold_buffer(buffer_id, cx);
12211 }
12212 })
12213 .ok();
12214 });
12215 }
12216 }
12217
12218 pub fn fold_function_bodies(
12219 &mut self,
12220 _: &actions::FoldFunctionBodies,
12221 window: &mut Window,
12222 cx: &mut Context<Self>,
12223 ) {
12224 let snapshot = self.buffer.read(cx).snapshot(cx);
12225
12226 let ranges = snapshot
12227 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
12228 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
12229 .collect::<Vec<_>>();
12230
12231 let creases = ranges
12232 .into_iter()
12233 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
12234 .collect();
12235
12236 self.fold_creases(creases, true, window, cx);
12237 }
12238
12239 pub fn fold_recursive(
12240 &mut self,
12241 _: &actions::FoldRecursive,
12242 window: &mut Window,
12243 cx: &mut Context<Self>,
12244 ) {
12245 let mut to_fold = Vec::new();
12246 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12247 let selections = self.selections.all_adjusted(cx);
12248
12249 for selection in selections {
12250 let range = selection.range().sorted();
12251 let buffer_start_row = range.start.row;
12252
12253 if range.start.row != range.end.row {
12254 let mut found = false;
12255 for row in range.start.row..=range.end.row {
12256 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
12257 found = true;
12258 to_fold.push(crease);
12259 }
12260 }
12261 if found {
12262 continue;
12263 }
12264 }
12265
12266 for row in (0..=range.start.row).rev() {
12267 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
12268 if crease.range().end.row >= buffer_start_row {
12269 to_fold.push(crease);
12270 } else {
12271 break;
12272 }
12273 }
12274 }
12275 }
12276
12277 self.fold_creases(to_fold, true, window, cx);
12278 }
12279
12280 pub fn fold_at(&mut self, fold_at: &FoldAt, window: &mut Window, cx: &mut Context<Self>) {
12281 let buffer_row = fold_at.buffer_row;
12282 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12283
12284 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
12285 let autoscroll = self
12286 .selections
12287 .all::<Point>(cx)
12288 .iter()
12289 .any(|selection| crease.range().overlaps(&selection.range()));
12290
12291 self.fold_creases(vec![crease], autoscroll, window, cx);
12292 }
12293 }
12294
12295 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
12296 if self.is_singleton(cx) {
12297 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12298 let buffer = &display_map.buffer_snapshot;
12299 let selections = self.selections.all::<Point>(cx);
12300 let ranges = selections
12301 .iter()
12302 .map(|s| {
12303 let range = s.display_range(&display_map).sorted();
12304 let mut start = range.start.to_point(&display_map);
12305 let mut end = range.end.to_point(&display_map);
12306 start.column = 0;
12307 end.column = buffer.line_len(MultiBufferRow(end.row));
12308 start..end
12309 })
12310 .collect::<Vec<_>>();
12311
12312 self.unfold_ranges(&ranges, true, true, cx);
12313 } else {
12314 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12315 let buffer_ids: HashSet<_> = multi_buffer_snapshot
12316 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
12317 .map(|(snapshot, _, _)| snapshot.remote_id())
12318 .collect();
12319 for buffer_id in buffer_ids {
12320 self.unfold_buffer(buffer_id, cx);
12321 }
12322 }
12323 }
12324
12325 pub fn unfold_recursive(
12326 &mut self,
12327 _: &UnfoldRecursive,
12328 _window: &mut Window,
12329 cx: &mut Context<Self>,
12330 ) {
12331 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12332 let selections = self.selections.all::<Point>(cx);
12333 let ranges = selections
12334 .iter()
12335 .map(|s| {
12336 let mut range = s.display_range(&display_map).sorted();
12337 *range.start.column_mut() = 0;
12338 *range.end.column_mut() = display_map.line_len(range.end.row());
12339 let start = range.start.to_point(&display_map);
12340 let end = range.end.to_point(&display_map);
12341 start..end
12342 })
12343 .collect::<Vec<_>>();
12344
12345 self.unfold_ranges(&ranges, true, true, cx);
12346 }
12347
12348 pub fn unfold_at(
12349 &mut self,
12350 unfold_at: &UnfoldAt,
12351 _window: &mut Window,
12352 cx: &mut Context<Self>,
12353 ) {
12354 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12355
12356 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
12357 ..Point::new(
12358 unfold_at.buffer_row.0,
12359 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
12360 );
12361
12362 let autoscroll = self
12363 .selections
12364 .all::<Point>(cx)
12365 .iter()
12366 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
12367
12368 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
12369 }
12370
12371 pub fn unfold_all(
12372 &mut self,
12373 _: &actions::UnfoldAll,
12374 _window: &mut Window,
12375 cx: &mut Context<Self>,
12376 ) {
12377 if self.buffer.read(cx).is_singleton() {
12378 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12379 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
12380 } else {
12381 self.toggle_fold_multiple_buffers = cx.spawn(|editor, mut cx| async move {
12382 editor
12383 .update(&mut cx, |editor, cx| {
12384 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
12385 editor.unfold_buffer(buffer_id, cx);
12386 }
12387 })
12388 .ok();
12389 });
12390 }
12391 }
12392
12393 pub fn fold_selected_ranges(
12394 &mut self,
12395 _: &FoldSelectedRanges,
12396 window: &mut Window,
12397 cx: &mut Context<Self>,
12398 ) {
12399 let selections = self.selections.all::<Point>(cx);
12400 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12401 let line_mode = self.selections.line_mode;
12402 let ranges = selections
12403 .into_iter()
12404 .map(|s| {
12405 if line_mode {
12406 let start = Point::new(s.start.row, 0);
12407 let end = Point::new(
12408 s.end.row,
12409 display_map
12410 .buffer_snapshot
12411 .line_len(MultiBufferRow(s.end.row)),
12412 );
12413 Crease::simple(start..end, display_map.fold_placeholder.clone())
12414 } else {
12415 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
12416 }
12417 })
12418 .collect::<Vec<_>>();
12419 self.fold_creases(ranges, true, window, cx);
12420 }
12421
12422 pub fn fold_ranges<T: ToOffset + Clone>(
12423 &mut self,
12424 ranges: Vec<Range<T>>,
12425 auto_scroll: bool,
12426 window: &mut Window,
12427 cx: &mut Context<Self>,
12428 ) {
12429 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12430 let ranges = ranges
12431 .into_iter()
12432 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
12433 .collect::<Vec<_>>();
12434 self.fold_creases(ranges, auto_scroll, window, cx);
12435 }
12436
12437 pub fn fold_creases<T: ToOffset + Clone>(
12438 &mut self,
12439 creases: Vec<Crease<T>>,
12440 auto_scroll: bool,
12441 window: &mut Window,
12442 cx: &mut Context<Self>,
12443 ) {
12444 if creases.is_empty() {
12445 return;
12446 }
12447
12448 let mut buffers_affected = HashSet::default();
12449 let multi_buffer = self.buffer().read(cx);
12450 for crease in &creases {
12451 if let Some((_, buffer, _)) =
12452 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
12453 {
12454 buffers_affected.insert(buffer.read(cx).remote_id());
12455 };
12456 }
12457
12458 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
12459
12460 if auto_scroll {
12461 self.request_autoscroll(Autoscroll::fit(), cx);
12462 }
12463
12464 cx.notify();
12465
12466 if let Some(active_diagnostics) = self.active_diagnostics.take() {
12467 // Clear diagnostics block when folding a range that contains it.
12468 let snapshot = self.snapshot(window, cx);
12469 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
12470 drop(snapshot);
12471 self.active_diagnostics = Some(active_diagnostics);
12472 self.dismiss_diagnostics(cx);
12473 } else {
12474 self.active_diagnostics = Some(active_diagnostics);
12475 }
12476 }
12477
12478 self.scrollbar_marker_state.dirty = true;
12479 }
12480
12481 /// Removes any folds whose ranges intersect any of the given ranges.
12482 pub fn unfold_ranges<T: ToOffset + Clone>(
12483 &mut self,
12484 ranges: &[Range<T>],
12485 inclusive: bool,
12486 auto_scroll: bool,
12487 cx: &mut Context<Self>,
12488 ) {
12489 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
12490 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
12491 });
12492 }
12493
12494 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
12495 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
12496 return;
12497 }
12498 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
12499 self.display_map
12500 .update(cx, |display_map, cx| display_map.fold_buffer(buffer_id, cx));
12501 cx.emit(EditorEvent::BufferFoldToggled {
12502 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
12503 folded: true,
12504 });
12505 cx.notify();
12506 }
12507
12508 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
12509 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
12510 return;
12511 }
12512 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
12513 self.display_map.update(cx, |display_map, cx| {
12514 display_map.unfold_buffer(buffer_id, cx);
12515 });
12516 cx.emit(EditorEvent::BufferFoldToggled {
12517 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
12518 folded: false,
12519 });
12520 cx.notify();
12521 }
12522
12523 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
12524 self.display_map.read(cx).is_buffer_folded(buffer)
12525 }
12526
12527 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
12528 self.display_map.read(cx).folded_buffers()
12529 }
12530
12531 /// Removes any folds with the given ranges.
12532 pub fn remove_folds_with_type<T: ToOffset + Clone>(
12533 &mut self,
12534 ranges: &[Range<T>],
12535 type_id: TypeId,
12536 auto_scroll: bool,
12537 cx: &mut Context<Self>,
12538 ) {
12539 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
12540 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
12541 });
12542 }
12543
12544 fn remove_folds_with<T: ToOffset + Clone>(
12545 &mut self,
12546 ranges: &[Range<T>],
12547 auto_scroll: bool,
12548 cx: &mut Context<Self>,
12549 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
12550 ) {
12551 if ranges.is_empty() {
12552 return;
12553 }
12554
12555 let mut buffers_affected = HashSet::default();
12556 let multi_buffer = self.buffer().read(cx);
12557 for range in ranges {
12558 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
12559 buffers_affected.insert(buffer.read(cx).remote_id());
12560 };
12561 }
12562
12563 self.display_map.update(cx, update);
12564
12565 if auto_scroll {
12566 self.request_autoscroll(Autoscroll::fit(), cx);
12567 }
12568
12569 cx.notify();
12570 self.scrollbar_marker_state.dirty = true;
12571 self.active_indent_guides_state.dirty = true;
12572 }
12573
12574 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
12575 self.display_map.read(cx).fold_placeholder.clone()
12576 }
12577
12578 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
12579 self.buffer.update(cx, |buffer, cx| {
12580 buffer.set_all_diff_hunks_expanded(cx);
12581 });
12582 }
12583
12584 pub fn set_distinguish_unstaged_diff_hunks(&mut self) {
12585 self.distinguish_unstaged_diff_hunks = true;
12586 }
12587
12588 pub fn expand_all_diff_hunks(
12589 &mut self,
12590 _: &ExpandAllHunkDiffs,
12591 _window: &mut Window,
12592 cx: &mut Context<Self>,
12593 ) {
12594 self.buffer.update(cx, |buffer, cx| {
12595 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
12596 });
12597 }
12598
12599 pub fn toggle_selected_diff_hunks(
12600 &mut self,
12601 _: &ToggleSelectedDiffHunks,
12602 _window: &mut Window,
12603 cx: &mut Context<Self>,
12604 ) {
12605 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
12606 self.toggle_diff_hunks_in_ranges(ranges, cx);
12607 }
12608
12609 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
12610 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
12611 self.buffer
12612 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
12613 }
12614
12615 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
12616 self.buffer.update(cx, |buffer, cx| {
12617 let ranges = vec![Anchor::min()..Anchor::max()];
12618 if !buffer.all_diff_hunks_expanded()
12619 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
12620 {
12621 buffer.collapse_diff_hunks(ranges, cx);
12622 true
12623 } else {
12624 false
12625 }
12626 })
12627 }
12628
12629 fn toggle_diff_hunks_in_ranges(
12630 &mut self,
12631 ranges: Vec<Range<Anchor>>,
12632 cx: &mut Context<'_, Editor>,
12633 ) {
12634 self.buffer.update(cx, |buffer, cx| {
12635 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
12636 buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
12637 })
12638 }
12639
12640 fn toggle_diff_hunks_in_ranges_narrow(
12641 &mut self,
12642 ranges: Vec<Range<Anchor>>,
12643 cx: &mut Context<'_, Editor>,
12644 ) {
12645 self.buffer.update(cx, |buffer, cx| {
12646 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
12647 buffer.expand_or_collapse_diff_hunks_narrow(ranges, expand, cx);
12648 })
12649 }
12650
12651 pub(crate) fn apply_all_diff_hunks(
12652 &mut self,
12653 _: &ApplyAllDiffHunks,
12654 window: &mut Window,
12655 cx: &mut Context<Self>,
12656 ) {
12657 let buffers = self.buffer.read(cx).all_buffers();
12658 for branch_buffer in buffers {
12659 branch_buffer.update(cx, |branch_buffer, cx| {
12660 branch_buffer.merge_into_base(Vec::new(), cx);
12661 });
12662 }
12663
12664 if let Some(project) = self.project.clone() {
12665 self.save(true, project, window, cx).detach_and_log_err(cx);
12666 }
12667 }
12668
12669 pub(crate) fn apply_selected_diff_hunks(
12670 &mut self,
12671 _: &ApplyDiffHunk,
12672 window: &mut Window,
12673 cx: &mut Context<Self>,
12674 ) {
12675 let snapshot = self.snapshot(window, cx);
12676 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx).into_iter());
12677 let mut ranges_by_buffer = HashMap::default();
12678 self.transact(window, cx, |editor, _window, cx| {
12679 for hunk in hunks {
12680 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
12681 ranges_by_buffer
12682 .entry(buffer.clone())
12683 .or_insert_with(Vec::new)
12684 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
12685 }
12686 }
12687
12688 for (buffer, ranges) in ranges_by_buffer {
12689 buffer.update(cx, |buffer, cx| {
12690 buffer.merge_into_base(ranges, cx);
12691 });
12692 }
12693 });
12694
12695 if let Some(project) = self.project.clone() {
12696 self.save(true, project, window, cx).detach_and_log_err(cx);
12697 }
12698 }
12699
12700 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
12701 if hovered != self.gutter_hovered {
12702 self.gutter_hovered = hovered;
12703 cx.notify();
12704 }
12705 }
12706
12707 pub fn insert_blocks(
12708 &mut self,
12709 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
12710 autoscroll: Option<Autoscroll>,
12711 cx: &mut Context<Self>,
12712 ) -> Vec<CustomBlockId> {
12713 let blocks = self
12714 .display_map
12715 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
12716 if let Some(autoscroll) = autoscroll {
12717 self.request_autoscroll(autoscroll, cx);
12718 }
12719 cx.notify();
12720 blocks
12721 }
12722
12723 pub fn resize_blocks(
12724 &mut self,
12725 heights: HashMap<CustomBlockId, u32>,
12726 autoscroll: Option<Autoscroll>,
12727 cx: &mut Context<Self>,
12728 ) {
12729 self.display_map
12730 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
12731 if let Some(autoscroll) = autoscroll {
12732 self.request_autoscroll(autoscroll, cx);
12733 }
12734 cx.notify();
12735 }
12736
12737 pub fn replace_blocks(
12738 &mut self,
12739 renderers: HashMap<CustomBlockId, RenderBlock>,
12740 autoscroll: Option<Autoscroll>,
12741 cx: &mut Context<Self>,
12742 ) {
12743 self.display_map
12744 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
12745 if let Some(autoscroll) = autoscroll {
12746 self.request_autoscroll(autoscroll, cx);
12747 }
12748 cx.notify();
12749 }
12750
12751 pub fn remove_blocks(
12752 &mut self,
12753 block_ids: HashSet<CustomBlockId>,
12754 autoscroll: Option<Autoscroll>,
12755 cx: &mut Context<Self>,
12756 ) {
12757 self.display_map.update(cx, |display_map, cx| {
12758 display_map.remove_blocks(block_ids, cx)
12759 });
12760 if let Some(autoscroll) = autoscroll {
12761 self.request_autoscroll(autoscroll, cx);
12762 }
12763 cx.notify();
12764 }
12765
12766 pub fn row_for_block(
12767 &self,
12768 block_id: CustomBlockId,
12769 cx: &mut Context<Self>,
12770 ) -> Option<DisplayRow> {
12771 self.display_map
12772 .update(cx, |map, cx| map.row_for_block(block_id, cx))
12773 }
12774
12775 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
12776 self.focused_block = Some(focused_block);
12777 }
12778
12779 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
12780 self.focused_block.take()
12781 }
12782
12783 pub fn insert_creases(
12784 &mut self,
12785 creases: impl IntoIterator<Item = Crease<Anchor>>,
12786 cx: &mut Context<Self>,
12787 ) -> Vec<CreaseId> {
12788 self.display_map
12789 .update(cx, |map, cx| map.insert_creases(creases, cx))
12790 }
12791
12792 pub fn remove_creases(
12793 &mut self,
12794 ids: impl IntoIterator<Item = CreaseId>,
12795 cx: &mut Context<Self>,
12796 ) {
12797 self.display_map
12798 .update(cx, |map, cx| map.remove_creases(ids, cx));
12799 }
12800
12801 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
12802 self.display_map
12803 .update(cx, |map, cx| map.snapshot(cx))
12804 .longest_row()
12805 }
12806
12807 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
12808 self.display_map
12809 .update(cx, |map, cx| map.snapshot(cx))
12810 .max_point()
12811 }
12812
12813 pub fn text(&self, cx: &App) -> String {
12814 self.buffer.read(cx).read(cx).text()
12815 }
12816
12817 pub fn is_empty(&self, cx: &App) -> bool {
12818 self.buffer.read(cx).read(cx).is_empty()
12819 }
12820
12821 pub fn text_option(&self, cx: &App) -> Option<String> {
12822 let text = self.text(cx);
12823 let text = text.trim();
12824
12825 if text.is_empty() {
12826 return None;
12827 }
12828
12829 Some(text.to_string())
12830 }
12831
12832 pub fn set_text(
12833 &mut self,
12834 text: impl Into<Arc<str>>,
12835 window: &mut Window,
12836 cx: &mut Context<Self>,
12837 ) {
12838 self.transact(window, cx, |this, _, cx| {
12839 this.buffer
12840 .read(cx)
12841 .as_singleton()
12842 .expect("you can only call set_text on editors for singleton buffers")
12843 .update(cx, |buffer, cx| buffer.set_text(text, cx));
12844 });
12845 }
12846
12847 pub fn display_text(&self, cx: &mut App) -> String {
12848 self.display_map
12849 .update(cx, |map, cx| map.snapshot(cx))
12850 .text()
12851 }
12852
12853 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
12854 let mut wrap_guides = smallvec::smallvec![];
12855
12856 if self.show_wrap_guides == Some(false) {
12857 return wrap_guides;
12858 }
12859
12860 let settings = self.buffer.read(cx).settings_at(0, cx);
12861 if settings.show_wrap_guides {
12862 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
12863 wrap_guides.push((soft_wrap as usize, true));
12864 } else if let SoftWrap::Bounded(soft_wrap) = self.soft_wrap_mode(cx) {
12865 wrap_guides.push((soft_wrap as usize, true));
12866 }
12867 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
12868 }
12869
12870 wrap_guides
12871 }
12872
12873 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
12874 let settings = self.buffer.read(cx).settings_at(0, cx);
12875 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
12876 match mode {
12877 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
12878 SoftWrap::None
12879 }
12880 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
12881 language_settings::SoftWrap::PreferredLineLength => {
12882 SoftWrap::Column(settings.preferred_line_length)
12883 }
12884 language_settings::SoftWrap::Bounded => {
12885 SoftWrap::Bounded(settings.preferred_line_length)
12886 }
12887 }
12888 }
12889
12890 pub fn set_soft_wrap_mode(
12891 &mut self,
12892 mode: language_settings::SoftWrap,
12893
12894 cx: &mut Context<Self>,
12895 ) {
12896 self.soft_wrap_mode_override = Some(mode);
12897 cx.notify();
12898 }
12899
12900 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
12901 self.text_style_refinement = Some(style);
12902 }
12903
12904 /// called by the Element so we know what style we were most recently rendered with.
12905 pub(crate) fn set_style(
12906 &mut self,
12907 style: EditorStyle,
12908 window: &mut Window,
12909 cx: &mut Context<Self>,
12910 ) {
12911 let rem_size = window.rem_size();
12912 self.display_map.update(cx, |map, cx| {
12913 map.set_font(
12914 style.text.font(),
12915 style.text.font_size.to_pixels(rem_size),
12916 cx,
12917 )
12918 });
12919 self.style = Some(style);
12920 }
12921
12922 pub fn style(&self) -> Option<&EditorStyle> {
12923 self.style.as_ref()
12924 }
12925
12926 // Called by the element. This method is not designed to be called outside of the editor
12927 // element's layout code because it does not notify when rewrapping is computed synchronously.
12928 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
12929 self.display_map
12930 .update(cx, |map, cx| map.set_wrap_width(width, cx))
12931 }
12932
12933 pub fn set_soft_wrap(&mut self) {
12934 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
12935 }
12936
12937 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
12938 if self.soft_wrap_mode_override.is_some() {
12939 self.soft_wrap_mode_override.take();
12940 } else {
12941 let soft_wrap = match self.soft_wrap_mode(cx) {
12942 SoftWrap::GitDiff => return,
12943 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
12944 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
12945 language_settings::SoftWrap::None
12946 }
12947 };
12948 self.soft_wrap_mode_override = Some(soft_wrap);
12949 }
12950 cx.notify();
12951 }
12952
12953 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
12954 let Some(workspace) = self.workspace() else {
12955 return;
12956 };
12957 let fs = workspace.read(cx).app_state().fs.clone();
12958 let current_show = TabBarSettings::get_global(cx).show;
12959 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
12960 setting.show = Some(!current_show);
12961 });
12962 }
12963
12964 pub fn toggle_indent_guides(
12965 &mut self,
12966 _: &ToggleIndentGuides,
12967 _: &mut Window,
12968 cx: &mut Context<Self>,
12969 ) {
12970 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
12971 self.buffer
12972 .read(cx)
12973 .settings_at(0, cx)
12974 .indent_guides
12975 .enabled
12976 });
12977 self.show_indent_guides = Some(!currently_enabled);
12978 cx.notify();
12979 }
12980
12981 fn should_show_indent_guides(&self) -> Option<bool> {
12982 self.show_indent_guides
12983 }
12984
12985 pub fn toggle_line_numbers(
12986 &mut self,
12987 _: &ToggleLineNumbers,
12988 _: &mut Window,
12989 cx: &mut Context<Self>,
12990 ) {
12991 let mut editor_settings = EditorSettings::get_global(cx).clone();
12992 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
12993 EditorSettings::override_global(editor_settings, cx);
12994 }
12995
12996 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
12997 self.use_relative_line_numbers
12998 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
12999 }
13000
13001 pub fn toggle_relative_line_numbers(
13002 &mut self,
13003 _: &ToggleRelativeLineNumbers,
13004 _: &mut Window,
13005 cx: &mut Context<Self>,
13006 ) {
13007 let is_relative = self.should_use_relative_line_numbers(cx);
13008 self.set_relative_line_number(Some(!is_relative), cx)
13009 }
13010
13011 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
13012 self.use_relative_line_numbers = is_relative;
13013 cx.notify();
13014 }
13015
13016 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
13017 self.show_gutter = show_gutter;
13018 cx.notify();
13019 }
13020
13021 pub fn set_show_scrollbars(&mut self, show_scrollbars: bool, cx: &mut Context<Self>) {
13022 self.show_scrollbars = show_scrollbars;
13023 cx.notify();
13024 }
13025
13026 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
13027 self.show_line_numbers = Some(show_line_numbers);
13028 cx.notify();
13029 }
13030
13031 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
13032 self.show_git_diff_gutter = Some(show_git_diff_gutter);
13033 cx.notify();
13034 }
13035
13036 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
13037 self.show_code_actions = Some(show_code_actions);
13038 cx.notify();
13039 }
13040
13041 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
13042 self.show_runnables = Some(show_runnables);
13043 cx.notify();
13044 }
13045
13046 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
13047 if self.display_map.read(cx).masked != masked {
13048 self.display_map.update(cx, |map, _| map.masked = masked);
13049 }
13050 cx.notify()
13051 }
13052
13053 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
13054 self.show_wrap_guides = Some(show_wrap_guides);
13055 cx.notify();
13056 }
13057
13058 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
13059 self.show_indent_guides = Some(show_indent_guides);
13060 cx.notify();
13061 }
13062
13063 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
13064 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
13065 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
13066 if let Some(dir) = file.abs_path(cx).parent() {
13067 return Some(dir.to_owned());
13068 }
13069 }
13070
13071 if let Some(project_path) = buffer.read(cx).project_path(cx) {
13072 return Some(project_path.path.to_path_buf());
13073 }
13074 }
13075
13076 None
13077 }
13078
13079 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
13080 self.active_excerpt(cx)?
13081 .1
13082 .read(cx)
13083 .file()
13084 .and_then(|f| f.as_local())
13085 }
13086
13087 fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
13088 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
13089 let project_path = buffer.read(cx).project_path(cx)?;
13090 let project = self.project.as_ref()?.read(cx);
13091 project.absolute_path(&project_path, cx)
13092 })
13093 }
13094
13095 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
13096 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
13097 let project_path = buffer.read(cx).project_path(cx)?;
13098 let project = self.project.as_ref()?.read(cx);
13099 let entry = project.entry_for_path(&project_path, cx)?;
13100 let path = entry.path.to_path_buf();
13101 Some(path)
13102 })
13103 }
13104
13105 pub fn reveal_in_finder(
13106 &mut self,
13107 _: &RevealInFileManager,
13108 _window: &mut Window,
13109 cx: &mut Context<Self>,
13110 ) {
13111 if let Some(target) = self.target_file(cx) {
13112 cx.reveal_path(&target.abs_path(cx));
13113 }
13114 }
13115
13116 pub fn copy_path(&mut self, _: &CopyPath, _window: &mut Window, cx: &mut Context<Self>) {
13117 if let Some(path) = self.target_file_abs_path(cx) {
13118 if let Some(path) = path.to_str() {
13119 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
13120 }
13121 }
13122 }
13123
13124 pub fn copy_relative_path(
13125 &mut self,
13126 _: &CopyRelativePath,
13127 _window: &mut Window,
13128 cx: &mut Context<Self>,
13129 ) {
13130 if let Some(path) = self.target_file_path(cx) {
13131 if let Some(path) = path.to_str() {
13132 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
13133 }
13134 }
13135 }
13136
13137 pub fn copy_file_name_without_extension(
13138 &mut self,
13139 _: &CopyFileNameWithoutExtension,
13140 _: &mut Window,
13141 cx: &mut Context<Self>,
13142 ) {
13143 if let Some(file) = self.target_file(cx) {
13144 if let Some(file_stem) = file.path().file_stem() {
13145 if let Some(name) = file_stem.to_str() {
13146 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
13147 }
13148 }
13149 }
13150 }
13151
13152 pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
13153 if let Some(file) = self.target_file(cx) {
13154 if let Some(file_name) = file.path().file_name() {
13155 if let Some(name) = file_name.to_str() {
13156 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
13157 }
13158 }
13159 }
13160 }
13161
13162 pub fn toggle_git_blame(
13163 &mut self,
13164 _: &ToggleGitBlame,
13165 window: &mut Window,
13166 cx: &mut Context<Self>,
13167 ) {
13168 self.show_git_blame_gutter = !self.show_git_blame_gutter;
13169
13170 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
13171 self.start_git_blame(true, window, cx);
13172 }
13173
13174 cx.notify();
13175 }
13176
13177 pub fn toggle_git_blame_inline(
13178 &mut self,
13179 _: &ToggleGitBlameInline,
13180 window: &mut Window,
13181 cx: &mut Context<Self>,
13182 ) {
13183 self.toggle_git_blame_inline_internal(true, window, cx);
13184 cx.notify();
13185 }
13186
13187 pub fn git_blame_inline_enabled(&self) -> bool {
13188 self.git_blame_inline_enabled
13189 }
13190
13191 pub fn toggle_selection_menu(
13192 &mut self,
13193 _: &ToggleSelectionMenu,
13194 _: &mut Window,
13195 cx: &mut Context<Self>,
13196 ) {
13197 self.show_selection_menu = self
13198 .show_selection_menu
13199 .map(|show_selections_menu| !show_selections_menu)
13200 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
13201
13202 cx.notify();
13203 }
13204
13205 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
13206 self.show_selection_menu
13207 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
13208 }
13209
13210 fn start_git_blame(
13211 &mut self,
13212 user_triggered: bool,
13213 window: &mut Window,
13214 cx: &mut Context<Self>,
13215 ) {
13216 if let Some(project) = self.project.as_ref() {
13217 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
13218 return;
13219 };
13220
13221 if buffer.read(cx).file().is_none() {
13222 return;
13223 }
13224
13225 let focused = self.focus_handle(cx).contains_focused(window, cx);
13226
13227 let project = project.clone();
13228 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
13229 self.blame_subscription =
13230 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
13231 self.blame = Some(blame);
13232 }
13233 }
13234
13235 fn toggle_git_blame_inline_internal(
13236 &mut self,
13237 user_triggered: bool,
13238 window: &mut Window,
13239 cx: &mut Context<Self>,
13240 ) {
13241 if self.git_blame_inline_enabled {
13242 self.git_blame_inline_enabled = false;
13243 self.show_git_blame_inline = false;
13244 self.show_git_blame_inline_delay_task.take();
13245 } else {
13246 self.git_blame_inline_enabled = true;
13247 self.start_git_blame_inline(user_triggered, window, cx);
13248 }
13249
13250 cx.notify();
13251 }
13252
13253 fn start_git_blame_inline(
13254 &mut self,
13255 user_triggered: bool,
13256 window: &mut Window,
13257 cx: &mut Context<Self>,
13258 ) {
13259 self.start_git_blame(user_triggered, window, cx);
13260
13261 if ProjectSettings::get_global(cx)
13262 .git
13263 .inline_blame_delay()
13264 .is_some()
13265 {
13266 self.start_inline_blame_timer(window, cx);
13267 } else {
13268 self.show_git_blame_inline = true
13269 }
13270 }
13271
13272 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
13273 self.blame.as_ref()
13274 }
13275
13276 pub fn show_git_blame_gutter(&self) -> bool {
13277 self.show_git_blame_gutter
13278 }
13279
13280 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
13281 self.show_git_blame_gutter && self.has_blame_entries(cx)
13282 }
13283
13284 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
13285 self.show_git_blame_inline
13286 && self.focus_handle.is_focused(window)
13287 && !self.newest_selection_head_on_empty_line(cx)
13288 && self.has_blame_entries(cx)
13289 }
13290
13291 fn has_blame_entries(&self, cx: &App) -> bool {
13292 self.blame()
13293 .map_or(false, |blame| blame.read(cx).has_generated_entries())
13294 }
13295
13296 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
13297 let cursor_anchor = self.selections.newest_anchor().head();
13298
13299 let snapshot = self.buffer.read(cx).snapshot(cx);
13300 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
13301
13302 snapshot.line_len(buffer_row) == 0
13303 }
13304
13305 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
13306 let buffer_and_selection = maybe!({
13307 let selection = self.selections.newest::<Point>(cx);
13308 let selection_range = selection.range();
13309
13310 let multi_buffer = self.buffer().read(cx);
13311 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
13312 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
13313
13314 let (buffer, range, _) = if selection.reversed {
13315 buffer_ranges.first()
13316 } else {
13317 buffer_ranges.last()
13318 }?;
13319
13320 let selection = text::ToPoint::to_point(&range.start, &buffer).row
13321 ..text::ToPoint::to_point(&range.end, &buffer).row;
13322 Some((
13323 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
13324 selection,
13325 ))
13326 });
13327
13328 let Some((buffer, selection)) = buffer_and_selection else {
13329 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
13330 };
13331
13332 let Some(project) = self.project.as_ref() else {
13333 return Task::ready(Err(anyhow!("editor does not have project")));
13334 };
13335
13336 project.update(cx, |project, cx| {
13337 project.get_permalink_to_line(&buffer, selection, cx)
13338 })
13339 }
13340
13341 pub fn copy_permalink_to_line(
13342 &mut self,
13343 _: &CopyPermalinkToLine,
13344 window: &mut Window,
13345 cx: &mut Context<Self>,
13346 ) {
13347 let permalink_task = self.get_permalink_to_line(cx);
13348 let workspace = self.workspace();
13349
13350 cx.spawn_in(window, |_, mut cx| async move {
13351 match permalink_task.await {
13352 Ok(permalink) => {
13353 cx.update(|_, cx| {
13354 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
13355 })
13356 .ok();
13357 }
13358 Err(err) => {
13359 let message = format!("Failed to copy permalink: {err}");
13360
13361 Err::<(), anyhow::Error>(err).log_err();
13362
13363 if let Some(workspace) = workspace {
13364 workspace
13365 .update_in(&mut cx, |workspace, _, cx| {
13366 struct CopyPermalinkToLine;
13367
13368 workspace.show_toast(
13369 Toast::new(
13370 NotificationId::unique::<CopyPermalinkToLine>(),
13371 message,
13372 ),
13373 cx,
13374 )
13375 })
13376 .ok();
13377 }
13378 }
13379 }
13380 })
13381 .detach();
13382 }
13383
13384 pub fn copy_file_location(
13385 &mut self,
13386 _: &CopyFileLocation,
13387 _: &mut Window,
13388 cx: &mut Context<Self>,
13389 ) {
13390 let selection = self.selections.newest::<Point>(cx).start.row + 1;
13391 if let Some(file) = self.target_file(cx) {
13392 if let Some(path) = file.path().to_str() {
13393 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
13394 }
13395 }
13396 }
13397
13398 pub fn open_permalink_to_line(
13399 &mut self,
13400 _: &OpenPermalinkToLine,
13401 window: &mut Window,
13402 cx: &mut Context<Self>,
13403 ) {
13404 let permalink_task = self.get_permalink_to_line(cx);
13405 let workspace = self.workspace();
13406
13407 cx.spawn_in(window, |_, mut cx| async move {
13408 match permalink_task.await {
13409 Ok(permalink) => {
13410 cx.update(|_, cx| {
13411 cx.open_url(permalink.as_ref());
13412 })
13413 .ok();
13414 }
13415 Err(err) => {
13416 let message = format!("Failed to open permalink: {err}");
13417
13418 Err::<(), anyhow::Error>(err).log_err();
13419
13420 if let Some(workspace) = workspace {
13421 workspace
13422 .update(&mut cx, |workspace, cx| {
13423 struct OpenPermalinkToLine;
13424
13425 workspace.show_toast(
13426 Toast::new(
13427 NotificationId::unique::<OpenPermalinkToLine>(),
13428 message,
13429 ),
13430 cx,
13431 )
13432 })
13433 .ok();
13434 }
13435 }
13436 }
13437 })
13438 .detach();
13439 }
13440
13441 pub fn insert_uuid_v4(
13442 &mut self,
13443 _: &InsertUuidV4,
13444 window: &mut Window,
13445 cx: &mut Context<Self>,
13446 ) {
13447 self.insert_uuid(UuidVersion::V4, window, cx);
13448 }
13449
13450 pub fn insert_uuid_v7(
13451 &mut self,
13452 _: &InsertUuidV7,
13453 window: &mut Window,
13454 cx: &mut Context<Self>,
13455 ) {
13456 self.insert_uuid(UuidVersion::V7, window, cx);
13457 }
13458
13459 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
13460 self.transact(window, cx, |this, window, cx| {
13461 let edits = this
13462 .selections
13463 .all::<Point>(cx)
13464 .into_iter()
13465 .map(|selection| {
13466 let uuid = match version {
13467 UuidVersion::V4 => uuid::Uuid::new_v4(),
13468 UuidVersion::V7 => uuid::Uuid::now_v7(),
13469 };
13470
13471 (selection.range(), uuid.to_string())
13472 });
13473 this.edit(edits, cx);
13474 this.refresh_inline_completion(true, false, window, cx);
13475 });
13476 }
13477
13478 pub fn open_selections_in_multibuffer(
13479 &mut self,
13480 _: &OpenSelectionsInMultibuffer,
13481 window: &mut Window,
13482 cx: &mut Context<Self>,
13483 ) {
13484 let multibuffer = self.buffer.read(cx);
13485
13486 let Some(buffer) = multibuffer.as_singleton() else {
13487 return;
13488 };
13489
13490 let Some(workspace) = self.workspace() else {
13491 return;
13492 };
13493
13494 let locations = self
13495 .selections
13496 .disjoint_anchors()
13497 .iter()
13498 .map(|range| Location {
13499 buffer: buffer.clone(),
13500 range: range.start.text_anchor..range.end.text_anchor,
13501 })
13502 .collect::<Vec<_>>();
13503
13504 let title = multibuffer.title(cx).to_string();
13505
13506 cx.spawn_in(window, |_, mut cx| async move {
13507 workspace.update_in(&mut cx, |workspace, window, cx| {
13508 Self::open_locations_in_multibuffer(
13509 workspace,
13510 locations,
13511 format!("Selections for '{title}'"),
13512 false,
13513 MultibufferSelectionMode::All,
13514 window,
13515 cx,
13516 );
13517 })
13518 })
13519 .detach();
13520 }
13521
13522 /// Adds a row highlight for the given range. If a row has multiple highlights, the
13523 /// last highlight added will be used.
13524 ///
13525 /// If the range ends at the beginning of a line, then that line will not be highlighted.
13526 pub fn highlight_rows<T: 'static>(
13527 &mut self,
13528 range: Range<Anchor>,
13529 color: Hsla,
13530 should_autoscroll: bool,
13531 cx: &mut Context<Self>,
13532 ) {
13533 let snapshot = self.buffer().read(cx).snapshot(cx);
13534 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
13535 let ix = row_highlights.binary_search_by(|highlight| {
13536 Ordering::Equal
13537 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
13538 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
13539 });
13540
13541 if let Err(mut ix) = ix {
13542 let index = post_inc(&mut self.highlight_order);
13543
13544 // If this range intersects with the preceding highlight, then merge it with
13545 // the preceding highlight. Otherwise insert a new highlight.
13546 let mut merged = false;
13547 if ix > 0 {
13548 let prev_highlight = &mut row_highlights[ix - 1];
13549 if prev_highlight
13550 .range
13551 .end
13552 .cmp(&range.start, &snapshot)
13553 .is_ge()
13554 {
13555 ix -= 1;
13556 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
13557 prev_highlight.range.end = range.end;
13558 }
13559 merged = true;
13560 prev_highlight.index = index;
13561 prev_highlight.color = color;
13562 prev_highlight.should_autoscroll = should_autoscroll;
13563 }
13564 }
13565
13566 if !merged {
13567 row_highlights.insert(
13568 ix,
13569 RowHighlight {
13570 range: range.clone(),
13571 index,
13572 color,
13573 should_autoscroll,
13574 },
13575 );
13576 }
13577
13578 // If any of the following highlights intersect with this one, merge them.
13579 while let Some(next_highlight) = row_highlights.get(ix + 1) {
13580 let highlight = &row_highlights[ix];
13581 if next_highlight
13582 .range
13583 .start
13584 .cmp(&highlight.range.end, &snapshot)
13585 .is_le()
13586 {
13587 if next_highlight
13588 .range
13589 .end
13590 .cmp(&highlight.range.end, &snapshot)
13591 .is_gt()
13592 {
13593 row_highlights[ix].range.end = next_highlight.range.end;
13594 }
13595 row_highlights.remove(ix + 1);
13596 } else {
13597 break;
13598 }
13599 }
13600 }
13601 }
13602
13603 /// Remove any highlighted row ranges of the given type that intersect the
13604 /// given ranges.
13605 pub fn remove_highlighted_rows<T: 'static>(
13606 &mut self,
13607 ranges_to_remove: Vec<Range<Anchor>>,
13608 cx: &mut Context<Self>,
13609 ) {
13610 let snapshot = self.buffer().read(cx).snapshot(cx);
13611 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
13612 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
13613 row_highlights.retain(|highlight| {
13614 while let Some(range_to_remove) = ranges_to_remove.peek() {
13615 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
13616 Ordering::Less | Ordering::Equal => {
13617 ranges_to_remove.next();
13618 }
13619 Ordering::Greater => {
13620 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
13621 Ordering::Less | Ordering::Equal => {
13622 return false;
13623 }
13624 Ordering::Greater => break,
13625 }
13626 }
13627 }
13628 }
13629
13630 true
13631 })
13632 }
13633
13634 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
13635 pub fn clear_row_highlights<T: 'static>(&mut self) {
13636 self.highlighted_rows.remove(&TypeId::of::<T>());
13637 }
13638
13639 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
13640 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
13641 self.highlighted_rows
13642 .get(&TypeId::of::<T>())
13643 .map_or(&[] as &[_], |vec| vec.as_slice())
13644 .iter()
13645 .map(|highlight| (highlight.range.clone(), highlight.color))
13646 }
13647
13648 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
13649 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
13650 /// Allows to ignore certain kinds of highlights.
13651 pub fn highlighted_display_rows(
13652 &self,
13653 window: &mut Window,
13654 cx: &mut App,
13655 ) -> BTreeMap<DisplayRow, Background> {
13656 let snapshot = self.snapshot(window, cx);
13657 let mut used_highlight_orders = HashMap::default();
13658 self.highlighted_rows
13659 .iter()
13660 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
13661 .fold(
13662 BTreeMap::<DisplayRow, Background>::new(),
13663 |mut unique_rows, highlight| {
13664 let start = highlight.range.start.to_display_point(&snapshot);
13665 let end = highlight.range.end.to_display_point(&snapshot);
13666 let start_row = start.row().0;
13667 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
13668 && end.column() == 0
13669 {
13670 end.row().0.saturating_sub(1)
13671 } else {
13672 end.row().0
13673 };
13674 for row in start_row..=end_row {
13675 let used_index =
13676 used_highlight_orders.entry(row).or_insert(highlight.index);
13677 if highlight.index >= *used_index {
13678 *used_index = highlight.index;
13679 unique_rows.insert(DisplayRow(row), highlight.color.into());
13680 }
13681 }
13682 unique_rows
13683 },
13684 )
13685 }
13686
13687 pub fn highlighted_display_row_for_autoscroll(
13688 &self,
13689 snapshot: &DisplaySnapshot,
13690 ) -> Option<DisplayRow> {
13691 self.highlighted_rows
13692 .values()
13693 .flat_map(|highlighted_rows| highlighted_rows.iter())
13694 .filter_map(|highlight| {
13695 if highlight.should_autoscroll {
13696 Some(highlight.range.start.to_display_point(snapshot).row())
13697 } else {
13698 None
13699 }
13700 })
13701 .min()
13702 }
13703
13704 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
13705 self.highlight_background::<SearchWithinRange>(
13706 ranges,
13707 |colors| colors.editor_document_highlight_read_background,
13708 cx,
13709 )
13710 }
13711
13712 pub fn set_breadcrumb_header(&mut self, new_header: String) {
13713 self.breadcrumb_header = Some(new_header);
13714 }
13715
13716 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
13717 self.clear_background_highlights::<SearchWithinRange>(cx);
13718 }
13719
13720 pub fn highlight_background<T: 'static>(
13721 &mut self,
13722 ranges: &[Range<Anchor>],
13723 color_fetcher: fn(&ThemeColors) -> Hsla,
13724 cx: &mut Context<Self>,
13725 ) {
13726 self.background_highlights
13727 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
13728 self.scrollbar_marker_state.dirty = true;
13729 cx.notify();
13730 }
13731
13732 pub fn clear_background_highlights<T: 'static>(
13733 &mut self,
13734 cx: &mut Context<Self>,
13735 ) -> Option<BackgroundHighlight> {
13736 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
13737 if !text_highlights.1.is_empty() {
13738 self.scrollbar_marker_state.dirty = true;
13739 cx.notify();
13740 }
13741 Some(text_highlights)
13742 }
13743
13744 pub fn highlight_gutter<T: 'static>(
13745 &mut self,
13746 ranges: &[Range<Anchor>],
13747 color_fetcher: fn(&App) -> Hsla,
13748 cx: &mut Context<Self>,
13749 ) {
13750 self.gutter_highlights
13751 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
13752 cx.notify();
13753 }
13754
13755 pub fn clear_gutter_highlights<T: 'static>(
13756 &mut self,
13757 cx: &mut Context<Self>,
13758 ) -> Option<GutterHighlight> {
13759 cx.notify();
13760 self.gutter_highlights.remove(&TypeId::of::<T>())
13761 }
13762
13763 #[cfg(feature = "test-support")]
13764 pub fn all_text_background_highlights(
13765 &self,
13766 window: &mut Window,
13767 cx: &mut Context<Self>,
13768 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
13769 let snapshot = self.snapshot(window, cx);
13770 let buffer = &snapshot.buffer_snapshot;
13771 let start = buffer.anchor_before(0);
13772 let end = buffer.anchor_after(buffer.len());
13773 let theme = cx.theme().colors();
13774 self.background_highlights_in_range(start..end, &snapshot, theme)
13775 }
13776
13777 #[cfg(feature = "test-support")]
13778 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
13779 let snapshot = self.buffer().read(cx).snapshot(cx);
13780
13781 let highlights = self
13782 .background_highlights
13783 .get(&TypeId::of::<items::BufferSearchHighlights>());
13784
13785 if let Some((_color, ranges)) = highlights {
13786 ranges
13787 .iter()
13788 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
13789 .collect_vec()
13790 } else {
13791 vec![]
13792 }
13793 }
13794
13795 fn document_highlights_for_position<'a>(
13796 &'a self,
13797 position: Anchor,
13798 buffer: &'a MultiBufferSnapshot,
13799 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
13800 let read_highlights = self
13801 .background_highlights
13802 .get(&TypeId::of::<DocumentHighlightRead>())
13803 .map(|h| &h.1);
13804 let write_highlights = self
13805 .background_highlights
13806 .get(&TypeId::of::<DocumentHighlightWrite>())
13807 .map(|h| &h.1);
13808 let left_position = position.bias_left(buffer);
13809 let right_position = position.bias_right(buffer);
13810 read_highlights
13811 .into_iter()
13812 .chain(write_highlights)
13813 .flat_map(move |ranges| {
13814 let start_ix = match ranges.binary_search_by(|probe| {
13815 let cmp = probe.end.cmp(&left_position, buffer);
13816 if cmp.is_ge() {
13817 Ordering::Greater
13818 } else {
13819 Ordering::Less
13820 }
13821 }) {
13822 Ok(i) | Err(i) => i,
13823 };
13824
13825 ranges[start_ix..]
13826 .iter()
13827 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
13828 })
13829 }
13830
13831 pub fn has_background_highlights<T: 'static>(&self) -> bool {
13832 self.background_highlights
13833 .get(&TypeId::of::<T>())
13834 .map_or(false, |(_, highlights)| !highlights.is_empty())
13835 }
13836
13837 pub fn background_highlights_in_range(
13838 &self,
13839 search_range: Range<Anchor>,
13840 display_snapshot: &DisplaySnapshot,
13841 theme: &ThemeColors,
13842 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
13843 let mut results = Vec::new();
13844 for (color_fetcher, ranges) in self.background_highlights.values() {
13845 let color = color_fetcher(theme);
13846 let start_ix = match ranges.binary_search_by(|probe| {
13847 let cmp = probe
13848 .end
13849 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
13850 if cmp.is_gt() {
13851 Ordering::Greater
13852 } else {
13853 Ordering::Less
13854 }
13855 }) {
13856 Ok(i) | Err(i) => i,
13857 };
13858 for range in &ranges[start_ix..] {
13859 if range
13860 .start
13861 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
13862 .is_ge()
13863 {
13864 break;
13865 }
13866
13867 let start = range.start.to_display_point(display_snapshot);
13868 let end = range.end.to_display_point(display_snapshot);
13869 results.push((start..end, color))
13870 }
13871 }
13872 results
13873 }
13874
13875 pub fn background_highlight_row_ranges<T: 'static>(
13876 &self,
13877 search_range: Range<Anchor>,
13878 display_snapshot: &DisplaySnapshot,
13879 count: usize,
13880 ) -> Vec<RangeInclusive<DisplayPoint>> {
13881 let mut results = Vec::new();
13882 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
13883 return vec![];
13884 };
13885
13886 let start_ix = match ranges.binary_search_by(|probe| {
13887 let cmp = probe
13888 .end
13889 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
13890 if cmp.is_gt() {
13891 Ordering::Greater
13892 } else {
13893 Ordering::Less
13894 }
13895 }) {
13896 Ok(i) | Err(i) => i,
13897 };
13898 let mut push_region = |start: Option<Point>, end: Option<Point>| {
13899 if let (Some(start_display), Some(end_display)) = (start, end) {
13900 results.push(
13901 start_display.to_display_point(display_snapshot)
13902 ..=end_display.to_display_point(display_snapshot),
13903 );
13904 }
13905 };
13906 let mut start_row: Option<Point> = None;
13907 let mut end_row: Option<Point> = None;
13908 if ranges.len() > count {
13909 return Vec::new();
13910 }
13911 for range in &ranges[start_ix..] {
13912 if range
13913 .start
13914 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
13915 .is_ge()
13916 {
13917 break;
13918 }
13919 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
13920 if let Some(current_row) = &end_row {
13921 if end.row == current_row.row {
13922 continue;
13923 }
13924 }
13925 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
13926 if start_row.is_none() {
13927 assert_eq!(end_row, None);
13928 start_row = Some(start);
13929 end_row = Some(end);
13930 continue;
13931 }
13932 if let Some(current_end) = end_row.as_mut() {
13933 if start.row > current_end.row + 1 {
13934 push_region(start_row, end_row);
13935 start_row = Some(start);
13936 end_row = Some(end);
13937 } else {
13938 // Merge two hunks.
13939 *current_end = end;
13940 }
13941 } else {
13942 unreachable!();
13943 }
13944 }
13945 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
13946 push_region(start_row, end_row);
13947 results
13948 }
13949
13950 pub fn gutter_highlights_in_range(
13951 &self,
13952 search_range: Range<Anchor>,
13953 display_snapshot: &DisplaySnapshot,
13954 cx: &App,
13955 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
13956 let mut results = Vec::new();
13957 for (color_fetcher, ranges) in self.gutter_highlights.values() {
13958 let color = color_fetcher(cx);
13959 let start_ix = match ranges.binary_search_by(|probe| {
13960 let cmp = probe
13961 .end
13962 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
13963 if cmp.is_gt() {
13964 Ordering::Greater
13965 } else {
13966 Ordering::Less
13967 }
13968 }) {
13969 Ok(i) | Err(i) => i,
13970 };
13971 for range in &ranges[start_ix..] {
13972 if range
13973 .start
13974 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
13975 .is_ge()
13976 {
13977 break;
13978 }
13979
13980 let start = range.start.to_display_point(display_snapshot);
13981 let end = range.end.to_display_point(display_snapshot);
13982 results.push((start..end, color))
13983 }
13984 }
13985 results
13986 }
13987
13988 /// Get the text ranges corresponding to the redaction query
13989 pub fn redacted_ranges(
13990 &self,
13991 search_range: Range<Anchor>,
13992 display_snapshot: &DisplaySnapshot,
13993 cx: &App,
13994 ) -> Vec<Range<DisplayPoint>> {
13995 display_snapshot
13996 .buffer_snapshot
13997 .redacted_ranges(search_range, |file| {
13998 if let Some(file) = file {
13999 file.is_private()
14000 && EditorSettings::get(
14001 Some(SettingsLocation {
14002 worktree_id: file.worktree_id(cx),
14003 path: file.path().as_ref(),
14004 }),
14005 cx,
14006 )
14007 .redact_private_values
14008 } else {
14009 false
14010 }
14011 })
14012 .map(|range| {
14013 range.start.to_display_point(display_snapshot)
14014 ..range.end.to_display_point(display_snapshot)
14015 })
14016 .collect()
14017 }
14018
14019 pub fn highlight_text<T: 'static>(
14020 &mut self,
14021 ranges: Vec<Range<Anchor>>,
14022 style: HighlightStyle,
14023 cx: &mut Context<Self>,
14024 ) {
14025 self.display_map.update(cx, |map, _| {
14026 map.highlight_text(TypeId::of::<T>(), ranges, style)
14027 });
14028 cx.notify();
14029 }
14030
14031 pub(crate) fn highlight_inlays<T: 'static>(
14032 &mut self,
14033 highlights: Vec<InlayHighlight>,
14034 style: HighlightStyle,
14035 cx: &mut Context<Self>,
14036 ) {
14037 self.display_map.update(cx, |map, _| {
14038 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
14039 });
14040 cx.notify();
14041 }
14042
14043 pub fn text_highlights<'a, T: 'static>(
14044 &'a self,
14045 cx: &'a App,
14046 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
14047 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
14048 }
14049
14050 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
14051 let cleared = self
14052 .display_map
14053 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
14054 if cleared {
14055 cx.notify();
14056 }
14057 }
14058
14059 pub fn previewing_edit_prediction_move(
14060 &mut self,
14061 ) -> Option<(Anchor, &mut EditPredictionPreview)> {
14062 if !self.edit_prediction_preview.is_active() {
14063 return None;
14064 };
14065
14066 self.active_inline_completion
14067 .as_ref()
14068 .and_then(|completion| match completion.completion {
14069 InlineCompletion::Move { target, .. } => {
14070 Some((target, &mut self.edit_prediction_preview))
14071 }
14072 _ => None,
14073 })
14074 }
14075
14076 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
14077 (self.read_only(cx) || self.blink_manager.read(cx).visible())
14078 && self.focus_handle.is_focused(window)
14079 }
14080
14081 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
14082 self.show_cursor_when_unfocused = is_enabled;
14083 cx.notify();
14084 }
14085
14086 pub fn lsp_store(&self, cx: &App) -> Option<Entity<LspStore>> {
14087 self.project
14088 .as_ref()
14089 .map(|project| project.read(cx).lsp_store())
14090 }
14091
14092 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
14093 cx.notify();
14094 }
14095
14096 fn on_buffer_event(
14097 &mut self,
14098 multibuffer: &Entity<MultiBuffer>,
14099 event: &multi_buffer::Event,
14100 window: &mut Window,
14101 cx: &mut Context<Self>,
14102 ) {
14103 match event {
14104 multi_buffer::Event::Edited {
14105 singleton_buffer_edited,
14106 edited_buffer: buffer_edited,
14107 } => {
14108 self.scrollbar_marker_state.dirty = true;
14109 self.active_indent_guides_state.dirty = true;
14110 self.refresh_active_diagnostics(cx);
14111 self.refresh_code_actions(window, cx);
14112 if self.has_active_inline_completion() {
14113 self.update_visible_inline_completion(window, cx);
14114 }
14115 if let Some(buffer) = buffer_edited {
14116 let buffer_id = buffer.read(cx).remote_id();
14117 if !self.registered_buffers.contains_key(&buffer_id) {
14118 if let Some(lsp_store) = self.lsp_store(cx) {
14119 lsp_store.update(cx, |lsp_store, cx| {
14120 self.registered_buffers.insert(
14121 buffer_id,
14122 lsp_store.register_buffer_with_language_servers(&buffer, cx),
14123 );
14124 })
14125 }
14126 }
14127 }
14128 cx.emit(EditorEvent::BufferEdited);
14129 cx.emit(SearchEvent::MatchesInvalidated);
14130 if *singleton_buffer_edited {
14131 if let Some(project) = &self.project {
14132 let project = project.read(cx);
14133 #[allow(clippy::mutable_key_type)]
14134 let languages_affected = multibuffer
14135 .read(cx)
14136 .all_buffers()
14137 .into_iter()
14138 .filter_map(|buffer| {
14139 let buffer = buffer.read(cx);
14140 let language = buffer.language()?;
14141 if project.is_local()
14142 && project
14143 .language_servers_for_local_buffer(buffer, cx)
14144 .count()
14145 == 0
14146 {
14147 None
14148 } else {
14149 Some(language)
14150 }
14151 })
14152 .cloned()
14153 .collect::<HashSet<_>>();
14154 if !languages_affected.is_empty() {
14155 self.refresh_inlay_hints(
14156 InlayHintRefreshReason::BufferEdited(languages_affected),
14157 cx,
14158 );
14159 }
14160 }
14161 }
14162
14163 let Some(project) = &self.project else { return };
14164 let (telemetry, is_via_ssh) = {
14165 let project = project.read(cx);
14166 let telemetry = project.client().telemetry().clone();
14167 let is_via_ssh = project.is_via_ssh();
14168 (telemetry, is_via_ssh)
14169 };
14170 refresh_linked_ranges(self, window, cx);
14171 telemetry.log_edit_event("editor", is_via_ssh);
14172 }
14173 multi_buffer::Event::ExcerptsAdded {
14174 buffer,
14175 predecessor,
14176 excerpts,
14177 } => {
14178 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
14179 let buffer_id = buffer.read(cx).remote_id();
14180 if self.buffer.read(cx).diff_for(buffer_id).is_none() {
14181 if let Some(project) = &self.project {
14182 get_uncommitted_diff_for_buffer(
14183 project,
14184 [buffer.clone()],
14185 self.buffer.clone(),
14186 cx,
14187 );
14188 }
14189 }
14190 cx.emit(EditorEvent::ExcerptsAdded {
14191 buffer: buffer.clone(),
14192 predecessor: *predecessor,
14193 excerpts: excerpts.clone(),
14194 });
14195 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
14196 }
14197 multi_buffer::Event::ExcerptsRemoved { ids } => {
14198 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
14199 let buffer = self.buffer.read(cx);
14200 self.registered_buffers
14201 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
14202 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
14203 }
14204 multi_buffer::Event::ExcerptsEdited { ids } => {
14205 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
14206 }
14207 multi_buffer::Event::ExcerptsExpanded { ids } => {
14208 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
14209 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
14210 }
14211 multi_buffer::Event::Reparsed(buffer_id) => {
14212 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
14213
14214 cx.emit(EditorEvent::Reparsed(*buffer_id));
14215 }
14216 multi_buffer::Event::DiffHunksToggled => {
14217 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
14218 }
14219 multi_buffer::Event::LanguageChanged(buffer_id) => {
14220 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
14221 cx.emit(EditorEvent::Reparsed(*buffer_id));
14222 cx.notify();
14223 }
14224 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
14225 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
14226 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
14227 cx.emit(EditorEvent::TitleChanged)
14228 }
14229 // multi_buffer::Event::DiffBaseChanged => {
14230 // self.scrollbar_marker_state.dirty = true;
14231 // cx.emit(EditorEvent::DiffBaseChanged);
14232 // cx.notify();
14233 // }
14234 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
14235 multi_buffer::Event::DiagnosticsUpdated => {
14236 self.refresh_active_diagnostics(cx);
14237 self.scrollbar_marker_state.dirty = true;
14238 cx.notify();
14239 }
14240 _ => {}
14241 };
14242 }
14243
14244 fn on_display_map_changed(
14245 &mut self,
14246 _: Entity<DisplayMap>,
14247 _: &mut Window,
14248 cx: &mut Context<Self>,
14249 ) {
14250 cx.notify();
14251 }
14252
14253 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
14254 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
14255 self.refresh_inline_completion(true, false, window, cx);
14256 self.refresh_inlay_hints(
14257 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
14258 self.selections.newest_anchor().head(),
14259 &self.buffer.read(cx).snapshot(cx),
14260 cx,
14261 )),
14262 cx,
14263 );
14264
14265 let old_cursor_shape = self.cursor_shape;
14266
14267 {
14268 let editor_settings = EditorSettings::get_global(cx);
14269 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
14270 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
14271 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
14272 }
14273
14274 if old_cursor_shape != self.cursor_shape {
14275 cx.emit(EditorEvent::CursorShapeChanged);
14276 }
14277
14278 let project_settings = ProjectSettings::get_global(cx);
14279 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
14280
14281 if self.mode == EditorMode::Full {
14282 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
14283 if self.git_blame_inline_enabled != inline_blame_enabled {
14284 self.toggle_git_blame_inline_internal(false, window, cx);
14285 }
14286 }
14287
14288 cx.notify();
14289 }
14290
14291 pub fn set_searchable(&mut self, searchable: bool) {
14292 self.searchable = searchable;
14293 }
14294
14295 pub fn searchable(&self) -> bool {
14296 self.searchable
14297 }
14298
14299 fn open_proposed_changes_editor(
14300 &mut self,
14301 _: &OpenProposedChangesEditor,
14302 window: &mut Window,
14303 cx: &mut Context<Self>,
14304 ) {
14305 let Some(workspace) = self.workspace() else {
14306 cx.propagate();
14307 return;
14308 };
14309
14310 let selections = self.selections.all::<usize>(cx);
14311 let multi_buffer = self.buffer.read(cx);
14312 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
14313 let mut new_selections_by_buffer = HashMap::default();
14314 for selection in selections {
14315 for (buffer, range, _) in
14316 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
14317 {
14318 let mut range = range.to_point(buffer);
14319 range.start.column = 0;
14320 range.end.column = buffer.line_len(range.end.row);
14321 new_selections_by_buffer
14322 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
14323 .or_insert(Vec::new())
14324 .push(range)
14325 }
14326 }
14327
14328 let proposed_changes_buffers = new_selections_by_buffer
14329 .into_iter()
14330 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
14331 .collect::<Vec<_>>();
14332 let proposed_changes_editor = cx.new(|cx| {
14333 ProposedChangesEditor::new(
14334 "Proposed changes",
14335 proposed_changes_buffers,
14336 self.project.clone(),
14337 window,
14338 cx,
14339 )
14340 });
14341
14342 window.defer(cx, move |window, cx| {
14343 workspace.update(cx, |workspace, cx| {
14344 workspace.active_pane().update(cx, |pane, cx| {
14345 pane.add_item(
14346 Box::new(proposed_changes_editor),
14347 true,
14348 true,
14349 None,
14350 window,
14351 cx,
14352 );
14353 });
14354 });
14355 });
14356 }
14357
14358 pub fn open_excerpts_in_split(
14359 &mut self,
14360 _: &OpenExcerptsSplit,
14361 window: &mut Window,
14362 cx: &mut Context<Self>,
14363 ) {
14364 self.open_excerpts_common(None, true, window, cx)
14365 }
14366
14367 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
14368 self.open_excerpts_common(None, false, window, cx)
14369 }
14370
14371 fn open_excerpts_common(
14372 &mut self,
14373 jump_data: Option<JumpData>,
14374 split: bool,
14375 window: &mut Window,
14376 cx: &mut Context<Self>,
14377 ) {
14378 let Some(workspace) = self.workspace() else {
14379 cx.propagate();
14380 return;
14381 };
14382
14383 if self.buffer.read(cx).is_singleton() {
14384 cx.propagate();
14385 return;
14386 }
14387
14388 let mut new_selections_by_buffer = HashMap::default();
14389 match &jump_data {
14390 Some(JumpData::MultiBufferPoint {
14391 excerpt_id,
14392 position,
14393 anchor,
14394 line_offset_from_top,
14395 }) => {
14396 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14397 if let Some(buffer) = multi_buffer_snapshot
14398 .buffer_id_for_excerpt(*excerpt_id)
14399 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
14400 {
14401 let buffer_snapshot = buffer.read(cx).snapshot();
14402 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
14403 language::ToPoint::to_point(anchor, &buffer_snapshot)
14404 } else {
14405 buffer_snapshot.clip_point(*position, Bias::Left)
14406 };
14407 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
14408 new_selections_by_buffer.insert(
14409 buffer,
14410 (
14411 vec![jump_to_offset..jump_to_offset],
14412 Some(*line_offset_from_top),
14413 ),
14414 );
14415 }
14416 }
14417 Some(JumpData::MultiBufferRow {
14418 row,
14419 line_offset_from_top,
14420 }) => {
14421 let point = MultiBufferPoint::new(row.0, 0);
14422 if let Some((buffer, buffer_point, _)) =
14423 self.buffer.read(cx).point_to_buffer_point(point, cx)
14424 {
14425 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
14426 new_selections_by_buffer
14427 .entry(buffer)
14428 .or_insert((Vec::new(), Some(*line_offset_from_top)))
14429 .0
14430 .push(buffer_offset..buffer_offset)
14431 }
14432 }
14433 None => {
14434 let selections = self.selections.all::<usize>(cx);
14435 let multi_buffer = self.buffer.read(cx);
14436 for selection in selections {
14437 for (buffer, mut range, _) in multi_buffer
14438 .snapshot(cx)
14439 .range_to_buffer_ranges(selection.range())
14440 {
14441 // When editing branch buffers, jump to the corresponding location
14442 // in their base buffer.
14443 let mut buffer_handle = multi_buffer.buffer(buffer.remote_id()).unwrap();
14444 let buffer = buffer_handle.read(cx);
14445 if let Some(base_buffer) = buffer.base_buffer() {
14446 range = buffer.range_to_version(range, &base_buffer.read(cx).version());
14447 buffer_handle = base_buffer;
14448 }
14449
14450 if selection.reversed {
14451 mem::swap(&mut range.start, &mut range.end);
14452 }
14453 new_selections_by_buffer
14454 .entry(buffer_handle)
14455 .or_insert((Vec::new(), None))
14456 .0
14457 .push(range)
14458 }
14459 }
14460 }
14461 }
14462
14463 if new_selections_by_buffer.is_empty() {
14464 return;
14465 }
14466
14467 // We defer the pane interaction because we ourselves are a workspace item
14468 // and activating a new item causes the pane to call a method on us reentrantly,
14469 // which panics if we're on the stack.
14470 window.defer(cx, move |window, cx| {
14471 workspace.update(cx, |workspace, cx| {
14472 let pane = if split {
14473 workspace.adjacent_pane(window, cx)
14474 } else {
14475 workspace.active_pane().clone()
14476 };
14477
14478 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
14479 let editor = buffer
14480 .read(cx)
14481 .file()
14482 .is_none()
14483 .then(|| {
14484 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
14485 // so `workspace.open_project_item` will never find them, always opening a new editor.
14486 // Instead, we try to activate the existing editor in the pane first.
14487 let (editor, pane_item_index) =
14488 pane.read(cx).items().enumerate().find_map(|(i, item)| {
14489 let editor = item.downcast::<Editor>()?;
14490 let singleton_buffer =
14491 editor.read(cx).buffer().read(cx).as_singleton()?;
14492 if singleton_buffer == buffer {
14493 Some((editor, i))
14494 } else {
14495 None
14496 }
14497 })?;
14498 pane.update(cx, |pane, cx| {
14499 pane.activate_item(pane_item_index, true, true, window, cx)
14500 });
14501 Some(editor)
14502 })
14503 .flatten()
14504 .unwrap_or_else(|| {
14505 workspace.open_project_item::<Self>(
14506 pane.clone(),
14507 buffer,
14508 true,
14509 true,
14510 window,
14511 cx,
14512 )
14513 });
14514
14515 editor.update(cx, |editor, cx| {
14516 let autoscroll = match scroll_offset {
14517 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
14518 None => Autoscroll::newest(),
14519 };
14520 let nav_history = editor.nav_history.take();
14521 editor.change_selections(Some(autoscroll), window, cx, |s| {
14522 s.select_ranges(ranges);
14523 });
14524 editor.nav_history = nav_history;
14525 });
14526 }
14527 })
14528 });
14529 }
14530
14531 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
14532 let snapshot = self.buffer.read(cx).read(cx);
14533 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
14534 Some(
14535 ranges
14536 .iter()
14537 .map(move |range| {
14538 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
14539 })
14540 .collect(),
14541 )
14542 }
14543
14544 fn selection_replacement_ranges(
14545 &self,
14546 range: Range<OffsetUtf16>,
14547 cx: &mut App,
14548 ) -> Vec<Range<OffsetUtf16>> {
14549 let selections = self.selections.all::<OffsetUtf16>(cx);
14550 let newest_selection = selections
14551 .iter()
14552 .max_by_key(|selection| selection.id)
14553 .unwrap();
14554 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
14555 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
14556 let snapshot = self.buffer.read(cx).read(cx);
14557 selections
14558 .into_iter()
14559 .map(|mut selection| {
14560 selection.start.0 =
14561 (selection.start.0 as isize).saturating_add(start_delta) as usize;
14562 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
14563 snapshot.clip_offset_utf16(selection.start, Bias::Left)
14564 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
14565 })
14566 .collect()
14567 }
14568
14569 fn report_editor_event(
14570 &self,
14571 event_type: &'static str,
14572 file_extension: Option<String>,
14573 cx: &App,
14574 ) {
14575 if cfg!(any(test, feature = "test-support")) {
14576 return;
14577 }
14578
14579 let Some(project) = &self.project else { return };
14580
14581 // If None, we are in a file without an extension
14582 let file = self
14583 .buffer
14584 .read(cx)
14585 .as_singleton()
14586 .and_then(|b| b.read(cx).file());
14587 let file_extension = file_extension.or(file
14588 .as_ref()
14589 .and_then(|file| Path::new(file.file_name(cx)).extension())
14590 .and_then(|e| e.to_str())
14591 .map(|a| a.to_string()));
14592
14593 let vim_mode = cx
14594 .global::<SettingsStore>()
14595 .raw_user_settings()
14596 .get("vim_mode")
14597 == Some(&serde_json::Value::Bool(true));
14598
14599 let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
14600 let copilot_enabled = edit_predictions_provider
14601 == language::language_settings::EditPredictionProvider::Copilot;
14602 let copilot_enabled_for_language = self
14603 .buffer
14604 .read(cx)
14605 .settings_at(0, cx)
14606 .show_edit_predictions;
14607
14608 let project = project.read(cx);
14609 telemetry::event!(
14610 event_type,
14611 file_extension,
14612 vim_mode,
14613 copilot_enabled,
14614 copilot_enabled_for_language,
14615 edit_predictions_provider,
14616 is_via_ssh = project.is_via_ssh(),
14617 );
14618 }
14619
14620 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
14621 /// with each line being an array of {text, highlight} objects.
14622 fn copy_highlight_json(
14623 &mut self,
14624 _: &CopyHighlightJson,
14625 window: &mut Window,
14626 cx: &mut Context<Self>,
14627 ) {
14628 #[derive(Serialize)]
14629 struct Chunk<'a> {
14630 text: String,
14631 highlight: Option<&'a str>,
14632 }
14633
14634 let snapshot = self.buffer.read(cx).snapshot(cx);
14635 let range = self
14636 .selected_text_range(false, window, cx)
14637 .and_then(|selection| {
14638 if selection.range.is_empty() {
14639 None
14640 } else {
14641 Some(selection.range)
14642 }
14643 })
14644 .unwrap_or_else(|| 0..snapshot.len());
14645
14646 let chunks = snapshot.chunks(range, true);
14647 let mut lines = Vec::new();
14648 let mut line: VecDeque<Chunk> = VecDeque::new();
14649
14650 let Some(style) = self.style.as_ref() else {
14651 return;
14652 };
14653
14654 for chunk in chunks {
14655 let highlight = chunk
14656 .syntax_highlight_id
14657 .and_then(|id| id.name(&style.syntax));
14658 let mut chunk_lines = chunk.text.split('\n').peekable();
14659 while let Some(text) = chunk_lines.next() {
14660 let mut merged_with_last_token = false;
14661 if let Some(last_token) = line.back_mut() {
14662 if last_token.highlight == highlight {
14663 last_token.text.push_str(text);
14664 merged_with_last_token = true;
14665 }
14666 }
14667
14668 if !merged_with_last_token {
14669 line.push_back(Chunk {
14670 text: text.into(),
14671 highlight,
14672 });
14673 }
14674
14675 if chunk_lines.peek().is_some() {
14676 if line.len() > 1 && line.front().unwrap().text.is_empty() {
14677 line.pop_front();
14678 }
14679 if line.len() > 1 && line.back().unwrap().text.is_empty() {
14680 line.pop_back();
14681 }
14682
14683 lines.push(mem::take(&mut line));
14684 }
14685 }
14686 }
14687
14688 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
14689 return;
14690 };
14691 cx.write_to_clipboard(ClipboardItem::new_string(lines));
14692 }
14693
14694 pub fn open_context_menu(
14695 &mut self,
14696 _: &OpenContextMenu,
14697 window: &mut Window,
14698 cx: &mut Context<Self>,
14699 ) {
14700 self.request_autoscroll(Autoscroll::newest(), cx);
14701 let position = self.selections.newest_display(cx).start;
14702 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
14703 }
14704
14705 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
14706 &self.inlay_hint_cache
14707 }
14708
14709 pub fn replay_insert_event(
14710 &mut self,
14711 text: &str,
14712 relative_utf16_range: Option<Range<isize>>,
14713 window: &mut Window,
14714 cx: &mut Context<Self>,
14715 ) {
14716 if !self.input_enabled {
14717 cx.emit(EditorEvent::InputIgnored { text: text.into() });
14718 return;
14719 }
14720 if let Some(relative_utf16_range) = relative_utf16_range {
14721 let selections = self.selections.all::<OffsetUtf16>(cx);
14722 self.change_selections(None, window, cx, |s| {
14723 let new_ranges = selections.into_iter().map(|range| {
14724 let start = OffsetUtf16(
14725 range
14726 .head()
14727 .0
14728 .saturating_add_signed(relative_utf16_range.start),
14729 );
14730 let end = OffsetUtf16(
14731 range
14732 .head()
14733 .0
14734 .saturating_add_signed(relative_utf16_range.end),
14735 );
14736 start..end
14737 });
14738 s.select_ranges(new_ranges);
14739 });
14740 }
14741
14742 self.handle_input(text, window, cx);
14743 }
14744
14745 pub fn supports_inlay_hints(&self, cx: &App) -> bool {
14746 let Some(provider) = self.semantics_provider.as_ref() else {
14747 return false;
14748 };
14749
14750 let mut supports = false;
14751 self.buffer().read(cx).for_each_buffer(|buffer| {
14752 supports |= provider.supports_inlay_hints(buffer, cx);
14753 });
14754 supports
14755 }
14756
14757 pub fn is_focused(&self, window: &Window) -> bool {
14758 self.focus_handle.is_focused(window)
14759 }
14760
14761 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
14762 cx.emit(EditorEvent::Focused);
14763
14764 if let Some(descendant) = self
14765 .last_focused_descendant
14766 .take()
14767 .and_then(|descendant| descendant.upgrade())
14768 {
14769 window.focus(&descendant);
14770 } else {
14771 if let Some(blame) = self.blame.as_ref() {
14772 blame.update(cx, GitBlame::focus)
14773 }
14774
14775 self.blink_manager.update(cx, BlinkManager::enable);
14776 self.show_cursor_names(window, cx);
14777 self.buffer.update(cx, |buffer, cx| {
14778 buffer.finalize_last_transaction(cx);
14779 if self.leader_peer_id.is_none() {
14780 buffer.set_active_selections(
14781 &self.selections.disjoint_anchors(),
14782 self.selections.line_mode,
14783 self.cursor_shape,
14784 cx,
14785 );
14786 }
14787 });
14788 }
14789 }
14790
14791 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
14792 cx.emit(EditorEvent::FocusedIn)
14793 }
14794
14795 fn handle_focus_out(
14796 &mut self,
14797 event: FocusOutEvent,
14798 _window: &mut Window,
14799 _cx: &mut Context<Self>,
14800 ) {
14801 if event.blurred != self.focus_handle {
14802 self.last_focused_descendant = Some(event.blurred);
14803 }
14804 }
14805
14806 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
14807 self.blink_manager.update(cx, BlinkManager::disable);
14808 self.buffer
14809 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
14810
14811 if let Some(blame) = self.blame.as_ref() {
14812 blame.update(cx, GitBlame::blur)
14813 }
14814 if !self.hover_state.focused(window, cx) {
14815 hide_hover(self, cx);
14816 }
14817
14818 self.hide_context_menu(window, cx);
14819 cx.emit(EditorEvent::Blurred);
14820 cx.notify();
14821 }
14822
14823 pub fn register_action<A: Action>(
14824 &mut self,
14825 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
14826 ) -> Subscription {
14827 let id = self.next_editor_action_id.post_inc();
14828 let listener = Arc::new(listener);
14829 self.editor_actions.borrow_mut().insert(
14830 id,
14831 Box::new(move |window, _| {
14832 let listener = listener.clone();
14833 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
14834 let action = action.downcast_ref().unwrap();
14835 if phase == DispatchPhase::Bubble {
14836 listener(action, window, cx)
14837 }
14838 })
14839 }),
14840 );
14841
14842 let editor_actions = self.editor_actions.clone();
14843 Subscription::new(move || {
14844 editor_actions.borrow_mut().remove(&id);
14845 })
14846 }
14847
14848 pub fn file_header_size(&self) -> u32 {
14849 FILE_HEADER_HEIGHT
14850 }
14851
14852 pub fn revert(
14853 &mut self,
14854 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
14855 window: &mut Window,
14856 cx: &mut Context<Self>,
14857 ) {
14858 self.buffer().update(cx, |multi_buffer, cx| {
14859 for (buffer_id, changes) in revert_changes {
14860 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
14861 buffer.update(cx, |buffer, cx| {
14862 buffer.edit(
14863 changes.into_iter().map(|(range, text)| {
14864 (range, text.to_string().map(Arc::<str>::from))
14865 }),
14866 None,
14867 cx,
14868 );
14869 });
14870 }
14871 }
14872 });
14873 self.change_selections(None, window, cx, |selections| selections.refresh());
14874 }
14875
14876 pub fn to_pixel_point(
14877 &self,
14878 source: multi_buffer::Anchor,
14879 editor_snapshot: &EditorSnapshot,
14880 window: &mut Window,
14881 ) -> Option<gpui::Point<Pixels>> {
14882 let source_point = source.to_display_point(editor_snapshot);
14883 self.display_to_pixel_point(source_point, editor_snapshot, window)
14884 }
14885
14886 pub fn display_to_pixel_point(
14887 &self,
14888 source: DisplayPoint,
14889 editor_snapshot: &EditorSnapshot,
14890 window: &mut Window,
14891 ) -> Option<gpui::Point<Pixels>> {
14892 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
14893 let text_layout_details = self.text_layout_details(window);
14894 let scroll_top = text_layout_details
14895 .scroll_anchor
14896 .scroll_position(editor_snapshot)
14897 .y;
14898
14899 if source.row().as_f32() < scroll_top.floor() {
14900 return None;
14901 }
14902 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
14903 let source_y = line_height * (source.row().as_f32() - scroll_top);
14904 Some(gpui::Point::new(source_x, source_y))
14905 }
14906
14907 pub fn has_visible_completions_menu(&self) -> bool {
14908 !self.edit_prediction_preview.is_active()
14909 && self.context_menu.borrow().as_ref().map_or(false, |menu| {
14910 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
14911 })
14912 }
14913
14914 pub fn register_addon<T: Addon>(&mut self, instance: T) {
14915 self.addons
14916 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
14917 }
14918
14919 pub fn unregister_addon<T: Addon>(&mut self) {
14920 self.addons.remove(&std::any::TypeId::of::<T>());
14921 }
14922
14923 pub fn addon<T: Addon>(&self) -> Option<&T> {
14924 let type_id = std::any::TypeId::of::<T>();
14925 self.addons
14926 .get(&type_id)
14927 .and_then(|item| item.to_any().downcast_ref::<T>())
14928 }
14929
14930 fn character_size(&self, window: &mut Window) -> gpui::Size<Pixels> {
14931 let text_layout_details = self.text_layout_details(window);
14932 let style = &text_layout_details.editor_style;
14933 let font_id = window.text_system().resolve_font(&style.text.font());
14934 let font_size = style.text.font_size.to_pixels(window.rem_size());
14935 let line_height = style.text.line_height_in_pixels(window.rem_size());
14936 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
14937
14938 gpui::Size::new(em_width, line_height)
14939 }
14940}
14941
14942fn get_uncommitted_diff_for_buffer(
14943 project: &Entity<Project>,
14944 buffers: impl IntoIterator<Item = Entity<Buffer>>,
14945 buffer: Entity<MultiBuffer>,
14946 cx: &mut App,
14947) {
14948 let mut tasks = Vec::new();
14949 project.update(cx, |project, cx| {
14950 for buffer in buffers {
14951 tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
14952 }
14953 });
14954 cx.spawn(|mut cx| async move {
14955 let diffs = futures::future::join_all(tasks).await;
14956 buffer
14957 .update(&mut cx, |buffer, cx| {
14958 for diff in diffs.into_iter().flatten() {
14959 buffer.add_diff(diff, cx);
14960 }
14961 })
14962 .ok();
14963 })
14964 .detach();
14965}
14966
14967fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
14968 let tab_size = tab_size.get() as usize;
14969 let mut width = offset;
14970
14971 for ch in text.chars() {
14972 width += if ch == '\t' {
14973 tab_size - (width % tab_size)
14974 } else {
14975 1
14976 };
14977 }
14978
14979 width - offset
14980}
14981
14982#[cfg(test)]
14983mod tests {
14984 use super::*;
14985
14986 #[test]
14987 fn test_string_size_with_expanded_tabs() {
14988 let nz = |val| NonZeroU32::new(val).unwrap();
14989 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
14990 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
14991 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
14992 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
14993 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
14994 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
14995 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
14996 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
14997 }
14998}
14999
15000/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
15001struct WordBreakingTokenizer<'a> {
15002 input: &'a str,
15003}
15004
15005impl<'a> WordBreakingTokenizer<'a> {
15006 fn new(input: &'a str) -> Self {
15007 Self { input }
15008 }
15009}
15010
15011fn is_char_ideographic(ch: char) -> bool {
15012 use unicode_script::Script::*;
15013 use unicode_script::UnicodeScript;
15014 matches!(ch.script(), Han | Tangut | Yi)
15015}
15016
15017fn is_grapheme_ideographic(text: &str) -> bool {
15018 text.chars().any(is_char_ideographic)
15019}
15020
15021fn is_grapheme_whitespace(text: &str) -> bool {
15022 text.chars().any(|x| x.is_whitespace())
15023}
15024
15025fn should_stay_with_preceding_ideograph(text: &str) -> bool {
15026 text.chars().next().map_or(false, |ch| {
15027 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
15028 })
15029}
15030
15031#[derive(PartialEq, Eq, Debug, Clone, Copy)]
15032struct WordBreakToken<'a> {
15033 token: &'a str,
15034 grapheme_len: usize,
15035 is_whitespace: bool,
15036}
15037
15038impl<'a> Iterator for WordBreakingTokenizer<'a> {
15039 /// Yields a span, the count of graphemes in the token, and whether it was
15040 /// whitespace. Note that it also breaks at word boundaries.
15041 type Item = WordBreakToken<'a>;
15042
15043 fn next(&mut self) -> Option<Self::Item> {
15044 use unicode_segmentation::UnicodeSegmentation;
15045 if self.input.is_empty() {
15046 return None;
15047 }
15048
15049 let mut iter = self.input.graphemes(true).peekable();
15050 let mut offset = 0;
15051 let mut graphemes = 0;
15052 if let Some(first_grapheme) = iter.next() {
15053 let is_whitespace = is_grapheme_whitespace(first_grapheme);
15054 offset += first_grapheme.len();
15055 graphemes += 1;
15056 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
15057 if let Some(grapheme) = iter.peek().copied() {
15058 if should_stay_with_preceding_ideograph(grapheme) {
15059 offset += grapheme.len();
15060 graphemes += 1;
15061 }
15062 }
15063 } else {
15064 let mut words = self.input[offset..].split_word_bound_indices().peekable();
15065 let mut next_word_bound = words.peek().copied();
15066 if next_word_bound.map_or(false, |(i, _)| i == 0) {
15067 next_word_bound = words.next();
15068 }
15069 while let Some(grapheme) = iter.peek().copied() {
15070 if next_word_bound.map_or(false, |(i, _)| i == offset) {
15071 break;
15072 };
15073 if is_grapheme_whitespace(grapheme) != is_whitespace {
15074 break;
15075 };
15076 offset += grapheme.len();
15077 graphemes += 1;
15078 iter.next();
15079 }
15080 }
15081 let token = &self.input[..offset];
15082 self.input = &self.input[offset..];
15083 if is_whitespace {
15084 Some(WordBreakToken {
15085 token: " ",
15086 grapheme_len: 1,
15087 is_whitespace: true,
15088 })
15089 } else {
15090 Some(WordBreakToken {
15091 token,
15092 grapheme_len: graphemes,
15093 is_whitespace: false,
15094 })
15095 }
15096 } else {
15097 None
15098 }
15099 }
15100}
15101
15102#[test]
15103fn test_word_breaking_tokenizer() {
15104 let tests: &[(&str, &[(&str, usize, bool)])] = &[
15105 ("", &[]),
15106 (" ", &[(" ", 1, true)]),
15107 ("Ʒ", &[("Ʒ", 1, false)]),
15108 ("Ǽ", &[("Ǽ", 1, false)]),
15109 ("⋑", &[("⋑", 1, false)]),
15110 ("⋑⋑", &[("⋑⋑", 2, false)]),
15111 (
15112 "原理,进而",
15113 &[
15114 ("原", 1, false),
15115 ("理,", 2, false),
15116 ("进", 1, false),
15117 ("而", 1, false),
15118 ],
15119 ),
15120 (
15121 "hello world",
15122 &[("hello", 5, false), (" ", 1, true), ("world", 5, false)],
15123 ),
15124 (
15125 "hello, world",
15126 &[("hello,", 6, false), (" ", 1, true), ("world", 5, false)],
15127 ),
15128 (
15129 " hello world",
15130 &[
15131 (" ", 1, true),
15132 ("hello", 5, false),
15133 (" ", 1, true),
15134 ("world", 5, false),
15135 ],
15136 ),
15137 (
15138 "这是什么 \n 钢笔",
15139 &[
15140 ("这", 1, false),
15141 ("是", 1, false),
15142 ("什", 1, false),
15143 ("么", 1, false),
15144 (" ", 1, true),
15145 ("钢", 1, false),
15146 ("笔", 1, false),
15147 ],
15148 ),
15149 (" mutton", &[(" ", 1, true), ("mutton", 6, false)]),
15150 ];
15151
15152 for (input, result) in tests {
15153 assert_eq!(
15154 WordBreakingTokenizer::new(input).collect::<Vec<_>>(),
15155 result
15156 .iter()
15157 .copied()
15158 .map(|(token, grapheme_len, is_whitespace)| WordBreakToken {
15159 token,
15160 grapheme_len,
15161 is_whitespace,
15162 })
15163 .collect::<Vec<_>>()
15164 );
15165 }
15166}
15167
15168fn wrap_with_prefix(
15169 line_prefix: String,
15170 unwrapped_text: String,
15171 wrap_column: usize,
15172 tab_size: NonZeroU32,
15173) -> String {
15174 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
15175 let mut wrapped_text = String::new();
15176 let mut current_line = line_prefix.clone();
15177
15178 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
15179 let mut current_line_len = line_prefix_len;
15180 for WordBreakToken {
15181 token,
15182 grapheme_len,
15183 is_whitespace,
15184 } in tokenizer
15185 {
15186 if current_line_len + grapheme_len > wrap_column && current_line_len != line_prefix_len {
15187 wrapped_text.push_str(current_line.trim_end());
15188 wrapped_text.push('\n');
15189 current_line.truncate(line_prefix.len());
15190 current_line_len = line_prefix_len;
15191 if !is_whitespace {
15192 current_line.push_str(token);
15193 current_line_len += grapheme_len;
15194 }
15195 } else if !is_whitespace {
15196 current_line.push_str(token);
15197 current_line_len += grapheme_len;
15198 } else if current_line_len != line_prefix_len {
15199 current_line.push(' ');
15200 current_line_len += 1;
15201 }
15202 }
15203
15204 if !current_line.is_empty() {
15205 wrapped_text.push_str(¤t_line);
15206 }
15207 wrapped_text
15208}
15209
15210#[test]
15211fn test_wrap_with_prefix() {
15212 assert_eq!(
15213 wrap_with_prefix(
15214 "# ".to_string(),
15215 "abcdefg".to_string(),
15216 4,
15217 NonZeroU32::new(4).unwrap()
15218 ),
15219 "# abcdefg"
15220 );
15221 assert_eq!(
15222 wrap_with_prefix(
15223 "".to_string(),
15224 "\thello world".to_string(),
15225 8,
15226 NonZeroU32::new(4).unwrap()
15227 ),
15228 "hello\nworld"
15229 );
15230 assert_eq!(
15231 wrap_with_prefix(
15232 "// ".to_string(),
15233 "xx \nyy zz aa bb cc".to_string(),
15234 12,
15235 NonZeroU32::new(4).unwrap()
15236 ),
15237 "// xx yy zz\n// aa bb cc"
15238 );
15239 assert_eq!(
15240 wrap_with_prefix(
15241 String::new(),
15242 "这是什么 \n 钢笔".to_string(),
15243 3,
15244 NonZeroU32::new(4).unwrap()
15245 ),
15246 "这是什\n么 钢\n笔"
15247 );
15248}
15249
15250pub trait CollaborationHub {
15251 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
15252 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
15253 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
15254}
15255
15256impl CollaborationHub for Entity<Project> {
15257 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
15258 self.read(cx).collaborators()
15259 }
15260
15261 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
15262 self.read(cx).user_store().read(cx).participant_indices()
15263 }
15264
15265 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
15266 let this = self.read(cx);
15267 let user_ids = this.collaborators().values().map(|c| c.user_id);
15268 this.user_store().read_with(cx, |user_store, cx| {
15269 user_store.participant_names(user_ids, cx)
15270 })
15271 }
15272}
15273
15274pub trait SemanticsProvider {
15275 fn hover(
15276 &self,
15277 buffer: &Entity<Buffer>,
15278 position: text::Anchor,
15279 cx: &mut App,
15280 ) -> Option<Task<Vec<project::Hover>>>;
15281
15282 fn inlay_hints(
15283 &self,
15284 buffer_handle: Entity<Buffer>,
15285 range: Range<text::Anchor>,
15286 cx: &mut App,
15287 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
15288
15289 fn resolve_inlay_hint(
15290 &self,
15291 hint: InlayHint,
15292 buffer_handle: Entity<Buffer>,
15293 server_id: LanguageServerId,
15294 cx: &mut App,
15295 ) -> Option<Task<anyhow::Result<InlayHint>>>;
15296
15297 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &App) -> bool;
15298
15299 fn document_highlights(
15300 &self,
15301 buffer: &Entity<Buffer>,
15302 position: text::Anchor,
15303 cx: &mut App,
15304 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
15305
15306 fn definitions(
15307 &self,
15308 buffer: &Entity<Buffer>,
15309 position: text::Anchor,
15310 kind: GotoDefinitionKind,
15311 cx: &mut App,
15312 ) -> Option<Task<Result<Vec<LocationLink>>>>;
15313
15314 fn range_for_rename(
15315 &self,
15316 buffer: &Entity<Buffer>,
15317 position: text::Anchor,
15318 cx: &mut App,
15319 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
15320
15321 fn perform_rename(
15322 &self,
15323 buffer: &Entity<Buffer>,
15324 position: text::Anchor,
15325 new_name: String,
15326 cx: &mut App,
15327 ) -> Option<Task<Result<ProjectTransaction>>>;
15328}
15329
15330pub trait CompletionProvider {
15331 fn completions(
15332 &self,
15333 buffer: &Entity<Buffer>,
15334 buffer_position: text::Anchor,
15335 trigger: CompletionContext,
15336 window: &mut Window,
15337 cx: &mut Context<Editor>,
15338 ) -> Task<Result<Vec<Completion>>>;
15339
15340 fn resolve_completions(
15341 &self,
15342 buffer: Entity<Buffer>,
15343 completion_indices: Vec<usize>,
15344 completions: Rc<RefCell<Box<[Completion]>>>,
15345 cx: &mut Context<Editor>,
15346 ) -> Task<Result<bool>>;
15347
15348 fn apply_additional_edits_for_completion(
15349 &self,
15350 _buffer: Entity<Buffer>,
15351 _completions: Rc<RefCell<Box<[Completion]>>>,
15352 _completion_index: usize,
15353 _push_to_history: bool,
15354 _cx: &mut Context<Editor>,
15355 ) -> Task<Result<Option<language::Transaction>>> {
15356 Task::ready(Ok(None))
15357 }
15358
15359 fn is_completion_trigger(
15360 &self,
15361 buffer: &Entity<Buffer>,
15362 position: language::Anchor,
15363 text: &str,
15364 trigger_in_words: bool,
15365 cx: &mut Context<Editor>,
15366 ) -> bool;
15367
15368 fn sort_completions(&self) -> bool {
15369 true
15370 }
15371}
15372
15373pub trait CodeActionProvider {
15374 fn id(&self) -> Arc<str>;
15375
15376 fn code_actions(
15377 &self,
15378 buffer: &Entity<Buffer>,
15379 range: Range<text::Anchor>,
15380 window: &mut Window,
15381 cx: &mut App,
15382 ) -> Task<Result<Vec<CodeAction>>>;
15383
15384 fn apply_code_action(
15385 &self,
15386 buffer_handle: Entity<Buffer>,
15387 action: CodeAction,
15388 excerpt_id: ExcerptId,
15389 push_to_history: bool,
15390 window: &mut Window,
15391 cx: &mut App,
15392 ) -> Task<Result<ProjectTransaction>>;
15393}
15394
15395impl CodeActionProvider for Entity<Project> {
15396 fn id(&self) -> Arc<str> {
15397 "project".into()
15398 }
15399
15400 fn code_actions(
15401 &self,
15402 buffer: &Entity<Buffer>,
15403 range: Range<text::Anchor>,
15404 _window: &mut Window,
15405 cx: &mut App,
15406 ) -> Task<Result<Vec<CodeAction>>> {
15407 self.update(cx, |project, cx| {
15408 project.code_actions(buffer, range, None, cx)
15409 })
15410 }
15411
15412 fn apply_code_action(
15413 &self,
15414 buffer_handle: Entity<Buffer>,
15415 action: CodeAction,
15416 _excerpt_id: ExcerptId,
15417 push_to_history: bool,
15418 _window: &mut Window,
15419 cx: &mut App,
15420 ) -> Task<Result<ProjectTransaction>> {
15421 self.update(cx, |project, cx| {
15422 project.apply_code_action(buffer_handle, action, push_to_history, cx)
15423 })
15424 }
15425}
15426
15427fn snippet_completions(
15428 project: &Project,
15429 buffer: &Entity<Buffer>,
15430 buffer_position: text::Anchor,
15431 cx: &mut App,
15432) -> Task<Result<Vec<Completion>>> {
15433 let language = buffer.read(cx).language_at(buffer_position);
15434 let language_name = language.as_ref().map(|language| language.lsp_id());
15435 let snippet_store = project.snippets().read(cx);
15436 let snippets = snippet_store.snippets_for(language_name, cx);
15437
15438 if snippets.is_empty() {
15439 return Task::ready(Ok(vec![]));
15440 }
15441 let snapshot = buffer.read(cx).text_snapshot();
15442 let chars: String = snapshot
15443 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
15444 .collect();
15445
15446 let scope = language.map(|language| language.default_scope());
15447 let executor = cx.background_executor().clone();
15448
15449 cx.background_executor().spawn(async move {
15450 let classifier = CharClassifier::new(scope).for_completion(true);
15451 let mut last_word = chars
15452 .chars()
15453 .take_while(|c| classifier.is_word(*c))
15454 .collect::<String>();
15455 last_word = last_word.chars().rev().collect();
15456
15457 if last_word.is_empty() {
15458 return Ok(vec![]);
15459 }
15460
15461 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
15462 let to_lsp = |point: &text::Anchor| {
15463 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
15464 point_to_lsp(end)
15465 };
15466 let lsp_end = to_lsp(&buffer_position);
15467
15468 let candidates = snippets
15469 .iter()
15470 .enumerate()
15471 .flat_map(|(ix, snippet)| {
15472 snippet
15473 .prefix
15474 .iter()
15475 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
15476 })
15477 .collect::<Vec<StringMatchCandidate>>();
15478
15479 let mut matches = fuzzy::match_strings(
15480 &candidates,
15481 &last_word,
15482 last_word.chars().any(|c| c.is_uppercase()),
15483 100,
15484 &Default::default(),
15485 executor,
15486 )
15487 .await;
15488
15489 // Remove all candidates where the query's start does not match the start of any word in the candidate
15490 if let Some(query_start) = last_word.chars().next() {
15491 matches.retain(|string_match| {
15492 split_words(&string_match.string).any(|word| {
15493 // Check that the first codepoint of the word as lowercase matches the first
15494 // codepoint of the query as lowercase
15495 word.chars()
15496 .flat_map(|codepoint| codepoint.to_lowercase())
15497 .zip(query_start.to_lowercase())
15498 .all(|(word_cp, query_cp)| word_cp == query_cp)
15499 })
15500 });
15501 }
15502
15503 let matched_strings = matches
15504 .into_iter()
15505 .map(|m| m.string)
15506 .collect::<HashSet<_>>();
15507
15508 let result: Vec<Completion> = snippets
15509 .into_iter()
15510 .filter_map(|snippet| {
15511 let matching_prefix = snippet
15512 .prefix
15513 .iter()
15514 .find(|prefix| matched_strings.contains(*prefix))?;
15515 let start = as_offset - last_word.len();
15516 let start = snapshot.anchor_before(start);
15517 let range = start..buffer_position;
15518 let lsp_start = to_lsp(&start);
15519 let lsp_range = lsp::Range {
15520 start: lsp_start,
15521 end: lsp_end,
15522 };
15523 Some(Completion {
15524 old_range: range,
15525 new_text: snippet.body.clone(),
15526 resolved: false,
15527 label: CodeLabel {
15528 text: matching_prefix.clone(),
15529 runs: vec![],
15530 filter_range: 0..matching_prefix.len(),
15531 },
15532 server_id: LanguageServerId(usize::MAX),
15533 documentation: snippet
15534 .description
15535 .clone()
15536 .map(CompletionDocumentation::SingleLine),
15537 lsp_completion: lsp::CompletionItem {
15538 label: snippet.prefix.first().unwrap().clone(),
15539 kind: Some(CompletionItemKind::SNIPPET),
15540 label_details: snippet.description.as_ref().map(|description| {
15541 lsp::CompletionItemLabelDetails {
15542 detail: Some(description.clone()),
15543 description: None,
15544 }
15545 }),
15546 insert_text_format: Some(InsertTextFormat::SNIPPET),
15547 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
15548 lsp::InsertReplaceEdit {
15549 new_text: snippet.body.clone(),
15550 insert: lsp_range,
15551 replace: lsp_range,
15552 },
15553 )),
15554 filter_text: Some(snippet.body.clone()),
15555 sort_text: Some(char::MAX.to_string()),
15556 ..Default::default()
15557 },
15558 confirm: None,
15559 })
15560 })
15561 .collect();
15562
15563 Ok(result)
15564 })
15565}
15566
15567impl CompletionProvider for Entity<Project> {
15568 fn completions(
15569 &self,
15570 buffer: &Entity<Buffer>,
15571 buffer_position: text::Anchor,
15572 options: CompletionContext,
15573 _window: &mut Window,
15574 cx: &mut Context<Editor>,
15575 ) -> Task<Result<Vec<Completion>>> {
15576 self.update(cx, |project, cx| {
15577 let snippets = snippet_completions(project, buffer, buffer_position, cx);
15578 let project_completions = project.completions(buffer, buffer_position, options, cx);
15579 cx.background_executor().spawn(async move {
15580 let mut completions = project_completions.await?;
15581 let snippets_completions = snippets.await?;
15582 completions.extend(snippets_completions);
15583 Ok(completions)
15584 })
15585 })
15586 }
15587
15588 fn resolve_completions(
15589 &self,
15590 buffer: Entity<Buffer>,
15591 completion_indices: Vec<usize>,
15592 completions: Rc<RefCell<Box<[Completion]>>>,
15593 cx: &mut Context<Editor>,
15594 ) -> Task<Result<bool>> {
15595 self.update(cx, |project, cx| {
15596 project.lsp_store().update(cx, |lsp_store, cx| {
15597 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
15598 })
15599 })
15600 }
15601
15602 fn apply_additional_edits_for_completion(
15603 &self,
15604 buffer: Entity<Buffer>,
15605 completions: Rc<RefCell<Box<[Completion]>>>,
15606 completion_index: usize,
15607 push_to_history: bool,
15608 cx: &mut Context<Editor>,
15609 ) -> Task<Result<Option<language::Transaction>>> {
15610 self.update(cx, |project, cx| {
15611 project.lsp_store().update(cx, |lsp_store, cx| {
15612 lsp_store.apply_additional_edits_for_completion(
15613 buffer,
15614 completions,
15615 completion_index,
15616 push_to_history,
15617 cx,
15618 )
15619 })
15620 })
15621 }
15622
15623 fn is_completion_trigger(
15624 &self,
15625 buffer: &Entity<Buffer>,
15626 position: language::Anchor,
15627 text: &str,
15628 trigger_in_words: bool,
15629 cx: &mut Context<Editor>,
15630 ) -> bool {
15631 let mut chars = text.chars();
15632 let char = if let Some(char) = chars.next() {
15633 char
15634 } else {
15635 return false;
15636 };
15637 if chars.next().is_some() {
15638 return false;
15639 }
15640
15641 let buffer = buffer.read(cx);
15642 let snapshot = buffer.snapshot();
15643 if !snapshot.settings_at(position, cx).show_completions_on_input {
15644 return false;
15645 }
15646 let classifier = snapshot.char_classifier_at(position).for_completion(true);
15647 if trigger_in_words && classifier.is_word(char) {
15648 return true;
15649 }
15650
15651 buffer.completion_triggers().contains(text)
15652 }
15653}
15654
15655impl SemanticsProvider for Entity<Project> {
15656 fn hover(
15657 &self,
15658 buffer: &Entity<Buffer>,
15659 position: text::Anchor,
15660 cx: &mut App,
15661 ) -> Option<Task<Vec<project::Hover>>> {
15662 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
15663 }
15664
15665 fn document_highlights(
15666 &self,
15667 buffer: &Entity<Buffer>,
15668 position: text::Anchor,
15669 cx: &mut App,
15670 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
15671 Some(self.update(cx, |project, cx| {
15672 project.document_highlights(buffer, position, cx)
15673 }))
15674 }
15675
15676 fn definitions(
15677 &self,
15678 buffer: &Entity<Buffer>,
15679 position: text::Anchor,
15680 kind: GotoDefinitionKind,
15681 cx: &mut App,
15682 ) -> Option<Task<Result<Vec<LocationLink>>>> {
15683 Some(self.update(cx, |project, cx| match kind {
15684 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
15685 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
15686 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
15687 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
15688 }))
15689 }
15690
15691 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &App) -> bool {
15692 // TODO: make this work for remote projects
15693 self.read(cx)
15694 .language_servers_for_local_buffer(buffer.read(cx), cx)
15695 .any(
15696 |(_, server)| match server.capabilities().inlay_hint_provider {
15697 Some(lsp::OneOf::Left(enabled)) => enabled,
15698 Some(lsp::OneOf::Right(_)) => true,
15699 None => false,
15700 },
15701 )
15702 }
15703
15704 fn inlay_hints(
15705 &self,
15706 buffer_handle: Entity<Buffer>,
15707 range: Range<text::Anchor>,
15708 cx: &mut App,
15709 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
15710 Some(self.update(cx, |project, cx| {
15711 project.inlay_hints(buffer_handle, range, cx)
15712 }))
15713 }
15714
15715 fn resolve_inlay_hint(
15716 &self,
15717 hint: InlayHint,
15718 buffer_handle: Entity<Buffer>,
15719 server_id: LanguageServerId,
15720 cx: &mut App,
15721 ) -> Option<Task<anyhow::Result<InlayHint>>> {
15722 Some(self.update(cx, |project, cx| {
15723 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
15724 }))
15725 }
15726
15727 fn range_for_rename(
15728 &self,
15729 buffer: &Entity<Buffer>,
15730 position: text::Anchor,
15731 cx: &mut App,
15732 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
15733 Some(self.update(cx, |project, cx| {
15734 let buffer = buffer.clone();
15735 let task = project.prepare_rename(buffer.clone(), position, cx);
15736 cx.spawn(|_, mut cx| async move {
15737 Ok(match task.await? {
15738 PrepareRenameResponse::Success(range) => Some(range),
15739 PrepareRenameResponse::InvalidPosition => None,
15740 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
15741 // Fallback on using TreeSitter info to determine identifier range
15742 buffer.update(&mut cx, |buffer, _| {
15743 let snapshot = buffer.snapshot();
15744 let (range, kind) = snapshot.surrounding_word(position);
15745 if kind != Some(CharKind::Word) {
15746 return None;
15747 }
15748 Some(
15749 snapshot.anchor_before(range.start)
15750 ..snapshot.anchor_after(range.end),
15751 )
15752 })?
15753 }
15754 })
15755 })
15756 }))
15757 }
15758
15759 fn perform_rename(
15760 &self,
15761 buffer: &Entity<Buffer>,
15762 position: text::Anchor,
15763 new_name: String,
15764 cx: &mut App,
15765 ) -> Option<Task<Result<ProjectTransaction>>> {
15766 Some(self.update(cx, |project, cx| {
15767 project.perform_rename(buffer.clone(), position, new_name, cx)
15768 }))
15769 }
15770}
15771
15772fn inlay_hint_settings(
15773 location: Anchor,
15774 snapshot: &MultiBufferSnapshot,
15775 cx: &mut Context<Editor>,
15776) -> InlayHintSettings {
15777 let file = snapshot.file_at(location);
15778 let language = snapshot.language_at(location).map(|l| l.name());
15779 language_settings(language, file, cx).inlay_hints
15780}
15781
15782fn consume_contiguous_rows(
15783 contiguous_row_selections: &mut Vec<Selection<Point>>,
15784 selection: &Selection<Point>,
15785 display_map: &DisplaySnapshot,
15786 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
15787) -> (MultiBufferRow, MultiBufferRow) {
15788 contiguous_row_selections.push(selection.clone());
15789 let start_row = MultiBufferRow(selection.start.row);
15790 let mut end_row = ending_row(selection, display_map);
15791
15792 while let Some(next_selection) = selections.peek() {
15793 if next_selection.start.row <= end_row.0 {
15794 end_row = ending_row(next_selection, display_map);
15795 contiguous_row_selections.push(selections.next().unwrap().clone());
15796 } else {
15797 break;
15798 }
15799 }
15800 (start_row, end_row)
15801}
15802
15803fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
15804 if next_selection.end.column > 0 || next_selection.is_empty() {
15805 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
15806 } else {
15807 MultiBufferRow(next_selection.end.row)
15808 }
15809}
15810
15811impl EditorSnapshot {
15812 pub fn remote_selections_in_range<'a>(
15813 &'a self,
15814 range: &'a Range<Anchor>,
15815 collaboration_hub: &dyn CollaborationHub,
15816 cx: &'a App,
15817 ) -> impl 'a + Iterator<Item = RemoteSelection> {
15818 let participant_names = collaboration_hub.user_names(cx);
15819 let participant_indices = collaboration_hub.user_participant_indices(cx);
15820 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
15821 let collaborators_by_replica_id = collaborators_by_peer_id
15822 .iter()
15823 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
15824 .collect::<HashMap<_, _>>();
15825 self.buffer_snapshot
15826 .selections_in_range(range, false)
15827 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
15828 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
15829 let participant_index = participant_indices.get(&collaborator.user_id).copied();
15830 let user_name = participant_names.get(&collaborator.user_id).cloned();
15831 Some(RemoteSelection {
15832 replica_id,
15833 selection,
15834 cursor_shape,
15835 line_mode,
15836 participant_index,
15837 peer_id: collaborator.peer_id,
15838 user_name,
15839 })
15840 })
15841 }
15842
15843 pub fn hunks_for_ranges(
15844 &self,
15845 ranges: impl Iterator<Item = Range<Point>>,
15846 ) -> Vec<MultiBufferDiffHunk> {
15847 let mut hunks = Vec::new();
15848 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
15849 HashMap::default();
15850 for query_range in ranges {
15851 let query_rows =
15852 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
15853 for hunk in self.buffer_snapshot.diff_hunks_in_range(
15854 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
15855 ) {
15856 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
15857 // when the caret is just above or just below the deleted hunk.
15858 let allow_adjacent = hunk.status().is_removed();
15859 let related_to_selection = if allow_adjacent {
15860 hunk.row_range.overlaps(&query_rows)
15861 || hunk.row_range.start == query_rows.end
15862 || hunk.row_range.end == query_rows.start
15863 } else {
15864 hunk.row_range.overlaps(&query_rows)
15865 };
15866 if related_to_selection {
15867 if !processed_buffer_rows
15868 .entry(hunk.buffer_id)
15869 .or_default()
15870 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
15871 {
15872 continue;
15873 }
15874 hunks.push(hunk);
15875 }
15876 }
15877 }
15878
15879 hunks
15880 }
15881
15882 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
15883 self.display_snapshot.buffer_snapshot.language_at(position)
15884 }
15885
15886 pub fn is_focused(&self) -> bool {
15887 self.is_focused
15888 }
15889
15890 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
15891 self.placeholder_text.as_ref()
15892 }
15893
15894 pub fn scroll_position(&self) -> gpui::Point<f32> {
15895 self.scroll_anchor.scroll_position(&self.display_snapshot)
15896 }
15897
15898 fn gutter_dimensions(
15899 &self,
15900 font_id: FontId,
15901 font_size: Pixels,
15902 max_line_number_width: Pixels,
15903 cx: &App,
15904 ) -> Option<GutterDimensions> {
15905 if !self.show_gutter {
15906 return None;
15907 }
15908
15909 let descent = cx.text_system().descent(font_id, font_size);
15910 let em_width = cx.text_system().em_width(font_id, font_size).log_err()?;
15911 let em_advance = cx.text_system().em_advance(font_id, font_size).log_err()?;
15912
15913 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
15914 matches!(
15915 ProjectSettings::get_global(cx).git.git_gutter,
15916 Some(GitGutterSetting::TrackedFiles)
15917 )
15918 });
15919 let gutter_settings = EditorSettings::get_global(cx).gutter;
15920 let show_line_numbers = self
15921 .show_line_numbers
15922 .unwrap_or(gutter_settings.line_numbers);
15923 let line_gutter_width = if show_line_numbers {
15924 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
15925 let min_width_for_number_on_gutter = em_advance * 4.0;
15926 max_line_number_width.max(min_width_for_number_on_gutter)
15927 } else {
15928 0.0.into()
15929 };
15930
15931 let show_code_actions = self
15932 .show_code_actions
15933 .unwrap_or(gutter_settings.code_actions);
15934
15935 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
15936
15937 let git_blame_entries_width =
15938 self.git_blame_gutter_max_author_length
15939 .map(|max_author_length| {
15940 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
15941
15942 /// The number of characters to dedicate to gaps and margins.
15943 const SPACING_WIDTH: usize = 4;
15944
15945 let max_char_count = max_author_length
15946 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
15947 + ::git::SHORT_SHA_LENGTH
15948 + MAX_RELATIVE_TIMESTAMP.len()
15949 + SPACING_WIDTH;
15950
15951 em_advance * max_char_count
15952 });
15953
15954 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
15955 left_padding += if show_code_actions || show_runnables {
15956 em_width * 3.0
15957 } else if show_git_gutter && show_line_numbers {
15958 em_width * 2.0
15959 } else if show_git_gutter || show_line_numbers {
15960 em_width
15961 } else {
15962 px(0.)
15963 };
15964
15965 let right_padding = if gutter_settings.folds && show_line_numbers {
15966 em_width * 4.0
15967 } else if gutter_settings.folds {
15968 em_width * 3.0
15969 } else if show_line_numbers {
15970 em_width
15971 } else {
15972 px(0.)
15973 };
15974
15975 Some(GutterDimensions {
15976 left_padding,
15977 right_padding,
15978 width: line_gutter_width + left_padding + right_padding,
15979 margin: -descent,
15980 git_blame_entries_width,
15981 })
15982 }
15983
15984 pub fn render_crease_toggle(
15985 &self,
15986 buffer_row: MultiBufferRow,
15987 row_contains_cursor: bool,
15988 editor: Entity<Editor>,
15989 window: &mut Window,
15990 cx: &mut App,
15991 ) -> Option<AnyElement> {
15992 let folded = self.is_line_folded(buffer_row);
15993 let mut is_foldable = false;
15994
15995 if let Some(crease) = self
15996 .crease_snapshot
15997 .query_row(buffer_row, &self.buffer_snapshot)
15998 {
15999 is_foldable = true;
16000 match crease {
16001 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
16002 if let Some(render_toggle) = render_toggle {
16003 let toggle_callback =
16004 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
16005 if folded {
16006 editor.update(cx, |editor, cx| {
16007 editor.fold_at(&crate::FoldAt { buffer_row }, window, cx)
16008 });
16009 } else {
16010 editor.update(cx, |editor, cx| {
16011 editor.unfold_at(
16012 &crate::UnfoldAt { buffer_row },
16013 window,
16014 cx,
16015 )
16016 });
16017 }
16018 });
16019 return Some((render_toggle)(
16020 buffer_row,
16021 folded,
16022 toggle_callback,
16023 window,
16024 cx,
16025 ));
16026 }
16027 }
16028 }
16029 }
16030
16031 is_foldable |= self.starts_indent(buffer_row);
16032
16033 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
16034 Some(
16035 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
16036 .toggle_state(folded)
16037 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
16038 if folded {
16039 this.unfold_at(&UnfoldAt { buffer_row }, window, cx);
16040 } else {
16041 this.fold_at(&FoldAt { buffer_row }, window, cx);
16042 }
16043 }))
16044 .into_any_element(),
16045 )
16046 } else {
16047 None
16048 }
16049 }
16050
16051 pub fn render_crease_trailer(
16052 &self,
16053 buffer_row: MultiBufferRow,
16054 window: &mut Window,
16055 cx: &mut App,
16056 ) -> Option<AnyElement> {
16057 let folded = self.is_line_folded(buffer_row);
16058 if let Crease::Inline { render_trailer, .. } = self
16059 .crease_snapshot
16060 .query_row(buffer_row, &self.buffer_snapshot)?
16061 {
16062 let render_trailer = render_trailer.as_ref()?;
16063 Some(render_trailer(buffer_row, folded, window, cx))
16064 } else {
16065 None
16066 }
16067 }
16068}
16069
16070impl Deref for EditorSnapshot {
16071 type Target = DisplaySnapshot;
16072
16073 fn deref(&self) -> &Self::Target {
16074 &self.display_snapshot
16075 }
16076}
16077
16078#[derive(Clone, Debug, PartialEq, Eq)]
16079pub enum EditorEvent {
16080 InputIgnored {
16081 text: Arc<str>,
16082 },
16083 InputHandled {
16084 utf16_range_to_replace: Option<Range<isize>>,
16085 text: Arc<str>,
16086 },
16087 ExcerptsAdded {
16088 buffer: Entity<Buffer>,
16089 predecessor: ExcerptId,
16090 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
16091 },
16092 ExcerptsRemoved {
16093 ids: Vec<ExcerptId>,
16094 },
16095 BufferFoldToggled {
16096 ids: Vec<ExcerptId>,
16097 folded: bool,
16098 },
16099 ExcerptsEdited {
16100 ids: Vec<ExcerptId>,
16101 },
16102 ExcerptsExpanded {
16103 ids: Vec<ExcerptId>,
16104 },
16105 BufferEdited,
16106 Edited {
16107 transaction_id: clock::Lamport,
16108 },
16109 Reparsed(BufferId),
16110 Focused,
16111 FocusedIn,
16112 Blurred,
16113 DirtyChanged,
16114 Saved,
16115 TitleChanged,
16116 DiffBaseChanged,
16117 SelectionsChanged {
16118 local: bool,
16119 },
16120 ScrollPositionChanged {
16121 local: bool,
16122 autoscroll: bool,
16123 },
16124 Closed,
16125 TransactionUndone {
16126 transaction_id: clock::Lamport,
16127 },
16128 TransactionBegun {
16129 transaction_id: clock::Lamport,
16130 },
16131 Reloaded,
16132 CursorShapeChanged,
16133}
16134
16135impl EventEmitter<EditorEvent> for Editor {}
16136
16137impl Focusable for Editor {
16138 fn focus_handle(&self, _cx: &App) -> FocusHandle {
16139 self.focus_handle.clone()
16140 }
16141}
16142
16143impl Render for Editor {
16144 fn render<'a>(&mut self, _: &mut Window, cx: &mut Context<'a, Self>) -> impl IntoElement {
16145 let settings = ThemeSettings::get_global(cx);
16146
16147 let mut text_style = match self.mode {
16148 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
16149 color: cx.theme().colors().editor_foreground,
16150 font_family: settings.ui_font.family.clone(),
16151 font_features: settings.ui_font.features.clone(),
16152 font_fallbacks: settings.ui_font.fallbacks.clone(),
16153 font_size: rems(0.875).into(),
16154 font_weight: settings.ui_font.weight,
16155 line_height: relative(settings.buffer_line_height.value()),
16156 ..Default::default()
16157 },
16158 EditorMode::Full => TextStyle {
16159 color: cx.theme().colors().editor_foreground,
16160 font_family: settings.buffer_font.family.clone(),
16161 font_features: settings.buffer_font.features.clone(),
16162 font_fallbacks: settings.buffer_font.fallbacks.clone(),
16163 font_size: settings.buffer_font_size().into(),
16164 font_weight: settings.buffer_font.weight,
16165 line_height: relative(settings.buffer_line_height.value()),
16166 ..Default::default()
16167 },
16168 };
16169 if let Some(text_style_refinement) = &self.text_style_refinement {
16170 text_style.refine(text_style_refinement)
16171 }
16172
16173 let background = match self.mode {
16174 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
16175 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
16176 EditorMode::Full => cx.theme().colors().editor_background,
16177 };
16178
16179 EditorElement::new(
16180 &cx.entity(),
16181 EditorStyle {
16182 background,
16183 local_player: cx.theme().players().local(),
16184 text: text_style,
16185 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
16186 syntax: cx.theme().syntax().clone(),
16187 status: cx.theme().status().clone(),
16188 inlay_hints_style: make_inlay_hints_style(cx),
16189 inline_completion_styles: make_suggestion_styles(cx),
16190 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
16191 },
16192 )
16193 }
16194}
16195
16196impl EntityInputHandler for Editor {
16197 fn text_for_range(
16198 &mut self,
16199 range_utf16: Range<usize>,
16200 adjusted_range: &mut Option<Range<usize>>,
16201 _: &mut Window,
16202 cx: &mut Context<Self>,
16203 ) -> Option<String> {
16204 let snapshot = self.buffer.read(cx).read(cx);
16205 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
16206 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
16207 if (start.0..end.0) != range_utf16 {
16208 adjusted_range.replace(start.0..end.0);
16209 }
16210 Some(snapshot.text_for_range(start..end).collect())
16211 }
16212
16213 fn selected_text_range(
16214 &mut self,
16215 ignore_disabled_input: bool,
16216 _: &mut Window,
16217 cx: &mut Context<Self>,
16218 ) -> Option<UTF16Selection> {
16219 // Prevent the IME menu from appearing when holding down an alphabetic key
16220 // while input is disabled.
16221 if !ignore_disabled_input && !self.input_enabled {
16222 return None;
16223 }
16224
16225 let selection = self.selections.newest::<OffsetUtf16>(cx);
16226 let range = selection.range();
16227
16228 Some(UTF16Selection {
16229 range: range.start.0..range.end.0,
16230 reversed: selection.reversed,
16231 })
16232 }
16233
16234 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
16235 let snapshot = self.buffer.read(cx).read(cx);
16236 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
16237 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
16238 }
16239
16240 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
16241 self.clear_highlights::<InputComposition>(cx);
16242 self.ime_transaction.take();
16243 }
16244
16245 fn replace_text_in_range(
16246 &mut self,
16247 range_utf16: Option<Range<usize>>,
16248 text: &str,
16249 window: &mut Window,
16250 cx: &mut Context<Self>,
16251 ) {
16252 if !self.input_enabled {
16253 cx.emit(EditorEvent::InputIgnored { text: text.into() });
16254 return;
16255 }
16256
16257 self.transact(window, cx, |this, window, cx| {
16258 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
16259 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
16260 Some(this.selection_replacement_ranges(range_utf16, cx))
16261 } else {
16262 this.marked_text_ranges(cx)
16263 };
16264
16265 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
16266 let newest_selection_id = this.selections.newest_anchor().id;
16267 this.selections
16268 .all::<OffsetUtf16>(cx)
16269 .iter()
16270 .zip(ranges_to_replace.iter())
16271 .find_map(|(selection, range)| {
16272 if selection.id == newest_selection_id {
16273 Some(
16274 (range.start.0 as isize - selection.head().0 as isize)
16275 ..(range.end.0 as isize - selection.head().0 as isize),
16276 )
16277 } else {
16278 None
16279 }
16280 })
16281 });
16282
16283 cx.emit(EditorEvent::InputHandled {
16284 utf16_range_to_replace: range_to_replace,
16285 text: text.into(),
16286 });
16287
16288 if let Some(new_selected_ranges) = new_selected_ranges {
16289 this.change_selections(None, window, cx, |selections| {
16290 selections.select_ranges(new_selected_ranges)
16291 });
16292 this.backspace(&Default::default(), window, cx);
16293 }
16294
16295 this.handle_input(text, window, cx);
16296 });
16297
16298 if let Some(transaction) = self.ime_transaction {
16299 self.buffer.update(cx, |buffer, cx| {
16300 buffer.group_until_transaction(transaction, cx);
16301 });
16302 }
16303
16304 self.unmark_text(window, cx);
16305 }
16306
16307 fn replace_and_mark_text_in_range(
16308 &mut self,
16309 range_utf16: Option<Range<usize>>,
16310 text: &str,
16311 new_selected_range_utf16: Option<Range<usize>>,
16312 window: &mut Window,
16313 cx: &mut Context<Self>,
16314 ) {
16315 if !self.input_enabled {
16316 return;
16317 }
16318
16319 let transaction = self.transact(window, cx, |this, window, cx| {
16320 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
16321 let snapshot = this.buffer.read(cx).read(cx);
16322 if let Some(relative_range_utf16) = range_utf16.as_ref() {
16323 for marked_range in &mut marked_ranges {
16324 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
16325 marked_range.start.0 += relative_range_utf16.start;
16326 marked_range.start =
16327 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
16328 marked_range.end =
16329 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
16330 }
16331 }
16332 Some(marked_ranges)
16333 } else if let Some(range_utf16) = range_utf16 {
16334 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
16335 Some(this.selection_replacement_ranges(range_utf16, cx))
16336 } else {
16337 None
16338 };
16339
16340 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
16341 let newest_selection_id = this.selections.newest_anchor().id;
16342 this.selections
16343 .all::<OffsetUtf16>(cx)
16344 .iter()
16345 .zip(ranges_to_replace.iter())
16346 .find_map(|(selection, range)| {
16347 if selection.id == newest_selection_id {
16348 Some(
16349 (range.start.0 as isize - selection.head().0 as isize)
16350 ..(range.end.0 as isize - selection.head().0 as isize),
16351 )
16352 } else {
16353 None
16354 }
16355 })
16356 });
16357
16358 cx.emit(EditorEvent::InputHandled {
16359 utf16_range_to_replace: range_to_replace,
16360 text: text.into(),
16361 });
16362
16363 if let Some(ranges) = ranges_to_replace {
16364 this.change_selections(None, window, cx, |s| s.select_ranges(ranges));
16365 }
16366
16367 let marked_ranges = {
16368 let snapshot = this.buffer.read(cx).read(cx);
16369 this.selections
16370 .disjoint_anchors()
16371 .iter()
16372 .map(|selection| {
16373 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
16374 })
16375 .collect::<Vec<_>>()
16376 };
16377
16378 if text.is_empty() {
16379 this.unmark_text(window, cx);
16380 } else {
16381 this.highlight_text::<InputComposition>(
16382 marked_ranges.clone(),
16383 HighlightStyle {
16384 underline: Some(UnderlineStyle {
16385 thickness: px(1.),
16386 color: None,
16387 wavy: false,
16388 }),
16389 ..Default::default()
16390 },
16391 cx,
16392 );
16393 }
16394
16395 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
16396 let use_autoclose = this.use_autoclose;
16397 let use_auto_surround = this.use_auto_surround;
16398 this.set_use_autoclose(false);
16399 this.set_use_auto_surround(false);
16400 this.handle_input(text, window, cx);
16401 this.set_use_autoclose(use_autoclose);
16402 this.set_use_auto_surround(use_auto_surround);
16403
16404 if let Some(new_selected_range) = new_selected_range_utf16 {
16405 let snapshot = this.buffer.read(cx).read(cx);
16406 let new_selected_ranges = marked_ranges
16407 .into_iter()
16408 .map(|marked_range| {
16409 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
16410 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
16411 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
16412 snapshot.clip_offset_utf16(new_start, Bias::Left)
16413 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
16414 })
16415 .collect::<Vec<_>>();
16416
16417 drop(snapshot);
16418 this.change_selections(None, window, cx, |selections| {
16419 selections.select_ranges(new_selected_ranges)
16420 });
16421 }
16422 });
16423
16424 self.ime_transaction = self.ime_transaction.or(transaction);
16425 if let Some(transaction) = self.ime_transaction {
16426 self.buffer.update(cx, |buffer, cx| {
16427 buffer.group_until_transaction(transaction, cx);
16428 });
16429 }
16430
16431 if self.text_highlights::<InputComposition>(cx).is_none() {
16432 self.ime_transaction.take();
16433 }
16434 }
16435
16436 fn bounds_for_range(
16437 &mut self,
16438 range_utf16: Range<usize>,
16439 element_bounds: gpui::Bounds<Pixels>,
16440 window: &mut Window,
16441 cx: &mut Context<Self>,
16442 ) -> Option<gpui::Bounds<Pixels>> {
16443 let text_layout_details = self.text_layout_details(window);
16444 let gpui::Size {
16445 width: em_width,
16446 height: line_height,
16447 } = self.character_size(window);
16448
16449 let snapshot = self.snapshot(window, cx);
16450 let scroll_position = snapshot.scroll_position();
16451 let scroll_left = scroll_position.x * em_width;
16452
16453 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
16454 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
16455 + self.gutter_dimensions.width
16456 + self.gutter_dimensions.margin;
16457 let y = line_height * (start.row().as_f32() - scroll_position.y);
16458
16459 Some(Bounds {
16460 origin: element_bounds.origin + point(x, y),
16461 size: size(em_width, line_height),
16462 })
16463 }
16464
16465 fn character_index_for_point(
16466 &mut self,
16467 point: gpui::Point<Pixels>,
16468 _window: &mut Window,
16469 _cx: &mut Context<Self>,
16470 ) -> Option<usize> {
16471 let position_map = self.last_position_map.as_ref()?;
16472 if !position_map.text_hitbox.contains(&point) {
16473 return None;
16474 }
16475 let display_point = position_map.point_for_position(point).previous_valid;
16476 let anchor = position_map
16477 .snapshot
16478 .display_point_to_anchor(display_point, Bias::Left);
16479 let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot);
16480 Some(utf16_offset.0)
16481 }
16482}
16483
16484trait SelectionExt {
16485 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
16486 fn spanned_rows(
16487 &self,
16488 include_end_if_at_line_start: bool,
16489 map: &DisplaySnapshot,
16490 ) -> Range<MultiBufferRow>;
16491}
16492
16493impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
16494 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
16495 let start = self
16496 .start
16497 .to_point(&map.buffer_snapshot)
16498 .to_display_point(map);
16499 let end = self
16500 .end
16501 .to_point(&map.buffer_snapshot)
16502 .to_display_point(map);
16503 if self.reversed {
16504 end..start
16505 } else {
16506 start..end
16507 }
16508 }
16509
16510 fn spanned_rows(
16511 &self,
16512 include_end_if_at_line_start: bool,
16513 map: &DisplaySnapshot,
16514 ) -> Range<MultiBufferRow> {
16515 let start = self.start.to_point(&map.buffer_snapshot);
16516 let mut end = self.end.to_point(&map.buffer_snapshot);
16517 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
16518 end.row -= 1;
16519 }
16520
16521 let buffer_start = map.prev_line_boundary(start).0;
16522 let buffer_end = map.next_line_boundary(end).0;
16523 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
16524 }
16525}
16526
16527impl<T: InvalidationRegion> InvalidationStack<T> {
16528 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
16529 where
16530 S: Clone + ToOffset,
16531 {
16532 while let Some(region) = self.last() {
16533 let all_selections_inside_invalidation_ranges =
16534 if selections.len() == region.ranges().len() {
16535 selections
16536 .iter()
16537 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
16538 .all(|(selection, invalidation_range)| {
16539 let head = selection.head().to_offset(buffer);
16540 invalidation_range.start <= head && invalidation_range.end >= head
16541 })
16542 } else {
16543 false
16544 };
16545
16546 if all_selections_inside_invalidation_ranges {
16547 break;
16548 } else {
16549 self.pop();
16550 }
16551 }
16552 }
16553}
16554
16555impl<T> Default for InvalidationStack<T> {
16556 fn default() -> Self {
16557 Self(Default::default())
16558 }
16559}
16560
16561impl<T> Deref for InvalidationStack<T> {
16562 type Target = Vec<T>;
16563
16564 fn deref(&self) -> &Self::Target {
16565 &self.0
16566 }
16567}
16568
16569impl<T> DerefMut for InvalidationStack<T> {
16570 fn deref_mut(&mut self) -> &mut Self::Target {
16571 &mut self.0
16572 }
16573}
16574
16575impl InvalidationRegion for SnippetState {
16576 fn ranges(&self) -> &[Range<Anchor>] {
16577 &self.ranges[self.active_index]
16578 }
16579}
16580
16581pub fn diagnostic_block_renderer(
16582 diagnostic: Diagnostic,
16583 max_message_rows: Option<u8>,
16584 allow_closing: bool,
16585 _is_valid: bool,
16586) -> RenderBlock {
16587 let (text_without_backticks, code_ranges) =
16588 highlight_diagnostic_message(&diagnostic, max_message_rows);
16589
16590 Arc::new(move |cx: &mut BlockContext| {
16591 let group_id: SharedString = cx.block_id.to_string().into();
16592
16593 let mut text_style = cx.window.text_style().clone();
16594 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
16595 let theme_settings = ThemeSettings::get_global(cx);
16596 text_style.font_family = theme_settings.buffer_font.family.clone();
16597 text_style.font_style = theme_settings.buffer_font.style;
16598 text_style.font_features = theme_settings.buffer_font.features.clone();
16599 text_style.font_weight = theme_settings.buffer_font.weight;
16600
16601 let multi_line_diagnostic = diagnostic.message.contains('\n');
16602
16603 let buttons = |diagnostic: &Diagnostic| {
16604 if multi_line_diagnostic {
16605 v_flex()
16606 } else {
16607 h_flex()
16608 }
16609 .when(allow_closing, |div| {
16610 div.children(diagnostic.is_primary.then(|| {
16611 IconButton::new("close-block", IconName::XCircle)
16612 .icon_color(Color::Muted)
16613 .size(ButtonSize::Compact)
16614 .style(ButtonStyle::Transparent)
16615 .visible_on_hover(group_id.clone())
16616 .on_click(move |_click, window, cx| {
16617 window.dispatch_action(Box::new(Cancel), cx)
16618 })
16619 .tooltip(|window, cx| {
16620 Tooltip::for_action("Close Diagnostics", &Cancel, window, cx)
16621 })
16622 }))
16623 })
16624 .child(
16625 IconButton::new("copy-block", IconName::Copy)
16626 .icon_color(Color::Muted)
16627 .size(ButtonSize::Compact)
16628 .style(ButtonStyle::Transparent)
16629 .visible_on_hover(group_id.clone())
16630 .on_click({
16631 let message = diagnostic.message.clone();
16632 move |_click, _, cx| {
16633 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
16634 }
16635 })
16636 .tooltip(Tooltip::text("Copy diagnostic message")),
16637 )
16638 };
16639
16640 let icon_size = buttons(&diagnostic).into_any_element().layout_as_root(
16641 AvailableSpace::min_size(),
16642 cx.window,
16643 cx.app,
16644 );
16645
16646 h_flex()
16647 .id(cx.block_id)
16648 .group(group_id.clone())
16649 .relative()
16650 .size_full()
16651 .block_mouse_down()
16652 .pl(cx.gutter_dimensions.width)
16653 .w(cx.max_width - cx.gutter_dimensions.full_width())
16654 .child(
16655 div()
16656 .flex()
16657 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
16658 .flex_shrink(),
16659 )
16660 .child(buttons(&diagnostic))
16661 .child(div().flex().flex_shrink_0().child(
16662 StyledText::new(text_without_backticks.clone()).with_highlights(
16663 &text_style,
16664 code_ranges.iter().map(|range| {
16665 (
16666 range.clone(),
16667 HighlightStyle {
16668 font_weight: Some(FontWeight::BOLD),
16669 ..Default::default()
16670 },
16671 )
16672 }),
16673 ),
16674 ))
16675 .into_any_element()
16676 })
16677}
16678
16679fn inline_completion_edit_text(
16680 current_snapshot: &BufferSnapshot,
16681 edits: &[(Range<Anchor>, String)],
16682 edit_preview: &EditPreview,
16683 include_deletions: bool,
16684 cx: &App,
16685) -> HighlightedText {
16686 let edits = edits
16687 .iter()
16688 .map(|(anchor, text)| {
16689 (
16690 anchor.start.text_anchor..anchor.end.text_anchor,
16691 text.clone(),
16692 )
16693 })
16694 .collect::<Vec<_>>();
16695
16696 edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
16697}
16698
16699pub fn highlight_diagnostic_message(
16700 diagnostic: &Diagnostic,
16701 mut max_message_rows: Option<u8>,
16702) -> (SharedString, Vec<Range<usize>>) {
16703 let mut text_without_backticks = String::new();
16704 let mut code_ranges = Vec::new();
16705
16706 if let Some(source) = &diagnostic.source {
16707 text_without_backticks.push_str(source);
16708 code_ranges.push(0..source.len());
16709 text_without_backticks.push_str(": ");
16710 }
16711
16712 let mut prev_offset = 0;
16713 let mut in_code_block = false;
16714 let has_row_limit = max_message_rows.is_some();
16715 let mut newline_indices = diagnostic
16716 .message
16717 .match_indices('\n')
16718 .filter(|_| has_row_limit)
16719 .map(|(ix, _)| ix)
16720 .fuse()
16721 .peekable();
16722
16723 for (quote_ix, _) in diagnostic
16724 .message
16725 .match_indices('`')
16726 .chain([(diagnostic.message.len(), "")])
16727 {
16728 let mut first_newline_ix = None;
16729 let mut last_newline_ix = None;
16730 while let Some(newline_ix) = newline_indices.peek() {
16731 if *newline_ix < quote_ix {
16732 if first_newline_ix.is_none() {
16733 first_newline_ix = Some(*newline_ix);
16734 }
16735 last_newline_ix = Some(*newline_ix);
16736
16737 if let Some(rows_left) = &mut max_message_rows {
16738 if *rows_left == 0 {
16739 break;
16740 } else {
16741 *rows_left -= 1;
16742 }
16743 }
16744 let _ = newline_indices.next();
16745 } else {
16746 break;
16747 }
16748 }
16749 let prev_len = text_without_backticks.len();
16750 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
16751 text_without_backticks.push_str(new_text);
16752 if in_code_block {
16753 code_ranges.push(prev_len..text_without_backticks.len());
16754 }
16755 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
16756 in_code_block = !in_code_block;
16757 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
16758 text_without_backticks.push_str("...");
16759 break;
16760 }
16761 }
16762
16763 (text_without_backticks.into(), code_ranges)
16764}
16765
16766fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
16767 match severity {
16768 DiagnosticSeverity::ERROR => colors.error,
16769 DiagnosticSeverity::WARNING => colors.warning,
16770 DiagnosticSeverity::INFORMATION => colors.info,
16771 DiagnosticSeverity::HINT => colors.info,
16772 _ => colors.ignored,
16773 }
16774}
16775
16776pub fn styled_runs_for_code_label<'a>(
16777 label: &'a CodeLabel,
16778 syntax_theme: &'a theme::SyntaxTheme,
16779) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
16780 let fade_out = HighlightStyle {
16781 fade_out: Some(0.35),
16782 ..Default::default()
16783 };
16784
16785 let mut prev_end = label.filter_range.end;
16786 label
16787 .runs
16788 .iter()
16789 .enumerate()
16790 .flat_map(move |(ix, (range, highlight_id))| {
16791 let style = if let Some(style) = highlight_id.style(syntax_theme) {
16792 style
16793 } else {
16794 return Default::default();
16795 };
16796 let mut muted_style = style;
16797 muted_style.highlight(fade_out);
16798
16799 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
16800 if range.start >= label.filter_range.end {
16801 if range.start > prev_end {
16802 runs.push((prev_end..range.start, fade_out));
16803 }
16804 runs.push((range.clone(), muted_style));
16805 } else if range.end <= label.filter_range.end {
16806 runs.push((range.clone(), style));
16807 } else {
16808 runs.push((range.start..label.filter_range.end, style));
16809 runs.push((label.filter_range.end..range.end, muted_style));
16810 }
16811 prev_end = cmp::max(prev_end, range.end);
16812
16813 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
16814 runs.push((prev_end..label.text.len(), fade_out));
16815 }
16816
16817 runs
16818 })
16819}
16820
16821pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
16822 let mut prev_index = 0;
16823 let mut prev_codepoint: Option<char> = None;
16824 text.char_indices()
16825 .chain([(text.len(), '\0')])
16826 .filter_map(move |(index, codepoint)| {
16827 let prev_codepoint = prev_codepoint.replace(codepoint)?;
16828 let is_boundary = index == text.len()
16829 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
16830 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
16831 if is_boundary {
16832 let chunk = &text[prev_index..index];
16833 prev_index = index;
16834 Some(chunk)
16835 } else {
16836 None
16837 }
16838 })
16839}
16840
16841pub trait RangeToAnchorExt: Sized {
16842 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
16843
16844 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
16845 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
16846 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
16847 }
16848}
16849
16850impl<T: ToOffset> RangeToAnchorExt for Range<T> {
16851 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
16852 let start_offset = self.start.to_offset(snapshot);
16853 let end_offset = self.end.to_offset(snapshot);
16854 if start_offset == end_offset {
16855 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
16856 } else {
16857 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
16858 }
16859 }
16860}
16861
16862pub trait RowExt {
16863 fn as_f32(&self) -> f32;
16864
16865 fn next_row(&self) -> Self;
16866
16867 fn previous_row(&self) -> Self;
16868
16869 fn minus(&self, other: Self) -> u32;
16870}
16871
16872impl RowExt for DisplayRow {
16873 fn as_f32(&self) -> f32 {
16874 self.0 as f32
16875 }
16876
16877 fn next_row(&self) -> Self {
16878 Self(self.0 + 1)
16879 }
16880
16881 fn previous_row(&self) -> Self {
16882 Self(self.0.saturating_sub(1))
16883 }
16884
16885 fn minus(&self, other: Self) -> u32 {
16886 self.0 - other.0
16887 }
16888}
16889
16890impl RowExt for MultiBufferRow {
16891 fn as_f32(&self) -> f32 {
16892 self.0 as f32
16893 }
16894
16895 fn next_row(&self) -> Self {
16896 Self(self.0 + 1)
16897 }
16898
16899 fn previous_row(&self) -> Self {
16900 Self(self.0.saturating_sub(1))
16901 }
16902
16903 fn minus(&self, other: Self) -> u32 {
16904 self.0 - other.0
16905 }
16906}
16907
16908trait RowRangeExt {
16909 type Row;
16910
16911 fn len(&self) -> usize;
16912
16913 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
16914}
16915
16916impl RowRangeExt for Range<MultiBufferRow> {
16917 type Row = MultiBufferRow;
16918
16919 fn len(&self) -> usize {
16920 (self.end.0 - self.start.0) as usize
16921 }
16922
16923 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
16924 (self.start.0..self.end.0).map(MultiBufferRow)
16925 }
16926}
16927
16928impl RowRangeExt for Range<DisplayRow> {
16929 type Row = DisplayRow;
16930
16931 fn len(&self) -> usize {
16932 (self.end.0 - self.start.0) as usize
16933 }
16934
16935 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
16936 (self.start.0..self.end.0).map(DisplayRow)
16937 }
16938}
16939
16940/// If select range has more than one line, we
16941/// just point the cursor to range.start.
16942fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
16943 if range.start.row == range.end.row {
16944 range
16945 } else {
16946 range.start..range.start
16947 }
16948}
16949pub struct KillRing(ClipboardItem);
16950impl Global for KillRing {}
16951
16952const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
16953
16954fn all_edits_insertions_or_deletions(
16955 edits: &Vec<(Range<Anchor>, String)>,
16956 snapshot: &MultiBufferSnapshot,
16957) -> bool {
16958 let mut all_insertions = true;
16959 let mut all_deletions = true;
16960
16961 for (range, new_text) in edits.iter() {
16962 let range_is_empty = range.to_offset(&snapshot).is_empty();
16963 let text_is_empty = new_text.is_empty();
16964
16965 if range_is_empty != text_is_empty {
16966 if range_is_empty {
16967 all_deletions = false;
16968 } else {
16969 all_insertions = false;
16970 }
16971 } else {
16972 return false;
16973 }
16974
16975 if !all_insertions && !all_deletions {
16976 return false;
16977 }
16978 }
16979 all_insertions || all_deletions
16980}