1mod blink_manager;
2pub mod display_map;
3mod editor_settings;
4mod element;
5mod inlay_hint_cache;
6
7mod git;
8mod highlight_matching_bracket;
9mod hover_popover;
10pub mod items;
11mod link_go_to_definition;
12mod mouse_context_menu;
13pub mod movement;
14pub mod multi_buffer;
15mod persistence;
16pub mod scroll;
17pub mod selections_collection;
18
19#[cfg(test)]
20mod editor_tests;
21#[cfg(any(test, feature = "test-support"))]
22pub mod test;
23
24use ::git::diff::DiffHunk;
25use aho_corasick::AhoCorasick;
26use anyhow::{anyhow, Result};
27use blink_manager::BlinkManager;
28use client::{ClickhouseEvent, TelemetrySettings};
29use clock::{Global, ReplicaId};
30use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
31use convert_case::{Case, Casing};
32use copilot::Copilot;
33pub use display_map::DisplayPoint;
34use display_map::*;
35pub use editor_settings::EditorSettings;
36pub use element::{
37 Cursor, EditorElement, HighlightedRange, HighlightedRangeLine, LineWithInvisibles,
38};
39use futures::FutureExt;
40use fuzzy::{StringMatch, StringMatchCandidate};
41use gpui::{
42 actions,
43 color::Color,
44 elements::*,
45 executor,
46 fonts::{self, HighlightStyle, TextStyle},
47 geometry::vector::Vector2F,
48 impl_actions,
49 keymap_matcher::KeymapContext,
50 platform::{CursorStyle, MouseButton},
51 serde_json, AnyElement, AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem, Element,
52 Entity, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
53 WindowContext,
54};
55use highlight_matching_bracket::refresh_matching_bracket_highlights;
56use hover_popover::{hide_hover, HoverState};
57use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
58pub use items::MAX_TAB_TITLE_LEN;
59use itertools::Itertools;
60pub use language::{char_kind, CharKind};
61use language::{
62 language_settings::{self, all_language_settings, InlayHintSettings},
63 AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion, CursorShape,
64 Diagnostic, DiagnosticSeverity, File, IndentKind, IndentSize, Language, OffsetRangeExt,
65 OffsetUtf16, Point, Selection, SelectionGoal, TransactionId,
66};
67use link_go_to_definition::{
68 hide_link_definition, show_link_definition, LinkDefinitionKind, LinkGoToDefinitionState,
69};
70use log::error;
71use multi_buffer::ToOffsetUtf16;
72pub use multi_buffer::{
73 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
74 ToPoint,
75};
76use ordered_float::OrderedFloat;
77use project::{FormatTrigger, Location, LocationLink, Project, ProjectPath, ProjectTransaction};
78use rand::{seq::SliceRandom, thread_rng};
79use scroll::{
80 autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide,
81};
82use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
83use serde::{Deserialize, Serialize};
84use settings::SettingsStore;
85use smallvec::SmallVec;
86use snippet::Snippet;
87use std::{
88 any::TypeId,
89 borrow::Cow,
90 cmp::{self, Ordering, Reverse},
91 mem,
92 num::NonZeroU32,
93 ops::{ControlFlow, Deref, DerefMut, Range, RangeInclusive},
94 path::Path,
95 sync::Arc,
96 time::{Duration, Instant},
97};
98pub use sum_tree::Bias;
99use text::Rope;
100use theme::{DiagnosticStyle, Theme, ThemeSettings};
101use util::{post_inc, RangeExt, ResultExt, TryFutureExt};
102use workspace::{ItemNavHistory, ViewId, Workspace};
103
104use crate::git::diff_hunk_to_display;
105
106const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
107const MAX_LINE_LEN: usize = 1024;
108const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
109const MAX_SELECTION_HISTORY_LEN: usize = 1024;
110const COPILOT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
111
112pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
113
114#[derive(Clone, Deserialize, PartialEq, Default)]
115pub struct SelectNext {
116 #[serde(default)]
117 pub replace_newest: bool,
118}
119
120#[derive(Clone, Deserialize, PartialEq, Default)]
121pub struct SelectPrevious {
122 #[serde(default)]
123 pub replace_newest: bool,
124}
125
126#[derive(Clone, Deserialize, PartialEq)]
127pub struct SelectToBeginningOfLine {
128 #[serde(default)]
129 stop_at_soft_wraps: bool,
130}
131
132#[derive(Clone, Default, Deserialize, PartialEq)]
133pub struct MovePageUp {
134 #[serde(default)]
135 center_cursor: bool,
136}
137
138#[derive(Clone, Default, Deserialize, PartialEq)]
139pub struct MovePageDown {
140 #[serde(default)]
141 center_cursor: bool,
142}
143
144#[derive(Clone, Deserialize, PartialEq)]
145pub struct SelectToEndOfLine {
146 #[serde(default)]
147 stop_at_soft_wraps: bool,
148}
149
150#[derive(Clone, Deserialize, PartialEq)]
151pub struct ToggleCodeActions {
152 #[serde(default)]
153 pub deployed_from_indicator: bool,
154}
155
156#[derive(Clone, Default, Deserialize, PartialEq)]
157pub struct ConfirmCompletion {
158 #[serde(default)]
159 pub item_ix: Option<usize>,
160}
161
162#[derive(Clone, Default, Deserialize, PartialEq)]
163pub struct ConfirmCodeAction {
164 #[serde(default)]
165 pub item_ix: Option<usize>,
166}
167
168#[derive(Clone, Default, Deserialize, PartialEq)]
169pub struct ToggleComments {
170 #[serde(default)]
171 pub advance_downwards: bool,
172}
173
174#[derive(Clone, Default, Deserialize, PartialEq)]
175pub struct FoldAt {
176 pub buffer_row: u32,
177}
178
179#[derive(Clone, Default, Deserialize, PartialEq)]
180pub struct UnfoldAt {
181 pub buffer_row: u32,
182}
183
184#[derive(Clone, Default, Deserialize, PartialEq)]
185pub struct GutterHover {
186 pub hovered: bool,
187}
188
189#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
190pub enum InlayId {
191 Suggestion(usize),
192 Hint(usize),
193}
194
195impl InlayId {
196 fn id(&self) -> usize {
197 match self {
198 Self::Suggestion(id) => *id,
199 Self::Hint(id) => *id,
200 }
201 }
202}
203
204actions!(
205 editor,
206 [
207 Cancel,
208 Backspace,
209 Delete,
210 Newline,
211 NewlineAbove,
212 NewlineBelow,
213 GoToDiagnostic,
214 GoToPrevDiagnostic,
215 GoToHunk,
216 GoToPrevHunk,
217 Indent,
218 Outdent,
219 DeleteLine,
220 DeleteToPreviousWordStart,
221 DeleteToPreviousSubwordStart,
222 DeleteToNextWordEnd,
223 DeleteToNextSubwordEnd,
224 DeleteToBeginningOfLine,
225 DeleteToEndOfLine,
226 CutToEndOfLine,
227 DuplicateLine,
228 MoveLineUp,
229 MoveLineDown,
230 JoinLines,
231 SortLinesCaseSensitive,
232 SortLinesCaseInsensitive,
233 ReverseLines,
234 ShuffleLines,
235 ConvertToUpperCase,
236 ConvertToLowerCase,
237 ConvertToTitleCase,
238 ConvertToSnakeCase,
239 ConvertToKebabCase,
240 ConvertToUpperCamelCase,
241 ConvertToLowerCamelCase,
242 Transpose,
243 Cut,
244 Copy,
245 Paste,
246 Undo,
247 Redo,
248 MoveUp,
249 PageUp,
250 MoveDown,
251 PageDown,
252 MoveLeft,
253 MoveRight,
254 MoveToPreviousWordStart,
255 MoveToPreviousSubwordStart,
256 MoveToNextWordEnd,
257 MoveToNextSubwordEnd,
258 MoveToBeginningOfLine,
259 MoveToEndOfLine,
260 MoveToStartOfParagraph,
261 MoveToEndOfParagraph,
262 MoveToBeginning,
263 MoveToEnd,
264 SelectUp,
265 SelectDown,
266 SelectLeft,
267 SelectRight,
268 SelectToPreviousWordStart,
269 SelectToPreviousSubwordStart,
270 SelectToNextWordEnd,
271 SelectToNextSubwordEnd,
272 SelectToStartOfParagraph,
273 SelectToEndOfParagraph,
274 SelectToBeginning,
275 SelectToEnd,
276 SelectAll,
277 SelectLine,
278 SplitSelectionIntoLines,
279 AddSelectionAbove,
280 AddSelectionBelow,
281 Tab,
282 TabPrev,
283 ShowCharacterPalette,
284 SelectLargerSyntaxNode,
285 SelectSmallerSyntaxNode,
286 GoToDefinition,
287 GoToDefinitionSplit,
288 GoToTypeDefinition,
289 GoToTypeDefinitionSplit,
290 MoveToEnclosingBracket,
291 UndoSelection,
292 RedoSelection,
293 FindAllReferences,
294 Rename,
295 ConfirmRename,
296 Fold,
297 UnfoldLines,
298 FoldSelectedRanges,
299 ShowCompletions,
300 OpenExcerpts,
301 RestartLanguageServer,
302 Hover,
303 Format,
304 ToggleSoftWrap,
305 ToggleInlayHints,
306 RevealInFinder,
307 CopyPath,
308 CopyRelativePath,
309 CopyHighlightJson,
310 ]
311);
312
313impl_actions!(
314 editor,
315 [
316 SelectNext,
317 SelectPrevious,
318 SelectToBeginningOfLine,
319 SelectToEndOfLine,
320 ToggleCodeActions,
321 MovePageUp,
322 MovePageDown,
323 ConfirmCompletion,
324 ConfirmCodeAction,
325 ToggleComments,
326 FoldAt,
327 UnfoldAt,
328 GutterHover
329 ]
330);
331
332enum DocumentHighlightRead {}
333enum DocumentHighlightWrite {}
334enum InputComposition {}
335
336#[derive(Copy, Clone, PartialEq, Eq)]
337pub enum Direction {
338 Prev,
339 Next,
340}
341
342pub fn init_settings(cx: &mut AppContext) {
343 settings::register::<EditorSettings>(cx);
344}
345
346pub fn init(cx: &mut AppContext) {
347 init_settings(cx);
348 cx.add_action(Editor::new_file);
349 cx.add_action(Editor::cancel);
350 cx.add_action(Editor::newline);
351 cx.add_action(Editor::newline_above);
352 cx.add_action(Editor::newline_below);
353 cx.add_action(Editor::backspace);
354 cx.add_action(Editor::delete);
355 cx.add_action(Editor::tab);
356 cx.add_action(Editor::tab_prev);
357 cx.add_action(Editor::indent);
358 cx.add_action(Editor::outdent);
359 cx.add_action(Editor::delete_line);
360 cx.add_action(Editor::join_lines);
361 cx.add_action(Editor::sort_lines_case_sensitive);
362 cx.add_action(Editor::sort_lines_case_insensitive);
363 cx.add_action(Editor::reverse_lines);
364 cx.add_action(Editor::shuffle_lines);
365 cx.add_action(Editor::convert_to_upper_case);
366 cx.add_action(Editor::convert_to_lower_case);
367 cx.add_action(Editor::convert_to_title_case);
368 cx.add_action(Editor::convert_to_snake_case);
369 cx.add_action(Editor::convert_to_kebab_case);
370 cx.add_action(Editor::convert_to_upper_camel_case);
371 cx.add_action(Editor::convert_to_lower_camel_case);
372 cx.add_action(Editor::delete_to_previous_word_start);
373 cx.add_action(Editor::delete_to_previous_subword_start);
374 cx.add_action(Editor::delete_to_next_word_end);
375 cx.add_action(Editor::delete_to_next_subword_end);
376 cx.add_action(Editor::delete_to_beginning_of_line);
377 cx.add_action(Editor::delete_to_end_of_line);
378 cx.add_action(Editor::cut_to_end_of_line);
379 cx.add_action(Editor::duplicate_line);
380 cx.add_action(Editor::move_line_up);
381 cx.add_action(Editor::move_line_down);
382 cx.add_action(Editor::transpose);
383 cx.add_action(Editor::cut);
384 cx.add_action(Editor::copy);
385 cx.add_action(Editor::paste);
386 cx.add_action(Editor::undo);
387 cx.add_action(Editor::redo);
388 cx.add_action(Editor::move_up);
389 cx.add_action(Editor::move_page_up);
390 cx.add_action(Editor::move_down);
391 cx.add_action(Editor::move_page_down);
392 cx.add_action(Editor::next_screen);
393 cx.add_action(Editor::move_left);
394 cx.add_action(Editor::move_right);
395 cx.add_action(Editor::move_to_previous_word_start);
396 cx.add_action(Editor::move_to_previous_subword_start);
397 cx.add_action(Editor::move_to_next_word_end);
398 cx.add_action(Editor::move_to_next_subword_end);
399 cx.add_action(Editor::move_to_beginning_of_line);
400 cx.add_action(Editor::move_to_end_of_line);
401 cx.add_action(Editor::move_to_start_of_paragraph);
402 cx.add_action(Editor::move_to_end_of_paragraph);
403 cx.add_action(Editor::move_to_beginning);
404 cx.add_action(Editor::move_to_end);
405 cx.add_action(Editor::select_up);
406 cx.add_action(Editor::select_down);
407 cx.add_action(Editor::select_left);
408 cx.add_action(Editor::select_right);
409 cx.add_action(Editor::select_to_previous_word_start);
410 cx.add_action(Editor::select_to_previous_subword_start);
411 cx.add_action(Editor::select_to_next_word_end);
412 cx.add_action(Editor::select_to_next_subword_end);
413 cx.add_action(Editor::select_to_beginning_of_line);
414 cx.add_action(Editor::select_to_end_of_line);
415 cx.add_action(Editor::select_to_start_of_paragraph);
416 cx.add_action(Editor::select_to_end_of_paragraph);
417 cx.add_action(Editor::select_to_beginning);
418 cx.add_action(Editor::select_to_end);
419 cx.add_action(Editor::select_all);
420 cx.add_action(Editor::select_line);
421 cx.add_action(Editor::split_selection_into_lines);
422 cx.add_action(Editor::add_selection_above);
423 cx.add_action(Editor::add_selection_below);
424 cx.add_action(Editor::select_next);
425 cx.add_action(Editor::select_previous);
426 cx.add_action(Editor::toggle_comments);
427 cx.add_action(Editor::select_larger_syntax_node);
428 cx.add_action(Editor::select_smaller_syntax_node);
429 cx.add_action(Editor::move_to_enclosing_bracket);
430 cx.add_action(Editor::undo_selection);
431 cx.add_action(Editor::redo_selection);
432 cx.add_action(Editor::go_to_diagnostic);
433 cx.add_action(Editor::go_to_prev_diagnostic);
434 cx.add_action(Editor::go_to_hunk);
435 cx.add_action(Editor::go_to_prev_hunk);
436 cx.add_action(Editor::go_to_definition);
437 cx.add_action(Editor::go_to_definition_split);
438 cx.add_action(Editor::go_to_type_definition);
439 cx.add_action(Editor::go_to_type_definition_split);
440 cx.add_action(Editor::fold);
441 cx.add_action(Editor::fold_at);
442 cx.add_action(Editor::unfold_lines);
443 cx.add_action(Editor::unfold_at);
444 cx.add_action(Editor::gutter_hover);
445 cx.add_action(Editor::fold_selected_ranges);
446 cx.add_action(Editor::show_completions);
447 cx.add_action(Editor::toggle_code_actions);
448 cx.add_action(Editor::open_excerpts);
449 cx.add_action(Editor::toggle_soft_wrap);
450 cx.add_action(Editor::toggle_inlay_hints);
451 cx.add_action(Editor::reveal_in_finder);
452 cx.add_action(Editor::copy_path);
453 cx.add_action(Editor::copy_relative_path);
454 cx.add_action(Editor::copy_highlight_json);
455 cx.add_async_action(Editor::format);
456 cx.add_action(Editor::restart_language_server);
457 cx.add_action(Editor::show_character_palette);
458 cx.add_async_action(Editor::confirm_completion);
459 cx.add_async_action(Editor::confirm_code_action);
460 cx.add_async_action(Editor::rename);
461 cx.add_async_action(Editor::confirm_rename);
462 cx.add_async_action(Editor::find_all_references);
463 cx.add_action(Editor::next_copilot_suggestion);
464 cx.add_action(Editor::previous_copilot_suggestion);
465 cx.add_action(Editor::copilot_suggest);
466
467 hover_popover::init(cx);
468 scroll::actions::init(cx);
469
470 workspace::register_project_item::<Editor>(cx);
471 workspace::register_followable_item::<Editor>(cx);
472 workspace::register_deserializable_item::<Editor>(cx);
473}
474
475trait InvalidationRegion {
476 fn ranges(&self) -> &[Range<Anchor>];
477}
478
479#[derive(Clone, Debug, PartialEq)]
480pub enum SelectPhase {
481 Begin {
482 position: DisplayPoint,
483 add: bool,
484 click_count: usize,
485 },
486 BeginColumnar {
487 position: DisplayPoint,
488 goal_column: u32,
489 },
490 Extend {
491 position: DisplayPoint,
492 click_count: usize,
493 },
494 Update {
495 position: DisplayPoint,
496 goal_column: u32,
497 scroll_position: Vector2F,
498 },
499 End,
500}
501
502#[derive(Clone, Debug)]
503pub enum SelectMode {
504 Character,
505 Word(Range<Anchor>),
506 Line(Range<Anchor>),
507 All,
508}
509
510#[derive(Copy, Clone, PartialEq, Eq, Debug)]
511pub enum EditorMode {
512 SingleLine,
513 AutoHeight { max_lines: usize },
514 Full,
515}
516
517#[derive(Clone, Debug)]
518pub enum SoftWrap {
519 None,
520 EditorWidth,
521 Column(u32),
522}
523
524#[derive(Clone)]
525pub struct EditorStyle {
526 pub text: TextStyle,
527 pub line_height_scalar: f32,
528 pub placeholder_text: Option<TextStyle>,
529 pub theme: theme::Editor,
530 pub theme_id: usize,
531}
532
533type CompletionId = usize;
534
535type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
536type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
537
538pub struct Editor {
539 handle: WeakViewHandle<Self>,
540 buffer: ModelHandle<MultiBuffer>,
541 display_map: ModelHandle<DisplayMap>,
542 pub selections: SelectionsCollection,
543 pub scroll_manager: ScrollManager,
544 columnar_selection_tail: Option<Anchor>,
545 add_selections_state: Option<AddSelectionsState>,
546 select_next_state: Option<SelectNextState>,
547 select_prev_state: Option<SelectNextState>,
548 selection_history: SelectionHistory,
549 autoclose_regions: Vec<AutocloseRegion>,
550 snippet_stack: InvalidationStack<SnippetState>,
551 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
552 ime_transaction: Option<TransactionId>,
553 active_diagnostics: Option<ActiveDiagnosticGroup>,
554 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
555 get_field_editor_theme: Option<Arc<GetFieldEditorTheme>>,
556 override_text_style: Option<Box<OverrideTextStyle>>,
557 project: Option<ModelHandle<Project>>,
558 focused: bool,
559 blink_manager: ModelHandle<BlinkManager>,
560 show_local_selections: bool,
561 mode: EditorMode,
562 show_gutter: bool,
563 show_wrap_guides: Option<bool>,
564 placeholder_text: Option<Arc<str>>,
565 highlighted_rows: Option<Range<u32>>,
566 #[allow(clippy::type_complexity)]
567 background_highlights: BTreeMap<TypeId, (fn(&Theme) -> Color, Vec<Range<Anchor>>)>,
568 nav_history: Option<ItemNavHistory>,
569 context_menu: Option<ContextMenu>,
570 mouse_context_menu: ViewHandle<context_menu::ContextMenu>,
571 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
572 next_completion_id: CompletionId,
573 available_code_actions: Option<(ModelHandle<Buffer>, Arc<[CodeAction]>)>,
574 code_actions_task: Option<Task<()>>,
575 document_highlights_task: Option<Task<()>>,
576 pending_rename: Option<RenameState>,
577 searchable: bool,
578 cursor_shape: CursorShape,
579 collapse_matches: bool,
580 autoindent_mode: Option<AutoindentMode>,
581 workspace: Option<(WeakViewHandle<Workspace>, i64)>,
582 keymap_context_layers: BTreeMap<TypeId, KeymapContext>,
583 input_enabled: bool,
584 read_only: bool,
585 leader_replica_id: Option<u16>,
586 remote_id: Option<ViewId>,
587 hover_state: HoverState,
588 gutter_hovered: bool,
589 link_go_to_definition_state: LinkGoToDefinitionState,
590 copilot_state: CopilotState,
591 inlay_hint_cache: InlayHintCache,
592 next_inlay_id: usize,
593 _subscriptions: Vec<Subscription>,
594 pixel_position_of_newest_cursor: Option<Vector2F>,
595}
596
597pub struct EditorSnapshot {
598 pub mode: EditorMode,
599 pub show_gutter: bool,
600 pub display_snapshot: DisplaySnapshot,
601 pub placeholder_text: Option<Arc<str>>,
602 is_focused: bool,
603 scroll_anchor: ScrollAnchor,
604 ongoing_scroll: OngoingScroll,
605}
606
607#[derive(Clone, Debug)]
608struct SelectionHistoryEntry {
609 selections: Arc<[Selection<Anchor>]>,
610 select_next_state: Option<SelectNextState>,
611 select_prev_state: Option<SelectNextState>,
612 add_selections_state: Option<AddSelectionsState>,
613}
614
615enum SelectionHistoryMode {
616 Normal,
617 Undoing,
618 Redoing,
619}
620
621impl Default for SelectionHistoryMode {
622 fn default() -> Self {
623 Self::Normal
624 }
625}
626
627#[derive(Default)]
628struct SelectionHistory {
629 #[allow(clippy::type_complexity)]
630 selections_by_transaction:
631 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
632 mode: SelectionHistoryMode,
633 undo_stack: VecDeque<SelectionHistoryEntry>,
634 redo_stack: VecDeque<SelectionHistoryEntry>,
635}
636
637impl SelectionHistory {
638 fn insert_transaction(
639 &mut self,
640 transaction_id: TransactionId,
641 selections: Arc<[Selection<Anchor>]>,
642 ) {
643 self.selections_by_transaction
644 .insert(transaction_id, (selections, None));
645 }
646
647 #[allow(clippy::type_complexity)]
648 fn transaction(
649 &self,
650 transaction_id: TransactionId,
651 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
652 self.selections_by_transaction.get(&transaction_id)
653 }
654
655 #[allow(clippy::type_complexity)]
656 fn transaction_mut(
657 &mut self,
658 transaction_id: TransactionId,
659 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
660 self.selections_by_transaction.get_mut(&transaction_id)
661 }
662
663 fn push(&mut self, entry: SelectionHistoryEntry) {
664 if !entry.selections.is_empty() {
665 match self.mode {
666 SelectionHistoryMode::Normal => {
667 self.push_undo(entry);
668 self.redo_stack.clear();
669 }
670 SelectionHistoryMode::Undoing => self.push_redo(entry),
671 SelectionHistoryMode::Redoing => self.push_undo(entry),
672 }
673 }
674 }
675
676 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
677 if self
678 .undo_stack
679 .back()
680 .map_or(true, |e| e.selections != entry.selections)
681 {
682 self.undo_stack.push_back(entry);
683 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
684 self.undo_stack.pop_front();
685 }
686 }
687 }
688
689 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
690 if self
691 .redo_stack
692 .back()
693 .map_or(true, |e| e.selections != entry.selections)
694 {
695 self.redo_stack.push_back(entry);
696 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
697 self.redo_stack.pop_front();
698 }
699 }
700 }
701}
702
703#[derive(Clone, Debug)]
704struct AddSelectionsState {
705 above: bool,
706 stack: Vec<usize>,
707}
708
709#[derive(Clone, Debug)]
710struct SelectNextState {
711 query: AhoCorasick,
712 wordwise: bool,
713 done: bool,
714}
715
716#[derive(Debug)]
717struct AutocloseRegion {
718 selection_id: usize,
719 range: Range<Anchor>,
720 pair: BracketPair,
721}
722
723#[derive(Debug)]
724struct SnippetState {
725 ranges: Vec<Vec<Range<Anchor>>>,
726 active_index: usize,
727}
728
729pub struct RenameState {
730 pub range: Range<Anchor>,
731 pub old_name: Arc<str>,
732 pub editor: ViewHandle<Editor>,
733 block_id: BlockId,
734}
735
736struct InvalidationStack<T>(Vec<T>);
737
738enum ContextMenu {
739 Completions(CompletionsMenu),
740 CodeActions(CodeActionsMenu),
741}
742
743impl ContextMenu {
744 fn select_first(&mut self, cx: &mut ViewContext<Editor>) -> bool {
745 if self.visible() {
746 match self {
747 ContextMenu::Completions(menu) => menu.select_first(cx),
748 ContextMenu::CodeActions(menu) => menu.select_first(cx),
749 }
750 true
751 } else {
752 false
753 }
754 }
755
756 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) -> bool {
757 if self.visible() {
758 match self {
759 ContextMenu::Completions(menu) => menu.select_prev(cx),
760 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
761 }
762 true
763 } else {
764 false
765 }
766 }
767
768 fn select_next(&mut self, cx: &mut ViewContext<Editor>) -> bool {
769 if self.visible() {
770 match self {
771 ContextMenu::Completions(menu) => menu.select_next(cx),
772 ContextMenu::CodeActions(menu) => menu.select_next(cx),
773 }
774 true
775 } else {
776 false
777 }
778 }
779
780 fn select_last(&mut self, cx: &mut ViewContext<Editor>) -> bool {
781 if self.visible() {
782 match self {
783 ContextMenu::Completions(menu) => menu.select_last(cx),
784 ContextMenu::CodeActions(menu) => menu.select_last(cx),
785 }
786 true
787 } else {
788 false
789 }
790 }
791
792 fn visible(&self) -> bool {
793 match self {
794 ContextMenu::Completions(menu) => menu.visible(),
795 ContextMenu::CodeActions(menu) => menu.visible(),
796 }
797 }
798
799 fn render(
800 &self,
801 cursor_position: DisplayPoint,
802 style: EditorStyle,
803 cx: &mut ViewContext<Editor>,
804 ) -> (DisplayPoint, AnyElement<Editor>) {
805 match self {
806 ContextMenu::Completions(menu) => (cursor_position, menu.render(style, cx)),
807 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, cx),
808 }
809 }
810}
811
812struct CompletionsMenu {
813 id: CompletionId,
814 initial_position: Anchor,
815 buffer: ModelHandle<Buffer>,
816 completions: Arc<[Completion]>,
817 match_candidates: Vec<StringMatchCandidate>,
818 matches: Arc<[StringMatch]>,
819 selected_item: usize,
820 list: UniformListState,
821}
822
823impl CompletionsMenu {
824 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
825 self.selected_item = 0;
826 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
827 cx.notify();
828 }
829
830 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
831 if self.selected_item > 0 {
832 self.selected_item -= 1;
833 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
834 }
835 cx.notify();
836 }
837
838 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
839 if self.selected_item + 1 < self.matches.len() {
840 self.selected_item += 1;
841 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
842 }
843 cx.notify();
844 }
845
846 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
847 self.selected_item = self.matches.len() - 1;
848 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
849 cx.notify();
850 }
851
852 fn visible(&self) -> bool {
853 !self.matches.is_empty()
854 }
855
856 fn render(&self, style: EditorStyle, cx: &mut ViewContext<Editor>) -> AnyElement<Editor> {
857 enum CompletionTag {}
858
859 let completions = self.completions.clone();
860 let matches = self.matches.clone();
861 let selected_item = self.selected_item;
862 let container_style = style.autocomplete.container;
863 UniformList::new(
864 self.list.clone(),
865 matches.len(),
866 cx,
867 move |_, range, items, cx| {
868 let start_ix = range.start;
869 for (ix, mat) in matches[range].iter().enumerate() {
870 let completion = &completions[mat.candidate_id];
871 let item_ix = start_ix + ix;
872 items.push(
873 MouseEventHandler::new::<CompletionTag, _>(
874 mat.candidate_id,
875 cx,
876 |state, _| {
877 let item_style = if item_ix == selected_item {
878 style.autocomplete.selected_item
879 } else if state.hovered() {
880 style.autocomplete.hovered_item
881 } else {
882 style.autocomplete.item
883 };
884
885 Text::new(completion.label.text.clone(), style.text.clone())
886 .with_soft_wrap(false)
887 .with_highlights(combine_syntax_and_fuzzy_match_highlights(
888 &completion.label.text,
889 style.text.color.into(),
890 styled_runs_for_code_label(
891 &completion.label,
892 &style.syntax,
893 ),
894 &mat.positions,
895 ))
896 .contained()
897 .with_style(item_style)
898 },
899 )
900 .with_cursor_style(CursorStyle::PointingHand)
901 .on_down(MouseButton::Left, move |_, this, cx| {
902 this.confirm_completion(
903 &ConfirmCompletion {
904 item_ix: Some(item_ix),
905 },
906 cx,
907 );
908 })
909 .into_any(),
910 );
911 }
912 },
913 )
914 .with_width_from_item(
915 self.matches
916 .iter()
917 .enumerate()
918 .max_by_key(|(_, mat)| {
919 self.completions[mat.candidate_id]
920 .label
921 .text
922 .chars()
923 .count()
924 })
925 .map(|(ix, _)| ix),
926 )
927 .contained()
928 .with_style(container_style)
929 .into_any()
930 }
931
932 pub async fn filter(&mut self, query: Option<&str>, executor: Arc<executor::Background>) {
933 let mut matches = if let Some(query) = query {
934 fuzzy::match_strings(
935 &self.match_candidates,
936 query,
937 query.chars().any(|c| c.is_uppercase()),
938 100,
939 &Default::default(),
940 executor,
941 )
942 .await
943 } else {
944 self.match_candidates
945 .iter()
946 .enumerate()
947 .map(|(candidate_id, candidate)| StringMatch {
948 candidate_id,
949 score: Default::default(),
950 positions: Default::default(),
951 string: candidate.string.clone(),
952 })
953 .collect()
954 };
955
956 //Remove all candidates where the query's start does not match the start of any word in the candidate
957 if let Some(query) = query {
958 if let Some(query_start) = query.chars().next() {
959 matches.retain(|string_match| {
960 split_words(&string_match.string).any(|word| {
961 //Check that the first codepoint of the word as lowercase matches the first
962 //codepoint of the query as lowercase
963 word.chars()
964 .flat_map(|codepoint| codepoint.to_lowercase())
965 .zip(query_start.to_lowercase())
966 .all(|(word_cp, query_cp)| word_cp == query_cp)
967 })
968 });
969 }
970 }
971
972 matches.sort_unstable_by_key(|mat| {
973 let completion = &self.completions[mat.candidate_id];
974 (
975 completion.lsp_completion.sort_text.as_ref(),
976 Reverse(OrderedFloat(mat.score)),
977 completion.sort_key(),
978 )
979 });
980
981 for mat in &mut matches {
982 let filter_start = self.completions[mat.candidate_id].label.filter_range.start;
983 for position in &mut mat.positions {
984 *position += filter_start;
985 }
986 }
987
988 self.matches = matches.into();
989 }
990}
991
992#[derive(Clone)]
993struct CodeActionsMenu {
994 actions: Arc<[CodeAction]>,
995 buffer: ModelHandle<Buffer>,
996 selected_item: usize,
997 list: UniformListState,
998 deployed_from_indicator: bool,
999}
1000
1001impl CodeActionsMenu {
1002 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
1003 self.selected_item = 0;
1004 cx.notify()
1005 }
1006
1007 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
1008 if self.selected_item > 0 {
1009 self.selected_item -= 1;
1010 cx.notify()
1011 }
1012 }
1013
1014 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
1015 if self.selected_item + 1 < self.actions.len() {
1016 self.selected_item += 1;
1017 cx.notify()
1018 }
1019 }
1020
1021 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
1022 self.selected_item = self.actions.len() - 1;
1023 cx.notify()
1024 }
1025
1026 fn visible(&self) -> bool {
1027 !self.actions.is_empty()
1028 }
1029
1030 fn render(
1031 &self,
1032 mut cursor_position: DisplayPoint,
1033 style: EditorStyle,
1034 cx: &mut ViewContext<Editor>,
1035 ) -> (DisplayPoint, AnyElement<Editor>) {
1036 enum ActionTag {}
1037
1038 let container_style = style.autocomplete.container;
1039 let actions = self.actions.clone();
1040 let selected_item = self.selected_item;
1041 let element = UniformList::new(
1042 self.list.clone(),
1043 actions.len(),
1044 cx,
1045 move |_, range, items, cx| {
1046 let start_ix = range.start;
1047 for (ix, action) in actions[range].iter().enumerate() {
1048 let item_ix = start_ix + ix;
1049 items.push(
1050 MouseEventHandler::new::<ActionTag, _>(item_ix, cx, |state, _| {
1051 let item_style = if item_ix == selected_item {
1052 style.autocomplete.selected_item
1053 } else if state.hovered() {
1054 style.autocomplete.hovered_item
1055 } else {
1056 style.autocomplete.item
1057 };
1058
1059 Text::new(action.lsp_action.title.clone(), style.text.clone())
1060 .with_soft_wrap(false)
1061 .contained()
1062 .with_style(item_style)
1063 })
1064 .with_cursor_style(CursorStyle::PointingHand)
1065 .on_down(MouseButton::Left, move |_, this, cx| {
1066 let workspace = this
1067 .workspace
1068 .as_ref()
1069 .and_then(|(workspace, _)| workspace.upgrade(cx));
1070 cx.window_context().defer(move |cx| {
1071 if let Some(workspace) = workspace {
1072 workspace.update(cx, |workspace, cx| {
1073 if let Some(task) = Editor::confirm_code_action(
1074 workspace,
1075 &Default::default(),
1076 cx,
1077 ) {
1078 task.detach_and_log_err(cx);
1079 }
1080 });
1081 }
1082 });
1083 })
1084 .into_any(),
1085 );
1086 }
1087 },
1088 )
1089 .with_width_from_item(
1090 self.actions
1091 .iter()
1092 .enumerate()
1093 .max_by_key(|(_, action)| action.lsp_action.title.chars().count())
1094 .map(|(ix, _)| ix),
1095 )
1096 .contained()
1097 .with_style(container_style)
1098 .into_any();
1099
1100 if self.deployed_from_indicator {
1101 *cursor_position.column_mut() = 0;
1102 }
1103
1104 (cursor_position, element)
1105 }
1106}
1107
1108pub struct CopilotState {
1109 excerpt_id: Option<ExcerptId>,
1110 pending_refresh: Task<Option<()>>,
1111 pending_cycling_refresh: Task<Option<()>>,
1112 cycled: bool,
1113 completions: Vec<copilot::Completion>,
1114 active_completion_index: usize,
1115 suggestion: Option<Inlay>,
1116}
1117
1118impl Default for CopilotState {
1119 fn default() -> Self {
1120 Self {
1121 excerpt_id: None,
1122 pending_cycling_refresh: Task::ready(Some(())),
1123 pending_refresh: Task::ready(Some(())),
1124 completions: Default::default(),
1125 active_completion_index: 0,
1126 cycled: false,
1127 suggestion: None,
1128 }
1129 }
1130}
1131
1132impl CopilotState {
1133 fn active_completion(&self) -> Option<&copilot::Completion> {
1134 self.completions.get(self.active_completion_index)
1135 }
1136
1137 fn text_for_active_completion(
1138 &self,
1139 cursor: Anchor,
1140 buffer: &MultiBufferSnapshot,
1141 ) -> Option<&str> {
1142 use language::ToOffset as _;
1143
1144 let completion = self.active_completion()?;
1145 let excerpt_id = self.excerpt_id?;
1146 let completion_buffer = buffer.buffer_for_excerpt(excerpt_id)?;
1147 if excerpt_id != cursor.excerpt_id
1148 || !completion.range.start.is_valid(completion_buffer)
1149 || !completion.range.end.is_valid(completion_buffer)
1150 {
1151 return None;
1152 }
1153
1154 let mut completion_range = completion.range.to_offset(&completion_buffer);
1155 let prefix_len = Self::common_prefix(
1156 completion_buffer.chars_for_range(completion_range.clone()),
1157 completion.text.chars(),
1158 );
1159 completion_range.start += prefix_len;
1160 let suffix_len = Self::common_prefix(
1161 completion_buffer.reversed_chars_for_range(completion_range.clone()),
1162 completion.text[prefix_len..].chars().rev(),
1163 );
1164 completion_range.end = completion_range.end.saturating_sub(suffix_len);
1165
1166 if completion_range.is_empty()
1167 && completion_range.start == cursor.text_anchor.to_offset(&completion_buffer)
1168 {
1169 Some(&completion.text[prefix_len..completion.text.len() - suffix_len])
1170 } else {
1171 None
1172 }
1173 }
1174
1175 fn cycle_completions(&mut self, direction: Direction) {
1176 match direction {
1177 Direction::Prev => {
1178 self.active_completion_index = if self.active_completion_index == 0 {
1179 self.completions.len().saturating_sub(1)
1180 } else {
1181 self.active_completion_index - 1
1182 };
1183 }
1184 Direction::Next => {
1185 if self.completions.len() == 0 {
1186 self.active_completion_index = 0
1187 } else {
1188 self.active_completion_index =
1189 (self.active_completion_index + 1) % self.completions.len();
1190 }
1191 }
1192 }
1193 }
1194
1195 fn push_completion(&mut self, new_completion: copilot::Completion) {
1196 for completion in &self.completions {
1197 if completion.text == new_completion.text && completion.range == new_completion.range {
1198 return;
1199 }
1200 }
1201 self.completions.push(new_completion);
1202 }
1203
1204 fn common_prefix<T1: Iterator<Item = char>, T2: Iterator<Item = char>>(a: T1, b: T2) -> usize {
1205 a.zip(b)
1206 .take_while(|(a, b)| a == b)
1207 .map(|(a, _)| a.len_utf8())
1208 .sum()
1209 }
1210}
1211
1212#[derive(Debug)]
1213struct ActiveDiagnosticGroup {
1214 primary_range: Range<Anchor>,
1215 primary_message: String,
1216 blocks: HashMap<BlockId, Diagnostic>,
1217 is_valid: bool,
1218}
1219
1220#[derive(Serialize, Deserialize)]
1221pub struct ClipboardSelection {
1222 pub len: usize,
1223 pub is_entire_line: bool,
1224 pub first_line_indent: u32,
1225}
1226
1227#[derive(Debug)]
1228pub struct NavigationData {
1229 cursor_anchor: Anchor,
1230 cursor_position: Point,
1231 scroll_anchor: ScrollAnchor,
1232 scroll_top_row: u32,
1233}
1234
1235pub struct EditorCreated(pub ViewHandle<Editor>);
1236
1237enum GotoDefinitionKind {
1238 Symbol,
1239 Type,
1240}
1241
1242#[derive(Debug, Clone)]
1243enum InlayHintRefreshReason {
1244 Toggle(bool),
1245 SettingsChange(InlayHintSettings),
1246 NewLinesShown,
1247 BufferEdited(HashSet<Arc<Language>>),
1248 RefreshRequested,
1249}
1250
1251impl Editor {
1252 pub fn single_line(
1253 field_editor_style: Option<Arc<GetFieldEditorTheme>>,
1254 cx: &mut ViewContext<Self>,
1255 ) -> Self {
1256 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
1257 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1258 Self::new(EditorMode::SingleLine, buffer, None, field_editor_style, cx)
1259 }
1260
1261 pub fn multi_line(
1262 field_editor_style: Option<Arc<GetFieldEditorTheme>>,
1263 cx: &mut ViewContext<Self>,
1264 ) -> Self {
1265 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
1266 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1267 Self::new(EditorMode::Full, buffer, None, field_editor_style, cx)
1268 }
1269
1270 pub fn auto_height(
1271 max_lines: usize,
1272 field_editor_style: Option<Arc<GetFieldEditorTheme>>,
1273 cx: &mut ViewContext<Self>,
1274 ) -> Self {
1275 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
1276 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1277 Self::new(
1278 EditorMode::AutoHeight { max_lines },
1279 buffer,
1280 None,
1281 field_editor_style,
1282 cx,
1283 )
1284 }
1285
1286 pub fn for_buffer(
1287 buffer: ModelHandle<Buffer>,
1288 project: Option<ModelHandle<Project>>,
1289 cx: &mut ViewContext<Self>,
1290 ) -> Self {
1291 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1292 Self::new(EditorMode::Full, buffer, project, None, cx)
1293 }
1294
1295 pub fn for_multibuffer(
1296 buffer: ModelHandle<MultiBuffer>,
1297 project: Option<ModelHandle<Project>>,
1298 cx: &mut ViewContext<Self>,
1299 ) -> Self {
1300 Self::new(EditorMode::Full, buffer, project, None, cx)
1301 }
1302
1303 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1304 let mut clone = Self::new(
1305 self.mode,
1306 self.buffer.clone(),
1307 self.project.clone(),
1308 self.get_field_editor_theme.clone(),
1309 cx,
1310 );
1311 self.display_map.update(cx, |display_map, cx| {
1312 let snapshot = display_map.snapshot(cx);
1313 clone.display_map.update(cx, |display_map, cx| {
1314 display_map.set_state(&snapshot, cx);
1315 });
1316 });
1317 clone.selections.clone_state(&self.selections);
1318 clone.scroll_manager.clone_state(&self.scroll_manager);
1319 clone.searchable = self.searchable;
1320 clone
1321 }
1322
1323 fn new(
1324 mode: EditorMode,
1325 buffer: ModelHandle<MultiBuffer>,
1326 project: Option<ModelHandle<Project>>,
1327 get_field_editor_theme: Option<Arc<GetFieldEditorTheme>>,
1328 cx: &mut ViewContext<Self>,
1329 ) -> Self {
1330 let editor_view_id = cx.view_id();
1331 let display_map = cx.add_model(|cx| {
1332 let settings = settings::get::<ThemeSettings>(cx);
1333 let style = build_style(settings, get_field_editor_theme.as_deref(), None, cx);
1334 DisplayMap::new(
1335 buffer.clone(),
1336 style.text.font_id,
1337 style.text.font_size,
1338 None,
1339 2,
1340 1,
1341 cx,
1342 )
1343 });
1344
1345 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1346
1347 let blink_manager = cx.add_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1348
1349 let soft_wrap_mode_override =
1350 (mode == EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
1351
1352 let mut project_subscriptions = Vec::new();
1353 if mode == EditorMode::Full {
1354 if let Some(project) = project.as_ref() {
1355 if buffer.read(cx).is_singleton() {
1356 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1357 cx.emit(Event::TitleChanged);
1358 }));
1359 }
1360 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1361 if let project::Event::RefreshInlayHints = event {
1362 editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1363 };
1364 }));
1365 }
1366 }
1367
1368 let inlay_hint_settings = inlay_hint_settings(
1369 selections.newest_anchor().head(),
1370 &buffer.read(cx).snapshot(cx),
1371 cx,
1372 );
1373
1374 let mut this = Self {
1375 handle: cx.weak_handle(),
1376 buffer: buffer.clone(),
1377 display_map: display_map.clone(),
1378 selections,
1379 scroll_manager: ScrollManager::new(),
1380 columnar_selection_tail: None,
1381 add_selections_state: None,
1382 select_next_state: None,
1383 select_prev_state: None,
1384 selection_history: Default::default(),
1385 autoclose_regions: Default::default(),
1386 snippet_stack: Default::default(),
1387 select_larger_syntax_node_stack: Vec::new(),
1388 ime_transaction: Default::default(),
1389 active_diagnostics: None,
1390 soft_wrap_mode_override,
1391 get_field_editor_theme,
1392 project,
1393 focused: false,
1394 blink_manager: blink_manager.clone(),
1395 show_local_selections: true,
1396 mode,
1397 show_gutter: mode == EditorMode::Full,
1398 show_wrap_guides: None,
1399 placeholder_text: None,
1400 highlighted_rows: None,
1401 background_highlights: Default::default(),
1402 nav_history: None,
1403 context_menu: None,
1404 mouse_context_menu: cx
1405 .add_view(|cx| context_menu::ContextMenu::new(editor_view_id, cx)),
1406 completion_tasks: Default::default(),
1407 next_completion_id: 0,
1408 next_inlay_id: 0,
1409 available_code_actions: Default::default(),
1410 code_actions_task: Default::default(),
1411 document_highlights_task: Default::default(),
1412 pending_rename: Default::default(),
1413 searchable: true,
1414 override_text_style: None,
1415 cursor_shape: Default::default(),
1416 autoindent_mode: Some(AutoindentMode::EachLine),
1417 collapse_matches: false,
1418 workspace: None,
1419 keymap_context_layers: Default::default(),
1420 input_enabled: true,
1421 read_only: false,
1422 leader_replica_id: None,
1423 remote_id: None,
1424 hover_state: Default::default(),
1425 link_go_to_definition_state: Default::default(),
1426 copilot_state: Default::default(),
1427 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1428 gutter_hovered: false,
1429 pixel_position_of_newest_cursor: None,
1430 _subscriptions: vec![
1431 cx.observe(&buffer, Self::on_buffer_changed),
1432 cx.subscribe(&buffer, Self::on_buffer_event),
1433 cx.observe(&display_map, Self::on_display_map_changed),
1434 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1435 cx.observe_global::<SettingsStore, _>(Self::settings_changed),
1436 ],
1437 };
1438
1439 this._subscriptions.extend(project_subscriptions);
1440
1441 this.end_selection(cx);
1442 this.scroll_manager.show_scrollbar(cx);
1443
1444 let editor_created_event = EditorCreated(cx.handle());
1445 cx.emit_global(editor_created_event);
1446
1447 if mode == EditorMode::Full {
1448 let should_auto_hide_scrollbars = cx.platform().should_auto_hide_scrollbars();
1449 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1450 }
1451
1452 this.report_editor_event("open", None, cx);
1453 this
1454 }
1455
1456 pub fn new_file(
1457 workspace: &mut Workspace,
1458 _: &workspace::NewFile,
1459 cx: &mut ViewContext<Workspace>,
1460 ) {
1461 let project = workspace.project().clone();
1462 if project.read(cx).is_remote() {
1463 cx.propagate_action();
1464 } else if let Some(buffer) = project
1465 .update(cx, |project, cx| project.create_buffer("", None, cx))
1466 .log_err()
1467 {
1468 workspace.add_item(
1469 Box::new(cx.add_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx))),
1470 cx,
1471 );
1472 }
1473 }
1474
1475 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
1476 self.buffer.read(cx).replica_id()
1477 }
1478
1479 pub fn leader_replica_id(&self) -> Option<ReplicaId> {
1480 self.leader_replica_id
1481 }
1482
1483 pub fn buffer(&self) -> &ModelHandle<MultiBuffer> {
1484 &self.buffer
1485 }
1486
1487 fn workspace(&self, cx: &AppContext) -> Option<ViewHandle<Workspace>> {
1488 self.workspace.as_ref()?.0.upgrade(cx)
1489 }
1490
1491 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
1492 self.buffer().read(cx).title(cx)
1493 }
1494
1495 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
1496 EditorSnapshot {
1497 mode: self.mode,
1498 show_gutter: self.show_gutter,
1499 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1500 scroll_anchor: self.scroll_manager.anchor(),
1501 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1502 placeholder_text: self.placeholder_text.clone(),
1503 is_focused: self
1504 .handle
1505 .upgrade(cx)
1506 .map_or(false, |handle| handle.is_focused(cx)),
1507 }
1508 }
1509
1510 pub fn language_at<'a, T: ToOffset>(
1511 &self,
1512 point: T,
1513 cx: &'a AppContext,
1514 ) -> Option<Arc<Language>> {
1515 self.buffer.read(cx).language_at(point, cx)
1516 }
1517
1518 pub fn file_at<'a, T: ToOffset>(&self, point: T, cx: &'a AppContext) -> Option<Arc<dyn File>> {
1519 self.buffer.read(cx).read(cx).file_at(point).cloned()
1520 }
1521
1522 pub fn active_excerpt(
1523 &self,
1524 cx: &AppContext,
1525 ) -> Option<(ExcerptId, ModelHandle<Buffer>, Range<text::Anchor>)> {
1526 self.buffer
1527 .read(cx)
1528 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1529 }
1530
1531 fn style(&self, cx: &AppContext) -> EditorStyle {
1532 build_style(
1533 settings::get::<ThemeSettings>(cx),
1534 self.get_field_editor_theme.as_deref(),
1535 self.override_text_style.as_deref(),
1536 cx,
1537 )
1538 }
1539
1540 pub fn mode(&self) -> EditorMode {
1541 self.mode
1542 }
1543
1544 pub fn set_placeholder_text(
1545 &mut self,
1546 placeholder_text: impl Into<Arc<str>>,
1547 cx: &mut ViewContext<Self>,
1548 ) {
1549 self.placeholder_text = Some(placeholder_text.into());
1550 cx.notify();
1551 }
1552
1553 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
1554 self.cursor_shape = cursor_shape;
1555 cx.notify();
1556 }
1557
1558 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1559 self.collapse_matches = collapse_matches;
1560 }
1561
1562 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1563 if self.collapse_matches {
1564 return range.start..range.start;
1565 }
1566 range.clone()
1567 }
1568
1569 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
1570 if self.display_map.read(cx).clip_at_line_ends != clip {
1571 self.display_map
1572 .update(cx, |map, _| map.clip_at_line_ends = clip);
1573 }
1574 }
1575
1576 pub fn set_keymap_context_layer<Tag: 'static>(
1577 &mut self,
1578 context: KeymapContext,
1579 cx: &mut ViewContext<Self>,
1580 ) {
1581 self.keymap_context_layers
1582 .insert(TypeId::of::<Tag>(), context);
1583 cx.notify();
1584 }
1585
1586 pub fn remove_keymap_context_layer<Tag: 'static>(&mut self, cx: &mut ViewContext<Self>) {
1587 self.keymap_context_layers.remove(&TypeId::of::<Tag>());
1588 cx.notify();
1589 }
1590
1591 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1592 self.input_enabled = input_enabled;
1593 }
1594
1595 pub fn set_autoindent(&mut self, autoindent: bool) {
1596 if autoindent {
1597 self.autoindent_mode = Some(AutoindentMode::EachLine);
1598 } else {
1599 self.autoindent_mode = None;
1600 }
1601 }
1602
1603 pub fn set_read_only(&mut self, read_only: bool) {
1604 self.read_only = read_only;
1605 }
1606
1607 fn selections_did_change(
1608 &mut self,
1609 local: bool,
1610 old_cursor_position: &Anchor,
1611 cx: &mut ViewContext<Self>,
1612 ) {
1613 if self.focused && self.leader_replica_id.is_none() {
1614 self.buffer.update(cx, |buffer, cx| {
1615 buffer.set_active_selections(
1616 &self.selections.disjoint_anchors(),
1617 self.selections.line_mode,
1618 self.cursor_shape,
1619 cx,
1620 )
1621 });
1622 }
1623
1624 let display_map = self
1625 .display_map
1626 .update(cx, |display_map, cx| display_map.snapshot(cx));
1627 let buffer = &display_map.buffer_snapshot;
1628 self.add_selections_state = None;
1629 self.select_next_state = None;
1630 self.select_prev_state = None;
1631 self.select_larger_syntax_node_stack.clear();
1632 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
1633 self.snippet_stack
1634 .invalidate(&self.selections.disjoint_anchors(), buffer);
1635 self.take_rename(false, cx);
1636
1637 let new_cursor_position = self.selections.newest_anchor().head();
1638
1639 self.push_to_nav_history(
1640 old_cursor_position.clone(),
1641 Some(new_cursor_position.to_point(buffer)),
1642 cx,
1643 );
1644
1645 if local {
1646 let new_cursor_position = self.selections.newest_anchor().head();
1647 let completion_menu = match self.context_menu.as_mut() {
1648 Some(ContextMenu::Completions(menu)) => Some(menu),
1649 _ => {
1650 self.context_menu.take();
1651 None
1652 }
1653 };
1654
1655 if let Some(completion_menu) = completion_menu {
1656 let cursor_position = new_cursor_position.to_offset(buffer);
1657 let (word_range, kind) =
1658 buffer.surrounding_word(completion_menu.initial_position.clone());
1659 if kind == Some(CharKind::Word)
1660 && word_range.to_inclusive().contains(&cursor_position)
1661 {
1662 let query = Self::completion_query(buffer, cursor_position);
1663 cx.background()
1664 .block(completion_menu.filter(query.as_deref(), cx.background().clone()));
1665 self.show_completions(&ShowCompletions, cx);
1666 } else {
1667 self.hide_context_menu(cx);
1668 }
1669 }
1670
1671 hide_hover(self, cx);
1672
1673 if old_cursor_position.to_display_point(&display_map).row()
1674 != new_cursor_position.to_display_point(&display_map).row()
1675 {
1676 self.available_code_actions.take();
1677 }
1678 self.refresh_code_actions(cx);
1679 self.refresh_document_highlights(cx);
1680 refresh_matching_bracket_highlights(self, cx);
1681 self.discard_copilot_suggestion(cx);
1682 }
1683
1684 self.blink_manager.update(cx, BlinkManager::pause_blinking);
1685 cx.emit(Event::SelectionsChanged { local });
1686 cx.notify();
1687 }
1688
1689 pub fn change_selections<R>(
1690 &mut self,
1691 autoscroll: Option<Autoscroll>,
1692 cx: &mut ViewContext<Self>,
1693 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
1694 ) -> R {
1695 let old_cursor_position = self.selections.newest_anchor().head();
1696 self.push_to_selection_history();
1697
1698 let (changed, result) = self.selections.change_with(cx, change);
1699
1700 if changed {
1701 if let Some(autoscroll) = autoscroll {
1702 self.request_autoscroll(autoscroll, cx);
1703 }
1704 self.selections_did_change(true, &old_cursor_position, cx);
1705 }
1706
1707 result
1708 }
1709
1710 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1711 where
1712 I: IntoIterator<Item = (Range<S>, T)>,
1713 S: ToOffset,
1714 T: Into<Arc<str>>,
1715 {
1716 if self.read_only {
1717 return;
1718 }
1719
1720 self.buffer
1721 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
1722 }
1723
1724 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1725 where
1726 I: IntoIterator<Item = (Range<S>, T)>,
1727 S: ToOffset,
1728 T: Into<Arc<str>>,
1729 {
1730 if self.read_only {
1731 return;
1732 }
1733
1734 self.buffer.update(cx, |buffer, cx| {
1735 buffer.edit(edits, self.autoindent_mode.clone(), cx)
1736 });
1737 }
1738
1739 pub fn edit_with_block_indent<I, S, T>(
1740 &mut self,
1741 edits: I,
1742 original_indent_columns: Vec<u32>,
1743 cx: &mut ViewContext<Self>,
1744 ) where
1745 I: IntoIterator<Item = (Range<S>, T)>,
1746 S: ToOffset,
1747 T: Into<Arc<str>>,
1748 {
1749 if self.read_only {
1750 return;
1751 }
1752
1753 self.buffer.update(cx, |buffer, cx| {
1754 buffer.edit(
1755 edits,
1756 Some(AutoindentMode::Block {
1757 original_indent_columns,
1758 }),
1759 cx,
1760 )
1761 });
1762 }
1763
1764 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
1765 self.hide_context_menu(cx);
1766
1767 match phase {
1768 SelectPhase::Begin {
1769 position,
1770 add,
1771 click_count,
1772 } => self.begin_selection(position, add, click_count, cx),
1773 SelectPhase::BeginColumnar {
1774 position,
1775 goal_column,
1776 } => self.begin_columnar_selection(position, goal_column, cx),
1777 SelectPhase::Extend {
1778 position,
1779 click_count,
1780 } => self.extend_selection(position, click_count, cx),
1781 SelectPhase::Update {
1782 position,
1783 goal_column,
1784 scroll_position,
1785 } => self.update_selection(position, goal_column, scroll_position, cx),
1786 SelectPhase::End => self.end_selection(cx),
1787 }
1788 }
1789
1790 fn extend_selection(
1791 &mut self,
1792 position: DisplayPoint,
1793 click_count: usize,
1794 cx: &mut ViewContext<Self>,
1795 ) {
1796 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1797 let tail = self.selections.newest::<usize>(cx).tail();
1798 self.begin_selection(position, false, click_count, cx);
1799
1800 let position = position.to_offset(&display_map, Bias::Left);
1801 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
1802
1803 let mut pending_selection = self
1804 .selections
1805 .pending_anchor()
1806 .expect("extend_selection not called with pending selection");
1807 if position >= tail {
1808 pending_selection.start = tail_anchor;
1809 } else {
1810 pending_selection.end = tail_anchor;
1811 pending_selection.reversed = true;
1812 }
1813
1814 let mut pending_mode = self.selections.pending_mode().unwrap();
1815 match &mut pending_mode {
1816 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
1817 _ => {}
1818 }
1819
1820 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
1821 s.set_pending(pending_selection, pending_mode)
1822 });
1823 }
1824
1825 fn begin_selection(
1826 &mut self,
1827 position: DisplayPoint,
1828 add: bool,
1829 click_count: usize,
1830 cx: &mut ViewContext<Self>,
1831 ) {
1832 if !self.focused {
1833 cx.focus_self();
1834 }
1835
1836 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1837 let buffer = &display_map.buffer_snapshot;
1838 let newest_selection = self.selections.newest_anchor().clone();
1839 let position = display_map.clip_point(position, Bias::Left);
1840
1841 let start;
1842 let end;
1843 let mode;
1844 let auto_scroll;
1845 match click_count {
1846 1 => {
1847 start = buffer.anchor_before(position.to_point(&display_map));
1848 end = start.clone();
1849 mode = SelectMode::Character;
1850 auto_scroll = true;
1851 }
1852 2 => {
1853 let range = movement::surrounding_word(&display_map, position);
1854 start = buffer.anchor_before(range.start.to_point(&display_map));
1855 end = buffer.anchor_before(range.end.to_point(&display_map));
1856 mode = SelectMode::Word(start.clone()..end.clone());
1857 auto_scroll = true;
1858 }
1859 3 => {
1860 let position = display_map
1861 .clip_point(position, Bias::Left)
1862 .to_point(&display_map);
1863 let line_start = display_map.prev_line_boundary(position).0;
1864 let next_line_start = buffer.clip_point(
1865 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1866 Bias::Left,
1867 );
1868 start = buffer.anchor_before(line_start);
1869 end = buffer.anchor_before(next_line_start);
1870 mode = SelectMode::Line(start.clone()..end.clone());
1871 auto_scroll = true;
1872 }
1873 _ => {
1874 start = buffer.anchor_before(0);
1875 end = buffer.anchor_before(buffer.len());
1876 mode = SelectMode::All;
1877 auto_scroll = false;
1878 }
1879 }
1880
1881 self.change_selections(auto_scroll.then(|| Autoscroll::newest()), cx, |s| {
1882 if !add {
1883 s.clear_disjoint();
1884 } else if click_count > 1 {
1885 s.delete(newest_selection.id)
1886 }
1887
1888 s.set_pending_anchor_range(start..end, mode);
1889 });
1890 }
1891
1892 fn begin_columnar_selection(
1893 &mut self,
1894 position: DisplayPoint,
1895 goal_column: u32,
1896 cx: &mut ViewContext<Self>,
1897 ) {
1898 if !self.focused {
1899 cx.focus_self();
1900 }
1901
1902 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1903 let tail = self.selections.newest::<Point>(cx).tail();
1904 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
1905
1906 self.select_columns(
1907 tail.to_display_point(&display_map),
1908 position,
1909 goal_column,
1910 &display_map,
1911 cx,
1912 );
1913 }
1914
1915 fn update_selection(
1916 &mut self,
1917 position: DisplayPoint,
1918 goal_column: u32,
1919 scroll_position: Vector2F,
1920 cx: &mut ViewContext<Self>,
1921 ) {
1922 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1923
1924 if let Some(tail) = self.columnar_selection_tail.as_ref() {
1925 let tail = tail.to_display_point(&display_map);
1926 self.select_columns(tail, position, goal_column, &display_map, cx);
1927 } else if let Some(mut pending) = self.selections.pending_anchor() {
1928 let buffer = self.buffer.read(cx).snapshot(cx);
1929 let head;
1930 let tail;
1931 let mode = self.selections.pending_mode().unwrap();
1932 match &mode {
1933 SelectMode::Character => {
1934 head = position.to_point(&display_map);
1935 tail = pending.tail().to_point(&buffer);
1936 }
1937 SelectMode::Word(original_range) => {
1938 let original_display_range = original_range.start.to_display_point(&display_map)
1939 ..original_range.end.to_display_point(&display_map);
1940 let original_buffer_range = original_display_range.start.to_point(&display_map)
1941 ..original_display_range.end.to_point(&display_map);
1942 if movement::is_inside_word(&display_map, position)
1943 || original_display_range.contains(&position)
1944 {
1945 let word_range = movement::surrounding_word(&display_map, position);
1946 if word_range.start < original_display_range.start {
1947 head = word_range.start.to_point(&display_map);
1948 } else {
1949 head = word_range.end.to_point(&display_map);
1950 }
1951 } else {
1952 head = position.to_point(&display_map);
1953 }
1954
1955 if head <= original_buffer_range.start {
1956 tail = original_buffer_range.end;
1957 } else {
1958 tail = original_buffer_range.start;
1959 }
1960 }
1961 SelectMode::Line(original_range) => {
1962 let original_range = original_range.to_point(&display_map.buffer_snapshot);
1963
1964 let position = display_map
1965 .clip_point(position, Bias::Left)
1966 .to_point(&display_map);
1967 let line_start = display_map.prev_line_boundary(position).0;
1968 let next_line_start = buffer.clip_point(
1969 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1970 Bias::Left,
1971 );
1972
1973 if line_start < original_range.start {
1974 head = line_start
1975 } else {
1976 head = next_line_start
1977 }
1978
1979 if head <= original_range.start {
1980 tail = original_range.end;
1981 } else {
1982 tail = original_range.start;
1983 }
1984 }
1985 SelectMode::All => {
1986 return;
1987 }
1988 };
1989
1990 if head < tail {
1991 pending.start = buffer.anchor_before(head);
1992 pending.end = buffer.anchor_before(tail);
1993 pending.reversed = true;
1994 } else {
1995 pending.start = buffer.anchor_before(tail);
1996 pending.end = buffer.anchor_before(head);
1997 pending.reversed = false;
1998 }
1999
2000 self.change_selections(None, cx, |s| {
2001 s.set_pending(pending, mode);
2002 });
2003 } else {
2004 error!("update_selection dispatched with no pending selection");
2005 return;
2006 }
2007
2008 self.set_scroll_position(scroll_position, cx);
2009 cx.notify();
2010 }
2011
2012 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
2013 self.columnar_selection_tail.take();
2014 if self.selections.pending_anchor().is_some() {
2015 let selections = self.selections.all::<usize>(cx);
2016 self.change_selections(None, cx, |s| {
2017 s.select(selections);
2018 s.clear_pending();
2019 });
2020 }
2021 }
2022
2023 fn select_columns(
2024 &mut self,
2025 tail: DisplayPoint,
2026 head: DisplayPoint,
2027 goal_column: u32,
2028 display_map: &DisplaySnapshot,
2029 cx: &mut ViewContext<Self>,
2030 ) {
2031 let start_row = cmp::min(tail.row(), head.row());
2032 let end_row = cmp::max(tail.row(), head.row());
2033 let start_column = cmp::min(tail.column(), goal_column);
2034 let end_column = cmp::max(tail.column(), goal_column);
2035 let reversed = start_column < tail.column();
2036
2037 let selection_ranges = (start_row..=end_row)
2038 .filter_map(|row| {
2039 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2040 let start = display_map
2041 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2042 .to_point(display_map);
2043 let end = display_map
2044 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2045 .to_point(display_map);
2046 if reversed {
2047 Some(end..start)
2048 } else {
2049 Some(start..end)
2050 }
2051 } else {
2052 None
2053 }
2054 })
2055 .collect::<Vec<_>>();
2056
2057 self.change_selections(None, cx, |s| {
2058 s.select_ranges(selection_ranges);
2059 });
2060 cx.notify();
2061 }
2062
2063 pub fn has_pending_nonempty_selection(&self) -> bool {
2064 let pending_nonempty_selection = match self.selections.pending_anchor() {
2065 Some(Selection { start, end, .. }) => start != end,
2066 None => false,
2067 };
2068 pending_nonempty_selection || self.columnar_selection_tail.is_some()
2069 }
2070
2071 pub fn has_pending_selection(&self) -> bool {
2072 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2073 }
2074
2075 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
2076 if self.take_rename(false, cx).is_some() {
2077 return;
2078 }
2079
2080 if hide_hover(self, cx) {
2081 return;
2082 }
2083
2084 if self.hide_context_menu(cx).is_some() {
2085 return;
2086 }
2087
2088 if self.discard_copilot_suggestion(cx) {
2089 return;
2090 }
2091
2092 if self.snippet_stack.pop().is_some() {
2093 return;
2094 }
2095
2096 if self.mode == EditorMode::Full {
2097 if self.active_diagnostics.is_some() {
2098 self.dismiss_diagnostics(cx);
2099 return;
2100 }
2101
2102 if self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel()) {
2103 return;
2104 }
2105 }
2106
2107 cx.propagate_action();
2108 }
2109
2110 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2111 let text: Arc<str> = text.into();
2112
2113 if self.read_only {
2114 return;
2115 }
2116 if !self.input_enabled {
2117 cx.emit(Event::InputIgnored { text });
2118 return;
2119 }
2120
2121 let selections = self.selections.all_adjusted(cx);
2122 let mut brace_inserted = false;
2123 let mut edits = Vec::new();
2124 let mut new_selections = Vec::with_capacity(selections.len());
2125 let mut new_autoclose_regions = Vec::new();
2126 let snapshot = self.buffer.read(cx).read(cx);
2127
2128 for (selection, autoclose_region) in
2129 self.selections_with_autoclose_regions(selections, &snapshot)
2130 {
2131 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2132 // Determine if the inserted text matches the opening or closing
2133 // bracket of any of this language's bracket pairs.
2134 let mut bracket_pair = None;
2135 let mut is_bracket_pair_start = false;
2136 for (pair, enabled) in scope.brackets() {
2137 if enabled && pair.close && pair.start.ends_with(text.as_ref()) {
2138 bracket_pair = Some(pair.clone());
2139 is_bracket_pair_start = true;
2140 break;
2141 } else if pair.end.as_str() == text.as_ref() {
2142 bracket_pair = Some(pair.clone());
2143 break;
2144 }
2145 }
2146
2147 if let Some(bracket_pair) = bracket_pair {
2148 if selection.is_empty() {
2149 if is_bracket_pair_start {
2150 let prefix_len = bracket_pair.start.len() - text.len();
2151
2152 // If the inserted text is a suffix of an opening bracket and the
2153 // selection is preceded by the rest of the opening bracket, then
2154 // insert the closing bracket.
2155 let following_text_allows_autoclose = snapshot
2156 .chars_at(selection.start)
2157 .next()
2158 .map_or(true, |c| scope.should_autoclose_before(c));
2159 let preceding_text_matches_prefix = prefix_len == 0
2160 || (selection.start.column >= (prefix_len as u32)
2161 && snapshot.contains_str_at(
2162 Point::new(
2163 selection.start.row,
2164 selection.start.column - (prefix_len as u32),
2165 ),
2166 &bracket_pair.start[..prefix_len],
2167 ));
2168 if following_text_allows_autoclose && preceding_text_matches_prefix {
2169 let anchor = snapshot.anchor_before(selection.end);
2170 new_selections.push((selection.map(|_| anchor), text.len()));
2171 new_autoclose_regions.push((
2172 anchor,
2173 text.len(),
2174 selection.id,
2175 bracket_pair.clone(),
2176 ));
2177 edits.push((
2178 selection.range(),
2179 format!("{}{}", text, bracket_pair.end).into(),
2180 ));
2181 brace_inserted = true;
2182 continue;
2183 }
2184 }
2185
2186 if let Some(region) = autoclose_region {
2187 // If the selection is followed by an auto-inserted closing bracket,
2188 // then don't insert that closing bracket again; just move the selection
2189 // past the closing bracket.
2190 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2191 && text.as_ref() == region.pair.end.as_str();
2192 if should_skip {
2193 let anchor = snapshot.anchor_after(selection.end);
2194 new_selections
2195 .push((selection.map(|_| anchor), region.pair.end.len()));
2196 continue;
2197 }
2198 }
2199 }
2200 // If an opening bracket is 1 character long and is typed while
2201 // text is selected, then surround that text with the bracket pair.
2202 else if is_bracket_pair_start && bracket_pair.start.chars().count() == 1 {
2203 edits.push((selection.start..selection.start, text.clone()));
2204 edits.push((
2205 selection.end..selection.end,
2206 bracket_pair.end.as_str().into(),
2207 ));
2208 brace_inserted = true;
2209 new_selections.push((
2210 Selection {
2211 id: selection.id,
2212 start: snapshot.anchor_after(selection.start),
2213 end: snapshot.anchor_before(selection.end),
2214 reversed: selection.reversed,
2215 goal: selection.goal,
2216 },
2217 0,
2218 ));
2219 continue;
2220 }
2221 }
2222 }
2223
2224 // If not handling any auto-close operation, then just replace the selected
2225 // text with the given input and move the selection to the end of the
2226 // newly inserted text.
2227 let anchor = snapshot.anchor_after(selection.end);
2228 new_selections.push((selection.map(|_| anchor), 0));
2229 edits.push((selection.start..selection.end, text.clone()));
2230 }
2231
2232 drop(snapshot);
2233 self.transact(cx, |this, cx| {
2234 this.buffer.update(cx, |buffer, cx| {
2235 buffer.edit(edits, this.autoindent_mode.clone(), cx);
2236 });
2237
2238 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
2239 let new_selection_deltas = new_selections.iter().map(|e| e.1);
2240 let snapshot = this.buffer.read(cx).read(cx);
2241 let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
2242 .zip(new_selection_deltas)
2243 .map(|(selection, delta)| selection.map(|e| e + delta))
2244 .collect::<Vec<_>>();
2245
2246 let mut i = 0;
2247 for (position, delta, selection_id, pair) in new_autoclose_regions {
2248 let position = position.to_offset(&snapshot) + delta;
2249 let start = snapshot.anchor_before(position);
2250 let end = snapshot.anchor_after(position);
2251 while let Some(existing_state) = this.autoclose_regions.get(i) {
2252 match existing_state.range.start.cmp(&start, &snapshot) {
2253 Ordering::Less => i += 1,
2254 Ordering::Greater => break,
2255 Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
2256 Ordering::Less => i += 1,
2257 Ordering::Equal => break,
2258 Ordering::Greater => break,
2259 },
2260 }
2261 }
2262 this.autoclose_regions.insert(
2263 i,
2264 AutocloseRegion {
2265 selection_id,
2266 range: start..end,
2267 pair,
2268 },
2269 );
2270 }
2271
2272 drop(snapshot);
2273 let had_active_copilot_suggestion = this.has_active_copilot_suggestion(cx);
2274 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
2275
2276 if !brace_inserted && settings::get::<EditorSettings>(cx).use_on_type_format {
2277 if let Some(on_type_format_task) =
2278 this.trigger_on_type_formatting(text.to_string(), cx)
2279 {
2280 on_type_format_task.detach_and_log_err(cx);
2281 }
2282 }
2283
2284 if had_active_copilot_suggestion {
2285 this.refresh_copilot_suggestions(true, cx);
2286 if !this.has_active_copilot_suggestion(cx) {
2287 this.trigger_completion_on_input(&text, cx);
2288 }
2289 } else {
2290 this.trigger_completion_on_input(&text, cx);
2291 this.refresh_copilot_suggestions(true, cx);
2292 }
2293 });
2294 }
2295
2296 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
2297 self.transact(cx, |this, cx| {
2298 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
2299 let selections = this.selections.all::<usize>(cx);
2300 let multi_buffer = this.buffer.read(cx);
2301 let buffer = multi_buffer.snapshot(cx);
2302 selections
2303 .iter()
2304 .map(|selection| {
2305 let start_point = selection.start.to_point(&buffer);
2306 let mut indent = buffer.indent_size_for_line(start_point.row);
2307 indent.len = cmp::min(indent.len, start_point.column);
2308 let start = selection.start;
2309 let end = selection.end;
2310 let is_cursor = start == end;
2311 let language_scope = buffer.language_scope_at(start);
2312 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
2313 &language_scope
2314 {
2315 let leading_whitespace_len = buffer
2316 .reversed_chars_at(start)
2317 .take_while(|c| c.is_whitespace() && *c != '\n')
2318 .map(|c| c.len_utf8())
2319 .sum::<usize>();
2320
2321 let trailing_whitespace_len = buffer
2322 .chars_at(end)
2323 .take_while(|c| c.is_whitespace() && *c != '\n')
2324 .map(|c| c.len_utf8())
2325 .sum::<usize>();
2326
2327 let insert_extra_newline =
2328 language.brackets().any(|(pair, enabled)| {
2329 let pair_start = pair.start.trim_end();
2330 let pair_end = pair.end.trim_start();
2331
2332 enabled
2333 && pair.newline
2334 && buffer.contains_str_at(
2335 end + trailing_whitespace_len,
2336 pair_end,
2337 )
2338 && buffer.contains_str_at(
2339 (start - leading_whitespace_len)
2340 .saturating_sub(pair_start.len()),
2341 pair_start,
2342 )
2343 });
2344 // Comment extension on newline is allowed only for cursor selections
2345 let comment_delimiter = language.line_comment_prefix().filter(|_| {
2346 let is_comment_extension_enabled =
2347 multi_buffer.settings_at(0, cx).extend_comment_on_newline;
2348 is_cursor && is_comment_extension_enabled
2349 });
2350 let comment_delimiter = if let Some(delimiter) = comment_delimiter {
2351 buffer
2352 .buffer_line_for_row(start_point.row)
2353 .is_some_and(|(snapshot, range)| {
2354 let mut index_of_first_non_whitespace = 0;
2355 let line_starts_with_comment = snapshot
2356 .chars_for_range(range)
2357 .skip_while(|c| {
2358 let should_skip = c.is_whitespace();
2359 if should_skip {
2360 index_of_first_non_whitespace += 1;
2361 }
2362 should_skip
2363 })
2364 .take(delimiter.len())
2365 .eq(delimiter.chars());
2366 let cursor_is_placed_after_comment_marker =
2367 index_of_first_non_whitespace + delimiter.len()
2368 <= start_point.column as usize;
2369 line_starts_with_comment
2370 && cursor_is_placed_after_comment_marker
2371 })
2372 .then(|| delimiter.clone())
2373 } else {
2374 None
2375 };
2376 (comment_delimiter, insert_extra_newline)
2377 } else {
2378 (None, false)
2379 };
2380
2381 let capacity_for_delimiter = comment_delimiter
2382 .as_deref()
2383 .map(str::len)
2384 .unwrap_or_default();
2385 let mut new_text =
2386 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
2387 new_text.push_str("\n");
2388 new_text.extend(indent.chars());
2389 if let Some(delimiter) = &comment_delimiter {
2390 new_text.push_str(&delimiter);
2391 }
2392 if insert_extra_newline {
2393 new_text = new_text.repeat(2);
2394 }
2395
2396 let anchor = buffer.anchor_after(end);
2397 let new_selection = selection.map(|_| anchor);
2398 (
2399 (start..end, new_text),
2400 (insert_extra_newline, new_selection),
2401 )
2402 })
2403 .unzip()
2404 };
2405
2406 this.edit_with_autoindent(edits, cx);
2407 let buffer = this.buffer.read(cx).snapshot(cx);
2408 let new_selections = selection_fixup_info
2409 .into_iter()
2410 .map(|(extra_newline_inserted, new_selection)| {
2411 let mut cursor = new_selection.end.to_point(&buffer);
2412 if extra_newline_inserted {
2413 cursor.row -= 1;
2414 cursor.column = buffer.line_len(cursor.row);
2415 }
2416 new_selection.map(|_| cursor)
2417 })
2418 .collect();
2419
2420 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
2421 this.refresh_copilot_suggestions(true, cx);
2422 });
2423 }
2424
2425 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
2426 let buffer = self.buffer.read(cx);
2427 let snapshot = buffer.snapshot(cx);
2428
2429 let mut edits = Vec::new();
2430 let mut rows = Vec::new();
2431 let mut rows_inserted = 0;
2432
2433 for selection in self.selections.all_adjusted(cx) {
2434 let cursor = selection.head();
2435 let row = cursor.row;
2436
2437 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
2438
2439 let newline = "\n".to_string();
2440 edits.push((start_of_line..start_of_line, newline));
2441
2442 rows.push(row + rows_inserted);
2443 rows_inserted += 1;
2444 }
2445
2446 self.transact(cx, |editor, cx| {
2447 editor.edit(edits, cx);
2448
2449 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
2450 let mut index = 0;
2451 s.move_cursors_with(|map, _, _| {
2452 let row = rows[index];
2453 index += 1;
2454
2455 let point = Point::new(row, 0);
2456 let boundary = map.next_line_boundary(point).1;
2457 let clipped = map.clip_point(boundary, Bias::Left);
2458
2459 (clipped, SelectionGoal::None)
2460 });
2461 });
2462
2463 let mut indent_edits = Vec::new();
2464 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
2465 for row in rows {
2466 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
2467 for (row, indent) in indents {
2468 if indent.len == 0 {
2469 continue;
2470 }
2471
2472 let text = match indent.kind {
2473 IndentKind::Space => " ".repeat(indent.len as usize),
2474 IndentKind::Tab => "\t".repeat(indent.len as usize),
2475 };
2476 let point = Point::new(row, 0);
2477 indent_edits.push((point..point, text));
2478 }
2479 }
2480 editor.edit(indent_edits, cx);
2481 });
2482 }
2483
2484 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
2485 let buffer = self.buffer.read(cx);
2486 let snapshot = buffer.snapshot(cx);
2487
2488 let mut edits = Vec::new();
2489 let mut rows = Vec::new();
2490 let mut rows_inserted = 0;
2491
2492 for selection in self.selections.all_adjusted(cx) {
2493 let cursor = selection.head();
2494 let row = cursor.row;
2495
2496 let point = Point::new(row + 1, 0);
2497 let start_of_line = snapshot.clip_point(point, Bias::Left);
2498
2499 let newline = "\n".to_string();
2500 edits.push((start_of_line..start_of_line, newline));
2501
2502 rows_inserted += 1;
2503 rows.push(row + rows_inserted);
2504 }
2505
2506 self.transact(cx, |editor, cx| {
2507 editor.edit(edits, cx);
2508
2509 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
2510 let mut index = 0;
2511 s.move_cursors_with(|map, _, _| {
2512 let row = rows[index];
2513 index += 1;
2514
2515 let point = Point::new(row, 0);
2516 let boundary = map.next_line_boundary(point).1;
2517 let clipped = map.clip_point(boundary, Bias::Left);
2518
2519 (clipped, SelectionGoal::None)
2520 });
2521 });
2522
2523 let mut indent_edits = Vec::new();
2524 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
2525 for row in rows {
2526 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
2527 for (row, indent) in indents {
2528 if indent.len == 0 {
2529 continue;
2530 }
2531
2532 let text = match indent.kind {
2533 IndentKind::Space => " ".repeat(indent.len as usize),
2534 IndentKind::Tab => "\t".repeat(indent.len as usize),
2535 };
2536 let point = Point::new(row, 0);
2537 indent_edits.push((point..point, text));
2538 }
2539 }
2540 editor.edit(indent_edits, cx);
2541 });
2542 }
2543
2544 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2545 self.insert_with_autoindent_mode(
2546 text,
2547 Some(AutoindentMode::Block {
2548 original_indent_columns: Vec::new(),
2549 }),
2550 cx,
2551 );
2552 }
2553
2554 fn insert_with_autoindent_mode(
2555 &mut self,
2556 text: &str,
2557 autoindent_mode: Option<AutoindentMode>,
2558 cx: &mut ViewContext<Self>,
2559 ) {
2560 if self.read_only {
2561 return;
2562 }
2563
2564 let text: Arc<str> = text.into();
2565 self.transact(cx, |this, cx| {
2566 let old_selections = this.selections.all_adjusted(cx);
2567 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
2568 let anchors = {
2569 let snapshot = buffer.read(cx);
2570 old_selections
2571 .iter()
2572 .map(|s| {
2573 let anchor = snapshot.anchor_after(s.head());
2574 s.map(|_| anchor)
2575 })
2576 .collect::<Vec<_>>()
2577 };
2578 buffer.edit(
2579 old_selections
2580 .iter()
2581 .map(|s| (s.start..s.end, text.clone())),
2582 autoindent_mode,
2583 cx,
2584 );
2585 anchors
2586 });
2587
2588 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
2589 s.select_anchors(selection_anchors);
2590 })
2591 });
2592 }
2593
2594 fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2595 if !settings::get::<EditorSettings>(cx).show_completions_on_input {
2596 return;
2597 }
2598
2599 let selection = self.selections.newest_anchor();
2600 if self
2601 .buffer
2602 .read(cx)
2603 .is_completion_trigger(selection.head(), text, cx)
2604 {
2605 self.show_completions(&ShowCompletions, cx);
2606 } else {
2607 self.hide_context_menu(cx);
2608 }
2609 }
2610
2611 /// If any empty selections is touching the start of its innermost containing autoclose
2612 /// region, expand it to select the brackets.
2613 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
2614 let selections = self.selections.all::<usize>(cx);
2615 let buffer = self.buffer.read(cx).read(cx);
2616 let mut new_selections = Vec::new();
2617 for (mut selection, region) in self.selections_with_autoclose_regions(selections, &buffer) {
2618 if let (Some(region), true) = (region, selection.is_empty()) {
2619 let mut range = region.range.to_offset(&buffer);
2620 if selection.start == range.start {
2621 if range.start >= region.pair.start.len() {
2622 range.start -= region.pair.start.len();
2623 if buffer.contains_str_at(range.start, ®ion.pair.start) {
2624 if buffer.contains_str_at(range.end, ®ion.pair.end) {
2625 range.end += region.pair.end.len();
2626 selection.start = range.start;
2627 selection.end = range.end;
2628 }
2629 }
2630 }
2631 }
2632 }
2633 new_selections.push(selection);
2634 }
2635
2636 drop(buffer);
2637 self.change_selections(None, cx, |selections| selections.select(new_selections));
2638 }
2639
2640 /// Iterate the given selections, and for each one, find the smallest surrounding
2641 /// autoclose region. This uses the ordering of the selections and the autoclose
2642 /// regions to avoid repeated comparisons.
2643 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
2644 &'a self,
2645 selections: impl IntoIterator<Item = Selection<D>>,
2646 buffer: &'a MultiBufferSnapshot,
2647 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
2648 let mut i = 0;
2649 let mut regions = self.autoclose_regions.as_slice();
2650 selections.into_iter().map(move |selection| {
2651 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
2652
2653 let mut enclosing = None;
2654 while let Some(pair_state) = regions.get(i) {
2655 if pair_state.range.end.to_offset(buffer) < range.start {
2656 regions = ®ions[i + 1..];
2657 i = 0;
2658 } else if pair_state.range.start.to_offset(buffer) > range.end {
2659 break;
2660 } else if pair_state.selection_id == selection.id {
2661 enclosing = Some(pair_state);
2662 i += 1;
2663 }
2664 }
2665
2666 (selection.clone(), enclosing)
2667 })
2668 }
2669
2670 /// Remove any autoclose regions that no longer contain their selection.
2671 fn invalidate_autoclose_regions(
2672 &mut self,
2673 mut selections: &[Selection<Anchor>],
2674 buffer: &MultiBufferSnapshot,
2675 ) {
2676 self.autoclose_regions.retain(|state| {
2677 let mut i = 0;
2678 while let Some(selection) = selections.get(i) {
2679 if selection.end.cmp(&state.range.start, buffer).is_lt() {
2680 selections = &selections[1..];
2681 continue;
2682 }
2683 if selection.start.cmp(&state.range.end, buffer).is_gt() {
2684 break;
2685 }
2686 if selection.id == state.selection_id {
2687 return true;
2688 } else {
2689 i += 1;
2690 }
2691 }
2692 false
2693 });
2694 }
2695 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
2696 let offset = position.to_offset(buffer);
2697 let (word_range, kind) = buffer.surrounding_word(offset);
2698 if offset > word_range.start && kind == Some(CharKind::Word) {
2699 Some(
2700 buffer
2701 .text_for_range(word_range.start..offset)
2702 .collect::<String>(),
2703 )
2704 } else {
2705 None
2706 }
2707 }
2708
2709 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
2710 self.refresh_inlay_hints(
2711 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
2712 cx,
2713 );
2714 }
2715
2716 pub fn inlay_hints_enabled(&self) -> bool {
2717 self.inlay_hint_cache.enabled
2718 }
2719
2720 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
2721 if self.project.is_none() || self.mode != EditorMode::Full {
2722 return;
2723 }
2724
2725 let (invalidate_cache, required_languages) = match reason {
2726 InlayHintRefreshReason::Toggle(enabled) => {
2727 self.inlay_hint_cache.enabled = enabled;
2728 if enabled {
2729 (InvalidationStrategy::RefreshRequested, None)
2730 } else {
2731 self.inlay_hint_cache.clear();
2732 self.splice_inlay_hints(
2733 self.visible_inlay_hints(cx)
2734 .iter()
2735 .map(|inlay| inlay.id)
2736 .collect(),
2737 Vec::new(),
2738 cx,
2739 );
2740 return;
2741 }
2742 }
2743 InlayHintRefreshReason::SettingsChange(new_settings) => {
2744 match self.inlay_hint_cache.update_settings(
2745 &self.buffer,
2746 new_settings,
2747 self.visible_inlay_hints(cx),
2748 cx,
2749 ) {
2750 ControlFlow::Break(Some(InlaySplice {
2751 to_remove,
2752 to_insert,
2753 })) => {
2754 self.splice_inlay_hints(to_remove, to_insert, cx);
2755 return;
2756 }
2757 ControlFlow::Break(None) => return,
2758 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
2759 }
2760 }
2761 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
2762 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
2763 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
2764 }
2765 InlayHintRefreshReason::RefreshRequested => {
2766 (InvalidationStrategy::RefreshRequested, None)
2767 }
2768 };
2769
2770 if let Some(InlaySplice {
2771 to_remove,
2772 to_insert,
2773 }) = self.inlay_hint_cache.spawn_hint_refresh(
2774 self.excerpt_visible_offsets(required_languages.as_ref(), cx),
2775 invalidate_cache,
2776 cx,
2777 ) {
2778 self.splice_inlay_hints(to_remove, to_insert, cx);
2779 }
2780 }
2781
2782 fn visible_inlay_hints(&self, cx: &ViewContext<'_, '_, Editor>) -> Vec<Inlay> {
2783 self.display_map
2784 .read(cx)
2785 .current_inlays()
2786 .filter(move |inlay| {
2787 Some(inlay.id) != self.copilot_state.suggestion.as_ref().map(|h| h.id)
2788 })
2789 .cloned()
2790 .collect()
2791 }
2792
2793 pub fn excerpt_visible_offsets(
2794 &self,
2795 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
2796 cx: &mut ViewContext<'_, '_, Editor>,
2797 ) -> HashMap<ExcerptId, (ModelHandle<Buffer>, Global, Range<usize>)> {
2798 let multi_buffer = self.buffer().read(cx);
2799 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
2800 let multi_buffer_visible_start = self
2801 .scroll_manager
2802 .anchor()
2803 .anchor
2804 .to_point(&multi_buffer_snapshot);
2805 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
2806 multi_buffer_visible_start
2807 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
2808 Bias::Left,
2809 );
2810 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
2811 multi_buffer
2812 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
2813 .into_iter()
2814 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
2815 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
2816 let buffer = buffer_handle.read(cx);
2817 let language = buffer.language()?;
2818 if let Some(restrict_to_languages) = restrict_to_languages {
2819 if !restrict_to_languages.contains(language) {
2820 return None;
2821 }
2822 }
2823 Some((
2824 excerpt_id,
2825 (
2826 buffer_handle,
2827 buffer.version().clone(),
2828 excerpt_visible_range,
2829 ),
2830 ))
2831 })
2832 .collect()
2833 }
2834
2835 fn splice_inlay_hints(
2836 &self,
2837 to_remove: Vec<InlayId>,
2838 to_insert: Vec<Inlay>,
2839 cx: &mut ViewContext<Self>,
2840 ) {
2841 self.display_map.update(cx, |display_map, cx| {
2842 display_map.splice_inlays(to_remove, to_insert, cx);
2843 });
2844 cx.notify();
2845 }
2846
2847 fn trigger_on_type_formatting(
2848 &self,
2849 input: String,
2850 cx: &mut ViewContext<Self>,
2851 ) -> Option<Task<Result<()>>> {
2852 if input.len() != 1 {
2853 return None;
2854 }
2855
2856 let project = self.project.as_ref()?;
2857 let position = self.selections.newest_anchor().head();
2858 let (buffer, buffer_position) = self
2859 .buffer
2860 .read(cx)
2861 .text_anchor_for_position(position.clone(), cx)?;
2862
2863 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
2864 // hence we do LSP request & edit on host side only — add formats to host's history.
2865 let push_to_lsp_host_history = true;
2866 // If this is not the host, append its history with new edits.
2867 let push_to_client_history = project.read(cx).is_remote();
2868
2869 let on_type_formatting = project.update(cx, |project, cx| {
2870 project.on_type_format(
2871 buffer.clone(),
2872 buffer_position,
2873 input,
2874 push_to_lsp_host_history,
2875 cx,
2876 )
2877 });
2878 Some(cx.spawn(|editor, mut cx| async move {
2879 if let Some(transaction) = on_type_formatting.await? {
2880 if push_to_client_history {
2881 buffer.update(&mut cx, |buffer, _| {
2882 buffer.push_transaction(transaction, Instant::now());
2883 });
2884 }
2885 editor.update(&mut cx, |editor, cx| {
2886 editor.refresh_document_highlights(cx);
2887 })?;
2888 }
2889 Ok(())
2890 }))
2891 }
2892
2893 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
2894 if self.pending_rename.is_some() {
2895 return;
2896 }
2897
2898 let project = if let Some(project) = self.project.clone() {
2899 project
2900 } else {
2901 return;
2902 };
2903
2904 let position = self.selections.newest_anchor().head();
2905 let (buffer, buffer_position) = if let Some(output) = self
2906 .buffer
2907 .read(cx)
2908 .text_anchor_for_position(position.clone(), cx)
2909 {
2910 output
2911 } else {
2912 return;
2913 };
2914
2915 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
2916 let completions = project.update(cx, |project, cx| {
2917 project.completions(&buffer, buffer_position, cx)
2918 });
2919
2920 let id = post_inc(&mut self.next_completion_id);
2921 let task = cx.spawn(|this, mut cx| {
2922 async move {
2923 let menu = if let Some(completions) = completions.await.log_err() {
2924 let mut menu = CompletionsMenu {
2925 id,
2926 initial_position: position,
2927 match_candidates: completions
2928 .iter()
2929 .enumerate()
2930 .map(|(id, completion)| {
2931 StringMatchCandidate::new(
2932 id,
2933 completion.label.text[completion.label.filter_range.clone()]
2934 .into(),
2935 )
2936 })
2937 .collect(),
2938 buffer,
2939 completions: completions.into(),
2940 matches: Vec::new().into(),
2941 selected_item: 0,
2942 list: Default::default(),
2943 };
2944 menu.filter(query.as_deref(), cx.background()).await;
2945 if menu.matches.is_empty() {
2946 None
2947 } else {
2948 Some(menu)
2949 }
2950 } else {
2951 None
2952 };
2953
2954 this.update(&mut cx, |this, cx| {
2955 this.completion_tasks.retain(|(task_id, _)| *task_id > id);
2956
2957 match this.context_menu.as_ref() {
2958 None => {}
2959 Some(ContextMenu::Completions(prev_menu)) => {
2960 if prev_menu.id > id {
2961 return;
2962 }
2963 }
2964 _ => return,
2965 }
2966
2967 if this.focused && menu.is_some() {
2968 let menu = menu.unwrap();
2969 this.show_context_menu(ContextMenu::Completions(menu), cx);
2970 } else if this.completion_tasks.is_empty() {
2971 // If there are no more completion tasks and the last menu was
2972 // empty, we should hide it. If it was already hidden, we should
2973 // also show the copilot suggestion when available.
2974 if this.hide_context_menu(cx).is_none() {
2975 this.update_visible_copilot_suggestion(cx);
2976 }
2977 }
2978 })?;
2979
2980 Ok::<_, anyhow::Error>(())
2981 }
2982 .log_err()
2983 });
2984 self.completion_tasks.push((id, task));
2985 }
2986
2987 pub fn confirm_completion(
2988 &mut self,
2989 action: &ConfirmCompletion,
2990 cx: &mut ViewContext<Self>,
2991 ) -> Option<Task<Result<()>>> {
2992 use language::ToOffset as _;
2993
2994 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
2995 menu
2996 } else {
2997 return None;
2998 };
2999
3000 let mat = completions_menu
3001 .matches
3002 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
3003 let buffer_handle = completions_menu.buffer;
3004 let completion = completions_menu.completions.get(mat.candidate_id)?;
3005
3006 let snippet;
3007 let text;
3008 if completion.is_snippet() {
3009 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
3010 text = snippet.as_ref().unwrap().text.clone();
3011 } else {
3012 snippet = None;
3013 text = completion.new_text.clone();
3014 };
3015 let selections = self.selections.all::<usize>(cx);
3016 let buffer = buffer_handle.read(cx);
3017 let old_range = completion.old_range.to_offset(buffer);
3018 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
3019
3020 let newest_selection = self.selections.newest_anchor();
3021 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
3022 return None;
3023 }
3024
3025 let lookbehind = newest_selection
3026 .start
3027 .text_anchor
3028 .to_offset(buffer)
3029 .saturating_sub(old_range.start);
3030 let lookahead = old_range
3031 .end
3032 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
3033 let mut common_prefix_len = old_text
3034 .bytes()
3035 .zip(text.bytes())
3036 .take_while(|(a, b)| a == b)
3037 .count();
3038
3039 let snapshot = self.buffer.read(cx).snapshot(cx);
3040 let mut ranges = Vec::new();
3041 for selection in &selections {
3042 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
3043 let start = selection.start.saturating_sub(lookbehind);
3044 let end = selection.end + lookahead;
3045 ranges.push(start + common_prefix_len..end);
3046 } else {
3047 common_prefix_len = 0;
3048 ranges.clear();
3049 ranges.extend(selections.iter().map(|s| {
3050 if s.id == newest_selection.id {
3051 old_range.clone()
3052 } else {
3053 s.start..s.end
3054 }
3055 }));
3056 break;
3057 }
3058 }
3059 let text = &text[common_prefix_len..];
3060
3061 self.transact(cx, |this, cx| {
3062 if let Some(mut snippet) = snippet {
3063 snippet.text = text.to_string();
3064 for tabstop in snippet.tabstops.iter_mut().flatten() {
3065 tabstop.start -= common_prefix_len as isize;
3066 tabstop.end -= common_prefix_len as isize;
3067 }
3068
3069 this.insert_snippet(&ranges, snippet, cx).log_err();
3070 } else {
3071 this.buffer.update(cx, |buffer, cx| {
3072 buffer.edit(
3073 ranges.iter().map(|range| (range.clone(), text)),
3074 this.autoindent_mode.clone(),
3075 cx,
3076 );
3077 });
3078 }
3079
3080 this.refresh_copilot_suggestions(true, cx);
3081 });
3082
3083 let project = self.project.clone()?;
3084 let apply_edits = project.update(cx, |project, cx| {
3085 project.apply_additional_edits_for_completion(
3086 buffer_handle,
3087 completion.clone(),
3088 true,
3089 cx,
3090 )
3091 });
3092 Some(cx.foreground().spawn(async move {
3093 apply_edits.await?;
3094 Ok(())
3095 }))
3096 }
3097
3098 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
3099 if matches!(
3100 self.context_menu.as_ref(),
3101 Some(ContextMenu::CodeActions(_))
3102 ) {
3103 self.context_menu.take();
3104 cx.notify();
3105 return;
3106 }
3107
3108 let deployed_from_indicator = action.deployed_from_indicator;
3109 let mut task = self.code_actions_task.take();
3110 cx.spawn(|this, mut cx| async move {
3111 while let Some(prev_task) = task {
3112 prev_task.await;
3113 task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
3114 }
3115
3116 this.update(&mut cx, |this, cx| {
3117 if this.focused {
3118 if let Some((buffer, actions)) = this.available_code_actions.clone() {
3119 this.show_context_menu(
3120 ContextMenu::CodeActions(CodeActionsMenu {
3121 buffer,
3122 actions,
3123 selected_item: Default::default(),
3124 list: Default::default(),
3125 deployed_from_indicator,
3126 }),
3127 cx,
3128 );
3129 }
3130 }
3131 })?;
3132
3133 Ok::<_, anyhow::Error>(())
3134 })
3135 .detach_and_log_err(cx);
3136 }
3137
3138 pub fn confirm_code_action(
3139 workspace: &mut Workspace,
3140 action: &ConfirmCodeAction,
3141 cx: &mut ViewContext<Workspace>,
3142 ) -> Option<Task<Result<()>>> {
3143 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
3144 let actions_menu = if let ContextMenu::CodeActions(menu) =
3145 editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
3146 {
3147 menu
3148 } else {
3149 return None;
3150 };
3151 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
3152 let action = actions_menu.actions.get(action_ix)?.clone();
3153 let title = action.lsp_action.title.clone();
3154 let buffer = actions_menu.buffer;
3155
3156 let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
3157 project.apply_code_action(buffer, action, true, cx)
3158 });
3159 let editor = editor.downgrade();
3160 Some(cx.spawn(|workspace, cx| async move {
3161 let project_transaction = apply_code_actions.await?;
3162 Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
3163 }))
3164 }
3165
3166 async fn open_project_transaction(
3167 this: &WeakViewHandle<Editor>,
3168 workspace: WeakViewHandle<Workspace>,
3169 transaction: ProjectTransaction,
3170 title: String,
3171 mut cx: AsyncAppContext,
3172 ) -> Result<()> {
3173 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx))?;
3174
3175 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
3176 entries.sort_unstable_by_key(|(buffer, _)| {
3177 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
3178 });
3179
3180 // If the project transaction's edits are all contained within this editor, then
3181 // avoid opening a new editor to display them.
3182
3183 if let Some((buffer, transaction)) = entries.first() {
3184 if entries.len() == 1 {
3185 let excerpt = this.read_with(&cx, |editor, cx| {
3186 editor
3187 .buffer()
3188 .read(cx)
3189 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
3190 })?;
3191 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
3192 if excerpted_buffer == *buffer {
3193 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
3194 let excerpt_range = excerpt_range.to_offset(buffer);
3195 buffer
3196 .edited_ranges_for_transaction::<usize>(transaction)
3197 .all(|range| {
3198 excerpt_range.start <= range.start
3199 && excerpt_range.end >= range.end
3200 })
3201 });
3202
3203 if all_edits_within_excerpt {
3204 return Ok(());
3205 }
3206 }
3207 }
3208 }
3209 } else {
3210 return Ok(());
3211 }
3212
3213 let mut ranges_to_highlight = Vec::new();
3214 let excerpt_buffer = cx.add_model(|cx| {
3215 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
3216 for (buffer_handle, transaction) in &entries {
3217 let buffer = buffer_handle.read(cx);
3218 ranges_to_highlight.extend(
3219 multibuffer.push_excerpts_with_context_lines(
3220 buffer_handle.clone(),
3221 buffer
3222 .edited_ranges_for_transaction::<usize>(transaction)
3223 .collect(),
3224 1,
3225 cx,
3226 ),
3227 );
3228 }
3229 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
3230 multibuffer
3231 });
3232
3233 workspace.update(&mut cx, |workspace, cx| {
3234 let project = workspace.project().clone();
3235 let editor =
3236 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
3237 workspace.add_item(Box::new(editor.clone()), cx);
3238 editor.update(cx, |editor, cx| {
3239 editor.highlight_background::<Self>(
3240 ranges_to_highlight,
3241 |theme| theme.editor.highlighted_line_background,
3242 cx,
3243 );
3244 });
3245 })?;
3246
3247 Ok(())
3248 }
3249
3250 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3251 let project = self.project.as_ref()?;
3252 let buffer = self.buffer.read(cx);
3253 let newest_selection = self.selections.newest_anchor().clone();
3254 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
3255 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
3256 if start_buffer != end_buffer {
3257 return None;
3258 }
3259
3260 let actions = project.update(cx, |project, cx| {
3261 project.code_actions(&start_buffer, start..end, cx)
3262 });
3263 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
3264 let actions = actions.await;
3265 this.update(&mut cx, |this, cx| {
3266 this.available_code_actions = actions.log_err().and_then(|actions| {
3267 if actions.is_empty() {
3268 None
3269 } else {
3270 Some((start_buffer, actions.into()))
3271 }
3272 });
3273 cx.notify();
3274 })
3275 .log_err();
3276 }));
3277 None
3278 }
3279
3280 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3281 if self.pending_rename.is_some() {
3282 return None;
3283 }
3284
3285 let project = self.project.as_ref()?;
3286 let buffer = self.buffer.read(cx);
3287 let newest_selection = self.selections.newest_anchor().clone();
3288 let cursor_position = newest_selection.head();
3289 let (cursor_buffer, cursor_buffer_position) =
3290 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
3291 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
3292 if cursor_buffer != tail_buffer {
3293 return None;
3294 }
3295
3296 let highlights = project.update(cx, |project, cx| {
3297 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
3298 });
3299
3300 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
3301 if let Some(highlights) = highlights.await.log_err() {
3302 this.update(&mut cx, |this, cx| {
3303 if this.pending_rename.is_some() {
3304 return;
3305 }
3306
3307 let buffer_id = cursor_position.buffer_id;
3308 let buffer = this.buffer.read(cx);
3309 if !buffer
3310 .text_anchor_for_position(cursor_position, cx)
3311 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
3312 {
3313 return;
3314 }
3315
3316 let cursor_buffer_snapshot = cursor_buffer.read(cx);
3317 let mut write_ranges = Vec::new();
3318 let mut read_ranges = Vec::new();
3319 for highlight in highlights {
3320 for (excerpt_id, excerpt_range) in
3321 buffer.excerpts_for_buffer(&cursor_buffer, cx)
3322 {
3323 let start = highlight
3324 .range
3325 .start
3326 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
3327 let end = highlight
3328 .range
3329 .end
3330 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
3331 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
3332 continue;
3333 }
3334
3335 let range = Anchor {
3336 buffer_id,
3337 excerpt_id: excerpt_id.clone(),
3338 text_anchor: start,
3339 }..Anchor {
3340 buffer_id,
3341 excerpt_id,
3342 text_anchor: end,
3343 };
3344 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
3345 write_ranges.push(range);
3346 } else {
3347 read_ranges.push(range);
3348 }
3349 }
3350 }
3351
3352 this.highlight_background::<DocumentHighlightRead>(
3353 read_ranges,
3354 |theme| theme.editor.document_highlight_read_background,
3355 cx,
3356 );
3357 this.highlight_background::<DocumentHighlightWrite>(
3358 write_ranges,
3359 |theme| theme.editor.document_highlight_write_background,
3360 cx,
3361 );
3362 cx.notify();
3363 })
3364 .log_err();
3365 }
3366 }));
3367 None
3368 }
3369
3370 fn refresh_copilot_suggestions(
3371 &mut self,
3372 debounce: bool,
3373 cx: &mut ViewContext<Self>,
3374 ) -> Option<()> {
3375 let copilot = Copilot::global(cx)?;
3376 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3377 self.clear_copilot_suggestions(cx);
3378 return None;
3379 }
3380 self.update_visible_copilot_suggestion(cx);
3381
3382 let snapshot = self.buffer.read(cx).snapshot(cx);
3383 let cursor = self.selections.newest_anchor().head();
3384 if !self.is_copilot_enabled_at(cursor, &snapshot, cx) {
3385 self.clear_copilot_suggestions(cx);
3386 return None;
3387 }
3388
3389 let (buffer, buffer_position) =
3390 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3391 self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move {
3392 if debounce {
3393 cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await;
3394 }
3395
3396 let completions = copilot
3397 .update(&mut cx, |copilot, cx| {
3398 copilot.completions(&buffer, buffer_position, cx)
3399 })
3400 .await
3401 .log_err()
3402 .into_iter()
3403 .flatten()
3404 .collect_vec();
3405
3406 this.update(&mut cx, |this, cx| {
3407 if !completions.is_empty() {
3408 this.copilot_state.cycled = false;
3409 this.copilot_state.pending_cycling_refresh = Task::ready(None);
3410 this.copilot_state.completions.clear();
3411 this.copilot_state.active_completion_index = 0;
3412 this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
3413 for completion in completions {
3414 this.copilot_state.push_completion(completion);
3415 }
3416 this.update_visible_copilot_suggestion(cx);
3417 }
3418 })
3419 .log_err()?;
3420 Some(())
3421 });
3422
3423 Some(())
3424 }
3425
3426 fn cycle_copilot_suggestions(
3427 &mut self,
3428 direction: Direction,
3429 cx: &mut ViewContext<Self>,
3430 ) -> Option<()> {
3431 let copilot = Copilot::global(cx)?;
3432 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3433 return None;
3434 }
3435
3436 if self.copilot_state.cycled {
3437 self.copilot_state.cycle_completions(direction);
3438 self.update_visible_copilot_suggestion(cx);
3439 } else {
3440 let cursor = self.selections.newest_anchor().head();
3441 let (buffer, buffer_position) =
3442 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3443 self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move {
3444 let completions = copilot
3445 .update(&mut cx, |copilot, cx| {
3446 copilot.completions_cycling(&buffer, buffer_position, cx)
3447 })
3448 .await;
3449
3450 this.update(&mut cx, |this, cx| {
3451 this.copilot_state.cycled = true;
3452 for completion in completions.log_err().into_iter().flatten() {
3453 this.copilot_state.push_completion(completion);
3454 }
3455 this.copilot_state.cycle_completions(direction);
3456 this.update_visible_copilot_suggestion(cx);
3457 })
3458 .log_err()?;
3459
3460 Some(())
3461 });
3462 }
3463
3464 Some(())
3465 }
3466
3467 fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext<Self>) {
3468 if !self.has_active_copilot_suggestion(cx) {
3469 self.refresh_copilot_suggestions(false, cx);
3470 return;
3471 }
3472
3473 self.update_visible_copilot_suggestion(cx);
3474 }
3475
3476 fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
3477 if self.has_active_copilot_suggestion(cx) {
3478 self.cycle_copilot_suggestions(Direction::Next, cx);
3479 } else {
3480 self.refresh_copilot_suggestions(false, cx);
3481 }
3482 }
3483
3484 fn previous_copilot_suggestion(
3485 &mut self,
3486 _: &copilot::PreviousSuggestion,
3487 cx: &mut ViewContext<Self>,
3488 ) {
3489 if self.has_active_copilot_suggestion(cx) {
3490 self.cycle_copilot_suggestions(Direction::Prev, cx);
3491 } else {
3492 self.refresh_copilot_suggestions(false, cx);
3493 }
3494 }
3495
3496 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3497 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
3498 if let Some((copilot, completion)) =
3499 Copilot::global(cx).zip(self.copilot_state.active_completion())
3500 {
3501 copilot
3502 .update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
3503 .detach_and_log_err(cx);
3504
3505 self.report_copilot_event(Some(completion.uuid.clone()), true, cx)
3506 }
3507 self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx);
3508 cx.notify();
3509 true
3510 } else {
3511 false
3512 }
3513 }
3514
3515 fn discard_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3516 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
3517 if let Some(copilot) = Copilot::global(cx) {
3518 copilot
3519 .update(cx, |copilot, cx| {
3520 copilot.discard_completions(&self.copilot_state.completions, cx)
3521 })
3522 .detach_and_log_err(cx);
3523
3524 self.report_copilot_event(None, false, cx)
3525 }
3526
3527 self.display_map.update(cx, |map, cx| {
3528 map.splice_inlays(vec![suggestion.id], Vec::new(), cx)
3529 });
3530 cx.notify();
3531 true
3532 } else {
3533 false
3534 }
3535 }
3536
3537 fn is_copilot_enabled_at(
3538 &self,
3539 location: Anchor,
3540 snapshot: &MultiBufferSnapshot,
3541 cx: &mut ViewContext<Self>,
3542 ) -> bool {
3543 let file = snapshot.file_at(location);
3544 let language = snapshot.language_at(location);
3545 let settings = all_language_settings(file, cx);
3546 settings.copilot_enabled(language, file.map(|f| f.path().as_ref()))
3547 }
3548
3549 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
3550 if let Some(suggestion) = self.copilot_state.suggestion.as_ref() {
3551 let buffer = self.buffer.read(cx).read(cx);
3552 suggestion.position.is_valid(&buffer)
3553 } else {
3554 false
3555 }
3556 }
3557
3558 fn take_active_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> Option<Inlay> {
3559 let suggestion = self.copilot_state.suggestion.take()?;
3560 self.display_map.update(cx, |map, cx| {
3561 map.splice_inlays(vec![suggestion.id], Default::default(), cx);
3562 });
3563 let buffer = self.buffer.read(cx).read(cx);
3564
3565 if suggestion.position.is_valid(&buffer) {
3566 Some(suggestion)
3567 } else {
3568 None
3569 }
3570 }
3571
3572 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
3573 let snapshot = self.buffer.read(cx).snapshot(cx);
3574 let selection = self.selections.newest_anchor();
3575 let cursor = selection.head();
3576
3577 if self.context_menu.is_some()
3578 || !self.completion_tasks.is_empty()
3579 || selection.start != selection.end
3580 {
3581 self.discard_copilot_suggestion(cx);
3582 } else if let Some(text) = self
3583 .copilot_state
3584 .text_for_active_completion(cursor, &snapshot)
3585 {
3586 let text = Rope::from(text);
3587 let mut to_remove = Vec::new();
3588 if let Some(suggestion) = self.copilot_state.suggestion.take() {
3589 to_remove.push(suggestion.id);
3590 }
3591
3592 let suggestion_inlay =
3593 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
3594 self.copilot_state.suggestion = Some(suggestion_inlay.clone());
3595 self.display_map.update(cx, move |map, cx| {
3596 map.splice_inlays(to_remove, vec![suggestion_inlay], cx)
3597 });
3598 cx.notify();
3599 } else {
3600 self.discard_copilot_suggestion(cx);
3601 }
3602 }
3603
3604 fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
3605 self.copilot_state = Default::default();
3606 self.discard_copilot_suggestion(cx);
3607 }
3608
3609 pub fn render_code_actions_indicator(
3610 &self,
3611 style: &EditorStyle,
3612 is_active: bool,
3613 cx: &mut ViewContext<Self>,
3614 ) -> Option<AnyElement<Self>> {
3615 if self.available_code_actions.is_some() {
3616 enum CodeActions {}
3617 Some(
3618 MouseEventHandler::new::<CodeActions, _>(0, cx, |state, _| {
3619 Svg::new("icons/bolt_8.svg").with_color(
3620 style
3621 .code_actions
3622 .indicator
3623 .in_state(is_active)
3624 .style_for(state)
3625 .color,
3626 )
3627 })
3628 .with_cursor_style(CursorStyle::PointingHand)
3629 .with_padding(Padding::uniform(3.))
3630 .on_down(MouseButton::Left, |_, this, cx| {
3631 this.toggle_code_actions(
3632 &ToggleCodeActions {
3633 deployed_from_indicator: true,
3634 },
3635 cx,
3636 );
3637 })
3638 .into_any(),
3639 )
3640 } else {
3641 None
3642 }
3643 }
3644
3645 pub fn render_fold_indicators(
3646 &self,
3647 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
3648 style: &EditorStyle,
3649 gutter_hovered: bool,
3650 line_height: f32,
3651 gutter_margin: f32,
3652 cx: &mut ViewContext<Self>,
3653 ) -> Vec<Option<AnyElement<Self>>> {
3654 enum FoldIndicators {}
3655
3656 let style = style.folds.clone();
3657
3658 fold_data
3659 .iter()
3660 .enumerate()
3661 .map(|(ix, fold_data)| {
3662 fold_data
3663 .map(|(fold_status, buffer_row, active)| {
3664 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
3665 MouseEventHandler::new::<FoldIndicators, _>(
3666 ix as usize,
3667 cx,
3668 |mouse_state, _| {
3669 Svg::new(match fold_status {
3670 FoldStatus::Folded => style.folded_icon.clone(),
3671 FoldStatus::Foldable => style.foldable_icon.clone(),
3672 })
3673 .with_color(
3674 style
3675 .indicator
3676 .in_state(fold_status == FoldStatus::Folded)
3677 .style_for(mouse_state)
3678 .color,
3679 )
3680 .constrained()
3681 .with_width(gutter_margin * style.icon_margin_scale)
3682 .aligned()
3683 .constrained()
3684 .with_height(line_height)
3685 .with_width(gutter_margin)
3686 .aligned()
3687 },
3688 )
3689 .with_cursor_style(CursorStyle::PointingHand)
3690 .with_padding(Padding::uniform(3.))
3691 .on_click(MouseButton::Left, {
3692 move |_, editor, cx| match fold_status {
3693 FoldStatus::Folded => {
3694 editor.unfold_at(&UnfoldAt { buffer_row }, cx);
3695 }
3696 FoldStatus::Foldable => {
3697 editor.fold_at(&FoldAt { buffer_row }, cx);
3698 }
3699 }
3700 })
3701 .into_any()
3702 })
3703 })
3704 .flatten()
3705 })
3706 .collect()
3707 }
3708
3709 pub fn context_menu_visible(&self) -> bool {
3710 self.context_menu
3711 .as_ref()
3712 .map_or(false, |menu| menu.visible())
3713 }
3714
3715 pub fn render_context_menu(
3716 &self,
3717 cursor_position: DisplayPoint,
3718 style: EditorStyle,
3719 cx: &mut ViewContext<Editor>,
3720 ) -> Option<(DisplayPoint, AnyElement<Editor>)> {
3721 self.context_menu
3722 .as_ref()
3723 .map(|menu| menu.render(cursor_position, style, cx))
3724 }
3725
3726 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
3727 if !matches!(menu, ContextMenu::Completions(_)) {
3728 self.completion_tasks.clear();
3729 }
3730 self.context_menu = Some(menu);
3731 self.discard_copilot_suggestion(cx);
3732 cx.notify();
3733 }
3734
3735 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
3736 cx.notify();
3737 self.completion_tasks.clear();
3738 let context_menu = self.context_menu.take();
3739 if context_menu.is_some() {
3740 self.update_visible_copilot_suggestion(cx);
3741 }
3742 context_menu
3743 }
3744
3745 pub fn insert_snippet(
3746 &mut self,
3747 insertion_ranges: &[Range<usize>],
3748 snippet: Snippet,
3749 cx: &mut ViewContext<Self>,
3750 ) -> Result<()> {
3751 let tabstops = self.buffer.update(cx, |buffer, cx| {
3752 let snippet_text: Arc<str> = snippet.text.clone().into();
3753 buffer.edit(
3754 insertion_ranges
3755 .iter()
3756 .cloned()
3757 .map(|range| (range, snippet_text.clone())),
3758 Some(AutoindentMode::EachLine),
3759 cx,
3760 );
3761
3762 let snapshot = &*buffer.read(cx);
3763 let snippet = &snippet;
3764 snippet
3765 .tabstops
3766 .iter()
3767 .map(|tabstop| {
3768 let mut tabstop_ranges = tabstop
3769 .iter()
3770 .flat_map(|tabstop_range| {
3771 let mut delta = 0_isize;
3772 insertion_ranges.iter().map(move |insertion_range| {
3773 let insertion_start = insertion_range.start as isize + delta;
3774 delta +=
3775 snippet.text.len() as isize - insertion_range.len() as isize;
3776
3777 let start = snapshot.anchor_before(
3778 (insertion_start + tabstop_range.start) as usize,
3779 );
3780 let end = snapshot
3781 .anchor_after((insertion_start + tabstop_range.end) as usize);
3782 start..end
3783 })
3784 })
3785 .collect::<Vec<_>>();
3786 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
3787 tabstop_ranges
3788 })
3789 .collect::<Vec<_>>()
3790 });
3791
3792 if let Some(tabstop) = tabstops.first() {
3793 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3794 s.select_ranges(tabstop.iter().cloned());
3795 });
3796 self.snippet_stack.push(SnippetState {
3797 active_index: 0,
3798 ranges: tabstops,
3799 });
3800 }
3801
3802 Ok(())
3803 }
3804
3805 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3806 self.move_to_snippet_tabstop(Bias::Right, cx)
3807 }
3808
3809 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3810 self.move_to_snippet_tabstop(Bias::Left, cx)
3811 }
3812
3813 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
3814 if let Some(mut snippet) = self.snippet_stack.pop() {
3815 match bias {
3816 Bias::Left => {
3817 if snippet.active_index > 0 {
3818 snippet.active_index -= 1;
3819 } else {
3820 self.snippet_stack.push(snippet);
3821 return false;
3822 }
3823 }
3824 Bias::Right => {
3825 if snippet.active_index + 1 < snippet.ranges.len() {
3826 snippet.active_index += 1;
3827 } else {
3828 self.snippet_stack.push(snippet);
3829 return false;
3830 }
3831 }
3832 }
3833 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
3834 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3835 s.select_anchor_ranges(current_ranges.iter().cloned())
3836 });
3837 // If snippet state is not at the last tabstop, push it back on the stack
3838 if snippet.active_index + 1 < snippet.ranges.len() {
3839 self.snippet_stack.push(snippet);
3840 }
3841 return true;
3842 }
3843 }
3844
3845 false
3846 }
3847
3848 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
3849 self.transact(cx, |this, cx| {
3850 this.select_all(&SelectAll, cx);
3851 this.insert("", cx);
3852 });
3853 }
3854
3855 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
3856 self.transact(cx, |this, cx| {
3857 this.select_autoclose_pair(cx);
3858 let mut selections = this.selections.all::<Point>(cx);
3859 if !this.selections.line_mode {
3860 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3861 for selection in &mut selections {
3862 if selection.is_empty() {
3863 let old_head = selection.head();
3864 let mut new_head =
3865 movement::left(&display_map, old_head.to_display_point(&display_map))
3866 .to_point(&display_map);
3867 if let Some((buffer, line_buffer_range)) = display_map
3868 .buffer_snapshot
3869 .buffer_line_for_row(old_head.row)
3870 {
3871 let indent_size =
3872 buffer.indent_size_for_line(line_buffer_range.start.row);
3873 let indent_len = match indent_size.kind {
3874 IndentKind::Space => {
3875 buffer.settings_at(line_buffer_range.start, cx).tab_size
3876 }
3877 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
3878 };
3879 if old_head.column <= indent_size.len && old_head.column > 0 {
3880 let indent_len = indent_len.get();
3881 new_head = cmp::min(
3882 new_head,
3883 Point::new(
3884 old_head.row,
3885 ((old_head.column - 1) / indent_len) * indent_len,
3886 ),
3887 );
3888 }
3889 }
3890
3891 selection.set_head(new_head, SelectionGoal::None);
3892 }
3893 }
3894 }
3895
3896 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3897 this.insert("", cx);
3898 this.refresh_copilot_suggestions(true, cx);
3899 });
3900 }
3901
3902 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
3903 self.transact(cx, |this, cx| {
3904 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3905 let line_mode = s.line_mode;
3906 s.move_with(|map, selection| {
3907 if selection.is_empty() && !line_mode {
3908 let cursor = movement::right(map, selection.head());
3909 selection.end = cursor;
3910 selection.reversed = true;
3911 selection.goal = SelectionGoal::None;
3912 }
3913 })
3914 });
3915 this.insert("", cx);
3916 this.refresh_copilot_suggestions(true, cx);
3917 });
3918 }
3919
3920 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
3921 if self.move_to_prev_snippet_tabstop(cx) {
3922 return;
3923 }
3924
3925 self.outdent(&Outdent, cx);
3926 }
3927
3928 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
3929 if self.move_to_next_snippet_tabstop(cx) {
3930 return;
3931 }
3932
3933 let mut selections = self.selections.all_adjusted(cx);
3934 let buffer = self.buffer.read(cx);
3935 let snapshot = buffer.snapshot(cx);
3936 let rows_iter = selections.iter().map(|s| s.head().row);
3937 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
3938
3939 let mut edits = Vec::new();
3940 let mut prev_edited_row = 0;
3941 let mut row_delta = 0;
3942 for selection in &mut selections {
3943 if selection.start.row != prev_edited_row {
3944 row_delta = 0;
3945 }
3946 prev_edited_row = selection.end.row;
3947
3948 // If the selection is non-empty, then increase the indentation of the selected lines.
3949 if !selection.is_empty() {
3950 row_delta =
3951 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3952 continue;
3953 }
3954
3955 // If the selection is empty and the cursor is in the leading whitespace before the
3956 // suggested indentation, then auto-indent the line.
3957 let cursor = selection.head();
3958 let current_indent = snapshot.indent_size_for_line(cursor.row);
3959 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
3960 if cursor.column < suggested_indent.len
3961 && cursor.column <= current_indent.len
3962 && current_indent.len <= suggested_indent.len
3963 {
3964 selection.start = Point::new(cursor.row, suggested_indent.len);
3965 selection.end = selection.start;
3966 if row_delta == 0 {
3967 edits.extend(Buffer::edit_for_indent_size_adjustment(
3968 cursor.row,
3969 current_indent,
3970 suggested_indent,
3971 ));
3972 row_delta = suggested_indent.len - current_indent.len;
3973 }
3974 continue;
3975 }
3976 }
3977
3978 // Accept copilot suggestion if there is only one selection and the cursor is not
3979 // in the leading whitespace.
3980 if self.selections.count() == 1
3981 && cursor.column >= current_indent.len
3982 && self.has_active_copilot_suggestion(cx)
3983 {
3984 self.accept_copilot_suggestion(cx);
3985 return;
3986 }
3987
3988 // Otherwise, insert a hard or soft tab.
3989 let settings = buffer.settings_at(cursor, cx);
3990 let tab_size = if settings.hard_tabs {
3991 IndentSize::tab()
3992 } else {
3993 let tab_size = settings.tab_size.get();
3994 let char_column = snapshot
3995 .text_for_range(Point::new(cursor.row, 0)..cursor)
3996 .flat_map(str::chars)
3997 .count()
3998 + row_delta as usize;
3999 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
4000 IndentSize::spaces(chars_to_next_tab_stop)
4001 };
4002 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
4003 selection.end = selection.start;
4004 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
4005 row_delta += tab_size.len;
4006 }
4007
4008 self.transact(cx, |this, cx| {
4009 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4010 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4011 this.refresh_copilot_suggestions(true, cx);
4012 });
4013 }
4014
4015 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
4016 let mut selections = self.selections.all::<Point>(cx);
4017 let mut prev_edited_row = 0;
4018 let mut row_delta = 0;
4019 let mut edits = Vec::new();
4020 let buffer = self.buffer.read(cx);
4021 let snapshot = buffer.snapshot(cx);
4022 for selection in &mut selections {
4023 if selection.start.row != prev_edited_row {
4024 row_delta = 0;
4025 }
4026 prev_edited_row = selection.end.row;
4027
4028 row_delta =
4029 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
4030 }
4031
4032 self.transact(cx, |this, cx| {
4033 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4034 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4035 });
4036 }
4037
4038 fn indent_selection(
4039 buffer: &MultiBuffer,
4040 snapshot: &MultiBufferSnapshot,
4041 selection: &mut Selection<Point>,
4042 edits: &mut Vec<(Range<Point>, String)>,
4043 delta_for_start_row: u32,
4044 cx: &AppContext,
4045 ) -> u32 {
4046 let settings = buffer.settings_at(selection.start, cx);
4047 let tab_size = settings.tab_size.get();
4048 let indent_kind = if settings.hard_tabs {
4049 IndentKind::Tab
4050 } else {
4051 IndentKind::Space
4052 };
4053 let mut start_row = selection.start.row;
4054 let mut end_row = selection.end.row + 1;
4055
4056 // If a selection ends at the beginning of a line, don't indent
4057 // that last line.
4058 if selection.end.column == 0 {
4059 end_row -= 1;
4060 }
4061
4062 // Avoid re-indenting a row that has already been indented by a
4063 // previous selection, but still update this selection's column
4064 // to reflect that indentation.
4065 if delta_for_start_row > 0 {
4066 start_row += 1;
4067 selection.start.column += delta_for_start_row;
4068 if selection.end.row == selection.start.row {
4069 selection.end.column += delta_for_start_row;
4070 }
4071 }
4072
4073 let mut delta_for_end_row = 0;
4074 for row in start_row..end_row {
4075 let current_indent = snapshot.indent_size_for_line(row);
4076 let indent_delta = match (current_indent.kind, indent_kind) {
4077 (IndentKind::Space, IndentKind::Space) => {
4078 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
4079 IndentSize::spaces(columns_to_next_tab_stop)
4080 }
4081 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
4082 (_, IndentKind::Tab) => IndentSize::tab(),
4083 };
4084
4085 let row_start = Point::new(row, 0);
4086 edits.push((
4087 row_start..row_start,
4088 indent_delta.chars().collect::<String>(),
4089 ));
4090
4091 // Update this selection's endpoints to reflect the indentation.
4092 if row == selection.start.row {
4093 selection.start.column += indent_delta.len;
4094 }
4095 if row == selection.end.row {
4096 selection.end.column += indent_delta.len;
4097 delta_for_end_row = indent_delta.len;
4098 }
4099 }
4100
4101 if selection.start.row == selection.end.row {
4102 delta_for_start_row + delta_for_end_row
4103 } else {
4104 delta_for_end_row
4105 }
4106 }
4107
4108 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
4109 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4110 let selections = self.selections.all::<Point>(cx);
4111 let mut deletion_ranges = Vec::new();
4112 let mut last_outdent = None;
4113 {
4114 let buffer = self.buffer.read(cx);
4115 let snapshot = buffer.snapshot(cx);
4116 for selection in &selections {
4117 let settings = buffer.settings_at(selection.start, cx);
4118 let tab_size = settings.tab_size.get();
4119 let mut rows = selection.spanned_rows(false, &display_map);
4120
4121 // Avoid re-outdenting a row that has already been outdented by a
4122 // previous selection.
4123 if let Some(last_row) = last_outdent {
4124 if last_row == rows.start {
4125 rows.start += 1;
4126 }
4127 }
4128
4129 for row in rows {
4130 let indent_size = snapshot.indent_size_for_line(row);
4131 if indent_size.len > 0 {
4132 let deletion_len = match indent_size.kind {
4133 IndentKind::Space => {
4134 let columns_to_prev_tab_stop = indent_size.len % tab_size;
4135 if columns_to_prev_tab_stop == 0 {
4136 tab_size
4137 } else {
4138 columns_to_prev_tab_stop
4139 }
4140 }
4141 IndentKind::Tab => 1,
4142 };
4143 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
4144 last_outdent = Some(row);
4145 }
4146 }
4147 }
4148 }
4149
4150 self.transact(cx, |this, cx| {
4151 this.buffer.update(cx, |buffer, cx| {
4152 let empty_str: Arc<str> = "".into();
4153 buffer.edit(
4154 deletion_ranges
4155 .into_iter()
4156 .map(|range| (range, empty_str.clone())),
4157 None,
4158 cx,
4159 );
4160 });
4161 let selections = this.selections.all::<usize>(cx);
4162 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4163 });
4164 }
4165
4166 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
4167 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4168 let selections = self.selections.all::<Point>(cx);
4169
4170 let mut new_cursors = Vec::new();
4171 let mut edit_ranges = Vec::new();
4172 let mut selections = selections.iter().peekable();
4173 while let Some(selection) = selections.next() {
4174 let mut rows = selection.spanned_rows(false, &display_map);
4175 let goal_display_column = selection.head().to_display_point(&display_map).column();
4176
4177 // Accumulate contiguous regions of rows that we want to delete.
4178 while let Some(next_selection) = selections.peek() {
4179 let next_rows = next_selection.spanned_rows(false, &display_map);
4180 if next_rows.start <= rows.end {
4181 rows.end = next_rows.end;
4182 selections.next().unwrap();
4183 } else {
4184 break;
4185 }
4186 }
4187
4188 let buffer = &display_map.buffer_snapshot;
4189 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
4190 let edit_end;
4191 let cursor_buffer_row;
4192 if buffer.max_point().row >= rows.end {
4193 // If there's a line after the range, delete the \n from the end of the row range
4194 // and position the cursor on the next line.
4195 edit_end = Point::new(rows.end, 0).to_offset(buffer);
4196 cursor_buffer_row = rows.end;
4197 } else {
4198 // If there isn't a line after the range, delete the \n from the line before the
4199 // start of the row range and position the cursor there.
4200 edit_start = edit_start.saturating_sub(1);
4201 edit_end = buffer.len();
4202 cursor_buffer_row = rows.start.saturating_sub(1);
4203 }
4204
4205 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
4206 *cursor.column_mut() =
4207 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
4208
4209 new_cursors.push((
4210 selection.id,
4211 buffer.anchor_after(cursor.to_point(&display_map)),
4212 ));
4213 edit_ranges.push(edit_start..edit_end);
4214 }
4215
4216 self.transact(cx, |this, cx| {
4217 let buffer = this.buffer.update(cx, |buffer, cx| {
4218 let empty_str: Arc<str> = "".into();
4219 buffer.edit(
4220 edit_ranges
4221 .into_iter()
4222 .map(|range| (range, empty_str.clone())),
4223 None,
4224 cx,
4225 );
4226 buffer.snapshot(cx)
4227 });
4228 let new_selections = new_cursors
4229 .into_iter()
4230 .map(|(id, cursor)| {
4231 let cursor = cursor.to_point(&buffer);
4232 Selection {
4233 id,
4234 start: cursor,
4235 end: cursor,
4236 reversed: false,
4237 goal: SelectionGoal::None,
4238 }
4239 })
4240 .collect();
4241
4242 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4243 s.select(new_selections);
4244 });
4245 });
4246 }
4247
4248 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
4249 let mut row_ranges = Vec::<Range<u32>>::new();
4250 for selection in self.selections.all::<Point>(cx) {
4251 let start = selection.start.row;
4252 let end = if selection.start.row == selection.end.row {
4253 selection.start.row + 1
4254 } else {
4255 selection.end.row
4256 };
4257
4258 if let Some(last_row_range) = row_ranges.last_mut() {
4259 if start <= last_row_range.end {
4260 last_row_range.end = end;
4261 continue;
4262 }
4263 }
4264 row_ranges.push(start..end);
4265 }
4266
4267 let snapshot = self.buffer.read(cx).snapshot(cx);
4268 let mut cursor_positions = Vec::new();
4269 for row_range in &row_ranges {
4270 let anchor = snapshot.anchor_before(Point::new(
4271 row_range.end - 1,
4272 snapshot.line_len(row_range.end - 1),
4273 ));
4274 cursor_positions.push(anchor.clone()..anchor);
4275 }
4276
4277 self.transact(cx, |this, cx| {
4278 for row_range in row_ranges.into_iter().rev() {
4279 for row in row_range.rev() {
4280 let end_of_line = Point::new(row, snapshot.line_len(row));
4281 let indent = snapshot.indent_size_for_line(row + 1);
4282 let start_of_next_line = Point::new(row + 1, indent.len);
4283
4284 let replace = if snapshot.line_len(row + 1) > indent.len {
4285 " "
4286 } else {
4287 ""
4288 };
4289
4290 this.buffer.update(cx, |buffer, cx| {
4291 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
4292 });
4293 }
4294 }
4295
4296 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4297 s.select_anchor_ranges(cursor_positions)
4298 });
4299 });
4300 }
4301
4302 pub fn sort_lines_case_sensitive(
4303 &mut self,
4304 _: &SortLinesCaseSensitive,
4305 cx: &mut ViewContext<Self>,
4306 ) {
4307 self.manipulate_lines(cx, |lines| lines.sort())
4308 }
4309
4310 pub fn sort_lines_case_insensitive(
4311 &mut self,
4312 _: &SortLinesCaseInsensitive,
4313 cx: &mut ViewContext<Self>,
4314 ) {
4315 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
4316 }
4317
4318 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
4319 self.manipulate_lines(cx, |lines| lines.reverse())
4320 }
4321
4322 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
4323 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
4324 }
4325
4326 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
4327 where
4328 Fn: FnMut(&mut [&str]),
4329 {
4330 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4331 let buffer = self.buffer.read(cx).snapshot(cx);
4332
4333 let mut edits = Vec::new();
4334
4335 let selections = self.selections.all::<Point>(cx);
4336 let mut selections = selections.iter().peekable();
4337 let mut contiguous_row_selections = Vec::new();
4338 let mut new_selections = Vec::new();
4339
4340 while let Some(selection) = selections.next() {
4341 let (start_row, end_row) = consume_contiguous_rows(
4342 &mut contiguous_row_selections,
4343 selection,
4344 &display_map,
4345 &mut selections,
4346 );
4347
4348 let start_point = Point::new(start_row, 0);
4349 let end_point = Point::new(end_row - 1, buffer.line_len(end_row - 1));
4350 let text = buffer
4351 .text_for_range(start_point..end_point)
4352 .collect::<String>();
4353 let mut lines = text.split("\n").collect_vec();
4354
4355 let lines_len = lines.len();
4356 callback(&mut lines);
4357
4358 // This is a current limitation with selections.
4359 // If we wanted to support removing or adding lines, we'd need to fix the logic associated with selections.
4360 debug_assert!(
4361 lines.len() == lines_len,
4362 "callback should not change the number of lines"
4363 );
4364
4365 edits.push((start_point..end_point, lines.join("\n")));
4366 let start_anchor = buffer.anchor_after(start_point);
4367 let end_anchor = buffer.anchor_before(end_point);
4368
4369 // Make selection and push
4370 new_selections.push(Selection {
4371 id: selection.id,
4372 start: start_anchor.to_offset(&buffer),
4373 end: end_anchor.to_offset(&buffer),
4374 goal: SelectionGoal::None,
4375 reversed: selection.reversed,
4376 });
4377 }
4378
4379 self.transact(cx, |this, cx| {
4380 this.buffer.update(cx, |buffer, cx| {
4381 buffer.edit(edits, None, cx);
4382 });
4383
4384 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4385 s.select(new_selections);
4386 });
4387
4388 this.request_autoscroll(Autoscroll::fit(), cx);
4389 });
4390 }
4391
4392 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
4393 self.manipulate_text(cx, |text| text.to_uppercase())
4394 }
4395
4396 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
4397 self.manipulate_text(cx, |text| text.to_lowercase())
4398 }
4399
4400 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
4401 self.manipulate_text(cx, |text| text.to_case(Case::Title))
4402 }
4403
4404 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
4405 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
4406 }
4407
4408 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
4409 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
4410 }
4411
4412 pub fn convert_to_upper_camel_case(
4413 &mut self,
4414 _: &ConvertToUpperCamelCase,
4415 cx: &mut ViewContext<Self>,
4416 ) {
4417 self.manipulate_text(cx, |text| text.to_case(Case::UpperCamel))
4418 }
4419
4420 pub fn convert_to_lower_camel_case(
4421 &mut self,
4422 _: &ConvertToLowerCamelCase,
4423 cx: &mut ViewContext<Self>,
4424 ) {
4425 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
4426 }
4427
4428 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
4429 where
4430 Fn: FnMut(&str) -> String,
4431 {
4432 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4433 let buffer = self.buffer.read(cx).snapshot(cx);
4434
4435 let mut new_selections = Vec::new();
4436 let mut edits = Vec::new();
4437 let mut selection_adjustment = 0i32;
4438
4439 for selection in self.selections.all::<usize>(cx) {
4440 let selection_is_empty = selection.is_empty();
4441
4442 let (start, end) = if selection_is_empty {
4443 let word_range = movement::surrounding_word(
4444 &display_map,
4445 selection.start.to_display_point(&display_map),
4446 );
4447 let start = word_range.start.to_offset(&display_map, Bias::Left);
4448 let end = word_range.end.to_offset(&display_map, Bias::Left);
4449 (start, end)
4450 } else {
4451 (selection.start, selection.end)
4452 };
4453
4454 let text = buffer.text_for_range(start..end).collect::<String>();
4455 let old_length = text.len() as i32;
4456 let text = callback(&text);
4457
4458 new_selections.push(Selection {
4459 start: (start as i32 - selection_adjustment) as usize,
4460 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
4461 goal: SelectionGoal::None,
4462 ..selection
4463 });
4464
4465 selection_adjustment += old_length - text.len() as i32;
4466
4467 edits.push((start..end, text));
4468 }
4469
4470 self.transact(cx, |this, cx| {
4471 this.buffer.update(cx, |buffer, cx| {
4472 buffer.edit(edits, None, cx);
4473 });
4474
4475 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4476 s.select(new_selections);
4477 });
4478
4479 this.request_autoscroll(Autoscroll::fit(), cx);
4480 });
4481 }
4482
4483 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
4484 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4485 let buffer = &display_map.buffer_snapshot;
4486 let selections = self.selections.all::<Point>(cx);
4487
4488 let mut edits = Vec::new();
4489 let mut selections_iter = selections.iter().peekable();
4490 while let Some(selection) = selections_iter.next() {
4491 // Avoid duplicating the same lines twice.
4492 let mut rows = selection.spanned_rows(false, &display_map);
4493
4494 while let Some(next_selection) = selections_iter.peek() {
4495 let next_rows = next_selection.spanned_rows(false, &display_map);
4496 if next_rows.start < rows.end {
4497 rows.end = next_rows.end;
4498 selections_iter.next().unwrap();
4499 } else {
4500 break;
4501 }
4502 }
4503
4504 // Copy the text from the selected row region and splice it at the start of the region.
4505 let start = Point::new(rows.start, 0);
4506 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
4507 let text = buffer
4508 .text_for_range(start..end)
4509 .chain(Some("\n"))
4510 .collect::<String>();
4511 edits.push((start..start, text));
4512 }
4513
4514 self.transact(cx, |this, cx| {
4515 this.buffer.update(cx, |buffer, cx| {
4516 buffer.edit(edits, None, cx);
4517 });
4518
4519 this.request_autoscroll(Autoscroll::fit(), cx);
4520 });
4521 }
4522
4523 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
4524 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4525 let buffer = self.buffer.read(cx).snapshot(cx);
4526
4527 let mut edits = Vec::new();
4528 let mut unfold_ranges = Vec::new();
4529 let mut refold_ranges = Vec::new();
4530
4531 let selections = self.selections.all::<Point>(cx);
4532 let mut selections = selections.iter().peekable();
4533 let mut contiguous_row_selections = Vec::new();
4534 let mut new_selections = Vec::new();
4535
4536 while let Some(selection) = selections.next() {
4537 // Find all the selections that span a contiguous row range
4538 let (start_row, end_row) = consume_contiguous_rows(
4539 &mut contiguous_row_selections,
4540 selection,
4541 &display_map,
4542 &mut selections,
4543 );
4544
4545 // Move the text spanned by the row range to be before the line preceding the row range
4546 if start_row > 0 {
4547 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
4548 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
4549 let insertion_point = display_map
4550 .prev_line_boundary(Point::new(start_row - 1, 0))
4551 .0;
4552
4553 // Don't move lines across excerpts
4554 if buffer
4555 .excerpt_boundaries_in_range((
4556 Bound::Excluded(insertion_point),
4557 Bound::Included(range_to_move.end),
4558 ))
4559 .next()
4560 .is_none()
4561 {
4562 let text = buffer
4563 .text_for_range(range_to_move.clone())
4564 .flat_map(|s| s.chars())
4565 .skip(1)
4566 .chain(['\n'])
4567 .collect::<String>();
4568
4569 edits.push((
4570 buffer.anchor_after(range_to_move.start)
4571 ..buffer.anchor_before(range_to_move.end),
4572 String::new(),
4573 ));
4574 let insertion_anchor = buffer.anchor_after(insertion_point);
4575 edits.push((insertion_anchor..insertion_anchor, text));
4576
4577 let row_delta = range_to_move.start.row - insertion_point.row + 1;
4578
4579 // Move selections up
4580 new_selections.extend(contiguous_row_selections.drain(..).map(
4581 |mut selection| {
4582 selection.start.row -= row_delta;
4583 selection.end.row -= row_delta;
4584 selection
4585 },
4586 ));
4587
4588 // Move folds up
4589 unfold_ranges.push(range_to_move.clone());
4590 for fold in display_map.folds_in_range(
4591 buffer.anchor_before(range_to_move.start)
4592 ..buffer.anchor_after(range_to_move.end),
4593 ) {
4594 let mut start = fold.start.to_point(&buffer);
4595 let mut end = fold.end.to_point(&buffer);
4596 start.row -= row_delta;
4597 end.row -= row_delta;
4598 refold_ranges.push(start..end);
4599 }
4600 }
4601 }
4602
4603 // If we didn't move line(s), preserve the existing selections
4604 new_selections.append(&mut contiguous_row_selections);
4605 }
4606
4607 self.transact(cx, |this, cx| {
4608 this.unfold_ranges(unfold_ranges, true, true, cx);
4609 this.buffer.update(cx, |buffer, cx| {
4610 for (range, text) in edits {
4611 buffer.edit([(range, text)], None, cx);
4612 }
4613 });
4614 this.fold_ranges(refold_ranges, true, cx);
4615 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4616 s.select(new_selections);
4617 })
4618 });
4619 }
4620
4621 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
4622 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4623 let buffer = self.buffer.read(cx).snapshot(cx);
4624
4625 let mut edits = Vec::new();
4626 let mut unfold_ranges = Vec::new();
4627 let mut refold_ranges = Vec::new();
4628
4629 let selections = self.selections.all::<Point>(cx);
4630 let mut selections = selections.iter().peekable();
4631 let mut contiguous_row_selections = Vec::new();
4632 let mut new_selections = Vec::new();
4633
4634 while let Some(selection) = selections.next() {
4635 // Find all the selections that span a contiguous row range
4636 let (start_row, end_row) = consume_contiguous_rows(
4637 &mut contiguous_row_selections,
4638 selection,
4639 &display_map,
4640 &mut selections,
4641 );
4642
4643 // Move the text spanned by the row range to be after the last line of the row range
4644 if end_row <= buffer.max_point().row {
4645 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
4646 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
4647
4648 // Don't move lines across excerpt boundaries
4649 if buffer
4650 .excerpt_boundaries_in_range((
4651 Bound::Excluded(range_to_move.start),
4652 Bound::Included(insertion_point),
4653 ))
4654 .next()
4655 .is_none()
4656 {
4657 let mut text = String::from("\n");
4658 text.extend(buffer.text_for_range(range_to_move.clone()));
4659 text.pop(); // Drop trailing newline
4660 edits.push((
4661 buffer.anchor_after(range_to_move.start)
4662 ..buffer.anchor_before(range_to_move.end),
4663 String::new(),
4664 ));
4665 let insertion_anchor = buffer.anchor_after(insertion_point);
4666 edits.push((insertion_anchor..insertion_anchor, text));
4667
4668 let row_delta = insertion_point.row - range_to_move.end.row + 1;
4669
4670 // Move selections down
4671 new_selections.extend(contiguous_row_selections.drain(..).map(
4672 |mut selection| {
4673 selection.start.row += row_delta;
4674 selection.end.row += row_delta;
4675 selection
4676 },
4677 ));
4678
4679 // Move folds down
4680 unfold_ranges.push(range_to_move.clone());
4681 for fold in display_map.folds_in_range(
4682 buffer.anchor_before(range_to_move.start)
4683 ..buffer.anchor_after(range_to_move.end),
4684 ) {
4685 let mut start = fold.start.to_point(&buffer);
4686 let mut end = fold.end.to_point(&buffer);
4687 start.row += row_delta;
4688 end.row += row_delta;
4689 refold_ranges.push(start..end);
4690 }
4691 }
4692 }
4693
4694 // If we didn't move line(s), preserve the existing selections
4695 new_selections.append(&mut contiguous_row_selections);
4696 }
4697
4698 self.transact(cx, |this, cx| {
4699 this.unfold_ranges(unfold_ranges, true, true, cx);
4700 this.buffer.update(cx, |buffer, cx| {
4701 for (range, text) in edits {
4702 buffer.edit([(range, text)], None, cx);
4703 }
4704 });
4705 this.fold_ranges(refold_ranges, true, cx);
4706 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
4707 });
4708 }
4709
4710 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
4711 self.transact(cx, |this, cx| {
4712 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4713 let mut edits: Vec<(Range<usize>, String)> = Default::default();
4714 let line_mode = s.line_mode;
4715 s.move_with(|display_map, selection| {
4716 if !selection.is_empty() || line_mode {
4717 return;
4718 }
4719
4720 let mut head = selection.head();
4721 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
4722 if head.column() == display_map.line_len(head.row()) {
4723 transpose_offset = display_map
4724 .buffer_snapshot
4725 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4726 }
4727
4728 if transpose_offset == 0 {
4729 return;
4730 }
4731
4732 *head.column_mut() += 1;
4733 head = display_map.clip_point(head, Bias::Right);
4734 selection.collapse_to(head, SelectionGoal::Column(head.column()));
4735
4736 let transpose_start = display_map
4737 .buffer_snapshot
4738 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4739 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
4740 let transpose_end = display_map
4741 .buffer_snapshot
4742 .clip_offset(transpose_offset + 1, Bias::Right);
4743 if let Some(ch) =
4744 display_map.buffer_snapshot.chars_at(transpose_start).next()
4745 {
4746 edits.push((transpose_start..transpose_offset, String::new()));
4747 edits.push((transpose_end..transpose_end, ch.to_string()));
4748 }
4749 }
4750 });
4751 edits
4752 });
4753 this.buffer
4754 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
4755 let selections = this.selections.all::<usize>(cx);
4756 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4757 s.select(selections);
4758 });
4759 });
4760 }
4761
4762 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
4763 let mut text = String::new();
4764 let buffer = self.buffer.read(cx).snapshot(cx);
4765 let mut selections = self.selections.all::<Point>(cx);
4766 let mut clipboard_selections = Vec::with_capacity(selections.len());
4767 {
4768 let max_point = buffer.max_point();
4769 for selection in &mut selections {
4770 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4771 if is_entire_line {
4772 selection.start = Point::new(selection.start.row, 0);
4773 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
4774 selection.goal = SelectionGoal::None;
4775 }
4776 let mut len = 0;
4777 for chunk in buffer.text_for_range(selection.start..selection.end) {
4778 text.push_str(chunk);
4779 len += chunk.len();
4780 }
4781 clipboard_selections.push(ClipboardSelection {
4782 len,
4783 is_entire_line,
4784 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
4785 });
4786 }
4787 }
4788
4789 self.transact(cx, |this, cx| {
4790 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4791 s.select(selections);
4792 });
4793 this.insert("", cx);
4794 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4795 });
4796 }
4797
4798 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
4799 let selections = self.selections.all::<Point>(cx);
4800 let buffer = self.buffer.read(cx).read(cx);
4801 let mut text = String::new();
4802
4803 let mut clipboard_selections = Vec::with_capacity(selections.len());
4804 {
4805 let max_point = buffer.max_point();
4806 for selection in selections.iter() {
4807 let mut start = selection.start;
4808 let mut end = selection.end;
4809 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4810 if is_entire_line {
4811 start = Point::new(start.row, 0);
4812 end = cmp::min(max_point, Point::new(end.row + 1, 0));
4813 }
4814 let mut len = 0;
4815 for chunk in buffer.text_for_range(start..end) {
4816 text.push_str(chunk);
4817 len += chunk.len();
4818 }
4819 clipboard_selections.push(ClipboardSelection {
4820 len,
4821 is_entire_line,
4822 first_line_indent: buffer.indent_size_for_line(start.row).len,
4823 });
4824 }
4825 }
4826
4827 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4828 }
4829
4830 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
4831 self.transact(cx, |this, cx| {
4832 if let Some(item) = cx.read_from_clipboard() {
4833 let mut clipboard_text = Cow::Borrowed(item.text());
4834 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
4835 let old_selections = this.selections.all::<usize>(cx);
4836 let all_selections_were_entire_line =
4837 clipboard_selections.iter().all(|s| s.is_entire_line);
4838 let first_selection_indent_column =
4839 clipboard_selections.first().map(|s| s.first_line_indent);
4840 if clipboard_selections.len() != old_selections.len() {
4841 let mut newline_separated_text = String::new();
4842 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
4843 let mut ix = 0;
4844 while let Some(clipboard_selection) = clipboard_selections.next() {
4845 newline_separated_text
4846 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
4847 ix += clipboard_selection.len;
4848 if clipboard_selections.peek().is_some() {
4849 newline_separated_text.push('\n');
4850 }
4851 }
4852 clipboard_text = Cow::Owned(newline_separated_text);
4853 }
4854
4855 this.buffer.update(cx, |buffer, cx| {
4856 let snapshot = buffer.read(cx);
4857 let mut start_offset = 0;
4858 let mut edits = Vec::new();
4859 let mut original_indent_columns = Vec::new();
4860 let line_mode = this.selections.line_mode;
4861 for (ix, selection) in old_selections.iter().enumerate() {
4862 let to_insert;
4863 let entire_line;
4864 let original_indent_column;
4865 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
4866 let end_offset = start_offset + clipboard_selection.len;
4867 to_insert = &clipboard_text[start_offset..end_offset];
4868 entire_line = clipboard_selection.is_entire_line;
4869 start_offset = end_offset;
4870 original_indent_column =
4871 Some(clipboard_selection.first_line_indent);
4872 } else {
4873 to_insert = clipboard_text.as_str();
4874 entire_line = all_selections_were_entire_line;
4875 original_indent_column = first_selection_indent_column
4876 }
4877
4878 // If the corresponding selection was empty when this slice of the
4879 // clipboard text was written, then the entire line containing the
4880 // selection was copied. If this selection is also currently empty,
4881 // then paste the line before the current line of the buffer.
4882 let range = if selection.is_empty() && !line_mode && entire_line {
4883 let column = selection.start.to_point(&snapshot).column as usize;
4884 let line_start = selection.start - column;
4885 line_start..line_start
4886 } else {
4887 selection.range()
4888 };
4889
4890 edits.push((range, to_insert));
4891 original_indent_columns.extend(original_indent_column);
4892 }
4893 drop(snapshot);
4894
4895 buffer.edit(
4896 edits,
4897 Some(AutoindentMode::Block {
4898 original_indent_columns,
4899 }),
4900 cx,
4901 );
4902 });
4903
4904 let selections = this.selections.all::<usize>(cx);
4905 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4906 } else {
4907 this.insert(&clipboard_text, cx);
4908 }
4909 }
4910 });
4911 }
4912
4913 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
4914 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
4915 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
4916 self.change_selections(None, cx, |s| {
4917 s.select_anchors(selections.to_vec());
4918 });
4919 }
4920 self.request_autoscroll(Autoscroll::fit(), cx);
4921 self.unmark_text(cx);
4922 self.refresh_copilot_suggestions(true, cx);
4923 cx.emit(Event::Edited);
4924 }
4925 }
4926
4927 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
4928 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
4929 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
4930 {
4931 self.change_selections(None, cx, |s| {
4932 s.select_anchors(selections.to_vec());
4933 });
4934 }
4935 self.request_autoscroll(Autoscroll::fit(), cx);
4936 self.unmark_text(cx);
4937 self.refresh_copilot_suggestions(true, cx);
4938 cx.emit(Event::Edited);
4939 }
4940 }
4941
4942 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
4943 self.buffer
4944 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
4945 }
4946
4947 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
4948 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4949 let line_mode = s.line_mode;
4950 s.move_with(|map, selection| {
4951 let cursor = if selection.is_empty() && !line_mode {
4952 movement::left(map, selection.start)
4953 } else {
4954 selection.start
4955 };
4956 selection.collapse_to(cursor, SelectionGoal::None);
4957 });
4958 })
4959 }
4960
4961 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
4962 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4963 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
4964 })
4965 }
4966
4967 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
4968 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4969 let line_mode = s.line_mode;
4970 s.move_with(|map, selection| {
4971 let cursor = if selection.is_empty() && !line_mode {
4972 movement::right(map, selection.end)
4973 } else {
4974 selection.end
4975 };
4976 selection.collapse_to(cursor, SelectionGoal::None)
4977 });
4978 })
4979 }
4980
4981 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
4982 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4983 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
4984 })
4985 }
4986
4987 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
4988 if self.take_rename(true, cx).is_some() {
4989 return;
4990 }
4991
4992 if let Some(context_menu) = self.context_menu.as_mut() {
4993 if context_menu.select_prev(cx) {
4994 return;
4995 }
4996 }
4997
4998 if matches!(self.mode, EditorMode::SingleLine) {
4999 cx.propagate_action();
5000 return;
5001 }
5002
5003 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5004 let line_mode = s.line_mode;
5005 s.move_with(|map, selection| {
5006 if !selection.is_empty() && !line_mode {
5007 selection.goal = SelectionGoal::None;
5008 }
5009 let (cursor, goal) = movement::up(map, selection.start, selection.goal, false);
5010 selection.collapse_to(cursor, goal);
5011 });
5012 })
5013 }
5014
5015 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
5016 if self.take_rename(true, cx).is_some() {
5017 return;
5018 }
5019
5020 if self
5021 .context_menu
5022 .as_mut()
5023 .map(|menu| menu.select_first(cx))
5024 .unwrap_or(false)
5025 {
5026 return;
5027 }
5028
5029 if matches!(self.mode, EditorMode::SingleLine) {
5030 cx.propagate_action();
5031 return;
5032 }
5033
5034 let row_count = if let Some(row_count) = self.visible_line_count() {
5035 row_count as u32 - 1
5036 } else {
5037 return;
5038 };
5039
5040 let autoscroll = if action.center_cursor {
5041 Autoscroll::center()
5042 } else {
5043 Autoscroll::fit()
5044 };
5045
5046 self.change_selections(Some(autoscroll), cx, |s| {
5047 let line_mode = s.line_mode;
5048 s.move_with(|map, selection| {
5049 if !selection.is_empty() && !line_mode {
5050 selection.goal = SelectionGoal::None;
5051 }
5052 let (cursor, goal) =
5053 movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
5054 selection.collapse_to(cursor, goal);
5055 });
5056 });
5057 }
5058
5059 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
5060 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5061 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
5062 })
5063 }
5064
5065 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
5066 self.take_rename(true, cx);
5067
5068 if let Some(context_menu) = self.context_menu.as_mut() {
5069 if context_menu.select_next(cx) {
5070 return;
5071 }
5072 }
5073
5074 if self.mode == EditorMode::SingleLine {
5075 cx.propagate_action();
5076 return;
5077 }
5078
5079 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5080 let line_mode = s.line_mode;
5081 s.move_with(|map, selection| {
5082 if !selection.is_empty() && !line_mode {
5083 selection.goal = SelectionGoal::None;
5084 }
5085 let (cursor, goal) = movement::down(map, selection.end, selection.goal, false);
5086 selection.collapse_to(cursor, goal);
5087 });
5088 });
5089 }
5090
5091 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
5092 if self.take_rename(true, cx).is_some() {
5093 return;
5094 }
5095
5096 if self
5097 .context_menu
5098 .as_mut()
5099 .map(|menu| menu.select_last(cx))
5100 .unwrap_or(false)
5101 {
5102 return;
5103 }
5104
5105 if matches!(self.mode, EditorMode::SingleLine) {
5106 cx.propagate_action();
5107 return;
5108 }
5109
5110 let row_count = if let Some(row_count) = self.visible_line_count() {
5111 row_count as u32 - 1
5112 } else {
5113 return;
5114 };
5115
5116 let autoscroll = if action.center_cursor {
5117 Autoscroll::center()
5118 } else {
5119 Autoscroll::fit()
5120 };
5121
5122 self.change_selections(Some(autoscroll), cx, |s| {
5123 let line_mode = s.line_mode;
5124 s.move_with(|map, selection| {
5125 if !selection.is_empty() && !line_mode {
5126 selection.goal = SelectionGoal::None;
5127 }
5128 let (cursor, goal) =
5129 movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
5130 selection.collapse_to(cursor, goal);
5131 });
5132 });
5133 }
5134
5135 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
5136 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5137 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
5138 });
5139 }
5140
5141 pub fn move_to_previous_word_start(
5142 &mut self,
5143 _: &MoveToPreviousWordStart,
5144 cx: &mut ViewContext<Self>,
5145 ) {
5146 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5147 s.move_cursors_with(|map, head, _| {
5148 (
5149 movement::previous_word_start(map, head),
5150 SelectionGoal::None,
5151 )
5152 });
5153 })
5154 }
5155
5156 pub fn move_to_previous_subword_start(
5157 &mut self,
5158 _: &MoveToPreviousSubwordStart,
5159 cx: &mut ViewContext<Self>,
5160 ) {
5161 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5162 s.move_cursors_with(|map, head, _| {
5163 (
5164 movement::previous_subword_start(map, head),
5165 SelectionGoal::None,
5166 )
5167 });
5168 })
5169 }
5170
5171 pub fn select_to_previous_word_start(
5172 &mut self,
5173 _: &SelectToPreviousWordStart,
5174 cx: &mut ViewContext<Self>,
5175 ) {
5176 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5177 s.move_heads_with(|map, head, _| {
5178 (
5179 movement::previous_word_start(map, head),
5180 SelectionGoal::None,
5181 )
5182 });
5183 })
5184 }
5185
5186 pub fn select_to_previous_subword_start(
5187 &mut self,
5188 _: &SelectToPreviousSubwordStart,
5189 cx: &mut ViewContext<Self>,
5190 ) {
5191 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5192 s.move_heads_with(|map, head, _| {
5193 (
5194 movement::previous_subword_start(map, head),
5195 SelectionGoal::None,
5196 )
5197 });
5198 })
5199 }
5200
5201 pub fn delete_to_previous_word_start(
5202 &mut self,
5203 _: &DeleteToPreviousWordStart,
5204 cx: &mut ViewContext<Self>,
5205 ) {
5206 self.transact(cx, |this, cx| {
5207 this.select_autoclose_pair(cx);
5208 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5209 let line_mode = s.line_mode;
5210 s.move_with(|map, selection| {
5211 if selection.is_empty() && !line_mode {
5212 let cursor = movement::previous_word_start(map, selection.head());
5213 selection.set_head(cursor, SelectionGoal::None);
5214 }
5215 });
5216 });
5217 this.insert("", cx);
5218 });
5219 }
5220
5221 pub fn delete_to_previous_subword_start(
5222 &mut self,
5223 _: &DeleteToPreviousSubwordStart,
5224 cx: &mut ViewContext<Self>,
5225 ) {
5226 self.transact(cx, |this, cx| {
5227 this.select_autoclose_pair(cx);
5228 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5229 let line_mode = s.line_mode;
5230 s.move_with(|map, selection| {
5231 if selection.is_empty() && !line_mode {
5232 let cursor = movement::previous_subword_start(map, selection.head());
5233 selection.set_head(cursor, SelectionGoal::None);
5234 }
5235 });
5236 });
5237 this.insert("", cx);
5238 });
5239 }
5240
5241 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
5242 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5243 s.move_cursors_with(|map, head, _| {
5244 (movement::next_word_end(map, head), SelectionGoal::None)
5245 });
5246 })
5247 }
5248
5249 pub fn move_to_next_subword_end(
5250 &mut self,
5251 _: &MoveToNextSubwordEnd,
5252 cx: &mut ViewContext<Self>,
5253 ) {
5254 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5255 s.move_cursors_with(|map, head, _| {
5256 (movement::next_subword_end(map, head), SelectionGoal::None)
5257 });
5258 })
5259 }
5260
5261 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
5262 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5263 s.move_heads_with(|map, head, _| {
5264 (movement::next_word_end(map, head), SelectionGoal::None)
5265 });
5266 })
5267 }
5268
5269 pub fn select_to_next_subword_end(
5270 &mut self,
5271 _: &SelectToNextSubwordEnd,
5272 cx: &mut ViewContext<Self>,
5273 ) {
5274 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5275 s.move_heads_with(|map, head, _| {
5276 (movement::next_subword_end(map, head), SelectionGoal::None)
5277 });
5278 })
5279 }
5280
5281 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
5282 self.transact(cx, |this, cx| {
5283 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5284 let line_mode = s.line_mode;
5285 s.move_with(|map, selection| {
5286 if selection.is_empty() && !line_mode {
5287 let cursor = movement::next_word_end(map, selection.head());
5288 selection.set_head(cursor, SelectionGoal::None);
5289 }
5290 });
5291 });
5292 this.insert("", cx);
5293 });
5294 }
5295
5296 pub fn delete_to_next_subword_end(
5297 &mut self,
5298 _: &DeleteToNextSubwordEnd,
5299 cx: &mut ViewContext<Self>,
5300 ) {
5301 self.transact(cx, |this, cx| {
5302 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5303 s.move_with(|map, selection| {
5304 if selection.is_empty() {
5305 let cursor = movement::next_subword_end(map, selection.head());
5306 selection.set_head(cursor, SelectionGoal::None);
5307 }
5308 });
5309 });
5310 this.insert("", cx);
5311 });
5312 }
5313
5314 pub fn move_to_beginning_of_line(
5315 &mut self,
5316 _: &MoveToBeginningOfLine,
5317 cx: &mut ViewContext<Self>,
5318 ) {
5319 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5320 s.move_cursors_with(|map, head, _| {
5321 (
5322 movement::indented_line_beginning(map, head, true),
5323 SelectionGoal::None,
5324 )
5325 });
5326 })
5327 }
5328
5329 pub fn select_to_beginning_of_line(
5330 &mut self,
5331 action: &SelectToBeginningOfLine,
5332 cx: &mut ViewContext<Self>,
5333 ) {
5334 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5335 s.move_heads_with(|map, head, _| {
5336 (
5337 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
5338 SelectionGoal::None,
5339 )
5340 });
5341 });
5342 }
5343
5344 pub fn delete_to_beginning_of_line(
5345 &mut self,
5346 _: &DeleteToBeginningOfLine,
5347 cx: &mut ViewContext<Self>,
5348 ) {
5349 self.transact(cx, |this, cx| {
5350 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5351 s.move_with(|_, selection| {
5352 selection.reversed = true;
5353 });
5354 });
5355
5356 this.select_to_beginning_of_line(
5357 &SelectToBeginningOfLine {
5358 stop_at_soft_wraps: false,
5359 },
5360 cx,
5361 );
5362 this.backspace(&Backspace, cx);
5363 });
5364 }
5365
5366 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
5367 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5368 s.move_cursors_with(|map, head, _| {
5369 (movement::line_end(map, head, true), SelectionGoal::None)
5370 });
5371 })
5372 }
5373
5374 pub fn select_to_end_of_line(
5375 &mut self,
5376 action: &SelectToEndOfLine,
5377 cx: &mut ViewContext<Self>,
5378 ) {
5379 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5380 s.move_heads_with(|map, head, _| {
5381 (
5382 movement::line_end(map, head, action.stop_at_soft_wraps),
5383 SelectionGoal::None,
5384 )
5385 });
5386 })
5387 }
5388
5389 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
5390 self.transact(cx, |this, cx| {
5391 this.select_to_end_of_line(
5392 &SelectToEndOfLine {
5393 stop_at_soft_wraps: false,
5394 },
5395 cx,
5396 );
5397 this.delete(&Delete, cx);
5398 });
5399 }
5400
5401 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
5402 self.transact(cx, |this, cx| {
5403 this.select_to_end_of_line(
5404 &SelectToEndOfLine {
5405 stop_at_soft_wraps: false,
5406 },
5407 cx,
5408 );
5409 this.cut(&Cut, cx);
5410 });
5411 }
5412
5413 pub fn move_to_start_of_paragraph(
5414 &mut self,
5415 _: &MoveToStartOfParagraph,
5416 cx: &mut ViewContext<Self>,
5417 ) {
5418 if matches!(self.mode, EditorMode::SingleLine) {
5419 cx.propagate_action();
5420 return;
5421 }
5422
5423 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5424 s.move_with(|map, selection| {
5425 selection.collapse_to(
5426 movement::start_of_paragraph(map, selection.head(), 1),
5427 SelectionGoal::None,
5428 )
5429 });
5430 })
5431 }
5432
5433 pub fn move_to_end_of_paragraph(
5434 &mut self,
5435 _: &MoveToEndOfParagraph,
5436 cx: &mut ViewContext<Self>,
5437 ) {
5438 if matches!(self.mode, EditorMode::SingleLine) {
5439 cx.propagate_action();
5440 return;
5441 }
5442
5443 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5444 s.move_with(|map, selection| {
5445 selection.collapse_to(
5446 movement::end_of_paragraph(map, selection.head(), 1),
5447 SelectionGoal::None,
5448 )
5449 });
5450 })
5451 }
5452
5453 pub fn select_to_start_of_paragraph(
5454 &mut self,
5455 _: &SelectToStartOfParagraph,
5456 cx: &mut ViewContext<Self>,
5457 ) {
5458 if matches!(self.mode, EditorMode::SingleLine) {
5459 cx.propagate_action();
5460 return;
5461 }
5462
5463 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5464 s.move_heads_with(|map, head, _| {
5465 (
5466 movement::start_of_paragraph(map, head, 1),
5467 SelectionGoal::None,
5468 )
5469 });
5470 })
5471 }
5472
5473 pub fn select_to_end_of_paragraph(
5474 &mut self,
5475 _: &SelectToEndOfParagraph,
5476 cx: &mut ViewContext<Self>,
5477 ) {
5478 if matches!(self.mode, EditorMode::SingleLine) {
5479 cx.propagate_action();
5480 return;
5481 }
5482
5483 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5484 s.move_heads_with(|map, head, _| {
5485 (
5486 movement::end_of_paragraph(map, head, 1),
5487 SelectionGoal::None,
5488 )
5489 });
5490 })
5491 }
5492
5493 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
5494 if matches!(self.mode, EditorMode::SingleLine) {
5495 cx.propagate_action();
5496 return;
5497 }
5498
5499 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5500 s.select_ranges(vec![0..0]);
5501 });
5502 }
5503
5504 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
5505 let mut selection = self.selections.last::<Point>(cx);
5506 selection.set_head(Point::zero(), SelectionGoal::None);
5507
5508 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5509 s.select(vec![selection]);
5510 });
5511 }
5512
5513 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
5514 if matches!(self.mode, EditorMode::SingleLine) {
5515 cx.propagate_action();
5516 return;
5517 }
5518
5519 let cursor = self.buffer.read(cx).read(cx).len();
5520 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5521 s.select_ranges(vec![cursor..cursor])
5522 });
5523 }
5524
5525 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
5526 self.nav_history = nav_history;
5527 }
5528
5529 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
5530 self.nav_history.as_ref()
5531 }
5532
5533 fn push_to_nav_history(
5534 &mut self,
5535 cursor_anchor: Anchor,
5536 new_position: Option<Point>,
5537 cx: &mut ViewContext<Self>,
5538 ) {
5539 if let Some(nav_history) = self.nav_history.as_mut() {
5540 let buffer = self.buffer.read(cx).read(cx);
5541 let cursor_position = cursor_anchor.to_point(&buffer);
5542 let scroll_state = self.scroll_manager.anchor();
5543 let scroll_top_row = scroll_state.top_row(&buffer);
5544 drop(buffer);
5545
5546 if let Some(new_position) = new_position {
5547 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
5548 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
5549 return;
5550 }
5551 }
5552
5553 nav_history.push(
5554 Some(NavigationData {
5555 cursor_anchor,
5556 cursor_position,
5557 scroll_anchor: scroll_state,
5558 scroll_top_row,
5559 }),
5560 cx,
5561 );
5562 }
5563 }
5564
5565 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
5566 let buffer = self.buffer.read(cx).snapshot(cx);
5567 let mut selection = self.selections.first::<usize>(cx);
5568 selection.set_head(buffer.len(), SelectionGoal::None);
5569 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5570 s.select(vec![selection]);
5571 });
5572 }
5573
5574 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
5575 let end = self.buffer.read(cx).read(cx).len();
5576 self.change_selections(None, cx, |s| {
5577 s.select_ranges(vec![0..end]);
5578 });
5579 }
5580
5581 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
5582 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5583 let mut selections = self.selections.all::<Point>(cx);
5584 let max_point = display_map.buffer_snapshot.max_point();
5585 for selection in &mut selections {
5586 let rows = selection.spanned_rows(true, &display_map);
5587 selection.start = Point::new(rows.start, 0);
5588 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
5589 selection.reversed = false;
5590 }
5591 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5592 s.select(selections);
5593 });
5594 }
5595
5596 pub fn split_selection_into_lines(
5597 &mut self,
5598 _: &SplitSelectionIntoLines,
5599 cx: &mut ViewContext<Self>,
5600 ) {
5601 let mut to_unfold = Vec::new();
5602 let mut new_selection_ranges = Vec::new();
5603 {
5604 let selections = self.selections.all::<Point>(cx);
5605 let buffer = self.buffer.read(cx).read(cx);
5606 for selection in selections {
5607 for row in selection.start.row..selection.end.row {
5608 let cursor = Point::new(row, buffer.line_len(row));
5609 new_selection_ranges.push(cursor..cursor);
5610 }
5611 new_selection_ranges.push(selection.end..selection.end);
5612 to_unfold.push(selection.start..selection.end);
5613 }
5614 }
5615 self.unfold_ranges(to_unfold, true, true, cx);
5616 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5617 s.select_ranges(new_selection_ranges);
5618 });
5619 }
5620
5621 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
5622 self.add_selection(true, cx);
5623 }
5624
5625 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
5626 self.add_selection(false, cx);
5627 }
5628
5629 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
5630 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5631 let mut selections = self.selections.all::<Point>(cx);
5632 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
5633 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
5634 let range = oldest_selection.display_range(&display_map).sorted();
5635 let columns = cmp::min(range.start.column(), range.end.column())
5636 ..cmp::max(range.start.column(), range.end.column());
5637
5638 selections.clear();
5639 let mut stack = Vec::new();
5640 for row in range.start.row()..=range.end.row() {
5641 if let Some(selection) = self.selections.build_columnar_selection(
5642 &display_map,
5643 row,
5644 &columns,
5645 oldest_selection.reversed,
5646 ) {
5647 stack.push(selection.id);
5648 selections.push(selection);
5649 }
5650 }
5651
5652 if above {
5653 stack.reverse();
5654 }
5655
5656 AddSelectionsState { above, stack }
5657 });
5658
5659 let last_added_selection = *state.stack.last().unwrap();
5660 let mut new_selections = Vec::new();
5661 if above == state.above {
5662 let end_row = if above {
5663 0
5664 } else {
5665 display_map.max_point().row()
5666 };
5667
5668 'outer: for selection in selections {
5669 if selection.id == last_added_selection {
5670 let range = selection.display_range(&display_map).sorted();
5671 debug_assert_eq!(range.start.row(), range.end.row());
5672 let mut row = range.start.row();
5673 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
5674 {
5675 start..end
5676 } else {
5677 cmp::min(range.start.column(), range.end.column())
5678 ..cmp::max(range.start.column(), range.end.column())
5679 };
5680
5681 while row != end_row {
5682 if above {
5683 row -= 1;
5684 } else {
5685 row += 1;
5686 }
5687
5688 if let Some(new_selection) = self.selections.build_columnar_selection(
5689 &display_map,
5690 row,
5691 &columns,
5692 selection.reversed,
5693 ) {
5694 state.stack.push(new_selection.id);
5695 if above {
5696 new_selections.push(new_selection);
5697 new_selections.push(selection);
5698 } else {
5699 new_selections.push(selection);
5700 new_selections.push(new_selection);
5701 }
5702
5703 continue 'outer;
5704 }
5705 }
5706 }
5707
5708 new_selections.push(selection);
5709 }
5710 } else {
5711 new_selections = selections;
5712 new_selections.retain(|s| s.id != last_added_selection);
5713 state.stack.pop();
5714 }
5715
5716 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5717 s.select(new_selections);
5718 });
5719 if state.stack.len() > 1 {
5720 self.add_selections_state = Some(state);
5721 }
5722 }
5723
5724 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
5725 self.push_to_selection_history();
5726 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5727 let buffer = &display_map.buffer_snapshot;
5728 let mut selections = self.selections.all::<usize>(cx);
5729 if let Some(mut select_next_state) = self.select_next_state.take() {
5730 let query = &select_next_state.query;
5731 if !select_next_state.done {
5732 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5733 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5734 let mut next_selected_range = None;
5735
5736 let bytes_after_last_selection =
5737 buffer.bytes_in_range(last_selection.end..buffer.len());
5738 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
5739 let query_matches = query
5740 .stream_find_iter(bytes_after_last_selection)
5741 .map(|result| (last_selection.end, result))
5742 .chain(
5743 query
5744 .stream_find_iter(bytes_before_first_selection)
5745 .map(|result| (0, result)),
5746 );
5747 for (start_offset, query_match) in query_matches {
5748 let query_match = query_match.unwrap(); // can only fail due to I/O
5749 let offset_range =
5750 start_offset + query_match.start()..start_offset + query_match.end();
5751 let display_range = offset_range.start.to_display_point(&display_map)
5752 ..offset_range.end.to_display_point(&display_map);
5753
5754 if !select_next_state.wordwise
5755 || (!movement::is_inside_word(&display_map, display_range.start)
5756 && !movement::is_inside_word(&display_map, display_range.end))
5757 {
5758 next_selected_range = Some(offset_range);
5759 break;
5760 }
5761 }
5762
5763 if let Some(next_selected_range) = next_selected_range {
5764 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5765 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5766 if action.replace_newest {
5767 s.delete(s.newest_anchor().id);
5768 }
5769 s.insert_range(next_selected_range);
5770 });
5771 } else {
5772 select_next_state.done = true;
5773 }
5774 }
5775
5776 self.select_next_state = Some(select_next_state);
5777 } else if selections.len() == 1 {
5778 let selection = selections.last_mut().unwrap();
5779 if selection.start == selection.end {
5780 let word_range = movement::surrounding_word(
5781 &display_map,
5782 selection.start.to_display_point(&display_map),
5783 );
5784 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5785 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5786 selection.goal = SelectionGoal::None;
5787 selection.reversed = false;
5788
5789 let query = buffer
5790 .text_for_range(selection.start..selection.end)
5791 .collect::<String>();
5792 let select_state = SelectNextState {
5793 query: AhoCorasick::new_auto_configured(&[query]),
5794 wordwise: true,
5795 done: false,
5796 };
5797 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5798 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5799 s.select(selections);
5800 });
5801 self.select_next_state = Some(select_state);
5802 } else {
5803 let query = buffer
5804 .text_for_range(selection.start..selection.end)
5805 .collect::<String>();
5806 self.select_next_state = Some(SelectNextState {
5807 query: AhoCorasick::new_auto_configured(&[query]),
5808 wordwise: false,
5809 done: false,
5810 });
5811 self.select_next(action, cx);
5812 }
5813 }
5814 }
5815
5816 pub fn select_previous(&mut self, action: &SelectPrevious, cx: &mut ViewContext<Self>) {
5817 self.push_to_selection_history();
5818 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5819 let buffer = &display_map.buffer_snapshot;
5820 let mut selections = self.selections.all::<usize>(cx);
5821 if let Some(mut select_prev_state) = self.select_prev_state.take() {
5822 let query = &select_prev_state.query;
5823 if !select_prev_state.done {
5824 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5825 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5826 let mut next_selected_range = None;
5827 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
5828 let bytes_before_last_selection =
5829 buffer.reversed_bytes_in_range(0..last_selection.start);
5830 let bytes_after_first_selection =
5831 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
5832 let query_matches = query
5833 .stream_find_iter(bytes_before_last_selection)
5834 .map(|result| (last_selection.start, result))
5835 .chain(
5836 query
5837 .stream_find_iter(bytes_after_first_selection)
5838 .map(|result| (buffer.len(), result)),
5839 );
5840 for (end_offset, query_match) in query_matches {
5841 let query_match = query_match.unwrap(); // can only fail due to I/O
5842 let offset_range =
5843 end_offset - query_match.end()..end_offset - query_match.start();
5844 let display_range = offset_range.start.to_display_point(&display_map)
5845 ..offset_range.end.to_display_point(&display_map);
5846
5847 if !select_prev_state.wordwise
5848 || (!movement::is_inside_word(&display_map, display_range.start)
5849 && !movement::is_inside_word(&display_map, display_range.end))
5850 {
5851 next_selected_range = Some(offset_range);
5852 break;
5853 }
5854 }
5855
5856 if let Some(next_selected_range) = next_selected_range {
5857 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5858 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5859 if action.replace_newest {
5860 s.delete(s.newest_anchor().id);
5861 }
5862 s.insert_range(next_selected_range);
5863 });
5864 } else {
5865 select_prev_state.done = true;
5866 }
5867 }
5868
5869 self.select_prev_state = Some(select_prev_state);
5870 } else if selections.len() == 1 {
5871 let selection = selections.last_mut().unwrap();
5872 if selection.start == selection.end {
5873 let word_range = movement::surrounding_word(
5874 &display_map,
5875 selection.start.to_display_point(&display_map),
5876 );
5877 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5878 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5879 selection.goal = SelectionGoal::None;
5880 selection.reversed = false;
5881
5882 let query = buffer
5883 .text_for_range(selection.start..selection.end)
5884 .collect::<String>();
5885 let query = query.chars().rev().collect::<String>();
5886 let select_state = SelectNextState {
5887 query: AhoCorasick::new_auto_configured(&[query]),
5888 wordwise: true,
5889 done: false,
5890 };
5891 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5892 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5893 s.select(selections);
5894 });
5895 self.select_prev_state = Some(select_state);
5896 } else {
5897 let query = buffer
5898 .text_for_range(selection.start..selection.end)
5899 .collect::<String>();
5900 let query = query.chars().rev().collect::<String>();
5901 self.select_prev_state = Some(SelectNextState {
5902 query: AhoCorasick::new_auto_configured(&[query]),
5903 wordwise: false,
5904 done: false,
5905 });
5906 self.select_previous(action, cx);
5907 }
5908 }
5909 }
5910
5911 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
5912 self.transact(cx, |this, cx| {
5913 let mut selections = this.selections.all::<Point>(cx);
5914 let mut edits = Vec::new();
5915 let mut selection_edit_ranges = Vec::new();
5916 let mut last_toggled_row = None;
5917 let snapshot = this.buffer.read(cx).read(cx);
5918 let empty_str: Arc<str> = "".into();
5919 let mut suffixes_inserted = Vec::new();
5920
5921 fn comment_prefix_range(
5922 snapshot: &MultiBufferSnapshot,
5923 row: u32,
5924 comment_prefix: &str,
5925 comment_prefix_whitespace: &str,
5926 ) -> Range<Point> {
5927 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
5928
5929 let mut line_bytes = snapshot
5930 .bytes_in_range(start..snapshot.max_point())
5931 .flatten()
5932 .copied();
5933
5934 // If this line currently begins with the line comment prefix, then record
5935 // the range containing the prefix.
5936 if line_bytes
5937 .by_ref()
5938 .take(comment_prefix.len())
5939 .eq(comment_prefix.bytes())
5940 {
5941 // Include any whitespace that matches the comment prefix.
5942 let matching_whitespace_len = line_bytes
5943 .zip(comment_prefix_whitespace.bytes())
5944 .take_while(|(a, b)| a == b)
5945 .count() as u32;
5946 let end = Point::new(
5947 start.row,
5948 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
5949 );
5950 start..end
5951 } else {
5952 start..start
5953 }
5954 }
5955
5956 fn comment_suffix_range(
5957 snapshot: &MultiBufferSnapshot,
5958 row: u32,
5959 comment_suffix: &str,
5960 comment_suffix_has_leading_space: bool,
5961 ) -> Range<Point> {
5962 let end = Point::new(row, snapshot.line_len(row));
5963 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
5964
5965 let mut line_end_bytes = snapshot
5966 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
5967 .flatten()
5968 .copied();
5969
5970 let leading_space_len = if suffix_start_column > 0
5971 && line_end_bytes.next() == Some(b' ')
5972 && comment_suffix_has_leading_space
5973 {
5974 1
5975 } else {
5976 0
5977 };
5978
5979 // If this line currently begins with the line comment prefix, then record
5980 // the range containing the prefix.
5981 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
5982 let start = Point::new(end.row, suffix_start_column - leading_space_len);
5983 start..end
5984 } else {
5985 end..end
5986 }
5987 }
5988
5989 // TODO: Handle selections that cross excerpts
5990 for selection in &mut selections {
5991 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
5992 let language = if let Some(language) =
5993 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
5994 {
5995 language
5996 } else {
5997 continue;
5998 };
5999
6000 selection_edit_ranges.clear();
6001
6002 // If multiple selections contain a given row, avoid processing that
6003 // row more than once.
6004 let mut start_row = selection.start.row;
6005 if last_toggled_row == Some(start_row) {
6006 start_row += 1;
6007 }
6008 let end_row =
6009 if selection.end.row > selection.start.row && selection.end.column == 0 {
6010 selection.end.row - 1
6011 } else {
6012 selection.end.row
6013 };
6014 last_toggled_row = Some(end_row);
6015
6016 if start_row > end_row {
6017 continue;
6018 }
6019
6020 // If the language has line comments, toggle those.
6021 if let Some(full_comment_prefix) = language.line_comment_prefix() {
6022 // Split the comment prefix's trailing whitespace into a separate string,
6023 // as that portion won't be used for detecting if a line is a comment.
6024 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6025 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6026 let mut all_selection_lines_are_comments = true;
6027
6028 for row in start_row..=end_row {
6029 if snapshot.is_line_blank(row) && start_row < end_row {
6030 continue;
6031 }
6032
6033 let prefix_range = comment_prefix_range(
6034 snapshot.deref(),
6035 row,
6036 comment_prefix,
6037 comment_prefix_whitespace,
6038 );
6039 if prefix_range.is_empty() {
6040 all_selection_lines_are_comments = false;
6041 }
6042 selection_edit_ranges.push(prefix_range);
6043 }
6044
6045 if all_selection_lines_are_comments {
6046 edits.extend(
6047 selection_edit_ranges
6048 .iter()
6049 .cloned()
6050 .map(|range| (range, empty_str.clone())),
6051 );
6052 } else {
6053 let min_column = selection_edit_ranges
6054 .iter()
6055 .map(|r| r.start.column)
6056 .min()
6057 .unwrap_or(0);
6058 edits.extend(selection_edit_ranges.iter().map(|range| {
6059 let position = Point::new(range.start.row, min_column);
6060 (position..position, full_comment_prefix.clone())
6061 }));
6062 }
6063 } else if let Some((full_comment_prefix, comment_suffix)) =
6064 language.block_comment_delimiters()
6065 {
6066 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6067 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6068 let prefix_range = comment_prefix_range(
6069 snapshot.deref(),
6070 start_row,
6071 comment_prefix,
6072 comment_prefix_whitespace,
6073 );
6074 let suffix_range = comment_suffix_range(
6075 snapshot.deref(),
6076 end_row,
6077 comment_suffix.trim_start_matches(' '),
6078 comment_suffix.starts_with(' '),
6079 );
6080
6081 if prefix_range.is_empty() || suffix_range.is_empty() {
6082 edits.push((
6083 prefix_range.start..prefix_range.start,
6084 full_comment_prefix.clone(),
6085 ));
6086 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
6087 suffixes_inserted.push((end_row, comment_suffix.len()));
6088 } else {
6089 edits.push((prefix_range, empty_str.clone()));
6090 edits.push((suffix_range, empty_str.clone()));
6091 }
6092 } else {
6093 continue;
6094 }
6095 }
6096
6097 drop(snapshot);
6098 this.buffer.update(cx, |buffer, cx| {
6099 buffer.edit(edits, None, cx);
6100 });
6101
6102 // Adjust selections so that they end before any comment suffixes that
6103 // were inserted.
6104 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
6105 let mut selections = this.selections.all::<Point>(cx);
6106 let snapshot = this.buffer.read(cx).read(cx);
6107 for selection in &mut selections {
6108 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
6109 match row.cmp(&selection.end.row) {
6110 Ordering::Less => {
6111 suffixes_inserted.next();
6112 continue;
6113 }
6114 Ordering::Greater => break,
6115 Ordering::Equal => {
6116 if selection.end.column == snapshot.line_len(row) {
6117 if selection.is_empty() {
6118 selection.start.column -= suffix_len as u32;
6119 }
6120 selection.end.column -= suffix_len as u32;
6121 }
6122 break;
6123 }
6124 }
6125 }
6126 }
6127
6128 drop(snapshot);
6129 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6130
6131 let selections = this.selections.all::<Point>(cx);
6132 let selections_on_single_row = selections.windows(2).all(|selections| {
6133 selections[0].start.row == selections[1].start.row
6134 && selections[0].end.row == selections[1].end.row
6135 && selections[0].start.row == selections[0].end.row
6136 });
6137 let selections_selecting = selections
6138 .iter()
6139 .any(|selection| selection.start != selection.end);
6140 let advance_downwards = action.advance_downwards
6141 && selections_on_single_row
6142 && !selections_selecting
6143 && this.mode != EditorMode::SingleLine;
6144
6145 if advance_downwards {
6146 let snapshot = this.buffer.read(cx).snapshot(cx);
6147
6148 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6149 s.move_cursors_with(|display_snapshot, display_point, _| {
6150 let mut point = display_point.to_point(display_snapshot);
6151 point.row += 1;
6152 point = snapshot.clip_point(point, Bias::Left);
6153 let display_point = point.to_display_point(display_snapshot);
6154 (display_point, SelectionGoal::Column(display_point.column()))
6155 })
6156 });
6157 }
6158 });
6159 }
6160
6161 pub fn select_larger_syntax_node(
6162 &mut self,
6163 _: &SelectLargerSyntaxNode,
6164 cx: &mut ViewContext<Self>,
6165 ) {
6166 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6167 let buffer = self.buffer.read(cx).snapshot(cx);
6168 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
6169
6170 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6171 let mut selected_larger_node = false;
6172 let new_selections = old_selections
6173 .iter()
6174 .map(|selection| {
6175 let old_range = selection.start..selection.end;
6176 let mut new_range = old_range.clone();
6177 while let Some(containing_range) =
6178 buffer.range_for_syntax_ancestor(new_range.clone())
6179 {
6180 new_range = containing_range;
6181 if !display_map.intersects_fold(new_range.start)
6182 && !display_map.intersects_fold(new_range.end)
6183 {
6184 break;
6185 }
6186 }
6187
6188 selected_larger_node |= new_range != old_range;
6189 Selection {
6190 id: selection.id,
6191 start: new_range.start,
6192 end: new_range.end,
6193 goal: SelectionGoal::None,
6194 reversed: selection.reversed,
6195 }
6196 })
6197 .collect::<Vec<_>>();
6198
6199 if selected_larger_node {
6200 stack.push(old_selections);
6201 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6202 s.select(new_selections);
6203 });
6204 }
6205 self.select_larger_syntax_node_stack = stack;
6206 }
6207
6208 pub fn select_smaller_syntax_node(
6209 &mut self,
6210 _: &SelectSmallerSyntaxNode,
6211 cx: &mut ViewContext<Self>,
6212 ) {
6213 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6214 if let Some(selections) = stack.pop() {
6215 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6216 s.select(selections.to_vec());
6217 });
6218 }
6219 self.select_larger_syntax_node_stack = stack;
6220 }
6221
6222 pub fn move_to_enclosing_bracket(
6223 &mut self,
6224 _: &MoveToEnclosingBracket,
6225 cx: &mut ViewContext<Self>,
6226 ) {
6227 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6228 s.move_offsets_with(|snapshot, selection| {
6229 let Some(enclosing_bracket_ranges) = snapshot.enclosing_bracket_ranges(selection.start..selection.end) else {
6230 return;
6231 };
6232
6233 let mut best_length = usize::MAX;
6234 let mut best_inside = false;
6235 let mut best_in_bracket_range = false;
6236 let mut best_destination = None;
6237 for (open, close) in enclosing_bracket_ranges {
6238 let close = close.to_inclusive();
6239 let length = close.end() - open.start;
6240 let inside = selection.start >= open.end && selection.end <= *close.start();
6241 let in_bracket_range = open.to_inclusive().contains(&selection.head()) || close.contains(&selection.head());
6242
6243 // If best is next to a bracket and current isn't, skip
6244 if !in_bracket_range && best_in_bracket_range {
6245 continue;
6246 }
6247
6248 // Prefer smaller lengths unless best is inside and current isn't
6249 if length > best_length && (best_inside || !inside) {
6250 continue;
6251 }
6252
6253 best_length = length;
6254 best_inside = inside;
6255 best_in_bracket_range = in_bracket_range;
6256 best_destination = Some(if close.contains(&selection.start) && close.contains(&selection.end) {
6257 if inside {
6258 open.end
6259 } else {
6260 open.start
6261 }
6262 } else {
6263 if inside {
6264 *close.start()
6265 } else {
6266 *close.end()
6267 }
6268 });
6269 }
6270
6271 if let Some(destination) = best_destination {
6272 selection.collapse_to(destination, SelectionGoal::None);
6273 }
6274 })
6275 });
6276 }
6277
6278 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
6279 self.end_selection(cx);
6280 self.selection_history.mode = SelectionHistoryMode::Undoing;
6281 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
6282 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6283 self.select_next_state = entry.select_next_state;
6284 self.select_prev_state = entry.select_prev_state;
6285 self.add_selections_state = entry.add_selections_state;
6286 self.request_autoscroll(Autoscroll::newest(), cx);
6287 }
6288 self.selection_history.mode = SelectionHistoryMode::Normal;
6289 }
6290
6291 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
6292 self.end_selection(cx);
6293 self.selection_history.mode = SelectionHistoryMode::Redoing;
6294 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
6295 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6296 self.select_next_state = entry.select_next_state;
6297 self.select_prev_state = entry.select_prev_state;
6298 self.add_selections_state = entry.add_selections_state;
6299 self.request_autoscroll(Autoscroll::newest(), cx);
6300 }
6301 self.selection_history.mode = SelectionHistoryMode::Normal;
6302 }
6303
6304 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
6305 self.go_to_diagnostic_impl(Direction::Next, cx)
6306 }
6307
6308 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
6309 self.go_to_diagnostic_impl(Direction::Prev, cx)
6310 }
6311
6312 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
6313 let buffer = self.buffer.read(cx).snapshot(cx);
6314 let selection = self.selections.newest::<usize>(cx);
6315
6316 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
6317 if direction == Direction::Next {
6318 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
6319 let (group_id, jump_to) = popover.activation_info();
6320 if self.activate_diagnostics(group_id, cx) {
6321 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6322 let mut new_selection = s.newest_anchor().clone();
6323 new_selection.collapse_to(jump_to, SelectionGoal::None);
6324 s.select_anchors(vec![new_selection.clone()]);
6325 });
6326 }
6327 return;
6328 }
6329 }
6330
6331 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
6332 active_diagnostics
6333 .primary_range
6334 .to_offset(&buffer)
6335 .to_inclusive()
6336 });
6337 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
6338 if active_primary_range.contains(&selection.head()) {
6339 *active_primary_range.end()
6340 } else {
6341 selection.head()
6342 }
6343 } else {
6344 selection.head()
6345 };
6346
6347 loop {
6348 let mut diagnostics = if direction == Direction::Prev {
6349 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
6350 } else {
6351 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
6352 };
6353 let group = diagnostics.find_map(|entry| {
6354 if entry.diagnostic.is_primary
6355 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
6356 && !entry.range.is_empty()
6357 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
6358 {
6359 Some((entry.range, entry.diagnostic.group_id))
6360 } else {
6361 None
6362 }
6363 });
6364
6365 if let Some((primary_range, group_id)) = group {
6366 if self.activate_diagnostics(group_id, cx) {
6367 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6368 s.select(vec![Selection {
6369 id: selection.id,
6370 start: primary_range.start,
6371 end: primary_range.start,
6372 reversed: false,
6373 goal: SelectionGoal::None,
6374 }]);
6375 });
6376 }
6377 break;
6378 } else {
6379 // Cycle around to the start of the buffer, potentially moving back to the start of
6380 // the currently active diagnostic.
6381 active_primary_range.take();
6382 if direction == Direction::Prev {
6383 if search_start == buffer.len() {
6384 break;
6385 } else {
6386 search_start = buffer.len();
6387 }
6388 } else if search_start == 0 {
6389 break;
6390 } else {
6391 search_start = 0;
6392 }
6393 }
6394 }
6395 }
6396
6397 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
6398 let snapshot = self
6399 .display_map
6400 .update(cx, |display_map, cx| display_map.snapshot(cx));
6401 let selection = self.selections.newest::<Point>(cx);
6402
6403 if !self.seek_in_direction(
6404 &snapshot,
6405 selection.head(),
6406 false,
6407 snapshot
6408 .buffer_snapshot
6409 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
6410 cx,
6411 ) {
6412 let wrapped_point = Point::zero();
6413 self.seek_in_direction(
6414 &snapshot,
6415 wrapped_point,
6416 true,
6417 snapshot
6418 .buffer_snapshot
6419 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
6420 cx,
6421 );
6422 }
6423 }
6424
6425 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
6426 let snapshot = self
6427 .display_map
6428 .update(cx, |display_map, cx| display_map.snapshot(cx));
6429 let selection = self.selections.newest::<Point>(cx);
6430
6431 if !self.seek_in_direction(
6432 &snapshot,
6433 selection.head(),
6434 false,
6435 snapshot
6436 .buffer_snapshot
6437 .git_diff_hunks_in_range_rev(0..selection.head().row),
6438 cx,
6439 ) {
6440 let wrapped_point = snapshot.buffer_snapshot.max_point();
6441 self.seek_in_direction(
6442 &snapshot,
6443 wrapped_point,
6444 true,
6445 snapshot
6446 .buffer_snapshot
6447 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
6448 cx,
6449 );
6450 }
6451 }
6452
6453 fn seek_in_direction(
6454 &mut self,
6455 snapshot: &DisplaySnapshot,
6456 initial_point: Point,
6457 is_wrapped: bool,
6458 hunks: impl Iterator<Item = DiffHunk<u32>>,
6459 cx: &mut ViewContext<Editor>,
6460 ) -> bool {
6461 let display_point = initial_point.to_display_point(snapshot);
6462 let mut hunks = hunks
6463 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
6464 .skip_while(|hunk| {
6465 if is_wrapped {
6466 false
6467 } else {
6468 hunk.contains_display_row(display_point.row())
6469 }
6470 })
6471 .dedup();
6472
6473 if let Some(hunk) = hunks.next() {
6474 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6475 let row = hunk.start_display_row();
6476 let point = DisplayPoint::new(row, 0);
6477 s.select_display_ranges([point..point]);
6478 });
6479
6480 true
6481 } else {
6482 false
6483 }
6484 }
6485
6486 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
6487 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
6488 }
6489
6490 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
6491 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx);
6492 }
6493
6494 pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
6495 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
6496 }
6497
6498 pub fn go_to_type_definition_split(
6499 &mut self,
6500 _: &GoToTypeDefinitionSplit,
6501 cx: &mut ViewContext<Self>,
6502 ) {
6503 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx);
6504 }
6505
6506 fn go_to_definition_of_kind(
6507 &mut self,
6508 kind: GotoDefinitionKind,
6509 split: bool,
6510 cx: &mut ViewContext<Self>,
6511 ) {
6512 let Some(workspace) = self.workspace(cx) else { return };
6513 let buffer = self.buffer.read(cx);
6514 let head = self.selections.newest::<usize>(cx).head();
6515 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
6516 text_anchor
6517 } else {
6518 return;
6519 };
6520
6521 let project = workspace.read(cx).project().clone();
6522 let definitions = project.update(cx, |project, cx| match kind {
6523 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
6524 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
6525 });
6526
6527 cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
6528 let definitions = definitions.await?;
6529 editor.update(&mut cx, |editor, cx| {
6530 editor.navigate_to_definitions(definitions, split, cx);
6531 })?;
6532 Ok::<(), anyhow::Error>(())
6533 })
6534 .detach_and_log_err(cx);
6535 }
6536
6537 pub fn navigate_to_definitions(
6538 &mut self,
6539 mut definitions: Vec<LocationLink>,
6540 split: bool,
6541 cx: &mut ViewContext<Editor>,
6542 ) {
6543 let Some(workspace) = self.workspace(cx) else { return };
6544 let pane = workspace.read(cx).active_pane().clone();
6545 // If there is one definition, just open it directly
6546 if definitions.len() == 1 {
6547 let definition = definitions.pop().unwrap();
6548 let range = definition
6549 .target
6550 .range
6551 .to_offset(definition.target.buffer.read(cx));
6552
6553 let range = self.range_for_match(&range);
6554 if Some(&definition.target.buffer) == self.buffer.read(cx).as_singleton().as_ref() {
6555 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6556 s.select_ranges([range]);
6557 });
6558 } else {
6559 cx.window_context().defer(move |cx| {
6560 let target_editor: ViewHandle<Self> = workspace.update(cx, |workspace, cx| {
6561 if split {
6562 workspace.split_project_item(definition.target.buffer.clone(), cx)
6563 } else {
6564 workspace.open_project_item(definition.target.buffer.clone(), cx)
6565 }
6566 });
6567 target_editor.update(cx, |target_editor, cx| {
6568 // When selecting a definition in a different buffer, disable the nav history
6569 // to avoid creating a history entry at the previous cursor location.
6570 pane.update(cx, |pane, _| pane.disable_history());
6571 target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
6572 s.select_ranges([range]);
6573 });
6574 pane.update(cx, |pane, _| pane.enable_history());
6575 });
6576 });
6577 }
6578 } else if !definitions.is_empty() {
6579 let replica_id = self.replica_id(cx);
6580 cx.window_context().defer(move |cx| {
6581 let title = definitions
6582 .iter()
6583 .find(|definition| definition.origin.is_some())
6584 .and_then(|definition| {
6585 definition.origin.as_ref().map(|origin| {
6586 let buffer = origin.buffer.read(cx);
6587 format!(
6588 "Definitions for {}",
6589 buffer
6590 .text_for_range(origin.range.clone())
6591 .collect::<String>()
6592 )
6593 })
6594 })
6595 .unwrap_or("Definitions".to_owned());
6596 let locations = definitions
6597 .into_iter()
6598 .map(|definition| definition.target)
6599 .collect();
6600 workspace.update(cx, |workspace, cx| {
6601 Self::open_locations_in_multibuffer(
6602 workspace, locations, replica_id, title, split, cx,
6603 )
6604 });
6605 });
6606 }
6607 }
6608
6609 pub fn find_all_references(
6610 workspace: &mut Workspace,
6611 _: &FindAllReferences,
6612 cx: &mut ViewContext<Workspace>,
6613 ) -> Option<Task<Result<()>>> {
6614 let active_item = workspace.active_item(cx)?;
6615 let editor_handle = active_item.act_as::<Self>(cx)?;
6616
6617 let editor = editor_handle.read(cx);
6618 let buffer = editor.buffer.read(cx);
6619 let head = editor.selections.newest::<usize>(cx).head();
6620 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
6621 let replica_id = editor.replica_id(cx);
6622
6623 let project = workspace.project().clone();
6624 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
6625 Some(cx.spawn_labeled(
6626 "Finding All References...",
6627 |workspace, mut cx| async move {
6628 let locations = references.await?;
6629 if locations.is_empty() {
6630 return Ok(());
6631 }
6632
6633 workspace.update(&mut cx, |workspace, cx| {
6634 let title = locations
6635 .first()
6636 .as_ref()
6637 .map(|location| {
6638 let buffer = location.buffer.read(cx);
6639 format!(
6640 "References to `{}`",
6641 buffer
6642 .text_for_range(location.range.clone())
6643 .collect::<String>()
6644 )
6645 })
6646 .unwrap();
6647 Self::open_locations_in_multibuffer(
6648 workspace, locations, replica_id, title, false, cx,
6649 );
6650 })?;
6651
6652 Ok(())
6653 },
6654 ))
6655 }
6656
6657 /// Opens a multibuffer with the given project locations in it
6658 pub fn open_locations_in_multibuffer(
6659 workspace: &mut Workspace,
6660 mut locations: Vec<Location>,
6661 replica_id: ReplicaId,
6662 title: String,
6663 split: bool,
6664 cx: &mut ViewContext<Workspace>,
6665 ) {
6666 // If there are multiple definitions, open them in a multibuffer
6667 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
6668 let mut locations = locations.into_iter().peekable();
6669 let mut ranges_to_highlight = Vec::new();
6670
6671 let excerpt_buffer = cx.add_model(|cx| {
6672 let mut multibuffer = MultiBuffer::new(replica_id);
6673 while let Some(location) = locations.next() {
6674 let buffer = location.buffer.read(cx);
6675 let mut ranges_for_buffer = Vec::new();
6676 let range = location.range.to_offset(buffer);
6677 ranges_for_buffer.push(range.clone());
6678
6679 while let Some(next_location) = locations.peek() {
6680 if next_location.buffer == location.buffer {
6681 ranges_for_buffer.push(next_location.range.to_offset(buffer));
6682 locations.next();
6683 } else {
6684 break;
6685 }
6686 }
6687
6688 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
6689 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
6690 location.buffer.clone(),
6691 ranges_for_buffer,
6692 1,
6693 cx,
6694 ))
6695 }
6696
6697 multibuffer.with_title(title)
6698 });
6699
6700 let editor = cx.add_view(|cx| {
6701 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
6702 });
6703 editor.update(cx, |editor, cx| {
6704 editor.highlight_background::<Self>(
6705 ranges_to_highlight,
6706 |theme| theme.editor.highlighted_line_background,
6707 cx,
6708 );
6709 });
6710 if split {
6711 workspace.split_item(Box::new(editor), cx);
6712 } else {
6713 workspace.add_item(Box::new(editor), cx);
6714 }
6715 }
6716
6717 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6718 use language::ToOffset as _;
6719
6720 let project = self.project.clone()?;
6721 let selection = self.selections.newest_anchor().clone();
6722 let (cursor_buffer, cursor_buffer_position) = self
6723 .buffer
6724 .read(cx)
6725 .text_anchor_for_position(selection.head(), cx)?;
6726 let (tail_buffer, _) = self
6727 .buffer
6728 .read(cx)
6729 .text_anchor_for_position(selection.tail(), cx)?;
6730 if tail_buffer != cursor_buffer {
6731 return None;
6732 }
6733
6734 let snapshot = cursor_buffer.read(cx).snapshot();
6735 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
6736 let prepare_rename = project.update(cx, |project, cx| {
6737 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
6738 });
6739
6740 Some(cx.spawn(|this, mut cx| async move {
6741 let rename_range = if let Some(range) = prepare_rename.await? {
6742 Some(range)
6743 } else {
6744 this.read_with(&cx, |this, cx| {
6745 let buffer = this.buffer.read(cx).snapshot(cx);
6746 let mut buffer_highlights = this
6747 .document_highlights_for_position(selection.head(), &buffer)
6748 .filter(|highlight| {
6749 highlight.start.excerpt_id() == selection.head().excerpt_id()
6750 && highlight.end.excerpt_id() == selection.head().excerpt_id()
6751 });
6752 buffer_highlights
6753 .next()
6754 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
6755 })?
6756 };
6757 if let Some(rename_range) = rename_range {
6758 let rename_buffer_range = rename_range.to_offset(&snapshot);
6759 let cursor_offset_in_rename_range =
6760 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
6761
6762 this.update(&mut cx, |this, cx| {
6763 this.take_rename(false, cx);
6764 let style = this.style(cx);
6765 let buffer = this.buffer.read(cx).read(cx);
6766 let cursor_offset = selection.head().to_offset(&buffer);
6767 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
6768 let rename_end = rename_start + rename_buffer_range.len();
6769 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
6770 let mut old_highlight_id = None;
6771 let old_name: Arc<str> = buffer
6772 .chunks(rename_start..rename_end, true)
6773 .map(|chunk| {
6774 if old_highlight_id.is_none() {
6775 old_highlight_id = chunk.syntax_highlight_id;
6776 }
6777 chunk.text
6778 })
6779 .collect::<String>()
6780 .into();
6781
6782 drop(buffer);
6783
6784 // Position the selection in the rename editor so that it matches the current selection.
6785 this.show_local_selections = false;
6786 let rename_editor = cx.add_view(|cx| {
6787 let mut editor = Editor::single_line(None, cx);
6788 if let Some(old_highlight_id) = old_highlight_id {
6789 editor.override_text_style =
6790 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
6791 }
6792 editor.buffer.update(cx, |buffer, cx| {
6793 buffer.edit([(0..0, old_name.clone())], None, cx)
6794 });
6795 editor.select_all(&SelectAll, cx);
6796 editor
6797 });
6798
6799 let ranges = this
6800 .clear_background_highlights::<DocumentHighlightWrite>(cx)
6801 .into_iter()
6802 .flat_map(|(_, ranges)| ranges)
6803 .chain(
6804 this.clear_background_highlights::<DocumentHighlightRead>(cx)
6805 .into_iter()
6806 .flat_map(|(_, ranges)| ranges),
6807 )
6808 .collect();
6809
6810 this.highlight_text::<Rename>(
6811 ranges,
6812 HighlightStyle {
6813 fade_out: Some(style.rename_fade),
6814 ..Default::default()
6815 },
6816 cx,
6817 );
6818 cx.focus(&rename_editor);
6819 let block_id = this.insert_blocks(
6820 [BlockProperties {
6821 style: BlockStyle::Flex,
6822 position: range.start.clone(),
6823 height: 1,
6824 render: Arc::new({
6825 let editor = rename_editor.clone();
6826 move |cx: &mut BlockContext| {
6827 ChildView::new(&editor, cx)
6828 .contained()
6829 .with_padding_left(cx.anchor_x)
6830 .into_any()
6831 }
6832 }),
6833 disposition: BlockDisposition::Below,
6834 }],
6835 Some(Autoscroll::fit()),
6836 cx,
6837 )[0];
6838 this.pending_rename = Some(RenameState {
6839 range,
6840 old_name,
6841 editor: rename_editor,
6842 block_id,
6843 });
6844 })?;
6845 }
6846
6847 Ok(())
6848 }))
6849 }
6850
6851 pub fn confirm_rename(
6852 workspace: &mut Workspace,
6853 _: &ConfirmRename,
6854 cx: &mut ViewContext<Workspace>,
6855 ) -> Option<Task<Result<()>>> {
6856 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
6857
6858 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
6859 let rename = editor.take_rename(false, cx)?;
6860 let buffer = editor.buffer.read(cx);
6861 let (start_buffer, start) =
6862 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
6863 let (end_buffer, end) =
6864 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
6865 if start_buffer == end_buffer {
6866 let new_name = rename.editor.read(cx).text(cx);
6867 Some((start_buffer, start..end, rename.old_name, new_name))
6868 } else {
6869 None
6870 }
6871 })?;
6872
6873 let rename = workspace.project().clone().update(cx, |project, cx| {
6874 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
6875 });
6876
6877 let editor = editor.downgrade();
6878 Some(cx.spawn(|workspace, mut cx| async move {
6879 let project_transaction = rename.await?;
6880 Self::open_project_transaction(
6881 &editor,
6882 workspace,
6883 project_transaction,
6884 format!("Rename: {} → {}", old_name, new_name),
6885 cx.clone(),
6886 )
6887 .await?;
6888
6889 editor.update(&mut cx, |editor, cx| {
6890 editor.refresh_document_highlights(cx);
6891 })?;
6892 Ok(())
6893 }))
6894 }
6895
6896 fn take_rename(
6897 &mut self,
6898 moving_cursor: bool,
6899 cx: &mut ViewContext<Self>,
6900 ) -> Option<RenameState> {
6901 let rename = self.pending_rename.take()?;
6902 self.remove_blocks(
6903 [rename.block_id].into_iter().collect(),
6904 Some(Autoscroll::fit()),
6905 cx,
6906 );
6907 self.clear_text_highlights::<Rename>(cx);
6908 self.show_local_selections = true;
6909
6910 if moving_cursor {
6911 let rename_editor = rename.editor.read(cx);
6912 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
6913
6914 // Update the selection to match the position of the selection inside
6915 // the rename editor.
6916 let snapshot = self.buffer.read(cx).read(cx);
6917 let rename_range = rename.range.to_offset(&snapshot);
6918 let cursor_in_editor = snapshot
6919 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
6920 .min(rename_range.end);
6921 drop(snapshot);
6922
6923 self.change_selections(None, cx, |s| {
6924 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
6925 });
6926 } else {
6927 self.refresh_document_highlights(cx);
6928 }
6929
6930 Some(rename)
6931 }
6932
6933 #[cfg(any(test, feature = "test-support"))]
6934 pub fn pending_rename(&self) -> Option<&RenameState> {
6935 self.pending_rename.as_ref()
6936 }
6937
6938 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6939 let project = match &self.project {
6940 Some(project) => project.clone(),
6941 None => return None,
6942 };
6943
6944 Some(self.perform_format(project, FormatTrigger::Manual, cx))
6945 }
6946
6947 fn perform_format(
6948 &mut self,
6949 project: ModelHandle<Project>,
6950 trigger: FormatTrigger,
6951 cx: &mut ViewContext<Self>,
6952 ) -> Task<Result<()>> {
6953 let buffer = self.buffer().clone();
6954 let buffers = buffer.read(cx).all_buffers();
6955
6956 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
6957 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
6958
6959 cx.spawn(|_, mut cx| async move {
6960 let transaction = futures::select_biased! {
6961 _ = timeout => {
6962 log::warn!("timed out waiting for formatting");
6963 None
6964 }
6965 transaction = format.log_err().fuse() => transaction,
6966 };
6967
6968 buffer.update(&mut cx, |buffer, cx| {
6969 if let Some(transaction) = transaction {
6970 if !buffer.is_singleton() {
6971 buffer.push_transaction(&transaction.0, cx);
6972 }
6973 }
6974
6975 cx.notify();
6976 });
6977
6978 Ok(())
6979 })
6980 }
6981
6982 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
6983 if let Some(project) = self.project.clone() {
6984 self.buffer.update(cx, |multi_buffer, cx| {
6985 project.update(cx, |project, cx| {
6986 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
6987 });
6988 })
6989 }
6990 }
6991
6992 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
6993 cx.show_character_palette();
6994 }
6995
6996 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
6997 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
6998 let buffer = self.buffer.read(cx).snapshot(cx);
6999 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
7000 let is_valid = buffer
7001 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
7002 .any(|entry| {
7003 entry.diagnostic.is_primary
7004 && !entry.range.is_empty()
7005 && entry.range.start == primary_range_start
7006 && entry.diagnostic.message == active_diagnostics.primary_message
7007 });
7008
7009 if is_valid != active_diagnostics.is_valid {
7010 active_diagnostics.is_valid = is_valid;
7011 let mut new_styles = HashMap::default();
7012 for (block_id, diagnostic) in &active_diagnostics.blocks {
7013 new_styles.insert(
7014 *block_id,
7015 diagnostic_block_renderer(diagnostic.clone(), is_valid),
7016 );
7017 }
7018 self.display_map
7019 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
7020 }
7021 }
7022 }
7023
7024 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
7025 self.dismiss_diagnostics(cx);
7026 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
7027 let buffer = self.buffer.read(cx).snapshot(cx);
7028
7029 let mut primary_range = None;
7030 let mut primary_message = None;
7031 let mut group_end = Point::zero();
7032 let diagnostic_group = buffer
7033 .diagnostic_group::<Point>(group_id)
7034 .map(|entry| {
7035 if entry.range.end > group_end {
7036 group_end = entry.range.end;
7037 }
7038 if entry.diagnostic.is_primary {
7039 primary_range = Some(entry.range.clone());
7040 primary_message = Some(entry.diagnostic.message.clone());
7041 }
7042 entry
7043 })
7044 .collect::<Vec<_>>();
7045 let primary_range = primary_range?;
7046 let primary_message = primary_message?;
7047 let primary_range =
7048 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
7049
7050 let blocks = display_map
7051 .insert_blocks(
7052 diagnostic_group.iter().map(|entry| {
7053 let diagnostic = entry.diagnostic.clone();
7054 let message_height = diagnostic.message.lines().count() as u8;
7055 BlockProperties {
7056 style: BlockStyle::Fixed,
7057 position: buffer.anchor_after(entry.range.start),
7058 height: message_height,
7059 render: diagnostic_block_renderer(diagnostic, true),
7060 disposition: BlockDisposition::Below,
7061 }
7062 }),
7063 cx,
7064 )
7065 .into_iter()
7066 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
7067 .collect();
7068
7069 Some(ActiveDiagnosticGroup {
7070 primary_range,
7071 primary_message,
7072 blocks,
7073 is_valid: true,
7074 })
7075 });
7076 self.active_diagnostics.is_some()
7077 }
7078
7079 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
7080 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
7081 self.display_map.update(cx, |display_map, cx| {
7082 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
7083 });
7084 cx.notify();
7085 }
7086 }
7087
7088 pub fn set_selections_from_remote(
7089 &mut self,
7090 selections: Vec<Selection<Anchor>>,
7091 pending_selection: Option<Selection<Anchor>>,
7092 cx: &mut ViewContext<Self>,
7093 ) {
7094 let old_cursor_position = self.selections.newest_anchor().head();
7095 self.selections.change_with(cx, |s| {
7096 s.select_anchors(selections);
7097 if let Some(pending_selection) = pending_selection {
7098 s.set_pending(pending_selection, SelectMode::Character);
7099 } else {
7100 s.clear_pending();
7101 }
7102 });
7103 self.selections_did_change(false, &old_cursor_position, cx);
7104 }
7105
7106 fn push_to_selection_history(&mut self) {
7107 self.selection_history.push(SelectionHistoryEntry {
7108 selections: self.selections.disjoint_anchors(),
7109 select_next_state: self.select_next_state.clone(),
7110 select_prev_state: self.select_prev_state.clone(),
7111 add_selections_state: self.add_selections_state.clone(),
7112 });
7113 }
7114
7115 pub fn transact(
7116 &mut self,
7117 cx: &mut ViewContext<Self>,
7118 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
7119 ) -> Option<TransactionId> {
7120 self.start_transaction_at(Instant::now(), cx);
7121 update(self, cx);
7122 self.end_transaction_at(Instant::now(), cx)
7123 }
7124
7125 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
7126 self.end_selection(cx);
7127 if let Some(tx_id) = self
7128 .buffer
7129 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
7130 {
7131 self.selection_history
7132 .insert_transaction(tx_id, self.selections.disjoint_anchors());
7133 }
7134 }
7135
7136 fn end_transaction_at(
7137 &mut self,
7138 now: Instant,
7139 cx: &mut ViewContext<Self>,
7140 ) -> Option<TransactionId> {
7141 if let Some(tx_id) = self
7142 .buffer
7143 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
7144 {
7145 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
7146 *end_selections = Some(self.selections.disjoint_anchors());
7147 } else {
7148 error!("unexpectedly ended a transaction that wasn't started by this editor");
7149 }
7150
7151 cx.emit(Event::Edited);
7152 Some(tx_id)
7153 } else {
7154 None
7155 }
7156 }
7157
7158 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
7159 let mut fold_ranges = Vec::new();
7160
7161 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7162
7163 let selections = self.selections.all::<Point>(cx);
7164 for selection in selections {
7165 let range = selection.range().sorted();
7166 let buffer_start_row = range.start.row;
7167
7168 for row in (0..=range.end.row).rev() {
7169 let fold_range = display_map.foldable_range(row);
7170
7171 if let Some(fold_range) = fold_range {
7172 if fold_range.end.row >= buffer_start_row {
7173 fold_ranges.push(fold_range);
7174 if row <= range.start.row {
7175 break;
7176 }
7177 }
7178 }
7179 }
7180 }
7181
7182 self.fold_ranges(fold_ranges, true, cx);
7183 }
7184
7185 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
7186 let buffer_row = fold_at.buffer_row;
7187 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7188
7189 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
7190 let autoscroll = self
7191 .selections
7192 .all::<Point>(cx)
7193 .iter()
7194 .any(|selection| fold_range.overlaps(&selection.range()));
7195
7196 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
7197 }
7198 }
7199
7200 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
7201 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7202 let buffer = &display_map.buffer_snapshot;
7203 let selections = self.selections.all::<Point>(cx);
7204 let ranges = selections
7205 .iter()
7206 .map(|s| {
7207 let range = s.display_range(&display_map).sorted();
7208 let mut start = range.start.to_point(&display_map);
7209 let mut end = range.end.to_point(&display_map);
7210 start.column = 0;
7211 end.column = buffer.line_len(end.row);
7212 start..end
7213 })
7214 .collect::<Vec<_>>();
7215
7216 self.unfold_ranges(ranges, true, true, cx);
7217 }
7218
7219 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
7220 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7221
7222 let intersection_range = Point::new(unfold_at.buffer_row, 0)
7223 ..Point::new(
7224 unfold_at.buffer_row,
7225 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
7226 );
7227
7228 let autoscroll = self
7229 .selections
7230 .all::<Point>(cx)
7231 .iter()
7232 .any(|selection| selection.range().overlaps(&intersection_range));
7233
7234 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
7235 }
7236
7237 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
7238 let selections = self.selections.all::<Point>(cx);
7239 let ranges = selections.into_iter().map(|s| s.start..s.end);
7240 self.fold_ranges(ranges, true, cx);
7241 }
7242
7243 pub fn fold_ranges<T: ToOffset + Clone>(
7244 &mut self,
7245 ranges: impl IntoIterator<Item = Range<T>>,
7246 auto_scroll: bool,
7247 cx: &mut ViewContext<Self>,
7248 ) {
7249 let mut ranges = ranges.into_iter().peekable();
7250 if ranges.peek().is_some() {
7251 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
7252
7253 if auto_scroll {
7254 self.request_autoscroll(Autoscroll::fit(), cx);
7255 }
7256
7257 cx.notify();
7258 }
7259 }
7260
7261 pub fn unfold_ranges<T: ToOffset + Clone>(
7262 &mut self,
7263 ranges: impl IntoIterator<Item = Range<T>>,
7264 inclusive: bool,
7265 auto_scroll: bool,
7266 cx: &mut ViewContext<Self>,
7267 ) {
7268 let mut ranges = ranges.into_iter().peekable();
7269 if ranges.peek().is_some() {
7270 self.display_map
7271 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
7272 if auto_scroll {
7273 self.request_autoscroll(Autoscroll::fit(), cx);
7274 }
7275
7276 cx.notify();
7277 }
7278 }
7279
7280 pub fn gutter_hover(
7281 &mut self,
7282 GutterHover { hovered }: &GutterHover,
7283 cx: &mut ViewContext<Self>,
7284 ) {
7285 self.gutter_hovered = *hovered;
7286 cx.notify();
7287 }
7288
7289 pub fn insert_blocks(
7290 &mut self,
7291 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
7292 autoscroll: Option<Autoscroll>,
7293 cx: &mut ViewContext<Self>,
7294 ) -> Vec<BlockId> {
7295 let blocks = self
7296 .display_map
7297 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
7298 if let Some(autoscroll) = autoscroll {
7299 self.request_autoscroll(autoscroll, cx);
7300 }
7301 blocks
7302 }
7303
7304 pub fn replace_blocks(
7305 &mut self,
7306 blocks: HashMap<BlockId, RenderBlock>,
7307 autoscroll: Option<Autoscroll>,
7308 cx: &mut ViewContext<Self>,
7309 ) {
7310 self.display_map
7311 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
7312 if let Some(autoscroll) = autoscroll {
7313 self.request_autoscroll(autoscroll, cx);
7314 }
7315 }
7316
7317 pub fn remove_blocks(
7318 &mut self,
7319 block_ids: HashSet<BlockId>,
7320 autoscroll: Option<Autoscroll>,
7321 cx: &mut ViewContext<Self>,
7322 ) {
7323 self.display_map.update(cx, |display_map, cx| {
7324 display_map.remove_blocks(block_ids, cx)
7325 });
7326 if let Some(autoscroll) = autoscroll {
7327 self.request_autoscroll(autoscroll, cx);
7328 }
7329 }
7330
7331 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
7332 self.display_map
7333 .update(cx, |map, cx| map.snapshot(cx))
7334 .longest_row()
7335 }
7336
7337 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
7338 self.display_map
7339 .update(cx, |map, cx| map.snapshot(cx))
7340 .max_point()
7341 }
7342
7343 pub fn text(&self, cx: &AppContext) -> String {
7344 self.buffer.read(cx).read(cx).text()
7345 }
7346
7347 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
7348 self.transact(cx, |this, cx| {
7349 this.buffer
7350 .read(cx)
7351 .as_singleton()
7352 .expect("you can only call set_text on editors for singleton buffers")
7353 .update(cx, |buffer, cx| buffer.set_text(text, cx));
7354 });
7355 }
7356
7357 pub fn display_text(&self, cx: &mut AppContext) -> String {
7358 self.display_map
7359 .update(cx, |map, cx| map.snapshot(cx))
7360 .text()
7361 }
7362
7363 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
7364 let mut wrap_guides = smallvec::smallvec![];
7365
7366 if self.show_wrap_guides == Some(false) {
7367 return wrap_guides;
7368 }
7369
7370 let settings = self.buffer.read(cx).settings_at(0, cx);
7371 if settings.show_wrap_guides {
7372 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
7373 wrap_guides.push((soft_wrap as usize, true));
7374 }
7375 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
7376 }
7377
7378 wrap_guides
7379 }
7380
7381 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
7382 let settings = self.buffer.read(cx).settings_at(0, cx);
7383 let mode = self
7384 .soft_wrap_mode_override
7385 .unwrap_or_else(|| settings.soft_wrap);
7386 match mode {
7387 language_settings::SoftWrap::None => SoftWrap::None,
7388 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
7389 language_settings::SoftWrap::PreferredLineLength => {
7390 SoftWrap::Column(settings.preferred_line_length)
7391 }
7392 }
7393 }
7394
7395 pub fn set_soft_wrap_mode(
7396 &mut self,
7397 mode: language_settings::SoftWrap,
7398 cx: &mut ViewContext<Self>,
7399 ) {
7400 self.soft_wrap_mode_override = Some(mode);
7401 cx.notify();
7402 }
7403
7404 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
7405 self.display_map
7406 .update(cx, |map, cx| map.set_wrap_width(width, cx))
7407 }
7408
7409 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
7410 if self.soft_wrap_mode_override.is_some() {
7411 self.soft_wrap_mode_override.take();
7412 } else {
7413 let soft_wrap = match self.soft_wrap_mode(cx) {
7414 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
7415 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
7416 };
7417 self.soft_wrap_mode_override = Some(soft_wrap);
7418 }
7419 cx.notify();
7420 }
7421
7422 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7423 self.show_gutter = show_gutter;
7424 cx.notify();
7425 }
7426
7427 pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7428 self.show_wrap_guides = Some(show_gutter);
7429 cx.notify();
7430 }
7431
7432 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
7433 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7434 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7435 cx.reveal_path(&file.abs_path(cx));
7436 }
7437 }
7438 }
7439
7440 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
7441 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7442 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7443 if let Some(path) = file.abs_path(cx).to_str() {
7444 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7445 }
7446 }
7447 }
7448 }
7449
7450 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
7451 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7452 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7453 if let Some(path) = file.path().to_str() {
7454 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7455 }
7456 }
7457 }
7458 }
7459
7460 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
7461 self.highlighted_rows = rows;
7462 }
7463
7464 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
7465 self.highlighted_rows.clone()
7466 }
7467
7468 pub fn highlight_background<T: 'static>(
7469 &mut self,
7470 ranges: Vec<Range<Anchor>>,
7471 color_fetcher: fn(&Theme) -> Color,
7472 cx: &mut ViewContext<Self>,
7473 ) {
7474 self.background_highlights
7475 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
7476 cx.notify();
7477 }
7478
7479 #[allow(clippy::type_complexity)]
7480 pub fn clear_background_highlights<T: 'static>(
7481 &mut self,
7482 cx: &mut ViewContext<Self>,
7483 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
7484 let highlights = self.background_highlights.remove(&TypeId::of::<T>());
7485 if highlights.is_some() {
7486 cx.notify();
7487 }
7488 highlights
7489 }
7490
7491 #[cfg(feature = "test-support")]
7492 pub fn all_background_highlights(
7493 &mut self,
7494 cx: &mut ViewContext<Self>,
7495 ) -> Vec<(Range<DisplayPoint>, Color)> {
7496 let snapshot = self.snapshot(cx);
7497 let buffer = &snapshot.buffer_snapshot;
7498 let start = buffer.anchor_before(0);
7499 let end = buffer.anchor_after(buffer.len());
7500 let theme = theme::current(cx);
7501 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
7502 }
7503
7504 fn document_highlights_for_position<'a>(
7505 &'a self,
7506 position: Anchor,
7507 buffer: &'a MultiBufferSnapshot,
7508 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
7509 let read_highlights = self
7510 .background_highlights
7511 .get(&TypeId::of::<DocumentHighlightRead>())
7512 .map(|h| &h.1);
7513 let write_highlights = self
7514 .background_highlights
7515 .get(&TypeId::of::<DocumentHighlightWrite>())
7516 .map(|h| &h.1);
7517 let left_position = position.bias_left(buffer);
7518 let right_position = position.bias_right(buffer);
7519 read_highlights
7520 .into_iter()
7521 .chain(write_highlights)
7522 .flat_map(move |ranges| {
7523 let start_ix = match ranges.binary_search_by(|probe| {
7524 let cmp = probe.end.cmp(&left_position, buffer);
7525 if cmp.is_ge() {
7526 Ordering::Greater
7527 } else {
7528 Ordering::Less
7529 }
7530 }) {
7531 Ok(i) | Err(i) => i,
7532 };
7533
7534 let right_position = right_position.clone();
7535 ranges[start_ix..]
7536 .iter()
7537 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
7538 })
7539 }
7540
7541 pub fn background_highlights_in_range(
7542 &self,
7543 search_range: Range<Anchor>,
7544 display_snapshot: &DisplaySnapshot,
7545 theme: &Theme,
7546 ) -> Vec<(Range<DisplayPoint>, Color)> {
7547 let mut results = Vec::new();
7548 let buffer = &display_snapshot.buffer_snapshot;
7549 for (color_fetcher, ranges) in self.background_highlights.values() {
7550 let color = color_fetcher(theme);
7551 let start_ix = match ranges.binary_search_by(|probe| {
7552 let cmp = probe.end.cmp(&search_range.start, buffer);
7553 if cmp.is_gt() {
7554 Ordering::Greater
7555 } else {
7556 Ordering::Less
7557 }
7558 }) {
7559 Ok(i) | Err(i) => i,
7560 };
7561 for range in &ranges[start_ix..] {
7562 if range.start.cmp(&search_range.end, buffer).is_ge() {
7563 break;
7564 }
7565 let start = range
7566 .start
7567 .to_point(buffer)
7568 .to_display_point(display_snapshot);
7569 let end = range
7570 .end
7571 .to_point(buffer)
7572 .to_display_point(display_snapshot);
7573 results.push((start..end, color))
7574 }
7575 }
7576 results
7577 }
7578 pub fn background_highlights_in_range_for<T: 'static>(
7579 &self,
7580 search_range: Range<Anchor>,
7581 display_snapshot: &DisplaySnapshot,
7582 theme: &Theme,
7583 ) -> Vec<(Range<DisplayPoint>, Color)> {
7584 let mut results = Vec::new();
7585 let buffer = &display_snapshot.buffer_snapshot;
7586 let Some((color_fetcher, ranges)) = self.background_highlights
7587 .get(&TypeId::of::<T>()) else {
7588 return vec![];
7589 };
7590
7591 let color = color_fetcher(theme);
7592 let start_ix = match ranges.binary_search_by(|probe| {
7593 let cmp = probe.end.cmp(&search_range.start, buffer);
7594 if cmp.is_gt() {
7595 Ordering::Greater
7596 } else {
7597 Ordering::Less
7598 }
7599 }) {
7600 Ok(i) | Err(i) => i,
7601 };
7602 for range in &ranges[start_ix..] {
7603 if range.start.cmp(&search_range.end, buffer).is_ge() {
7604 break;
7605 }
7606 let start = range
7607 .start
7608 .to_point(buffer)
7609 .to_display_point(display_snapshot);
7610 let end = range
7611 .end
7612 .to_point(buffer)
7613 .to_display_point(display_snapshot);
7614 results.push((start..end, color))
7615 }
7616
7617 results
7618 }
7619
7620 pub fn background_highlight_row_ranges<T: 'static>(
7621 &self,
7622 search_range: Range<Anchor>,
7623 display_snapshot: &DisplaySnapshot,
7624 count: usize,
7625 ) -> Vec<RangeInclusive<DisplayPoint>> {
7626 let mut results = Vec::new();
7627 let buffer = &display_snapshot.buffer_snapshot;
7628 let Some((_, ranges)) = self.background_highlights
7629 .get(&TypeId::of::<T>()) else {
7630 return vec![];
7631 };
7632
7633 let start_ix = match ranges.binary_search_by(|probe| {
7634 let cmp = probe.end.cmp(&search_range.start, buffer);
7635 if cmp.is_gt() {
7636 Ordering::Greater
7637 } else {
7638 Ordering::Less
7639 }
7640 }) {
7641 Ok(i) | Err(i) => i,
7642 };
7643 let mut push_region = |start: Option<Point>, end: Option<Point>| {
7644 if let (Some(start_display), Some(end_display)) = (start, end) {
7645 results.push(
7646 start_display.to_display_point(display_snapshot)
7647 ..=end_display.to_display_point(display_snapshot),
7648 );
7649 }
7650 };
7651 let mut start_row: Option<Point> = None;
7652 let mut end_row: Option<Point> = None;
7653 if ranges.len() > count {
7654 return vec![];
7655 }
7656 for range in &ranges[start_ix..] {
7657 if range.start.cmp(&search_range.end, buffer).is_ge() {
7658 break;
7659 }
7660 let end = range.end.to_point(buffer);
7661 if let Some(current_row) = &end_row {
7662 if end.row == current_row.row {
7663 continue;
7664 }
7665 }
7666 let start = range.start.to_point(buffer);
7667
7668 if start_row.is_none() {
7669 assert_eq!(end_row, None);
7670 start_row = Some(start);
7671 end_row = Some(end);
7672 continue;
7673 }
7674 if let Some(current_end) = end_row.as_mut() {
7675 if start.row > current_end.row + 1 {
7676 push_region(start_row, end_row);
7677 start_row = Some(start);
7678 end_row = Some(end);
7679 } else {
7680 // Merge two hunks.
7681 *current_end = end;
7682 }
7683 } else {
7684 unreachable!();
7685 }
7686 }
7687 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
7688 push_region(start_row, end_row);
7689 results
7690 }
7691
7692 pub fn highlight_text<T: 'static>(
7693 &mut self,
7694 ranges: Vec<Range<Anchor>>,
7695 style: HighlightStyle,
7696 cx: &mut ViewContext<Self>,
7697 ) {
7698 self.display_map.update(cx, |map, _| {
7699 map.highlight_text(TypeId::of::<T>(), ranges, style)
7700 });
7701 cx.notify();
7702 }
7703
7704 pub fn text_highlights<'a, T: 'static>(
7705 &'a self,
7706 cx: &'a AppContext,
7707 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
7708 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
7709 }
7710
7711 pub fn clear_text_highlights<T: 'static>(
7712 &mut self,
7713 cx: &mut ViewContext<Self>,
7714 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
7715 let highlights = self
7716 .display_map
7717 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
7718 if highlights.is_some() {
7719 cx.notify();
7720 }
7721 highlights
7722 }
7723
7724 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
7725 self.blink_manager.read(cx).visible() && self.focused
7726 }
7727
7728 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
7729 cx.notify();
7730 }
7731
7732 fn on_buffer_event(
7733 &mut self,
7734 multibuffer: ModelHandle<MultiBuffer>,
7735 event: &multi_buffer::Event,
7736 cx: &mut ViewContext<Self>,
7737 ) {
7738 match event {
7739 multi_buffer::Event::Edited => {
7740 self.refresh_active_diagnostics(cx);
7741 self.refresh_code_actions(cx);
7742 if self.has_active_copilot_suggestion(cx) {
7743 self.update_visible_copilot_suggestion(cx);
7744 }
7745 cx.emit(Event::BufferEdited);
7746
7747 if let Some(project) = &self.project {
7748 let project = project.read(cx);
7749 let languages_affected = multibuffer
7750 .read(cx)
7751 .all_buffers()
7752 .into_iter()
7753 .filter_map(|buffer| {
7754 let buffer = buffer.read(cx);
7755 let language = buffer.language()?;
7756 if project.is_local()
7757 && project.language_servers_for_buffer(buffer, cx).count() == 0
7758 {
7759 None
7760 } else {
7761 Some(language)
7762 }
7763 })
7764 .cloned()
7765 .collect::<HashSet<_>>();
7766 if !languages_affected.is_empty() {
7767 self.refresh_inlay_hints(
7768 InlayHintRefreshReason::BufferEdited(languages_affected),
7769 cx,
7770 );
7771 }
7772 }
7773 }
7774 multi_buffer::Event::ExcerptsAdded {
7775 buffer,
7776 predecessor,
7777 excerpts,
7778 } => cx.emit(Event::ExcerptsAdded {
7779 buffer: buffer.clone(),
7780 predecessor: *predecessor,
7781 excerpts: excerpts.clone(),
7782 }),
7783 multi_buffer::Event::ExcerptsRemoved { ids } => {
7784 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
7785 }
7786 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
7787 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
7788 multi_buffer::Event::Saved => cx.emit(Event::Saved),
7789 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
7790 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
7791 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
7792 multi_buffer::Event::Closed => cx.emit(Event::Closed),
7793 multi_buffer::Event::DiagnosticsUpdated => {
7794 self.refresh_active_diagnostics(cx);
7795 }
7796 _ => {}
7797 };
7798 }
7799
7800 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
7801 cx.notify();
7802 }
7803
7804 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
7805 self.refresh_copilot_suggestions(true, cx);
7806 self.refresh_inlay_hints(
7807 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
7808 self.selections.newest_anchor().head(),
7809 &self.buffer.read(cx).snapshot(cx),
7810 cx,
7811 )),
7812 cx,
7813 );
7814 }
7815
7816 pub fn set_searchable(&mut self, searchable: bool) {
7817 self.searchable = searchable;
7818 }
7819
7820 pub fn searchable(&self) -> bool {
7821 self.searchable
7822 }
7823
7824 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
7825 let active_item = workspace.active_item(cx);
7826 let editor_handle = if let Some(editor) = active_item
7827 .as_ref()
7828 .and_then(|item| item.act_as::<Self>(cx))
7829 {
7830 editor
7831 } else {
7832 cx.propagate_action();
7833 return;
7834 };
7835
7836 let editor = editor_handle.read(cx);
7837 let buffer = editor.buffer.read(cx);
7838 if buffer.is_singleton() {
7839 cx.propagate_action();
7840 return;
7841 }
7842
7843 let mut new_selections_by_buffer = HashMap::default();
7844 for selection in editor.selections.all::<usize>(cx) {
7845 for (buffer, mut range, _) in
7846 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
7847 {
7848 if selection.reversed {
7849 mem::swap(&mut range.start, &mut range.end);
7850 }
7851 new_selections_by_buffer
7852 .entry(buffer)
7853 .or_insert(Vec::new())
7854 .push(range)
7855 }
7856 }
7857
7858 editor_handle.update(cx, |editor, cx| {
7859 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
7860 });
7861 let pane = workspace.active_pane().clone();
7862 pane.update(cx, |pane, _| pane.disable_history());
7863
7864 // We defer the pane interaction because we ourselves are a workspace item
7865 // and activating a new item causes the pane to call a method on us reentrantly,
7866 // which panics if we're on the stack.
7867 cx.defer(move |workspace, cx| {
7868 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
7869 let editor = workspace.open_project_item::<Self>(buffer, cx);
7870 editor.update(cx, |editor, cx| {
7871 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7872 s.select_ranges(ranges);
7873 });
7874 });
7875 }
7876
7877 pane.update(cx, |pane, _| pane.enable_history());
7878 });
7879 }
7880
7881 fn jump(
7882 workspace: &mut Workspace,
7883 path: ProjectPath,
7884 position: Point,
7885 anchor: language::Anchor,
7886 cx: &mut ViewContext<Workspace>,
7887 ) {
7888 let editor = workspace.open_path(path, None, true, cx);
7889 cx.spawn(|_, mut cx| async move {
7890 let editor = editor
7891 .await?
7892 .downcast::<Editor>()
7893 .ok_or_else(|| anyhow!("opened item was not an editor"))?
7894 .downgrade();
7895 editor.update(&mut cx, |editor, cx| {
7896 let buffer = editor
7897 .buffer()
7898 .read(cx)
7899 .as_singleton()
7900 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
7901 let buffer = buffer.read(cx);
7902 let cursor = if buffer.can_resolve(&anchor) {
7903 language::ToPoint::to_point(&anchor, buffer)
7904 } else {
7905 buffer.clip_point(position, Bias::Left)
7906 };
7907
7908 let nav_history = editor.nav_history.take();
7909 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7910 s.select_ranges([cursor..cursor]);
7911 });
7912 editor.nav_history = nav_history;
7913
7914 anyhow::Ok(())
7915 })??;
7916
7917 anyhow::Ok(())
7918 })
7919 .detach_and_log_err(cx);
7920 }
7921
7922 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
7923 let snapshot = self.buffer.read(cx).read(cx);
7924 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
7925 Some(
7926 ranges
7927 .iter()
7928 .map(move |range| {
7929 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
7930 })
7931 .collect(),
7932 )
7933 }
7934
7935 fn selection_replacement_ranges(
7936 &self,
7937 range: Range<OffsetUtf16>,
7938 cx: &AppContext,
7939 ) -> Vec<Range<OffsetUtf16>> {
7940 let selections = self.selections.all::<OffsetUtf16>(cx);
7941 let newest_selection = selections
7942 .iter()
7943 .max_by_key(|selection| selection.id)
7944 .unwrap();
7945 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
7946 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
7947 let snapshot = self.buffer.read(cx).read(cx);
7948 selections
7949 .into_iter()
7950 .map(|mut selection| {
7951 selection.start.0 =
7952 (selection.start.0 as isize).saturating_add(start_delta) as usize;
7953 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
7954 snapshot.clip_offset_utf16(selection.start, Bias::Left)
7955 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
7956 })
7957 .collect()
7958 }
7959
7960 fn report_copilot_event(
7961 &self,
7962 suggestion_id: Option<String>,
7963 suggestion_accepted: bool,
7964 cx: &AppContext,
7965 ) {
7966 let Some(project) = &self.project else {
7967 return
7968 };
7969
7970 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
7971 let file_extension = self
7972 .buffer
7973 .read(cx)
7974 .as_singleton()
7975 .and_then(|b| b.read(cx).file())
7976 .and_then(|file| Path::new(file.file_name(cx)).extension())
7977 .and_then(|e| e.to_str())
7978 .map(|a| a.to_string());
7979
7980 let telemetry = project.read(cx).client().telemetry().clone();
7981 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7982
7983 let event = ClickhouseEvent::Copilot {
7984 suggestion_id,
7985 suggestion_accepted,
7986 file_extension,
7987 };
7988 telemetry.report_clickhouse_event(event, telemetry_settings);
7989 }
7990
7991 fn report_editor_event(
7992 &self,
7993 operation: &'static str,
7994 file_extension: Option<String>,
7995 cx: &AppContext,
7996 ) {
7997 let Some(project) = &self.project else {
7998 return
7999 };
8000
8001 // If None, we are in a file without an extension
8002 let file = self
8003 .buffer
8004 .read(cx)
8005 .as_singleton()
8006 .and_then(|b| b.read(cx).file());
8007 let file_extension = file_extension.or(file
8008 .as_ref()
8009 .and_then(|file| Path::new(file.file_name(cx)).extension())
8010 .and_then(|e| e.to_str())
8011 .map(|a| a.to_string()));
8012
8013 let vim_mode = cx
8014 .global::<SettingsStore>()
8015 .raw_user_settings()
8016 .get("vim_mode")
8017 == Some(&serde_json::Value::Bool(true));
8018 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
8019 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
8020 let copilot_enabled_for_language = self
8021 .buffer
8022 .read(cx)
8023 .settings_at(0, cx)
8024 .show_copilot_suggestions;
8025
8026 let telemetry = project.read(cx).client().telemetry().clone();
8027 let event = ClickhouseEvent::Editor {
8028 file_extension,
8029 vim_mode,
8030 operation,
8031 copilot_enabled,
8032 copilot_enabled_for_language,
8033 };
8034 telemetry.report_clickhouse_event(event, telemetry_settings)
8035 }
8036
8037 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
8038 /// with each line being an array of {text, highlight} objects.
8039 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
8040 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
8041 return;
8042 };
8043
8044 #[derive(Serialize)]
8045 struct Chunk<'a> {
8046 text: String,
8047 highlight: Option<&'a str>,
8048 }
8049
8050 let snapshot = buffer.read(cx).snapshot();
8051 let range = self
8052 .selected_text_range(cx)
8053 .and_then(|selected_range| {
8054 if selected_range.is_empty() {
8055 None
8056 } else {
8057 Some(selected_range)
8058 }
8059 })
8060 .unwrap_or_else(|| 0..snapshot.len());
8061
8062 let chunks = snapshot.chunks(range, true);
8063 let mut lines = Vec::new();
8064 let mut line: VecDeque<Chunk> = VecDeque::new();
8065
8066 let theme = &theme::current(cx).editor.syntax;
8067
8068 for chunk in chunks {
8069 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
8070 let mut chunk_lines = chunk.text.split("\n").peekable();
8071 while let Some(text) = chunk_lines.next() {
8072 let mut merged_with_last_token = false;
8073 if let Some(last_token) = line.back_mut() {
8074 if last_token.highlight == highlight {
8075 last_token.text.push_str(text);
8076 merged_with_last_token = true;
8077 }
8078 }
8079
8080 if !merged_with_last_token {
8081 line.push_back(Chunk {
8082 text: text.into(),
8083 highlight,
8084 });
8085 }
8086
8087 if chunk_lines.peek().is_some() {
8088 if line.len() > 1 && line.front().unwrap().text.is_empty() {
8089 line.pop_front();
8090 }
8091 if line.len() > 1 && line.back().unwrap().text.is_empty() {
8092 line.pop_back();
8093 }
8094
8095 lines.push(mem::take(&mut line));
8096 }
8097 }
8098 }
8099
8100 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
8101 cx.write_to_clipboard(ClipboardItem::new(lines));
8102 }
8103
8104 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
8105 &self.inlay_hint_cache
8106 }
8107}
8108
8109fn inlay_hint_settings(
8110 location: Anchor,
8111 snapshot: &MultiBufferSnapshot,
8112 cx: &mut ViewContext<'_, '_, Editor>,
8113) -> InlayHintSettings {
8114 let file = snapshot.file_at(location);
8115 let language = snapshot.language_at(location);
8116 let settings = all_language_settings(file, cx);
8117 settings
8118 .language(language.map(|l| l.name()).as_deref())
8119 .inlay_hints
8120}
8121
8122fn consume_contiguous_rows(
8123 contiguous_row_selections: &mut Vec<Selection<Point>>,
8124 selection: &Selection<Point>,
8125 display_map: &DisplaySnapshot,
8126 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
8127) -> (u32, u32) {
8128 contiguous_row_selections.push(selection.clone());
8129 let start_row = selection.start.row;
8130 let mut end_row = ending_row(selection, display_map);
8131
8132 while let Some(next_selection) = selections.peek() {
8133 if next_selection.start.row <= end_row {
8134 end_row = ending_row(next_selection, display_map);
8135 contiguous_row_selections.push(selections.next().unwrap().clone());
8136 } else {
8137 break;
8138 }
8139 }
8140 (start_row, end_row)
8141}
8142
8143fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
8144 if next_selection.end.column > 0 || next_selection.is_empty() {
8145 display_map.next_line_boundary(next_selection.end).0.row + 1
8146 } else {
8147 next_selection.end.row
8148 }
8149}
8150
8151impl EditorSnapshot {
8152 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
8153 self.display_snapshot.buffer_snapshot.language_at(position)
8154 }
8155
8156 pub fn is_focused(&self) -> bool {
8157 self.is_focused
8158 }
8159
8160 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
8161 self.placeholder_text.as_ref()
8162 }
8163
8164 pub fn scroll_position(&self) -> Vector2F {
8165 self.scroll_anchor.scroll_position(&self.display_snapshot)
8166 }
8167}
8168
8169impl Deref for EditorSnapshot {
8170 type Target = DisplaySnapshot;
8171
8172 fn deref(&self) -> &Self::Target {
8173 &self.display_snapshot
8174 }
8175}
8176
8177#[derive(Clone, Debug, PartialEq, Eq)]
8178pub enum Event {
8179 InputIgnored {
8180 text: Arc<str>,
8181 },
8182 ExcerptsAdded {
8183 buffer: ModelHandle<Buffer>,
8184 predecessor: ExcerptId,
8185 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
8186 },
8187 ExcerptsRemoved {
8188 ids: Vec<ExcerptId>,
8189 },
8190 BufferEdited,
8191 Edited,
8192 Reparsed,
8193 Focused,
8194 Blurred,
8195 DirtyChanged,
8196 Saved,
8197 TitleChanged,
8198 DiffBaseChanged,
8199 SelectionsChanged {
8200 local: bool,
8201 },
8202 ScrollPositionChanged {
8203 local: bool,
8204 autoscroll: bool,
8205 },
8206 Closed,
8207}
8208
8209pub struct EditorFocused(pub ViewHandle<Editor>);
8210pub struct EditorBlurred(pub ViewHandle<Editor>);
8211pub struct EditorReleased(pub WeakViewHandle<Editor>);
8212
8213impl Entity for Editor {
8214 type Event = Event;
8215
8216 fn release(&mut self, cx: &mut AppContext) {
8217 cx.emit_global(EditorReleased(self.handle.clone()));
8218 }
8219}
8220
8221impl View for Editor {
8222 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
8223 let style = self.style(cx);
8224 let font_changed = self.display_map.update(cx, |map, cx| {
8225 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
8226 map.set_font(style.text.font_id, style.text.font_size, cx)
8227 });
8228
8229 if font_changed {
8230 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
8231 hide_hover(editor, cx);
8232 hide_link_definition(editor, cx);
8233 });
8234 }
8235
8236 Stack::new()
8237 .with_child(EditorElement::new(style.clone()))
8238 .with_child(ChildView::new(&self.mouse_context_menu, cx))
8239 .into_any()
8240 }
8241
8242 fn ui_name() -> &'static str {
8243 "Editor"
8244 }
8245
8246 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
8247 if cx.is_self_focused() {
8248 let focused_event = EditorFocused(cx.handle());
8249 cx.emit(Event::Focused);
8250 cx.emit_global(focused_event);
8251 }
8252 if let Some(rename) = self.pending_rename.as_ref() {
8253 cx.focus(&rename.editor);
8254 } else {
8255 if !self.focused {
8256 self.blink_manager.update(cx, BlinkManager::enable);
8257 }
8258 self.focused = true;
8259 self.buffer.update(cx, |buffer, cx| {
8260 buffer.finalize_last_transaction(cx);
8261 if self.leader_replica_id.is_none() {
8262 buffer.set_active_selections(
8263 &self.selections.disjoint_anchors(),
8264 self.selections.line_mode,
8265 self.cursor_shape,
8266 cx,
8267 );
8268 }
8269 });
8270 }
8271 }
8272
8273 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
8274 let blurred_event = EditorBlurred(cx.handle());
8275 cx.emit_global(blurred_event);
8276 self.focused = false;
8277 self.blink_manager.update(cx, BlinkManager::disable);
8278 self.buffer
8279 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
8280 self.hide_context_menu(cx);
8281 hide_hover(self, cx);
8282 cx.emit(Event::Blurred);
8283 cx.notify();
8284 }
8285
8286 fn modifiers_changed(
8287 &mut self,
8288 event: &gpui::platform::ModifiersChangedEvent,
8289 cx: &mut ViewContext<Self>,
8290 ) -> bool {
8291 let pending_selection = self.has_pending_selection();
8292
8293 if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() {
8294 if event.cmd && !pending_selection {
8295 let snapshot = self.snapshot(cx);
8296 let kind = if event.shift {
8297 LinkDefinitionKind::Type
8298 } else {
8299 LinkDefinitionKind::Symbol
8300 };
8301
8302 show_link_definition(kind, self, point, snapshot, cx);
8303 return false;
8304 }
8305 }
8306
8307 {
8308 if self.link_go_to_definition_state.symbol_range.is_some()
8309 || !self.link_go_to_definition_state.definitions.is_empty()
8310 {
8311 self.link_go_to_definition_state.symbol_range.take();
8312 self.link_go_to_definition_state.definitions.clear();
8313 cx.notify();
8314 }
8315
8316 self.link_go_to_definition_state.task = None;
8317
8318 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
8319 }
8320
8321 false
8322 }
8323
8324 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
8325 Self::reset_to_default_keymap_context(keymap);
8326 let mode = match self.mode {
8327 EditorMode::SingleLine => "single_line",
8328 EditorMode::AutoHeight { .. } => "auto_height",
8329 EditorMode::Full => "full",
8330 };
8331 keymap.add_key("mode", mode);
8332 if self.pending_rename.is_some() {
8333 keymap.add_identifier("renaming");
8334 }
8335 match self.context_menu.as_ref() {
8336 Some(ContextMenu::Completions(_)) => {
8337 keymap.add_identifier("menu");
8338 keymap.add_identifier("showing_completions")
8339 }
8340 Some(ContextMenu::CodeActions(_)) => {
8341 keymap.add_identifier("menu");
8342 keymap.add_identifier("showing_code_actions")
8343 }
8344 None => {}
8345 }
8346 for layer in self.keymap_context_layers.values() {
8347 keymap.extend(layer);
8348 }
8349
8350 if let Some(extension) = self
8351 .buffer
8352 .read(cx)
8353 .as_singleton()
8354 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
8355 {
8356 keymap.add_key("extension", extension.to_string());
8357 }
8358 }
8359
8360 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
8361 Some(
8362 self.buffer
8363 .read(cx)
8364 .read(cx)
8365 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
8366 .collect(),
8367 )
8368 }
8369
8370 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8371 // Prevent the IME menu from appearing when holding down an alphabetic key
8372 // while input is disabled.
8373 if !self.input_enabled {
8374 return None;
8375 }
8376
8377 let range = self.selections.newest::<OffsetUtf16>(cx).range();
8378 Some(range.start.0..range.end.0)
8379 }
8380
8381 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8382 let snapshot = self.buffer.read(cx).read(cx);
8383 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
8384 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
8385 }
8386
8387 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
8388 self.clear_text_highlights::<InputComposition>(cx);
8389 self.ime_transaction.take();
8390 }
8391
8392 fn replace_text_in_range(
8393 &mut self,
8394 range_utf16: Option<Range<usize>>,
8395 text: &str,
8396 cx: &mut ViewContext<Self>,
8397 ) {
8398 self.transact(cx, |this, cx| {
8399 if this.input_enabled {
8400 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
8401 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8402 Some(this.selection_replacement_ranges(range_utf16, cx))
8403 } else {
8404 this.marked_text_ranges(cx)
8405 };
8406
8407 if let Some(new_selected_ranges) = new_selected_ranges {
8408 this.change_selections(None, cx, |selections| {
8409 selections.select_ranges(new_selected_ranges)
8410 });
8411 }
8412 }
8413
8414 this.handle_input(text, cx);
8415 });
8416
8417 if !self.input_enabled {
8418 return;
8419 }
8420
8421 if let Some(transaction) = self.ime_transaction {
8422 self.buffer.update(cx, |buffer, cx| {
8423 buffer.group_until_transaction(transaction, cx);
8424 });
8425 }
8426
8427 self.unmark_text(cx);
8428 }
8429
8430 fn replace_and_mark_text_in_range(
8431 &mut self,
8432 range_utf16: Option<Range<usize>>,
8433 text: &str,
8434 new_selected_range_utf16: Option<Range<usize>>,
8435 cx: &mut ViewContext<Self>,
8436 ) {
8437 if !self.input_enabled {
8438 return;
8439 }
8440
8441 let transaction = self.transact(cx, |this, cx| {
8442 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
8443 let snapshot = this.buffer.read(cx).read(cx);
8444 if let Some(relative_range_utf16) = range_utf16.as_ref() {
8445 for marked_range in &mut marked_ranges {
8446 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
8447 marked_range.start.0 += relative_range_utf16.start;
8448 marked_range.start =
8449 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
8450 marked_range.end =
8451 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
8452 }
8453 }
8454 Some(marked_ranges)
8455 } else if let Some(range_utf16) = range_utf16 {
8456 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8457 Some(this.selection_replacement_ranges(range_utf16, cx))
8458 } else {
8459 None
8460 };
8461
8462 if let Some(ranges) = ranges_to_replace {
8463 this.change_selections(None, cx, |s| s.select_ranges(ranges));
8464 }
8465
8466 let marked_ranges = {
8467 let snapshot = this.buffer.read(cx).read(cx);
8468 this.selections
8469 .disjoint_anchors()
8470 .iter()
8471 .map(|selection| {
8472 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
8473 })
8474 .collect::<Vec<_>>()
8475 };
8476
8477 if text.is_empty() {
8478 this.unmark_text(cx);
8479 } else {
8480 this.highlight_text::<InputComposition>(
8481 marked_ranges.clone(),
8482 this.style(cx).composition_mark,
8483 cx,
8484 );
8485 }
8486
8487 this.handle_input(text, cx);
8488
8489 if let Some(new_selected_range) = new_selected_range_utf16 {
8490 let snapshot = this.buffer.read(cx).read(cx);
8491 let new_selected_ranges = marked_ranges
8492 .into_iter()
8493 .map(|marked_range| {
8494 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
8495 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
8496 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
8497 snapshot.clip_offset_utf16(new_start, Bias::Left)
8498 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
8499 })
8500 .collect::<Vec<_>>();
8501
8502 drop(snapshot);
8503 this.change_selections(None, cx, |selections| {
8504 selections.select_ranges(new_selected_ranges)
8505 });
8506 }
8507 });
8508
8509 self.ime_transaction = self.ime_transaction.or(transaction);
8510 if let Some(transaction) = self.ime_transaction {
8511 self.buffer.update(cx, |buffer, cx| {
8512 buffer.group_until_transaction(transaction, cx);
8513 });
8514 }
8515
8516 if self.text_highlights::<InputComposition>(cx).is_none() {
8517 self.ime_transaction.take();
8518 }
8519 }
8520}
8521
8522fn build_style(
8523 settings: &ThemeSettings,
8524 get_field_editor_theme: Option<&GetFieldEditorTheme>,
8525 override_text_style: Option<&OverrideTextStyle>,
8526 cx: &AppContext,
8527) -> EditorStyle {
8528 let font_cache = cx.font_cache();
8529 let line_height_scalar = settings.line_height();
8530 let theme_id = settings.theme.meta.id;
8531 let mut theme = settings.theme.editor.clone();
8532 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
8533 let field_editor_theme = get_field_editor_theme(&settings.theme);
8534 theme.text_color = field_editor_theme.text.color;
8535 theme.selection = field_editor_theme.selection;
8536 theme.background = field_editor_theme
8537 .container
8538 .background_color
8539 .unwrap_or_default();
8540 EditorStyle {
8541 text: field_editor_theme.text,
8542 placeholder_text: field_editor_theme.placeholder_text,
8543 line_height_scalar,
8544 theme,
8545 theme_id,
8546 }
8547 } else {
8548 let font_family_id = settings.buffer_font_family;
8549 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
8550 let font_properties = Default::default();
8551 let font_id = font_cache
8552 .select_font(font_family_id, &font_properties)
8553 .unwrap();
8554 let font_size = settings.buffer_font_size(cx);
8555 EditorStyle {
8556 text: TextStyle {
8557 color: settings.theme.editor.text_color,
8558 font_family_name,
8559 font_family_id,
8560 font_id,
8561 font_size,
8562 font_properties,
8563 underline: Default::default(),
8564 },
8565 placeholder_text: None,
8566 line_height_scalar,
8567 theme,
8568 theme_id,
8569 }
8570 };
8571
8572 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
8573 if let Some(highlighted) = style
8574 .text
8575 .clone()
8576 .highlight(highlight_style, font_cache)
8577 .log_err()
8578 {
8579 style.text = highlighted;
8580 }
8581 }
8582
8583 style
8584}
8585
8586trait SelectionExt {
8587 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
8588 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
8589 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
8590 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
8591 -> Range<u32>;
8592}
8593
8594impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
8595 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
8596 let start = self.start.to_point(buffer);
8597 let end = self.end.to_point(buffer);
8598 if self.reversed {
8599 end..start
8600 } else {
8601 start..end
8602 }
8603 }
8604
8605 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
8606 let start = self.start.to_offset(buffer);
8607 let end = self.end.to_offset(buffer);
8608 if self.reversed {
8609 end..start
8610 } else {
8611 start..end
8612 }
8613 }
8614
8615 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
8616 let start = self
8617 .start
8618 .to_point(&map.buffer_snapshot)
8619 .to_display_point(map);
8620 let end = self
8621 .end
8622 .to_point(&map.buffer_snapshot)
8623 .to_display_point(map);
8624 if self.reversed {
8625 end..start
8626 } else {
8627 start..end
8628 }
8629 }
8630
8631 fn spanned_rows(
8632 &self,
8633 include_end_if_at_line_start: bool,
8634 map: &DisplaySnapshot,
8635 ) -> Range<u32> {
8636 let start = self.start.to_point(&map.buffer_snapshot);
8637 let mut end = self.end.to_point(&map.buffer_snapshot);
8638 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
8639 end.row -= 1;
8640 }
8641
8642 let buffer_start = map.prev_line_boundary(start).0;
8643 let buffer_end = map.next_line_boundary(end).0;
8644 buffer_start.row..buffer_end.row + 1
8645 }
8646}
8647
8648impl<T: InvalidationRegion> InvalidationStack<T> {
8649 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
8650 where
8651 S: Clone + ToOffset,
8652 {
8653 while let Some(region) = self.last() {
8654 let all_selections_inside_invalidation_ranges =
8655 if selections.len() == region.ranges().len() {
8656 selections
8657 .iter()
8658 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
8659 .all(|(selection, invalidation_range)| {
8660 let head = selection.head().to_offset(buffer);
8661 invalidation_range.start <= head && invalidation_range.end >= head
8662 })
8663 } else {
8664 false
8665 };
8666
8667 if all_selections_inside_invalidation_ranges {
8668 break;
8669 } else {
8670 self.pop();
8671 }
8672 }
8673 }
8674}
8675
8676impl<T> Default for InvalidationStack<T> {
8677 fn default() -> Self {
8678 Self(Default::default())
8679 }
8680}
8681
8682impl<T> Deref for InvalidationStack<T> {
8683 type Target = Vec<T>;
8684
8685 fn deref(&self) -> &Self::Target {
8686 &self.0
8687 }
8688}
8689
8690impl<T> DerefMut for InvalidationStack<T> {
8691 fn deref_mut(&mut self) -> &mut Self::Target {
8692 &mut self.0
8693 }
8694}
8695
8696impl InvalidationRegion for SnippetState {
8697 fn ranges(&self) -> &[Range<Anchor>] {
8698 &self.ranges[self.active_index]
8699 }
8700}
8701
8702impl Deref for EditorStyle {
8703 type Target = theme::Editor;
8704
8705 fn deref(&self) -> &Self::Target {
8706 &self.theme
8707 }
8708}
8709
8710pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
8711 let mut highlighted_lines = Vec::new();
8712
8713 for (index, line) in diagnostic.message.lines().enumerate() {
8714 let line = match &diagnostic.source {
8715 Some(source) if index == 0 => {
8716 let source_highlight = Vec::from_iter(0..source.len());
8717 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
8718 }
8719
8720 _ => highlight_diagnostic_message(Vec::new(), line),
8721 };
8722 highlighted_lines.push(line);
8723 }
8724 let message = diagnostic.message;
8725 Arc::new(move |cx: &mut BlockContext| {
8726 let message = message.clone();
8727 let settings = settings::get::<ThemeSettings>(cx);
8728 let tooltip_style = settings.theme.tooltip.clone();
8729 let theme = &settings.theme.editor;
8730 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
8731 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
8732 let anchor_x = cx.anchor_x;
8733 enum BlockContextToolip {}
8734 MouseEventHandler::new::<BlockContext, _>(cx.block_id, cx, |_, _| {
8735 Flex::column()
8736 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
8737 Label::new(
8738 line.clone(),
8739 style.message.clone().with_font_size(font_size),
8740 )
8741 .with_highlights(highlights.clone())
8742 .contained()
8743 .with_margin_left(anchor_x)
8744 }))
8745 .aligned()
8746 .left()
8747 .into_any()
8748 })
8749 .with_cursor_style(CursorStyle::PointingHand)
8750 .on_click(MouseButton::Left, move |_, _, cx| {
8751 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
8752 })
8753 // We really need to rethink this ID system...
8754 .with_tooltip::<BlockContextToolip>(
8755 cx.block_id,
8756 "Copy diagnostic message",
8757 None,
8758 tooltip_style,
8759 cx,
8760 )
8761 .into_any()
8762 })
8763}
8764
8765pub fn highlight_diagnostic_message(
8766 initial_highlights: Vec<usize>,
8767 message: &str,
8768) -> (String, Vec<usize>) {
8769 let mut message_without_backticks = String::new();
8770 let mut prev_offset = 0;
8771 let mut inside_block = false;
8772 let mut highlights = initial_highlights;
8773 for (match_ix, (offset, _)) in message
8774 .match_indices('`')
8775 .chain([(message.len(), "")])
8776 .enumerate()
8777 {
8778 message_without_backticks.push_str(&message[prev_offset..offset]);
8779 if inside_block {
8780 highlights.extend(prev_offset - match_ix..offset - match_ix);
8781 }
8782
8783 inside_block = !inside_block;
8784 prev_offset = offset + 1;
8785 }
8786
8787 (message_without_backticks, highlights)
8788}
8789
8790pub fn diagnostic_style(
8791 severity: DiagnosticSeverity,
8792 valid: bool,
8793 theme: &theme::Editor,
8794) -> DiagnosticStyle {
8795 match (severity, valid) {
8796 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
8797 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
8798 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
8799 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
8800 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
8801 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
8802 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
8803 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
8804 _ => theme.invalid_hint_diagnostic.clone(),
8805 }
8806}
8807
8808pub fn combine_syntax_and_fuzzy_match_highlights(
8809 text: &str,
8810 default_style: HighlightStyle,
8811 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
8812 match_indices: &[usize],
8813) -> Vec<(Range<usize>, HighlightStyle)> {
8814 let mut result = Vec::new();
8815 let mut match_indices = match_indices.iter().copied().peekable();
8816
8817 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
8818 {
8819 syntax_highlight.weight = None;
8820
8821 // Add highlights for any fuzzy match characters before the next
8822 // syntax highlight range.
8823 while let Some(&match_index) = match_indices.peek() {
8824 if match_index >= range.start {
8825 break;
8826 }
8827 match_indices.next();
8828 let end_index = char_ix_after(match_index, text);
8829 let mut match_style = default_style;
8830 match_style.weight = Some(fonts::Weight::BOLD);
8831 result.push((match_index..end_index, match_style));
8832 }
8833
8834 if range.start == usize::MAX {
8835 break;
8836 }
8837
8838 // Add highlights for any fuzzy match characters within the
8839 // syntax highlight range.
8840 let mut offset = range.start;
8841 while let Some(&match_index) = match_indices.peek() {
8842 if match_index >= range.end {
8843 break;
8844 }
8845
8846 match_indices.next();
8847 if match_index > offset {
8848 result.push((offset..match_index, syntax_highlight));
8849 }
8850
8851 let mut end_index = char_ix_after(match_index, text);
8852 while let Some(&next_match_index) = match_indices.peek() {
8853 if next_match_index == end_index && next_match_index < range.end {
8854 end_index = char_ix_after(next_match_index, text);
8855 match_indices.next();
8856 } else {
8857 break;
8858 }
8859 }
8860
8861 let mut match_style = syntax_highlight;
8862 match_style.weight = Some(fonts::Weight::BOLD);
8863 result.push((match_index..end_index, match_style));
8864 offset = end_index;
8865 }
8866
8867 if offset < range.end {
8868 result.push((offset..range.end, syntax_highlight));
8869 }
8870 }
8871
8872 fn char_ix_after(ix: usize, text: &str) -> usize {
8873 ix + text[ix..].chars().next().unwrap().len_utf8()
8874 }
8875
8876 result
8877}
8878
8879pub fn styled_runs_for_code_label<'a>(
8880 label: &'a CodeLabel,
8881 syntax_theme: &'a theme::SyntaxTheme,
8882) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
8883 let fade_out = HighlightStyle {
8884 fade_out: Some(0.35),
8885 ..Default::default()
8886 };
8887
8888 let mut prev_end = label.filter_range.end;
8889 label
8890 .runs
8891 .iter()
8892 .enumerate()
8893 .flat_map(move |(ix, (range, highlight_id))| {
8894 let style = if let Some(style) = highlight_id.style(syntax_theme) {
8895 style
8896 } else {
8897 return Default::default();
8898 };
8899 let mut muted_style = style;
8900 muted_style.highlight(fade_out);
8901
8902 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
8903 if range.start >= label.filter_range.end {
8904 if range.start > prev_end {
8905 runs.push((prev_end..range.start, fade_out));
8906 }
8907 runs.push((range.clone(), muted_style));
8908 } else if range.end <= label.filter_range.end {
8909 runs.push((range.clone(), style));
8910 } else {
8911 runs.push((range.start..label.filter_range.end, style));
8912 runs.push((label.filter_range.end..range.end, muted_style));
8913 }
8914 prev_end = cmp::max(prev_end, range.end);
8915
8916 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
8917 runs.push((prev_end..label.text.len(), fade_out));
8918 }
8919
8920 runs
8921 })
8922}
8923
8924pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
8925 let mut index = 0;
8926 let mut codepoints = text.char_indices().peekable();
8927
8928 std::iter::from_fn(move || {
8929 let start_index = index;
8930 while let Some((new_index, codepoint)) = codepoints.next() {
8931 index = new_index + codepoint.len_utf8();
8932 let current_upper = codepoint.is_uppercase();
8933 let next_upper = codepoints
8934 .peek()
8935 .map(|(_, c)| c.is_uppercase())
8936 .unwrap_or(false);
8937
8938 if !current_upper && next_upper {
8939 return Some(&text[start_index..index]);
8940 }
8941 }
8942
8943 index = text.len();
8944 if start_index < text.len() {
8945 return Some(&text[start_index..]);
8946 }
8947 None
8948 })
8949 .flat_map(|word| word.split_inclusive('_'))
8950}
8951
8952trait RangeToAnchorExt {
8953 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
8954}
8955
8956impl<T: ToOffset> RangeToAnchorExt for Range<T> {
8957 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
8958 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
8959 }
8960}