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