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