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