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