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