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