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