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