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 behaviour.
15pub mod actions;
16mod blame_entry_tooltip;
17mod blink_manager;
18mod debounced_delay;
19pub mod display_map;
20mod editor_settings;
21mod element;
22mod git;
23mod highlight_matching_bracket;
24mod hover_links;
25mod hover_popover;
26mod hunk_diff;
27mod indent_guides;
28mod inlay_hint_cache;
29mod inline_completion_provider;
30pub mod items;
31mod linked_editing_ranges;
32mod mouse_context_menu;
33pub mod movement;
34mod persistence;
35mod rust_analyzer_ext;
36pub mod scroll;
37mod selections_collection;
38pub mod tasks;
39
40#[cfg(test)]
41mod editor_tests;
42#[cfg(any(test, feature = "test-support"))]
43pub mod test;
44use ::git::diff::{DiffHunk, DiffHunkStatus};
45use ::git::{parse_git_remote_url, BuildPermalinkParams, GitHostingProviderRegistry};
46pub(crate) use actions::*;
47use aho_corasick::AhoCorasick;
48use anyhow::{anyhow, Context as _, Result};
49use blink_manager::BlinkManager;
50use client::{Collaborator, ParticipantIndex};
51use clock::ReplicaId;
52use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
53use convert_case::{Case, Casing};
54use debounced_delay::DebouncedDelay;
55use display_map::*;
56pub use display_map::{DisplayPoint, FoldPlaceholder};
57pub use editor_settings::{CurrentLineHighlight, EditorSettings};
58use element::LineWithInvisibles;
59pub use element::{
60 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
61};
62use futures::FutureExt;
63use fuzzy::{StringMatch, StringMatchCandidate};
64use git::blame::GitBlame;
65use git::diff_hunk_to_display;
66use gpui::{
67 div, impl_actions, point, prelude::*, px, relative, size, uniform_list, Action, AnyElement,
68 AppContext, AsyncWindowContext, AvailableSpace, BackgroundExecutor, Bounds, ClipboardItem,
69 Context, DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusOutEvent, FocusableView,
70 FontId, FontStyle, FontWeight, HighlightStyle, Hsla, InteractiveText, KeyContext,
71 ListSizingBehavior, Model, MouseButton, PaintQuad, ParentElement, Pixels, Render, SharedString,
72 Size, StrikethroughStyle, Styled, StyledText, Subscription, Task, TextStyle, UnderlineStyle,
73 UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext, WeakFocusHandle,
74 WeakView, WhiteSpace, WindowContext,
75};
76use highlight_matching_bracket::refresh_matching_bracket_highlights;
77use hover_popover::{hide_hover, HoverState};
78use hunk_diff::ExpandedHunks;
79pub(crate) use hunk_diff::HunkToExpand;
80use indent_guides::ActiveIndentGuidesState;
81use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
82pub use inline_completion_provider::*;
83pub use items::MAX_TAB_TITLE_LEN;
84use itertools::Itertools;
85use language::{
86 char_kind,
87 language_settings::{self, all_language_settings, InlayHintSettings},
88 markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
89 CursorShape, Diagnostic, Documentation, IndentKind, IndentSize, Language, OffsetRangeExt,
90 Point, Selection, SelectionGoal, TransactionId,
91};
92use language::{BufferRow, Runnable, RunnableRange};
93use linked_editing_ranges::refresh_linked_ranges;
94use task::{ResolvedTask, TaskTemplate, TaskVariables};
95
96use hover_links::{HoverLink, HoveredLinkState, InlayHighlight};
97pub use lsp::CompletionContext;
98use lsp::{CompletionTriggerKind, DiagnosticSeverity, LanguageServerId};
99use mouse_context_menu::MouseContextMenu;
100use movement::TextLayoutDetails;
101pub use multi_buffer::{
102 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
103 ToPoint,
104};
105use multi_buffer::{ExpandExcerptDirection, MultiBufferPoint, MultiBufferRow, ToOffsetUtf16};
106use ordered_float::OrderedFloat;
107use parking_lot::{Mutex, RwLock};
108use project::project_settings::{GitGutterSetting, ProjectSettings};
109use project::{
110 CodeAction, Completion, FormatTrigger, Item, Location, Project, ProjectPath,
111 ProjectTransaction, TaskSourceKind, WorktreeId,
112};
113use rand::prelude::*;
114use rpc::{proto::*, ErrorExt};
115use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
116use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
117use serde::{Deserialize, Serialize};
118use settings::{update_settings_file, Settings, SettingsStore};
119use smallvec::SmallVec;
120use snippet::Snippet;
121use std::{
122 any::TypeId,
123 borrow::Cow,
124 cell::RefCell,
125 cmp::{self, Ordering, Reverse},
126 mem,
127 num::NonZeroU32,
128 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
129 path::Path,
130 rc::Rc,
131 sync::Arc,
132 time::{Duration, Instant},
133};
134pub use sum_tree::Bias;
135use sum_tree::TreeMap;
136use text::{BufferId, OffsetUtf16, Rope};
137use theme::{
138 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
139 ThemeColors, ThemeSettings,
140};
141use ui::{
142 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize,
143 ListItem, Popover, Tooltip,
144};
145use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
146use workspace::item::{ItemHandle, PreviewTabsSettings};
147use workspace::notifications::{DetachAndPromptErr, NotificationId};
148use workspace::{
149 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
150};
151use workspace::{OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
152
153use crate::hover_links::find_url;
154
155pub const FILE_HEADER_HEIGHT: u8 = 1;
156pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u8 = 1;
157pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u8 = 1;
158pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
159const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
160const MAX_LINE_LEN: usize = 1024;
161const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
162const MAX_SELECTION_HISTORY_LEN: usize = 1024;
163pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
164#[doc(hidden)]
165pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
166#[doc(hidden)]
167pub const DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
168
169pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
170
171pub fn render_parsed_markdown(
172 element_id: impl Into<ElementId>,
173 parsed: &language::ParsedMarkdown,
174 editor_style: &EditorStyle,
175 workspace: Option<WeakView<Workspace>>,
176 cx: &mut WindowContext,
177) -> InteractiveText {
178 let code_span_background_color = cx
179 .theme()
180 .colors()
181 .editor_document_highlight_read_background;
182
183 let highlights = gpui::combine_highlights(
184 parsed.highlights.iter().filter_map(|(range, highlight)| {
185 let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
186 Some((range.clone(), highlight))
187 }),
188 parsed
189 .regions
190 .iter()
191 .zip(&parsed.region_ranges)
192 .filter_map(|(region, range)| {
193 if region.code {
194 Some((
195 range.clone(),
196 HighlightStyle {
197 background_color: Some(code_span_background_color),
198 ..Default::default()
199 },
200 ))
201 } else {
202 None
203 }
204 }),
205 );
206
207 let mut links = Vec::new();
208 let mut link_ranges = Vec::new();
209 for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
210 if let Some(link) = region.link.clone() {
211 links.push(link);
212 link_ranges.push(range.clone());
213 }
214 }
215
216 InteractiveText::new(
217 element_id,
218 StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
219 )
220 .on_click(link_ranges, move |clicked_range_ix, cx| {
221 match &links[clicked_range_ix] {
222 markdown::Link::Web { url } => cx.open_url(url),
223 markdown::Link::Path { path } => {
224 if let Some(workspace) = &workspace {
225 _ = workspace.update(cx, |workspace, cx| {
226 workspace.open_abs_path(path.clone(), false, cx).detach();
227 });
228 }
229 }
230 }
231 })
232}
233
234#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
235pub(crate) enum InlayId {
236 Suggestion(usize),
237 Hint(usize),
238}
239
240impl InlayId {
241 fn id(&self) -> usize {
242 match self {
243 Self::Suggestion(id) => *id,
244 Self::Hint(id) => *id,
245 }
246 }
247}
248
249enum DiffRowHighlight {}
250enum DocumentHighlightRead {}
251enum DocumentHighlightWrite {}
252enum InputComposition {}
253
254#[derive(Copy, Clone, PartialEq, Eq)]
255pub enum Direction {
256 Prev,
257 Next,
258}
259
260pub fn init_settings(cx: &mut AppContext) {
261 EditorSettings::register(cx);
262}
263
264pub fn init(cx: &mut AppContext) {
265 init_settings(cx);
266
267 workspace::register_project_item::<Editor>(cx);
268 workspace::register_followable_item::<Editor>(cx);
269 workspace::register_deserializable_item::<Editor>(cx);
270 cx.observe_new_views(
271 |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
272 workspace.register_action(Editor::new_file);
273 workspace.register_action(Editor::new_file_in_direction);
274 },
275 )
276 .detach();
277
278 cx.on_action(move |_: &workspace::NewFile, cx| {
279 let app_state = workspace::AppState::global(cx);
280 if let Some(app_state) = app_state.upgrade() {
281 workspace::open_new(app_state, cx, |workspace, cx| {
282 Editor::new_file(workspace, &Default::default(), cx)
283 })
284 .detach();
285 }
286 });
287 cx.on_action(move |_: &workspace::NewWindow, cx| {
288 let app_state = workspace::AppState::global(cx);
289 if let Some(app_state) = app_state.upgrade() {
290 workspace::open_new(app_state, cx, |workspace, cx| {
291 Editor::new_file(workspace, &Default::default(), cx)
292 })
293 .detach();
294 }
295 });
296}
297
298pub struct SearchWithinRange;
299
300trait InvalidationRegion {
301 fn ranges(&self) -> &[Range<Anchor>];
302}
303
304#[derive(Clone, Debug, PartialEq)]
305pub enum SelectPhase {
306 Begin {
307 position: DisplayPoint,
308 add: bool,
309 click_count: usize,
310 },
311 BeginColumnar {
312 position: DisplayPoint,
313 reset: bool,
314 goal_column: u32,
315 },
316 Extend {
317 position: DisplayPoint,
318 click_count: usize,
319 },
320 Update {
321 position: DisplayPoint,
322 goal_column: u32,
323 scroll_delta: gpui::Point<f32>,
324 },
325 End,
326}
327
328#[derive(Clone, Debug)]
329pub enum SelectMode {
330 Character,
331 Word(Range<Anchor>),
332 Line(Range<Anchor>),
333 All,
334}
335
336#[derive(Copy, Clone, PartialEq, Eq, Debug)]
337pub enum EditorMode {
338 SingleLine,
339 AutoHeight { max_lines: usize },
340 Full,
341}
342
343#[derive(Clone, Debug)]
344pub enum SoftWrap {
345 None,
346 PreferLine,
347 EditorWidth,
348 Column(u32),
349}
350
351#[derive(Clone)]
352pub struct EditorStyle {
353 pub background: Hsla,
354 pub local_player: PlayerColor,
355 pub text: TextStyle,
356 pub scrollbar_width: Pixels,
357 pub syntax: Arc<SyntaxTheme>,
358 pub status: StatusColors,
359 pub inlay_hints_style: HighlightStyle,
360 pub suggestions_style: HighlightStyle,
361}
362
363impl Default for EditorStyle {
364 fn default() -> Self {
365 Self {
366 background: Hsla::default(),
367 local_player: PlayerColor::default(),
368 text: TextStyle::default(),
369 scrollbar_width: Pixels::default(),
370 syntax: Default::default(),
371 // HACK: Status colors don't have a real default.
372 // We should look into removing the status colors from the editor
373 // style and retrieve them directly from the theme.
374 status: StatusColors::dark(),
375 inlay_hints_style: HighlightStyle::default(),
376 suggestions_style: HighlightStyle::default(),
377 }
378 }
379}
380
381type CompletionId = usize;
382
383#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
384struct EditorActionId(usize);
385
386impl EditorActionId {
387 pub fn post_inc(&mut self) -> Self {
388 let answer = self.0;
389
390 *self = Self(answer + 1);
391
392 Self(answer)
393 }
394}
395
396// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
397// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
398
399type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
400type GutterHighlight = (fn(&AppContext) -> Hsla, Arc<[Range<Anchor>]>);
401
402struct ScrollbarMarkerState {
403 scrollbar_size: Size<Pixels>,
404 dirty: bool,
405 markers: Arc<[PaintQuad]>,
406 pending_refresh: Option<Task<Result<()>>>,
407}
408
409impl ScrollbarMarkerState {
410 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
411 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
412 }
413}
414
415impl Default for ScrollbarMarkerState {
416 fn default() -> Self {
417 Self {
418 scrollbar_size: Size::default(),
419 dirty: false,
420 markers: Arc::from([]),
421 pending_refresh: None,
422 }
423 }
424}
425
426#[derive(Clone, Debug)]
427struct RunnableTasks {
428 templates: Vec<(TaskSourceKind, TaskTemplate)>,
429 offset: MultiBufferOffset,
430 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
431 column: u32,
432 // Values of all named captures, including those starting with '_'
433 extra_variables: HashMap<String, String>,
434 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
435 context_range: Range<BufferOffset>,
436}
437
438#[derive(Clone)]
439struct ResolvedTasks {
440 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
441 position: Anchor,
442}
443#[derive(Copy, Clone, Debug)]
444struct MultiBufferOffset(usize);
445#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
446struct BufferOffset(usize);
447/// Zed's primary text input `View`, allowing users to edit a [`MultiBuffer`]
448///
449/// See the [module level documentation](self) for more information.
450pub struct Editor {
451 focus_handle: FocusHandle,
452 last_focused_descendant: Option<WeakFocusHandle>,
453 /// The text buffer being edited
454 buffer: Model<MultiBuffer>,
455 /// Map of how text in the buffer should be displayed.
456 /// Handles soft wraps, folds, fake inlay text insertions, etc.
457 pub display_map: Model<DisplayMap>,
458 pub selections: SelectionsCollection,
459 pub scroll_manager: ScrollManager,
460 columnar_selection_tail: Option<Anchor>,
461 add_selections_state: Option<AddSelectionsState>,
462 select_next_state: Option<SelectNextState>,
463 select_prev_state: Option<SelectNextState>,
464 selection_history: SelectionHistory,
465 autoclose_regions: Vec<AutocloseRegion>,
466 snippet_stack: InvalidationStack<SnippetState>,
467 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
468 ime_transaction: Option<TransactionId>,
469 active_diagnostics: Option<ActiveDiagnosticGroup>,
470 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
471 project: Option<Model<Project>>,
472 completion_provider: Option<Box<dyn CompletionProvider>>,
473 collaboration_hub: Option<Box<dyn CollaborationHub>>,
474 blink_manager: Model<BlinkManager>,
475 show_cursor_names: bool,
476 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
477 pub show_local_selections: bool,
478 mode: EditorMode,
479 show_breadcrumbs: bool,
480 show_gutter: bool,
481 show_line_numbers: Option<bool>,
482 show_git_diff_gutter: Option<bool>,
483 show_code_actions: Option<bool>,
484 show_wrap_guides: Option<bool>,
485 show_indent_guides: Option<bool>,
486 placeholder_text: Option<Arc<str>>,
487 highlight_order: usize,
488 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
489 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
490 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
491 scrollbar_marker_state: ScrollbarMarkerState,
492 active_indent_guides_state: ActiveIndentGuidesState,
493 nav_history: Option<ItemNavHistory>,
494 context_menu: RwLock<Option<ContextMenu>>,
495 mouse_context_menu: Option<MouseContextMenu>,
496 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
497 find_all_references_task_sources: Vec<Anchor>,
498 next_completion_id: CompletionId,
499 completion_documentation_pre_resolve_debounce: DebouncedDelay,
500 available_code_actions: Option<(Location, Arc<[CodeAction]>)>,
501 code_actions_task: Option<Task<()>>,
502 document_highlights_task: Option<Task<()>>,
503 linked_editing_range_task: Option<Task<Option<()>>>,
504 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
505 pending_rename: Option<RenameState>,
506 searchable: bool,
507 cursor_shape: CursorShape,
508 current_line_highlight: Option<CurrentLineHighlight>,
509 collapse_matches: bool,
510 autoindent_mode: Option<AutoindentMode>,
511 workspace: Option<(WeakView<Workspace>, Option<WorkspaceId>)>,
512 keymap_context_layers: BTreeMap<TypeId, KeyContext>,
513 input_enabled: bool,
514 use_modal_editing: bool,
515 read_only: bool,
516 leader_peer_id: Option<PeerId>,
517 remote_id: Option<ViewId>,
518 hover_state: HoverState,
519 gutter_hovered: bool,
520 hovered_link_state: Option<HoveredLinkState>,
521 inline_completion_provider: Option<RegisteredInlineCompletionProvider>,
522 active_inline_completion: Option<Inlay>,
523 show_inline_completions: bool,
524 inlay_hint_cache: InlayHintCache,
525 expanded_hunks: ExpandedHunks,
526 next_inlay_id: usize,
527 _subscriptions: Vec<Subscription>,
528 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
529 gutter_dimensions: GutterDimensions,
530 pub vim_replace_map: HashMap<Range<usize>, String>,
531 style: Option<EditorStyle>,
532 next_editor_action_id: EditorActionId,
533 editor_actions: Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut ViewContext<Self>)>>>>,
534 use_autoclose: bool,
535 auto_replace_emoji_shortcode: bool,
536 show_git_blame_gutter: bool,
537 show_git_blame_inline: bool,
538 show_git_blame_inline_delay_task: Option<Task<()>>,
539 git_blame_inline_enabled: bool,
540 blame: Option<Model<GitBlame>>,
541 blame_subscription: Option<Subscription>,
542 custom_context_menu: Option<
543 Box<
544 dyn 'static
545 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
546 >,
547 >,
548 last_bounds: Option<Bounds<Pixels>>,
549 expect_bounds_change: Option<Bounds<Pixels>>,
550 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
551 tasks_update_task: Option<Task<()>>,
552 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
553 file_header_size: u8,
554}
555
556#[derive(Clone)]
557pub struct EditorSnapshot {
558 pub mode: EditorMode,
559 show_gutter: bool,
560 show_line_numbers: Option<bool>,
561 show_git_diff_gutter: Option<bool>,
562 show_code_actions: Option<bool>,
563 render_git_blame_gutter: bool,
564 pub display_snapshot: DisplaySnapshot,
565 pub placeholder_text: Option<Arc<str>>,
566 is_focused: bool,
567 scroll_anchor: ScrollAnchor,
568 ongoing_scroll: OngoingScroll,
569 current_line_highlight: CurrentLineHighlight,
570 gutter_hovered: bool,
571}
572
573const GIT_BLAME_GUTTER_WIDTH_CHARS: f32 = 53.;
574
575#[derive(Debug, Clone, Copy)]
576pub struct GutterDimensions {
577 pub left_padding: Pixels,
578 pub right_padding: Pixels,
579 pub width: Pixels,
580 pub margin: Pixels,
581 pub git_blame_entries_width: Option<Pixels>,
582}
583
584impl GutterDimensions {
585 /// The full width of the space taken up by the gutter.
586 pub fn full_width(&self) -> Pixels {
587 self.margin + self.width
588 }
589
590 /// The width of the space reserved for the fold indicators,
591 /// use alongside 'justify_end' and `gutter_width` to
592 /// right align content with the line numbers
593 pub fn fold_area_width(&self) -> Pixels {
594 self.margin + self.right_padding
595 }
596}
597
598impl Default for GutterDimensions {
599 fn default() -> Self {
600 Self {
601 left_padding: Pixels::ZERO,
602 right_padding: Pixels::ZERO,
603 width: Pixels::ZERO,
604 margin: Pixels::ZERO,
605 git_blame_entries_width: None,
606 }
607 }
608}
609
610#[derive(Debug)]
611pub struct RemoteSelection {
612 pub replica_id: ReplicaId,
613 pub selection: Selection<Anchor>,
614 pub cursor_shape: CursorShape,
615 pub peer_id: PeerId,
616 pub line_mode: bool,
617 pub participant_index: Option<ParticipantIndex>,
618 pub user_name: Option<SharedString>,
619}
620
621#[derive(Clone, Debug)]
622struct SelectionHistoryEntry {
623 selections: Arc<[Selection<Anchor>]>,
624 select_next_state: Option<SelectNextState>,
625 select_prev_state: Option<SelectNextState>,
626 add_selections_state: Option<AddSelectionsState>,
627}
628
629enum SelectionHistoryMode {
630 Normal,
631 Undoing,
632 Redoing,
633}
634
635#[derive(Clone, PartialEq, Eq, Hash)]
636struct HoveredCursor {
637 replica_id: u16,
638 selection_id: usize,
639}
640
641impl Default for SelectionHistoryMode {
642 fn default() -> Self {
643 Self::Normal
644 }
645}
646
647#[derive(Default)]
648struct SelectionHistory {
649 #[allow(clippy::type_complexity)]
650 selections_by_transaction:
651 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
652 mode: SelectionHistoryMode,
653 undo_stack: VecDeque<SelectionHistoryEntry>,
654 redo_stack: VecDeque<SelectionHistoryEntry>,
655}
656
657impl SelectionHistory {
658 fn insert_transaction(
659 &mut self,
660 transaction_id: TransactionId,
661 selections: Arc<[Selection<Anchor>]>,
662 ) {
663 self.selections_by_transaction
664 .insert(transaction_id, (selections, None));
665 }
666
667 #[allow(clippy::type_complexity)]
668 fn transaction(
669 &self,
670 transaction_id: TransactionId,
671 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
672 self.selections_by_transaction.get(&transaction_id)
673 }
674
675 #[allow(clippy::type_complexity)]
676 fn transaction_mut(
677 &mut self,
678 transaction_id: TransactionId,
679 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
680 self.selections_by_transaction.get_mut(&transaction_id)
681 }
682
683 fn push(&mut self, entry: SelectionHistoryEntry) {
684 if !entry.selections.is_empty() {
685 match self.mode {
686 SelectionHistoryMode::Normal => {
687 self.push_undo(entry);
688 self.redo_stack.clear();
689 }
690 SelectionHistoryMode::Undoing => self.push_redo(entry),
691 SelectionHistoryMode::Redoing => self.push_undo(entry),
692 }
693 }
694 }
695
696 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
697 if self
698 .undo_stack
699 .back()
700 .map_or(true, |e| e.selections != entry.selections)
701 {
702 self.undo_stack.push_back(entry);
703 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
704 self.undo_stack.pop_front();
705 }
706 }
707 }
708
709 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
710 if self
711 .redo_stack
712 .back()
713 .map_or(true, |e| e.selections != entry.selections)
714 {
715 self.redo_stack.push_back(entry);
716 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
717 self.redo_stack.pop_front();
718 }
719 }
720 }
721}
722
723struct RowHighlight {
724 index: usize,
725 range: RangeInclusive<Anchor>,
726 color: Option<Hsla>,
727 should_autoscroll: bool,
728}
729
730#[derive(Clone, Debug)]
731struct AddSelectionsState {
732 above: bool,
733 stack: Vec<usize>,
734}
735
736#[derive(Clone)]
737struct SelectNextState {
738 query: AhoCorasick,
739 wordwise: bool,
740 done: bool,
741}
742
743impl std::fmt::Debug for SelectNextState {
744 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
745 f.debug_struct(std::any::type_name::<Self>())
746 .field("wordwise", &self.wordwise)
747 .field("done", &self.done)
748 .finish()
749 }
750}
751
752#[derive(Debug)]
753struct AutocloseRegion {
754 selection_id: usize,
755 range: Range<Anchor>,
756 pair: BracketPair,
757}
758
759#[derive(Debug)]
760struct SnippetState {
761 ranges: Vec<Vec<Range<Anchor>>>,
762 active_index: usize,
763}
764
765#[doc(hidden)]
766pub struct RenameState {
767 pub range: Range<Anchor>,
768 pub old_name: Arc<str>,
769 pub editor: View<Editor>,
770 block_id: BlockId,
771}
772
773struct InvalidationStack<T>(Vec<T>);
774
775struct RegisteredInlineCompletionProvider {
776 provider: Arc<dyn InlineCompletionProviderHandle>,
777 _subscription: Subscription,
778}
779
780enum ContextMenu {
781 Completions(CompletionsMenu),
782 CodeActions(CodeActionsMenu),
783}
784
785impl ContextMenu {
786 fn select_first(
787 &mut self,
788 project: Option<&Model<Project>>,
789 cx: &mut ViewContext<Editor>,
790 ) -> bool {
791 if self.visible() {
792 match self {
793 ContextMenu::Completions(menu) => menu.select_first(project, cx),
794 ContextMenu::CodeActions(menu) => menu.select_first(cx),
795 }
796 true
797 } else {
798 false
799 }
800 }
801
802 fn select_prev(
803 &mut self,
804 project: Option<&Model<Project>>,
805 cx: &mut ViewContext<Editor>,
806 ) -> bool {
807 if self.visible() {
808 match self {
809 ContextMenu::Completions(menu) => menu.select_prev(project, cx),
810 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
811 }
812 true
813 } else {
814 false
815 }
816 }
817
818 fn select_next(
819 &mut self,
820 project: Option<&Model<Project>>,
821 cx: &mut ViewContext<Editor>,
822 ) -> bool {
823 if self.visible() {
824 match self {
825 ContextMenu::Completions(menu) => menu.select_next(project, cx),
826 ContextMenu::CodeActions(menu) => menu.select_next(cx),
827 }
828 true
829 } else {
830 false
831 }
832 }
833
834 fn select_last(
835 &mut self,
836 project: Option<&Model<Project>>,
837 cx: &mut ViewContext<Editor>,
838 ) -> bool {
839 if self.visible() {
840 match self {
841 ContextMenu::Completions(menu) => menu.select_last(project, cx),
842 ContextMenu::CodeActions(menu) => menu.select_last(cx),
843 }
844 true
845 } else {
846 false
847 }
848 }
849
850 fn visible(&self) -> bool {
851 match self {
852 ContextMenu::Completions(menu) => menu.visible(),
853 ContextMenu::CodeActions(menu) => menu.visible(),
854 }
855 }
856
857 fn render(
858 &self,
859 cursor_position: DisplayPoint,
860 style: &EditorStyle,
861 max_height: Pixels,
862 workspace: Option<WeakView<Workspace>>,
863 cx: &mut ViewContext<Editor>,
864 ) -> (ContextMenuOrigin, AnyElement) {
865 match self {
866 ContextMenu::Completions(menu) => (
867 ContextMenuOrigin::EditorPoint(cursor_position),
868 menu.render(style, max_height, workspace, cx),
869 ),
870 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, max_height, cx),
871 }
872 }
873}
874
875enum ContextMenuOrigin {
876 EditorPoint(DisplayPoint),
877 GutterIndicator(DisplayRow),
878}
879
880#[derive(Clone)]
881struct CompletionsMenu {
882 id: CompletionId,
883 initial_position: Anchor,
884 buffer: Model<Buffer>,
885 completions: Arc<RwLock<Box<[Completion]>>>,
886 match_candidates: Arc<[StringMatchCandidate]>,
887 matches: Arc<[StringMatch]>,
888 selected_item: usize,
889 scroll_handle: UniformListScrollHandle,
890 selected_completion_documentation_resolve_debounce: Arc<Mutex<DebouncedDelay>>,
891}
892
893impl CompletionsMenu {
894 fn select_first(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
895 self.selected_item = 0;
896 self.scroll_handle.scroll_to_item(self.selected_item);
897 self.attempt_resolve_selected_completion_documentation(project, cx);
898 cx.notify();
899 }
900
901 fn select_prev(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
902 if self.selected_item > 0 {
903 self.selected_item -= 1;
904 } else {
905 self.selected_item = self.matches.len() - 1;
906 }
907 self.scroll_handle.scroll_to_item(self.selected_item);
908 self.attempt_resolve_selected_completion_documentation(project, cx);
909 cx.notify();
910 }
911
912 fn select_next(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
913 if self.selected_item + 1 < self.matches.len() {
914 self.selected_item += 1;
915 } else {
916 self.selected_item = 0;
917 }
918 self.scroll_handle.scroll_to_item(self.selected_item);
919 self.attempt_resolve_selected_completion_documentation(project, cx);
920 cx.notify();
921 }
922
923 fn select_last(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
924 self.selected_item = self.matches.len() - 1;
925 self.scroll_handle.scroll_to_item(self.selected_item);
926 self.attempt_resolve_selected_completion_documentation(project, cx);
927 cx.notify();
928 }
929
930 fn pre_resolve_completion_documentation(
931 buffer: Model<Buffer>,
932 completions: Arc<RwLock<Box<[Completion]>>>,
933 matches: Arc<[StringMatch]>,
934 editor: &Editor,
935 cx: &mut ViewContext<Editor>,
936 ) -> Task<()> {
937 let settings = EditorSettings::get_global(cx);
938 if !settings.show_completion_documentation {
939 return Task::ready(());
940 }
941
942 let Some(provider) = editor.completion_provider.as_ref() else {
943 return Task::ready(());
944 };
945
946 let resolve_task = provider.resolve_completions(
947 buffer,
948 matches.iter().map(|m| m.candidate_id).collect(),
949 completions.clone(),
950 cx,
951 );
952
953 return cx.spawn(move |this, mut cx| async move {
954 if let Some(true) = resolve_task.await.log_err() {
955 this.update(&mut cx, |_, cx| cx.notify()).ok();
956 }
957 });
958 }
959
960 fn attempt_resolve_selected_completion_documentation(
961 &mut self,
962 project: Option<&Model<Project>>,
963 cx: &mut ViewContext<Editor>,
964 ) {
965 let settings = EditorSettings::get_global(cx);
966 if !settings.show_completion_documentation {
967 return;
968 }
969
970 let completion_index = self.matches[self.selected_item].candidate_id;
971 let Some(project) = project else {
972 return;
973 };
974
975 let resolve_task = project.update(cx, |project, cx| {
976 project.resolve_completions(
977 self.buffer.clone(),
978 vec![completion_index],
979 self.completions.clone(),
980 cx,
981 )
982 });
983
984 let delay_ms =
985 EditorSettings::get_global(cx).completion_documentation_secondary_query_debounce;
986 let delay = Duration::from_millis(delay_ms);
987
988 self.selected_completion_documentation_resolve_debounce
989 .lock()
990 .fire_new(delay, cx, |_, cx| {
991 cx.spawn(move |this, mut cx| async move {
992 if let Some(true) = resolve_task.await.log_err() {
993 this.update(&mut cx, |_, cx| cx.notify()).ok();
994 }
995 })
996 });
997 }
998
999 fn visible(&self) -> bool {
1000 !self.matches.is_empty()
1001 }
1002
1003 fn render(
1004 &self,
1005 style: &EditorStyle,
1006 max_height: Pixels,
1007 workspace: Option<WeakView<Workspace>>,
1008 cx: &mut ViewContext<Editor>,
1009 ) -> AnyElement {
1010 let settings = EditorSettings::get_global(cx);
1011 let show_completion_documentation = settings.show_completion_documentation;
1012
1013 let widest_completion_ix = self
1014 .matches
1015 .iter()
1016 .enumerate()
1017 .max_by_key(|(_, mat)| {
1018 let completions = self.completions.read();
1019 let completion = &completions[mat.candidate_id];
1020 let documentation = &completion.documentation;
1021
1022 let mut len = completion.label.text.chars().count();
1023 if let Some(Documentation::SingleLine(text)) = documentation {
1024 if show_completion_documentation {
1025 len += text.chars().count();
1026 }
1027 }
1028
1029 len
1030 })
1031 .map(|(ix, _)| ix);
1032
1033 let completions = self.completions.clone();
1034 let matches = self.matches.clone();
1035 let selected_item = self.selected_item;
1036 let style = style.clone();
1037
1038 let multiline_docs = if show_completion_documentation {
1039 let mat = &self.matches[selected_item];
1040 let multiline_docs = match &self.completions.read()[mat.candidate_id].documentation {
1041 Some(Documentation::MultiLinePlainText(text)) => {
1042 Some(div().child(SharedString::from(text.clone())))
1043 }
1044 Some(Documentation::MultiLineMarkdown(parsed)) if !parsed.text.is_empty() => {
1045 Some(div().child(render_parsed_markdown(
1046 "completions_markdown",
1047 parsed,
1048 &style,
1049 workspace,
1050 cx,
1051 )))
1052 }
1053 _ => None,
1054 };
1055 multiline_docs.map(|div| {
1056 div.id("multiline_docs")
1057 .max_h(max_height)
1058 .flex_1()
1059 .px_1p5()
1060 .py_1()
1061 .min_w(px(260.))
1062 .max_w(px(640.))
1063 .w(px(500.))
1064 .overflow_y_scroll()
1065 .occlude()
1066 })
1067 } else {
1068 None
1069 };
1070
1071 let list = uniform_list(
1072 cx.view().clone(),
1073 "completions",
1074 matches.len(),
1075 move |_editor, range, cx| {
1076 let start_ix = range.start;
1077 let completions_guard = completions.read();
1078
1079 matches[range]
1080 .iter()
1081 .enumerate()
1082 .map(|(ix, mat)| {
1083 let item_ix = start_ix + ix;
1084 let candidate_id = mat.candidate_id;
1085 let completion = &completions_guard[candidate_id];
1086
1087 let documentation = if show_completion_documentation {
1088 &completion.documentation
1089 } else {
1090 &None
1091 };
1092
1093 let highlights = gpui::combine_highlights(
1094 mat.ranges().map(|range| (range, FontWeight::BOLD.into())),
1095 styled_runs_for_code_label(&completion.label, &style.syntax).map(
1096 |(range, mut highlight)| {
1097 // Ignore font weight for syntax highlighting, as we'll use it
1098 // for fuzzy matches.
1099 highlight.font_weight = None;
1100
1101 if completion.lsp_completion.deprecated.unwrap_or(false) {
1102 highlight.strikethrough = Some(StrikethroughStyle {
1103 thickness: 1.0.into(),
1104 ..Default::default()
1105 });
1106 highlight.color = Some(cx.theme().colors().text_muted);
1107 }
1108
1109 (range, highlight)
1110 },
1111 ),
1112 );
1113 let completion_label = StyledText::new(completion.label.text.clone())
1114 .with_highlights(&style.text, highlights);
1115 let documentation_label =
1116 if let Some(Documentation::SingleLine(text)) = documentation {
1117 if text.trim().is_empty() {
1118 None
1119 } else {
1120 Some(
1121 h_flex().ml_4().child(
1122 Label::new(text.clone())
1123 .size(LabelSize::Small)
1124 .color(Color::Muted),
1125 ),
1126 )
1127 }
1128 } else {
1129 None
1130 };
1131
1132 div().min_w(px(220.)).max_w(px(540.)).child(
1133 ListItem::new(mat.candidate_id)
1134 .inset(true)
1135 .selected(item_ix == selected_item)
1136 .on_click(cx.listener(move |editor, _event, cx| {
1137 cx.stop_propagation();
1138 if let Some(task) = editor.confirm_completion(
1139 &ConfirmCompletion {
1140 item_ix: Some(item_ix),
1141 },
1142 cx,
1143 ) {
1144 task.detach_and_log_err(cx)
1145 }
1146 }))
1147 .child(h_flex().overflow_hidden().child(completion_label))
1148 .end_slot::<Div>(documentation_label),
1149 )
1150 })
1151 .collect()
1152 },
1153 )
1154 .occlude()
1155 .max_h(max_height)
1156 .track_scroll(self.scroll_handle.clone())
1157 .with_width_from_item(widest_completion_ix)
1158 .with_sizing_behavior(ListSizingBehavior::Infer);
1159
1160 Popover::new()
1161 .child(list)
1162 .when_some(multiline_docs, |popover, multiline_docs| {
1163 popover.aside(multiline_docs)
1164 })
1165 .into_any_element()
1166 }
1167
1168 pub async fn filter(&mut self, query: Option<&str>, executor: BackgroundExecutor) {
1169 let mut matches = if let Some(query) = query {
1170 fuzzy::match_strings(
1171 &self.match_candidates,
1172 query,
1173 query.chars().any(|c| c.is_uppercase()),
1174 100,
1175 &Default::default(),
1176 executor,
1177 )
1178 .await
1179 } else {
1180 self.match_candidates
1181 .iter()
1182 .enumerate()
1183 .map(|(candidate_id, candidate)| StringMatch {
1184 candidate_id,
1185 score: Default::default(),
1186 positions: Default::default(),
1187 string: candidate.string.clone(),
1188 })
1189 .collect()
1190 };
1191
1192 // Remove all candidates where the query's start does not match the start of any word in the candidate
1193 if let Some(query) = query {
1194 if let Some(query_start) = query.chars().next() {
1195 matches.retain(|string_match| {
1196 split_words(&string_match.string).any(|word| {
1197 // Check that the first codepoint of the word as lowercase matches the first
1198 // codepoint of the query as lowercase
1199 word.chars()
1200 .flat_map(|codepoint| codepoint.to_lowercase())
1201 .zip(query_start.to_lowercase())
1202 .all(|(word_cp, query_cp)| word_cp == query_cp)
1203 })
1204 });
1205 }
1206 }
1207
1208 let completions = self.completions.read();
1209 matches.sort_unstable_by_key(|mat| {
1210 // We do want to strike a balance here between what the language server tells us
1211 // to sort by (the sort_text) and what are "obvious" good matches (i.e. when you type
1212 // `Creat` and there is a local variable called `CreateComponent`).
1213 // So what we do is: we bucket all matches into two buckets
1214 // - Strong matches
1215 // - Weak matches
1216 // Strong matches are the ones with a high fuzzy-matcher score (the "obvious" matches)
1217 // and the Weak matches are the rest.
1218 //
1219 // For the strong matches, we sort by the language-servers score first and for the weak
1220 // matches, we prefer our fuzzy finder first.
1221 //
1222 // The thinking behind that: it's useless to take the sort_text the language-server gives
1223 // us into account when it's obviously a bad match.
1224
1225 #[derive(PartialEq, Eq, PartialOrd, Ord)]
1226 enum MatchScore<'a> {
1227 Strong {
1228 sort_text: Option<&'a str>,
1229 score: Reverse<OrderedFloat<f64>>,
1230 sort_key: (usize, &'a str),
1231 },
1232 Weak {
1233 score: Reverse<OrderedFloat<f64>>,
1234 sort_text: Option<&'a str>,
1235 sort_key: (usize, &'a str),
1236 },
1237 }
1238
1239 let completion = &completions[mat.candidate_id];
1240 let sort_key = completion.sort_key();
1241 let sort_text = completion.lsp_completion.sort_text.as_deref();
1242 let score = Reverse(OrderedFloat(mat.score));
1243
1244 if mat.score >= 0.2 {
1245 MatchScore::Strong {
1246 sort_text,
1247 score,
1248 sort_key,
1249 }
1250 } else {
1251 MatchScore::Weak {
1252 score,
1253 sort_text,
1254 sort_key,
1255 }
1256 }
1257 });
1258
1259 for mat in &mut matches {
1260 let completion = &completions[mat.candidate_id];
1261 mat.string.clone_from(&completion.label.text);
1262 for position in &mut mat.positions {
1263 *position += completion.label.filter_range.start;
1264 }
1265 }
1266 drop(completions);
1267
1268 self.matches = matches.into();
1269 self.selected_item = 0;
1270 }
1271}
1272
1273#[derive(Clone)]
1274struct CodeActionContents {
1275 tasks: Option<Arc<ResolvedTasks>>,
1276 actions: Option<Arc<[CodeAction]>>,
1277}
1278
1279impl CodeActionContents {
1280 fn len(&self) -> usize {
1281 match (&self.tasks, &self.actions) {
1282 (Some(tasks), Some(actions)) => actions.len() + tasks.templates.len(),
1283 (Some(tasks), None) => tasks.templates.len(),
1284 (None, Some(actions)) => actions.len(),
1285 (None, None) => 0,
1286 }
1287 }
1288
1289 fn is_empty(&self) -> bool {
1290 match (&self.tasks, &self.actions) {
1291 (Some(tasks), Some(actions)) => actions.is_empty() && tasks.templates.is_empty(),
1292 (Some(tasks), None) => tasks.templates.is_empty(),
1293 (None, Some(actions)) => actions.is_empty(),
1294 (None, None) => true,
1295 }
1296 }
1297
1298 fn iter(&self) -> impl Iterator<Item = CodeActionsItem> + '_ {
1299 self.tasks
1300 .iter()
1301 .flat_map(|tasks| {
1302 tasks
1303 .templates
1304 .iter()
1305 .map(|(kind, task)| CodeActionsItem::Task(kind.clone(), task.clone()))
1306 })
1307 .chain(self.actions.iter().flat_map(|actions| {
1308 actions
1309 .iter()
1310 .map(|action| CodeActionsItem::CodeAction(action.clone()))
1311 }))
1312 }
1313 fn get(&self, index: usize) -> Option<CodeActionsItem> {
1314 match (&self.tasks, &self.actions) {
1315 (Some(tasks), Some(actions)) => {
1316 if index < tasks.templates.len() {
1317 tasks
1318 .templates
1319 .get(index)
1320 .cloned()
1321 .map(|(kind, task)| CodeActionsItem::Task(kind, task))
1322 } else {
1323 actions
1324 .get(index - tasks.templates.len())
1325 .cloned()
1326 .map(CodeActionsItem::CodeAction)
1327 }
1328 }
1329 (Some(tasks), None) => tasks
1330 .templates
1331 .get(index)
1332 .cloned()
1333 .map(|(kind, task)| CodeActionsItem::Task(kind, task)),
1334 (None, Some(actions)) => actions.get(index).cloned().map(CodeActionsItem::CodeAction),
1335 (None, None) => None,
1336 }
1337 }
1338}
1339
1340#[allow(clippy::large_enum_variant)]
1341#[derive(Clone)]
1342enum CodeActionsItem {
1343 Task(TaskSourceKind, ResolvedTask),
1344 CodeAction(CodeAction),
1345}
1346
1347impl CodeActionsItem {
1348 fn as_task(&self) -> Option<&ResolvedTask> {
1349 let Self::Task(_, task) = self else {
1350 return None;
1351 };
1352 Some(task)
1353 }
1354 fn as_code_action(&self) -> Option<&CodeAction> {
1355 let Self::CodeAction(action) = self else {
1356 return None;
1357 };
1358 Some(action)
1359 }
1360 fn label(&self) -> String {
1361 match self {
1362 Self::CodeAction(action) => action.lsp_action.title.clone(),
1363 Self::Task(_, task) => task.resolved_label.clone(),
1364 }
1365 }
1366}
1367
1368struct CodeActionsMenu {
1369 actions: CodeActionContents,
1370 buffer: Model<Buffer>,
1371 selected_item: usize,
1372 scroll_handle: UniformListScrollHandle,
1373 deployed_from_indicator: Option<DisplayRow>,
1374}
1375
1376impl CodeActionsMenu {
1377 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
1378 self.selected_item = 0;
1379 self.scroll_handle.scroll_to_item(self.selected_item);
1380 cx.notify()
1381 }
1382
1383 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
1384 if self.selected_item > 0 {
1385 self.selected_item -= 1;
1386 } else {
1387 self.selected_item = self.actions.len() - 1;
1388 }
1389 self.scroll_handle.scroll_to_item(self.selected_item);
1390 cx.notify();
1391 }
1392
1393 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
1394 if self.selected_item + 1 < self.actions.len() {
1395 self.selected_item += 1;
1396 } else {
1397 self.selected_item = 0;
1398 }
1399 self.scroll_handle.scroll_to_item(self.selected_item);
1400 cx.notify();
1401 }
1402
1403 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
1404 self.selected_item = self.actions.len() - 1;
1405 self.scroll_handle.scroll_to_item(self.selected_item);
1406 cx.notify()
1407 }
1408
1409 fn visible(&self) -> bool {
1410 !self.actions.is_empty()
1411 }
1412
1413 fn render(
1414 &self,
1415 cursor_position: DisplayPoint,
1416 _style: &EditorStyle,
1417 max_height: Pixels,
1418 cx: &mut ViewContext<Editor>,
1419 ) -> (ContextMenuOrigin, AnyElement) {
1420 let actions = self.actions.clone();
1421 let selected_item = self.selected_item;
1422 let element = uniform_list(
1423 cx.view().clone(),
1424 "code_actions_menu",
1425 self.actions.len(),
1426 move |_this, range, cx| {
1427 actions
1428 .iter()
1429 .skip(range.start)
1430 .take(range.end - range.start)
1431 .enumerate()
1432 .map(|(ix, action)| {
1433 let item_ix = range.start + ix;
1434 let selected = selected_item == item_ix;
1435 let colors = cx.theme().colors();
1436 div()
1437 .px_2()
1438 .text_color(colors.text)
1439 .when(selected, |style| {
1440 style
1441 .bg(colors.element_active)
1442 .text_color(colors.text_accent)
1443 })
1444 .hover(|style| {
1445 style
1446 .bg(colors.element_hover)
1447 .text_color(colors.text_accent)
1448 })
1449 .whitespace_nowrap()
1450 .when_some(action.as_code_action(), |this, action| {
1451 this.on_mouse_down(
1452 MouseButton::Left,
1453 cx.listener(move |editor, _, cx| {
1454 cx.stop_propagation();
1455 if let Some(task) = editor.confirm_code_action(
1456 &ConfirmCodeAction {
1457 item_ix: Some(item_ix),
1458 },
1459 cx,
1460 ) {
1461 task.detach_and_log_err(cx)
1462 }
1463 }),
1464 )
1465 // TASK: It would be good to make lsp_action.title a SharedString to avoid allocating here.
1466 .child(SharedString::from(action.lsp_action.title.clone()))
1467 })
1468 .when_some(action.as_task(), |this, task| {
1469 this.on_mouse_down(
1470 MouseButton::Left,
1471 cx.listener(move |editor, _, cx| {
1472 cx.stop_propagation();
1473 if let Some(task) = editor.confirm_code_action(
1474 &ConfirmCodeAction {
1475 item_ix: Some(item_ix),
1476 },
1477 cx,
1478 ) {
1479 task.detach_and_log_err(cx)
1480 }
1481 }),
1482 )
1483 .child(SharedString::from(task.resolved_label.clone()))
1484 })
1485 })
1486 .collect()
1487 },
1488 )
1489 .elevation_1(cx)
1490 .px_2()
1491 .py_1()
1492 .max_h(max_height)
1493 .occlude()
1494 .track_scroll(self.scroll_handle.clone())
1495 .with_width_from_item(
1496 self.actions
1497 .iter()
1498 .enumerate()
1499 .max_by_key(|(_, action)| match action {
1500 CodeActionsItem::Task(_, task) => task.resolved_label.chars().count(),
1501 CodeActionsItem::CodeAction(action) => action.lsp_action.title.chars().count(),
1502 })
1503 .map(|(ix, _)| ix),
1504 )
1505 .with_sizing_behavior(ListSizingBehavior::Infer)
1506 .into_any_element();
1507
1508 let cursor_position = if let Some(row) = self.deployed_from_indicator {
1509 ContextMenuOrigin::GutterIndicator(row)
1510 } else {
1511 ContextMenuOrigin::EditorPoint(cursor_position)
1512 };
1513
1514 (cursor_position, element)
1515 }
1516}
1517
1518#[derive(Debug)]
1519struct ActiveDiagnosticGroup {
1520 primary_range: Range<Anchor>,
1521 primary_message: String,
1522 group_id: usize,
1523 blocks: HashMap<BlockId, Diagnostic>,
1524 is_valid: bool,
1525}
1526
1527#[derive(Serialize, Deserialize, Clone, Debug)]
1528pub struct ClipboardSelection {
1529 pub len: usize,
1530 pub is_entire_line: bool,
1531 pub first_line_indent: u32,
1532}
1533
1534#[derive(Debug)]
1535pub(crate) struct NavigationData {
1536 cursor_anchor: Anchor,
1537 cursor_position: Point,
1538 scroll_anchor: ScrollAnchor,
1539 scroll_top_row: u32,
1540}
1541
1542enum GotoDefinitionKind {
1543 Symbol,
1544 Type,
1545 Implementation,
1546}
1547
1548#[derive(Debug, Clone)]
1549enum InlayHintRefreshReason {
1550 Toggle(bool),
1551 SettingsChange(InlayHintSettings),
1552 NewLinesShown,
1553 BufferEdited(HashSet<Arc<Language>>),
1554 RefreshRequested,
1555 ExcerptsRemoved(Vec<ExcerptId>),
1556}
1557
1558impl InlayHintRefreshReason {
1559 fn description(&self) -> &'static str {
1560 match self {
1561 Self::Toggle(_) => "toggle",
1562 Self::SettingsChange(_) => "settings change",
1563 Self::NewLinesShown => "new lines shown",
1564 Self::BufferEdited(_) => "buffer edited",
1565 Self::RefreshRequested => "refresh requested",
1566 Self::ExcerptsRemoved(_) => "excerpts removed",
1567 }
1568 }
1569}
1570
1571impl Editor {
1572 pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
1573 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1574 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1575 Self::new(EditorMode::SingleLine, buffer, None, false, cx)
1576 }
1577
1578 pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
1579 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1580 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1581 Self::new(EditorMode::Full, buffer, None, false, cx)
1582 }
1583
1584 pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
1585 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1586 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1587 Self::new(
1588 EditorMode::AutoHeight { max_lines },
1589 buffer,
1590 None,
1591 false,
1592 cx,
1593 )
1594 }
1595
1596 pub fn for_buffer(
1597 buffer: Model<Buffer>,
1598 project: Option<Model<Project>>,
1599 cx: &mut ViewContext<Self>,
1600 ) -> Self {
1601 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1602 Self::new(EditorMode::Full, buffer, project, false, cx)
1603 }
1604
1605 pub fn for_multibuffer(
1606 buffer: Model<MultiBuffer>,
1607 project: Option<Model<Project>>,
1608 show_excerpt_controls: bool,
1609 cx: &mut ViewContext<Self>,
1610 ) -> Self {
1611 Self::new(EditorMode::Full, buffer, project, show_excerpt_controls, cx)
1612 }
1613
1614 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1615 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1616 let mut clone = Self::new(
1617 self.mode,
1618 self.buffer.clone(),
1619 self.project.clone(),
1620 show_excerpt_controls,
1621 cx,
1622 );
1623 self.display_map.update(cx, |display_map, cx| {
1624 let snapshot = display_map.snapshot(cx);
1625 clone.display_map.update(cx, |display_map, cx| {
1626 display_map.set_state(&snapshot, cx);
1627 });
1628 });
1629 clone.selections.clone_state(&self.selections);
1630 clone.scroll_manager.clone_state(&self.scroll_manager);
1631 clone.searchable = self.searchable;
1632 clone
1633 }
1634
1635 fn new(
1636 mode: EditorMode,
1637 buffer: Model<MultiBuffer>,
1638 project: Option<Model<Project>>,
1639 show_excerpt_controls: bool,
1640 cx: &mut ViewContext<Self>,
1641 ) -> Self {
1642 let style = cx.text_style();
1643 let font_size = style.font_size.to_pixels(cx.rem_size());
1644 let editor = cx.view().downgrade();
1645 let fold_placeholder = FoldPlaceholder {
1646 constrain_width: true,
1647 render: Arc::new(move |fold_id, fold_range, cx| {
1648 let editor = editor.clone();
1649 div()
1650 .id(fold_id)
1651 .bg(cx.theme().colors().ghost_element_background)
1652 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1653 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1654 .rounded_sm()
1655 .size_full()
1656 .cursor_pointer()
1657 .child("⋯")
1658 .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
1659 .on_click(move |_, cx| {
1660 editor
1661 .update(cx, |editor, cx| {
1662 editor.unfold_ranges(
1663 [fold_range.start..fold_range.end],
1664 true,
1665 false,
1666 cx,
1667 );
1668 cx.stop_propagation();
1669 })
1670 .ok();
1671 })
1672 .into_any()
1673 }),
1674 merge_adjacent: true,
1675 };
1676 let file_header_size = if show_excerpt_controls { 3 } else { 2 };
1677 let display_map = cx.new_model(|cx| {
1678 DisplayMap::new(
1679 buffer.clone(),
1680 style.font(),
1681 font_size,
1682 None,
1683 show_excerpt_controls,
1684 file_header_size,
1685 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1686 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1687 fold_placeholder,
1688 cx,
1689 )
1690 });
1691
1692 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1693
1694 let blink_manager = cx.new_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1695
1696 let soft_wrap_mode_override =
1697 (mode == EditorMode::SingleLine).then(|| language_settings::SoftWrap::PreferLine);
1698
1699 let mut project_subscriptions = Vec::new();
1700 if mode == EditorMode::Full {
1701 if let Some(project) = project.as_ref() {
1702 if buffer.read(cx).is_singleton() {
1703 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1704 cx.emit(EditorEvent::TitleChanged);
1705 }));
1706 }
1707 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1708 if let project::Event::RefreshInlayHints = event {
1709 editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1710 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1711 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1712 let focus_handle = editor.focus_handle(cx);
1713 if focus_handle.is_focused(cx) {
1714 let snapshot = buffer.read(cx).snapshot();
1715 for (range, snippet) in snippet_edits {
1716 let editor_range =
1717 language::range_from_lsp(*range).to_offset(&snapshot);
1718 editor
1719 .insert_snippet(&[editor_range], snippet.clone(), cx)
1720 .ok();
1721 }
1722 }
1723 }
1724 }
1725 }));
1726 let task_inventory = project.read(cx).task_inventory().clone();
1727 project_subscriptions.push(cx.observe(&task_inventory, |editor, _, cx| {
1728 editor.tasks_update_task = Some(editor.refresh_runnables(cx));
1729 }));
1730 }
1731 }
1732
1733 let inlay_hint_settings = inlay_hint_settings(
1734 selections.newest_anchor().head(),
1735 &buffer.read(cx).snapshot(cx),
1736 cx,
1737 );
1738 let focus_handle = cx.focus_handle();
1739 cx.on_focus(&focus_handle, Self::handle_focus).detach();
1740 cx.on_focus_out(&focus_handle, Self::handle_focus_out)
1741 .detach();
1742 cx.on_blur(&focus_handle, Self::handle_blur).detach();
1743
1744 let show_indent_guides = if mode == EditorMode::SingleLine {
1745 Some(false)
1746 } else {
1747 None
1748 };
1749
1750 let mut this = Self {
1751 focus_handle,
1752 last_focused_descendant: None,
1753 buffer: buffer.clone(),
1754 display_map: display_map.clone(),
1755 selections,
1756 scroll_manager: ScrollManager::new(cx),
1757 columnar_selection_tail: None,
1758 add_selections_state: None,
1759 select_next_state: None,
1760 select_prev_state: None,
1761 selection_history: Default::default(),
1762 autoclose_regions: Default::default(),
1763 snippet_stack: Default::default(),
1764 select_larger_syntax_node_stack: Vec::new(),
1765 ime_transaction: Default::default(),
1766 active_diagnostics: None,
1767 soft_wrap_mode_override,
1768 completion_provider: project.clone().map(|project| Box::new(project) as _),
1769 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1770 project,
1771 blink_manager: blink_manager.clone(),
1772 show_local_selections: true,
1773 mode,
1774 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1775 show_gutter: mode == EditorMode::Full,
1776 show_line_numbers: None,
1777 show_git_diff_gutter: None,
1778 show_code_actions: None,
1779 show_wrap_guides: None,
1780 show_indent_guides,
1781 placeholder_text: None,
1782 highlight_order: 0,
1783 highlighted_rows: HashMap::default(),
1784 background_highlights: Default::default(),
1785 gutter_highlights: TreeMap::default(),
1786 scrollbar_marker_state: ScrollbarMarkerState::default(),
1787 active_indent_guides_state: ActiveIndentGuidesState::default(),
1788 nav_history: None,
1789 context_menu: RwLock::new(None),
1790 mouse_context_menu: None,
1791 completion_tasks: Default::default(),
1792 find_all_references_task_sources: Vec::new(),
1793 next_completion_id: 0,
1794 completion_documentation_pre_resolve_debounce: DebouncedDelay::new(),
1795 next_inlay_id: 0,
1796 available_code_actions: Default::default(),
1797 code_actions_task: Default::default(),
1798 document_highlights_task: Default::default(),
1799 linked_editing_range_task: Default::default(),
1800 pending_rename: Default::default(),
1801 searchable: true,
1802 cursor_shape: Default::default(),
1803 current_line_highlight: None,
1804 autoindent_mode: Some(AutoindentMode::EachLine),
1805 collapse_matches: false,
1806 workspace: None,
1807 keymap_context_layers: Default::default(),
1808 input_enabled: true,
1809 use_modal_editing: mode == EditorMode::Full,
1810 read_only: false,
1811 use_autoclose: true,
1812 auto_replace_emoji_shortcode: false,
1813 leader_peer_id: None,
1814 remote_id: None,
1815 hover_state: Default::default(),
1816 hovered_link_state: Default::default(),
1817 inline_completion_provider: None,
1818 active_inline_completion: None,
1819 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1820 expanded_hunks: ExpandedHunks::default(),
1821 gutter_hovered: false,
1822 pixel_position_of_newest_cursor: None,
1823 last_bounds: None,
1824 expect_bounds_change: None,
1825 gutter_dimensions: GutterDimensions::default(),
1826 style: None,
1827 show_cursor_names: false,
1828 hovered_cursors: Default::default(),
1829 next_editor_action_id: EditorActionId::default(),
1830 editor_actions: Rc::default(),
1831 vim_replace_map: Default::default(),
1832 show_inline_completions: mode == EditorMode::Full,
1833 custom_context_menu: None,
1834 show_git_blame_gutter: false,
1835 show_git_blame_inline: false,
1836 show_git_blame_inline_delay_task: None,
1837 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1838 blame: None,
1839 blame_subscription: None,
1840 file_header_size,
1841 tasks: Default::default(),
1842 _subscriptions: vec![
1843 cx.observe(&buffer, Self::on_buffer_changed),
1844 cx.subscribe(&buffer, Self::on_buffer_event),
1845 cx.observe(&display_map, Self::on_display_map_changed),
1846 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1847 cx.observe_global::<SettingsStore>(Self::settings_changed),
1848 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1849 cx.observe_window_activation(|editor, cx| {
1850 let active = cx.is_window_active();
1851 editor.blink_manager.update(cx, |blink_manager, cx| {
1852 if active {
1853 blink_manager.enable(cx);
1854 } else {
1855 blink_manager.show_cursor(cx);
1856 blink_manager.disable(cx);
1857 }
1858 });
1859 }),
1860 ],
1861 tasks_update_task: None,
1862 linked_edit_ranges: Default::default(),
1863 previous_search_ranges: None,
1864 };
1865 this.tasks_update_task = Some(this.refresh_runnables(cx));
1866 this._subscriptions.extend(project_subscriptions);
1867
1868 this.end_selection(cx);
1869 this.scroll_manager.show_scrollbar(cx);
1870
1871 if mode == EditorMode::Full {
1872 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1873 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1874
1875 if this.git_blame_inline_enabled {
1876 this.git_blame_inline_enabled = true;
1877 this.start_git_blame_inline(false, cx);
1878 }
1879 }
1880
1881 this.report_editor_event("open", None, cx);
1882 this
1883 }
1884
1885 pub fn mouse_menu_is_focused(&self, cx: &mut WindowContext) -> bool {
1886 self.mouse_context_menu
1887 .as_ref()
1888 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(cx))
1889 }
1890
1891 fn key_context(&self, cx: &AppContext) -> KeyContext {
1892 let mut key_context = KeyContext::new_with_defaults();
1893 key_context.add("Editor");
1894 let mode = match self.mode {
1895 EditorMode::SingleLine => "single_line",
1896 EditorMode::AutoHeight { .. } => "auto_height",
1897 EditorMode::Full => "full",
1898 };
1899 key_context.set("mode", mode);
1900 if self.pending_rename.is_some() {
1901 key_context.add("renaming");
1902 }
1903 if self.context_menu_visible() {
1904 match self.context_menu.read().as_ref() {
1905 Some(ContextMenu::Completions(_)) => {
1906 key_context.add("menu");
1907 key_context.add("showing_completions")
1908 }
1909 Some(ContextMenu::CodeActions(_)) => {
1910 key_context.add("menu");
1911 key_context.add("showing_code_actions")
1912 }
1913 None => {}
1914 }
1915 }
1916
1917 for layer in self.keymap_context_layers.values() {
1918 key_context.extend(layer);
1919 }
1920
1921 if let Some(extension) = self
1922 .buffer
1923 .read(cx)
1924 .as_singleton()
1925 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
1926 {
1927 key_context.set("extension", extension.to_string());
1928 }
1929
1930 if self.has_active_inline_completion(cx) {
1931 key_context.add("copilot_suggestion");
1932 key_context.add("inline_completion");
1933 }
1934
1935 key_context
1936 }
1937
1938 pub fn new_file(
1939 workspace: &mut Workspace,
1940 _: &workspace::NewFile,
1941 cx: &mut ViewContext<Workspace>,
1942 ) {
1943 let project = workspace.project().clone();
1944 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1945
1946 cx.spawn(|workspace, mut cx| async move {
1947 let buffer = create.await?;
1948 workspace.update(&mut cx, |workspace, cx| {
1949 workspace.add_item_to_active_pane(
1950 Box::new(
1951 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)),
1952 ),
1953 None,
1954 cx,
1955 )
1956 })
1957 })
1958 .detach_and_prompt_err("Failed to create buffer", cx, |e, _| match e.error_code() {
1959 ErrorCode::RemoteUpgradeRequired => Some(format!(
1960 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1961 e.error_tag("required").unwrap_or("the latest version")
1962 )),
1963 _ => None,
1964 });
1965 }
1966
1967 pub fn new_file_in_direction(
1968 workspace: &mut Workspace,
1969 action: &workspace::NewFileInDirection,
1970 cx: &mut ViewContext<Workspace>,
1971 ) {
1972 let project = workspace.project().clone();
1973 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1974 let direction = action.0;
1975
1976 cx.spawn(|workspace, mut cx| async move {
1977 let buffer = create.await?;
1978 workspace.update(&mut cx, move |workspace, cx| {
1979 workspace.split_item(
1980 direction,
1981 Box::new(
1982 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)),
1983 ),
1984 cx,
1985 )
1986 })?;
1987 anyhow::Ok(())
1988 })
1989 .detach_and_prompt_err("Failed to create buffer", cx, |e, _| match e.error_code() {
1990 ErrorCode::RemoteUpgradeRequired => Some(format!(
1991 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1992 e.error_tag("required").unwrap_or("the latest version")
1993 )),
1994 _ => None,
1995 });
1996 }
1997
1998 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
1999 self.buffer.read(cx).replica_id()
2000 }
2001
2002 pub fn leader_peer_id(&self) -> Option<PeerId> {
2003 self.leader_peer_id
2004 }
2005
2006 pub fn buffer(&self) -> &Model<MultiBuffer> {
2007 &self.buffer
2008 }
2009
2010 pub fn workspace(&self) -> Option<View<Workspace>> {
2011 self.workspace.as_ref()?.0.upgrade()
2012 }
2013
2014 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
2015 self.buffer().read(cx).title(cx)
2016 }
2017
2018 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
2019 EditorSnapshot {
2020 mode: self.mode,
2021 show_gutter: self.show_gutter,
2022 show_line_numbers: self.show_line_numbers,
2023 show_git_diff_gutter: self.show_git_diff_gutter,
2024 show_code_actions: self.show_code_actions,
2025 render_git_blame_gutter: self.render_git_blame_gutter(cx),
2026 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
2027 scroll_anchor: self.scroll_manager.anchor(),
2028 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
2029 placeholder_text: self.placeholder_text.clone(),
2030 is_focused: self.focus_handle.is_focused(cx),
2031 current_line_highlight: self
2032 .current_line_highlight
2033 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
2034 gutter_hovered: self.gutter_hovered,
2035 }
2036 }
2037
2038 pub fn language_at<T: ToOffset>(&self, point: T, cx: &AppContext) -> Option<Arc<Language>> {
2039 self.buffer.read(cx).language_at(point, cx)
2040 }
2041
2042 pub fn file_at<T: ToOffset>(
2043 &self,
2044 point: T,
2045 cx: &AppContext,
2046 ) -> Option<Arc<dyn language::File>> {
2047 self.buffer.read(cx).read(cx).file_at(point).cloned()
2048 }
2049
2050 pub fn active_excerpt(
2051 &self,
2052 cx: &AppContext,
2053 ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
2054 self.buffer
2055 .read(cx)
2056 .excerpt_containing(self.selections.newest_anchor().head(), cx)
2057 }
2058
2059 pub fn mode(&self) -> EditorMode {
2060 self.mode
2061 }
2062
2063 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
2064 self.collaboration_hub.as_deref()
2065 }
2066
2067 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
2068 self.collaboration_hub = Some(hub);
2069 }
2070
2071 pub fn set_custom_context_menu(
2072 &mut self,
2073 f: impl 'static
2074 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
2075 ) {
2076 self.custom_context_menu = Some(Box::new(f))
2077 }
2078
2079 pub fn set_completion_provider(&mut self, provider: Box<dyn CompletionProvider>) {
2080 self.completion_provider = Some(provider);
2081 }
2082
2083 pub fn set_inline_completion_provider<T>(
2084 &mut self,
2085 provider: Option<Model<T>>,
2086 cx: &mut ViewContext<Self>,
2087 ) where
2088 T: InlineCompletionProvider,
2089 {
2090 self.inline_completion_provider =
2091 provider.map(|provider| RegisteredInlineCompletionProvider {
2092 _subscription: cx.observe(&provider, |this, _, cx| {
2093 if this.focus_handle.is_focused(cx) {
2094 this.update_visible_inline_completion(cx);
2095 }
2096 }),
2097 provider: Arc::new(provider),
2098 });
2099 self.refresh_inline_completion(false, cx);
2100 }
2101
2102 pub fn placeholder_text(&self, _cx: &mut WindowContext) -> Option<&str> {
2103 self.placeholder_text.as_deref()
2104 }
2105
2106 pub fn set_placeholder_text(
2107 &mut self,
2108 placeholder_text: impl Into<Arc<str>>,
2109 cx: &mut ViewContext<Self>,
2110 ) {
2111 let placeholder_text = Some(placeholder_text.into());
2112 if self.placeholder_text != placeholder_text {
2113 self.placeholder_text = placeholder_text;
2114 cx.notify();
2115 }
2116 }
2117
2118 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
2119 self.cursor_shape = cursor_shape;
2120 cx.notify();
2121 }
2122
2123 pub fn set_current_line_highlight(
2124 &mut self,
2125 current_line_highlight: Option<CurrentLineHighlight>,
2126 ) {
2127 self.current_line_highlight = current_line_highlight;
2128 }
2129
2130 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
2131 self.collapse_matches = collapse_matches;
2132 }
2133
2134 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
2135 if self.collapse_matches {
2136 return range.start..range.start;
2137 }
2138 range.clone()
2139 }
2140
2141 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
2142 if self.display_map.read(cx).clip_at_line_ends != clip {
2143 self.display_map
2144 .update(cx, |map, _| map.clip_at_line_ends = clip);
2145 }
2146 }
2147
2148 pub fn set_keymap_context_layer<Tag: 'static>(
2149 &mut self,
2150 context: KeyContext,
2151 cx: &mut ViewContext<Self>,
2152 ) {
2153 self.keymap_context_layers
2154 .insert(TypeId::of::<Tag>(), context);
2155 cx.notify();
2156 }
2157
2158 pub fn remove_keymap_context_layer<Tag: 'static>(&mut self, cx: &mut ViewContext<Self>) {
2159 self.keymap_context_layers.remove(&TypeId::of::<Tag>());
2160 cx.notify();
2161 }
2162
2163 pub fn set_input_enabled(&mut self, input_enabled: bool) {
2164 self.input_enabled = input_enabled;
2165 }
2166
2167 pub fn set_autoindent(&mut self, autoindent: bool) {
2168 if autoindent {
2169 self.autoindent_mode = Some(AutoindentMode::EachLine);
2170 } else {
2171 self.autoindent_mode = None;
2172 }
2173 }
2174
2175 pub fn read_only(&self, cx: &AppContext) -> bool {
2176 self.read_only || self.buffer.read(cx).read_only()
2177 }
2178
2179 pub fn set_read_only(&mut self, read_only: bool) {
2180 self.read_only = read_only;
2181 }
2182
2183 pub fn set_use_autoclose(&mut self, autoclose: bool) {
2184 self.use_autoclose = autoclose;
2185 }
2186
2187 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
2188 self.auto_replace_emoji_shortcode = auto_replace;
2189 }
2190
2191 pub fn set_show_inline_completions(&mut self, show_inline_completions: bool) {
2192 self.show_inline_completions = show_inline_completions;
2193 }
2194
2195 pub fn set_use_modal_editing(&mut self, to: bool) {
2196 self.use_modal_editing = to;
2197 }
2198
2199 pub fn use_modal_editing(&self) -> bool {
2200 self.use_modal_editing
2201 }
2202
2203 fn selections_did_change(
2204 &mut self,
2205 local: bool,
2206 old_cursor_position: &Anchor,
2207 show_completions: bool,
2208 cx: &mut ViewContext<Self>,
2209 ) {
2210 // Copy selections to primary selection buffer
2211 #[cfg(target_os = "linux")]
2212 if local {
2213 let selections = self.selections.all::<usize>(cx);
2214 let buffer_handle = self.buffer.read(cx).read(cx);
2215
2216 let mut text = String::new();
2217 for (index, selection) in selections.iter().enumerate() {
2218 let text_for_selection = buffer_handle
2219 .text_for_range(selection.start..selection.end)
2220 .collect::<String>();
2221
2222 text.push_str(&text_for_selection);
2223 if index != selections.len() - 1 {
2224 text.push('\n');
2225 }
2226 }
2227
2228 if !text.is_empty() {
2229 cx.write_to_primary(ClipboardItem::new(text));
2230 }
2231 }
2232
2233 if self.focus_handle.is_focused(cx) && self.leader_peer_id.is_none() {
2234 self.buffer.update(cx, |buffer, cx| {
2235 buffer.set_active_selections(
2236 &self.selections.disjoint_anchors(),
2237 self.selections.line_mode,
2238 self.cursor_shape,
2239 cx,
2240 )
2241 });
2242 }
2243 let display_map = self
2244 .display_map
2245 .update(cx, |display_map, cx| display_map.snapshot(cx));
2246 let buffer = &display_map.buffer_snapshot;
2247 self.add_selections_state = None;
2248 self.select_next_state = None;
2249 self.select_prev_state = None;
2250 self.select_larger_syntax_node_stack.clear();
2251 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2252 self.snippet_stack
2253 .invalidate(&self.selections.disjoint_anchors(), buffer);
2254 self.take_rename(false, cx);
2255
2256 let new_cursor_position = self.selections.newest_anchor().head();
2257
2258 self.push_to_nav_history(
2259 *old_cursor_position,
2260 Some(new_cursor_position.to_point(buffer)),
2261 cx,
2262 );
2263
2264 if local {
2265 let new_cursor_position = self.selections.newest_anchor().head();
2266 let mut context_menu = self.context_menu.write();
2267 let completion_menu = match context_menu.as_ref() {
2268 Some(ContextMenu::Completions(menu)) => Some(menu),
2269
2270 _ => {
2271 *context_menu = None;
2272 None
2273 }
2274 };
2275
2276 if let Some(completion_menu) = completion_menu {
2277 let cursor_position = new_cursor_position.to_offset(buffer);
2278 let (word_range, kind) = buffer.surrounding_word(completion_menu.initial_position);
2279 if kind == Some(CharKind::Word)
2280 && word_range.to_inclusive().contains(&cursor_position)
2281 {
2282 let mut completion_menu = completion_menu.clone();
2283 drop(context_menu);
2284
2285 let query = Self::completion_query(buffer, cursor_position);
2286 cx.spawn(move |this, mut cx| async move {
2287 completion_menu
2288 .filter(query.as_deref(), cx.background_executor().clone())
2289 .await;
2290
2291 this.update(&mut cx, |this, cx| {
2292 let mut context_menu = this.context_menu.write();
2293 let Some(ContextMenu::Completions(menu)) = context_menu.as_ref() else {
2294 return;
2295 };
2296
2297 if menu.id > completion_menu.id {
2298 return;
2299 }
2300
2301 *context_menu = Some(ContextMenu::Completions(completion_menu));
2302 drop(context_menu);
2303 cx.notify();
2304 })
2305 })
2306 .detach();
2307
2308 if show_completions {
2309 self.show_completions(&ShowCompletions { trigger: None }, cx);
2310 }
2311 } else {
2312 drop(context_menu);
2313 self.hide_context_menu(cx);
2314 }
2315 } else {
2316 drop(context_menu);
2317 }
2318
2319 hide_hover(self, cx);
2320
2321 if old_cursor_position.to_display_point(&display_map).row()
2322 != new_cursor_position.to_display_point(&display_map).row()
2323 {
2324 self.available_code_actions.take();
2325 }
2326 self.refresh_code_actions(cx);
2327 self.refresh_document_highlights(cx);
2328 refresh_matching_bracket_highlights(self, cx);
2329 self.discard_inline_completion(false, cx);
2330 linked_editing_ranges::refresh_linked_ranges(self, cx);
2331 if self.git_blame_inline_enabled {
2332 self.start_inline_blame_timer(cx);
2333 }
2334 }
2335
2336 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2337 cx.emit(EditorEvent::SelectionsChanged { local });
2338
2339 if self.selections.disjoint_anchors().len() == 1 {
2340 cx.emit(SearchEvent::ActiveMatchChanged)
2341 }
2342 cx.notify();
2343 }
2344
2345 pub fn change_selections<R>(
2346 &mut self,
2347 autoscroll: Option<Autoscroll>,
2348 cx: &mut ViewContext<Self>,
2349 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2350 ) -> R {
2351 self.change_selections_inner(autoscroll, true, cx, change)
2352 }
2353
2354 pub fn change_selections_inner<R>(
2355 &mut self,
2356 autoscroll: Option<Autoscroll>,
2357 request_completions: bool,
2358 cx: &mut ViewContext<Self>,
2359 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2360 ) -> R {
2361 let old_cursor_position = self.selections.newest_anchor().head();
2362 self.push_to_selection_history();
2363
2364 let (changed, result) = self.selections.change_with(cx, change);
2365
2366 if changed {
2367 if let Some(autoscroll) = autoscroll {
2368 self.request_autoscroll(autoscroll, cx);
2369 }
2370 self.selections_did_change(true, &old_cursor_position, request_completions, cx);
2371 }
2372
2373 result
2374 }
2375
2376 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2377 where
2378 I: IntoIterator<Item = (Range<S>, T)>,
2379 S: ToOffset,
2380 T: Into<Arc<str>>,
2381 {
2382 if self.read_only(cx) {
2383 return;
2384 }
2385
2386 self.buffer
2387 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2388 }
2389
2390 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2391 where
2392 I: IntoIterator<Item = (Range<S>, T)>,
2393 S: ToOffset,
2394 T: Into<Arc<str>>,
2395 {
2396 if self.read_only(cx) {
2397 return;
2398 }
2399
2400 self.buffer.update(cx, |buffer, cx| {
2401 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2402 });
2403 }
2404
2405 pub fn edit_with_block_indent<I, S, T>(
2406 &mut self,
2407 edits: I,
2408 original_indent_columns: Vec<u32>,
2409 cx: &mut ViewContext<Self>,
2410 ) where
2411 I: IntoIterator<Item = (Range<S>, T)>,
2412 S: ToOffset,
2413 T: Into<Arc<str>>,
2414 {
2415 if self.read_only(cx) {
2416 return;
2417 }
2418
2419 self.buffer.update(cx, |buffer, cx| {
2420 buffer.edit(
2421 edits,
2422 Some(AutoindentMode::Block {
2423 original_indent_columns,
2424 }),
2425 cx,
2426 )
2427 });
2428 }
2429
2430 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
2431 self.hide_context_menu(cx);
2432
2433 match phase {
2434 SelectPhase::Begin {
2435 position,
2436 add,
2437 click_count,
2438 } => self.begin_selection(position, add, click_count, cx),
2439 SelectPhase::BeginColumnar {
2440 position,
2441 goal_column,
2442 reset,
2443 } => self.begin_columnar_selection(position, goal_column, reset, cx),
2444 SelectPhase::Extend {
2445 position,
2446 click_count,
2447 } => self.extend_selection(position, click_count, cx),
2448 SelectPhase::Update {
2449 position,
2450 goal_column,
2451 scroll_delta,
2452 } => self.update_selection(position, goal_column, scroll_delta, cx),
2453 SelectPhase::End => self.end_selection(cx),
2454 }
2455 }
2456
2457 fn extend_selection(
2458 &mut self,
2459 position: DisplayPoint,
2460 click_count: usize,
2461 cx: &mut ViewContext<Self>,
2462 ) {
2463 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2464 let tail = self.selections.newest::<usize>(cx).tail();
2465 self.begin_selection(position, false, click_count, cx);
2466
2467 let position = position.to_offset(&display_map, Bias::Left);
2468 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2469
2470 let mut pending_selection = self
2471 .selections
2472 .pending_anchor()
2473 .expect("extend_selection not called with pending selection");
2474 if position >= tail {
2475 pending_selection.start = tail_anchor;
2476 } else {
2477 pending_selection.end = tail_anchor;
2478 pending_selection.reversed = true;
2479 }
2480
2481 let mut pending_mode = self.selections.pending_mode().unwrap();
2482 match &mut pending_mode {
2483 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2484 _ => {}
2485 }
2486
2487 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
2488 s.set_pending(pending_selection, pending_mode)
2489 });
2490 }
2491
2492 fn begin_selection(
2493 &mut self,
2494 position: DisplayPoint,
2495 add: bool,
2496 click_count: usize,
2497 cx: &mut ViewContext<Self>,
2498 ) {
2499 if !self.focus_handle.is_focused(cx) {
2500 cx.focus(&self.focus_handle);
2501 }
2502
2503 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2504 let buffer = &display_map.buffer_snapshot;
2505 let newest_selection = self.selections.newest_anchor().clone();
2506 let position = display_map.clip_point(position, Bias::Left);
2507
2508 let start;
2509 let end;
2510 let mode;
2511 let auto_scroll;
2512 match click_count {
2513 1 => {
2514 start = buffer.anchor_before(position.to_point(&display_map));
2515 end = start;
2516 mode = SelectMode::Character;
2517 auto_scroll = true;
2518 }
2519 2 => {
2520 let range = movement::surrounding_word(&display_map, position);
2521 start = buffer.anchor_before(range.start.to_point(&display_map));
2522 end = buffer.anchor_before(range.end.to_point(&display_map));
2523 mode = SelectMode::Word(start..end);
2524 auto_scroll = true;
2525 }
2526 3 => {
2527 let position = display_map
2528 .clip_point(position, Bias::Left)
2529 .to_point(&display_map);
2530 let line_start = display_map.prev_line_boundary(position).0;
2531 let next_line_start = buffer.clip_point(
2532 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2533 Bias::Left,
2534 );
2535 start = buffer.anchor_before(line_start);
2536 end = buffer.anchor_before(next_line_start);
2537 mode = SelectMode::Line(start..end);
2538 auto_scroll = true;
2539 }
2540 _ => {
2541 start = buffer.anchor_before(0);
2542 end = buffer.anchor_before(buffer.len());
2543 mode = SelectMode::All;
2544 auto_scroll = false;
2545 }
2546 }
2547
2548 self.change_selections(auto_scroll.then(|| Autoscroll::newest()), cx, |s| {
2549 if !add {
2550 s.clear_disjoint();
2551 } else if click_count > 1 {
2552 s.delete(newest_selection.id)
2553 }
2554
2555 s.set_pending_anchor_range(start..end, mode);
2556 });
2557 }
2558
2559 fn begin_columnar_selection(
2560 &mut self,
2561 position: DisplayPoint,
2562 goal_column: u32,
2563 reset: bool,
2564 cx: &mut ViewContext<Self>,
2565 ) {
2566 if !self.focus_handle.is_focused(cx) {
2567 cx.focus(&self.focus_handle);
2568 }
2569
2570 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2571
2572 if reset {
2573 let pointer_position = display_map
2574 .buffer_snapshot
2575 .anchor_before(position.to_point(&display_map));
2576
2577 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
2578 s.clear_disjoint();
2579 s.set_pending_anchor_range(
2580 pointer_position..pointer_position,
2581 SelectMode::Character,
2582 );
2583 });
2584 }
2585
2586 let tail = self.selections.newest::<Point>(cx).tail();
2587 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2588
2589 if !reset {
2590 self.select_columns(
2591 tail.to_display_point(&display_map),
2592 position,
2593 goal_column,
2594 &display_map,
2595 cx,
2596 );
2597 }
2598 }
2599
2600 fn update_selection(
2601 &mut self,
2602 position: DisplayPoint,
2603 goal_column: u32,
2604 scroll_delta: gpui::Point<f32>,
2605 cx: &mut ViewContext<Self>,
2606 ) {
2607 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2608
2609 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2610 let tail = tail.to_display_point(&display_map);
2611 self.select_columns(tail, position, goal_column, &display_map, cx);
2612 } else if let Some(mut pending) = self.selections.pending_anchor() {
2613 let buffer = self.buffer.read(cx).snapshot(cx);
2614 let head;
2615 let tail;
2616 let mode = self.selections.pending_mode().unwrap();
2617 match &mode {
2618 SelectMode::Character => {
2619 head = position.to_point(&display_map);
2620 tail = pending.tail().to_point(&buffer);
2621 }
2622 SelectMode::Word(original_range) => {
2623 let original_display_range = original_range.start.to_display_point(&display_map)
2624 ..original_range.end.to_display_point(&display_map);
2625 let original_buffer_range = original_display_range.start.to_point(&display_map)
2626 ..original_display_range.end.to_point(&display_map);
2627 if movement::is_inside_word(&display_map, position)
2628 || original_display_range.contains(&position)
2629 {
2630 let word_range = movement::surrounding_word(&display_map, position);
2631 if word_range.start < original_display_range.start {
2632 head = word_range.start.to_point(&display_map);
2633 } else {
2634 head = word_range.end.to_point(&display_map);
2635 }
2636 } else {
2637 head = position.to_point(&display_map);
2638 }
2639
2640 if head <= original_buffer_range.start {
2641 tail = original_buffer_range.end;
2642 } else {
2643 tail = original_buffer_range.start;
2644 }
2645 }
2646 SelectMode::Line(original_range) => {
2647 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2648
2649 let position = display_map
2650 .clip_point(position, Bias::Left)
2651 .to_point(&display_map);
2652 let line_start = display_map.prev_line_boundary(position).0;
2653 let next_line_start = buffer.clip_point(
2654 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2655 Bias::Left,
2656 );
2657
2658 if line_start < original_range.start {
2659 head = line_start
2660 } else {
2661 head = next_line_start
2662 }
2663
2664 if head <= original_range.start {
2665 tail = original_range.end;
2666 } else {
2667 tail = original_range.start;
2668 }
2669 }
2670 SelectMode::All => {
2671 return;
2672 }
2673 };
2674
2675 if head < tail {
2676 pending.start = buffer.anchor_before(head);
2677 pending.end = buffer.anchor_before(tail);
2678 pending.reversed = true;
2679 } else {
2680 pending.start = buffer.anchor_before(tail);
2681 pending.end = buffer.anchor_before(head);
2682 pending.reversed = false;
2683 }
2684
2685 self.change_selections(None, cx, |s| {
2686 s.set_pending(pending, mode);
2687 });
2688 } else {
2689 log::error!("update_selection dispatched with no pending selection");
2690 return;
2691 }
2692
2693 self.apply_scroll_delta(scroll_delta, cx);
2694 cx.notify();
2695 }
2696
2697 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
2698 self.columnar_selection_tail.take();
2699 if self.selections.pending_anchor().is_some() {
2700 let selections = self.selections.all::<usize>(cx);
2701 self.change_selections(None, cx, |s| {
2702 s.select(selections);
2703 s.clear_pending();
2704 });
2705 }
2706 }
2707
2708 fn select_columns(
2709 &mut self,
2710 tail: DisplayPoint,
2711 head: DisplayPoint,
2712 goal_column: u32,
2713 display_map: &DisplaySnapshot,
2714 cx: &mut ViewContext<Self>,
2715 ) {
2716 let start_row = cmp::min(tail.row(), head.row());
2717 let end_row = cmp::max(tail.row(), head.row());
2718 let start_column = cmp::min(tail.column(), goal_column);
2719 let end_column = cmp::max(tail.column(), goal_column);
2720 let reversed = start_column < tail.column();
2721
2722 let selection_ranges = (start_row.0..=end_row.0)
2723 .map(DisplayRow)
2724 .filter_map(|row| {
2725 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2726 let start = display_map
2727 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2728 .to_point(display_map);
2729 let end = display_map
2730 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2731 .to_point(display_map);
2732 if reversed {
2733 Some(end..start)
2734 } else {
2735 Some(start..end)
2736 }
2737 } else {
2738 None
2739 }
2740 })
2741 .collect::<Vec<_>>();
2742
2743 self.change_selections(None, cx, |s| {
2744 s.select_ranges(selection_ranges);
2745 });
2746 cx.notify();
2747 }
2748
2749 pub fn has_pending_nonempty_selection(&self) -> bool {
2750 let pending_nonempty_selection = match self.selections.pending_anchor() {
2751 Some(Selection { start, end, .. }) => start != end,
2752 None => false,
2753 };
2754
2755 pending_nonempty_selection
2756 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2757 }
2758
2759 pub fn has_pending_selection(&self) -> bool {
2760 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2761 }
2762
2763 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
2764 self.clear_expanded_diff_hunks(cx);
2765 if self.dismiss_menus_and_popups(true, cx) {
2766 return;
2767 }
2768
2769 if self.mode == EditorMode::Full {
2770 if self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel()) {
2771 return;
2772 }
2773 }
2774
2775 cx.propagate();
2776 }
2777
2778 pub fn dismiss_menus_and_popups(
2779 &mut self,
2780 should_report_inline_completion_event: bool,
2781 cx: &mut ViewContext<Self>,
2782 ) -> bool {
2783 if self.take_rename(false, cx).is_some() {
2784 return true;
2785 }
2786
2787 if hide_hover(self, cx) {
2788 return true;
2789 }
2790
2791 if self.hide_context_menu(cx).is_some() {
2792 return true;
2793 }
2794
2795 if self.discard_inline_completion(should_report_inline_completion_event, cx) {
2796 return true;
2797 }
2798
2799 if self.snippet_stack.pop().is_some() {
2800 return true;
2801 }
2802
2803 if self.mode == EditorMode::Full {
2804 if self.active_diagnostics.is_some() {
2805 self.dismiss_diagnostics(cx);
2806 return true;
2807 }
2808 }
2809
2810 false
2811 }
2812
2813 fn linked_editing_ranges_for(
2814 &self,
2815 selection: Range<text::Anchor>,
2816 cx: &AppContext,
2817 ) -> Option<HashMap<Model<Buffer>, Vec<Range<text::Anchor>>>> {
2818 if self.linked_edit_ranges.is_empty() {
2819 return None;
2820 }
2821 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2822 selection.end.buffer_id.and_then(|end_buffer_id| {
2823 if selection.start.buffer_id != Some(end_buffer_id) {
2824 return None;
2825 }
2826 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2827 let snapshot = buffer.read(cx).snapshot();
2828 self.linked_edit_ranges
2829 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2830 .map(|ranges| (ranges, snapshot, buffer))
2831 })?;
2832 use text::ToOffset as TO;
2833 // find offset from the start of current range to current cursor position
2834 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2835
2836 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2837 let start_difference = start_offset - start_byte_offset;
2838 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2839 let end_difference = end_offset - start_byte_offset;
2840 // Current range has associated linked ranges.
2841 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2842 for range in linked_ranges.iter() {
2843 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2844 let end_offset = start_offset + end_difference;
2845 let start_offset = start_offset + start_difference;
2846 let start = buffer_snapshot.anchor_after(start_offset);
2847 let end = buffer_snapshot.anchor_after(end_offset);
2848 linked_edits
2849 .entry(buffer.clone())
2850 .or_default()
2851 .push(start..end);
2852 }
2853 Some(linked_edits)
2854 }
2855
2856 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2857 let text: Arc<str> = text.into();
2858
2859 if self.read_only(cx) {
2860 return;
2861 }
2862
2863 let selections = self.selections.all_adjusted(cx);
2864 let mut brace_inserted = false;
2865 let mut edits = Vec::new();
2866 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2867 let mut new_selections = Vec::with_capacity(selections.len());
2868 let mut new_autoclose_regions = Vec::new();
2869 let snapshot = self.buffer.read(cx).read(cx);
2870
2871 for (selection, autoclose_region) in
2872 self.selections_with_autoclose_regions(selections, &snapshot)
2873 {
2874 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2875 // Determine if the inserted text matches the opening or closing
2876 // bracket of any of this language's bracket pairs.
2877 let mut bracket_pair = None;
2878 let mut is_bracket_pair_start = false;
2879 let mut is_bracket_pair_end = false;
2880 if !text.is_empty() {
2881 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
2882 // and they are removing the character that triggered IME popup.
2883 for (pair, enabled) in scope.brackets() {
2884 if !pair.close {
2885 continue;
2886 }
2887
2888 if enabled && pair.start.ends_with(text.as_ref()) {
2889 bracket_pair = Some(pair.clone());
2890 is_bracket_pair_start = true;
2891 break;
2892 }
2893 if pair.end.as_str() == text.as_ref() {
2894 bracket_pair = Some(pair.clone());
2895 is_bracket_pair_end = true;
2896 break;
2897 }
2898 }
2899 }
2900
2901 if let Some(bracket_pair) = bracket_pair {
2902 let autoclose = self.use_autoclose
2903 && snapshot.settings_at(selection.start, cx).use_autoclose;
2904
2905 if selection.is_empty() {
2906 if is_bracket_pair_start {
2907 let prefix_len = bracket_pair.start.len() - text.len();
2908
2909 // If the inserted text is a suffix of an opening bracket and the
2910 // selection is preceded by the rest of the opening bracket, then
2911 // insert the closing bracket.
2912 let following_text_allows_autoclose = snapshot
2913 .chars_at(selection.start)
2914 .next()
2915 .map_or(true, |c| scope.should_autoclose_before(c));
2916 let preceding_text_matches_prefix = prefix_len == 0
2917 || (selection.start.column >= (prefix_len as u32)
2918 && snapshot.contains_str_at(
2919 Point::new(
2920 selection.start.row,
2921 selection.start.column - (prefix_len as u32),
2922 ),
2923 &bracket_pair.start[..prefix_len],
2924 ));
2925 if autoclose
2926 && following_text_allows_autoclose
2927 && preceding_text_matches_prefix
2928 {
2929 let anchor = snapshot.anchor_before(selection.end);
2930 new_selections.push((selection.map(|_| anchor), text.len()));
2931 new_autoclose_regions.push((
2932 anchor,
2933 text.len(),
2934 selection.id,
2935 bracket_pair.clone(),
2936 ));
2937 edits.push((
2938 selection.range(),
2939 format!("{}{}", text, bracket_pair.end).into(),
2940 ));
2941 brace_inserted = true;
2942 continue;
2943 }
2944 }
2945
2946 if let Some(region) = autoclose_region {
2947 // If the selection is followed by an auto-inserted closing bracket,
2948 // then don't insert that closing bracket again; just move the selection
2949 // past the closing bracket.
2950 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2951 && text.as_ref() == region.pair.end.as_str();
2952 if should_skip {
2953 let anchor = snapshot.anchor_after(selection.end);
2954 new_selections
2955 .push((selection.map(|_| anchor), region.pair.end.len()));
2956 continue;
2957 }
2958 }
2959
2960 let always_treat_brackets_as_autoclosed = snapshot
2961 .settings_at(selection.start, cx)
2962 .always_treat_brackets_as_autoclosed;
2963 if always_treat_brackets_as_autoclosed
2964 && is_bracket_pair_end
2965 && snapshot.contains_str_at(selection.end, text.as_ref())
2966 {
2967 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
2968 // and the inserted text is a closing bracket and the selection is followed
2969 // by the closing bracket then move the selection past the closing bracket.
2970 let anchor = snapshot.anchor_after(selection.end);
2971 new_selections.push((selection.map(|_| anchor), text.len()));
2972 continue;
2973 }
2974 }
2975 // If an opening bracket is 1 character long and is typed while
2976 // text is selected, then surround that text with the bracket pair.
2977 else if autoclose
2978 && is_bracket_pair_start
2979 && bracket_pair.start.chars().count() == 1
2980 {
2981 edits.push((selection.start..selection.start, text.clone()));
2982 edits.push((
2983 selection.end..selection.end,
2984 bracket_pair.end.as_str().into(),
2985 ));
2986 brace_inserted = true;
2987 new_selections.push((
2988 Selection {
2989 id: selection.id,
2990 start: snapshot.anchor_after(selection.start),
2991 end: snapshot.anchor_before(selection.end),
2992 reversed: selection.reversed,
2993 goal: selection.goal,
2994 },
2995 0,
2996 ));
2997 continue;
2998 }
2999 }
3000 }
3001
3002 if self.auto_replace_emoji_shortcode
3003 && selection.is_empty()
3004 && text.as_ref().ends_with(':')
3005 {
3006 if let Some(possible_emoji_short_code) =
3007 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
3008 {
3009 if !possible_emoji_short_code.is_empty() {
3010 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
3011 let emoji_shortcode_start = Point::new(
3012 selection.start.row,
3013 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
3014 );
3015
3016 // Remove shortcode from buffer
3017 edits.push((
3018 emoji_shortcode_start..selection.start,
3019 "".to_string().into(),
3020 ));
3021 new_selections.push((
3022 Selection {
3023 id: selection.id,
3024 start: snapshot.anchor_after(emoji_shortcode_start),
3025 end: snapshot.anchor_before(selection.start),
3026 reversed: selection.reversed,
3027 goal: selection.goal,
3028 },
3029 0,
3030 ));
3031
3032 // Insert emoji
3033 let selection_start_anchor = snapshot.anchor_after(selection.start);
3034 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3035 edits.push((selection.start..selection.end, emoji.to_string().into()));
3036
3037 continue;
3038 }
3039 }
3040 }
3041 }
3042
3043 // If not handling any auto-close operation, then just replace the selected
3044 // text with the given input and move the selection to the end of the
3045 // newly inserted text.
3046 let anchor = snapshot.anchor_after(selection.end);
3047 if !self.linked_edit_ranges.is_empty() {
3048 let start_anchor = snapshot.anchor_before(selection.start);
3049 if let Some(ranges) =
3050 self.linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3051 {
3052 for (buffer, edits) in ranges {
3053 linked_edits
3054 .entry(buffer.clone())
3055 .or_default()
3056 .extend(edits.into_iter().map(|range| (range, text.clone())));
3057 }
3058 }
3059 }
3060
3061 new_selections.push((selection.map(|_| anchor), 0));
3062 edits.push((selection.start..selection.end, text.clone()));
3063 }
3064
3065 drop(snapshot);
3066
3067 self.transact(cx, |this, cx| {
3068 this.buffer.update(cx, |buffer, cx| {
3069 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3070 });
3071 for (buffer, edits) in linked_edits {
3072 buffer.update(cx, |buffer, cx| {
3073 let snapshot = buffer.snapshot();
3074 let edits = edits
3075 .into_iter()
3076 .map(|(range, text)| {
3077 use text::ToPoint as TP;
3078 let end_point = TP::to_point(&range.end, &snapshot);
3079 let start_point = TP::to_point(&range.start, &snapshot);
3080 (start_point..end_point, text)
3081 })
3082 .sorted_by_key(|(range, _)| range.start)
3083 .collect::<Vec<_>>();
3084 buffer.edit(edits, None, cx);
3085 })
3086 }
3087 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3088 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3089 let snapshot = this.buffer.read(cx).read(cx);
3090 let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
3091 .zip(new_selection_deltas)
3092 .map(|(selection, delta)| Selection {
3093 id: selection.id,
3094 start: selection.start + delta,
3095 end: selection.end + delta,
3096 reversed: selection.reversed,
3097 goal: SelectionGoal::None,
3098 })
3099 .collect::<Vec<_>>();
3100
3101 let mut i = 0;
3102 for (position, delta, selection_id, pair) in new_autoclose_regions {
3103 let position = position.to_offset(&snapshot) + delta;
3104 let start = snapshot.anchor_before(position);
3105 let end = snapshot.anchor_after(position);
3106 while let Some(existing_state) = this.autoclose_regions.get(i) {
3107 match existing_state.range.start.cmp(&start, &snapshot) {
3108 Ordering::Less => i += 1,
3109 Ordering::Greater => break,
3110 Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
3111 Ordering::Less => i += 1,
3112 Ordering::Equal => break,
3113 Ordering::Greater => break,
3114 },
3115 }
3116 }
3117 this.autoclose_regions.insert(
3118 i,
3119 AutocloseRegion {
3120 selection_id,
3121 range: start..end,
3122 pair,
3123 },
3124 );
3125 }
3126
3127 drop(snapshot);
3128 let had_active_inline_completion = this.has_active_inline_completion(cx);
3129 this.change_selections_inner(Some(Autoscroll::fit()), false, cx, |s| {
3130 s.select(new_selections)
3131 });
3132
3133 if !brace_inserted && EditorSettings::get_global(cx).use_on_type_format {
3134 if let Some(on_type_format_task) =
3135 this.trigger_on_type_formatting(text.to_string(), cx)
3136 {
3137 on_type_format_task.detach_and_log_err(cx);
3138 }
3139 }
3140
3141 let trigger_in_words = !had_active_inline_completion;
3142 this.trigger_completion_on_input(&text, trigger_in_words, cx);
3143 linked_editing_ranges::refresh_linked_ranges(this, cx);
3144 this.refresh_inline_completion(true, cx);
3145 });
3146 }
3147
3148 fn find_possible_emoji_shortcode_at_position(
3149 snapshot: &MultiBufferSnapshot,
3150 position: Point,
3151 ) -> Option<String> {
3152 let mut chars = Vec::new();
3153 let mut found_colon = false;
3154 for char in snapshot.reversed_chars_at(position).take(100) {
3155 // Found a possible emoji shortcode in the middle of the buffer
3156 if found_colon {
3157 if char.is_whitespace() {
3158 chars.reverse();
3159 return Some(chars.iter().collect());
3160 }
3161 // If the previous character is not a whitespace, we are in the middle of a word
3162 // and we only want to complete the shortcode if the word is made up of other emojis
3163 let mut containing_word = String::new();
3164 for ch in snapshot
3165 .reversed_chars_at(position)
3166 .skip(chars.len() + 1)
3167 .take(100)
3168 {
3169 if ch.is_whitespace() {
3170 break;
3171 }
3172 containing_word.push(ch);
3173 }
3174 let containing_word = containing_word.chars().rev().collect::<String>();
3175 if util::word_consists_of_emojis(containing_word.as_str()) {
3176 chars.reverse();
3177 return Some(chars.iter().collect());
3178 }
3179 }
3180
3181 if char.is_whitespace() || !char.is_ascii() {
3182 return None;
3183 }
3184 if char == ':' {
3185 found_colon = true;
3186 } else {
3187 chars.push(char);
3188 }
3189 }
3190 // Found a possible emoji shortcode at the beginning of the buffer
3191 chars.reverse();
3192 Some(chars.iter().collect())
3193 }
3194
3195 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
3196 self.transact(cx, |this, cx| {
3197 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3198 let selections = this.selections.all::<usize>(cx);
3199 let multi_buffer = this.buffer.read(cx);
3200 let buffer = multi_buffer.snapshot(cx);
3201 selections
3202 .iter()
3203 .map(|selection| {
3204 let start_point = selection.start.to_point(&buffer);
3205 let mut indent =
3206 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3207 indent.len = cmp::min(indent.len, start_point.column);
3208 let start = selection.start;
3209 let end = selection.end;
3210 let selection_is_empty = start == end;
3211 let language_scope = buffer.language_scope_at(start);
3212 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3213 &language_scope
3214 {
3215 let leading_whitespace_len = buffer
3216 .reversed_chars_at(start)
3217 .take_while(|c| c.is_whitespace() && *c != '\n')
3218 .map(|c| c.len_utf8())
3219 .sum::<usize>();
3220
3221 let trailing_whitespace_len = buffer
3222 .chars_at(end)
3223 .take_while(|c| c.is_whitespace() && *c != '\n')
3224 .map(|c| c.len_utf8())
3225 .sum::<usize>();
3226
3227 let insert_extra_newline =
3228 language.brackets().any(|(pair, enabled)| {
3229 let pair_start = pair.start.trim_end();
3230 let pair_end = pair.end.trim_start();
3231
3232 enabled
3233 && pair.newline
3234 && buffer.contains_str_at(
3235 end + trailing_whitespace_len,
3236 pair_end,
3237 )
3238 && buffer.contains_str_at(
3239 (start - leading_whitespace_len)
3240 .saturating_sub(pair_start.len()),
3241 pair_start,
3242 )
3243 });
3244
3245 // Comment extension on newline is allowed only for cursor selections
3246 let comment_delimiter = maybe!({
3247 if !selection_is_empty {
3248 return None;
3249 }
3250
3251 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
3252 return None;
3253 }
3254
3255 let delimiters = language.line_comment_prefixes();
3256 let max_len_of_delimiter =
3257 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3258 let (snapshot, range) =
3259 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3260
3261 let mut index_of_first_non_whitespace = 0;
3262 let comment_candidate = snapshot
3263 .chars_for_range(range)
3264 .skip_while(|c| {
3265 let should_skip = c.is_whitespace();
3266 if should_skip {
3267 index_of_first_non_whitespace += 1;
3268 }
3269 should_skip
3270 })
3271 .take(max_len_of_delimiter)
3272 .collect::<String>();
3273 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3274 comment_candidate.starts_with(comment_prefix.as_ref())
3275 })?;
3276 let cursor_is_placed_after_comment_marker =
3277 index_of_first_non_whitespace + comment_prefix.len()
3278 <= start_point.column as usize;
3279 if cursor_is_placed_after_comment_marker {
3280 Some(comment_prefix.clone())
3281 } else {
3282 None
3283 }
3284 });
3285 (comment_delimiter, insert_extra_newline)
3286 } else {
3287 (None, false)
3288 };
3289
3290 let capacity_for_delimiter = comment_delimiter
3291 .as_deref()
3292 .map(str::len)
3293 .unwrap_or_default();
3294 let mut new_text =
3295 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3296 new_text.push_str("\n");
3297 new_text.extend(indent.chars());
3298 if let Some(delimiter) = &comment_delimiter {
3299 new_text.push_str(&delimiter);
3300 }
3301 if insert_extra_newline {
3302 new_text = new_text.repeat(2);
3303 }
3304
3305 let anchor = buffer.anchor_after(end);
3306 let new_selection = selection.map(|_| anchor);
3307 (
3308 (start..end, new_text),
3309 (insert_extra_newline, new_selection),
3310 )
3311 })
3312 .unzip()
3313 };
3314
3315 this.edit_with_autoindent(edits, cx);
3316 let buffer = this.buffer.read(cx).snapshot(cx);
3317 let new_selections = selection_fixup_info
3318 .into_iter()
3319 .map(|(extra_newline_inserted, new_selection)| {
3320 let mut cursor = new_selection.end.to_point(&buffer);
3321 if extra_newline_inserted {
3322 cursor.row -= 1;
3323 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3324 }
3325 new_selection.map(|_| cursor)
3326 })
3327 .collect();
3328
3329 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
3330 this.refresh_inline_completion(true, cx);
3331 });
3332 }
3333
3334 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
3335 let buffer = self.buffer.read(cx);
3336 let snapshot = buffer.snapshot(cx);
3337
3338 let mut edits = Vec::new();
3339 let mut rows = Vec::new();
3340
3341 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3342 let cursor = selection.head();
3343 let row = cursor.row;
3344
3345 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3346
3347 let newline = "\n".to_string();
3348 edits.push((start_of_line..start_of_line, newline));
3349
3350 rows.push(row + rows_inserted as u32);
3351 }
3352
3353 self.transact(cx, |editor, cx| {
3354 editor.edit(edits, cx);
3355
3356 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3357 let mut index = 0;
3358 s.move_cursors_with(|map, _, _| {
3359 let row = rows[index];
3360 index += 1;
3361
3362 let point = Point::new(row, 0);
3363 let boundary = map.next_line_boundary(point).1;
3364 let clipped = map.clip_point(boundary, Bias::Left);
3365
3366 (clipped, SelectionGoal::None)
3367 });
3368 });
3369
3370 let mut indent_edits = Vec::new();
3371 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3372 for row in rows {
3373 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3374 for (row, indent) in indents {
3375 if indent.len == 0 {
3376 continue;
3377 }
3378
3379 let text = match indent.kind {
3380 IndentKind::Space => " ".repeat(indent.len as usize),
3381 IndentKind::Tab => "\t".repeat(indent.len as usize),
3382 };
3383 let point = Point::new(row.0, 0);
3384 indent_edits.push((point..point, text));
3385 }
3386 }
3387 editor.edit(indent_edits, cx);
3388 });
3389 }
3390
3391 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
3392 let buffer = self.buffer.read(cx);
3393 let snapshot = buffer.snapshot(cx);
3394
3395 let mut edits = Vec::new();
3396 let mut rows = Vec::new();
3397 let mut rows_inserted = 0;
3398
3399 for selection in self.selections.all_adjusted(cx) {
3400 let cursor = selection.head();
3401 let row = cursor.row;
3402
3403 let point = Point::new(row + 1, 0);
3404 let start_of_line = snapshot.clip_point(point, Bias::Left);
3405
3406 let newline = "\n".to_string();
3407 edits.push((start_of_line..start_of_line, newline));
3408
3409 rows_inserted += 1;
3410 rows.push(row + rows_inserted);
3411 }
3412
3413 self.transact(cx, |editor, cx| {
3414 editor.edit(edits, cx);
3415
3416 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3417 let mut index = 0;
3418 s.move_cursors_with(|map, _, _| {
3419 let row = rows[index];
3420 index += 1;
3421
3422 let point = Point::new(row, 0);
3423 let boundary = map.next_line_boundary(point).1;
3424 let clipped = map.clip_point(boundary, Bias::Left);
3425
3426 (clipped, SelectionGoal::None)
3427 });
3428 });
3429
3430 let mut indent_edits = Vec::new();
3431 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3432 for row in rows {
3433 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3434 for (row, indent) in indents {
3435 if indent.len == 0 {
3436 continue;
3437 }
3438
3439 let text = match indent.kind {
3440 IndentKind::Space => " ".repeat(indent.len as usize),
3441 IndentKind::Tab => "\t".repeat(indent.len as usize),
3442 };
3443 let point = Point::new(row.0, 0);
3444 indent_edits.push((point..point, text));
3445 }
3446 }
3447 editor.edit(indent_edits, cx);
3448 });
3449 }
3450
3451 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3452 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3453 original_indent_columns: Vec::new(),
3454 });
3455 self.insert_with_autoindent_mode(text, autoindent, cx);
3456 }
3457
3458 fn insert_with_autoindent_mode(
3459 &mut self,
3460 text: &str,
3461 autoindent_mode: Option<AutoindentMode>,
3462 cx: &mut ViewContext<Self>,
3463 ) {
3464 if self.read_only(cx) {
3465 return;
3466 }
3467
3468 let text: Arc<str> = text.into();
3469 self.transact(cx, |this, cx| {
3470 let old_selections = this.selections.all_adjusted(cx);
3471 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3472 let anchors = {
3473 let snapshot = buffer.read(cx);
3474 old_selections
3475 .iter()
3476 .map(|s| {
3477 let anchor = snapshot.anchor_after(s.head());
3478 s.map(|_| anchor)
3479 })
3480 .collect::<Vec<_>>()
3481 };
3482 buffer.edit(
3483 old_selections
3484 .iter()
3485 .map(|s| (s.start..s.end, text.clone())),
3486 autoindent_mode,
3487 cx,
3488 );
3489 anchors
3490 });
3491
3492 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3493 s.select_anchors(selection_anchors);
3494 })
3495 });
3496 }
3497
3498 fn trigger_completion_on_input(
3499 &mut self,
3500 text: &str,
3501 trigger_in_words: bool,
3502 cx: &mut ViewContext<Self>,
3503 ) {
3504 if self.is_completion_trigger(text, trigger_in_words, cx) {
3505 self.show_completions(
3506 &ShowCompletions {
3507 trigger: text.chars().last(),
3508 },
3509 cx,
3510 );
3511 } else {
3512 self.hide_context_menu(cx);
3513 }
3514 }
3515
3516 fn is_completion_trigger(
3517 &self,
3518 text: &str,
3519 trigger_in_words: bool,
3520 cx: &mut ViewContext<Self>,
3521 ) -> bool {
3522 let position = self.selections.newest_anchor().head();
3523 let multibuffer = self.buffer.read(cx);
3524 let Some(buffer) = position
3525 .buffer_id
3526 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3527 else {
3528 return false;
3529 };
3530
3531 if let Some(completion_provider) = &self.completion_provider {
3532 completion_provider.is_completion_trigger(
3533 &buffer,
3534 position.text_anchor,
3535 text,
3536 trigger_in_words,
3537 cx,
3538 )
3539 } else {
3540 false
3541 }
3542 }
3543
3544 /// If any empty selections is touching the start of its innermost containing autoclose
3545 /// region, expand it to select the brackets.
3546 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
3547 let selections = self.selections.all::<usize>(cx);
3548 let buffer = self.buffer.read(cx).read(cx);
3549 let new_selections = self
3550 .selections_with_autoclose_regions(selections, &buffer)
3551 .map(|(mut selection, region)| {
3552 if !selection.is_empty() {
3553 return selection;
3554 }
3555
3556 if let Some(region) = region {
3557 let mut range = region.range.to_offset(&buffer);
3558 if selection.start == range.start && range.start >= region.pair.start.len() {
3559 range.start -= region.pair.start.len();
3560 if buffer.contains_str_at(range.start, ®ion.pair.start)
3561 && buffer.contains_str_at(range.end, ®ion.pair.end)
3562 {
3563 range.end += region.pair.end.len();
3564 selection.start = range.start;
3565 selection.end = range.end;
3566
3567 return selection;
3568 }
3569 }
3570 }
3571
3572 let always_treat_brackets_as_autoclosed = buffer
3573 .settings_at(selection.start, cx)
3574 .always_treat_brackets_as_autoclosed;
3575
3576 if !always_treat_brackets_as_autoclosed {
3577 return selection;
3578 }
3579
3580 if let Some(scope) = buffer.language_scope_at(selection.start) {
3581 for (pair, enabled) in scope.brackets() {
3582 if !enabled || !pair.close {
3583 continue;
3584 }
3585
3586 if buffer.contains_str_at(selection.start, &pair.end) {
3587 let pair_start_len = pair.start.len();
3588 if buffer.contains_str_at(selection.start - pair_start_len, &pair.start)
3589 {
3590 selection.start -= pair_start_len;
3591 selection.end += pair.end.len();
3592
3593 return selection;
3594 }
3595 }
3596 }
3597 }
3598
3599 selection
3600 })
3601 .collect();
3602
3603 drop(buffer);
3604 self.change_selections(None, cx, |selections| selections.select(new_selections));
3605 }
3606
3607 /// Iterate the given selections, and for each one, find the smallest surrounding
3608 /// autoclose region. This uses the ordering of the selections and the autoclose
3609 /// regions to avoid repeated comparisons.
3610 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3611 &'a self,
3612 selections: impl IntoIterator<Item = Selection<D>>,
3613 buffer: &'a MultiBufferSnapshot,
3614 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3615 let mut i = 0;
3616 let mut regions = self.autoclose_regions.as_slice();
3617 selections.into_iter().map(move |selection| {
3618 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3619
3620 let mut enclosing = None;
3621 while let Some(pair_state) = regions.get(i) {
3622 if pair_state.range.end.to_offset(buffer) < range.start {
3623 regions = ®ions[i + 1..];
3624 i = 0;
3625 } else if pair_state.range.start.to_offset(buffer) > range.end {
3626 break;
3627 } else {
3628 if pair_state.selection_id == selection.id {
3629 enclosing = Some(pair_state);
3630 }
3631 i += 1;
3632 }
3633 }
3634
3635 (selection.clone(), enclosing)
3636 })
3637 }
3638
3639 /// Remove any autoclose regions that no longer contain their selection.
3640 fn invalidate_autoclose_regions(
3641 &mut self,
3642 mut selections: &[Selection<Anchor>],
3643 buffer: &MultiBufferSnapshot,
3644 ) {
3645 self.autoclose_regions.retain(|state| {
3646 let mut i = 0;
3647 while let Some(selection) = selections.get(i) {
3648 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3649 selections = &selections[1..];
3650 continue;
3651 }
3652 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3653 break;
3654 }
3655 if selection.id == state.selection_id {
3656 return true;
3657 } else {
3658 i += 1;
3659 }
3660 }
3661 false
3662 });
3663 }
3664
3665 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3666 let offset = position.to_offset(buffer);
3667 let (word_range, kind) = buffer.surrounding_word(offset);
3668 if offset > word_range.start && kind == Some(CharKind::Word) {
3669 Some(
3670 buffer
3671 .text_for_range(word_range.start..offset)
3672 .collect::<String>(),
3673 )
3674 } else {
3675 None
3676 }
3677 }
3678
3679 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
3680 self.refresh_inlay_hints(
3681 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
3682 cx,
3683 );
3684 }
3685
3686 pub fn inlay_hints_enabled(&self) -> bool {
3687 self.inlay_hint_cache.enabled
3688 }
3689
3690 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
3691 if self.project.is_none() || self.mode != EditorMode::Full {
3692 return;
3693 }
3694
3695 let reason_description = reason.description();
3696 let ignore_debounce = matches!(
3697 reason,
3698 InlayHintRefreshReason::SettingsChange(_)
3699 | InlayHintRefreshReason::Toggle(_)
3700 | InlayHintRefreshReason::ExcerptsRemoved(_)
3701 );
3702 let (invalidate_cache, required_languages) = match reason {
3703 InlayHintRefreshReason::Toggle(enabled) => {
3704 self.inlay_hint_cache.enabled = enabled;
3705 if enabled {
3706 (InvalidationStrategy::RefreshRequested, None)
3707 } else {
3708 self.inlay_hint_cache.clear();
3709 self.splice_inlays(
3710 self.visible_inlay_hints(cx)
3711 .iter()
3712 .map(|inlay| inlay.id)
3713 .collect(),
3714 Vec::new(),
3715 cx,
3716 );
3717 return;
3718 }
3719 }
3720 InlayHintRefreshReason::SettingsChange(new_settings) => {
3721 match self.inlay_hint_cache.update_settings(
3722 &self.buffer,
3723 new_settings,
3724 self.visible_inlay_hints(cx),
3725 cx,
3726 ) {
3727 ControlFlow::Break(Some(InlaySplice {
3728 to_remove,
3729 to_insert,
3730 })) => {
3731 self.splice_inlays(to_remove, to_insert, cx);
3732 return;
3733 }
3734 ControlFlow::Break(None) => return,
3735 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3736 }
3737 }
3738 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3739 if let Some(InlaySplice {
3740 to_remove,
3741 to_insert,
3742 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3743 {
3744 self.splice_inlays(to_remove, to_insert, cx);
3745 }
3746 return;
3747 }
3748 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3749 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3750 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3751 }
3752 InlayHintRefreshReason::RefreshRequested => {
3753 (InvalidationStrategy::RefreshRequested, None)
3754 }
3755 };
3756
3757 if let Some(InlaySplice {
3758 to_remove,
3759 to_insert,
3760 }) = self.inlay_hint_cache.spawn_hint_refresh(
3761 reason_description,
3762 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3763 invalidate_cache,
3764 ignore_debounce,
3765 cx,
3766 ) {
3767 self.splice_inlays(to_remove, to_insert, cx);
3768 }
3769 }
3770
3771 fn visible_inlay_hints(&self, cx: &ViewContext<'_, Editor>) -> Vec<Inlay> {
3772 self.display_map
3773 .read(cx)
3774 .current_inlays()
3775 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3776 .cloned()
3777 .collect()
3778 }
3779
3780 pub fn excerpts_for_inlay_hints_query(
3781 &self,
3782 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3783 cx: &mut ViewContext<Editor>,
3784 ) -> HashMap<ExcerptId, (Model<Buffer>, clock::Global, Range<usize>)> {
3785 let Some(project) = self.project.as_ref() else {
3786 return HashMap::default();
3787 };
3788 let project = project.read(cx);
3789 let multi_buffer = self.buffer().read(cx);
3790 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3791 let multi_buffer_visible_start = self
3792 .scroll_manager
3793 .anchor()
3794 .anchor
3795 .to_point(&multi_buffer_snapshot);
3796 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3797 multi_buffer_visible_start
3798 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3799 Bias::Left,
3800 );
3801 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3802 multi_buffer
3803 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
3804 .into_iter()
3805 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3806 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
3807 let buffer = buffer_handle.read(cx);
3808 let buffer_file = project::File::from_dyn(buffer.file())?;
3809 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3810 let worktree_entry = buffer_worktree
3811 .read(cx)
3812 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3813 if worktree_entry.is_ignored {
3814 return None;
3815 }
3816
3817 let language = buffer.language()?;
3818 if let Some(restrict_to_languages) = restrict_to_languages {
3819 if !restrict_to_languages.contains(language) {
3820 return None;
3821 }
3822 }
3823 Some((
3824 excerpt_id,
3825 (
3826 buffer_handle,
3827 buffer.version().clone(),
3828 excerpt_visible_range,
3829 ),
3830 ))
3831 })
3832 .collect()
3833 }
3834
3835 pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
3836 TextLayoutDetails {
3837 text_system: cx.text_system().clone(),
3838 editor_style: self.style.clone().unwrap(),
3839 rem_size: cx.rem_size(),
3840 scroll_anchor: self.scroll_manager.anchor(),
3841 visible_rows: self.visible_line_count(),
3842 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
3843 }
3844 }
3845
3846 fn splice_inlays(
3847 &self,
3848 to_remove: Vec<InlayId>,
3849 to_insert: Vec<Inlay>,
3850 cx: &mut ViewContext<Self>,
3851 ) {
3852 self.display_map.update(cx, |display_map, cx| {
3853 display_map.splice_inlays(to_remove, to_insert, cx);
3854 });
3855 cx.notify();
3856 }
3857
3858 fn trigger_on_type_formatting(
3859 &self,
3860 input: String,
3861 cx: &mut ViewContext<Self>,
3862 ) -> Option<Task<Result<()>>> {
3863 if input.len() != 1 {
3864 return None;
3865 }
3866
3867 let project = self.project.as_ref()?;
3868 let position = self.selections.newest_anchor().head();
3869 let (buffer, buffer_position) = self
3870 .buffer
3871 .read(cx)
3872 .text_anchor_for_position(position, cx)?;
3873
3874 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3875 // hence we do LSP request & edit on host side only — add formats to host's history.
3876 let push_to_lsp_host_history = true;
3877 // If this is not the host, append its history with new edits.
3878 let push_to_client_history = project.read(cx).is_remote();
3879
3880 let on_type_formatting = project.update(cx, |project, cx| {
3881 project.on_type_format(
3882 buffer.clone(),
3883 buffer_position,
3884 input,
3885 push_to_lsp_host_history,
3886 cx,
3887 )
3888 });
3889 Some(cx.spawn(|editor, mut cx| async move {
3890 if let Some(transaction) = on_type_formatting.await? {
3891 if push_to_client_history {
3892 buffer
3893 .update(&mut cx, |buffer, _| {
3894 buffer.push_transaction(transaction, Instant::now());
3895 })
3896 .ok();
3897 }
3898 editor.update(&mut cx, |editor, cx| {
3899 editor.refresh_document_highlights(cx);
3900 })?;
3901 }
3902 Ok(())
3903 }))
3904 }
3905
3906 pub fn show_completions(&mut self, options: &ShowCompletions, cx: &mut ViewContext<Self>) {
3907 if self.pending_rename.is_some() {
3908 return;
3909 }
3910
3911 let Some(provider) = self.completion_provider.as_ref() else {
3912 return;
3913 };
3914
3915 let position = self.selections.newest_anchor().head();
3916 let (buffer, buffer_position) =
3917 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
3918 output
3919 } else {
3920 return;
3921 };
3922
3923 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
3924 let is_followup_invoke = {
3925 let context_menu_state = self.context_menu.read();
3926 matches!(
3927 context_menu_state.deref(),
3928 Some(ContextMenu::Completions(_))
3929 )
3930 };
3931 let trigger_kind = match (options.trigger, is_followup_invoke) {
3932 (_, true) => CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS,
3933 (Some(_), _) => CompletionTriggerKind::TRIGGER_CHARACTER,
3934 _ => CompletionTriggerKind::INVOKED,
3935 };
3936 let completion_context = CompletionContext {
3937 trigger_character: options.trigger.and_then(|c| {
3938 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
3939 Some(String::from(c))
3940 } else {
3941 None
3942 }
3943 }),
3944 trigger_kind,
3945 };
3946 let completions = provider.completions(&buffer, buffer_position, completion_context, cx);
3947
3948 let id = post_inc(&mut self.next_completion_id);
3949 let task = cx.spawn(|this, mut cx| {
3950 async move {
3951 this.update(&mut cx, |this, _| {
3952 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
3953 })?;
3954 let completions = completions.await.log_err();
3955 let menu = if let Some(completions) = completions {
3956 let mut menu = CompletionsMenu {
3957 id,
3958 initial_position: position,
3959 match_candidates: completions
3960 .iter()
3961 .enumerate()
3962 .map(|(id, completion)| {
3963 StringMatchCandidate::new(
3964 id,
3965 completion.label.text[completion.label.filter_range.clone()]
3966 .into(),
3967 )
3968 })
3969 .collect(),
3970 buffer: buffer.clone(),
3971 completions: Arc::new(RwLock::new(completions.into())),
3972 matches: Vec::new().into(),
3973 selected_item: 0,
3974 scroll_handle: UniformListScrollHandle::new(),
3975 selected_completion_documentation_resolve_debounce: Arc::new(Mutex::new(
3976 DebouncedDelay::new(),
3977 )),
3978 };
3979 menu.filter(query.as_deref(), cx.background_executor().clone())
3980 .await;
3981
3982 if menu.matches.is_empty() {
3983 None
3984 } else {
3985 this.update(&mut cx, |editor, cx| {
3986 let completions = menu.completions.clone();
3987 let matches = menu.matches.clone();
3988
3989 let delay_ms = EditorSettings::get_global(cx)
3990 .completion_documentation_secondary_query_debounce;
3991 let delay = Duration::from_millis(delay_ms);
3992 editor
3993 .completion_documentation_pre_resolve_debounce
3994 .fire_new(delay, cx, |editor, cx| {
3995 CompletionsMenu::pre_resolve_completion_documentation(
3996 buffer,
3997 completions,
3998 matches,
3999 editor,
4000 cx,
4001 )
4002 });
4003 })
4004 .ok();
4005 Some(menu)
4006 }
4007 } else {
4008 None
4009 };
4010
4011 this.update(&mut cx, |this, cx| {
4012 let mut context_menu = this.context_menu.write();
4013 match context_menu.as_ref() {
4014 None => {}
4015
4016 Some(ContextMenu::Completions(prev_menu)) => {
4017 if prev_menu.id > id {
4018 return;
4019 }
4020 }
4021
4022 _ => return,
4023 }
4024
4025 if this.focus_handle.is_focused(cx) && menu.is_some() {
4026 let menu = menu.unwrap();
4027 *context_menu = Some(ContextMenu::Completions(menu));
4028 drop(context_menu);
4029 this.discard_inline_completion(false, cx);
4030 cx.notify();
4031 } else if this.completion_tasks.len() <= 1 {
4032 // If there are no more completion tasks and the last menu was
4033 // empty, we should hide it. If it was already hidden, we should
4034 // also show the copilot completion when available.
4035 drop(context_menu);
4036 if this.hide_context_menu(cx).is_none() {
4037 this.update_visible_inline_completion(cx);
4038 }
4039 }
4040 })?;
4041
4042 Ok::<_, anyhow::Error>(())
4043 }
4044 .log_err()
4045 });
4046
4047 self.completion_tasks.push((id, task));
4048 }
4049
4050 pub fn confirm_completion(
4051 &mut self,
4052 action: &ConfirmCompletion,
4053 cx: &mut ViewContext<Self>,
4054 ) -> Option<Task<Result<()>>> {
4055 use language::ToOffset as _;
4056
4057 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
4058 menu
4059 } else {
4060 return None;
4061 };
4062
4063 let mat = completions_menu
4064 .matches
4065 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
4066 let buffer_handle = completions_menu.buffer;
4067 let completions = completions_menu.completions.read();
4068 let completion = completions.get(mat.candidate_id)?;
4069 cx.stop_propagation();
4070
4071 let snippet;
4072 let text;
4073
4074 if completion.is_snippet() {
4075 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4076 text = snippet.as_ref().unwrap().text.clone();
4077 } else {
4078 snippet = None;
4079 text = completion.new_text.clone();
4080 };
4081 let selections = self.selections.all::<usize>(cx);
4082 let buffer = buffer_handle.read(cx);
4083 let old_range = completion.old_range.to_offset(buffer);
4084 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4085
4086 let newest_selection = self.selections.newest_anchor();
4087 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4088 return None;
4089 }
4090
4091 let lookbehind = newest_selection
4092 .start
4093 .text_anchor
4094 .to_offset(buffer)
4095 .saturating_sub(old_range.start);
4096 let lookahead = old_range
4097 .end
4098 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4099 let mut common_prefix_len = old_text
4100 .bytes()
4101 .zip(text.bytes())
4102 .take_while(|(a, b)| a == b)
4103 .count();
4104
4105 let snapshot = self.buffer.read(cx).snapshot(cx);
4106 let mut range_to_replace: Option<Range<isize>> = None;
4107 let mut ranges = Vec::new();
4108 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4109 for selection in &selections {
4110 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4111 let start = selection.start.saturating_sub(lookbehind);
4112 let end = selection.end + lookahead;
4113 if selection.id == newest_selection.id {
4114 range_to_replace = Some(
4115 ((start + common_prefix_len) as isize - selection.start as isize)
4116 ..(end as isize - selection.start as isize),
4117 );
4118 }
4119 ranges.push(start + common_prefix_len..end);
4120 } else {
4121 common_prefix_len = 0;
4122 ranges.clear();
4123 ranges.extend(selections.iter().map(|s| {
4124 if s.id == newest_selection.id {
4125 range_to_replace = Some(
4126 old_range.start.to_offset_utf16(&snapshot).0 as isize
4127 - selection.start as isize
4128 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4129 - selection.start as isize,
4130 );
4131 old_range.clone()
4132 } else {
4133 s.start..s.end
4134 }
4135 }));
4136 break;
4137 }
4138 if !self.linked_edit_ranges.is_empty() {
4139 let start_anchor = snapshot.anchor_before(selection.head());
4140 let end_anchor = snapshot.anchor_after(selection.tail());
4141 if let Some(ranges) = self
4142 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4143 {
4144 for (buffer, edits) in ranges {
4145 linked_edits.entry(buffer.clone()).or_default().extend(
4146 edits
4147 .into_iter()
4148 .map(|range| (range, text[common_prefix_len..].to_owned())),
4149 );
4150 }
4151 }
4152 }
4153 }
4154 let text = &text[common_prefix_len..];
4155
4156 cx.emit(EditorEvent::InputHandled {
4157 utf16_range_to_replace: range_to_replace,
4158 text: text.into(),
4159 });
4160
4161 self.transact(cx, |this, cx| {
4162 if let Some(mut snippet) = snippet {
4163 snippet.text = text.to_string();
4164 for tabstop in snippet.tabstops.iter_mut().flatten() {
4165 tabstop.start -= common_prefix_len as isize;
4166 tabstop.end -= common_prefix_len as isize;
4167 }
4168
4169 this.insert_snippet(&ranges, snippet, cx).log_err();
4170 } else {
4171 this.buffer.update(cx, |buffer, cx| {
4172 buffer.edit(
4173 ranges.iter().map(|range| (range.clone(), text)),
4174 this.autoindent_mode.clone(),
4175 cx,
4176 );
4177 });
4178 }
4179 for (buffer, edits) in linked_edits {
4180 buffer.update(cx, |buffer, cx| {
4181 let snapshot = buffer.snapshot();
4182 let edits = edits
4183 .into_iter()
4184 .map(|(range, text)| {
4185 use text::ToPoint as TP;
4186 let end_point = TP::to_point(&range.end, &snapshot);
4187 let start_point = TP::to_point(&range.start, &snapshot);
4188 (start_point..end_point, text)
4189 })
4190 .sorted_by_key(|(range, _)| range.start)
4191 .collect::<Vec<_>>();
4192 buffer.edit(edits, None, cx);
4193 })
4194 }
4195
4196 this.refresh_inline_completion(true, cx);
4197 });
4198
4199 if let Some(confirm) = completion.confirm.as_ref() {
4200 (confirm)(cx);
4201 }
4202
4203 if completion.show_new_completions_on_confirm {
4204 self.show_completions(&ShowCompletions { trigger: None }, cx);
4205 }
4206
4207 let provider = self.completion_provider.as_ref()?;
4208 let apply_edits = provider.apply_additional_edits_for_completion(
4209 buffer_handle,
4210 completion.clone(),
4211 true,
4212 cx,
4213 );
4214 Some(cx.foreground_executor().spawn(async move {
4215 apply_edits.await?;
4216 Ok(())
4217 }))
4218 }
4219
4220 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
4221 let mut context_menu = self.context_menu.write();
4222 if let Some(ContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4223 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4224 // Toggle if we're selecting the same one
4225 *context_menu = None;
4226 cx.notify();
4227 return;
4228 } else {
4229 // Otherwise, clear it and start a new one
4230 *context_menu = None;
4231 cx.notify();
4232 }
4233 }
4234 drop(context_menu);
4235 let snapshot = self.snapshot(cx);
4236 let deployed_from_indicator = action.deployed_from_indicator;
4237 let mut task = self.code_actions_task.take();
4238 let action = action.clone();
4239 cx.spawn(|editor, mut cx| async move {
4240 while let Some(prev_task) = task {
4241 prev_task.await;
4242 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4243 }
4244
4245 let spawned_test_task = editor.update(&mut cx, |editor, cx| {
4246 if editor.focus_handle.is_focused(cx) {
4247 let multibuffer_point = action
4248 .deployed_from_indicator
4249 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4250 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4251 let (buffer, buffer_row) = snapshot
4252 .buffer_snapshot
4253 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4254 .and_then(|(buffer_snapshot, range)| {
4255 editor
4256 .buffer
4257 .read(cx)
4258 .buffer(buffer_snapshot.remote_id())
4259 .map(|buffer| (buffer, range.start.row))
4260 })?;
4261 let (_, code_actions) = editor
4262 .available_code_actions
4263 .clone()
4264 .and_then(|(location, code_actions)| {
4265 let snapshot = location.buffer.read(cx).snapshot();
4266 let point_range = location.range.to_point(&snapshot);
4267 let point_range = point_range.start.row..=point_range.end.row;
4268 if point_range.contains(&buffer_row) {
4269 Some((location, code_actions))
4270 } else {
4271 None
4272 }
4273 })
4274 .unzip();
4275 let buffer_id = buffer.read(cx).remote_id();
4276 let tasks = editor
4277 .tasks
4278 .get(&(buffer_id, buffer_row))
4279 .map(|t| Arc::new(t.to_owned()));
4280 if tasks.is_none() && code_actions.is_none() {
4281 return None;
4282 }
4283
4284 editor.completion_tasks.clear();
4285 editor.discard_inline_completion(false, cx);
4286 let task_context =
4287 tasks
4288 .as_ref()
4289 .zip(editor.project.clone())
4290 .map(|(tasks, project)| {
4291 let position = Point::new(buffer_row, tasks.column);
4292 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
4293 let location = Location {
4294 buffer: buffer.clone(),
4295 range: range_start..range_start,
4296 };
4297 // Fill in the environmental variables from the tree-sitter captures
4298 let mut captured_task_variables = TaskVariables::default();
4299 for (capture_name, value) in tasks.extra_variables.clone() {
4300 captured_task_variables.insert(
4301 task::VariableName::Custom(capture_name.into()),
4302 value.clone(),
4303 );
4304 }
4305 project.update(cx, |project, cx| {
4306 project.task_context_for_location(
4307 captured_task_variables,
4308 location,
4309 cx,
4310 )
4311 })
4312 });
4313
4314 Some(cx.spawn(|editor, mut cx| async move {
4315 let task_context = match task_context {
4316 Some(task_context) => task_context.await,
4317 None => None,
4318 };
4319 let resolved_tasks =
4320 tasks.zip(task_context).map(|(tasks, task_context)| {
4321 Arc::new(ResolvedTasks {
4322 templates: tasks
4323 .templates
4324 .iter()
4325 .filter_map(|(kind, template)| {
4326 template
4327 .resolve_task(&kind.to_id_base(), &task_context)
4328 .map(|task| (kind.clone(), task))
4329 })
4330 .collect(),
4331 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4332 multibuffer_point.row,
4333 tasks.column,
4334 )),
4335 })
4336 });
4337 let spawn_straight_away = resolved_tasks
4338 .as_ref()
4339 .map_or(false, |tasks| tasks.templates.len() == 1)
4340 && code_actions
4341 .as_ref()
4342 .map_or(true, |actions| actions.is_empty());
4343 if let Some(task) = editor
4344 .update(&mut cx, |editor, cx| {
4345 *editor.context_menu.write() =
4346 Some(ContextMenu::CodeActions(CodeActionsMenu {
4347 buffer,
4348 actions: CodeActionContents {
4349 tasks: resolved_tasks,
4350 actions: code_actions,
4351 },
4352 selected_item: Default::default(),
4353 scroll_handle: UniformListScrollHandle::default(),
4354 deployed_from_indicator,
4355 }));
4356 if spawn_straight_away {
4357 if let Some(task) = editor.confirm_code_action(
4358 &ConfirmCodeAction { item_ix: Some(0) },
4359 cx,
4360 ) {
4361 cx.notify();
4362 return task;
4363 }
4364 }
4365 cx.notify();
4366 Task::ready(Ok(()))
4367 })
4368 .ok()
4369 {
4370 task.await
4371 } else {
4372 Ok(())
4373 }
4374 }))
4375 } else {
4376 Some(Task::ready(Ok(())))
4377 }
4378 })?;
4379 if let Some(task) = spawned_test_task {
4380 task.await?;
4381 }
4382
4383 Ok::<_, anyhow::Error>(())
4384 })
4385 .detach_and_log_err(cx);
4386 }
4387
4388 pub fn confirm_code_action(
4389 &mut self,
4390 action: &ConfirmCodeAction,
4391 cx: &mut ViewContext<Self>,
4392 ) -> Option<Task<Result<()>>> {
4393 let actions_menu = if let ContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
4394 menu
4395 } else {
4396 return None;
4397 };
4398 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4399 let action = actions_menu.actions.get(action_ix)?;
4400 let title = action.label();
4401 let buffer = actions_menu.buffer;
4402 let workspace = self.workspace()?;
4403
4404 match action {
4405 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4406 workspace.update(cx, |workspace, cx| {
4407 workspace::tasks::schedule_resolved_task(
4408 workspace,
4409 task_source_kind,
4410 resolved_task,
4411 false,
4412 cx,
4413 );
4414
4415 Some(Task::ready(Ok(())))
4416 })
4417 }
4418 CodeActionsItem::CodeAction(action) => {
4419 let apply_code_actions = workspace
4420 .read(cx)
4421 .project()
4422 .clone()
4423 .update(cx, |project, cx| {
4424 project.apply_code_action(buffer, action, true, cx)
4425 });
4426 let workspace = workspace.downgrade();
4427 Some(cx.spawn(|editor, cx| async move {
4428 let project_transaction = apply_code_actions.await?;
4429 Self::open_project_transaction(
4430 &editor,
4431 workspace,
4432 project_transaction,
4433 title,
4434 cx,
4435 )
4436 .await
4437 }))
4438 }
4439 }
4440 }
4441
4442 pub async fn open_project_transaction(
4443 this: &WeakView<Editor>,
4444 workspace: WeakView<Workspace>,
4445 transaction: ProjectTransaction,
4446 title: String,
4447 mut cx: AsyncWindowContext,
4448 ) -> Result<()> {
4449 let replica_id = this.update(&mut cx, |this, cx| this.replica_id(cx))?;
4450
4451 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4452 cx.update(|cx| {
4453 entries.sort_unstable_by_key(|(buffer, _)| {
4454 buffer.read(cx).file().map(|f| f.path().clone())
4455 });
4456 })?;
4457
4458 // If the project transaction's edits are all contained within this editor, then
4459 // avoid opening a new editor to display them.
4460
4461 if let Some((buffer, transaction)) = entries.first() {
4462 if entries.len() == 1 {
4463 let excerpt = this.update(&mut cx, |editor, cx| {
4464 editor
4465 .buffer()
4466 .read(cx)
4467 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4468 })?;
4469 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4470 if excerpted_buffer == *buffer {
4471 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4472 let excerpt_range = excerpt_range.to_offset(buffer);
4473 buffer
4474 .edited_ranges_for_transaction::<usize>(transaction)
4475 .all(|range| {
4476 excerpt_range.start <= range.start
4477 && excerpt_range.end >= range.end
4478 })
4479 })?;
4480
4481 if all_edits_within_excerpt {
4482 return Ok(());
4483 }
4484 }
4485 }
4486 }
4487 } else {
4488 return Ok(());
4489 }
4490
4491 let mut ranges_to_highlight = Vec::new();
4492 let excerpt_buffer = cx.new_model(|cx| {
4493 let mut multibuffer =
4494 MultiBuffer::new(replica_id, Capability::ReadWrite).with_title(title);
4495 for (buffer_handle, transaction) in &entries {
4496 let buffer = buffer_handle.read(cx);
4497 ranges_to_highlight.extend(
4498 multibuffer.push_excerpts_with_context_lines(
4499 buffer_handle.clone(),
4500 buffer
4501 .edited_ranges_for_transaction::<usize>(transaction)
4502 .collect(),
4503 DEFAULT_MULTIBUFFER_CONTEXT,
4504 cx,
4505 ),
4506 );
4507 }
4508 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4509 multibuffer
4510 })?;
4511
4512 workspace.update(&mut cx, |workspace, cx| {
4513 let project = workspace.project().clone();
4514 let editor =
4515 cx.new_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, cx));
4516 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, cx);
4517 editor.update(cx, |editor, cx| {
4518 editor.highlight_background::<Self>(
4519 &ranges_to_highlight,
4520 |theme| theme.editor_highlighted_line_background,
4521 cx,
4522 );
4523 });
4524 })?;
4525
4526 Ok(())
4527 }
4528
4529 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4530 let project = self.project.clone()?;
4531 let buffer = self.buffer.read(cx);
4532 let newest_selection = self.selections.newest_anchor().clone();
4533 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4534 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4535 if start_buffer != end_buffer {
4536 return None;
4537 }
4538
4539 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
4540 cx.background_executor()
4541 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4542 .await;
4543
4544 let actions = if let Ok(code_actions) = project.update(&mut cx, |project, cx| {
4545 project.code_actions(&start_buffer, start..end, cx)
4546 }) {
4547 code_actions.await
4548 } else {
4549 Vec::new()
4550 };
4551
4552 this.update(&mut cx, |this, cx| {
4553 this.available_code_actions = if actions.is_empty() {
4554 None
4555 } else {
4556 Some((
4557 Location {
4558 buffer: start_buffer,
4559 range: start..end,
4560 },
4561 actions.into(),
4562 ))
4563 };
4564 cx.notify();
4565 })
4566 .log_err();
4567 }));
4568 None
4569 }
4570
4571 fn start_inline_blame_timer(&mut self, cx: &mut ViewContext<Self>) {
4572 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4573 self.show_git_blame_inline = false;
4574
4575 self.show_git_blame_inline_delay_task = Some(cx.spawn(|this, mut cx| async move {
4576 cx.background_executor().timer(delay).await;
4577
4578 this.update(&mut cx, |this, cx| {
4579 this.show_git_blame_inline = true;
4580 cx.notify();
4581 })
4582 .log_err();
4583 }));
4584 }
4585 }
4586
4587 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4588 if self.pending_rename.is_some() {
4589 return None;
4590 }
4591
4592 let project = self.project.clone()?;
4593 let buffer = self.buffer.read(cx);
4594 let newest_selection = self.selections.newest_anchor().clone();
4595 let cursor_position = newest_selection.head();
4596 let (cursor_buffer, cursor_buffer_position) =
4597 buffer.text_anchor_for_position(cursor_position, cx)?;
4598 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4599 if cursor_buffer != tail_buffer {
4600 return None;
4601 }
4602
4603 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4604 cx.background_executor()
4605 .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT)
4606 .await;
4607
4608 let highlights = if let Some(highlights) = project
4609 .update(&mut cx, |project, cx| {
4610 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4611 })
4612 .log_err()
4613 {
4614 highlights.await.log_err()
4615 } else {
4616 None
4617 };
4618
4619 if let Some(highlights) = highlights {
4620 this.update(&mut cx, |this, cx| {
4621 if this.pending_rename.is_some() {
4622 return;
4623 }
4624
4625 let buffer_id = cursor_position.buffer_id;
4626 let buffer = this.buffer.read(cx);
4627 if !buffer
4628 .text_anchor_for_position(cursor_position, cx)
4629 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4630 {
4631 return;
4632 }
4633
4634 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4635 let mut write_ranges = Vec::new();
4636 let mut read_ranges = Vec::new();
4637 for highlight in highlights {
4638 for (excerpt_id, excerpt_range) in
4639 buffer.excerpts_for_buffer(&cursor_buffer, cx)
4640 {
4641 let start = highlight
4642 .range
4643 .start
4644 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4645 let end = highlight
4646 .range
4647 .end
4648 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4649 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4650 continue;
4651 }
4652
4653 let range = Anchor {
4654 buffer_id,
4655 excerpt_id: excerpt_id,
4656 text_anchor: start,
4657 }..Anchor {
4658 buffer_id,
4659 excerpt_id,
4660 text_anchor: end,
4661 };
4662 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4663 write_ranges.push(range);
4664 } else {
4665 read_ranges.push(range);
4666 }
4667 }
4668 }
4669
4670 this.highlight_background::<DocumentHighlightRead>(
4671 &read_ranges,
4672 |theme| theme.editor_document_highlight_read_background,
4673 cx,
4674 );
4675 this.highlight_background::<DocumentHighlightWrite>(
4676 &write_ranges,
4677 |theme| theme.editor_document_highlight_write_background,
4678 cx,
4679 );
4680 cx.notify();
4681 })
4682 .log_err();
4683 }
4684 }));
4685 None
4686 }
4687
4688 fn refresh_inline_completion(
4689 &mut self,
4690 debounce: bool,
4691 cx: &mut ViewContext<Self>,
4692 ) -> Option<()> {
4693 let provider = self.inline_completion_provider()?;
4694 let cursor = self.selections.newest_anchor().head();
4695 let (buffer, cursor_buffer_position) =
4696 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4697 if !self.show_inline_completions
4698 || !provider.is_enabled(&buffer, cursor_buffer_position, cx)
4699 {
4700 self.discard_inline_completion(false, cx);
4701 return None;
4702 }
4703
4704 self.update_visible_inline_completion(cx);
4705 provider.refresh(buffer, cursor_buffer_position, debounce, cx);
4706 Some(())
4707 }
4708
4709 fn cycle_inline_completion(
4710 &mut self,
4711 direction: Direction,
4712 cx: &mut ViewContext<Self>,
4713 ) -> Option<()> {
4714 let provider = self.inline_completion_provider()?;
4715 let cursor = self.selections.newest_anchor().head();
4716 let (buffer, cursor_buffer_position) =
4717 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4718 if !self.show_inline_completions
4719 || !provider.is_enabled(&buffer, cursor_buffer_position, cx)
4720 {
4721 return None;
4722 }
4723
4724 provider.cycle(buffer, cursor_buffer_position, direction, cx);
4725 self.update_visible_inline_completion(cx);
4726
4727 Some(())
4728 }
4729
4730 pub fn show_inline_completion(&mut self, _: &ShowInlineCompletion, cx: &mut ViewContext<Self>) {
4731 if !self.has_active_inline_completion(cx) {
4732 self.refresh_inline_completion(false, cx);
4733 return;
4734 }
4735
4736 self.update_visible_inline_completion(cx);
4737 }
4738
4739 pub fn display_cursor_names(&mut self, _: &DisplayCursorNames, cx: &mut ViewContext<Self>) {
4740 self.show_cursor_names(cx);
4741 }
4742
4743 fn show_cursor_names(&mut self, cx: &mut ViewContext<Self>) {
4744 self.show_cursor_names = true;
4745 cx.notify();
4746 cx.spawn(|this, mut cx| async move {
4747 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
4748 this.update(&mut cx, |this, cx| {
4749 this.show_cursor_names = false;
4750 cx.notify()
4751 })
4752 .ok()
4753 })
4754 .detach();
4755 }
4756
4757 pub fn next_inline_completion(&mut self, _: &NextInlineCompletion, cx: &mut ViewContext<Self>) {
4758 if self.has_active_inline_completion(cx) {
4759 self.cycle_inline_completion(Direction::Next, cx);
4760 } else {
4761 let is_copilot_disabled = self.refresh_inline_completion(false, cx).is_none();
4762 if is_copilot_disabled {
4763 cx.propagate();
4764 }
4765 }
4766 }
4767
4768 pub fn previous_inline_completion(
4769 &mut self,
4770 _: &PreviousInlineCompletion,
4771 cx: &mut ViewContext<Self>,
4772 ) {
4773 if self.has_active_inline_completion(cx) {
4774 self.cycle_inline_completion(Direction::Prev, cx);
4775 } else {
4776 let is_copilot_disabled = self.refresh_inline_completion(false, cx).is_none();
4777 if is_copilot_disabled {
4778 cx.propagate();
4779 }
4780 }
4781 }
4782
4783 pub fn accept_inline_completion(
4784 &mut self,
4785 _: &AcceptInlineCompletion,
4786 cx: &mut ViewContext<Self>,
4787 ) {
4788 let Some(completion) = self.take_active_inline_completion(cx) else {
4789 return;
4790 };
4791 if let Some(provider) = self.inline_completion_provider() {
4792 provider.accept(cx);
4793 }
4794
4795 cx.emit(EditorEvent::InputHandled {
4796 utf16_range_to_replace: None,
4797 text: completion.text.to_string().into(),
4798 });
4799 self.insert_with_autoindent_mode(&completion.text.to_string(), None, cx);
4800 self.refresh_inline_completion(true, cx);
4801 cx.notify();
4802 }
4803
4804 pub fn accept_partial_inline_completion(
4805 &mut self,
4806 _: &AcceptPartialInlineCompletion,
4807 cx: &mut ViewContext<Self>,
4808 ) {
4809 if self.selections.count() == 1 && self.has_active_inline_completion(cx) {
4810 if let Some(completion) = self.take_active_inline_completion(cx) {
4811 let mut partial_completion = completion
4812 .text
4813 .chars()
4814 .by_ref()
4815 .take_while(|c| c.is_alphabetic())
4816 .collect::<String>();
4817 if partial_completion.is_empty() {
4818 partial_completion = completion
4819 .text
4820 .chars()
4821 .by_ref()
4822 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
4823 .collect::<String>();
4824 }
4825
4826 cx.emit(EditorEvent::InputHandled {
4827 utf16_range_to_replace: None,
4828 text: partial_completion.clone().into(),
4829 });
4830 self.insert_with_autoindent_mode(&partial_completion, None, cx);
4831 self.refresh_inline_completion(true, cx);
4832 cx.notify();
4833 }
4834 }
4835 }
4836
4837 fn discard_inline_completion(
4838 &mut self,
4839 should_report_inline_completion_event: bool,
4840 cx: &mut ViewContext<Self>,
4841 ) -> bool {
4842 if let Some(provider) = self.inline_completion_provider() {
4843 provider.discard(should_report_inline_completion_event, cx);
4844 }
4845
4846 self.take_active_inline_completion(cx).is_some()
4847 }
4848
4849 pub fn has_active_inline_completion(&self, cx: &AppContext) -> bool {
4850 if let Some(completion) = self.active_inline_completion.as_ref() {
4851 let buffer = self.buffer.read(cx).read(cx);
4852 completion.position.is_valid(&buffer)
4853 } else {
4854 false
4855 }
4856 }
4857
4858 fn take_active_inline_completion(&mut self, cx: &mut ViewContext<Self>) -> Option<Inlay> {
4859 let completion = self.active_inline_completion.take()?;
4860 self.display_map.update(cx, |map, cx| {
4861 map.splice_inlays(vec![completion.id], Default::default(), cx);
4862 });
4863 let buffer = self.buffer.read(cx).read(cx);
4864
4865 if completion.position.is_valid(&buffer) {
4866 Some(completion)
4867 } else {
4868 None
4869 }
4870 }
4871
4872 fn update_visible_inline_completion(&mut self, cx: &mut ViewContext<Self>) {
4873 let selection = self.selections.newest_anchor();
4874 let cursor = selection.head();
4875
4876 if self.context_menu.read().is_none()
4877 && self.completion_tasks.is_empty()
4878 && selection.start == selection.end
4879 {
4880 if let Some(provider) = self.inline_completion_provider() {
4881 if let Some((buffer, cursor_buffer_position)) =
4882 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
4883 {
4884 if let Some(text) =
4885 provider.active_completion_text(&buffer, cursor_buffer_position, cx)
4886 {
4887 let text = Rope::from(text);
4888 let mut to_remove = Vec::new();
4889 if let Some(completion) = self.active_inline_completion.take() {
4890 to_remove.push(completion.id);
4891 }
4892
4893 let completion_inlay =
4894 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
4895 self.active_inline_completion = Some(completion_inlay.clone());
4896 self.display_map.update(cx, move |map, cx| {
4897 map.splice_inlays(to_remove, vec![completion_inlay], cx)
4898 });
4899 cx.notify();
4900 return;
4901 }
4902 }
4903 }
4904 }
4905
4906 self.discard_inline_completion(false, cx);
4907 }
4908
4909 fn inline_completion_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
4910 Some(self.inline_completion_provider.as_ref()?.provider.clone())
4911 }
4912
4913 fn render_code_actions_indicator(
4914 &self,
4915 _style: &EditorStyle,
4916 row: DisplayRow,
4917 is_active: bool,
4918 cx: &mut ViewContext<Self>,
4919 ) -> Option<IconButton> {
4920 if self.available_code_actions.is_some() {
4921 Some(
4922 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
4923 .shape(ui::IconButtonShape::Square)
4924 .icon_size(IconSize::XSmall)
4925 .icon_color(Color::Muted)
4926 .selected(is_active)
4927 .on_click(cx.listener(move |editor, _e, cx| {
4928 editor.focus(cx);
4929 editor.toggle_code_actions(
4930 &ToggleCodeActions {
4931 deployed_from_indicator: Some(row),
4932 },
4933 cx,
4934 );
4935 })),
4936 )
4937 } else {
4938 None
4939 }
4940 }
4941
4942 fn clear_tasks(&mut self) {
4943 self.tasks.clear()
4944 }
4945
4946 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
4947 if let Some(_) = self.tasks.insert(key, value) {
4948 // This case should hopefully be rare, but just in case...
4949 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
4950 }
4951 }
4952
4953 fn render_run_indicator(
4954 &self,
4955 _style: &EditorStyle,
4956 is_active: bool,
4957 row: DisplayRow,
4958 cx: &mut ViewContext<Self>,
4959 ) -> IconButton {
4960 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
4961 .shape(ui::IconButtonShape::Square)
4962 .icon_size(IconSize::XSmall)
4963 .icon_color(Color::Muted)
4964 .selected(is_active)
4965 .on_click(cx.listener(move |editor, _e, cx| {
4966 editor.focus(cx);
4967 editor.toggle_code_actions(
4968 &ToggleCodeActions {
4969 deployed_from_indicator: Some(row),
4970 },
4971 cx,
4972 );
4973 }))
4974 }
4975
4976 pub fn context_menu_visible(&self) -> bool {
4977 self.context_menu
4978 .read()
4979 .as_ref()
4980 .map_or(false, |menu| menu.visible())
4981 }
4982
4983 fn render_context_menu(
4984 &self,
4985 cursor_position: DisplayPoint,
4986 style: &EditorStyle,
4987 max_height: Pixels,
4988 cx: &mut ViewContext<Editor>,
4989 ) -> Option<(ContextMenuOrigin, AnyElement)> {
4990 self.context_menu.read().as_ref().map(|menu| {
4991 menu.render(
4992 cursor_position,
4993 style,
4994 max_height,
4995 self.workspace.as_ref().map(|(w, _)| w.clone()),
4996 cx,
4997 )
4998 })
4999 }
5000
5001 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
5002 cx.notify();
5003 self.completion_tasks.clear();
5004 let context_menu = self.context_menu.write().take();
5005 if context_menu.is_some() {
5006 self.update_visible_inline_completion(cx);
5007 }
5008 context_menu
5009 }
5010
5011 pub fn insert_snippet(
5012 &mut self,
5013 insertion_ranges: &[Range<usize>],
5014 snippet: Snippet,
5015 cx: &mut ViewContext<Self>,
5016 ) -> Result<()> {
5017 struct Tabstop<T> {
5018 is_end_tabstop: bool,
5019 ranges: Vec<Range<T>>,
5020 }
5021
5022 let tabstops = self.buffer.update(cx, |buffer, cx| {
5023 let snippet_text: Arc<str> = snippet.text.clone().into();
5024 buffer.edit(
5025 insertion_ranges
5026 .iter()
5027 .cloned()
5028 .map(|range| (range, snippet_text.clone())),
5029 Some(AutoindentMode::EachLine),
5030 cx,
5031 );
5032
5033 let snapshot = &*buffer.read(cx);
5034 let snippet = &snippet;
5035 snippet
5036 .tabstops
5037 .iter()
5038 .map(|tabstop| {
5039 let is_end_tabstop = tabstop.first().map_or(false, |tabstop| {
5040 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
5041 });
5042 let mut tabstop_ranges = tabstop
5043 .iter()
5044 .flat_map(|tabstop_range| {
5045 let mut delta = 0_isize;
5046 insertion_ranges.iter().map(move |insertion_range| {
5047 let insertion_start = insertion_range.start as isize + delta;
5048 delta +=
5049 snippet.text.len() as isize - insertion_range.len() as isize;
5050
5051 let start = ((insertion_start + tabstop_range.start) as usize)
5052 .min(snapshot.len());
5053 let end = ((insertion_start + tabstop_range.end) as usize)
5054 .min(snapshot.len());
5055 snapshot.anchor_before(start)..snapshot.anchor_after(end)
5056 })
5057 })
5058 .collect::<Vec<_>>();
5059 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
5060
5061 Tabstop {
5062 is_end_tabstop,
5063 ranges: tabstop_ranges,
5064 }
5065 })
5066 .collect::<Vec<_>>()
5067 });
5068
5069 if let Some(tabstop) = tabstops.first() {
5070 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5071 s.select_ranges(tabstop.ranges.iter().cloned());
5072 });
5073
5074 // If we're already at the last tabstop and it's at the end of the snippet,
5075 // we're done, we don't need to keep the state around.
5076 if !tabstop.is_end_tabstop {
5077 let ranges = tabstops
5078 .into_iter()
5079 .map(|tabstop| tabstop.ranges)
5080 .collect::<Vec<_>>();
5081 self.snippet_stack.push(SnippetState {
5082 active_index: 0,
5083 ranges,
5084 });
5085 }
5086
5087 // Check whether the just-entered snippet ends with an auto-closable bracket.
5088 if self.autoclose_regions.is_empty() {
5089 let snapshot = self.buffer.read(cx).snapshot(cx);
5090 for selection in &mut self.selections.all::<Point>(cx) {
5091 let selection_head = selection.head();
5092 let Some(scope) = snapshot.language_scope_at(selection_head) else {
5093 continue;
5094 };
5095
5096 let mut bracket_pair = None;
5097 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
5098 let prev_chars = snapshot
5099 .reversed_chars_at(selection_head)
5100 .collect::<String>();
5101 for (pair, enabled) in scope.brackets() {
5102 if enabled
5103 && pair.close
5104 && prev_chars.starts_with(pair.start.as_str())
5105 && next_chars.starts_with(pair.end.as_str())
5106 {
5107 bracket_pair = Some(pair.clone());
5108 break;
5109 }
5110 }
5111 if let Some(pair) = bracket_pair {
5112 let start = snapshot.anchor_after(selection_head);
5113 let end = snapshot.anchor_after(selection_head);
5114 self.autoclose_regions.push(AutocloseRegion {
5115 selection_id: selection.id,
5116 range: start..end,
5117 pair,
5118 });
5119 }
5120 }
5121 }
5122 }
5123 Ok(())
5124 }
5125
5126 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5127 self.move_to_snippet_tabstop(Bias::Right, cx)
5128 }
5129
5130 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5131 self.move_to_snippet_tabstop(Bias::Left, cx)
5132 }
5133
5134 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
5135 if let Some(mut snippet) = self.snippet_stack.pop() {
5136 match bias {
5137 Bias::Left => {
5138 if snippet.active_index > 0 {
5139 snippet.active_index -= 1;
5140 } else {
5141 self.snippet_stack.push(snippet);
5142 return false;
5143 }
5144 }
5145 Bias::Right => {
5146 if snippet.active_index + 1 < snippet.ranges.len() {
5147 snippet.active_index += 1;
5148 } else {
5149 self.snippet_stack.push(snippet);
5150 return false;
5151 }
5152 }
5153 }
5154 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
5155 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5156 s.select_anchor_ranges(current_ranges.iter().cloned())
5157 });
5158 // If snippet state is not at the last tabstop, push it back on the stack
5159 if snippet.active_index + 1 < snippet.ranges.len() {
5160 self.snippet_stack.push(snippet);
5161 }
5162 return true;
5163 }
5164 }
5165
5166 false
5167 }
5168
5169 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
5170 self.transact(cx, |this, cx| {
5171 this.select_all(&SelectAll, cx);
5172 this.insert("", cx);
5173 });
5174 }
5175
5176 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
5177 self.transact(cx, |this, cx| {
5178 this.select_autoclose_pair(cx);
5179 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
5180 if !this.linked_edit_ranges.is_empty() {
5181 let selections = this.selections.all::<MultiBufferPoint>(cx);
5182 let snapshot = this.buffer.read(cx).snapshot(cx);
5183
5184 for selection in selections.iter() {
5185 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
5186 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
5187 if selection_start.buffer_id != selection_end.buffer_id {
5188 continue;
5189 }
5190 if let Some(ranges) =
5191 this.linked_editing_ranges_for(selection_start..selection_end, cx)
5192 {
5193 for (buffer, entries) in ranges {
5194 linked_ranges.entry(buffer).or_default().extend(entries);
5195 }
5196 }
5197 }
5198 }
5199
5200 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
5201 if !this.selections.line_mode {
5202 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
5203 for selection in &mut selections {
5204 if selection.is_empty() {
5205 let old_head = selection.head();
5206 let mut new_head =
5207 movement::left(&display_map, old_head.to_display_point(&display_map))
5208 .to_point(&display_map);
5209 if let Some((buffer, line_buffer_range)) = display_map
5210 .buffer_snapshot
5211 .buffer_line_for_row(MultiBufferRow(old_head.row))
5212 {
5213 let indent_size =
5214 buffer.indent_size_for_line(line_buffer_range.start.row);
5215 let indent_len = match indent_size.kind {
5216 IndentKind::Space => {
5217 buffer.settings_at(line_buffer_range.start, cx).tab_size
5218 }
5219 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
5220 };
5221 if old_head.column <= indent_size.len && old_head.column > 0 {
5222 let indent_len = indent_len.get();
5223 new_head = cmp::min(
5224 new_head,
5225 MultiBufferPoint::new(
5226 old_head.row,
5227 ((old_head.column - 1) / indent_len) * indent_len,
5228 ),
5229 );
5230 }
5231 }
5232
5233 selection.set_head(new_head, SelectionGoal::None);
5234 }
5235 }
5236 }
5237
5238 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5239 this.insert("", cx);
5240 let empty_str: Arc<str> = Arc::from("");
5241 for (buffer, edits) in linked_ranges {
5242 let snapshot = buffer.read(cx).snapshot();
5243 use text::ToPoint as TP;
5244
5245 let edits = edits
5246 .into_iter()
5247 .map(|range| {
5248 let end_point = TP::to_point(&range.end, &snapshot);
5249 let mut start_point = TP::to_point(&range.start, &snapshot);
5250
5251 if end_point == start_point {
5252 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
5253 .saturating_sub(1);
5254 start_point = TP::to_point(&offset, &snapshot);
5255 };
5256
5257 (start_point..end_point, empty_str.clone())
5258 })
5259 .sorted_by_key(|(range, _)| range.start)
5260 .collect::<Vec<_>>();
5261 buffer.update(cx, |this, cx| {
5262 this.edit(edits, None, cx);
5263 })
5264 }
5265 this.refresh_inline_completion(true, cx);
5266 linked_editing_ranges::refresh_linked_ranges(this, cx);
5267 });
5268 }
5269
5270 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
5271 self.transact(cx, |this, cx| {
5272 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5273 let line_mode = s.line_mode;
5274 s.move_with(|map, selection| {
5275 if selection.is_empty() && !line_mode {
5276 let cursor = movement::right(map, selection.head());
5277 selection.end = cursor;
5278 selection.reversed = true;
5279 selection.goal = SelectionGoal::None;
5280 }
5281 })
5282 });
5283 this.insert("", cx);
5284 this.refresh_inline_completion(true, cx);
5285 });
5286 }
5287
5288 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
5289 if self.move_to_prev_snippet_tabstop(cx) {
5290 return;
5291 }
5292
5293 self.outdent(&Outdent, cx);
5294 }
5295
5296 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
5297 if self.move_to_next_snippet_tabstop(cx) || self.read_only(cx) {
5298 return;
5299 }
5300
5301 let mut selections = self.selections.all_adjusted(cx);
5302 let buffer = self.buffer.read(cx);
5303 let snapshot = buffer.snapshot(cx);
5304 let rows_iter = selections.iter().map(|s| s.head().row);
5305 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
5306
5307 let mut edits = Vec::new();
5308 let mut prev_edited_row = 0;
5309 let mut row_delta = 0;
5310 for selection in &mut selections {
5311 if selection.start.row != prev_edited_row {
5312 row_delta = 0;
5313 }
5314 prev_edited_row = selection.end.row;
5315
5316 // If the selection is non-empty, then increase the indentation of the selected lines.
5317 if !selection.is_empty() {
5318 row_delta =
5319 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5320 continue;
5321 }
5322
5323 // If the selection is empty and the cursor is in the leading whitespace before the
5324 // suggested indentation, then auto-indent the line.
5325 let cursor = selection.head();
5326 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
5327 if let Some(suggested_indent) =
5328 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
5329 {
5330 if cursor.column < suggested_indent.len
5331 && cursor.column <= current_indent.len
5332 && current_indent.len <= suggested_indent.len
5333 {
5334 selection.start = Point::new(cursor.row, suggested_indent.len);
5335 selection.end = selection.start;
5336 if row_delta == 0 {
5337 edits.extend(Buffer::edit_for_indent_size_adjustment(
5338 cursor.row,
5339 current_indent,
5340 suggested_indent,
5341 ));
5342 row_delta = suggested_indent.len - current_indent.len;
5343 }
5344 continue;
5345 }
5346 }
5347
5348 // Otherwise, insert a hard or soft tab.
5349 let settings = buffer.settings_at(cursor, cx);
5350 let tab_size = if settings.hard_tabs {
5351 IndentSize::tab()
5352 } else {
5353 let tab_size = settings.tab_size.get();
5354 let char_column = snapshot
5355 .text_for_range(Point::new(cursor.row, 0)..cursor)
5356 .flat_map(str::chars)
5357 .count()
5358 + row_delta as usize;
5359 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
5360 IndentSize::spaces(chars_to_next_tab_stop)
5361 };
5362 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
5363 selection.end = selection.start;
5364 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
5365 row_delta += tab_size.len;
5366 }
5367
5368 self.transact(cx, |this, cx| {
5369 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5370 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5371 this.refresh_inline_completion(true, cx);
5372 });
5373 }
5374
5375 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
5376 if self.read_only(cx) {
5377 return;
5378 }
5379 let mut selections = self.selections.all::<Point>(cx);
5380 let mut prev_edited_row = 0;
5381 let mut row_delta = 0;
5382 let mut edits = Vec::new();
5383 let buffer = self.buffer.read(cx);
5384 let snapshot = buffer.snapshot(cx);
5385 for selection in &mut selections {
5386 if selection.start.row != prev_edited_row {
5387 row_delta = 0;
5388 }
5389 prev_edited_row = selection.end.row;
5390
5391 row_delta =
5392 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5393 }
5394
5395 self.transact(cx, |this, cx| {
5396 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5397 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5398 });
5399 }
5400
5401 fn indent_selection(
5402 buffer: &MultiBuffer,
5403 snapshot: &MultiBufferSnapshot,
5404 selection: &mut Selection<Point>,
5405 edits: &mut Vec<(Range<Point>, String)>,
5406 delta_for_start_row: u32,
5407 cx: &AppContext,
5408 ) -> u32 {
5409 let settings = buffer.settings_at(selection.start, cx);
5410 let tab_size = settings.tab_size.get();
5411 let indent_kind = if settings.hard_tabs {
5412 IndentKind::Tab
5413 } else {
5414 IndentKind::Space
5415 };
5416 let mut start_row = selection.start.row;
5417 let mut end_row = selection.end.row + 1;
5418
5419 // If a selection ends at the beginning of a line, don't indent
5420 // that last line.
5421 if selection.end.column == 0 && selection.end.row > selection.start.row {
5422 end_row -= 1;
5423 }
5424
5425 // Avoid re-indenting a row that has already been indented by a
5426 // previous selection, but still update this selection's column
5427 // to reflect that indentation.
5428 if delta_for_start_row > 0 {
5429 start_row += 1;
5430 selection.start.column += delta_for_start_row;
5431 if selection.end.row == selection.start.row {
5432 selection.end.column += delta_for_start_row;
5433 }
5434 }
5435
5436 let mut delta_for_end_row = 0;
5437 let has_multiple_rows = start_row + 1 != end_row;
5438 for row in start_row..end_row {
5439 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
5440 let indent_delta = match (current_indent.kind, indent_kind) {
5441 (IndentKind::Space, IndentKind::Space) => {
5442 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
5443 IndentSize::spaces(columns_to_next_tab_stop)
5444 }
5445 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
5446 (_, IndentKind::Tab) => IndentSize::tab(),
5447 };
5448
5449 let start = if has_multiple_rows || current_indent.len < selection.start.column {
5450 0
5451 } else {
5452 selection.start.column
5453 };
5454 let row_start = Point::new(row, start);
5455 edits.push((
5456 row_start..row_start,
5457 indent_delta.chars().collect::<String>(),
5458 ));
5459
5460 // Update this selection's endpoints to reflect the indentation.
5461 if row == selection.start.row {
5462 selection.start.column += indent_delta.len;
5463 }
5464 if row == selection.end.row {
5465 selection.end.column += indent_delta.len;
5466 delta_for_end_row = indent_delta.len;
5467 }
5468 }
5469
5470 if selection.start.row == selection.end.row {
5471 delta_for_start_row + delta_for_end_row
5472 } else {
5473 delta_for_end_row
5474 }
5475 }
5476
5477 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
5478 if self.read_only(cx) {
5479 return;
5480 }
5481 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5482 let selections = self.selections.all::<Point>(cx);
5483 let mut deletion_ranges = Vec::new();
5484 let mut last_outdent = None;
5485 {
5486 let buffer = self.buffer.read(cx);
5487 let snapshot = buffer.snapshot(cx);
5488 for selection in &selections {
5489 let settings = buffer.settings_at(selection.start, cx);
5490 let tab_size = settings.tab_size.get();
5491 let mut rows = selection.spanned_rows(false, &display_map);
5492
5493 // Avoid re-outdenting a row that has already been outdented by a
5494 // previous selection.
5495 if let Some(last_row) = last_outdent {
5496 if last_row == rows.start {
5497 rows.start = rows.start.next_row();
5498 }
5499 }
5500 let has_multiple_rows = rows.len() > 1;
5501 for row in rows.iter_rows() {
5502 let indent_size = snapshot.indent_size_for_line(row);
5503 if indent_size.len > 0 {
5504 let deletion_len = match indent_size.kind {
5505 IndentKind::Space => {
5506 let columns_to_prev_tab_stop = indent_size.len % tab_size;
5507 if columns_to_prev_tab_stop == 0 {
5508 tab_size
5509 } else {
5510 columns_to_prev_tab_stop
5511 }
5512 }
5513 IndentKind::Tab => 1,
5514 };
5515 let start = if has_multiple_rows
5516 || deletion_len > selection.start.column
5517 || indent_size.len < selection.start.column
5518 {
5519 0
5520 } else {
5521 selection.start.column - deletion_len
5522 };
5523 deletion_ranges.push(
5524 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
5525 );
5526 last_outdent = Some(row);
5527 }
5528 }
5529 }
5530 }
5531
5532 self.transact(cx, |this, cx| {
5533 this.buffer.update(cx, |buffer, cx| {
5534 let empty_str: Arc<str> = "".into();
5535 buffer.edit(
5536 deletion_ranges
5537 .into_iter()
5538 .map(|range| (range, empty_str.clone())),
5539 None,
5540 cx,
5541 );
5542 });
5543 let selections = this.selections.all::<usize>(cx);
5544 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5545 });
5546 }
5547
5548 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
5549 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5550 let selections = self.selections.all::<Point>(cx);
5551
5552 let mut new_cursors = Vec::new();
5553 let mut edit_ranges = Vec::new();
5554 let mut selections = selections.iter().peekable();
5555 while let Some(selection) = selections.next() {
5556 let mut rows = selection.spanned_rows(false, &display_map);
5557 let goal_display_column = selection.head().to_display_point(&display_map).column();
5558
5559 // Accumulate contiguous regions of rows that we want to delete.
5560 while let Some(next_selection) = selections.peek() {
5561 let next_rows = next_selection.spanned_rows(false, &display_map);
5562 if next_rows.start <= rows.end {
5563 rows.end = next_rows.end;
5564 selections.next().unwrap();
5565 } else {
5566 break;
5567 }
5568 }
5569
5570 let buffer = &display_map.buffer_snapshot;
5571 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
5572 let edit_end;
5573 let cursor_buffer_row;
5574 if buffer.max_point().row >= rows.end.0 {
5575 // If there's a line after the range, delete the \n from the end of the row range
5576 // and position the cursor on the next line.
5577 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
5578 cursor_buffer_row = rows.end;
5579 } else {
5580 // If there isn't a line after the range, delete the \n from the line before the
5581 // start of the row range and position the cursor there.
5582 edit_start = edit_start.saturating_sub(1);
5583 edit_end = buffer.len();
5584 cursor_buffer_row = rows.start.previous_row();
5585 }
5586
5587 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
5588 *cursor.column_mut() =
5589 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
5590
5591 new_cursors.push((
5592 selection.id,
5593 buffer.anchor_after(cursor.to_point(&display_map)),
5594 ));
5595 edit_ranges.push(edit_start..edit_end);
5596 }
5597
5598 self.transact(cx, |this, cx| {
5599 let buffer = this.buffer.update(cx, |buffer, cx| {
5600 let empty_str: Arc<str> = "".into();
5601 buffer.edit(
5602 edit_ranges
5603 .into_iter()
5604 .map(|range| (range, empty_str.clone())),
5605 None,
5606 cx,
5607 );
5608 buffer.snapshot(cx)
5609 });
5610 let new_selections = new_cursors
5611 .into_iter()
5612 .map(|(id, cursor)| {
5613 let cursor = cursor.to_point(&buffer);
5614 Selection {
5615 id,
5616 start: cursor,
5617 end: cursor,
5618 reversed: false,
5619 goal: SelectionGoal::None,
5620 }
5621 })
5622 .collect();
5623
5624 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5625 s.select(new_selections);
5626 });
5627 });
5628 }
5629
5630 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
5631 if self.read_only(cx) {
5632 return;
5633 }
5634 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
5635 for selection in self.selections.all::<Point>(cx) {
5636 let start = MultiBufferRow(selection.start.row);
5637 let end = if selection.start.row == selection.end.row {
5638 MultiBufferRow(selection.start.row + 1)
5639 } else {
5640 MultiBufferRow(selection.end.row)
5641 };
5642
5643 if let Some(last_row_range) = row_ranges.last_mut() {
5644 if start <= last_row_range.end {
5645 last_row_range.end = end;
5646 continue;
5647 }
5648 }
5649 row_ranges.push(start..end);
5650 }
5651
5652 let snapshot = self.buffer.read(cx).snapshot(cx);
5653 let mut cursor_positions = Vec::new();
5654 for row_range in &row_ranges {
5655 let anchor = snapshot.anchor_before(Point::new(
5656 row_range.end.previous_row().0,
5657 snapshot.line_len(row_range.end.previous_row()),
5658 ));
5659 cursor_positions.push(anchor..anchor);
5660 }
5661
5662 self.transact(cx, |this, cx| {
5663 for row_range in row_ranges.into_iter().rev() {
5664 for row in row_range.iter_rows().rev() {
5665 let end_of_line = Point::new(row.0, snapshot.line_len(row));
5666 let next_line_row = row.next_row();
5667 let indent = snapshot.indent_size_for_line(next_line_row);
5668 let start_of_next_line = Point::new(next_line_row.0, indent.len);
5669
5670 let replace = if snapshot.line_len(next_line_row) > indent.len {
5671 " "
5672 } else {
5673 ""
5674 };
5675
5676 this.buffer.update(cx, |buffer, cx| {
5677 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
5678 });
5679 }
5680 }
5681
5682 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5683 s.select_anchor_ranges(cursor_positions)
5684 });
5685 });
5686 }
5687
5688 pub fn sort_lines_case_sensitive(
5689 &mut self,
5690 _: &SortLinesCaseSensitive,
5691 cx: &mut ViewContext<Self>,
5692 ) {
5693 self.manipulate_lines(cx, |lines| lines.sort())
5694 }
5695
5696 pub fn sort_lines_case_insensitive(
5697 &mut self,
5698 _: &SortLinesCaseInsensitive,
5699 cx: &mut ViewContext<Self>,
5700 ) {
5701 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
5702 }
5703
5704 pub fn unique_lines_case_insensitive(
5705 &mut self,
5706 _: &UniqueLinesCaseInsensitive,
5707 cx: &mut ViewContext<Self>,
5708 ) {
5709 self.manipulate_lines(cx, |lines| {
5710 let mut seen = HashSet::default();
5711 lines.retain(|line| seen.insert(line.to_lowercase()));
5712 })
5713 }
5714
5715 pub fn unique_lines_case_sensitive(
5716 &mut self,
5717 _: &UniqueLinesCaseSensitive,
5718 cx: &mut ViewContext<Self>,
5719 ) {
5720 self.manipulate_lines(cx, |lines| {
5721 let mut seen = HashSet::default();
5722 lines.retain(|line| seen.insert(*line));
5723 })
5724 }
5725
5726 pub fn revert_selected_hunks(&mut self, _: &RevertSelectedHunks, cx: &mut ViewContext<Self>) {
5727 let revert_changes = self.gather_revert_changes(&self.selections.disjoint_anchors(), cx);
5728 if !revert_changes.is_empty() {
5729 self.transact(cx, |editor, cx| {
5730 editor.buffer().update(cx, |multi_buffer, cx| {
5731 for (buffer_id, changes) in revert_changes {
5732 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
5733 buffer.update(cx, |buffer, cx| {
5734 buffer.edit(
5735 changes.into_iter().map(|(range, text)| {
5736 (range, text.to_string().map(Arc::<str>::from))
5737 }),
5738 None,
5739 cx,
5740 );
5741 });
5742 }
5743 }
5744 });
5745 editor.change_selections(None, cx, |selections| selections.refresh());
5746 });
5747 }
5748 }
5749
5750 pub fn open_active_item_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
5751 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
5752 let project_path = buffer.read(cx).project_path(cx)?;
5753 let project = self.project.as_ref()?.read(cx);
5754 let entry = project.entry_for_path(&project_path, cx)?;
5755 let abs_path = project.absolute_path(&project_path, cx)?;
5756 let parent = if entry.is_symlink {
5757 abs_path.canonicalize().ok()?
5758 } else {
5759 abs_path
5760 }
5761 .parent()?
5762 .to_path_buf();
5763 Some(parent)
5764 }) {
5765 cx.dispatch_action(OpenTerminal { working_directory }.boxed_clone());
5766 }
5767 }
5768
5769 fn gather_revert_changes(
5770 &mut self,
5771 selections: &[Selection<Anchor>],
5772 cx: &mut ViewContext<'_, Editor>,
5773 ) -> HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>> {
5774 let mut revert_changes = HashMap::default();
5775 self.buffer.update(cx, |multi_buffer, cx| {
5776 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
5777 for hunk in hunks_for_selections(&multi_buffer_snapshot, selections) {
5778 Self::prepare_revert_change(&mut revert_changes, &multi_buffer, &hunk, cx);
5779 }
5780 });
5781 revert_changes
5782 }
5783
5784 fn prepare_revert_change(
5785 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
5786 multi_buffer: &MultiBuffer,
5787 hunk: &DiffHunk<MultiBufferRow>,
5788 cx: &mut AppContext,
5789 ) -> Option<()> {
5790 let buffer = multi_buffer.buffer(hunk.buffer_id)?;
5791 let buffer = buffer.read(cx);
5792 let original_text = buffer.diff_base()?.slice(hunk.diff_base_byte_range.clone());
5793 let buffer_snapshot = buffer.snapshot();
5794 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
5795 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
5796 probe
5797 .0
5798 .start
5799 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
5800 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
5801 }) {
5802 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
5803 Some(())
5804 } else {
5805 None
5806 }
5807 }
5808
5809 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
5810 self.manipulate_lines(cx, |lines| lines.reverse())
5811 }
5812
5813 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
5814 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
5815 }
5816
5817 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
5818 where
5819 Fn: FnMut(&mut Vec<&str>),
5820 {
5821 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5822 let buffer = self.buffer.read(cx).snapshot(cx);
5823
5824 let mut edits = Vec::new();
5825
5826 let selections = self.selections.all::<Point>(cx);
5827 let mut selections = selections.iter().peekable();
5828 let mut contiguous_row_selections = Vec::new();
5829 let mut new_selections = Vec::new();
5830 let mut added_lines = 0;
5831 let mut removed_lines = 0;
5832
5833 while let Some(selection) = selections.next() {
5834 let (start_row, end_row) = consume_contiguous_rows(
5835 &mut contiguous_row_selections,
5836 selection,
5837 &display_map,
5838 &mut selections,
5839 );
5840
5841 let start_point = Point::new(start_row.0, 0);
5842 let end_point = Point::new(
5843 end_row.previous_row().0,
5844 buffer.line_len(end_row.previous_row()),
5845 );
5846 let text = buffer
5847 .text_for_range(start_point..end_point)
5848 .collect::<String>();
5849
5850 let mut lines = text.split('\n').collect_vec();
5851
5852 let lines_before = lines.len();
5853 callback(&mut lines);
5854 let lines_after = lines.len();
5855
5856 edits.push((start_point..end_point, lines.join("\n")));
5857
5858 // Selections must change based on added and removed line count
5859 let start_row =
5860 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
5861 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
5862 new_selections.push(Selection {
5863 id: selection.id,
5864 start: start_row,
5865 end: end_row,
5866 goal: SelectionGoal::None,
5867 reversed: selection.reversed,
5868 });
5869
5870 if lines_after > lines_before {
5871 added_lines += lines_after - lines_before;
5872 } else if lines_before > lines_after {
5873 removed_lines += lines_before - lines_after;
5874 }
5875 }
5876
5877 self.transact(cx, |this, cx| {
5878 let buffer = this.buffer.update(cx, |buffer, cx| {
5879 buffer.edit(edits, None, cx);
5880 buffer.snapshot(cx)
5881 });
5882
5883 // Recalculate offsets on newly edited buffer
5884 let new_selections = new_selections
5885 .iter()
5886 .map(|s| {
5887 let start_point = Point::new(s.start.0, 0);
5888 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
5889 Selection {
5890 id: s.id,
5891 start: buffer.point_to_offset(start_point),
5892 end: buffer.point_to_offset(end_point),
5893 goal: s.goal,
5894 reversed: s.reversed,
5895 }
5896 })
5897 .collect();
5898
5899 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5900 s.select(new_selections);
5901 });
5902
5903 this.request_autoscroll(Autoscroll::fit(), cx);
5904 });
5905 }
5906
5907 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
5908 self.manipulate_text(cx, |text| text.to_uppercase())
5909 }
5910
5911 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
5912 self.manipulate_text(cx, |text| text.to_lowercase())
5913 }
5914
5915 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
5916 self.manipulate_text(cx, |text| {
5917 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
5918 // https://github.com/rutrum/convert-case/issues/16
5919 text.split('\n')
5920 .map(|line| line.to_case(Case::Title))
5921 .join("\n")
5922 })
5923 }
5924
5925 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
5926 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
5927 }
5928
5929 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
5930 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
5931 }
5932
5933 pub fn convert_to_upper_camel_case(
5934 &mut self,
5935 _: &ConvertToUpperCamelCase,
5936 cx: &mut ViewContext<Self>,
5937 ) {
5938 self.manipulate_text(cx, |text| {
5939 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
5940 // https://github.com/rutrum/convert-case/issues/16
5941 text.split('\n')
5942 .map(|line| line.to_case(Case::UpperCamel))
5943 .join("\n")
5944 })
5945 }
5946
5947 pub fn convert_to_lower_camel_case(
5948 &mut self,
5949 _: &ConvertToLowerCamelCase,
5950 cx: &mut ViewContext<Self>,
5951 ) {
5952 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
5953 }
5954
5955 pub fn convert_to_opposite_case(
5956 &mut self,
5957 _: &ConvertToOppositeCase,
5958 cx: &mut ViewContext<Self>,
5959 ) {
5960 self.manipulate_text(cx, |text| {
5961 text.chars()
5962 .fold(String::with_capacity(text.len()), |mut t, c| {
5963 if c.is_uppercase() {
5964 t.extend(c.to_lowercase());
5965 } else {
5966 t.extend(c.to_uppercase());
5967 }
5968 t
5969 })
5970 })
5971 }
5972
5973 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
5974 where
5975 Fn: FnMut(&str) -> String,
5976 {
5977 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5978 let buffer = self.buffer.read(cx).snapshot(cx);
5979
5980 let mut new_selections = Vec::new();
5981 let mut edits = Vec::new();
5982 let mut selection_adjustment = 0i32;
5983
5984 for selection in self.selections.all::<usize>(cx) {
5985 let selection_is_empty = selection.is_empty();
5986
5987 let (start, end) = if selection_is_empty {
5988 let word_range = movement::surrounding_word(
5989 &display_map,
5990 selection.start.to_display_point(&display_map),
5991 );
5992 let start = word_range.start.to_offset(&display_map, Bias::Left);
5993 let end = word_range.end.to_offset(&display_map, Bias::Left);
5994 (start, end)
5995 } else {
5996 (selection.start, selection.end)
5997 };
5998
5999 let text = buffer.text_for_range(start..end).collect::<String>();
6000 let old_length = text.len() as i32;
6001 let text = callback(&text);
6002
6003 new_selections.push(Selection {
6004 start: (start as i32 - selection_adjustment) as usize,
6005 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
6006 goal: SelectionGoal::None,
6007 ..selection
6008 });
6009
6010 selection_adjustment += old_length - text.len() as i32;
6011
6012 edits.push((start..end, text));
6013 }
6014
6015 self.transact(cx, |this, cx| {
6016 this.buffer.update(cx, |buffer, cx| {
6017 buffer.edit(edits, None, cx);
6018 });
6019
6020 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6021 s.select(new_selections);
6022 });
6023
6024 this.request_autoscroll(Autoscroll::fit(), cx);
6025 });
6026 }
6027
6028 pub fn duplicate_line(&mut self, upwards: bool, cx: &mut ViewContext<Self>) {
6029 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6030 let buffer = &display_map.buffer_snapshot;
6031 let selections = self.selections.all::<Point>(cx);
6032
6033 let mut edits = Vec::new();
6034 let mut selections_iter = selections.iter().peekable();
6035 while let Some(selection) = selections_iter.next() {
6036 // Avoid duplicating the same lines twice.
6037 let mut rows = selection.spanned_rows(false, &display_map);
6038
6039 while let Some(next_selection) = selections_iter.peek() {
6040 let next_rows = next_selection.spanned_rows(false, &display_map);
6041 if next_rows.start < rows.end {
6042 rows.end = next_rows.end;
6043 selections_iter.next().unwrap();
6044 } else {
6045 break;
6046 }
6047 }
6048
6049 // Copy the text from the selected row region and splice it either at the start
6050 // or end of the region.
6051 let start = Point::new(rows.start.0, 0);
6052 let end = Point::new(
6053 rows.end.previous_row().0,
6054 buffer.line_len(rows.end.previous_row()),
6055 );
6056 let text = buffer
6057 .text_for_range(start..end)
6058 .chain(Some("\n"))
6059 .collect::<String>();
6060 let insert_location = if upwards {
6061 Point::new(rows.end.0, 0)
6062 } else {
6063 start
6064 };
6065 edits.push((insert_location..insert_location, text));
6066 }
6067
6068 self.transact(cx, |this, cx| {
6069 this.buffer.update(cx, |buffer, cx| {
6070 buffer.edit(edits, None, cx);
6071 });
6072
6073 this.request_autoscroll(Autoscroll::fit(), cx);
6074 });
6075 }
6076
6077 pub fn duplicate_line_up(&mut self, _: &DuplicateLineUp, cx: &mut ViewContext<Self>) {
6078 self.duplicate_line(true, cx);
6079 }
6080
6081 pub fn duplicate_line_down(&mut self, _: &DuplicateLineDown, cx: &mut ViewContext<Self>) {
6082 self.duplicate_line(false, cx);
6083 }
6084
6085 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
6086 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6087 let buffer = self.buffer.read(cx).snapshot(cx);
6088
6089 let mut edits = Vec::new();
6090 let mut unfold_ranges = Vec::new();
6091 let mut refold_ranges = Vec::new();
6092
6093 let selections = self.selections.all::<Point>(cx);
6094 let mut selections = selections.iter().peekable();
6095 let mut contiguous_row_selections = Vec::new();
6096 let mut new_selections = Vec::new();
6097
6098 while let Some(selection) = selections.next() {
6099 // Find all the selections that span a contiguous row range
6100 let (start_row, end_row) = consume_contiguous_rows(
6101 &mut contiguous_row_selections,
6102 selection,
6103 &display_map,
6104 &mut selections,
6105 );
6106
6107 // Move the text spanned by the row range to be before the line preceding the row range
6108 if start_row.0 > 0 {
6109 let range_to_move = Point::new(
6110 start_row.previous_row().0,
6111 buffer.line_len(start_row.previous_row()),
6112 )
6113 ..Point::new(
6114 end_row.previous_row().0,
6115 buffer.line_len(end_row.previous_row()),
6116 );
6117 let insertion_point = display_map
6118 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
6119 .0;
6120
6121 // Don't move lines across excerpts
6122 if buffer
6123 .excerpt_boundaries_in_range((
6124 Bound::Excluded(insertion_point),
6125 Bound::Included(range_to_move.end),
6126 ))
6127 .next()
6128 .is_none()
6129 {
6130 let text = buffer
6131 .text_for_range(range_to_move.clone())
6132 .flat_map(|s| s.chars())
6133 .skip(1)
6134 .chain(['\n'])
6135 .collect::<String>();
6136
6137 edits.push((
6138 buffer.anchor_after(range_to_move.start)
6139 ..buffer.anchor_before(range_to_move.end),
6140 String::new(),
6141 ));
6142 let insertion_anchor = buffer.anchor_after(insertion_point);
6143 edits.push((insertion_anchor..insertion_anchor, text));
6144
6145 let row_delta = range_to_move.start.row - insertion_point.row + 1;
6146
6147 // Move selections up
6148 new_selections.extend(contiguous_row_selections.drain(..).map(
6149 |mut selection| {
6150 selection.start.row -= row_delta;
6151 selection.end.row -= row_delta;
6152 selection
6153 },
6154 ));
6155
6156 // Move folds up
6157 unfold_ranges.push(range_to_move.clone());
6158 for fold in display_map.folds_in_range(
6159 buffer.anchor_before(range_to_move.start)
6160 ..buffer.anchor_after(range_to_move.end),
6161 ) {
6162 let mut start = fold.range.start.to_point(&buffer);
6163 let mut end = fold.range.end.to_point(&buffer);
6164 start.row -= row_delta;
6165 end.row -= row_delta;
6166 refold_ranges.push((start..end, fold.placeholder.clone()));
6167 }
6168 }
6169 }
6170
6171 // If we didn't move line(s), preserve the existing selections
6172 new_selections.append(&mut contiguous_row_selections);
6173 }
6174
6175 self.transact(cx, |this, cx| {
6176 this.unfold_ranges(unfold_ranges, true, true, cx);
6177 this.buffer.update(cx, |buffer, cx| {
6178 for (range, text) in edits {
6179 buffer.edit([(range, text)], None, cx);
6180 }
6181 });
6182 this.fold_ranges(refold_ranges, true, cx);
6183 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6184 s.select(new_selections);
6185 })
6186 });
6187 }
6188
6189 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
6190 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6191 let buffer = self.buffer.read(cx).snapshot(cx);
6192
6193 let mut edits = Vec::new();
6194 let mut unfold_ranges = Vec::new();
6195 let mut refold_ranges = Vec::new();
6196
6197 let selections = self.selections.all::<Point>(cx);
6198 let mut selections = selections.iter().peekable();
6199 let mut contiguous_row_selections = Vec::new();
6200 let mut new_selections = Vec::new();
6201
6202 while let Some(selection) = selections.next() {
6203 // Find all the selections that span a contiguous row range
6204 let (start_row, end_row) = consume_contiguous_rows(
6205 &mut contiguous_row_selections,
6206 selection,
6207 &display_map,
6208 &mut selections,
6209 );
6210
6211 // Move the text spanned by the row range to be after the last line of the row range
6212 if end_row.0 <= buffer.max_point().row {
6213 let range_to_move =
6214 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
6215 let insertion_point = display_map
6216 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
6217 .0;
6218
6219 // Don't move lines across excerpt boundaries
6220 if buffer
6221 .excerpt_boundaries_in_range((
6222 Bound::Excluded(range_to_move.start),
6223 Bound::Included(insertion_point),
6224 ))
6225 .next()
6226 .is_none()
6227 {
6228 let mut text = String::from("\n");
6229 text.extend(buffer.text_for_range(range_to_move.clone()));
6230 text.pop(); // Drop trailing newline
6231 edits.push((
6232 buffer.anchor_after(range_to_move.start)
6233 ..buffer.anchor_before(range_to_move.end),
6234 String::new(),
6235 ));
6236 let insertion_anchor = buffer.anchor_after(insertion_point);
6237 edits.push((insertion_anchor..insertion_anchor, text));
6238
6239 let row_delta = insertion_point.row - range_to_move.end.row + 1;
6240
6241 // Move selections down
6242 new_selections.extend(contiguous_row_selections.drain(..).map(
6243 |mut selection| {
6244 selection.start.row += row_delta;
6245 selection.end.row += row_delta;
6246 selection
6247 },
6248 ));
6249
6250 // Move folds down
6251 unfold_ranges.push(range_to_move.clone());
6252 for fold in display_map.folds_in_range(
6253 buffer.anchor_before(range_to_move.start)
6254 ..buffer.anchor_after(range_to_move.end),
6255 ) {
6256 let mut start = fold.range.start.to_point(&buffer);
6257 let mut end = fold.range.end.to_point(&buffer);
6258 start.row += row_delta;
6259 end.row += row_delta;
6260 refold_ranges.push((start..end, fold.placeholder.clone()));
6261 }
6262 }
6263 }
6264
6265 // If we didn't move line(s), preserve the existing selections
6266 new_selections.append(&mut contiguous_row_selections);
6267 }
6268
6269 self.transact(cx, |this, cx| {
6270 this.unfold_ranges(unfold_ranges, true, true, cx);
6271 this.buffer.update(cx, |buffer, cx| {
6272 for (range, text) in edits {
6273 buffer.edit([(range, text)], None, cx);
6274 }
6275 });
6276 this.fold_ranges(refold_ranges, true, cx);
6277 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
6278 });
6279 }
6280
6281 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
6282 let text_layout_details = &self.text_layout_details(cx);
6283 self.transact(cx, |this, cx| {
6284 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6285 let mut edits: Vec<(Range<usize>, String)> = Default::default();
6286 let line_mode = s.line_mode;
6287 s.move_with(|display_map, selection| {
6288 if !selection.is_empty() || line_mode {
6289 return;
6290 }
6291
6292 let mut head = selection.head();
6293 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
6294 if head.column() == display_map.line_len(head.row()) {
6295 transpose_offset = display_map
6296 .buffer_snapshot
6297 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6298 }
6299
6300 if transpose_offset == 0 {
6301 return;
6302 }
6303
6304 *head.column_mut() += 1;
6305 head = display_map.clip_point(head, Bias::Right);
6306 let goal = SelectionGoal::HorizontalPosition(
6307 display_map
6308 .x_for_display_point(head, &text_layout_details)
6309 .into(),
6310 );
6311 selection.collapse_to(head, goal);
6312
6313 let transpose_start = display_map
6314 .buffer_snapshot
6315 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6316 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
6317 let transpose_end = display_map
6318 .buffer_snapshot
6319 .clip_offset(transpose_offset + 1, Bias::Right);
6320 if let Some(ch) =
6321 display_map.buffer_snapshot.chars_at(transpose_start).next()
6322 {
6323 edits.push((transpose_start..transpose_offset, String::new()));
6324 edits.push((transpose_end..transpose_end, ch.to_string()));
6325 }
6326 }
6327 });
6328 edits
6329 });
6330 this.buffer
6331 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
6332 let selections = this.selections.all::<usize>(cx);
6333 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6334 s.select(selections);
6335 });
6336 });
6337 }
6338
6339 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
6340 let mut text = String::new();
6341 let buffer = self.buffer.read(cx).snapshot(cx);
6342 let mut selections = self.selections.all::<Point>(cx);
6343 let mut clipboard_selections = Vec::with_capacity(selections.len());
6344 {
6345 let max_point = buffer.max_point();
6346 let mut is_first = true;
6347 for selection in &mut selections {
6348 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6349 if is_entire_line {
6350 selection.start = Point::new(selection.start.row, 0);
6351 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
6352 selection.goal = SelectionGoal::None;
6353 }
6354 if is_first {
6355 is_first = false;
6356 } else {
6357 text += "\n";
6358 }
6359 let mut len = 0;
6360 for chunk in buffer.text_for_range(selection.start..selection.end) {
6361 text.push_str(chunk);
6362 len += chunk.len();
6363 }
6364 clipboard_selections.push(ClipboardSelection {
6365 len,
6366 is_entire_line,
6367 first_line_indent: buffer
6368 .indent_size_for_line(MultiBufferRow(selection.start.row))
6369 .len,
6370 });
6371 }
6372 }
6373
6374 self.transact(cx, |this, cx| {
6375 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6376 s.select(selections);
6377 });
6378 this.insert("", cx);
6379 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
6380 });
6381 }
6382
6383 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
6384 let selections = self.selections.all::<Point>(cx);
6385 let buffer = self.buffer.read(cx).read(cx);
6386 let mut text = String::new();
6387
6388 let mut clipboard_selections = Vec::with_capacity(selections.len());
6389 {
6390 let max_point = buffer.max_point();
6391 let mut is_first = true;
6392 for selection in selections.iter() {
6393 let mut start = selection.start;
6394 let mut end = selection.end;
6395 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6396 if is_entire_line {
6397 start = Point::new(start.row, 0);
6398 end = cmp::min(max_point, Point::new(end.row + 1, 0));
6399 }
6400 if is_first {
6401 is_first = false;
6402 } else {
6403 text += "\n";
6404 }
6405 let mut len = 0;
6406 for chunk in buffer.text_for_range(start..end) {
6407 text.push_str(chunk);
6408 len += chunk.len();
6409 }
6410 clipboard_selections.push(ClipboardSelection {
6411 len,
6412 is_entire_line,
6413 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
6414 });
6415 }
6416 }
6417
6418 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
6419 }
6420
6421 pub fn do_paste(
6422 &mut self,
6423 text: &String,
6424 clipboard_selections: Option<Vec<ClipboardSelection>>,
6425 handle_entire_lines: bool,
6426 cx: &mut ViewContext<Self>,
6427 ) {
6428 if self.read_only(cx) {
6429 return;
6430 }
6431
6432 let clipboard_text = Cow::Borrowed(text);
6433
6434 self.transact(cx, |this, cx| {
6435 if let Some(mut clipboard_selections) = clipboard_selections {
6436 let old_selections = this.selections.all::<usize>(cx);
6437 let all_selections_were_entire_line =
6438 clipboard_selections.iter().all(|s| s.is_entire_line);
6439 let first_selection_indent_column =
6440 clipboard_selections.first().map(|s| s.first_line_indent);
6441 if clipboard_selections.len() != old_selections.len() {
6442 clipboard_selections.drain(..);
6443 }
6444
6445 this.buffer.update(cx, |buffer, cx| {
6446 let snapshot = buffer.read(cx);
6447 let mut start_offset = 0;
6448 let mut edits = Vec::new();
6449 let mut original_indent_columns = Vec::new();
6450 for (ix, selection) in old_selections.iter().enumerate() {
6451 let to_insert;
6452 let entire_line;
6453 let original_indent_column;
6454 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
6455 let end_offset = start_offset + clipboard_selection.len;
6456 to_insert = &clipboard_text[start_offset..end_offset];
6457 entire_line = clipboard_selection.is_entire_line;
6458 start_offset = end_offset + 1;
6459 original_indent_column = Some(clipboard_selection.first_line_indent);
6460 } else {
6461 to_insert = clipboard_text.as_str();
6462 entire_line = all_selections_were_entire_line;
6463 original_indent_column = first_selection_indent_column
6464 }
6465
6466 // If the corresponding selection was empty when this slice of the
6467 // clipboard text was written, then the entire line containing the
6468 // selection was copied. If this selection is also currently empty,
6469 // then paste the line before the current line of the buffer.
6470 let range = if selection.is_empty() && handle_entire_lines && entire_line {
6471 let column = selection.start.to_point(&snapshot).column as usize;
6472 let line_start = selection.start - column;
6473 line_start..line_start
6474 } else {
6475 selection.range()
6476 };
6477
6478 edits.push((range, to_insert));
6479 original_indent_columns.extend(original_indent_column);
6480 }
6481 drop(snapshot);
6482
6483 buffer.edit(
6484 edits,
6485 Some(AutoindentMode::Block {
6486 original_indent_columns,
6487 }),
6488 cx,
6489 );
6490 });
6491
6492 let selections = this.selections.all::<usize>(cx);
6493 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6494 } else {
6495 this.insert(&clipboard_text, cx);
6496 }
6497 });
6498 }
6499
6500 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
6501 if let Some(item) = cx.read_from_clipboard() {
6502 self.do_paste(
6503 item.text(),
6504 item.metadata::<Vec<ClipboardSelection>>(),
6505 true,
6506 cx,
6507 )
6508 };
6509 }
6510
6511 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
6512 if self.read_only(cx) {
6513 return;
6514 }
6515
6516 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
6517 if let Some((selections, _)) =
6518 self.selection_history.transaction(transaction_id).cloned()
6519 {
6520 self.change_selections(None, cx, |s| {
6521 s.select_anchors(selections.to_vec());
6522 });
6523 }
6524 self.request_autoscroll(Autoscroll::fit(), cx);
6525 self.unmark_text(cx);
6526 self.refresh_inline_completion(true, cx);
6527 cx.emit(EditorEvent::Edited { transaction_id });
6528 cx.emit(EditorEvent::TransactionUndone { transaction_id });
6529 }
6530 }
6531
6532 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
6533 if self.read_only(cx) {
6534 return;
6535 }
6536
6537 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
6538 if let Some((_, Some(selections))) =
6539 self.selection_history.transaction(transaction_id).cloned()
6540 {
6541 self.change_selections(None, cx, |s| {
6542 s.select_anchors(selections.to_vec());
6543 });
6544 }
6545 self.request_autoscroll(Autoscroll::fit(), cx);
6546 self.unmark_text(cx);
6547 self.refresh_inline_completion(true, cx);
6548 cx.emit(EditorEvent::Edited { transaction_id });
6549 }
6550 }
6551
6552 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
6553 self.buffer
6554 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
6555 }
6556
6557 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut ViewContext<Self>) {
6558 self.buffer
6559 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
6560 }
6561
6562 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
6563 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6564 let line_mode = s.line_mode;
6565 s.move_with(|map, selection| {
6566 let cursor = if selection.is_empty() && !line_mode {
6567 movement::left(map, selection.start)
6568 } else {
6569 selection.start
6570 };
6571 selection.collapse_to(cursor, SelectionGoal::None);
6572 });
6573 })
6574 }
6575
6576 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
6577 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6578 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
6579 })
6580 }
6581
6582 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
6583 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6584 let line_mode = s.line_mode;
6585 s.move_with(|map, selection| {
6586 let cursor = if selection.is_empty() && !line_mode {
6587 movement::right(map, selection.end)
6588 } else {
6589 selection.end
6590 };
6591 selection.collapse_to(cursor, SelectionGoal::None)
6592 });
6593 })
6594 }
6595
6596 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
6597 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6598 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
6599 })
6600 }
6601
6602 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
6603 if self.take_rename(true, cx).is_some() {
6604 return;
6605 }
6606
6607 if matches!(self.mode, EditorMode::SingleLine) {
6608 cx.propagate();
6609 return;
6610 }
6611
6612 let text_layout_details = &self.text_layout_details(cx);
6613 let selection_count = self.selections.count();
6614 let first_selection = self.selections.first_anchor();
6615
6616 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6617 let line_mode = s.line_mode;
6618 s.move_with(|map, selection| {
6619 if !selection.is_empty() && !line_mode {
6620 selection.goal = SelectionGoal::None;
6621 }
6622 let (cursor, goal) = movement::up(
6623 map,
6624 selection.start,
6625 selection.goal,
6626 false,
6627 &text_layout_details,
6628 );
6629 selection.collapse_to(cursor, goal);
6630 });
6631 });
6632
6633 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
6634 {
6635 cx.propagate();
6636 }
6637 }
6638
6639 pub fn move_up_by_lines(&mut self, action: &MoveUpByLines, cx: &mut ViewContext<Self>) {
6640 if self.take_rename(true, cx).is_some() {
6641 return;
6642 }
6643
6644 if matches!(self.mode, EditorMode::SingleLine) {
6645 cx.propagate();
6646 return;
6647 }
6648
6649 let text_layout_details = &self.text_layout_details(cx);
6650
6651 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6652 let line_mode = s.line_mode;
6653 s.move_with(|map, selection| {
6654 if !selection.is_empty() && !line_mode {
6655 selection.goal = SelectionGoal::None;
6656 }
6657 let (cursor, goal) = movement::up_by_rows(
6658 map,
6659 selection.start,
6660 action.lines,
6661 selection.goal,
6662 false,
6663 &text_layout_details,
6664 );
6665 selection.collapse_to(cursor, goal);
6666 });
6667 })
6668 }
6669
6670 pub fn move_down_by_lines(&mut self, action: &MoveDownByLines, cx: &mut ViewContext<Self>) {
6671 if self.take_rename(true, cx).is_some() {
6672 return;
6673 }
6674
6675 if matches!(self.mode, EditorMode::SingleLine) {
6676 cx.propagate();
6677 return;
6678 }
6679
6680 let text_layout_details = &self.text_layout_details(cx);
6681
6682 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6683 let line_mode = s.line_mode;
6684 s.move_with(|map, selection| {
6685 if !selection.is_empty() && !line_mode {
6686 selection.goal = SelectionGoal::None;
6687 }
6688 let (cursor, goal) = movement::down_by_rows(
6689 map,
6690 selection.start,
6691 action.lines,
6692 selection.goal,
6693 false,
6694 &text_layout_details,
6695 );
6696 selection.collapse_to(cursor, goal);
6697 });
6698 })
6699 }
6700
6701 pub fn select_down_by_lines(&mut self, action: &SelectDownByLines, cx: &mut ViewContext<Self>) {
6702 let text_layout_details = &self.text_layout_details(cx);
6703 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6704 s.move_heads_with(|map, head, goal| {
6705 movement::down_by_rows(map, head, action.lines, goal, false, &text_layout_details)
6706 })
6707 })
6708 }
6709
6710 pub fn select_up_by_lines(&mut self, action: &SelectUpByLines, cx: &mut ViewContext<Self>) {
6711 let text_layout_details = &self.text_layout_details(cx);
6712 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6713 s.move_heads_with(|map, head, goal| {
6714 movement::up_by_rows(map, head, action.lines, goal, false, &text_layout_details)
6715 })
6716 })
6717 }
6718
6719 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
6720 if self.take_rename(true, cx).is_some() {
6721 return;
6722 }
6723
6724 if matches!(self.mode, EditorMode::SingleLine) {
6725 cx.propagate();
6726 return;
6727 }
6728
6729 let row_count = if let Some(row_count) = self.visible_line_count() {
6730 row_count as u32 - 1
6731 } else {
6732 return;
6733 };
6734
6735 let autoscroll = if action.center_cursor {
6736 Autoscroll::center()
6737 } else {
6738 Autoscroll::fit()
6739 };
6740
6741 let text_layout_details = &self.text_layout_details(cx);
6742
6743 self.change_selections(Some(autoscroll), cx, |s| {
6744 let line_mode = s.line_mode;
6745 s.move_with(|map, selection| {
6746 if !selection.is_empty() && !line_mode {
6747 selection.goal = SelectionGoal::None;
6748 }
6749 let (cursor, goal) = movement::up_by_rows(
6750 map,
6751 selection.end,
6752 row_count,
6753 selection.goal,
6754 false,
6755 &text_layout_details,
6756 );
6757 selection.collapse_to(cursor, goal);
6758 });
6759 });
6760 }
6761
6762 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
6763 let text_layout_details = &self.text_layout_details(cx);
6764 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6765 s.move_heads_with(|map, head, goal| {
6766 movement::up(map, head, goal, false, &text_layout_details)
6767 })
6768 })
6769 }
6770
6771 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
6772 self.take_rename(true, cx);
6773
6774 if self.mode == EditorMode::SingleLine {
6775 cx.propagate();
6776 return;
6777 }
6778
6779 let text_layout_details = &self.text_layout_details(cx);
6780 let selection_count = self.selections.count();
6781 let first_selection = self.selections.first_anchor();
6782
6783 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6784 let line_mode = s.line_mode;
6785 s.move_with(|map, selection| {
6786 if !selection.is_empty() && !line_mode {
6787 selection.goal = SelectionGoal::None;
6788 }
6789 let (cursor, goal) = movement::down(
6790 map,
6791 selection.end,
6792 selection.goal,
6793 false,
6794 &text_layout_details,
6795 );
6796 selection.collapse_to(cursor, goal);
6797 });
6798 });
6799
6800 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
6801 {
6802 cx.propagate();
6803 }
6804 }
6805
6806 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
6807 if self.take_rename(true, cx).is_some() {
6808 return;
6809 }
6810
6811 if self
6812 .context_menu
6813 .write()
6814 .as_mut()
6815 .map(|menu| menu.select_last(self.project.as_ref(), cx))
6816 .unwrap_or(false)
6817 {
6818 return;
6819 }
6820
6821 if matches!(self.mode, EditorMode::SingleLine) {
6822 cx.propagate();
6823 return;
6824 }
6825
6826 let row_count = if let Some(row_count) = self.visible_line_count() {
6827 row_count as u32 - 1
6828 } else {
6829 return;
6830 };
6831
6832 let autoscroll = if action.center_cursor {
6833 Autoscroll::center()
6834 } else {
6835 Autoscroll::fit()
6836 };
6837
6838 let text_layout_details = &self.text_layout_details(cx);
6839 self.change_selections(Some(autoscroll), cx, |s| {
6840 let line_mode = s.line_mode;
6841 s.move_with(|map, selection| {
6842 if !selection.is_empty() && !line_mode {
6843 selection.goal = SelectionGoal::None;
6844 }
6845 let (cursor, goal) = movement::down_by_rows(
6846 map,
6847 selection.end,
6848 row_count,
6849 selection.goal,
6850 false,
6851 &text_layout_details,
6852 );
6853 selection.collapse_to(cursor, goal);
6854 });
6855 });
6856 }
6857
6858 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
6859 let text_layout_details = &self.text_layout_details(cx);
6860 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6861 s.move_heads_with(|map, head, goal| {
6862 movement::down(map, head, goal, false, &text_layout_details)
6863 })
6864 });
6865 }
6866
6867 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
6868 if let Some(context_menu) = self.context_menu.write().as_mut() {
6869 context_menu.select_first(self.project.as_ref(), cx);
6870 }
6871 }
6872
6873 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
6874 if let Some(context_menu) = self.context_menu.write().as_mut() {
6875 context_menu.select_prev(self.project.as_ref(), cx);
6876 }
6877 }
6878
6879 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
6880 if let Some(context_menu) = self.context_menu.write().as_mut() {
6881 context_menu.select_next(self.project.as_ref(), cx);
6882 }
6883 }
6884
6885 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
6886 if let Some(context_menu) = self.context_menu.write().as_mut() {
6887 context_menu.select_last(self.project.as_ref(), cx);
6888 }
6889 }
6890
6891 pub fn move_to_previous_word_start(
6892 &mut self,
6893 _: &MoveToPreviousWordStart,
6894 cx: &mut ViewContext<Self>,
6895 ) {
6896 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6897 s.move_cursors_with(|map, head, _| {
6898 (
6899 movement::previous_word_start(map, head),
6900 SelectionGoal::None,
6901 )
6902 });
6903 })
6904 }
6905
6906 pub fn move_to_previous_subword_start(
6907 &mut self,
6908 _: &MoveToPreviousSubwordStart,
6909 cx: &mut ViewContext<Self>,
6910 ) {
6911 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6912 s.move_cursors_with(|map, head, _| {
6913 (
6914 movement::previous_subword_start(map, head),
6915 SelectionGoal::None,
6916 )
6917 });
6918 })
6919 }
6920
6921 pub fn select_to_previous_word_start(
6922 &mut self,
6923 _: &SelectToPreviousWordStart,
6924 cx: &mut ViewContext<Self>,
6925 ) {
6926 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6927 s.move_heads_with(|map, head, _| {
6928 (
6929 movement::previous_word_start(map, head),
6930 SelectionGoal::None,
6931 )
6932 });
6933 })
6934 }
6935
6936 pub fn select_to_previous_subword_start(
6937 &mut self,
6938 _: &SelectToPreviousSubwordStart,
6939 cx: &mut ViewContext<Self>,
6940 ) {
6941 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6942 s.move_heads_with(|map, head, _| {
6943 (
6944 movement::previous_subword_start(map, head),
6945 SelectionGoal::None,
6946 )
6947 });
6948 })
6949 }
6950
6951 pub fn delete_to_previous_word_start(
6952 &mut self,
6953 _: &DeleteToPreviousWordStart,
6954 cx: &mut ViewContext<Self>,
6955 ) {
6956 self.transact(cx, |this, cx| {
6957 this.select_autoclose_pair(cx);
6958 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6959 let line_mode = s.line_mode;
6960 s.move_with(|map, selection| {
6961 if selection.is_empty() && !line_mode {
6962 let cursor = movement::previous_word_start(map, selection.head());
6963 selection.set_head(cursor, SelectionGoal::None);
6964 }
6965 });
6966 });
6967 this.insert("", cx);
6968 });
6969 }
6970
6971 pub fn delete_to_previous_subword_start(
6972 &mut self,
6973 _: &DeleteToPreviousSubwordStart,
6974 cx: &mut ViewContext<Self>,
6975 ) {
6976 self.transact(cx, |this, cx| {
6977 this.select_autoclose_pair(cx);
6978 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6979 let line_mode = s.line_mode;
6980 s.move_with(|map, selection| {
6981 if selection.is_empty() && !line_mode {
6982 let cursor = movement::previous_subword_start(map, selection.head());
6983 selection.set_head(cursor, SelectionGoal::None);
6984 }
6985 });
6986 });
6987 this.insert("", cx);
6988 });
6989 }
6990
6991 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
6992 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6993 s.move_cursors_with(|map, head, _| {
6994 (movement::next_word_end(map, head), SelectionGoal::None)
6995 });
6996 })
6997 }
6998
6999 pub fn move_to_next_subword_end(
7000 &mut self,
7001 _: &MoveToNextSubwordEnd,
7002 cx: &mut ViewContext<Self>,
7003 ) {
7004 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7005 s.move_cursors_with(|map, head, _| {
7006 (movement::next_subword_end(map, head), SelectionGoal::None)
7007 });
7008 })
7009 }
7010
7011 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
7012 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7013 s.move_heads_with(|map, head, _| {
7014 (movement::next_word_end(map, head), SelectionGoal::None)
7015 });
7016 })
7017 }
7018
7019 pub fn select_to_next_subword_end(
7020 &mut self,
7021 _: &SelectToNextSubwordEnd,
7022 cx: &mut ViewContext<Self>,
7023 ) {
7024 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7025 s.move_heads_with(|map, head, _| {
7026 (movement::next_subword_end(map, head), SelectionGoal::None)
7027 });
7028 })
7029 }
7030
7031 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
7032 self.transact(cx, |this, cx| {
7033 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7034 let line_mode = s.line_mode;
7035 s.move_with(|map, selection| {
7036 if selection.is_empty() && !line_mode {
7037 let cursor = movement::next_word_end(map, selection.head());
7038 selection.set_head(cursor, SelectionGoal::None);
7039 }
7040 });
7041 });
7042 this.insert("", cx);
7043 });
7044 }
7045
7046 pub fn delete_to_next_subword_end(
7047 &mut self,
7048 _: &DeleteToNextSubwordEnd,
7049 cx: &mut ViewContext<Self>,
7050 ) {
7051 self.transact(cx, |this, cx| {
7052 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7053 s.move_with(|map, selection| {
7054 if selection.is_empty() {
7055 let cursor = movement::next_subword_end(map, selection.head());
7056 selection.set_head(cursor, SelectionGoal::None);
7057 }
7058 });
7059 });
7060 this.insert("", cx);
7061 });
7062 }
7063
7064 pub fn move_to_beginning_of_line(
7065 &mut self,
7066 action: &MoveToBeginningOfLine,
7067 cx: &mut ViewContext<Self>,
7068 ) {
7069 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7070 s.move_cursors_with(|map, head, _| {
7071 (
7072 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7073 SelectionGoal::None,
7074 )
7075 });
7076 })
7077 }
7078
7079 pub fn select_to_beginning_of_line(
7080 &mut self,
7081 action: &SelectToBeginningOfLine,
7082 cx: &mut ViewContext<Self>,
7083 ) {
7084 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7085 s.move_heads_with(|map, head, _| {
7086 (
7087 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7088 SelectionGoal::None,
7089 )
7090 });
7091 });
7092 }
7093
7094 pub fn delete_to_beginning_of_line(
7095 &mut self,
7096 _: &DeleteToBeginningOfLine,
7097 cx: &mut ViewContext<Self>,
7098 ) {
7099 self.transact(cx, |this, cx| {
7100 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7101 s.move_with(|_, selection| {
7102 selection.reversed = true;
7103 });
7104 });
7105
7106 this.select_to_beginning_of_line(
7107 &SelectToBeginningOfLine {
7108 stop_at_soft_wraps: false,
7109 },
7110 cx,
7111 );
7112 this.backspace(&Backspace, cx);
7113 });
7114 }
7115
7116 pub fn move_to_end_of_line(&mut self, action: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
7117 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7118 s.move_cursors_with(|map, head, _| {
7119 (
7120 movement::line_end(map, head, action.stop_at_soft_wraps),
7121 SelectionGoal::None,
7122 )
7123 });
7124 })
7125 }
7126
7127 pub fn select_to_end_of_line(
7128 &mut self,
7129 action: &SelectToEndOfLine,
7130 cx: &mut ViewContext<Self>,
7131 ) {
7132 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7133 s.move_heads_with(|map, head, _| {
7134 (
7135 movement::line_end(map, head, action.stop_at_soft_wraps),
7136 SelectionGoal::None,
7137 )
7138 });
7139 })
7140 }
7141
7142 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
7143 self.transact(cx, |this, cx| {
7144 this.select_to_end_of_line(
7145 &SelectToEndOfLine {
7146 stop_at_soft_wraps: false,
7147 },
7148 cx,
7149 );
7150 this.delete(&Delete, cx);
7151 });
7152 }
7153
7154 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
7155 self.transact(cx, |this, cx| {
7156 this.select_to_end_of_line(
7157 &SelectToEndOfLine {
7158 stop_at_soft_wraps: false,
7159 },
7160 cx,
7161 );
7162 this.cut(&Cut, cx);
7163 });
7164 }
7165
7166 pub fn move_to_start_of_paragraph(
7167 &mut self,
7168 _: &MoveToStartOfParagraph,
7169 cx: &mut ViewContext<Self>,
7170 ) {
7171 if matches!(self.mode, EditorMode::SingleLine) {
7172 cx.propagate();
7173 return;
7174 }
7175
7176 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7177 s.move_with(|map, selection| {
7178 selection.collapse_to(
7179 movement::start_of_paragraph(map, selection.head(), 1),
7180 SelectionGoal::None,
7181 )
7182 });
7183 })
7184 }
7185
7186 pub fn move_to_end_of_paragraph(
7187 &mut self,
7188 _: &MoveToEndOfParagraph,
7189 cx: &mut ViewContext<Self>,
7190 ) {
7191 if matches!(self.mode, EditorMode::SingleLine) {
7192 cx.propagate();
7193 return;
7194 }
7195
7196 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7197 s.move_with(|map, selection| {
7198 selection.collapse_to(
7199 movement::end_of_paragraph(map, selection.head(), 1),
7200 SelectionGoal::None,
7201 )
7202 });
7203 })
7204 }
7205
7206 pub fn select_to_start_of_paragraph(
7207 &mut self,
7208 _: &SelectToStartOfParagraph,
7209 cx: &mut ViewContext<Self>,
7210 ) {
7211 if matches!(self.mode, EditorMode::SingleLine) {
7212 cx.propagate();
7213 return;
7214 }
7215
7216 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7217 s.move_heads_with(|map, head, _| {
7218 (
7219 movement::start_of_paragraph(map, head, 1),
7220 SelectionGoal::None,
7221 )
7222 });
7223 })
7224 }
7225
7226 pub fn select_to_end_of_paragraph(
7227 &mut self,
7228 _: &SelectToEndOfParagraph,
7229 cx: &mut ViewContext<Self>,
7230 ) {
7231 if matches!(self.mode, EditorMode::SingleLine) {
7232 cx.propagate();
7233 return;
7234 }
7235
7236 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7237 s.move_heads_with(|map, head, _| {
7238 (
7239 movement::end_of_paragraph(map, head, 1),
7240 SelectionGoal::None,
7241 )
7242 });
7243 })
7244 }
7245
7246 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
7247 if matches!(self.mode, EditorMode::SingleLine) {
7248 cx.propagate();
7249 return;
7250 }
7251
7252 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7253 s.select_ranges(vec![0..0]);
7254 });
7255 }
7256
7257 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
7258 let mut selection = self.selections.last::<Point>(cx);
7259 selection.set_head(Point::zero(), SelectionGoal::None);
7260
7261 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7262 s.select(vec![selection]);
7263 });
7264 }
7265
7266 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
7267 if matches!(self.mode, EditorMode::SingleLine) {
7268 cx.propagate();
7269 return;
7270 }
7271
7272 let cursor = self.buffer.read(cx).read(cx).len();
7273 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7274 s.select_ranges(vec![cursor..cursor])
7275 });
7276 }
7277
7278 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
7279 self.nav_history = nav_history;
7280 }
7281
7282 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
7283 self.nav_history.as_ref()
7284 }
7285
7286 fn push_to_nav_history(
7287 &mut self,
7288 cursor_anchor: Anchor,
7289 new_position: Option<Point>,
7290 cx: &mut ViewContext<Self>,
7291 ) {
7292 if let Some(nav_history) = self.nav_history.as_mut() {
7293 let buffer = self.buffer.read(cx).read(cx);
7294 let cursor_position = cursor_anchor.to_point(&buffer);
7295 let scroll_state = self.scroll_manager.anchor();
7296 let scroll_top_row = scroll_state.top_row(&buffer);
7297 drop(buffer);
7298
7299 if let Some(new_position) = new_position {
7300 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
7301 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
7302 return;
7303 }
7304 }
7305
7306 nav_history.push(
7307 Some(NavigationData {
7308 cursor_anchor,
7309 cursor_position,
7310 scroll_anchor: scroll_state,
7311 scroll_top_row,
7312 }),
7313 cx,
7314 );
7315 }
7316 }
7317
7318 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
7319 let buffer = self.buffer.read(cx).snapshot(cx);
7320 let mut selection = self.selections.first::<usize>(cx);
7321 selection.set_head(buffer.len(), SelectionGoal::None);
7322 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7323 s.select(vec![selection]);
7324 });
7325 }
7326
7327 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
7328 let end = self.buffer.read(cx).read(cx).len();
7329 self.change_selections(None, cx, |s| {
7330 s.select_ranges(vec![0..end]);
7331 });
7332 }
7333
7334 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
7335 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7336 let mut selections = self.selections.all::<Point>(cx);
7337 let max_point = display_map.buffer_snapshot.max_point();
7338 for selection in &mut selections {
7339 let rows = selection.spanned_rows(true, &display_map);
7340 selection.start = Point::new(rows.start.0, 0);
7341 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
7342 selection.reversed = false;
7343 }
7344 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7345 s.select(selections);
7346 });
7347 }
7348
7349 pub fn split_selection_into_lines(
7350 &mut self,
7351 _: &SplitSelectionIntoLines,
7352 cx: &mut ViewContext<Self>,
7353 ) {
7354 let mut to_unfold = Vec::new();
7355 let mut new_selection_ranges = Vec::new();
7356 {
7357 let selections = self.selections.all::<Point>(cx);
7358 let buffer = self.buffer.read(cx).read(cx);
7359 for selection in selections {
7360 for row in selection.start.row..selection.end.row {
7361 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
7362 new_selection_ranges.push(cursor..cursor);
7363 }
7364 new_selection_ranges.push(selection.end..selection.end);
7365 to_unfold.push(selection.start..selection.end);
7366 }
7367 }
7368 self.unfold_ranges(to_unfold, true, true, cx);
7369 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7370 s.select_ranges(new_selection_ranges);
7371 });
7372 }
7373
7374 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
7375 self.add_selection(true, cx);
7376 }
7377
7378 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
7379 self.add_selection(false, cx);
7380 }
7381
7382 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
7383 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7384 let mut selections = self.selections.all::<Point>(cx);
7385 let text_layout_details = self.text_layout_details(cx);
7386 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
7387 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
7388 let range = oldest_selection.display_range(&display_map).sorted();
7389
7390 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
7391 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
7392 let positions = start_x.min(end_x)..start_x.max(end_x);
7393
7394 selections.clear();
7395 let mut stack = Vec::new();
7396 for row in range.start.row().0..=range.end.row().0 {
7397 if let Some(selection) = self.selections.build_columnar_selection(
7398 &display_map,
7399 DisplayRow(row),
7400 &positions,
7401 oldest_selection.reversed,
7402 &text_layout_details,
7403 ) {
7404 stack.push(selection.id);
7405 selections.push(selection);
7406 }
7407 }
7408
7409 if above {
7410 stack.reverse();
7411 }
7412
7413 AddSelectionsState { above, stack }
7414 });
7415
7416 let last_added_selection = *state.stack.last().unwrap();
7417 let mut new_selections = Vec::new();
7418 if above == state.above {
7419 let end_row = if above {
7420 DisplayRow(0)
7421 } else {
7422 display_map.max_point().row()
7423 };
7424
7425 'outer: for selection in selections {
7426 if selection.id == last_added_selection {
7427 let range = selection.display_range(&display_map).sorted();
7428 debug_assert_eq!(range.start.row(), range.end.row());
7429 let mut row = range.start.row();
7430 let positions =
7431 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
7432 px(start)..px(end)
7433 } else {
7434 let start_x =
7435 display_map.x_for_display_point(range.start, &text_layout_details);
7436 let end_x =
7437 display_map.x_for_display_point(range.end, &text_layout_details);
7438 start_x.min(end_x)..start_x.max(end_x)
7439 };
7440
7441 while row != end_row {
7442 if above {
7443 row.0 -= 1;
7444 } else {
7445 row.0 += 1;
7446 }
7447
7448 if let Some(new_selection) = self.selections.build_columnar_selection(
7449 &display_map,
7450 row,
7451 &positions,
7452 selection.reversed,
7453 &text_layout_details,
7454 ) {
7455 state.stack.push(new_selection.id);
7456 if above {
7457 new_selections.push(new_selection);
7458 new_selections.push(selection);
7459 } else {
7460 new_selections.push(selection);
7461 new_selections.push(new_selection);
7462 }
7463
7464 continue 'outer;
7465 }
7466 }
7467 }
7468
7469 new_selections.push(selection);
7470 }
7471 } else {
7472 new_selections = selections;
7473 new_selections.retain(|s| s.id != last_added_selection);
7474 state.stack.pop();
7475 }
7476
7477 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7478 s.select(new_selections);
7479 });
7480 if state.stack.len() > 1 {
7481 self.add_selections_state = Some(state);
7482 }
7483 }
7484
7485 pub fn select_next_match_internal(
7486 &mut self,
7487 display_map: &DisplaySnapshot,
7488 replace_newest: bool,
7489 autoscroll: Option<Autoscroll>,
7490 cx: &mut ViewContext<Self>,
7491 ) -> Result<()> {
7492 fn select_next_match_ranges(
7493 this: &mut Editor,
7494 range: Range<usize>,
7495 replace_newest: bool,
7496 auto_scroll: Option<Autoscroll>,
7497 cx: &mut ViewContext<Editor>,
7498 ) {
7499 this.unfold_ranges([range.clone()], false, true, cx);
7500 this.change_selections(auto_scroll, cx, |s| {
7501 if replace_newest {
7502 s.delete(s.newest_anchor().id);
7503 }
7504 s.insert_range(range.clone());
7505 });
7506 }
7507
7508 let buffer = &display_map.buffer_snapshot;
7509 let mut selections = self.selections.all::<usize>(cx);
7510 if let Some(mut select_next_state) = self.select_next_state.take() {
7511 let query = &select_next_state.query;
7512 if !select_next_state.done {
7513 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
7514 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
7515 let mut next_selected_range = None;
7516
7517 let bytes_after_last_selection =
7518 buffer.bytes_in_range(last_selection.end..buffer.len());
7519 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
7520 let query_matches = query
7521 .stream_find_iter(bytes_after_last_selection)
7522 .map(|result| (last_selection.end, result))
7523 .chain(
7524 query
7525 .stream_find_iter(bytes_before_first_selection)
7526 .map(|result| (0, result)),
7527 );
7528
7529 for (start_offset, query_match) in query_matches {
7530 let query_match = query_match.unwrap(); // can only fail due to I/O
7531 let offset_range =
7532 start_offset + query_match.start()..start_offset + query_match.end();
7533 let display_range = offset_range.start.to_display_point(&display_map)
7534 ..offset_range.end.to_display_point(&display_map);
7535
7536 if !select_next_state.wordwise
7537 || (!movement::is_inside_word(&display_map, display_range.start)
7538 && !movement::is_inside_word(&display_map, display_range.end))
7539 {
7540 // TODO: This is n^2, because we might check all the selections
7541 if !selections
7542 .iter()
7543 .any(|selection| selection.range().overlaps(&offset_range))
7544 {
7545 next_selected_range = Some(offset_range);
7546 break;
7547 }
7548 }
7549 }
7550
7551 if let Some(next_selected_range) = next_selected_range {
7552 select_next_match_ranges(
7553 self,
7554 next_selected_range,
7555 replace_newest,
7556 autoscroll,
7557 cx,
7558 );
7559 } else {
7560 select_next_state.done = true;
7561 }
7562 }
7563
7564 self.select_next_state = Some(select_next_state);
7565 } else {
7566 let mut only_carets = true;
7567 let mut same_text_selected = true;
7568 let mut selected_text = None;
7569
7570 let mut selections_iter = selections.iter().peekable();
7571 while let Some(selection) = selections_iter.next() {
7572 if selection.start != selection.end {
7573 only_carets = false;
7574 }
7575
7576 if same_text_selected {
7577 if selected_text.is_none() {
7578 selected_text =
7579 Some(buffer.text_for_range(selection.range()).collect::<String>());
7580 }
7581
7582 if let Some(next_selection) = selections_iter.peek() {
7583 if next_selection.range().len() == selection.range().len() {
7584 let next_selected_text = buffer
7585 .text_for_range(next_selection.range())
7586 .collect::<String>();
7587 if Some(next_selected_text) != selected_text {
7588 same_text_selected = false;
7589 selected_text = None;
7590 }
7591 } else {
7592 same_text_selected = false;
7593 selected_text = None;
7594 }
7595 }
7596 }
7597 }
7598
7599 if only_carets {
7600 for selection in &mut selections {
7601 let word_range = movement::surrounding_word(
7602 &display_map,
7603 selection.start.to_display_point(&display_map),
7604 );
7605 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
7606 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
7607 selection.goal = SelectionGoal::None;
7608 selection.reversed = false;
7609 select_next_match_ranges(
7610 self,
7611 selection.start..selection.end,
7612 replace_newest,
7613 autoscroll,
7614 cx,
7615 );
7616 }
7617
7618 if selections.len() == 1 {
7619 let selection = selections
7620 .last()
7621 .expect("ensured that there's only one selection");
7622 let query = buffer
7623 .text_for_range(selection.start..selection.end)
7624 .collect::<String>();
7625 let is_empty = query.is_empty();
7626 let select_state = SelectNextState {
7627 query: AhoCorasick::new(&[query])?,
7628 wordwise: true,
7629 done: is_empty,
7630 };
7631 self.select_next_state = Some(select_state);
7632 } else {
7633 self.select_next_state = None;
7634 }
7635 } else if let Some(selected_text) = selected_text {
7636 self.select_next_state = Some(SelectNextState {
7637 query: AhoCorasick::new(&[selected_text])?,
7638 wordwise: false,
7639 done: false,
7640 });
7641 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
7642 }
7643 }
7644 Ok(())
7645 }
7646
7647 pub fn select_all_matches(
7648 &mut self,
7649 _action: &SelectAllMatches,
7650 cx: &mut ViewContext<Self>,
7651 ) -> Result<()> {
7652 self.push_to_selection_history();
7653 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7654
7655 self.select_next_match_internal(&display_map, false, None, cx)?;
7656 let Some(select_next_state) = self.select_next_state.as_mut() else {
7657 return Ok(());
7658 };
7659 if select_next_state.done {
7660 return Ok(());
7661 }
7662
7663 let mut new_selections = self.selections.all::<usize>(cx);
7664
7665 let buffer = &display_map.buffer_snapshot;
7666 let query_matches = select_next_state
7667 .query
7668 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
7669
7670 for query_match in query_matches {
7671 let query_match = query_match.unwrap(); // can only fail due to I/O
7672 let offset_range = query_match.start()..query_match.end();
7673 let display_range = offset_range.start.to_display_point(&display_map)
7674 ..offset_range.end.to_display_point(&display_map);
7675
7676 if !select_next_state.wordwise
7677 || (!movement::is_inside_word(&display_map, display_range.start)
7678 && !movement::is_inside_word(&display_map, display_range.end))
7679 {
7680 self.selections.change_with(cx, |selections| {
7681 new_selections.push(Selection {
7682 id: selections.new_selection_id(),
7683 start: offset_range.start,
7684 end: offset_range.end,
7685 reversed: false,
7686 goal: SelectionGoal::None,
7687 });
7688 });
7689 }
7690 }
7691
7692 new_selections.sort_by_key(|selection| selection.start);
7693 let mut ix = 0;
7694 while ix + 1 < new_selections.len() {
7695 let current_selection = &new_selections[ix];
7696 let next_selection = &new_selections[ix + 1];
7697 if current_selection.range().overlaps(&next_selection.range()) {
7698 if current_selection.id < next_selection.id {
7699 new_selections.remove(ix + 1);
7700 } else {
7701 new_selections.remove(ix);
7702 }
7703 } else {
7704 ix += 1;
7705 }
7706 }
7707
7708 select_next_state.done = true;
7709 self.unfold_ranges(
7710 new_selections.iter().map(|selection| selection.range()),
7711 false,
7712 false,
7713 cx,
7714 );
7715 self.change_selections(Some(Autoscroll::fit()), cx, |selections| {
7716 selections.select(new_selections)
7717 });
7718
7719 Ok(())
7720 }
7721
7722 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
7723 self.push_to_selection_history();
7724 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7725 self.select_next_match_internal(
7726 &display_map,
7727 action.replace_newest,
7728 Some(Autoscroll::newest()),
7729 cx,
7730 )?;
7731 Ok(())
7732 }
7733
7734 pub fn select_previous(
7735 &mut self,
7736 action: &SelectPrevious,
7737 cx: &mut ViewContext<Self>,
7738 ) -> Result<()> {
7739 self.push_to_selection_history();
7740 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7741 let buffer = &display_map.buffer_snapshot;
7742 let mut selections = self.selections.all::<usize>(cx);
7743 if let Some(mut select_prev_state) = self.select_prev_state.take() {
7744 let query = &select_prev_state.query;
7745 if !select_prev_state.done {
7746 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
7747 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
7748 let mut next_selected_range = None;
7749 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
7750 let bytes_before_last_selection =
7751 buffer.reversed_bytes_in_range(0..last_selection.start);
7752 let bytes_after_first_selection =
7753 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
7754 let query_matches = query
7755 .stream_find_iter(bytes_before_last_selection)
7756 .map(|result| (last_selection.start, result))
7757 .chain(
7758 query
7759 .stream_find_iter(bytes_after_first_selection)
7760 .map(|result| (buffer.len(), result)),
7761 );
7762 for (end_offset, query_match) in query_matches {
7763 let query_match = query_match.unwrap(); // can only fail due to I/O
7764 let offset_range =
7765 end_offset - query_match.end()..end_offset - query_match.start();
7766 let display_range = offset_range.start.to_display_point(&display_map)
7767 ..offset_range.end.to_display_point(&display_map);
7768
7769 if !select_prev_state.wordwise
7770 || (!movement::is_inside_word(&display_map, display_range.start)
7771 && !movement::is_inside_word(&display_map, display_range.end))
7772 {
7773 next_selected_range = Some(offset_range);
7774 break;
7775 }
7776 }
7777
7778 if let Some(next_selected_range) = next_selected_range {
7779 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
7780 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
7781 if action.replace_newest {
7782 s.delete(s.newest_anchor().id);
7783 }
7784 s.insert_range(next_selected_range);
7785 });
7786 } else {
7787 select_prev_state.done = true;
7788 }
7789 }
7790
7791 self.select_prev_state = Some(select_prev_state);
7792 } else {
7793 let mut only_carets = true;
7794 let mut same_text_selected = true;
7795 let mut selected_text = None;
7796
7797 let mut selections_iter = selections.iter().peekable();
7798 while let Some(selection) = selections_iter.next() {
7799 if selection.start != selection.end {
7800 only_carets = false;
7801 }
7802
7803 if same_text_selected {
7804 if selected_text.is_none() {
7805 selected_text =
7806 Some(buffer.text_for_range(selection.range()).collect::<String>());
7807 }
7808
7809 if let Some(next_selection) = selections_iter.peek() {
7810 if next_selection.range().len() == selection.range().len() {
7811 let next_selected_text = buffer
7812 .text_for_range(next_selection.range())
7813 .collect::<String>();
7814 if Some(next_selected_text) != selected_text {
7815 same_text_selected = false;
7816 selected_text = None;
7817 }
7818 } else {
7819 same_text_selected = false;
7820 selected_text = None;
7821 }
7822 }
7823 }
7824 }
7825
7826 if only_carets {
7827 for selection in &mut selections {
7828 let word_range = movement::surrounding_word(
7829 &display_map,
7830 selection.start.to_display_point(&display_map),
7831 );
7832 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
7833 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
7834 selection.goal = SelectionGoal::None;
7835 selection.reversed = false;
7836 }
7837 if selections.len() == 1 {
7838 let selection = selections
7839 .last()
7840 .expect("ensured that there's only one selection");
7841 let query = buffer
7842 .text_for_range(selection.start..selection.end)
7843 .collect::<String>();
7844 let is_empty = query.is_empty();
7845 let select_state = SelectNextState {
7846 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
7847 wordwise: true,
7848 done: is_empty,
7849 };
7850 self.select_prev_state = Some(select_state);
7851 } else {
7852 self.select_prev_state = None;
7853 }
7854
7855 self.unfold_ranges(
7856 selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
7857 false,
7858 true,
7859 cx,
7860 );
7861 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
7862 s.select(selections);
7863 });
7864 } else if let Some(selected_text) = selected_text {
7865 self.select_prev_state = Some(SelectNextState {
7866 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
7867 wordwise: false,
7868 done: false,
7869 });
7870 self.select_previous(action, cx)?;
7871 }
7872 }
7873 Ok(())
7874 }
7875
7876 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
7877 let text_layout_details = &self.text_layout_details(cx);
7878 self.transact(cx, |this, cx| {
7879 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
7880 let mut edits = Vec::new();
7881 let mut selection_edit_ranges = Vec::new();
7882 let mut last_toggled_row = None;
7883 let snapshot = this.buffer.read(cx).read(cx);
7884 let empty_str: Arc<str> = "".into();
7885 let mut suffixes_inserted = Vec::new();
7886
7887 fn comment_prefix_range(
7888 snapshot: &MultiBufferSnapshot,
7889 row: MultiBufferRow,
7890 comment_prefix: &str,
7891 comment_prefix_whitespace: &str,
7892 ) -> Range<Point> {
7893 let start = Point::new(row.0, snapshot.indent_size_for_line(row).len);
7894
7895 let mut line_bytes = snapshot
7896 .bytes_in_range(start..snapshot.max_point())
7897 .flatten()
7898 .copied();
7899
7900 // If this line currently begins with the line comment prefix, then record
7901 // the range containing the prefix.
7902 if line_bytes
7903 .by_ref()
7904 .take(comment_prefix.len())
7905 .eq(comment_prefix.bytes())
7906 {
7907 // Include any whitespace that matches the comment prefix.
7908 let matching_whitespace_len = line_bytes
7909 .zip(comment_prefix_whitespace.bytes())
7910 .take_while(|(a, b)| a == b)
7911 .count() as u32;
7912 let end = Point::new(
7913 start.row,
7914 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
7915 );
7916 start..end
7917 } else {
7918 start..start
7919 }
7920 }
7921
7922 fn comment_suffix_range(
7923 snapshot: &MultiBufferSnapshot,
7924 row: MultiBufferRow,
7925 comment_suffix: &str,
7926 comment_suffix_has_leading_space: bool,
7927 ) -> Range<Point> {
7928 let end = Point::new(row.0, snapshot.line_len(row));
7929 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
7930
7931 let mut line_end_bytes = snapshot
7932 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
7933 .flatten()
7934 .copied();
7935
7936 let leading_space_len = if suffix_start_column > 0
7937 && line_end_bytes.next() == Some(b' ')
7938 && comment_suffix_has_leading_space
7939 {
7940 1
7941 } else {
7942 0
7943 };
7944
7945 // If this line currently begins with the line comment prefix, then record
7946 // the range containing the prefix.
7947 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
7948 let start = Point::new(end.row, suffix_start_column - leading_space_len);
7949 start..end
7950 } else {
7951 end..end
7952 }
7953 }
7954
7955 // TODO: Handle selections that cross excerpts
7956 for selection in &mut selections {
7957 let start_column = snapshot
7958 .indent_size_for_line(MultiBufferRow(selection.start.row))
7959 .len;
7960 let language = if let Some(language) =
7961 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
7962 {
7963 language
7964 } else {
7965 continue;
7966 };
7967
7968 selection_edit_ranges.clear();
7969
7970 // If multiple selections contain a given row, avoid processing that
7971 // row more than once.
7972 let mut start_row = MultiBufferRow(selection.start.row);
7973 if last_toggled_row == Some(start_row) {
7974 start_row = start_row.next_row();
7975 }
7976 let end_row =
7977 if selection.end.row > selection.start.row && selection.end.column == 0 {
7978 MultiBufferRow(selection.end.row - 1)
7979 } else {
7980 MultiBufferRow(selection.end.row)
7981 };
7982 last_toggled_row = Some(end_row);
7983
7984 if start_row > end_row {
7985 continue;
7986 }
7987
7988 // If the language has line comments, toggle those.
7989 let full_comment_prefixes = language.line_comment_prefixes();
7990 if !full_comment_prefixes.is_empty() {
7991 let first_prefix = full_comment_prefixes
7992 .first()
7993 .expect("prefixes is non-empty");
7994 let prefix_trimmed_lengths = full_comment_prefixes
7995 .iter()
7996 .map(|p| p.trim_end_matches(' ').len())
7997 .collect::<SmallVec<[usize; 4]>>();
7998
7999 let mut all_selection_lines_are_comments = true;
8000
8001 for row in start_row.0..=end_row.0 {
8002 let row = MultiBufferRow(row);
8003 if start_row < end_row && snapshot.is_line_blank(row) {
8004 continue;
8005 }
8006
8007 let prefix_range = full_comment_prefixes
8008 .iter()
8009 .zip(prefix_trimmed_lengths.iter().copied())
8010 .map(|(prefix, trimmed_prefix_len)| {
8011 comment_prefix_range(
8012 snapshot.deref(),
8013 row,
8014 &prefix[..trimmed_prefix_len],
8015 &prefix[trimmed_prefix_len..],
8016 )
8017 })
8018 .max_by_key(|range| range.end.column - range.start.column)
8019 .expect("prefixes is non-empty");
8020
8021 if prefix_range.is_empty() {
8022 all_selection_lines_are_comments = false;
8023 }
8024
8025 selection_edit_ranges.push(prefix_range);
8026 }
8027
8028 if all_selection_lines_are_comments {
8029 edits.extend(
8030 selection_edit_ranges
8031 .iter()
8032 .cloned()
8033 .map(|range| (range, empty_str.clone())),
8034 );
8035 } else {
8036 let min_column = selection_edit_ranges
8037 .iter()
8038 .map(|range| range.start.column)
8039 .min()
8040 .unwrap_or(0);
8041 edits.extend(selection_edit_ranges.iter().map(|range| {
8042 let position = Point::new(range.start.row, min_column);
8043 (position..position, first_prefix.clone())
8044 }));
8045 }
8046 } else if let Some((full_comment_prefix, comment_suffix)) =
8047 language.block_comment_delimiters()
8048 {
8049 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
8050 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
8051 let prefix_range = comment_prefix_range(
8052 snapshot.deref(),
8053 start_row,
8054 comment_prefix,
8055 comment_prefix_whitespace,
8056 );
8057 let suffix_range = comment_suffix_range(
8058 snapshot.deref(),
8059 end_row,
8060 comment_suffix.trim_start_matches(' '),
8061 comment_suffix.starts_with(' '),
8062 );
8063
8064 if prefix_range.is_empty() || suffix_range.is_empty() {
8065 edits.push((
8066 prefix_range.start..prefix_range.start,
8067 full_comment_prefix.clone(),
8068 ));
8069 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
8070 suffixes_inserted.push((end_row, comment_suffix.len()));
8071 } else {
8072 edits.push((prefix_range, empty_str.clone()));
8073 edits.push((suffix_range, empty_str.clone()));
8074 }
8075 } else {
8076 continue;
8077 }
8078 }
8079
8080 drop(snapshot);
8081 this.buffer.update(cx, |buffer, cx| {
8082 buffer.edit(edits, None, cx);
8083 });
8084
8085 // Adjust selections so that they end before any comment suffixes that
8086 // were inserted.
8087 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
8088 let mut selections = this.selections.all::<Point>(cx);
8089 let snapshot = this.buffer.read(cx).read(cx);
8090 for selection in &mut selections {
8091 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
8092 match row.cmp(&MultiBufferRow(selection.end.row)) {
8093 Ordering::Less => {
8094 suffixes_inserted.next();
8095 continue;
8096 }
8097 Ordering::Greater => break,
8098 Ordering::Equal => {
8099 if selection.end.column == snapshot.line_len(row) {
8100 if selection.is_empty() {
8101 selection.start.column -= suffix_len as u32;
8102 }
8103 selection.end.column -= suffix_len as u32;
8104 }
8105 break;
8106 }
8107 }
8108 }
8109 }
8110
8111 drop(snapshot);
8112 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
8113
8114 let selections = this.selections.all::<Point>(cx);
8115 let selections_on_single_row = selections.windows(2).all(|selections| {
8116 selections[0].start.row == selections[1].start.row
8117 && selections[0].end.row == selections[1].end.row
8118 && selections[0].start.row == selections[0].end.row
8119 });
8120 let selections_selecting = selections
8121 .iter()
8122 .any(|selection| selection.start != selection.end);
8123 let advance_downwards = action.advance_downwards
8124 && selections_on_single_row
8125 && !selections_selecting
8126 && this.mode != EditorMode::SingleLine;
8127
8128 if advance_downwards {
8129 let snapshot = this.buffer.read(cx).snapshot(cx);
8130
8131 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8132 s.move_cursors_with(|display_snapshot, display_point, _| {
8133 let mut point = display_point.to_point(display_snapshot);
8134 point.row += 1;
8135 point = snapshot.clip_point(point, Bias::Left);
8136 let display_point = point.to_display_point(display_snapshot);
8137 let goal = SelectionGoal::HorizontalPosition(
8138 display_snapshot
8139 .x_for_display_point(display_point, &text_layout_details)
8140 .into(),
8141 );
8142 (display_point, goal)
8143 })
8144 });
8145 }
8146 });
8147 }
8148
8149 pub fn select_larger_syntax_node(
8150 &mut self,
8151 _: &SelectLargerSyntaxNode,
8152 cx: &mut ViewContext<Self>,
8153 ) {
8154 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8155 let buffer = self.buffer.read(cx).snapshot(cx);
8156 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8157
8158 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8159 let mut selected_larger_node = false;
8160 let new_selections = old_selections
8161 .iter()
8162 .map(|selection| {
8163 let old_range = selection.start..selection.end;
8164 let mut new_range = old_range.clone();
8165 while let Some(containing_range) =
8166 buffer.range_for_syntax_ancestor(new_range.clone())
8167 {
8168 new_range = containing_range;
8169 if !display_map.intersects_fold(new_range.start)
8170 && !display_map.intersects_fold(new_range.end)
8171 {
8172 break;
8173 }
8174 }
8175
8176 selected_larger_node |= new_range != old_range;
8177 Selection {
8178 id: selection.id,
8179 start: new_range.start,
8180 end: new_range.end,
8181 goal: SelectionGoal::None,
8182 reversed: selection.reversed,
8183 }
8184 })
8185 .collect::<Vec<_>>();
8186
8187 if selected_larger_node {
8188 stack.push(old_selections);
8189 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8190 s.select(new_selections);
8191 });
8192 }
8193 self.select_larger_syntax_node_stack = stack;
8194 }
8195
8196 pub fn select_smaller_syntax_node(
8197 &mut self,
8198 _: &SelectSmallerSyntaxNode,
8199 cx: &mut ViewContext<Self>,
8200 ) {
8201 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8202 if let Some(selections) = stack.pop() {
8203 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8204 s.select(selections.to_vec());
8205 });
8206 }
8207 self.select_larger_syntax_node_stack = stack;
8208 }
8209
8210 fn refresh_runnables(&mut self, cx: &mut ViewContext<Self>) -> Task<()> {
8211 let project = self.project.clone();
8212 cx.spawn(|this, mut cx| async move {
8213 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
8214 this.display_map.update(cx, |map, cx| map.snapshot(cx))
8215 }) else {
8216 return;
8217 };
8218
8219 let Some(project) = project else {
8220 return;
8221 };
8222
8223 let hide_runnables = project
8224 .update(&mut cx, |project, cx| {
8225 // Do not display any test indicators in non-dev server remote projects.
8226 project.is_remote() && project.ssh_connection_string(cx).is_none()
8227 })
8228 .unwrap_or(true);
8229 if hide_runnables {
8230 return;
8231 }
8232 let new_rows =
8233 cx.background_executor()
8234 .spawn({
8235 let snapshot = display_snapshot.clone();
8236 async move {
8237 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
8238 }
8239 })
8240 .await;
8241 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
8242
8243 this.update(&mut cx, |this, _| {
8244 this.clear_tasks();
8245 for (key, value) in rows {
8246 this.insert_tasks(key, value);
8247 }
8248 })
8249 .ok();
8250 })
8251 }
8252 fn fetch_runnable_ranges(
8253 snapshot: &DisplaySnapshot,
8254 range: Range<Anchor>,
8255 ) -> Vec<language::RunnableRange> {
8256 snapshot.buffer_snapshot.runnable_ranges(range).collect()
8257 }
8258
8259 fn runnable_rows(
8260 project: Model<Project>,
8261 snapshot: DisplaySnapshot,
8262 runnable_ranges: Vec<RunnableRange>,
8263 mut cx: AsyncWindowContext,
8264 ) -> Vec<((BufferId, u32), RunnableTasks)> {
8265 runnable_ranges
8266 .into_iter()
8267 .filter_map(|mut runnable| {
8268 let tasks = cx
8269 .update(|cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
8270 .ok()?;
8271 if tasks.is_empty() {
8272 return None;
8273 }
8274
8275 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
8276
8277 let row = snapshot
8278 .buffer_snapshot
8279 .buffer_line_for_row(MultiBufferRow(point.row))?
8280 .1
8281 .start
8282 .row;
8283
8284 let context_range =
8285 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
8286 Some((
8287 (runnable.buffer_id, row),
8288 RunnableTasks {
8289 templates: tasks,
8290 offset: MultiBufferOffset(runnable.run_range.start),
8291 context_range,
8292 column: point.column,
8293 extra_variables: runnable.extra_captures,
8294 },
8295 ))
8296 })
8297 .collect()
8298 }
8299
8300 fn templates_with_tags(
8301 project: &Model<Project>,
8302 runnable: &mut Runnable,
8303 cx: &WindowContext<'_>,
8304 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
8305 let (inventory, worktree_id) = project.read_with(cx, |project, cx| {
8306 let worktree_id = project
8307 .buffer_for_id(runnable.buffer)
8308 .and_then(|buffer| buffer.read(cx).file())
8309 .map(|file| WorktreeId::from_usize(file.worktree_id()));
8310
8311 (project.task_inventory().clone(), worktree_id)
8312 });
8313
8314 let inventory = inventory.read(cx);
8315 let tags = mem::take(&mut runnable.tags);
8316 let mut tags: Vec<_> = tags
8317 .into_iter()
8318 .flat_map(|tag| {
8319 let tag = tag.0.clone();
8320 inventory
8321 .list_tasks(Some(runnable.language.clone()), worktree_id)
8322 .into_iter()
8323 .filter(move |(_, template)| {
8324 template.tags.iter().any(|source_tag| source_tag == &tag)
8325 })
8326 })
8327 .sorted_by_key(|(kind, _)| kind.to_owned())
8328 .collect();
8329 if let Some((leading_tag_source, _)) = tags.first() {
8330 // Strongest source wins; if we have worktree tag binding, prefer that to
8331 // global and language bindings;
8332 // if we have a global binding, prefer that to language binding.
8333 let first_mismatch = tags
8334 .iter()
8335 .position(|(tag_source, _)| tag_source != leading_tag_source);
8336 if let Some(index) = first_mismatch {
8337 tags.truncate(index);
8338 }
8339 }
8340
8341 tags
8342 }
8343
8344 pub fn move_to_enclosing_bracket(
8345 &mut self,
8346 _: &MoveToEnclosingBracket,
8347 cx: &mut ViewContext<Self>,
8348 ) {
8349 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8350 s.move_offsets_with(|snapshot, selection| {
8351 let Some(enclosing_bracket_ranges) =
8352 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
8353 else {
8354 return;
8355 };
8356
8357 let mut best_length = usize::MAX;
8358 let mut best_inside = false;
8359 let mut best_in_bracket_range = false;
8360 let mut best_destination = None;
8361 for (open, close) in enclosing_bracket_ranges {
8362 let close = close.to_inclusive();
8363 let length = close.end() - open.start;
8364 let inside = selection.start >= open.end && selection.end <= *close.start();
8365 let in_bracket_range = open.to_inclusive().contains(&selection.head())
8366 || close.contains(&selection.head());
8367
8368 // If best is next to a bracket and current isn't, skip
8369 if !in_bracket_range && best_in_bracket_range {
8370 continue;
8371 }
8372
8373 // Prefer smaller lengths unless best is inside and current isn't
8374 if length > best_length && (best_inside || !inside) {
8375 continue;
8376 }
8377
8378 best_length = length;
8379 best_inside = inside;
8380 best_in_bracket_range = in_bracket_range;
8381 best_destination = Some(
8382 if close.contains(&selection.start) && close.contains(&selection.end) {
8383 if inside {
8384 open.end
8385 } else {
8386 open.start
8387 }
8388 } else {
8389 if inside {
8390 *close.start()
8391 } else {
8392 *close.end()
8393 }
8394 },
8395 );
8396 }
8397
8398 if let Some(destination) = best_destination {
8399 selection.collapse_to(destination, SelectionGoal::None);
8400 }
8401 })
8402 });
8403 }
8404
8405 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
8406 self.end_selection(cx);
8407 self.selection_history.mode = SelectionHistoryMode::Undoing;
8408 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
8409 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8410 self.select_next_state = entry.select_next_state;
8411 self.select_prev_state = entry.select_prev_state;
8412 self.add_selections_state = entry.add_selections_state;
8413 self.request_autoscroll(Autoscroll::newest(), cx);
8414 }
8415 self.selection_history.mode = SelectionHistoryMode::Normal;
8416 }
8417
8418 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
8419 self.end_selection(cx);
8420 self.selection_history.mode = SelectionHistoryMode::Redoing;
8421 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
8422 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8423 self.select_next_state = entry.select_next_state;
8424 self.select_prev_state = entry.select_prev_state;
8425 self.add_selections_state = entry.add_selections_state;
8426 self.request_autoscroll(Autoscroll::newest(), cx);
8427 }
8428 self.selection_history.mode = SelectionHistoryMode::Normal;
8429 }
8430
8431 pub fn expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
8432 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
8433 }
8434
8435 pub fn expand_excerpts_down(
8436 &mut self,
8437 action: &ExpandExcerptsDown,
8438 cx: &mut ViewContext<Self>,
8439 ) {
8440 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
8441 }
8442
8443 pub fn expand_excerpts_up(&mut self, action: &ExpandExcerptsUp, cx: &mut ViewContext<Self>) {
8444 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
8445 }
8446
8447 pub fn expand_excerpts_for_direction(
8448 &mut self,
8449 lines: u32,
8450 direction: ExpandExcerptDirection,
8451 cx: &mut ViewContext<Self>,
8452 ) {
8453 let selections = self.selections.disjoint_anchors();
8454
8455 let lines = if lines == 0 {
8456 EditorSettings::get_global(cx).expand_excerpt_lines
8457 } else {
8458 lines
8459 };
8460
8461 self.buffer.update(cx, |buffer, cx| {
8462 buffer.expand_excerpts(
8463 selections
8464 .into_iter()
8465 .map(|selection| selection.head().excerpt_id)
8466 .dedup(),
8467 lines,
8468 direction,
8469 cx,
8470 )
8471 })
8472 }
8473
8474 pub fn expand_excerpt(
8475 &mut self,
8476 excerpt: ExcerptId,
8477 direction: ExpandExcerptDirection,
8478 cx: &mut ViewContext<Self>,
8479 ) {
8480 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
8481 self.buffer.update(cx, |buffer, cx| {
8482 buffer.expand_excerpts([excerpt], lines, direction, cx)
8483 })
8484 }
8485
8486 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
8487 self.go_to_diagnostic_impl(Direction::Next, cx)
8488 }
8489
8490 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
8491 self.go_to_diagnostic_impl(Direction::Prev, cx)
8492 }
8493
8494 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
8495 let buffer = self.buffer.read(cx).snapshot(cx);
8496 let selection = self.selections.newest::<usize>(cx);
8497
8498 // If there is an active Diagnostic Popover jump to its diagnostic instead.
8499 if direction == Direction::Next {
8500 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
8501 let (group_id, jump_to) = popover.activation_info();
8502 if self.activate_diagnostics(group_id, cx) {
8503 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8504 let mut new_selection = s.newest_anchor().clone();
8505 new_selection.collapse_to(jump_to, SelectionGoal::None);
8506 s.select_anchors(vec![new_selection.clone()]);
8507 });
8508 }
8509 return;
8510 }
8511 }
8512
8513 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
8514 active_diagnostics
8515 .primary_range
8516 .to_offset(&buffer)
8517 .to_inclusive()
8518 });
8519 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
8520 if active_primary_range.contains(&selection.head()) {
8521 *active_primary_range.start()
8522 } else {
8523 selection.head()
8524 }
8525 } else {
8526 selection.head()
8527 };
8528 let snapshot = self.snapshot(cx);
8529 loop {
8530 let diagnostics = if direction == Direction::Prev {
8531 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
8532 } else {
8533 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
8534 }
8535 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start));
8536 let group = diagnostics
8537 // relies on diagnostics_in_range to return diagnostics with the same starting range to
8538 // be sorted in a stable way
8539 // skip until we are at current active diagnostic, if it exists
8540 .skip_while(|entry| {
8541 (match direction {
8542 Direction::Prev => entry.range.start >= search_start,
8543 Direction::Next => entry.range.start <= search_start,
8544 }) && self
8545 .active_diagnostics
8546 .as_ref()
8547 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
8548 })
8549 .find_map(|entry| {
8550 if entry.diagnostic.is_primary
8551 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
8552 && !entry.range.is_empty()
8553 // if we match with the active diagnostic, skip it
8554 && Some(entry.diagnostic.group_id)
8555 != self.active_diagnostics.as_ref().map(|d| d.group_id)
8556 {
8557 Some((entry.range, entry.diagnostic.group_id))
8558 } else {
8559 None
8560 }
8561 });
8562
8563 if let Some((primary_range, group_id)) = group {
8564 if self.activate_diagnostics(group_id, cx) {
8565 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8566 s.select(vec![Selection {
8567 id: selection.id,
8568 start: primary_range.start,
8569 end: primary_range.start,
8570 reversed: false,
8571 goal: SelectionGoal::None,
8572 }]);
8573 });
8574 }
8575 break;
8576 } else {
8577 // Cycle around to the start of the buffer, potentially moving back to the start of
8578 // the currently active diagnostic.
8579 active_primary_range.take();
8580 if direction == Direction::Prev {
8581 if search_start == buffer.len() {
8582 break;
8583 } else {
8584 search_start = buffer.len();
8585 }
8586 } else if search_start == 0 {
8587 break;
8588 } else {
8589 search_start = 0;
8590 }
8591 }
8592 }
8593 }
8594
8595 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
8596 let snapshot = self
8597 .display_map
8598 .update(cx, |display_map, cx| display_map.snapshot(cx));
8599 let selection = self.selections.newest::<Point>(cx);
8600
8601 if !self.seek_in_direction(
8602 &snapshot,
8603 selection.head(),
8604 false,
8605 snapshot.buffer_snapshot.git_diff_hunks_in_range(
8606 MultiBufferRow(selection.head().row + 1)..MultiBufferRow::MAX,
8607 ),
8608 cx,
8609 ) {
8610 let wrapped_point = Point::zero();
8611 self.seek_in_direction(
8612 &snapshot,
8613 wrapped_point,
8614 true,
8615 snapshot.buffer_snapshot.git_diff_hunks_in_range(
8616 MultiBufferRow(wrapped_point.row + 1)..MultiBufferRow::MAX,
8617 ),
8618 cx,
8619 );
8620 }
8621 }
8622
8623 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
8624 let snapshot = self
8625 .display_map
8626 .update(cx, |display_map, cx| display_map.snapshot(cx));
8627 let selection = self.selections.newest::<Point>(cx);
8628
8629 if !self.seek_in_direction(
8630 &snapshot,
8631 selection.head(),
8632 false,
8633 snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
8634 MultiBufferRow(0)..MultiBufferRow(selection.head().row),
8635 ),
8636 cx,
8637 ) {
8638 let wrapped_point = snapshot.buffer_snapshot.max_point();
8639 self.seek_in_direction(
8640 &snapshot,
8641 wrapped_point,
8642 true,
8643 snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
8644 MultiBufferRow(0)..MultiBufferRow(wrapped_point.row),
8645 ),
8646 cx,
8647 );
8648 }
8649 }
8650
8651 fn seek_in_direction(
8652 &mut self,
8653 snapshot: &DisplaySnapshot,
8654 initial_point: Point,
8655 is_wrapped: bool,
8656 hunks: impl Iterator<Item = DiffHunk<MultiBufferRow>>,
8657 cx: &mut ViewContext<Editor>,
8658 ) -> bool {
8659 let display_point = initial_point.to_display_point(snapshot);
8660 let mut hunks = hunks
8661 .map(|hunk| diff_hunk_to_display(&hunk, &snapshot))
8662 .filter(|hunk| {
8663 if is_wrapped {
8664 true
8665 } else {
8666 !hunk.contains_display_row(display_point.row())
8667 }
8668 })
8669 .dedup();
8670
8671 if let Some(hunk) = hunks.next() {
8672 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8673 let row = hunk.start_display_row();
8674 let point = DisplayPoint::new(row, 0);
8675 s.select_display_ranges([point..point]);
8676 });
8677
8678 true
8679 } else {
8680 false
8681 }
8682 }
8683
8684 pub fn go_to_definition(
8685 &mut self,
8686 _: &GoToDefinition,
8687 cx: &mut ViewContext<Self>,
8688 ) -> Task<Result<bool>> {
8689 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx)
8690 }
8691
8692 pub fn go_to_implementation(
8693 &mut self,
8694 _: &GoToImplementation,
8695 cx: &mut ViewContext<Self>,
8696 ) -> Task<Result<bool>> {
8697 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx)
8698 }
8699
8700 pub fn go_to_implementation_split(
8701 &mut self,
8702 _: &GoToImplementationSplit,
8703 cx: &mut ViewContext<Self>,
8704 ) -> Task<Result<bool>> {
8705 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx)
8706 }
8707
8708 pub fn go_to_type_definition(
8709 &mut self,
8710 _: &GoToTypeDefinition,
8711 cx: &mut ViewContext<Self>,
8712 ) -> Task<Result<bool>> {
8713 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx)
8714 }
8715
8716 pub fn go_to_definition_split(
8717 &mut self,
8718 _: &GoToDefinitionSplit,
8719 cx: &mut ViewContext<Self>,
8720 ) -> Task<Result<bool>> {
8721 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx)
8722 }
8723
8724 pub fn go_to_type_definition_split(
8725 &mut self,
8726 _: &GoToTypeDefinitionSplit,
8727 cx: &mut ViewContext<Self>,
8728 ) -> Task<Result<bool>> {
8729 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx)
8730 }
8731
8732 fn go_to_definition_of_kind(
8733 &mut self,
8734 kind: GotoDefinitionKind,
8735 split: bool,
8736 cx: &mut ViewContext<Self>,
8737 ) -> Task<Result<bool>> {
8738 let Some(workspace) = self.workspace() else {
8739 return Task::ready(Ok(false));
8740 };
8741 let buffer = self.buffer.read(cx);
8742 let head = self.selections.newest::<usize>(cx).head();
8743 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
8744 text_anchor
8745 } else {
8746 return Task::ready(Ok(false));
8747 };
8748
8749 let project = workspace.read(cx).project().clone();
8750 let definitions = project.update(cx, |project, cx| match kind {
8751 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
8752 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
8753 GotoDefinitionKind::Implementation => project.implementation(&buffer, head, cx),
8754 });
8755
8756 cx.spawn(|editor, mut cx| async move {
8757 let definitions = definitions.await?;
8758 let navigated = editor
8759 .update(&mut cx, |editor, cx| {
8760 editor.navigate_to_hover_links(
8761 Some(kind),
8762 definitions
8763 .into_iter()
8764 .filter(|location| {
8765 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
8766 })
8767 .map(HoverLink::Text)
8768 .collect::<Vec<_>>(),
8769 split,
8770 cx,
8771 )
8772 })?
8773 .await?;
8774 anyhow::Ok(navigated)
8775 })
8776 }
8777
8778 pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext<Self>) {
8779 let position = self.selections.newest_anchor().head();
8780 let Some((buffer, buffer_position)) =
8781 self.buffer.read(cx).text_anchor_for_position(position, cx)
8782 else {
8783 return;
8784 };
8785
8786 cx.spawn(|editor, mut cx| async move {
8787 if let Some((_, url)) = find_url(&buffer, buffer_position, cx.clone()) {
8788 editor.update(&mut cx, |_, cx| {
8789 cx.open_url(&url);
8790 })
8791 } else {
8792 Ok(())
8793 }
8794 })
8795 .detach();
8796 }
8797
8798 pub(crate) fn navigate_to_hover_links(
8799 &mut self,
8800 kind: Option<GotoDefinitionKind>,
8801 mut definitions: Vec<HoverLink>,
8802 split: bool,
8803 cx: &mut ViewContext<Editor>,
8804 ) -> Task<Result<bool>> {
8805 // If there is one definition, just open it directly
8806 if definitions.len() == 1 {
8807 let definition = definitions.pop().unwrap();
8808 let target_task = match definition {
8809 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
8810 HoverLink::InlayHint(lsp_location, server_id) => {
8811 self.compute_target_location(lsp_location, server_id, cx)
8812 }
8813 HoverLink::Url(url) => {
8814 cx.open_url(&url);
8815 Task::ready(Ok(None))
8816 }
8817 };
8818 cx.spawn(|editor, mut cx| async move {
8819 let target = target_task.await.context("target resolution task")?;
8820 if let Some(target) = target {
8821 editor.update(&mut cx, |editor, cx| {
8822 let Some(workspace) = editor.workspace() else {
8823 return false;
8824 };
8825 let pane = workspace.read(cx).active_pane().clone();
8826
8827 let range = target.range.to_offset(target.buffer.read(cx));
8828 let range = editor.range_for_match(&range);
8829
8830 /// If select range has more than one line, we
8831 /// just point the cursor to range.start.
8832 fn check_multiline_range(
8833 buffer: &Buffer,
8834 range: Range<usize>,
8835 ) -> Range<usize> {
8836 if buffer.offset_to_point(range.start).row
8837 == buffer.offset_to_point(range.end).row
8838 {
8839 range
8840 } else {
8841 range.start..range.start
8842 }
8843 }
8844
8845 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
8846 let buffer = target.buffer.read(cx);
8847 let range = check_multiline_range(buffer, range);
8848 editor.change_selections(Some(Autoscroll::focused()), cx, |s| {
8849 s.select_ranges([range]);
8850 });
8851 } else {
8852 cx.window_context().defer(move |cx| {
8853 let target_editor: View<Self> =
8854 workspace.update(cx, |workspace, cx| {
8855 let pane = if split {
8856 workspace.adjacent_pane(cx)
8857 } else {
8858 workspace.active_pane().clone()
8859 };
8860
8861 workspace.open_project_item(pane, target.buffer.clone(), cx)
8862 });
8863 target_editor.update(cx, |target_editor, cx| {
8864 // When selecting a definition in a different buffer, disable the nav history
8865 // to avoid creating a history entry at the previous cursor location.
8866 pane.update(cx, |pane, _| pane.disable_history());
8867 let buffer = target.buffer.read(cx);
8868 let range = check_multiline_range(buffer, range);
8869 target_editor.change_selections(
8870 Some(Autoscroll::focused()),
8871 cx,
8872 |s| {
8873 s.select_ranges([range]);
8874 },
8875 );
8876 pane.update(cx, |pane, _| pane.enable_history());
8877 });
8878 });
8879 }
8880 true
8881 })
8882 } else {
8883 Ok(false)
8884 }
8885 })
8886 } else if !definitions.is_empty() {
8887 let replica_id = self.replica_id(cx);
8888 cx.spawn(|editor, mut cx| async move {
8889 let (title, location_tasks, workspace) = editor
8890 .update(&mut cx, |editor, cx| {
8891 let tab_kind = match kind {
8892 Some(GotoDefinitionKind::Implementation) => "Implementations",
8893 _ => "Definitions",
8894 };
8895 let title = definitions
8896 .iter()
8897 .find_map(|definition| match definition {
8898 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
8899 let buffer = origin.buffer.read(cx);
8900 format!(
8901 "{} for {}",
8902 tab_kind,
8903 buffer
8904 .text_for_range(origin.range.clone())
8905 .collect::<String>()
8906 )
8907 }),
8908 HoverLink::InlayHint(_, _) => None,
8909 HoverLink::Url(_) => None,
8910 })
8911 .unwrap_or(tab_kind.to_string());
8912 let location_tasks = definitions
8913 .into_iter()
8914 .map(|definition| match definition {
8915 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
8916 HoverLink::InlayHint(lsp_location, server_id) => {
8917 editor.compute_target_location(lsp_location, server_id, cx)
8918 }
8919 HoverLink::Url(_) => Task::ready(Ok(None)),
8920 })
8921 .collect::<Vec<_>>();
8922 (title, location_tasks, editor.workspace().clone())
8923 })
8924 .context("location tasks preparation")?;
8925
8926 let locations = futures::future::join_all(location_tasks)
8927 .await
8928 .into_iter()
8929 .filter_map(|location| location.transpose())
8930 .collect::<Result<_>>()
8931 .context("location tasks")?;
8932
8933 let Some(workspace) = workspace else {
8934 return Ok(false);
8935 };
8936 let opened = workspace
8937 .update(&mut cx, |workspace, cx| {
8938 Self::open_locations_in_multibuffer(
8939 workspace, locations, replica_id, title, split, cx,
8940 )
8941 })
8942 .ok();
8943
8944 anyhow::Ok(opened.is_some())
8945 })
8946 } else {
8947 Task::ready(Ok(false))
8948 }
8949 }
8950
8951 fn compute_target_location(
8952 &self,
8953 lsp_location: lsp::Location,
8954 server_id: LanguageServerId,
8955 cx: &mut ViewContext<Editor>,
8956 ) -> Task<anyhow::Result<Option<Location>>> {
8957 let Some(project) = self.project.clone() else {
8958 return Task::Ready(Some(Ok(None)));
8959 };
8960
8961 cx.spawn(move |editor, mut cx| async move {
8962 let location_task = editor.update(&mut cx, |editor, cx| {
8963 project.update(cx, |project, cx| {
8964 let language_server_name =
8965 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
8966 project
8967 .language_server_for_buffer(buffer.read(cx), server_id, cx)
8968 .map(|(lsp_adapter, _)| lsp_adapter.name.clone())
8969 });
8970 language_server_name.map(|language_server_name| {
8971 project.open_local_buffer_via_lsp(
8972 lsp::Uri::from(lsp_location.uri.clone()),
8973 server_id,
8974 language_server_name,
8975 cx,
8976 )
8977 })
8978 })
8979 })?;
8980 let location = match location_task {
8981 Some(task) => Some({
8982 let target_buffer_handle = task.await.context("open local buffer")?;
8983 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
8984 let target_start = target_buffer
8985 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
8986 let target_end = target_buffer
8987 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
8988 target_buffer.anchor_after(target_start)
8989 ..target_buffer.anchor_before(target_end)
8990 })?;
8991 Location {
8992 buffer: target_buffer_handle,
8993 range,
8994 }
8995 }),
8996 None => None,
8997 };
8998 Ok(location)
8999 })
9000 }
9001
9002 pub fn find_all_references(
9003 &mut self,
9004 _: &FindAllReferences,
9005 cx: &mut ViewContext<Self>,
9006 ) -> Option<Task<Result<()>>> {
9007 let multi_buffer = self.buffer.read(cx);
9008 let selection = self.selections.newest::<usize>(cx);
9009 let head = selection.head();
9010
9011 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
9012 let head_anchor = multi_buffer_snapshot.anchor_at(
9013 head,
9014 if head < selection.tail() {
9015 Bias::Right
9016 } else {
9017 Bias::Left
9018 },
9019 );
9020
9021 match self
9022 .find_all_references_task_sources
9023 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
9024 {
9025 Ok(_) => {
9026 log::info!(
9027 "Ignoring repeated FindAllReferences invocation with the position of already running task"
9028 );
9029 return None;
9030 }
9031 Err(i) => {
9032 self.find_all_references_task_sources.insert(i, head_anchor);
9033 }
9034 }
9035
9036 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
9037 let replica_id = self.replica_id(cx);
9038 let workspace = self.workspace()?;
9039 let project = workspace.read(cx).project().clone();
9040 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
9041 Some(cx.spawn(|editor, mut cx| async move {
9042 let _cleanup = defer({
9043 let mut cx = cx.clone();
9044 move || {
9045 let _ = editor.update(&mut cx, |editor, _| {
9046 if let Ok(i) =
9047 editor
9048 .find_all_references_task_sources
9049 .binary_search_by(|anchor| {
9050 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
9051 })
9052 {
9053 editor.find_all_references_task_sources.remove(i);
9054 }
9055 });
9056 }
9057 });
9058
9059 let locations = references.await?;
9060 if locations.is_empty() {
9061 return anyhow::Ok(());
9062 }
9063
9064 workspace.update(&mut cx, |workspace, cx| {
9065 let title = locations
9066 .first()
9067 .as_ref()
9068 .map(|location| {
9069 let buffer = location.buffer.read(cx);
9070 format!(
9071 "References to `{}`",
9072 buffer
9073 .text_for_range(location.range.clone())
9074 .collect::<String>()
9075 )
9076 })
9077 .unwrap();
9078 Self::open_locations_in_multibuffer(
9079 workspace, locations, replica_id, title, false, cx,
9080 );
9081 })
9082 }))
9083 }
9084
9085 /// Opens a multibuffer with the given project locations in it
9086 pub fn open_locations_in_multibuffer(
9087 workspace: &mut Workspace,
9088 mut locations: Vec<Location>,
9089 replica_id: ReplicaId,
9090 title: String,
9091 split: bool,
9092 cx: &mut ViewContext<Workspace>,
9093 ) {
9094 // If there are multiple definitions, open them in a multibuffer
9095 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
9096 let mut locations = locations.into_iter().peekable();
9097 let mut ranges_to_highlight = Vec::new();
9098 let capability = workspace.project().read(cx).capability();
9099
9100 let excerpt_buffer = cx.new_model(|cx| {
9101 let mut multibuffer = MultiBuffer::new(replica_id, capability);
9102 while let Some(location) = locations.next() {
9103 let buffer = location.buffer.read(cx);
9104 let mut ranges_for_buffer = Vec::new();
9105 let range = location.range.to_offset(buffer);
9106 ranges_for_buffer.push(range.clone());
9107
9108 while let Some(next_location) = locations.peek() {
9109 if next_location.buffer == location.buffer {
9110 ranges_for_buffer.push(next_location.range.to_offset(buffer));
9111 locations.next();
9112 } else {
9113 break;
9114 }
9115 }
9116
9117 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
9118 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
9119 location.buffer.clone(),
9120 ranges_for_buffer,
9121 DEFAULT_MULTIBUFFER_CONTEXT,
9122 cx,
9123 ))
9124 }
9125
9126 multibuffer.with_title(title)
9127 });
9128
9129 let editor = cx.new_view(|cx| {
9130 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), true, cx)
9131 });
9132 editor.update(cx, |editor, cx| {
9133 editor.highlight_background::<Self>(
9134 &ranges_to_highlight,
9135 |theme| theme.editor_highlighted_line_background,
9136 cx,
9137 );
9138 });
9139
9140 let item = Box::new(editor);
9141 let item_id = item.item_id();
9142
9143 if split {
9144 workspace.split_item(SplitDirection::Right, item.clone(), cx);
9145 } else {
9146 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
9147 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
9148 pane.close_current_preview_item(cx)
9149 } else {
9150 None
9151 }
9152 });
9153 workspace.add_item_to_active_pane(item.clone(), destination_index, cx);
9154 }
9155 workspace.active_pane().update(cx, |pane, cx| {
9156 pane.set_preview_item_id(Some(item_id), cx);
9157 });
9158 }
9159
9160 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9161 use language::ToOffset as _;
9162
9163 let project = self.project.clone()?;
9164 let selection = self.selections.newest_anchor().clone();
9165 let (cursor_buffer, cursor_buffer_position) = self
9166 .buffer
9167 .read(cx)
9168 .text_anchor_for_position(selection.head(), cx)?;
9169 let (tail_buffer, cursor_buffer_position_end) = self
9170 .buffer
9171 .read(cx)
9172 .text_anchor_for_position(selection.tail(), cx)?;
9173 if tail_buffer != cursor_buffer {
9174 return None;
9175 }
9176
9177 let snapshot = cursor_buffer.read(cx).snapshot();
9178 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
9179 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
9180 let prepare_rename = project.update(cx, |project, cx| {
9181 project.prepare_rename(cursor_buffer.clone(), cursor_buffer_offset, cx)
9182 });
9183 drop(snapshot);
9184
9185 Some(cx.spawn(|this, mut cx| async move {
9186 let rename_range = if let Some(range) = prepare_rename.await? {
9187 Some(range)
9188 } else {
9189 this.update(&mut cx, |this, cx| {
9190 let buffer = this.buffer.read(cx).snapshot(cx);
9191 let mut buffer_highlights = this
9192 .document_highlights_for_position(selection.head(), &buffer)
9193 .filter(|highlight| {
9194 highlight.start.excerpt_id == selection.head().excerpt_id
9195 && highlight.end.excerpt_id == selection.head().excerpt_id
9196 });
9197 buffer_highlights
9198 .next()
9199 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
9200 })?
9201 };
9202 if let Some(rename_range) = rename_range {
9203 this.update(&mut cx, |this, cx| {
9204 let snapshot = cursor_buffer.read(cx).snapshot();
9205 let rename_buffer_range = rename_range.to_offset(&snapshot);
9206 let cursor_offset_in_rename_range =
9207 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
9208 let cursor_offset_in_rename_range_end =
9209 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
9210
9211 this.take_rename(false, cx);
9212 let buffer = this.buffer.read(cx).read(cx);
9213 let cursor_offset = selection.head().to_offset(&buffer);
9214 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
9215 let rename_end = rename_start + rename_buffer_range.len();
9216 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
9217 let mut old_highlight_id = None;
9218 let old_name: Arc<str> = buffer
9219 .chunks(rename_start..rename_end, true)
9220 .map(|chunk| {
9221 if old_highlight_id.is_none() {
9222 old_highlight_id = chunk.syntax_highlight_id;
9223 }
9224 chunk.text
9225 })
9226 .collect::<String>()
9227 .into();
9228
9229 drop(buffer);
9230
9231 // Position the selection in the rename editor so that it matches the current selection.
9232 this.show_local_selections = false;
9233 let rename_editor = cx.new_view(|cx| {
9234 let mut editor = Editor::single_line(cx);
9235 editor.buffer.update(cx, |buffer, cx| {
9236 buffer.edit([(0..0, old_name.clone())], None, cx)
9237 });
9238 let rename_selection_range = match cursor_offset_in_rename_range
9239 .cmp(&cursor_offset_in_rename_range_end)
9240 {
9241 Ordering::Equal => {
9242 editor.select_all(&SelectAll, cx);
9243 return editor;
9244 }
9245 Ordering::Less => {
9246 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
9247 }
9248 Ordering::Greater => {
9249 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
9250 }
9251 };
9252 if rename_selection_range.end > old_name.len() {
9253 editor.select_all(&SelectAll, cx);
9254 } else {
9255 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
9256 s.select_ranges([rename_selection_range]);
9257 });
9258 }
9259 editor
9260 });
9261
9262 let write_highlights =
9263 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
9264 let read_highlights =
9265 this.clear_background_highlights::<DocumentHighlightRead>(cx);
9266 let ranges = write_highlights
9267 .iter()
9268 .flat_map(|(_, ranges)| ranges.iter())
9269 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
9270 .cloned()
9271 .collect();
9272
9273 this.highlight_text::<Rename>(
9274 ranges,
9275 HighlightStyle {
9276 fade_out: Some(0.6),
9277 ..Default::default()
9278 },
9279 cx,
9280 );
9281 let rename_focus_handle = rename_editor.focus_handle(cx);
9282 cx.focus(&rename_focus_handle);
9283 let block_id = this.insert_blocks(
9284 [BlockProperties {
9285 style: BlockStyle::Flex,
9286 position: range.start,
9287 height: 1,
9288 render: Box::new({
9289 let rename_editor = rename_editor.clone();
9290 move |cx: &mut BlockContext| {
9291 let mut text_style = cx.editor_style.text.clone();
9292 if let Some(highlight_style) = old_highlight_id
9293 .and_then(|h| h.style(&cx.editor_style.syntax))
9294 {
9295 text_style = text_style.highlight(highlight_style);
9296 }
9297 div()
9298 .pl(cx.anchor_x)
9299 .child(EditorElement::new(
9300 &rename_editor,
9301 EditorStyle {
9302 background: cx.theme().system().transparent,
9303 local_player: cx.editor_style.local_player,
9304 text: text_style,
9305 scrollbar_width: cx.editor_style.scrollbar_width,
9306 syntax: cx.editor_style.syntax.clone(),
9307 status: cx.editor_style.status.clone(),
9308 inlay_hints_style: HighlightStyle {
9309 color: Some(cx.theme().status().hint),
9310 font_weight: Some(FontWeight::BOLD),
9311 ..HighlightStyle::default()
9312 },
9313 suggestions_style: HighlightStyle {
9314 color: Some(cx.theme().status().predictive),
9315 ..HighlightStyle::default()
9316 },
9317 },
9318 ))
9319 .into_any_element()
9320 }
9321 }),
9322 disposition: BlockDisposition::Below,
9323 }],
9324 Some(Autoscroll::fit()),
9325 cx,
9326 )[0];
9327 this.pending_rename = Some(RenameState {
9328 range,
9329 old_name,
9330 editor: rename_editor,
9331 block_id,
9332 });
9333 })?;
9334 }
9335
9336 Ok(())
9337 }))
9338 }
9339
9340 pub fn confirm_rename(
9341 &mut self,
9342 _: &ConfirmRename,
9343 cx: &mut ViewContext<Self>,
9344 ) -> Option<Task<Result<()>>> {
9345 let rename = self.take_rename(false, cx)?;
9346 let workspace = self.workspace()?;
9347 let (start_buffer, start) = self
9348 .buffer
9349 .read(cx)
9350 .text_anchor_for_position(rename.range.start, cx)?;
9351 let (end_buffer, end) = self
9352 .buffer
9353 .read(cx)
9354 .text_anchor_for_position(rename.range.end, cx)?;
9355 if start_buffer != end_buffer {
9356 return None;
9357 }
9358
9359 let buffer = start_buffer;
9360 let range = start..end;
9361 let old_name = rename.old_name;
9362 let new_name = rename.editor.read(cx).text(cx);
9363
9364 let rename = workspace
9365 .read(cx)
9366 .project()
9367 .clone()
9368 .update(cx, |project, cx| {
9369 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
9370 });
9371 let workspace = workspace.downgrade();
9372
9373 Some(cx.spawn(|editor, mut cx| async move {
9374 let project_transaction = rename.await?;
9375 Self::open_project_transaction(
9376 &editor,
9377 workspace,
9378 project_transaction,
9379 format!("Rename: {} → {}", old_name, new_name),
9380 cx.clone(),
9381 )
9382 .await?;
9383
9384 editor.update(&mut cx, |editor, cx| {
9385 editor.refresh_document_highlights(cx);
9386 })?;
9387 Ok(())
9388 }))
9389 }
9390
9391 fn take_rename(
9392 &mut self,
9393 moving_cursor: bool,
9394 cx: &mut ViewContext<Self>,
9395 ) -> Option<RenameState> {
9396 let rename = self.pending_rename.take()?;
9397 if rename.editor.focus_handle(cx).is_focused(cx) {
9398 cx.focus(&self.focus_handle);
9399 }
9400
9401 self.remove_blocks(
9402 [rename.block_id].into_iter().collect(),
9403 Some(Autoscroll::fit()),
9404 cx,
9405 );
9406 self.clear_highlights::<Rename>(cx);
9407 self.show_local_selections = true;
9408
9409 if moving_cursor {
9410 let rename_editor = rename.editor.read(cx);
9411 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
9412
9413 // Update the selection to match the position of the selection inside
9414 // the rename editor.
9415 let snapshot = self.buffer.read(cx).read(cx);
9416 let rename_range = rename.range.to_offset(&snapshot);
9417 let cursor_in_editor = snapshot
9418 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
9419 .min(rename_range.end);
9420 drop(snapshot);
9421
9422 self.change_selections(None, cx, |s| {
9423 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
9424 });
9425 } else {
9426 self.refresh_document_highlights(cx);
9427 }
9428
9429 Some(rename)
9430 }
9431
9432 pub fn pending_rename(&self) -> Option<&RenameState> {
9433 self.pending_rename.as_ref()
9434 }
9435
9436 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9437 let project = match &self.project {
9438 Some(project) => project.clone(),
9439 None => return None,
9440 };
9441
9442 Some(self.perform_format(project, FormatTrigger::Manual, cx))
9443 }
9444
9445 fn perform_format(
9446 &mut self,
9447 project: Model<Project>,
9448 trigger: FormatTrigger,
9449 cx: &mut ViewContext<Self>,
9450 ) -> Task<Result<()>> {
9451 let buffer = self.buffer().clone();
9452 let mut buffers = buffer.read(cx).all_buffers();
9453 if trigger == FormatTrigger::Save {
9454 buffers.retain(|buffer| buffer.read(cx).is_dirty());
9455 }
9456
9457 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
9458 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
9459
9460 cx.spawn(|_, mut cx| async move {
9461 let transaction = futures::select_biased! {
9462 () = timeout => {
9463 log::warn!("timed out waiting for formatting");
9464 None
9465 }
9466 transaction = format.log_err().fuse() => transaction,
9467 };
9468
9469 buffer
9470 .update(&mut cx, |buffer, cx| {
9471 if let Some(transaction) = transaction {
9472 if !buffer.is_singleton() {
9473 buffer.push_transaction(&transaction.0, cx);
9474 }
9475 }
9476
9477 cx.notify();
9478 })
9479 .ok();
9480
9481 Ok(())
9482 })
9483 }
9484
9485 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
9486 if let Some(project) = self.project.clone() {
9487 self.buffer.update(cx, |multi_buffer, cx| {
9488 project.update(cx, |project, cx| {
9489 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
9490 });
9491 })
9492 }
9493 }
9494
9495 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
9496 cx.show_character_palette();
9497 }
9498
9499 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
9500 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
9501 let buffer = self.buffer.read(cx).snapshot(cx);
9502 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
9503 let is_valid = buffer
9504 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
9505 .any(|entry| {
9506 entry.diagnostic.is_primary
9507 && !entry.range.is_empty()
9508 && entry.range.start == primary_range_start
9509 && entry.diagnostic.message == active_diagnostics.primary_message
9510 });
9511
9512 if is_valid != active_diagnostics.is_valid {
9513 active_diagnostics.is_valid = is_valid;
9514 let mut new_styles = HashMap::default();
9515 for (block_id, diagnostic) in &active_diagnostics.blocks {
9516 new_styles.insert(
9517 *block_id,
9518 (
9519 None,
9520 diagnostic_block_renderer(diagnostic.clone(), is_valid),
9521 ),
9522 );
9523 }
9524 self.display_map.update(cx, |display_map, cx| {
9525 display_map.replace_blocks(new_styles, cx)
9526 });
9527 }
9528 }
9529 }
9530
9531 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
9532 self.dismiss_diagnostics(cx);
9533 let snapshot = self.snapshot(cx);
9534 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
9535 let buffer = self.buffer.read(cx).snapshot(cx);
9536
9537 let mut primary_range = None;
9538 let mut primary_message = None;
9539 let mut group_end = Point::zero();
9540 let diagnostic_group = buffer
9541 .diagnostic_group::<MultiBufferPoint>(group_id)
9542 .filter_map(|entry| {
9543 if snapshot.is_line_folded(MultiBufferRow(entry.range.start.row))
9544 && (entry.range.start.row == entry.range.end.row
9545 || snapshot.is_line_folded(MultiBufferRow(entry.range.end.row)))
9546 {
9547 return None;
9548 }
9549 if entry.range.end > group_end {
9550 group_end = entry.range.end;
9551 }
9552 if entry.diagnostic.is_primary {
9553 primary_range = Some(entry.range.clone());
9554 primary_message = Some(entry.diagnostic.message.clone());
9555 }
9556 Some(entry)
9557 })
9558 .collect::<Vec<_>>();
9559 let primary_range = primary_range?;
9560 let primary_message = primary_message?;
9561 let primary_range =
9562 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
9563
9564 let blocks = display_map
9565 .insert_blocks(
9566 diagnostic_group.iter().map(|entry| {
9567 let diagnostic = entry.diagnostic.clone();
9568 let message_height = diagnostic.message.matches('\n').count() as u8 + 1;
9569 BlockProperties {
9570 style: BlockStyle::Fixed,
9571 position: buffer.anchor_after(entry.range.start),
9572 height: message_height,
9573 render: diagnostic_block_renderer(diagnostic, true),
9574 disposition: BlockDisposition::Below,
9575 }
9576 }),
9577 cx,
9578 )
9579 .into_iter()
9580 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
9581 .collect();
9582
9583 Some(ActiveDiagnosticGroup {
9584 primary_range,
9585 primary_message,
9586 group_id,
9587 blocks,
9588 is_valid: true,
9589 })
9590 });
9591 self.active_diagnostics.is_some()
9592 }
9593
9594 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
9595 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
9596 self.display_map.update(cx, |display_map, cx| {
9597 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
9598 });
9599 cx.notify();
9600 }
9601 }
9602
9603 pub fn set_selections_from_remote(
9604 &mut self,
9605 selections: Vec<Selection<Anchor>>,
9606 pending_selection: Option<Selection<Anchor>>,
9607 cx: &mut ViewContext<Self>,
9608 ) {
9609 let old_cursor_position = self.selections.newest_anchor().head();
9610 self.selections.change_with(cx, |s| {
9611 s.select_anchors(selections);
9612 if let Some(pending_selection) = pending_selection {
9613 s.set_pending(pending_selection, SelectMode::Character);
9614 } else {
9615 s.clear_pending();
9616 }
9617 });
9618 self.selections_did_change(false, &old_cursor_position, true, cx);
9619 }
9620
9621 fn push_to_selection_history(&mut self) {
9622 self.selection_history.push(SelectionHistoryEntry {
9623 selections: self.selections.disjoint_anchors(),
9624 select_next_state: self.select_next_state.clone(),
9625 select_prev_state: self.select_prev_state.clone(),
9626 add_selections_state: self.add_selections_state.clone(),
9627 });
9628 }
9629
9630 pub fn transact(
9631 &mut self,
9632 cx: &mut ViewContext<Self>,
9633 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
9634 ) -> Option<TransactionId> {
9635 self.start_transaction_at(Instant::now(), cx);
9636 update(self, cx);
9637 self.end_transaction_at(Instant::now(), cx)
9638 }
9639
9640 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
9641 self.end_selection(cx);
9642 if let Some(tx_id) = self
9643 .buffer
9644 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
9645 {
9646 self.selection_history
9647 .insert_transaction(tx_id, self.selections.disjoint_anchors());
9648 cx.emit(EditorEvent::TransactionBegun {
9649 transaction_id: tx_id,
9650 })
9651 }
9652 }
9653
9654 fn end_transaction_at(
9655 &mut self,
9656 now: Instant,
9657 cx: &mut ViewContext<Self>,
9658 ) -> Option<TransactionId> {
9659 if let Some(transaction_id) = self
9660 .buffer
9661 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
9662 {
9663 if let Some((_, end_selections)) =
9664 self.selection_history.transaction_mut(transaction_id)
9665 {
9666 *end_selections = Some(self.selections.disjoint_anchors());
9667 } else {
9668 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
9669 }
9670
9671 cx.emit(EditorEvent::Edited { transaction_id });
9672 Some(transaction_id)
9673 } else {
9674 None
9675 }
9676 }
9677
9678 pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
9679 let mut fold_ranges = Vec::new();
9680
9681 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9682
9683 let selections = self.selections.all_adjusted(cx);
9684 for selection in selections {
9685 let range = selection.range().sorted();
9686 let buffer_start_row = range.start.row;
9687
9688 for row in (0..=range.end.row).rev() {
9689 if let Some((foldable_range, fold_text)) =
9690 display_map.foldable_range(MultiBufferRow(row))
9691 {
9692 if foldable_range.end.row >= buffer_start_row {
9693 fold_ranges.push((foldable_range, fold_text));
9694 if row <= range.start.row {
9695 break;
9696 }
9697 }
9698 }
9699 }
9700 }
9701
9702 self.fold_ranges(fold_ranges, true, cx);
9703 }
9704
9705 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
9706 let buffer_row = fold_at.buffer_row;
9707 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9708
9709 if let Some((fold_range, placeholder)) = display_map.foldable_range(buffer_row) {
9710 let autoscroll = self
9711 .selections
9712 .all::<Point>(cx)
9713 .iter()
9714 .any(|selection| fold_range.overlaps(&selection.range()));
9715
9716 self.fold_ranges([(fold_range, placeholder)], autoscroll, cx);
9717 }
9718 }
9719
9720 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
9721 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9722 let buffer = &display_map.buffer_snapshot;
9723 let selections = self.selections.all::<Point>(cx);
9724 let ranges = selections
9725 .iter()
9726 .map(|s| {
9727 let range = s.display_range(&display_map).sorted();
9728 let mut start = range.start.to_point(&display_map);
9729 let mut end = range.end.to_point(&display_map);
9730 start.column = 0;
9731 end.column = buffer.line_len(MultiBufferRow(end.row));
9732 start..end
9733 })
9734 .collect::<Vec<_>>();
9735
9736 self.unfold_ranges(ranges, true, true, cx);
9737 }
9738
9739 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
9740 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9741
9742 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
9743 ..Point::new(
9744 unfold_at.buffer_row.0,
9745 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
9746 );
9747
9748 let autoscroll = self
9749 .selections
9750 .all::<Point>(cx)
9751 .iter()
9752 .any(|selection| selection.range().overlaps(&intersection_range));
9753
9754 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
9755 }
9756
9757 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
9758 let selections = self.selections.all::<Point>(cx);
9759 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9760 let line_mode = self.selections.line_mode;
9761 let ranges = selections.into_iter().map(|s| {
9762 if line_mode {
9763 let start = Point::new(s.start.row, 0);
9764 let end = Point::new(
9765 s.end.row,
9766 display_map
9767 .buffer_snapshot
9768 .line_len(MultiBufferRow(s.end.row)),
9769 );
9770 (start..end, display_map.fold_placeholder.clone())
9771 } else {
9772 (s.start..s.end, display_map.fold_placeholder.clone())
9773 }
9774 });
9775 self.fold_ranges(ranges, true, cx);
9776 }
9777
9778 pub fn fold_ranges<T: ToOffset + Clone>(
9779 &mut self,
9780 ranges: impl IntoIterator<Item = (Range<T>, FoldPlaceholder)>,
9781 auto_scroll: bool,
9782 cx: &mut ViewContext<Self>,
9783 ) {
9784 let mut fold_ranges = Vec::new();
9785 let mut buffers_affected = HashMap::default();
9786 let multi_buffer = self.buffer().read(cx);
9787 for (fold_range, fold_text) in ranges {
9788 if let Some((_, buffer, _)) =
9789 multi_buffer.excerpt_containing(fold_range.start.clone(), cx)
9790 {
9791 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
9792 };
9793 fold_ranges.push((fold_range, fold_text));
9794 }
9795
9796 let mut ranges = fold_ranges.into_iter().peekable();
9797 if ranges.peek().is_some() {
9798 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
9799
9800 if auto_scroll {
9801 self.request_autoscroll(Autoscroll::fit(), cx);
9802 }
9803
9804 for buffer in buffers_affected.into_values() {
9805 self.sync_expanded_diff_hunks(buffer, cx);
9806 }
9807
9808 cx.notify();
9809
9810 if let Some(active_diagnostics) = self.active_diagnostics.take() {
9811 // Clear diagnostics block when folding a range that contains it.
9812 let snapshot = self.snapshot(cx);
9813 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
9814 drop(snapshot);
9815 self.active_diagnostics = Some(active_diagnostics);
9816 self.dismiss_diagnostics(cx);
9817 } else {
9818 self.active_diagnostics = Some(active_diagnostics);
9819 }
9820 }
9821
9822 self.scrollbar_marker_state.dirty = true;
9823 }
9824 }
9825
9826 pub fn unfold_ranges<T: ToOffset + Clone>(
9827 &mut self,
9828 ranges: impl IntoIterator<Item = Range<T>>,
9829 inclusive: bool,
9830 auto_scroll: bool,
9831 cx: &mut ViewContext<Self>,
9832 ) {
9833 let mut unfold_ranges = Vec::new();
9834 let mut buffers_affected = HashMap::default();
9835 let multi_buffer = self.buffer().read(cx);
9836 for range in ranges {
9837 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
9838 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
9839 };
9840 unfold_ranges.push(range);
9841 }
9842
9843 let mut ranges = unfold_ranges.into_iter().peekable();
9844 if ranges.peek().is_some() {
9845 self.display_map
9846 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
9847 if auto_scroll {
9848 self.request_autoscroll(Autoscroll::fit(), cx);
9849 }
9850
9851 for buffer in buffers_affected.into_values() {
9852 self.sync_expanded_diff_hunks(buffer, cx);
9853 }
9854
9855 cx.notify();
9856 self.scrollbar_marker_state.dirty = true;
9857 self.active_indent_guides_state.dirty = true;
9858 }
9859 }
9860
9861 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
9862 if hovered != self.gutter_hovered {
9863 self.gutter_hovered = hovered;
9864 cx.notify();
9865 }
9866 }
9867
9868 pub fn insert_blocks(
9869 &mut self,
9870 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
9871 autoscroll: Option<Autoscroll>,
9872 cx: &mut ViewContext<Self>,
9873 ) -> Vec<BlockId> {
9874 let blocks = self
9875 .display_map
9876 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
9877 if let Some(autoscroll) = autoscroll {
9878 self.request_autoscroll(autoscroll, cx);
9879 }
9880 blocks
9881 }
9882
9883 pub fn replace_blocks(
9884 &mut self,
9885 blocks: HashMap<BlockId, (Option<u8>, RenderBlock)>,
9886 autoscroll: Option<Autoscroll>,
9887 cx: &mut ViewContext<Self>,
9888 ) {
9889 self.display_map
9890 .update(cx, |display_map, cx| display_map.replace_blocks(blocks, cx));
9891 if let Some(autoscroll) = autoscroll {
9892 self.request_autoscroll(autoscroll, cx);
9893 }
9894 }
9895
9896 pub fn remove_blocks(
9897 &mut self,
9898 block_ids: HashSet<BlockId>,
9899 autoscroll: Option<Autoscroll>,
9900 cx: &mut ViewContext<Self>,
9901 ) {
9902 self.display_map.update(cx, |display_map, cx| {
9903 display_map.remove_blocks(block_ids, cx)
9904 });
9905 if let Some(autoscroll) = autoscroll {
9906 self.request_autoscroll(autoscroll, cx);
9907 }
9908 }
9909
9910 pub fn insert_flaps(
9911 &mut self,
9912 flaps: impl IntoIterator<Item = Flap>,
9913 cx: &mut ViewContext<Self>,
9914 ) -> Vec<FlapId> {
9915 self.display_map
9916 .update(cx, |map, cx| map.insert_flaps(flaps, cx))
9917 }
9918
9919 pub fn remove_flaps(
9920 &mut self,
9921 ids: impl IntoIterator<Item = FlapId>,
9922 cx: &mut ViewContext<Self>,
9923 ) {
9924 self.display_map
9925 .update(cx, |map, cx| map.remove_flaps(ids, cx));
9926 }
9927
9928 pub fn longest_row(&self, cx: &mut AppContext) -> DisplayRow {
9929 self.display_map
9930 .update(cx, |map, cx| map.snapshot(cx))
9931 .longest_row()
9932 }
9933
9934 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
9935 self.display_map
9936 .update(cx, |map, cx| map.snapshot(cx))
9937 .max_point()
9938 }
9939
9940 pub fn text(&self, cx: &AppContext) -> String {
9941 self.buffer.read(cx).read(cx).text()
9942 }
9943
9944 pub fn text_option(&self, cx: &AppContext) -> Option<String> {
9945 let text = self.text(cx);
9946 let text = text.trim();
9947
9948 if text.is_empty() {
9949 return None;
9950 }
9951
9952 Some(text.to_string())
9953 }
9954
9955 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
9956 self.transact(cx, |this, cx| {
9957 this.buffer
9958 .read(cx)
9959 .as_singleton()
9960 .expect("you can only call set_text on editors for singleton buffers")
9961 .update(cx, |buffer, cx| buffer.set_text(text, cx));
9962 });
9963 }
9964
9965 pub fn display_text(&self, cx: &mut AppContext) -> String {
9966 self.display_map
9967 .update(cx, |map, cx| map.snapshot(cx))
9968 .text()
9969 }
9970
9971 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
9972 let mut wrap_guides = smallvec::smallvec![];
9973
9974 if self.show_wrap_guides == Some(false) {
9975 return wrap_guides;
9976 }
9977
9978 let settings = self.buffer.read(cx).settings_at(0, cx);
9979 if settings.show_wrap_guides {
9980 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
9981 wrap_guides.push((soft_wrap as usize, true));
9982 }
9983 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
9984 }
9985
9986 wrap_guides
9987 }
9988
9989 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
9990 let settings = self.buffer.read(cx).settings_at(0, cx);
9991 let mode = self
9992 .soft_wrap_mode_override
9993 .unwrap_or_else(|| settings.soft_wrap);
9994 match mode {
9995 language_settings::SoftWrap::None => SoftWrap::None,
9996 language_settings::SoftWrap::PreferLine => SoftWrap::PreferLine,
9997 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
9998 language_settings::SoftWrap::PreferredLineLength => {
9999 SoftWrap::Column(settings.preferred_line_length)
10000 }
10001 }
10002 }
10003
10004 pub fn set_soft_wrap_mode(
10005 &mut self,
10006 mode: language_settings::SoftWrap,
10007 cx: &mut ViewContext<Self>,
10008 ) {
10009 self.soft_wrap_mode_override = Some(mode);
10010 cx.notify();
10011 }
10012
10013 pub fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
10014 let rem_size = cx.rem_size();
10015 self.display_map.update(cx, |map, cx| {
10016 map.set_font(
10017 style.text.font(),
10018 style.text.font_size.to_pixels(rem_size),
10019 cx,
10020 )
10021 });
10022 self.style = Some(style);
10023 }
10024
10025 pub fn style(&self) -> Option<&EditorStyle> {
10026 self.style.as_ref()
10027 }
10028
10029 // Called by the element. This method is not designed to be called outside of the editor
10030 // element's layout code because it does not notify when rewrapping is computed synchronously.
10031 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
10032 self.display_map
10033 .update(cx, |map, cx| map.set_wrap_width(width, cx))
10034 }
10035
10036 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
10037 if self.soft_wrap_mode_override.is_some() {
10038 self.soft_wrap_mode_override.take();
10039 } else {
10040 let soft_wrap = match self.soft_wrap_mode(cx) {
10041 SoftWrap::None | SoftWrap::PreferLine => language_settings::SoftWrap::EditorWidth,
10042 SoftWrap::EditorWidth | SoftWrap::Column(_) => {
10043 language_settings::SoftWrap::PreferLine
10044 }
10045 };
10046 self.soft_wrap_mode_override = Some(soft_wrap);
10047 }
10048 cx.notify();
10049 }
10050
10051 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, cx: &mut ViewContext<Self>) {
10052 let Some(workspace) = self.workspace() else {
10053 return;
10054 };
10055 let fs = workspace.read(cx).app_state().fs.clone();
10056 let current_show = TabBarSettings::get_global(cx).show;
10057 update_settings_file::<TabBarSettings>(fs, cx, move |setting| {
10058 setting.show = Some(!current_show);
10059 });
10060 }
10061
10062 pub fn toggle_indent_guides(&mut self, _: &ToggleIndentGuides, cx: &mut ViewContext<Self>) {
10063 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
10064 self.buffer
10065 .read(cx)
10066 .settings_at(0, cx)
10067 .indent_guides
10068 .enabled
10069 });
10070 self.show_indent_guides = Some(!currently_enabled);
10071 cx.notify();
10072 }
10073
10074 fn should_show_indent_guides(&self) -> Option<bool> {
10075 self.show_indent_guides
10076 }
10077
10078 pub fn toggle_line_numbers(&mut self, _: &ToggleLineNumbers, cx: &mut ViewContext<Self>) {
10079 let mut editor_settings = EditorSettings::get_global(cx).clone();
10080 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
10081 EditorSettings::override_global(editor_settings, cx);
10082 }
10083
10084 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
10085 self.show_gutter = show_gutter;
10086 cx.notify();
10087 }
10088
10089 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut ViewContext<Self>) {
10090 self.show_line_numbers = Some(show_line_numbers);
10091 cx.notify();
10092 }
10093
10094 pub fn set_show_git_diff_gutter(
10095 &mut self,
10096 show_git_diff_gutter: bool,
10097 cx: &mut ViewContext<Self>,
10098 ) {
10099 self.show_git_diff_gutter = Some(show_git_diff_gutter);
10100 cx.notify();
10101 }
10102
10103 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut ViewContext<Self>) {
10104 self.show_code_actions = Some(show_code_actions);
10105 cx.notify();
10106 }
10107
10108 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut ViewContext<Self>) {
10109 self.show_wrap_guides = Some(show_wrap_guides);
10110 cx.notify();
10111 }
10112
10113 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut ViewContext<Self>) {
10114 self.show_indent_guides = Some(show_indent_guides);
10115 cx.notify();
10116 }
10117
10118 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
10119 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10120 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10121 cx.reveal_path(&file.abs_path(cx));
10122 }
10123 }
10124 }
10125
10126 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
10127 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10128 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10129 if let Some(path) = file.abs_path(cx).to_str() {
10130 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
10131 }
10132 }
10133 }
10134 }
10135
10136 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
10137 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10138 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10139 if let Some(path) = file.path().to_str() {
10140 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
10141 }
10142 }
10143 }
10144 }
10145
10146 pub fn toggle_git_blame(&mut self, _: &ToggleGitBlame, cx: &mut ViewContext<Self>) {
10147 self.show_git_blame_gutter = !self.show_git_blame_gutter;
10148
10149 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
10150 self.start_git_blame(true, cx);
10151 }
10152
10153 cx.notify();
10154 }
10155
10156 pub fn toggle_git_blame_inline(
10157 &mut self,
10158 _: &ToggleGitBlameInline,
10159 cx: &mut ViewContext<Self>,
10160 ) {
10161 self.toggle_git_blame_inline_internal(true, cx);
10162 cx.notify();
10163 }
10164
10165 pub fn git_blame_inline_enabled(&self) -> bool {
10166 self.git_blame_inline_enabled
10167 }
10168
10169 fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
10170 if let Some(project) = self.project.as_ref() {
10171 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
10172 return;
10173 };
10174
10175 if buffer.read(cx).file().is_none() {
10176 return;
10177 }
10178
10179 let focused = self.focus_handle(cx).contains_focused(cx);
10180
10181 let project = project.clone();
10182 let blame =
10183 cx.new_model(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
10184 self.blame_subscription = Some(cx.observe(&blame, |_, _, cx| cx.notify()));
10185 self.blame = Some(blame);
10186 }
10187 }
10188
10189 fn toggle_git_blame_inline_internal(
10190 &mut self,
10191 user_triggered: bool,
10192 cx: &mut ViewContext<Self>,
10193 ) {
10194 if self.git_blame_inline_enabled {
10195 self.git_blame_inline_enabled = false;
10196 self.show_git_blame_inline = false;
10197 self.show_git_blame_inline_delay_task.take();
10198 } else {
10199 self.git_blame_inline_enabled = true;
10200 self.start_git_blame_inline(user_triggered, cx);
10201 }
10202
10203 cx.notify();
10204 }
10205
10206 fn start_git_blame_inline(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
10207 self.start_git_blame(user_triggered, cx);
10208
10209 if ProjectSettings::get_global(cx)
10210 .git
10211 .inline_blame_delay()
10212 .is_some()
10213 {
10214 self.start_inline_blame_timer(cx);
10215 } else {
10216 self.show_git_blame_inline = true
10217 }
10218 }
10219
10220 pub fn blame(&self) -> Option<&Model<GitBlame>> {
10221 self.blame.as_ref()
10222 }
10223
10224 pub fn render_git_blame_gutter(&mut self, cx: &mut WindowContext) -> bool {
10225 self.show_git_blame_gutter && self.has_blame_entries(cx)
10226 }
10227
10228 pub fn render_git_blame_inline(&mut self, cx: &mut WindowContext) -> bool {
10229 self.show_git_blame_inline
10230 && self.focus_handle.is_focused(cx)
10231 && !self.newest_selection_head_on_empty_line(cx)
10232 && self.has_blame_entries(cx)
10233 }
10234
10235 fn has_blame_entries(&self, cx: &mut WindowContext) -> bool {
10236 self.blame()
10237 .map_or(false, |blame| blame.read(cx).has_generated_entries())
10238 }
10239
10240 fn newest_selection_head_on_empty_line(&mut self, cx: &mut WindowContext) -> bool {
10241 let cursor_anchor = self.selections.newest_anchor().head();
10242
10243 let snapshot = self.buffer.read(cx).snapshot(cx);
10244 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
10245
10246 snapshot.line_len(buffer_row) == 0
10247 }
10248
10249 fn get_permalink_to_line(&mut self, cx: &mut ViewContext<Self>) -> Result<url::Url> {
10250 let (path, selection, repo) = maybe!({
10251 let project_handle = self.project.as_ref()?.clone();
10252 let project = project_handle.read(cx);
10253
10254 let selection = self.selections.newest::<Point>(cx);
10255 let selection_range = selection.range();
10256
10257 let (buffer, selection) = if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10258 (buffer, selection_range.start.row..selection_range.end.row)
10259 } else {
10260 let buffer_ranges = self
10261 .buffer()
10262 .read(cx)
10263 .range_to_buffer_ranges(selection_range, cx);
10264
10265 let (buffer, range, _) = if selection.reversed {
10266 buffer_ranges.first()
10267 } else {
10268 buffer_ranges.last()
10269 }?;
10270
10271 let snapshot = buffer.read(cx).snapshot();
10272 let selection = text::ToPoint::to_point(&range.start, &snapshot).row
10273 ..text::ToPoint::to_point(&range.end, &snapshot).row;
10274 (buffer.clone(), selection)
10275 };
10276
10277 let path = buffer
10278 .read(cx)
10279 .file()?
10280 .as_local()?
10281 .path()
10282 .to_str()?
10283 .to_string();
10284 let repo = project.get_repo(&buffer.read(cx).project_path(cx)?, cx)?;
10285 Some((path, selection, repo))
10286 })
10287 .ok_or_else(|| anyhow!("unable to open git repository"))?;
10288
10289 const REMOTE_NAME: &str = "origin";
10290 let origin_url = repo
10291 .remote_url(REMOTE_NAME)
10292 .ok_or_else(|| anyhow!("remote \"{REMOTE_NAME}\" not found"))?;
10293 let sha = repo
10294 .head_sha()
10295 .ok_or_else(|| anyhow!("failed to read HEAD SHA"))?;
10296
10297 let (provider, remote) =
10298 parse_git_remote_url(GitHostingProviderRegistry::default_global(cx), &origin_url)
10299 .ok_or_else(|| anyhow!("failed to parse Git remote URL"))?;
10300
10301 Ok(provider.build_permalink(
10302 remote,
10303 BuildPermalinkParams {
10304 sha: &sha,
10305 path: &path,
10306 selection: Some(selection),
10307 },
10308 ))
10309 }
10310
10311 pub fn copy_permalink_to_line(&mut self, _: &CopyPermalinkToLine, cx: &mut ViewContext<Self>) {
10312 let permalink = self.get_permalink_to_line(cx);
10313
10314 match permalink {
10315 Ok(permalink) => {
10316 cx.write_to_clipboard(ClipboardItem::new(permalink.to_string()));
10317 }
10318 Err(err) => {
10319 let message = format!("Failed to copy permalink: {err}");
10320
10321 Err::<(), anyhow::Error>(err).log_err();
10322
10323 if let Some(workspace) = self.workspace() {
10324 workspace.update(cx, |workspace, cx| {
10325 struct CopyPermalinkToLine;
10326
10327 workspace.show_toast(
10328 Toast::new(NotificationId::unique::<CopyPermalinkToLine>(), message),
10329 cx,
10330 )
10331 })
10332 }
10333 }
10334 }
10335 }
10336
10337 pub fn open_permalink_to_line(&mut self, _: &OpenPermalinkToLine, cx: &mut ViewContext<Self>) {
10338 let permalink = self.get_permalink_to_line(cx);
10339
10340 match permalink {
10341 Ok(permalink) => {
10342 cx.open_url(permalink.as_ref());
10343 }
10344 Err(err) => {
10345 let message = format!("Failed to open permalink: {err}");
10346
10347 Err::<(), anyhow::Error>(err).log_err();
10348
10349 if let Some(workspace) = self.workspace() {
10350 workspace.update(cx, |workspace, cx| {
10351 struct OpenPermalinkToLine;
10352
10353 workspace.show_toast(
10354 Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
10355 cx,
10356 )
10357 })
10358 }
10359 }
10360 }
10361 }
10362
10363 /// Adds or removes (on `None` color) a highlight for the rows corresponding to the anchor range given.
10364 /// On matching anchor range, replaces the old highlight; does not clear the other existing highlights.
10365 /// If multiple anchor ranges will produce highlights for the same row, the last range added will be used.
10366 pub fn highlight_rows<T: 'static>(
10367 &mut self,
10368 rows: RangeInclusive<Anchor>,
10369 color: Option<Hsla>,
10370 should_autoscroll: bool,
10371 cx: &mut ViewContext<Self>,
10372 ) {
10373 let snapshot = self.buffer().read(cx).snapshot(cx);
10374 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
10375 let existing_highlight_index = row_highlights.binary_search_by(|highlight| {
10376 highlight
10377 .range
10378 .start()
10379 .cmp(&rows.start(), &snapshot)
10380 .then(highlight.range.end().cmp(&rows.end(), &snapshot))
10381 });
10382 match (color, existing_highlight_index) {
10383 (Some(_), Ok(ix)) | (_, Err(ix)) => row_highlights.insert(
10384 ix,
10385 RowHighlight {
10386 index: post_inc(&mut self.highlight_order),
10387 range: rows,
10388 should_autoscroll,
10389 color,
10390 },
10391 ),
10392 (None, Ok(i)) => {
10393 row_highlights.remove(i);
10394 }
10395 }
10396 }
10397
10398 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
10399 pub fn clear_row_highlights<T: 'static>(&mut self) {
10400 self.highlighted_rows.remove(&TypeId::of::<T>());
10401 }
10402
10403 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
10404 pub fn highlighted_rows<T: 'static>(
10405 &self,
10406 ) -> Option<impl Iterator<Item = (&RangeInclusive<Anchor>, Option<&Hsla>)>> {
10407 Some(
10408 self.highlighted_rows
10409 .get(&TypeId::of::<T>())?
10410 .iter()
10411 .map(|highlight| (&highlight.range, highlight.color.as_ref())),
10412 )
10413 }
10414
10415 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
10416 /// Rerturns a map of display rows that are highlighted and their corresponding highlight color.
10417 /// Allows to ignore certain kinds of highlights.
10418 pub fn highlighted_display_rows(
10419 &mut self,
10420 cx: &mut WindowContext,
10421 ) -> BTreeMap<DisplayRow, Hsla> {
10422 let snapshot = self.snapshot(cx);
10423 let mut used_highlight_orders = HashMap::default();
10424 self.highlighted_rows
10425 .iter()
10426 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
10427 .fold(
10428 BTreeMap::<DisplayRow, Hsla>::new(),
10429 |mut unique_rows, highlight| {
10430 let start_row = highlight.range.start().to_display_point(&snapshot).row();
10431 let end_row = highlight.range.end().to_display_point(&snapshot).row();
10432 for row in start_row.0..=end_row.0 {
10433 let used_index =
10434 used_highlight_orders.entry(row).or_insert(highlight.index);
10435 if highlight.index >= *used_index {
10436 *used_index = highlight.index;
10437 match highlight.color {
10438 Some(hsla) => unique_rows.insert(DisplayRow(row), hsla),
10439 None => unique_rows.remove(&DisplayRow(row)),
10440 };
10441 }
10442 }
10443 unique_rows
10444 },
10445 )
10446 }
10447
10448 pub fn highlighted_display_row_for_autoscroll(
10449 &self,
10450 snapshot: &DisplaySnapshot,
10451 ) -> Option<DisplayRow> {
10452 self.highlighted_rows
10453 .values()
10454 .flat_map(|highlighted_rows| highlighted_rows.iter())
10455 .filter_map(|highlight| {
10456 if highlight.color.is_none() || !highlight.should_autoscroll {
10457 return None;
10458 }
10459 Some(highlight.range.start().to_display_point(&snapshot).row())
10460 })
10461 .min()
10462 }
10463
10464 pub fn set_search_within_ranges(
10465 &mut self,
10466 ranges: &[Range<Anchor>],
10467 cx: &mut ViewContext<Self>,
10468 ) {
10469 self.highlight_background::<SearchWithinRange>(
10470 ranges,
10471 |colors| colors.editor_document_highlight_read_background,
10472 cx,
10473 )
10474 }
10475
10476 pub fn clear_search_within_ranges(&mut self, cx: &mut ViewContext<Self>) {
10477 self.clear_background_highlights::<SearchWithinRange>(cx);
10478 }
10479
10480 pub fn highlight_background<T: 'static>(
10481 &mut self,
10482 ranges: &[Range<Anchor>],
10483 color_fetcher: fn(&ThemeColors) -> Hsla,
10484 cx: &mut ViewContext<Self>,
10485 ) {
10486 let snapshot = self.snapshot(cx);
10487 // this is to try and catch a panic sooner
10488 for range in ranges {
10489 snapshot
10490 .buffer_snapshot
10491 .summary_for_anchor::<usize>(&range.start);
10492 snapshot
10493 .buffer_snapshot
10494 .summary_for_anchor::<usize>(&range.end);
10495 }
10496
10497 self.background_highlights
10498 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
10499 self.scrollbar_marker_state.dirty = true;
10500 cx.notify();
10501 }
10502
10503 pub fn clear_background_highlights<T: 'static>(
10504 &mut self,
10505 cx: &mut ViewContext<Self>,
10506 ) -> Option<BackgroundHighlight> {
10507 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
10508 if !text_highlights.1.is_empty() {
10509 self.scrollbar_marker_state.dirty = true;
10510 cx.notify();
10511 }
10512 Some(text_highlights)
10513 }
10514
10515 pub fn highlight_gutter<T: 'static>(
10516 &mut self,
10517 ranges: &[Range<Anchor>],
10518 color_fetcher: fn(&AppContext) -> Hsla,
10519 cx: &mut ViewContext<Self>,
10520 ) {
10521 self.gutter_highlights
10522 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
10523 cx.notify();
10524 }
10525
10526 pub fn clear_gutter_highlights<T: 'static>(
10527 &mut self,
10528 cx: &mut ViewContext<Self>,
10529 ) -> Option<GutterHighlight> {
10530 cx.notify();
10531 self.gutter_highlights.remove(&TypeId::of::<T>())
10532 }
10533
10534 #[cfg(feature = "test-support")]
10535 pub fn all_text_background_highlights(
10536 &mut self,
10537 cx: &mut ViewContext<Self>,
10538 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
10539 let snapshot = self.snapshot(cx);
10540 let buffer = &snapshot.buffer_snapshot;
10541 let start = buffer.anchor_before(0);
10542 let end = buffer.anchor_after(buffer.len());
10543 let theme = cx.theme().colors();
10544 self.background_highlights_in_range(start..end, &snapshot, theme)
10545 }
10546
10547 #[cfg(feature = "test-support")]
10548 pub fn search_background_highlights(
10549 &mut self,
10550 cx: &mut ViewContext<Self>,
10551 ) -> Vec<Range<Point>> {
10552 let snapshot = self.buffer().read(cx).snapshot(cx);
10553
10554 let highlights = self
10555 .background_highlights
10556 .get(&TypeId::of::<items::BufferSearchHighlights>());
10557
10558 if let Some((_color, ranges)) = highlights {
10559 ranges
10560 .iter()
10561 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
10562 .collect_vec()
10563 } else {
10564 vec![]
10565 }
10566 }
10567
10568 fn document_highlights_for_position<'a>(
10569 &'a self,
10570 position: Anchor,
10571 buffer: &'a MultiBufferSnapshot,
10572 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
10573 let read_highlights = self
10574 .background_highlights
10575 .get(&TypeId::of::<DocumentHighlightRead>())
10576 .map(|h| &h.1);
10577 let write_highlights = self
10578 .background_highlights
10579 .get(&TypeId::of::<DocumentHighlightWrite>())
10580 .map(|h| &h.1);
10581 let left_position = position.bias_left(buffer);
10582 let right_position = position.bias_right(buffer);
10583 read_highlights
10584 .into_iter()
10585 .chain(write_highlights)
10586 .flat_map(move |ranges| {
10587 let start_ix = match ranges.binary_search_by(|probe| {
10588 let cmp = probe.end.cmp(&left_position, buffer);
10589 if cmp.is_ge() {
10590 Ordering::Greater
10591 } else {
10592 Ordering::Less
10593 }
10594 }) {
10595 Ok(i) | Err(i) => i,
10596 };
10597
10598 ranges[start_ix..]
10599 .iter()
10600 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
10601 })
10602 }
10603
10604 pub fn has_background_highlights<T: 'static>(&self) -> bool {
10605 self.background_highlights
10606 .get(&TypeId::of::<T>())
10607 .map_or(false, |(_, highlights)| !highlights.is_empty())
10608 }
10609
10610 pub fn background_highlights_in_range(
10611 &self,
10612 search_range: Range<Anchor>,
10613 display_snapshot: &DisplaySnapshot,
10614 theme: &ThemeColors,
10615 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
10616 let mut results = Vec::new();
10617 for (color_fetcher, ranges) in self.background_highlights.values() {
10618 let color = color_fetcher(theme);
10619 let start_ix = match ranges.binary_search_by(|probe| {
10620 let cmp = probe
10621 .end
10622 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
10623 if cmp.is_gt() {
10624 Ordering::Greater
10625 } else {
10626 Ordering::Less
10627 }
10628 }) {
10629 Ok(i) | Err(i) => i,
10630 };
10631 for range in &ranges[start_ix..] {
10632 if range
10633 .start
10634 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
10635 .is_ge()
10636 {
10637 break;
10638 }
10639
10640 let start = range.start.to_display_point(&display_snapshot);
10641 let end = range.end.to_display_point(&display_snapshot);
10642 results.push((start..end, color))
10643 }
10644 }
10645 results
10646 }
10647
10648 pub fn background_highlight_row_ranges<T: 'static>(
10649 &self,
10650 search_range: Range<Anchor>,
10651 display_snapshot: &DisplaySnapshot,
10652 count: usize,
10653 ) -> Vec<RangeInclusive<DisplayPoint>> {
10654 let mut results = Vec::new();
10655 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
10656 return vec![];
10657 };
10658
10659 let start_ix = match ranges.binary_search_by(|probe| {
10660 let cmp = probe
10661 .end
10662 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
10663 if cmp.is_gt() {
10664 Ordering::Greater
10665 } else {
10666 Ordering::Less
10667 }
10668 }) {
10669 Ok(i) | Err(i) => i,
10670 };
10671 let mut push_region = |start: Option<Point>, end: Option<Point>| {
10672 if let (Some(start_display), Some(end_display)) = (start, end) {
10673 results.push(
10674 start_display.to_display_point(display_snapshot)
10675 ..=end_display.to_display_point(display_snapshot),
10676 );
10677 }
10678 };
10679 let mut start_row: Option<Point> = None;
10680 let mut end_row: Option<Point> = None;
10681 if ranges.len() > count {
10682 return Vec::new();
10683 }
10684 for range in &ranges[start_ix..] {
10685 if range
10686 .start
10687 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
10688 .is_ge()
10689 {
10690 break;
10691 }
10692 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
10693 if let Some(current_row) = &end_row {
10694 if end.row == current_row.row {
10695 continue;
10696 }
10697 }
10698 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
10699 if start_row.is_none() {
10700 assert_eq!(end_row, None);
10701 start_row = Some(start);
10702 end_row = Some(end);
10703 continue;
10704 }
10705 if let Some(current_end) = end_row.as_mut() {
10706 if start.row > current_end.row + 1 {
10707 push_region(start_row, end_row);
10708 start_row = Some(start);
10709 end_row = Some(end);
10710 } else {
10711 // Merge two hunks.
10712 *current_end = end;
10713 }
10714 } else {
10715 unreachable!();
10716 }
10717 }
10718 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
10719 push_region(start_row, end_row);
10720 results
10721 }
10722
10723 pub fn gutter_highlights_in_range(
10724 &self,
10725 search_range: Range<Anchor>,
10726 display_snapshot: &DisplaySnapshot,
10727 cx: &AppContext,
10728 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
10729 let mut results = Vec::new();
10730 for (color_fetcher, ranges) in self.gutter_highlights.values() {
10731 let color = color_fetcher(cx);
10732 let start_ix = match ranges.binary_search_by(|probe| {
10733 let cmp = probe
10734 .end
10735 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
10736 if cmp.is_gt() {
10737 Ordering::Greater
10738 } else {
10739 Ordering::Less
10740 }
10741 }) {
10742 Ok(i) | Err(i) => i,
10743 };
10744 for range in &ranges[start_ix..] {
10745 if range
10746 .start
10747 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
10748 .is_ge()
10749 {
10750 break;
10751 }
10752
10753 let start = range.start.to_display_point(&display_snapshot);
10754 let end = range.end.to_display_point(&display_snapshot);
10755 results.push((start..end, color))
10756 }
10757 }
10758 results
10759 }
10760
10761 /// Get the text ranges corresponding to the redaction query
10762 pub fn redacted_ranges(
10763 &self,
10764 search_range: Range<Anchor>,
10765 display_snapshot: &DisplaySnapshot,
10766 cx: &WindowContext,
10767 ) -> Vec<Range<DisplayPoint>> {
10768 display_snapshot
10769 .buffer_snapshot
10770 .redacted_ranges(search_range, |file| {
10771 if let Some(file) = file {
10772 file.is_private()
10773 && EditorSettings::get(Some(file.as_ref().into()), cx).redact_private_values
10774 } else {
10775 false
10776 }
10777 })
10778 .map(|range| {
10779 range.start.to_display_point(display_snapshot)
10780 ..range.end.to_display_point(display_snapshot)
10781 })
10782 .collect()
10783 }
10784
10785 pub fn highlight_text<T: 'static>(
10786 &mut self,
10787 ranges: Vec<Range<Anchor>>,
10788 style: HighlightStyle,
10789 cx: &mut ViewContext<Self>,
10790 ) {
10791 self.display_map.update(cx, |map, _| {
10792 map.highlight_text(TypeId::of::<T>(), ranges, style)
10793 });
10794 cx.notify();
10795 }
10796
10797 pub(crate) fn highlight_inlays<T: 'static>(
10798 &mut self,
10799 highlights: Vec<InlayHighlight>,
10800 style: HighlightStyle,
10801 cx: &mut ViewContext<Self>,
10802 ) {
10803 self.display_map.update(cx, |map, _| {
10804 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
10805 });
10806 cx.notify();
10807 }
10808
10809 pub fn text_highlights<'a, T: 'static>(
10810 &'a self,
10811 cx: &'a AppContext,
10812 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
10813 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
10814 }
10815
10816 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
10817 let cleared = self
10818 .display_map
10819 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
10820 if cleared {
10821 cx.notify();
10822 }
10823 }
10824
10825 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
10826 (self.read_only(cx) || self.blink_manager.read(cx).visible())
10827 && self.focus_handle.is_focused(cx)
10828 }
10829
10830 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
10831 cx.notify();
10832 }
10833
10834 fn on_buffer_event(
10835 &mut self,
10836 multibuffer: Model<MultiBuffer>,
10837 event: &multi_buffer::Event,
10838 cx: &mut ViewContext<Self>,
10839 ) {
10840 match event {
10841 multi_buffer::Event::Edited {
10842 singleton_buffer_edited,
10843 } => {
10844 self.scrollbar_marker_state.dirty = true;
10845 self.active_indent_guides_state.dirty = true;
10846 self.refresh_active_diagnostics(cx);
10847 self.refresh_code_actions(cx);
10848 if self.has_active_inline_completion(cx) {
10849 self.update_visible_inline_completion(cx);
10850 }
10851 cx.emit(EditorEvent::BufferEdited);
10852 cx.emit(SearchEvent::MatchesInvalidated);
10853 if *singleton_buffer_edited {
10854 if let Some(project) = &self.project {
10855 let project = project.read(cx);
10856 let languages_affected = multibuffer
10857 .read(cx)
10858 .all_buffers()
10859 .into_iter()
10860 .filter_map(|buffer| {
10861 let buffer = buffer.read(cx);
10862 let language = buffer.language()?;
10863 if project.is_local()
10864 && project.language_servers_for_buffer(buffer, cx).count() == 0
10865 {
10866 None
10867 } else {
10868 Some(language)
10869 }
10870 })
10871 .cloned()
10872 .collect::<HashSet<_>>();
10873 if !languages_affected.is_empty() {
10874 self.refresh_inlay_hints(
10875 InlayHintRefreshReason::BufferEdited(languages_affected),
10876 cx,
10877 );
10878 }
10879 }
10880 }
10881
10882 let Some(project) = &self.project else { return };
10883 let telemetry = project.read(cx).client().telemetry().clone();
10884 refresh_linked_ranges(self, cx);
10885 telemetry.log_edit_event("editor");
10886 }
10887 multi_buffer::Event::ExcerptsAdded {
10888 buffer,
10889 predecessor,
10890 excerpts,
10891 } => {
10892 self.tasks_update_task = Some(self.refresh_runnables(cx));
10893 cx.emit(EditorEvent::ExcerptsAdded {
10894 buffer: buffer.clone(),
10895 predecessor: *predecessor,
10896 excerpts: excerpts.clone(),
10897 });
10898 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
10899 }
10900 multi_buffer::Event::ExcerptsRemoved { ids } => {
10901 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
10902 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
10903 }
10904 multi_buffer::Event::ExcerptsEdited { ids } => {
10905 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
10906 }
10907 multi_buffer::Event::ExcerptsExpanded { ids } => {
10908 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
10909 }
10910 multi_buffer::Event::Reparsed(buffer_id) => {
10911 self.tasks_update_task = Some(self.refresh_runnables(cx));
10912
10913 cx.emit(EditorEvent::Reparsed(*buffer_id));
10914 }
10915 multi_buffer::Event::LanguageChanged(buffer_id) => {
10916 linked_editing_ranges::refresh_linked_ranges(self, cx);
10917 cx.emit(EditorEvent::Reparsed(*buffer_id));
10918 cx.notify();
10919 }
10920 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
10921 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
10922 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
10923 cx.emit(EditorEvent::TitleChanged)
10924 }
10925 multi_buffer::Event::DiffBaseChanged => {
10926 self.scrollbar_marker_state.dirty = true;
10927 cx.emit(EditorEvent::DiffBaseChanged);
10928 cx.notify();
10929 }
10930 multi_buffer::Event::DiffUpdated { buffer } => {
10931 self.sync_expanded_diff_hunks(buffer.clone(), cx);
10932 cx.notify();
10933 }
10934 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
10935 multi_buffer::Event::DiagnosticsUpdated => {
10936 self.refresh_active_diagnostics(cx);
10937 self.scrollbar_marker_state.dirty = true;
10938 cx.notify();
10939 }
10940 _ => {}
10941 };
10942 }
10943
10944 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
10945 cx.notify();
10946 }
10947
10948 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
10949 self.refresh_inline_completion(true, cx);
10950 self.refresh_inlay_hints(
10951 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
10952 self.selections.newest_anchor().head(),
10953 &self.buffer.read(cx).snapshot(cx),
10954 cx,
10955 )),
10956 cx,
10957 );
10958 let editor_settings = EditorSettings::get_global(cx);
10959 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
10960 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
10961
10962 if self.mode == EditorMode::Full {
10963 let inline_blame_enabled = ProjectSettings::get_global(cx).git.inline_blame_enabled();
10964 if self.git_blame_inline_enabled != inline_blame_enabled {
10965 self.toggle_git_blame_inline_internal(false, cx);
10966 }
10967 }
10968
10969 cx.notify();
10970 }
10971
10972 pub fn set_searchable(&mut self, searchable: bool) {
10973 self.searchable = searchable;
10974 }
10975
10976 pub fn searchable(&self) -> bool {
10977 self.searchable
10978 }
10979
10980 fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext<Self>) {
10981 self.open_excerpts_common(true, cx)
10982 }
10983
10984 fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
10985 self.open_excerpts_common(false, cx)
10986 }
10987
10988 fn open_excerpts_common(&mut self, split: bool, cx: &mut ViewContext<Self>) {
10989 let buffer = self.buffer.read(cx);
10990 if buffer.is_singleton() {
10991 cx.propagate();
10992 return;
10993 }
10994
10995 let Some(workspace) = self.workspace() else {
10996 cx.propagate();
10997 return;
10998 };
10999
11000 let mut new_selections_by_buffer = HashMap::default();
11001 for selection in self.selections.all::<usize>(cx) {
11002 for (buffer, mut range, _) in
11003 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
11004 {
11005 if selection.reversed {
11006 mem::swap(&mut range.start, &mut range.end);
11007 }
11008 new_selections_by_buffer
11009 .entry(buffer)
11010 .or_insert(Vec::new())
11011 .push(range)
11012 }
11013 }
11014
11015 // We defer the pane interaction because we ourselves are a workspace item
11016 // and activating a new item causes the pane to call a method on us reentrantly,
11017 // which panics if we're on the stack.
11018 cx.window_context().defer(move |cx| {
11019 workspace.update(cx, |workspace, cx| {
11020 let pane = if split {
11021 workspace.adjacent_pane(cx)
11022 } else {
11023 workspace.active_pane().clone()
11024 };
11025
11026 for (buffer, ranges) in new_selections_by_buffer {
11027 let editor = workspace.open_project_item::<Self>(pane.clone(), buffer, cx);
11028 editor.update(cx, |editor, cx| {
11029 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
11030 s.select_ranges(ranges);
11031 });
11032 });
11033 }
11034 })
11035 });
11036 }
11037
11038 fn jump(
11039 &mut self,
11040 path: ProjectPath,
11041 position: Point,
11042 anchor: language::Anchor,
11043 offset_from_top: u32,
11044 cx: &mut ViewContext<Self>,
11045 ) {
11046 let workspace = self.workspace();
11047 cx.spawn(|_, mut cx| async move {
11048 let workspace = workspace.ok_or_else(|| anyhow!("cannot jump without workspace"))?;
11049 let editor = workspace.update(&mut cx, |workspace, cx| {
11050 // Reset the preview item id before opening the new item
11051 workspace.active_pane().update(cx, |pane, cx| {
11052 pane.set_preview_item_id(None, cx);
11053 });
11054 workspace.open_path_preview(path, None, true, true, cx)
11055 })?;
11056 let editor = editor
11057 .await?
11058 .downcast::<Editor>()
11059 .ok_or_else(|| anyhow!("opened item was not an editor"))?
11060 .downgrade();
11061 editor.update(&mut cx, |editor, cx| {
11062 let buffer = editor
11063 .buffer()
11064 .read(cx)
11065 .as_singleton()
11066 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
11067 let buffer = buffer.read(cx);
11068 let cursor = if buffer.can_resolve(&anchor) {
11069 language::ToPoint::to_point(&anchor, buffer)
11070 } else {
11071 buffer.clip_point(position, Bias::Left)
11072 };
11073
11074 let nav_history = editor.nav_history.take();
11075 editor.change_selections(
11076 Some(Autoscroll::top_relative(offset_from_top as usize)),
11077 cx,
11078 |s| {
11079 s.select_ranges([cursor..cursor]);
11080 },
11081 );
11082 editor.nav_history = nav_history;
11083
11084 anyhow::Ok(())
11085 })??;
11086
11087 anyhow::Ok(())
11088 })
11089 .detach_and_log_err(cx);
11090 }
11091
11092 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
11093 let snapshot = self.buffer.read(cx).read(cx);
11094 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
11095 Some(
11096 ranges
11097 .iter()
11098 .map(move |range| {
11099 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
11100 })
11101 .collect(),
11102 )
11103 }
11104
11105 fn selection_replacement_ranges(
11106 &self,
11107 range: Range<OffsetUtf16>,
11108 cx: &AppContext,
11109 ) -> Vec<Range<OffsetUtf16>> {
11110 let selections = self.selections.all::<OffsetUtf16>(cx);
11111 let newest_selection = selections
11112 .iter()
11113 .max_by_key(|selection| selection.id)
11114 .unwrap();
11115 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
11116 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
11117 let snapshot = self.buffer.read(cx).read(cx);
11118 selections
11119 .into_iter()
11120 .map(|mut selection| {
11121 selection.start.0 =
11122 (selection.start.0 as isize).saturating_add(start_delta) as usize;
11123 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
11124 snapshot.clip_offset_utf16(selection.start, Bias::Left)
11125 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
11126 })
11127 .collect()
11128 }
11129
11130 fn report_editor_event(
11131 &self,
11132 operation: &'static str,
11133 file_extension: Option<String>,
11134 cx: &AppContext,
11135 ) {
11136 if cfg!(any(test, feature = "test-support")) {
11137 return;
11138 }
11139
11140 let Some(project) = &self.project else { return };
11141
11142 // If None, we are in a file without an extension
11143 let file = self
11144 .buffer
11145 .read(cx)
11146 .as_singleton()
11147 .and_then(|b| b.read(cx).file());
11148 let file_extension = file_extension.or(file
11149 .as_ref()
11150 .and_then(|file| Path::new(file.file_name(cx)).extension())
11151 .and_then(|e| e.to_str())
11152 .map(|a| a.to_string()));
11153
11154 let vim_mode = cx
11155 .global::<SettingsStore>()
11156 .raw_user_settings()
11157 .get("vim_mode")
11158 == Some(&serde_json::Value::Bool(true));
11159
11160 let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
11161 == language::language_settings::InlineCompletionProvider::Copilot;
11162 let copilot_enabled_for_language = self
11163 .buffer
11164 .read(cx)
11165 .settings_at(0, cx)
11166 .show_inline_completions;
11167
11168 let telemetry = project.read(cx).client().telemetry().clone();
11169 telemetry.report_editor_event(
11170 file_extension,
11171 vim_mode,
11172 operation,
11173 copilot_enabled,
11174 copilot_enabled_for_language,
11175 )
11176 }
11177
11178 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
11179 /// with each line being an array of {text, highlight} objects.
11180 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
11181 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
11182 return;
11183 };
11184
11185 #[derive(Serialize)]
11186 struct Chunk<'a> {
11187 text: String,
11188 highlight: Option<&'a str>,
11189 }
11190
11191 let snapshot = buffer.read(cx).snapshot();
11192 let range = self
11193 .selected_text_range(cx)
11194 .and_then(|selected_range| {
11195 if selected_range.is_empty() {
11196 None
11197 } else {
11198 Some(selected_range)
11199 }
11200 })
11201 .unwrap_or_else(|| 0..snapshot.len());
11202
11203 let chunks = snapshot.chunks(range, true);
11204 let mut lines = Vec::new();
11205 let mut line: VecDeque<Chunk> = VecDeque::new();
11206
11207 let Some(style) = self.style.as_ref() else {
11208 return;
11209 };
11210
11211 for chunk in chunks {
11212 let highlight = chunk
11213 .syntax_highlight_id
11214 .and_then(|id| id.name(&style.syntax));
11215 let mut chunk_lines = chunk.text.split('\n').peekable();
11216 while let Some(text) = chunk_lines.next() {
11217 let mut merged_with_last_token = false;
11218 if let Some(last_token) = line.back_mut() {
11219 if last_token.highlight == highlight {
11220 last_token.text.push_str(text);
11221 merged_with_last_token = true;
11222 }
11223 }
11224
11225 if !merged_with_last_token {
11226 line.push_back(Chunk {
11227 text: text.into(),
11228 highlight,
11229 });
11230 }
11231
11232 if chunk_lines.peek().is_some() {
11233 if line.len() > 1 && line.front().unwrap().text.is_empty() {
11234 line.pop_front();
11235 }
11236 if line.len() > 1 && line.back().unwrap().text.is_empty() {
11237 line.pop_back();
11238 }
11239
11240 lines.push(mem::take(&mut line));
11241 }
11242 }
11243 }
11244
11245 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
11246 return;
11247 };
11248 cx.write_to_clipboard(ClipboardItem::new(lines));
11249 }
11250
11251 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
11252 &self.inlay_hint_cache
11253 }
11254
11255 pub fn replay_insert_event(
11256 &mut self,
11257 text: &str,
11258 relative_utf16_range: Option<Range<isize>>,
11259 cx: &mut ViewContext<Self>,
11260 ) {
11261 if !self.input_enabled {
11262 cx.emit(EditorEvent::InputIgnored { text: text.into() });
11263 return;
11264 }
11265 if let Some(relative_utf16_range) = relative_utf16_range {
11266 let selections = self.selections.all::<OffsetUtf16>(cx);
11267 self.change_selections(None, cx, |s| {
11268 let new_ranges = selections.into_iter().map(|range| {
11269 let start = OffsetUtf16(
11270 range
11271 .head()
11272 .0
11273 .saturating_add_signed(relative_utf16_range.start),
11274 );
11275 let end = OffsetUtf16(
11276 range
11277 .head()
11278 .0
11279 .saturating_add_signed(relative_utf16_range.end),
11280 );
11281 start..end
11282 });
11283 s.select_ranges(new_ranges);
11284 });
11285 }
11286
11287 self.handle_input(text, cx);
11288 }
11289
11290 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
11291 let Some(project) = self.project.as_ref() else {
11292 return false;
11293 };
11294 let project = project.read(cx);
11295
11296 let mut supports = false;
11297 self.buffer().read(cx).for_each_buffer(|buffer| {
11298 if !supports {
11299 supports = project
11300 .language_servers_for_buffer(buffer.read(cx), cx)
11301 .any(
11302 |(_, server)| match server.capabilities().inlay_hint_provider {
11303 Some(lsp::OneOf::Left(enabled)) => enabled,
11304 Some(lsp::OneOf::Right(_)) => true,
11305 None => false,
11306 },
11307 )
11308 }
11309 });
11310 supports
11311 }
11312
11313 pub fn focus(&self, cx: &mut WindowContext) {
11314 cx.focus(&self.focus_handle)
11315 }
11316
11317 pub fn is_focused(&self, cx: &WindowContext) -> bool {
11318 self.focus_handle.is_focused(cx)
11319 }
11320
11321 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
11322 cx.emit(EditorEvent::Focused);
11323
11324 if let Some(descendant) = self
11325 .last_focused_descendant
11326 .take()
11327 .and_then(|descendant| descendant.upgrade())
11328 {
11329 cx.focus(&descendant);
11330 } else {
11331 if let Some(blame) = self.blame.as_ref() {
11332 blame.update(cx, GitBlame::focus)
11333 }
11334
11335 self.blink_manager.update(cx, BlinkManager::enable);
11336 self.show_cursor_names(cx);
11337 self.buffer.update(cx, |buffer, cx| {
11338 buffer.finalize_last_transaction(cx);
11339 if self.leader_peer_id.is_none() {
11340 buffer.set_active_selections(
11341 &self.selections.disjoint_anchors(),
11342 self.selections.line_mode,
11343 self.cursor_shape,
11344 cx,
11345 );
11346 }
11347 });
11348 }
11349 }
11350
11351 fn handle_focus_out(&mut self, event: FocusOutEvent, _cx: &mut ViewContext<Self>) {
11352 if event.blurred != self.focus_handle {
11353 self.last_focused_descendant = Some(event.blurred);
11354 }
11355 }
11356
11357 pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
11358 self.blink_manager.update(cx, BlinkManager::disable);
11359 self.buffer
11360 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
11361
11362 if let Some(blame) = self.blame.as_ref() {
11363 blame.update(cx, GitBlame::blur)
11364 }
11365 self.hide_context_menu(cx);
11366 hide_hover(self, cx);
11367 cx.emit(EditorEvent::Blurred);
11368 cx.notify();
11369 }
11370
11371 pub fn register_action<A: Action>(
11372 &mut self,
11373 listener: impl Fn(&A, &mut WindowContext) + 'static,
11374 ) -> Subscription {
11375 let id = self.next_editor_action_id.post_inc();
11376 let listener = Arc::new(listener);
11377 self.editor_actions.borrow_mut().insert(
11378 id,
11379 Box::new(move |cx| {
11380 let _view = cx.view().clone();
11381 let cx = cx.window_context();
11382 let listener = listener.clone();
11383 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
11384 let action = action.downcast_ref().unwrap();
11385 if phase == DispatchPhase::Bubble {
11386 listener(action, cx)
11387 }
11388 })
11389 }),
11390 );
11391
11392 let editor_actions = self.editor_actions.clone();
11393 Subscription::new(move || {
11394 editor_actions.borrow_mut().remove(&id);
11395 })
11396 }
11397
11398 pub fn file_header_size(&self) -> u8 {
11399 self.file_header_size
11400 }
11401}
11402
11403fn hunks_for_selections(
11404 multi_buffer_snapshot: &MultiBufferSnapshot,
11405 selections: &[Selection<Anchor>],
11406) -> Vec<DiffHunk<MultiBufferRow>> {
11407 let mut hunks = Vec::with_capacity(selections.len());
11408 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
11409 HashMap::default();
11410 let buffer_rows_for_selections = selections.iter().map(|selection| {
11411 let head = selection.head();
11412 let tail = selection.tail();
11413 let start = MultiBufferRow(tail.to_point(&multi_buffer_snapshot).row);
11414 let end = MultiBufferRow(head.to_point(&multi_buffer_snapshot).row);
11415 if start > end {
11416 end..start
11417 } else {
11418 start..end
11419 }
11420 });
11421
11422 for selected_multi_buffer_rows in buffer_rows_for_selections {
11423 let query_rows =
11424 selected_multi_buffer_rows.start..selected_multi_buffer_rows.end.next_row();
11425 for hunk in multi_buffer_snapshot.git_diff_hunks_in_range(query_rows.clone()) {
11426 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
11427 // when the caret is just above or just below the deleted hunk.
11428 let allow_adjacent = hunk_status(&hunk) == DiffHunkStatus::Removed;
11429 let related_to_selection = if allow_adjacent {
11430 hunk.associated_range.overlaps(&query_rows)
11431 || hunk.associated_range.start == query_rows.end
11432 || hunk.associated_range.end == query_rows.start
11433 } else {
11434 // `selected_multi_buffer_rows` are inclusive (e.g. [2..2] means 2nd row is selected)
11435 // `hunk.associated_range` is exclusive (e.g. [2..3] means 2nd row is selected)
11436 hunk.associated_range.overlaps(&selected_multi_buffer_rows)
11437 || selected_multi_buffer_rows.end == hunk.associated_range.start
11438 };
11439 if related_to_selection {
11440 if !processed_buffer_rows
11441 .entry(hunk.buffer_id)
11442 .or_default()
11443 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
11444 {
11445 continue;
11446 }
11447 hunks.push(hunk);
11448 }
11449 }
11450 }
11451
11452 hunks
11453}
11454
11455pub trait CollaborationHub {
11456 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
11457 fn user_participant_indices<'a>(
11458 &self,
11459 cx: &'a AppContext,
11460 ) -> &'a HashMap<u64, ParticipantIndex>;
11461 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
11462}
11463
11464impl CollaborationHub for Model<Project> {
11465 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
11466 self.read(cx).collaborators()
11467 }
11468
11469 fn user_participant_indices<'a>(
11470 &self,
11471 cx: &'a AppContext,
11472 ) -> &'a HashMap<u64, ParticipantIndex> {
11473 self.read(cx).user_store().read(cx).participant_indices()
11474 }
11475
11476 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
11477 let this = self.read(cx);
11478 let user_ids = this.collaborators().values().map(|c| c.user_id);
11479 this.user_store().read_with(cx, |user_store, cx| {
11480 user_store.participant_names(user_ids, cx)
11481 })
11482 }
11483}
11484
11485pub trait CompletionProvider {
11486 fn completions(
11487 &self,
11488 buffer: &Model<Buffer>,
11489 buffer_position: text::Anchor,
11490 trigger: CompletionContext,
11491 cx: &mut ViewContext<Editor>,
11492 ) -> Task<Result<Vec<Completion>>>;
11493
11494 fn resolve_completions(
11495 &self,
11496 buffer: Model<Buffer>,
11497 completion_indices: Vec<usize>,
11498 completions: Arc<RwLock<Box<[Completion]>>>,
11499 cx: &mut ViewContext<Editor>,
11500 ) -> Task<Result<bool>>;
11501
11502 fn apply_additional_edits_for_completion(
11503 &self,
11504 buffer: Model<Buffer>,
11505 completion: Completion,
11506 push_to_history: bool,
11507 cx: &mut ViewContext<Editor>,
11508 ) -> Task<Result<Option<language::Transaction>>>;
11509
11510 fn is_completion_trigger(
11511 &self,
11512 buffer: &Model<Buffer>,
11513 position: language::Anchor,
11514 text: &str,
11515 trigger_in_words: bool,
11516 cx: &mut ViewContext<Editor>,
11517 ) -> bool;
11518}
11519
11520impl CompletionProvider for Model<Project> {
11521 fn completions(
11522 &self,
11523 buffer: &Model<Buffer>,
11524 buffer_position: text::Anchor,
11525 options: CompletionContext,
11526 cx: &mut ViewContext<Editor>,
11527 ) -> Task<Result<Vec<Completion>>> {
11528 self.update(cx, |project, cx| {
11529 project.completions(&buffer, buffer_position, options, cx)
11530 })
11531 }
11532
11533 fn resolve_completions(
11534 &self,
11535 buffer: Model<Buffer>,
11536 completion_indices: Vec<usize>,
11537 completions: Arc<RwLock<Box<[Completion]>>>,
11538 cx: &mut ViewContext<Editor>,
11539 ) -> Task<Result<bool>> {
11540 self.update(cx, |project, cx| {
11541 project.resolve_completions(buffer, completion_indices, completions, cx)
11542 })
11543 }
11544
11545 fn apply_additional_edits_for_completion(
11546 &self,
11547 buffer: Model<Buffer>,
11548 completion: Completion,
11549 push_to_history: bool,
11550 cx: &mut ViewContext<Editor>,
11551 ) -> Task<Result<Option<language::Transaction>>> {
11552 self.update(cx, |project, cx| {
11553 project.apply_additional_edits_for_completion(buffer, completion, push_to_history, cx)
11554 })
11555 }
11556
11557 fn is_completion_trigger(
11558 &self,
11559 buffer: &Model<Buffer>,
11560 position: language::Anchor,
11561 text: &str,
11562 trigger_in_words: bool,
11563 cx: &mut ViewContext<Editor>,
11564 ) -> bool {
11565 if !EditorSettings::get_global(cx).show_completions_on_input {
11566 return false;
11567 }
11568
11569 let mut chars = text.chars();
11570 let char = if let Some(char) = chars.next() {
11571 char
11572 } else {
11573 return false;
11574 };
11575 if chars.next().is_some() {
11576 return false;
11577 }
11578
11579 let buffer = buffer.read(cx);
11580 let scope = buffer.snapshot().language_scope_at(position);
11581 if trigger_in_words && char_kind(&scope, char) == CharKind::Word {
11582 return true;
11583 }
11584
11585 buffer
11586 .completion_triggers()
11587 .iter()
11588 .any(|string| string == text)
11589 }
11590}
11591
11592fn inlay_hint_settings(
11593 location: Anchor,
11594 snapshot: &MultiBufferSnapshot,
11595 cx: &mut ViewContext<'_, Editor>,
11596) -> InlayHintSettings {
11597 let file = snapshot.file_at(location);
11598 let language = snapshot.language_at(location);
11599 let settings = all_language_settings(file, cx);
11600 settings
11601 .language(language.map(|l| l.name()).as_deref())
11602 .inlay_hints
11603}
11604
11605fn consume_contiguous_rows(
11606 contiguous_row_selections: &mut Vec<Selection<Point>>,
11607 selection: &Selection<Point>,
11608 display_map: &DisplaySnapshot,
11609 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
11610) -> (MultiBufferRow, MultiBufferRow) {
11611 contiguous_row_selections.push(selection.clone());
11612 let start_row = MultiBufferRow(selection.start.row);
11613 let mut end_row = ending_row(selection, display_map);
11614
11615 while let Some(next_selection) = selections.peek() {
11616 if next_selection.start.row <= end_row.0 {
11617 end_row = ending_row(next_selection, display_map);
11618 contiguous_row_selections.push(selections.next().unwrap().clone());
11619 } else {
11620 break;
11621 }
11622 }
11623 (start_row, end_row)
11624}
11625
11626fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
11627 if next_selection.end.column > 0 || next_selection.is_empty() {
11628 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
11629 } else {
11630 MultiBufferRow(next_selection.end.row)
11631 }
11632}
11633
11634impl EditorSnapshot {
11635 pub fn remote_selections_in_range<'a>(
11636 &'a self,
11637 range: &'a Range<Anchor>,
11638 collaboration_hub: &dyn CollaborationHub,
11639 cx: &'a AppContext,
11640 ) -> impl 'a + Iterator<Item = RemoteSelection> {
11641 let participant_names = collaboration_hub.user_names(cx);
11642 let participant_indices = collaboration_hub.user_participant_indices(cx);
11643 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
11644 let collaborators_by_replica_id = collaborators_by_peer_id
11645 .iter()
11646 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
11647 .collect::<HashMap<_, _>>();
11648 self.buffer_snapshot
11649 .remote_selections_in_range(range)
11650 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
11651 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
11652 let participant_index = participant_indices.get(&collaborator.user_id).copied();
11653 let user_name = participant_names.get(&collaborator.user_id).cloned();
11654 Some(RemoteSelection {
11655 replica_id,
11656 selection,
11657 cursor_shape,
11658 line_mode,
11659 participant_index,
11660 peer_id: collaborator.peer_id,
11661 user_name,
11662 })
11663 })
11664 }
11665
11666 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
11667 self.display_snapshot.buffer_snapshot.language_at(position)
11668 }
11669
11670 pub fn is_focused(&self) -> bool {
11671 self.is_focused
11672 }
11673
11674 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
11675 self.placeholder_text.as_ref()
11676 }
11677
11678 pub fn scroll_position(&self) -> gpui::Point<f32> {
11679 self.scroll_anchor.scroll_position(&self.display_snapshot)
11680 }
11681
11682 pub fn gutter_dimensions(
11683 &self,
11684 font_id: FontId,
11685 font_size: Pixels,
11686 em_width: Pixels,
11687 max_line_number_width: Pixels,
11688 cx: &AppContext,
11689 ) -> GutterDimensions {
11690 if !self.show_gutter {
11691 return GutterDimensions::default();
11692 }
11693 let descent = cx.text_system().descent(font_id, font_size);
11694
11695 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
11696 matches!(
11697 ProjectSettings::get_global(cx).git.git_gutter,
11698 Some(GitGutterSetting::TrackedFiles)
11699 )
11700 });
11701 let gutter_settings = EditorSettings::get_global(cx).gutter;
11702 let show_line_numbers = self
11703 .show_line_numbers
11704 .unwrap_or_else(|| gutter_settings.line_numbers);
11705 let line_gutter_width = if show_line_numbers {
11706 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
11707 let min_width_for_number_on_gutter = em_width * 4.0;
11708 max_line_number_width.max(min_width_for_number_on_gutter)
11709 } else {
11710 0.0.into()
11711 };
11712
11713 let show_code_actions = self
11714 .show_code_actions
11715 .unwrap_or_else(|| gutter_settings.code_actions);
11716
11717 let git_blame_entries_width = self
11718 .render_git_blame_gutter
11719 .then_some(em_width * GIT_BLAME_GUTTER_WIDTH_CHARS);
11720
11721 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
11722 left_padding += if show_code_actions {
11723 em_width * 3.0
11724 } else if show_git_gutter && show_line_numbers {
11725 em_width * 2.0
11726 } else if show_git_gutter || show_line_numbers {
11727 em_width
11728 } else {
11729 px(0.)
11730 };
11731
11732 let right_padding = if gutter_settings.folds && show_line_numbers {
11733 em_width * 4.0
11734 } else if gutter_settings.folds {
11735 em_width * 3.0
11736 } else if show_line_numbers {
11737 em_width
11738 } else {
11739 px(0.)
11740 };
11741
11742 GutterDimensions {
11743 left_padding,
11744 right_padding,
11745 width: line_gutter_width + left_padding + right_padding,
11746 margin: -descent,
11747 git_blame_entries_width,
11748 }
11749 }
11750
11751 pub fn render_fold_toggle(
11752 &self,
11753 buffer_row: MultiBufferRow,
11754 row_contains_cursor: bool,
11755 editor: View<Editor>,
11756 cx: &mut WindowContext,
11757 ) -> Option<AnyElement> {
11758 let folded = self.is_line_folded(buffer_row);
11759
11760 if let Some(flap) = self
11761 .flap_snapshot
11762 .query_row(buffer_row, &self.buffer_snapshot)
11763 {
11764 let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
11765 if folded {
11766 editor.update(cx, |editor, cx| {
11767 editor.fold_at(&crate::FoldAt { buffer_row }, cx)
11768 });
11769 } else {
11770 editor.update(cx, |editor, cx| {
11771 editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
11772 });
11773 }
11774 });
11775
11776 Some((flap.render_toggle)(
11777 buffer_row,
11778 folded,
11779 toggle_callback,
11780 cx,
11781 ))
11782 } else if folded
11783 || (self.starts_indent(buffer_row) && (row_contains_cursor || self.gutter_hovered))
11784 {
11785 Some(
11786 Disclosure::new(("indent-fold-indicator", buffer_row.0), !folded)
11787 .selected(folded)
11788 .on_click(cx.listener_for(&editor, move |this, _e, cx| {
11789 if folded {
11790 this.unfold_at(&UnfoldAt { buffer_row }, cx);
11791 } else {
11792 this.fold_at(&FoldAt { buffer_row }, cx);
11793 }
11794 }))
11795 .into_any_element(),
11796 )
11797 } else {
11798 None
11799 }
11800 }
11801
11802 pub fn render_flap_trailer(
11803 &self,
11804 buffer_row: MultiBufferRow,
11805 cx: &mut WindowContext,
11806 ) -> Option<AnyElement> {
11807 let folded = self.is_line_folded(buffer_row);
11808 let flap = self
11809 .flap_snapshot
11810 .query_row(buffer_row, &self.buffer_snapshot)?;
11811 Some((flap.render_trailer)(buffer_row, folded, cx))
11812 }
11813}
11814
11815impl Deref for EditorSnapshot {
11816 type Target = DisplaySnapshot;
11817
11818 fn deref(&self) -> &Self::Target {
11819 &self.display_snapshot
11820 }
11821}
11822
11823#[derive(Clone, Debug, PartialEq, Eq)]
11824pub enum EditorEvent {
11825 InputIgnored {
11826 text: Arc<str>,
11827 },
11828 InputHandled {
11829 utf16_range_to_replace: Option<Range<isize>>,
11830 text: Arc<str>,
11831 },
11832 ExcerptsAdded {
11833 buffer: Model<Buffer>,
11834 predecessor: ExcerptId,
11835 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
11836 },
11837 ExcerptsRemoved {
11838 ids: Vec<ExcerptId>,
11839 },
11840 ExcerptsEdited {
11841 ids: Vec<ExcerptId>,
11842 },
11843 ExcerptsExpanded {
11844 ids: Vec<ExcerptId>,
11845 },
11846 BufferEdited,
11847 Edited {
11848 transaction_id: clock::Lamport,
11849 },
11850 Reparsed(BufferId),
11851 Focused,
11852 Blurred,
11853 DirtyChanged,
11854 Saved,
11855 TitleChanged,
11856 DiffBaseChanged,
11857 SelectionsChanged {
11858 local: bool,
11859 },
11860 ScrollPositionChanged {
11861 local: bool,
11862 autoscroll: bool,
11863 },
11864 Closed,
11865 TransactionUndone {
11866 transaction_id: clock::Lamport,
11867 },
11868 TransactionBegun {
11869 transaction_id: clock::Lamport,
11870 },
11871}
11872
11873impl EventEmitter<EditorEvent> for Editor {}
11874
11875impl FocusableView for Editor {
11876 fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
11877 self.focus_handle.clone()
11878 }
11879}
11880
11881impl Render for Editor {
11882 fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
11883 let settings = ThemeSettings::get_global(cx);
11884
11885 let text_style = match self.mode {
11886 EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
11887 color: cx.theme().colors().editor_foreground,
11888 font_family: settings.ui_font.family.clone(),
11889 font_features: settings.ui_font.features.clone(),
11890 font_size: rems(0.875).into(),
11891 font_weight: settings.ui_font.weight,
11892 font_style: FontStyle::Normal,
11893 line_height: relative(settings.buffer_line_height.value()),
11894 background_color: None,
11895 underline: None,
11896 strikethrough: None,
11897 white_space: WhiteSpace::Normal,
11898 },
11899 EditorMode::Full => TextStyle {
11900 color: cx.theme().colors().editor_foreground,
11901 font_family: settings.buffer_font.family.clone(),
11902 font_features: settings.buffer_font.features.clone(),
11903 font_size: settings.buffer_font_size(cx).into(),
11904 font_weight: settings.buffer_font.weight,
11905 font_style: FontStyle::Normal,
11906 line_height: relative(settings.buffer_line_height.value()),
11907 background_color: None,
11908 underline: None,
11909 strikethrough: None,
11910 white_space: WhiteSpace::Normal,
11911 },
11912 };
11913
11914 let background = match self.mode {
11915 EditorMode::SingleLine => cx.theme().system().transparent,
11916 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
11917 EditorMode::Full => cx.theme().colors().editor_background,
11918 };
11919
11920 EditorElement::new(
11921 cx.view(),
11922 EditorStyle {
11923 background,
11924 local_player: cx.theme().players().local(),
11925 text: text_style,
11926 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
11927 syntax: cx.theme().syntax().clone(),
11928 status: cx.theme().status().clone(),
11929 inlay_hints_style: HighlightStyle {
11930 color: Some(cx.theme().status().hint),
11931 ..HighlightStyle::default()
11932 },
11933 suggestions_style: HighlightStyle {
11934 color: Some(cx.theme().status().predictive),
11935 ..HighlightStyle::default()
11936 },
11937 },
11938 )
11939 }
11940}
11941
11942impl ViewInputHandler for Editor {
11943 fn text_for_range(
11944 &mut self,
11945 range_utf16: Range<usize>,
11946 cx: &mut ViewContext<Self>,
11947 ) -> Option<String> {
11948 Some(
11949 self.buffer
11950 .read(cx)
11951 .read(cx)
11952 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
11953 .collect(),
11954 )
11955 }
11956
11957 fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
11958 // Prevent the IME menu from appearing when holding down an alphabetic key
11959 // while input is disabled.
11960 if !self.input_enabled {
11961 return None;
11962 }
11963
11964 let range = self.selections.newest::<OffsetUtf16>(cx).range();
11965 Some(range.start.0..range.end.0)
11966 }
11967
11968 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
11969 let snapshot = self.buffer.read(cx).read(cx);
11970 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
11971 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
11972 }
11973
11974 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
11975 self.clear_highlights::<InputComposition>(cx);
11976 self.ime_transaction.take();
11977 }
11978
11979 fn replace_text_in_range(
11980 &mut self,
11981 range_utf16: Option<Range<usize>>,
11982 text: &str,
11983 cx: &mut ViewContext<Self>,
11984 ) {
11985 if !self.input_enabled {
11986 cx.emit(EditorEvent::InputIgnored { text: text.into() });
11987 return;
11988 }
11989
11990 self.transact(cx, |this, cx| {
11991 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
11992 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
11993 Some(this.selection_replacement_ranges(range_utf16, cx))
11994 } else {
11995 this.marked_text_ranges(cx)
11996 };
11997
11998 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
11999 let newest_selection_id = this.selections.newest_anchor().id;
12000 this.selections
12001 .all::<OffsetUtf16>(cx)
12002 .iter()
12003 .zip(ranges_to_replace.iter())
12004 .find_map(|(selection, range)| {
12005 if selection.id == newest_selection_id {
12006 Some(
12007 (range.start.0 as isize - selection.head().0 as isize)
12008 ..(range.end.0 as isize - selection.head().0 as isize),
12009 )
12010 } else {
12011 None
12012 }
12013 })
12014 });
12015
12016 cx.emit(EditorEvent::InputHandled {
12017 utf16_range_to_replace: range_to_replace,
12018 text: text.into(),
12019 });
12020
12021 if let Some(new_selected_ranges) = new_selected_ranges {
12022 this.change_selections(None, cx, |selections| {
12023 selections.select_ranges(new_selected_ranges)
12024 });
12025 this.backspace(&Default::default(), cx);
12026 }
12027
12028 this.handle_input(text, cx);
12029 });
12030
12031 if let Some(transaction) = self.ime_transaction {
12032 self.buffer.update(cx, |buffer, cx| {
12033 buffer.group_until_transaction(transaction, cx);
12034 });
12035 }
12036
12037 self.unmark_text(cx);
12038 }
12039
12040 fn replace_and_mark_text_in_range(
12041 &mut self,
12042 range_utf16: Option<Range<usize>>,
12043 text: &str,
12044 new_selected_range_utf16: Option<Range<usize>>,
12045 cx: &mut ViewContext<Self>,
12046 ) {
12047 if !self.input_enabled {
12048 return;
12049 }
12050
12051 let transaction = self.transact(cx, |this, cx| {
12052 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
12053 let snapshot = this.buffer.read(cx).read(cx);
12054 if let Some(relative_range_utf16) = range_utf16.as_ref() {
12055 for marked_range in &mut marked_ranges {
12056 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
12057 marked_range.start.0 += relative_range_utf16.start;
12058 marked_range.start =
12059 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
12060 marked_range.end =
12061 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
12062 }
12063 }
12064 Some(marked_ranges)
12065 } else if let Some(range_utf16) = range_utf16 {
12066 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
12067 Some(this.selection_replacement_ranges(range_utf16, cx))
12068 } else {
12069 None
12070 };
12071
12072 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
12073 let newest_selection_id = this.selections.newest_anchor().id;
12074 this.selections
12075 .all::<OffsetUtf16>(cx)
12076 .iter()
12077 .zip(ranges_to_replace.iter())
12078 .find_map(|(selection, range)| {
12079 if selection.id == newest_selection_id {
12080 Some(
12081 (range.start.0 as isize - selection.head().0 as isize)
12082 ..(range.end.0 as isize - selection.head().0 as isize),
12083 )
12084 } else {
12085 None
12086 }
12087 })
12088 });
12089
12090 cx.emit(EditorEvent::InputHandled {
12091 utf16_range_to_replace: range_to_replace,
12092 text: text.into(),
12093 });
12094
12095 if let Some(ranges) = ranges_to_replace {
12096 this.change_selections(None, cx, |s| s.select_ranges(ranges));
12097 }
12098
12099 let marked_ranges = {
12100 let snapshot = this.buffer.read(cx).read(cx);
12101 this.selections
12102 .disjoint_anchors()
12103 .iter()
12104 .map(|selection| {
12105 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
12106 })
12107 .collect::<Vec<_>>()
12108 };
12109
12110 if text.is_empty() {
12111 this.unmark_text(cx);
12112 } else {
12113 this.highlight_text::<InputComposition>(
12114 marked_ranges.clone(),
12115 HighlightStyle {
12116 underline: Some(UnderlineStyle {
12117 thickness: px(1.),
12118 color: None,
12119 wavy: false,
12120 }),
12121 ..Default::default()
12122 },
12123 cx,
12124 );
12125 }
12126
12127 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
12128 let use_autoclose = this.use_autoclose;
12129 this.set_use_autoclose(false);
12130 this.handle_input(text, cx);
12131 this.set_use_autoclose(use_autoclose);
12132
12133 if let Some(new_selected_range) = new_selected_range_utf16 {
12134 let snapshot = this.buffer.read(cx).read(cx);
12135 let new_selected_ranges = marked_ranges
12136 .into_iter()
12137 .map(|marked_range| {
12138 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
12139 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
12140 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
12141 snapshot.clip_offset_utf16(new_start, Bias::Left)
12142 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
12143 })
12144 .collect::<Vec<_>>();
12145
12146 drop(snapshot);
12147 this.change_selections(None, cx, |selections| {
12148 selections.select_ranges(new_selected_ranges)
12149 });
12150 }
12151 });
12152
12153 self.ime_transaction = self.ime_transaction.or(transaction);
12154 if let Some(transaction) = self.ime_transaction {
12155 self.buffer.update(cx, |buffer, cx| {
12156 buffer.group_until_transaction(transaction, cx);
12157 });
12158 }
12159
12160 if self.text_highlights::<InputComposition>(cx).is_none() {
12161 self.ime_transaction.take();
12162 }
12163 }
12164
12165 fn bounds_for_range(
12166 &mut self,
12167 range_utf16: Range<usize>,
12168 element_bounds: gpui::Bounds<Pixels>,
12169 cx: &mut ViewContext<Self>,
12170 ) -> Option<gpui::Bounds<Pixels>> {
12171 let text_layout_details = self.text_layout_details(cx);
12172 let style = &text_layout_details.editor_style;
12173 let font_id = cx.text_system().resolve_font(&style.text.font());
12174 let font_size = style.text.font_size.to_pixels(cx.rem_size());
12175 let line_height = style.text.line_height_in_pixels(cx.rem_size());
12176 let em_width = cx
12177 .text_system()
12178 .typographic_bounds(font_id, font_size, 'm')
12179 .unwrap()
12180 .size
12181 .width;
12182
12183 let snapshot = self.snapshot(cx);
12184 let scroll_position = snapshot.scroll_position();
12185 let scroll_left = scroll_position.x * em_width;
12186
12187 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
12188 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
12189 + self.gutter_dimensions.width;
12190 let y = line_height * (start.row().as_f32() - scroll_position.y);
12191
12192 Some(Bounds {
12193 origin: element_bounds.origin + point(x, y),
12194 size: size(em_width, line_height),
12195 })
12196 }
12197}
12198
12199trait SelectionExt {
12200 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
12201 fn spanned_rows(
12202 &self,
12203 include_end_if_at_line_start: bool,
12204 map: &DisplaySnapshot,
12205 ) -> Range<MultiBufferRow>;
12206}
12207
12208impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
12209 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
12210 let start = self
12211 .start
12212 .to_point(&map.buffer_snapshot)
12213 .to_display_point(map);
12214 let end = self
12215 .end
12216 .to_point(&map.buffer_snapshot)
12217 .to_display_point(map);
12218 if self.reversed {
12219 end..start
12220 } else {
12221 start..end
12222 }
12223 }
12224
12225 fn spanned_rows(
12226 &self,
12227 include_end_if_at_line_start: bool,
12228 map: &DisplaySnapshot,
12229 ) -> Range<MultiBufferRow> {
12230 let start = self.start.to_point(&map.buffer_snapshot);
12231 let mut end = self.end.to_point(&map.buffer_snapshot);
12232 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
12233 end.row -= 1;
12234 }
12235
12236 let buffer_start = map.prev_line_boundary(start).0;
12237 let buffer_end = map.next_line_boundary(end).0;
12238 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
12239 }
12240}
12241
12242impl<T: InvalidationRegion> InvalidationStack<T> {
12243 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
12244 where
12245 S: Clone + ToOffset,
12246 {
12247 while let Some(region) = self.last() {
12248 let all_selections_inside_invalidation_ranges =
12249 if selections.len() == region.ranges().len() {
12250 selections
12251 .iter()
12252 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
12253 .all(|(selection, invalidation_range)| {
12254 let head = selection.head().to_offset(buffer);
12255 invalidation_range.start <= head && invalidation_range.end >= head
12256 })
12257 } else {
12258 false
12259 };
12260
12261 if all_selections_inside_invalidation_ranges {
12262 break;
12263 } else {
12264 self.pop();
12265 }
12266 }
12267 }
12268}
12269
12270impl<T> Default for InvalidationStack<T> {
12271 fn default() -> Self {
12272 Self(Default::default())
12273 }
12274}
12275
12276impl<T> Deref for InvalidationStack<T> {
12277 type Target = Vec<T>;
12278
12279 fn deref(&self) -> &Self::Target {
12280 &self.0
12281 }
12282}
12283
12284impl<T> DerefMut for InvalidationStack<T> {
12285 fn deref_mut(&mut self) -> &mut Self::Target {
12286 &mut self.0
12287 }
12288}
12289
12290impl InvalidationRegion for SnippetState {
12291 fn ranges(&self) -> &[Range<Anchor>] {
12292 &self.ranges[self.active_index]
12293 }
12294}
12295
12296pub fn diagnostic_block_renderer(diagnostic: Diagnostic, _is_valid: bool) -> RenderBlock {
12297 let (text_without_backticks, code_ranges) = highlight_diagnostic_message(&diagnostic);
12298
12299 Box::new(move |cx: &mut BlockContext| {
12300 let group_id: SharedString = cx.block_id.to_string().into();
12301
12302 let mut text_style = cx.text_style().clone();
12303 text_style.color = diagnostic_style(diagnostic.severity, true, cx.theme().status());
12304 let theme_settings = ThemeSettings::get_global(cx);
12305 text_style.font_family = theme_settings.buffer_font.family.clone();
12306 text_style.font_style = theme_settings.buffer_font.style;
12307 text_style.font_features = theme_settings.buffer_font.features.clone();
12308 text_style.font_weight = theme_settings.buffer_font.weight;
12309
12310 let multi_line_diagnostic = diagnostic.message.contains('\n');
12311
12312 let buttons = |diagnostic: &Diagnostic, block_id: usize| {
12313 if multi_line_diagnostic {
12314 v_flex()
12315 } else {
12316 h_flex()
12317 }
12318 .children(diagnostic.is_primary.then(|| {
12319 IconButton::new(("close-block", block_id), IconName::XCircle)
12320 .icon_color(Color::Muted)
12321 .size(ButtonSize::Compact)
12322 .style(ButtonStyle::Transparent)
12323 .visible_on_hover(group_id.clone())
12324 .on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
12325 .tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
12326 }))
12327 .child(
12328 IconButton::new(("copy-block", block_id), IconName::Copy)
12329 .icon_color(Color::Muted)
12330 .size(ButtonSize::Compact)
12331 .style(ButtonStyle::Transparent)
12332 .visible_on_hover(group_id.clone())
12333 .on_click({
12334 let message = diagnostic.message.clone();
12335 move |_click, cx| cx.write_to_clipboard(ClipboardItem::new(message.clone()))
12336 })
12337 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
12338 )
12339 };
12340
12341 let icon_size = buttons(&diagnostic, cx.block_id)
12342 .into_any_element()
12343 .layout_as_root(AvailableSpace::min_size(), cx);
12344
12345 h_flex()
12346 .id(cx.block_id)
12347 .group(group_id.clone())
12348 .relative()
12349 .size_full()
12350 .pl(cx.gutter_dimensions.width)
12351 .w(cx.max_width + cx.gutter_dimensions.width)
12352 .child(
12353 div()
12354 .flex()
12355 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
12356 .flex_shrink(),
12357 )
12358 .child(buttons(&diagnostic, cx.block_id))
12359 .child(div().flex().flex_shrink_0().child(
12360 StyledText::new(text_without_backticks.clone()).with_highlights(
12361 &text_style,
12362 code_ranges.iter().map(|range| {
12363 (
12364 range.clone(),
12365 HighlightStyle {
12366 font_weight: Some(FontWeight::BOLD),
12367 ..Default::default()
12368 },
12369 )
12370 }),
12371 ),
12372 ))
12373 .into_any_element()
12374 })
12375}
12376
12377pub fn highlight_diagnostic_message(diagnostic: &Diagnostic) -> (SharedString, Vec<Range<usize>>) {
12378 let mut text_without_backticks = String::new();
12379 let mut code_ranges = Vec::new();
12380
12381 if let Some(source) = &diagnostic.source {
12382 text_without_backticks.push_str(&source);
12383 code_ranges.push(0..source.len());
12384 text_without_backticks.push_str(": ");
12385 }
12386
12387 let mut prev_offset = 0;
12388 let mut in_code_block = false;
12389 for (ix, _) in diagnostic
12390 .message
12391 .match_indices('`')
12392 .chain([(diagnostic.message.len(), "")])
12393 {
12394 let prev_len = text_without_backticks.len();
12395 text_without_backticks.push_str(&diagnostic.message[prev_offset..ix]);
12396 prev_offset = ix + 1;
12397 if in_code_block {
12398 code_ranges.push(prev_len..text_without_backticks.len());
12399 in_code_block = false;
12400 } else {
12401 in_code_block = true;
12402 }
12403 }
12404
12405 (text_without_backticks.into(), code_ranges)
12406}
12407
12408fn diagnostic_style(severity: DiagnosticSeverity, valid: bool, colors: &StatusColors) -> Hsla {
12409 match (severity, valid) {
12410 (DiagnosticSeverity::ERROR, true) => colors.error,
12411 (DiagnosticSeverity::ERROR, false) => colors.error,
12412 (DiagnosticSeverity::WARNING, true) => colors.warning,
12413 (DiagnosticSeverity::WARNING, false) => colors.warning,
12414 (DiagnosticSeverity::INFORMATION, true) => colors.info,
12415 (DiagnosticSeverity::INFORMATION, false) => colors.info,
12416 (DiagnosticSeverity::HINT, true) => colors.info,
12417 (DiagnosticSeverity::HINT, false) => colors.info,
12418 _ => colors.ignored,
12419 }
12420}
12421
12422pub fn styled_runs_for_code_label<'a>(
12423 label: &'a CodeLabel,
12424 syntax_theme: &'a theme::SyntaxTheme,
12425) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
12426 let fade_out = HighlightStyle {
12427 fade_out: Some(0.35),
12428 ..Default::default()
12429 };
12430
12431 let mut prev_end = label.filter_range.end;
12432 label
12433 .runs
12434 .iter()
12435 .enumerate()
12436 .flat_map(move |(ix, (range, highlight_id))| {
12437 let style = if let Some(style) = highlight_id.style(syntax_theme) {
12438 style
12439 } else {
12440 return Default::default();
12441 };
12442 let mut muted_style = style;
12443 muted_style.highlight(fade_out);
12444
12445 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
12446 if range.start >= label.filter_range.end {
12447 if range.start > prev_end {
12448 runs.push((prev_end..range.start, fade_out));
12449 }
12450 runs.push((range.clone(), muted_style));
12451 } else if range.end <= label.filter_range.end {
12452 runs.push((range.clone(), style));
12453 } else {
12454 runs.push((range.start..label.filter_range.end, style));
12455 runs.push((label.filter_range.end..range.end, muted_style));
12456 }
12457 prev_end = cmp::max(prev_end, range.end);
12458
12459 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
12460 runs.push((prev_end..label.text.len(), fade_out));
12461 }
12462
12463 runs
12464 })
12465}
12466
12467pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
12468 let mut prev_index = 0;
12469 let mut prev_codepoint: Option<char> = None;
12470 text.char_indices()
12471 .chain([(text.len(), '\0')])
12472 .filter_map(move |(index, codepoint)| {
12473 let prev_codepoint = prev_codepoint.replace(codepoint)?;
12474 let is_boundary = index == text.len()
12475 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
12476 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
12477 if is_boundary {
12478 let chunk = &text[prev_index..index];
12479 prev_index = index;
12480 Some(chunk)
12481 } else {
12482 None
12483 }
12484 })
12485}
12486
12487trait RangeToAnchorExt {
12488 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
12489}
12490
12491impl<T: ToOffset> RangeToAnchorExt for Range<T> {
12492 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
12493 let start_offset = self.start.to_offset(snapshot);
12494 let end_offset = self.end.to_offset(snapshot);
12495 if start_offset == end_offset {
12496 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
12497 } else {
12498 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
12499 }
12500 }
12501}
12502
12503pub trait RowExt {
12504 fn as_f32(&self) -> f32;
12505
12506 fn next_row(&self) -> Self;
12507
12508 fn previous_row(&self) -> Self;
12509
12510 fn minus(&self, other: Self) -> u32;
12511}
12512
12513impl RowExt for DisplayRow {
12514 fn as_f32(&self) -> f32 {
12515 self.0 as f32
12516 }
12517
12518 fn next_row(&self) -> Self {
12519 Self(self.0 + 1)
12520 }
12521
12522 fn previous_row(&self) -> Self {
12523 Self(self.0.saturating_sub(1))
12524 }
12525
12526 fn minus(&self, other: Self) -> u32 {
12527 self.0 - other.0
12528 }
12529}
12530
12531impl RowExt for MultiBufferRow {
12532 fn as_f32(&self) -> f32 {
12533 self.0 as f32
12534 }
12535
12536 fn next_row(&self) -> Self {
12537 Self(self.0 + 1)
12538 }
12539
12540 fn previous_row(&self) -> Self {
12541 Self(self.0.saturating_sub(1))
12542 }
12543
12544 fn minus(&self, other: Self) -> u32 {
12545 self.0 - other.0
12546 }
12547}
12548
12549trait RowRangeExt {
12550 type Row;
12551
12552 fn len(&self) -> usize;
12553
12554 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
12555}
12556
12557impl RowRangeExt for Range<MultiBufferRow> {
12558 type Row = MultiBufferRow;
12559
12560 fn len(&self) -> usize {
12561 (self.end.0 - self.start.0) as usize
12562 }
12563
12564 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
12565 (self.start.0..self.end.0).map(MultiBufferRow)
12566 }
12567}
12568
12569impl RowRangeExt for Range<DisplayRow> {
12570 type Row = DisplayRow;
12571
12572 fn len(&self) -> usize {
12573 (self.end.0 - self.start.0) as usize
12574 }
12575
12576 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
12577 (self.start.0..self.end.0).map(DisplayRow)
12578 }
12579}
12580
12581fn hunk_status(hunk: &DiffHunk<MultiBufferRow>) -> DiffHunkStatus {
12582 if hunk.diff_base_byte_range.is_empty() {
12583 DiffHunkStatus::Added
12584 } else if hunk.associated_range.is_empty() {
12585 DiffHunkStatus::Removed
12586 } else {
12587 DiffHunkStatus::Modified
12588 }
12589}