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